Skip to content

Commit 365a886

Browse files
committed
fix: enforce SSL requirement for public access
XTLS/Xray-core#3884
1 parent b0a987f commit 365a886

File tree

1 file changed

+80
-15
lines changed

1 file changed

+80
-15
lines changed

main.py

Lines changed: 80 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,91 @@
1-
import uvicorn
2-
from app import app
3-
from config import (
4-
DEBUG,
5-
UVICORN_HOST,
6-
UVICORN_PORT,
7-
UVICORN_UDS,
8-
UVICORN_SSL_CERTFILE,
9-
UVICORN_SSL_KEYFILE
10-
)
1+
import click
112
import logging
3+
import os
4+
import ssl
5+
6+
import uvicorn
7+
from cryptography import x509
8+
from cryptography.hazmat.backends import default_backend
9+
10+
from app import app, logger
11+
from config import (DEBUG, UVICORN_HOST, UVICORN_PORT, UVICORN_SSL_CERTFILE,
12+
UVICORN_SSL_KEYFILE, UVICORN_UDS)
13+
14+
15+
def validate_cert_and_key(cert_file_path, key_file_path):
16+
if not os.path.isfile(cert_file_path):
17+
raise ValueError(f"SSL certificate file '{cert_file_path}' does not exist.")
18+
if not os.path.isfile(key_file_path):
19+
raise ValueError(f"SSL key file '{key_file_path}' does not exist.")
20+
21+
try:
22+
context = ssl.create_default_context()
23+
context.load_cert_chain(certfile=cert_file_path, keyfile=key_file_path)
24+
except ssl.SSLError as e:
25+
raise ValueError(f"SSL Error: {e}")
26+
27+
try:
28+
with open(cert_file_path, 'rb') as cert_file:
29+
cert_data = cert_file.read()
30+
cert = x509.load_pem_x509_certificate(cert_data, default_backend())
31+
32+
if cert.issuer == cert.subject:
33+
raise ValueError("The certificate is self-signed and not issued by a trusted CA.")
34+
35+
except Exception as e:
36+
raise ValueError(f"Certificate verification failed: {e}")
37+
1238

1339
if __name__ == "__main__":
1440
# Do NOT change workers count for now
1541
# multi-workers support isn't implemented yet for APScheduler and XRay module
42+
43+
bind_args = {}
44+
45+
if UVICORN_SSL_CERTFILE and UVICORN_SSL_KEYFILE:
46+
validate_cert_and_key(UVICORN_SSL_CERTFILE, UVICORN_SSL_KEYFILE)
47+
48+
bind_args['ssl_certfile'] = UVICORN_SSL_CERTFILE
49+
bind_args['ssl_keyfile'] = UVICORN_SSL_KEYFILE
50+
51+
if UVICORN_UDS:
52+
bind_args['uds'] = UVICORN_UDS
53+
else:
54+
bind_args['host'] = UVICORN_HOST
55+
bind_args['port'] = UVICORN_PORT
56+
57+
else:
58+
if UVICORN_UDS:
59+
bind_args['uds'] = UVICORN_UDS
60+
else:
61+
62+
logger.warning(f"""
63+
{click.style('IMPORTANT!', blink=True, bold=True, fg="yellow")}
64+
You're running Marzban without specifying {click.style('UVICORN_SSL_CERTFILE', italic=True, fg="magenta")} and {click.style('UVICORN_SSL_KEYFILE', italic=True, fg="magenta")}.
65+
The application will only be accessible through localhost. This means that {click.style('Marzban and subscription URLs will not be accessible externally', bold=True)}.
66+
67+
If you need external access, please provide the SSL files to allow the server to bind to 0.0.0.0. Alternatively, you can run the server on localhost or a Unix socket and use a reverse proxy, such as Nginx or Caddy, to handle SSL termination and provide external access.
68+
69+
If you wish to continue without SSL, you can use SSH port forwarding to access the application from your machine. note that in this case, subscription functionality will not work.
70+
71+
Use the following command:
72+
73+
{click.style(f'ssh -L {UVICORN_PORT}:localhost:{UVICORN_PORT} user@server', italic=True, fg="cyan")}
74+
75+
Then, navigate to {click.style(f'http://127.0.0.1:{UVICORN_PORT}', bold=True)} on your computer.
76+
""")
77+
78+
bind_args['host'] = '127.0.0.1'
79+
bind_args['port'] = UVICORN_PORT
80+
81+
if DEBUG:
82+
bind_args['uds'] = None
83+
bind_args['host'] = '0.0.0.0'
84+
1685
try:
1786
uvicorn.run(
1887
"main:app",
19-
host=('0.0.0.0' if DEBUG else UVICORN_HOST),
20-
port=UVICORN_PORT,
21-
uds=(None if DEBUG else UVICORN_UDS),
22-
ssl_certfile=UVICORN_SSL_CERTFILE,
23-
ssl_keyfile=UVICORN_SSL_KEYFILE,
88+
**bind_args,
2489
workers=1,
2590
reload=DEBUG,
2691
log_level=logging.DEBUG if DEBUG else logging.INFO

0 commit comments

Comments
 (0)