forked from PasarGuard/panel
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.py
More file actions
152 lines (118 loc) · 4.82 KB
/
main.py
File metadata and controls
152 lines (118 loc) · 4.82 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
import ipaddress
import logging
import os
import socket
import ssl
import click
import uvicorn
from cryptography import x509
from cryptography.hazmat.backends import default_backend
import dashboard # noqa
from app import app, logger # noqa
from app.utils.logger import LOGGING_CONFIG
from config import (
DEBUG,
UVICORN_HOST,
UVICORN_LOOP,
UVICORN_PORT,
UVICORN_SSL_CERTFILE,
UVICORN_SSL_KEYFILE,
UVICORN_UDS,
)
def check_and_modify_ip(ip_address: str) -> str:
"""
Check if an IP address is private. If not, return localhost.
IPv4 Private range = [
"192.168.0.0",
"192.168.255.255",
"10.0.0.0",
"10.255.255.255",
"172.16.0.0",
"172.31.255.255"
]
Args:
ip_address (str): IP address to check
Returns:
str: Original IP if private, otherwise localhost
Raises:
ValueError: If the provided IP address is invalid, return localhost.
"""
try:
# Attempt to resolve hostname to IP address
resolved_ip = socket.gethostbyname(ip_address)
# Convert string to IP address object
ip = ipaddress.ip_address(resolved_ip)
if ip == ipaddress.ip_address("0.0.0.0"):
return "localhost"
elif ip.is_private:
return ip_address
else:
return "localhost"
except ValueError:
return "localhost"
def validate_cert_and_key(cert_file_path, key_file_path):
if not os.path.isfile(cert_file_path):
raise ValueError(f"SSL certificate file '{cert_file_path}' does not exist.")
if not os.path.isfile(key_file_path):
raise ValueError(f"SSL key file '{key_file_path}' does not exist.")
try:
context = ssl.create_default_context()
context.load_cert_chain(certfile=cert_file_path, keyfile=key_file_path)
except ssl.SSLError as e:
raise ValueError(f"SSL Error: {e}")
try:
with open(cert_file_path, "rb") as cert_file:
cert_data = cert_file.read()
cert = x509.load_pem_x509_certificate(cert_data, default_backend())
if cert.issuer == cert.subject:
raise ValueError("The certificate is self-signed and not issued by a trusted CA.")
except Exception as e:
raise ValueError(f"Certificate verification failed: {e}")
if __name__ == "__main__":
# Do NOT change workers count for now
# multi-workers support isn't implemented yet for APScheduler and XRay module
bind_args = {}
if UVICORN_SSL_CERTFILE and UVICORN_SSL_KEYFILE:
validate_cert_and_key(UVICORN_SSL_CERTFILE, UVICORN_SSL_KEYFILE)
bind_args["ssl_certfile"] = UVICORN_SSL_CERTFILE
bind_args["ssl_keyfile"] = UVICORN_SSL_KEYFILE
if UVICORN_UDS:
bind_args["uds"] = UVICORN_UDS
else:
bind_args["host"] = UVICORN_HOST
bind_args["port"] = UVICORN_PORT
else:
if UVICORN_UDS:
bind_args["uds"] = UVICORN_UDS
else:
ip = check_and_modify_ip(UVICORN_HOST)
logger.warning(f"""
{click.style("IMPORTANT!", blink=True, bold=True, fg="yellow")}
You're running PasarGuard without specifying {click.style("UVICORN_SSL_CERTFILE", italic=True, fg="magenta")} and {click.style("UVICORN_SSL_KEYFILE", italic=True, fg="magenta")}.
The application will only be accessible through localhost. This means that {click.style("Marzban and subscription URLs will not be accessible externally", bold=True)}.
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.
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.
Use the following command:
{click.style(f"ssh -L {UVICORN_PORT}:localhost:{UVICORN_PORT} user@server", italic=True, fg="cyan")}
Then, navigate to {click.style(f"http://{ip}:{UVICORN_PORT}", bold=True)} on your computer.
""")
bind_args["host"] = ip
bind_args["port"] = UVICORN_PORT
if DEBUG:
bind_args["uds"] = None
bind_args["host"] = "0.0.0.0"
log_level = logging.DEBUG if DEBUG else logging.INFO
LOGGING_CONFIG["loggers"]["uvicorn"]["level"] = log_level
LOGGING_CONFIG["loggers"]["uvicorn.error"]["level"] = log_level
LOGGING_CONFIG["loggers"]["uvicorn.access"]["level"] = log_level
try:
uvicorn.run(
"main:app",
**bind_args,
workers=1,
reload=DEBUG,
log_config=LOGGING_CONFIG,
loop=UVICORN_LOOP,
)
except FileNotFoundError: # to prevent error on removing unix sock
pass