mirror of
https://github.com/house-of-vanity/OutFleet.git
synced 2025-07-07 01:24:06 +00:00
Reworked dynamic keys link generation.
This commit is contained in:
38
main.py
38
main.py
@ -47,8 +47,10 @@ NAMESPACE = k8s.NAMESPACE
|
|||||||
SERVERS = list()
|
SERVERS = list()
|
||||||
BROKEN_SERVERS = list()
|
BROKEN_SERVERS = list()
|
||||||
CLIENTS = dict()
|
CLIENTS = dict()
|
||||||
VERSION = '6'
|
VERSION = '7'
|
||||||
SECRET_LINK_LENGTH = 8
|
SECRET_LINK_LENGTH = 8
|
||||||
|
SECRET_LINK_PREFIX = '$2b$12$'
|
||||||
|
SS_PREFIX = "\u0005\u00DC\u005F\u00E0\u0001\u0020"
|
||||||
HOSTNAME = ""
|
HOSTNAME = ""
|
||||||
WRONG_DOOR = "Hey buddy, i think you got the wrong door the leather-club is two blocks down"
|
WRONG_DOOR = "Hey buddy, i think you got the wrong door the leather-club is two blocks down"
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
@ -147,8 +149,11 @@ def clients():
|
|||||||
return render_template(
|
return render_template(
|
||||||
"clients.html",
|
"clients.html",
|
||||||
SERVERS=SERVERS,
|
SERVERS=SERVERS,
|
||||||
|
bcrypt=bcrypt,
|
||||||
CLIENTS=CLIENTS,
|
CLIENTS=CLIENTS,
|
||||||
VERSION=VERSION,
|
VERSION=VERSION,
|
||||||
|
SECRET_LINK_LENGTH=SECRET_LINK_LENGTH,
|
||||||
|
SECRET_LINK_PREFIX=SECRET_LINK_PREFIX,
|
||||||
K8S_NAMESPACE=k8s.NAMESPACE,
|
K8S_NAMESPACE=k8s.NAMESPACE,
|
||||||
nt=request.args.get("nt"),
|
nt=request.args.get("nt"),
|
||||||
nl=request.args.get("nl"),
|
nl=request.args.get("nl"),
|
||||||
@ -314,8 +319,15 @@ def del_client():
|
|||||||
return redirect(url_for("clients", nt="User has been deleted"))
|
return redirect(url_for("clients", nt="User has been deleted"))
|
||||||
|
|
||||||
|
|
||||||
@app.route("/dynamic/<hash_secret>", methods=["GET"], strict_slashes=False)
|
@app.route("/dynamic/<path:hash_secret>", methods=["GET"], strict_slashes=False)
|
||||||
def dynamic(hash_secret):
|
def dynamic(hash_secret):
|
||||||
|
# Depricated scheme.
|
||||||
|
for server in SERVERS:
|
||||||
|
if hash_secret.startswith(server.data["name"]):
|
||||||
|
log.warning("Deprecated key request")
|
||||||
|
server_name = hash_secret.split('/')[0]
|
||||||
|
client_id = hash_secret.split('/')[1]
|
||||||
|
return dynamic_depticated(server_name, client_id)
|
||||||
try:
|
try:
|
||||||
short_hash_server = hash_secret[0:SECRET_LINK_LENGTH]
|
short_hash_server = hash_secret[0:SECRET_LINK_LENGTH]
|
||||||
short_hash_client = hash_secret[SECRET_LINK_LENGTH:SECRET_LINK_LENGTH * 2 ]
|
short_hash_client = hash_secret[SECRET_LINK_LENGTH:SECRET_LINK_LENGTH * 2 ]
|
||||||
@ -324,7 +336,6 @@ def dynamic(hash_secret):
|
|||||||
hash_client = None
|
hash_client = None
|
||||||
server = None
|
server = None
|
||||||
client = None
|
client = None
|
||||||
log.info(f"short_hash_server {short_hash_server} short_hash_client {short_hash_client} client_provided_secret {client_provided_secret}")
|
|
||||||
for _server in SERVERS:
|
for _server in SERVERS:
|
||||||
if _server.data["local_server_id"][:SECRET_LINK_LENGTH] == short_hash_server:
|
if _server.data["local_server_id"][:SECRET_LINK_LENGTH] == short_hash_server:
|
||||||
hash_server = _server.data["local_server_id"]
|
hash_server = _server.data["local_server_id"]
|
||||||
@ -336,41 +347,37 @@ def dynamic(hash_secret):
|
|||||||
client = CLIENTS[client_id]
|
client = CLIENTS[client_id]
|
||||||
|
|
||||||
if server and client:
|
if server and client:
|
||||||
|
|
||||||
client_shadowsocks_key = next(
|
client_shadowsocks_key = next(
|
||||||
(item for item in server.data["keys"] if item.key_id == client["name"]), None
|
(item for item in server.data["keys"] if item.key_id == client["name"]), None
|
||||||
)
|
)
|
||||||
|
|
||||||
salt = bcrypt.gensalt()
|
|
||||||
secret_string = hash_server + hash_client
|
secret_string = hash_server + hash_client
|
||||||
hash_password = bcrypt.hashpw(
|
|
||||||
password=secret_string.encode('utf-8'),
|
|
||||||
salt=salt
|
|
||||||
)
|
|
||||||
hash_password =
|
|
||||||
log.info(f"")
|
|
||||||
log.info(f"got srv {short_hash_server}, clt {short_hash_client}, correct secret {hash_password}")
|
|
||||||
check_secret_hash = bcrypt.checkpw(
|
check_secret_hash = bcrypt.checkpw(
|
||||||
password=secret_string.encode('utf-8'),
|
password=secret_string.encode('utf-8'),
|
||||||
hashed_password=client_provided_secret.encode('utf-8')
|
hashed_password=f"{SECRET_LINK_PREFIX}{client_provided_secret}".encode('utf-8')
|
||||||
)
|
)
|
||||||
if check_secret_hash:
|
if check_secret_hash:
|
||||||
|
log.info(f"Client {client['name']} has been requested ssconf for {server.data['name']}. Bcrypt client hash {client_provided_secret[0:16]}...[FULL HASH SECURED]")
|
||||||
return {
|
return {
|
||||||
"server": server.data["hostname_for_access_keys"],
|
"server": server.data["hostname_for_access_keys"],
|
||||||
"server_port": client_shadowsocks_key.port,
|
"server_port": client_shadowsocks_key.port,
|
||||||
"password": client_shadowsocks_key.password,
|
"password": client_shadowsocks_key.password,
|
||||||
"method": client_shadowsocks_key.method,
|
"method": client_shadowsocks_key.method,
|
||||||
|
"prefix": SS_PREFIX,
|
||||||
"info": "Managed by OutFleet [github.com/house-of-vanity/OutFleet/]",
|
"info": "Managed by OutFleet [github.com/house-of-vanity/OutFleet/]",
|
||||||
}
|
}
|
||||||
else:
|
else:
|
||||||
|
log.warning(f"Hack attempt! Client secret does not match: {client_provided_secret}")
|
||||||
return WRONG_DOOR
|
return WRONG_DOOR
|
||||||
else:
|
else:
|
||||||
|
log.warning(f"Hack attempt! Client or server doesn't exist. payload: {hash_secret[0:200]}")
|
||||||
return WRONG_DOOR
|
return WRONG_DOOR
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
log.info(f"EXP: {e}")
|
log.error(f"Dynamic V2 parse error: {e}")
|
||||||
return WRONG_DOOR
|
return WRONG_DOOR
|
||||||
|
|
||||||
|
|
||||||
@app.route("/dynamic/<server_name>/<client_id>", methods=["GET"], strict_slashes=False)
|
|
||||||
def dynamic_depticated(server_name, client_id):
|
def dynamic_depticated(server_name, client_id):
|
||||||
try:
|
try:
|
||||||
client = next(
|
client = next(
|
||||||
@ -385,13 +392,14 @@ def dynamic_depticated(server_name, client_id):
|
|||||||
if server and client and key:
|
if server and client and key:
|
||||||
if server.data["local_server_id"] in client["servers"]:
|
if server.data["local_server_id"] in client["servers"]:
|
||||||
log.info(
|
log.info(
|
||||||
"Client %s wants ssconf for %s", client["name"], server.data["name"]
|
"Client %s has been requested ssconf for %s", client["name"], server.data["name"]
|
||||||
)
|
)
|
||||||
return {
|
return {
|
||||||
"server": server.data["hostname_for_access_keys"],
|
"server": server.data["hostname_for_access_keys"],
|
||||||
"server_port": key.port,
|
"server_port": key.port,
|
||||||
"password": key.password,
|
"password": key.password,
|
||||||
"method": key.method,
|
"method": key.method,
|
||||||
|
"prefix":SS_PREFIX,
|
||||||
"info": "Managed by OutFleet [github.com/house-of-vanity/OutFleet/]",
|
"info": "Managed by OutFleet [github.com/house-of-vanity/OutFleet/]",
|
||||||
}
|
}
|
||||||
else:
|
else:
|
||||||
|
@ -126,10 +126,15 @@ Same keys will work simultaneously on many devices.
|
|||||||
<tbody>
|
<tbody>
|
||||||
{% for server in SERVERS %}
|
{% for server in SERVERS %}
|
||||||
{% if server.info()['local_server_id'] in client['servers'] %}
|
{% if server.info()['local_server_id'] in client['servers'] %}
|
||||||
|
{% set salt = bcrypt.gensalt() %}
|
||||||
|
{% set secret_string = server.info()['local_server_id'] + selected_client %}
|
||||||
|
{% set hash_secret = bcrypt.hashpw(
|
||||||
|
password=secret_string.encode('utf-8'),
|
||||||
|
salt=salt).decode('utf-8') %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{ server.info()['name'] }}</td>
|
<td>{{ server.info()['name'] }}</td>
|
||||||
<td>
|
<td>
|
||||||
<p style="font-size: 10pt">{% for key in server.data["keys"] %}{% if key.key_id == client['name'] %}ssconf://{{ dynamic_hostname }}/dynamic/{{server.info()['name']}}/{{selected_client}}#{{server.info()['comment']}}{% endif %}{% endfor %}</p>
|
<p style="font-size: 10pt">{% for key in server.data["keys"] %}{% if key.key_id == client['name'] %}ssconf://{{ dynamic_hostname }}/dynamic/{{server.info()['local_server_id'][0:SECRET_LINK_LENGTH]}}{{selected_client[0:SECRET_LINK_LENGTH]}}{{hash_secret[SECRET_LINK_PREFIX|length:]}}#{{server.info()['comment']}}{% endif %}{% endfor %}</p>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
@ -96,11 +96,11 @@
|
|||||||
<fieldset>
|
<fieldset>
|
||||||
<div class="pure-g">
|
<div class="pure-g">
|
||||||
<div class="pure-u-1 pure-u-md-1-3">
|
<div class="pure-u-1 pure-u-md-1-3">
|
||||||
<label for="name">Server Name</br> Note that this will not be reflected on the devices of the users that you invited to connect to it.</label>
|
<label for="name">Server Name</br>This will not be exposed to client</label>
|
||||||
<input type="text" id="name" class="pure-u-23-24" name="name" value="{{server.info()['name']}}"/>
|
<input type="text" id="name" class="pure-u-23-24" name="name" value="{{server.info()['name']}}"/>
|
||||||
</div>
|
</div>
|
||||||
<div class="pure-u-1 pure-u-md-1-3">
|
<div class="pure-u-1 pure-u-md-1-3">
|
||||||
<label for="comment">Comment</br>This value will be used as "Server name" in client app.</label>
|
<label for="comment">Comment</br>This will be exposed to client and will be used as "Server name" in client Outline app</label>
|
||||||
<input type="text" id="comment" class="pure-u-23-24" name="comment" value="{{server.info()['comment']}}"/>
|
<input type="text" id="comment" class="pure-u-23-24" name="comment" value="{{server.info()['comment']}}"/>
|
||||||
</div>
|
</div>
|
||||||
<div class="pure-u-1 pure-u-md-1-3">
|
<div class="pure-u-1 pure-u-md-1-3">
|
||||||
|
Reference in New Issue
Block a user