new connection
This commit is contained in:
parent
9f37fd8b78
commit
70004e0645
6
1.py
Normal file
6
1.py
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import keyring
|
||||||
|
|
||||||
|
# keyring.get_password(service_name, username)
|
||||||
|
value = keyring.get_password("openvpn-totp-offad", "static")
|
||||||
|
print(value) # None, если ничего не сохранено
|
||||||
|
|
||||||
@ -21,3 +21,7 @@
|
|||||||
|
|
||||||
Правятся вручную или через пункт меню «Открыть настройки»; перечитываются на лету.
|
Правятся вручную или через пункт меню «Открыть настройки»; перечитываются на лету.
|
||||||
Секреты (`secret`, `static`) в конфиге не хранятся — они в keyring под именем `keyring_service`.
|
Секреты (`secret`, `static`) в конфиге не хранятся — они в keyring под именем `keyring_service`.
|
||||||
|
|
||||||
|
## Сервис для декодирования
|
||||||
|
|
||||||
|
https://2fafast.com/tools/qr-decoder/
|
||||||
|
|||||||
@ -25,14 +25,15 @@ def main():
|
|||||||
static = os.environ.get("OVPN_STATIC_PASS")
|
static = os.environ.get("OVPN_STATIC_PASS")
|
||||||
|
|
||||||
missing = [name for name, val in
|
missing = [name for name, val in
|
||||||
(("OVPN_TOTP_SECRET", secret), ("OVPN_STATIC_PASS", static))
|
(("OVPN_TOTP_SECRET", secret),)
|
||||||
if not val]
|
if not val]
|
||||||
if missing:
|
if missing:
|
||||||
sys.exit(f"Не заданы переменные окружения: {', '.join(missing)}")
|
sys.exit(f"Не заданы переменные окружения: {', '.join(missing)}")
|
||||||
|
|
||||||
service = keyring_service()
|
service = keyring_service()
|
||||||
keyring.set_password(service, "secret", secret)
|
keyring.set_password(service, "secret", secret)
|
||||||
keyring.set_password(service, "static", static)
|
if static:
|
||||||
|
keyring.set_password(service, "static", static)
|
||||||
print(f"Сохранено в keyring (сервис: {service}).")
|
print(f"Сохранено в keyring (сервис: {service}).")
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
"""launch_vpn.py — auth = static+TOTP, конкатенация без разделителя.
|
"""launch_vpn.py — auth = static+TOTP, конкатенация без разделителя.
|
||||||
Параметры берутся из общего vpn_config.json (см. launch_vpn_gui.py);
|
Параметры берутся из общего vpn_config.json (см. launch_vpn_gui.py);
|
||||||
секреты — из keyring."""
|
секреты — из keyring."""
|
||||||
import subprocess, pyotp, keyring, tempfile, json, os
|
import subprocess, pyotp, keyring, tempfile, json, os, base64
|
||||||
|
|
||||||
CONFIG_PATH = os.path.join(
|
CONFIG_PATH = os.path.join(
|
||||||
os.path.dirname(os.path.abspath(__file__)), "vpn_config.json")
|
os.path.dirname(os.path.abspath(__file__)), "vpn_config.json")
|
||||||
@ -28,12 +28,18 @@ def main():
|
|||||||
cfg = load_config()
|
cfg = load_config()
|
||||||
service = cfg.get("keyring_service", "openvpn-totp")
|
service = cfg.get("keyring_service", "openvpn-totp")
|
||||||
secret = keyring.get_password(service, "secret")
|
secret = keyring.get_password(service, "secret")
|
||||||
static = keyring.get_password(service, "static")
|
# static — необязателен: если его нет в keyring, пароль = только TOTP-код.
|
||||||
|
static = keyring.get_password(service, "static") or ""
|
||||||
code = pyotp.TOTP(secret).now()
|
code = pyotp.TOTP(secret).now()
|
||||||
|
if cfg.get("static_challenge"):
|
||||||
|
password = (f"SCRV1:{base64.b64encode(static.encode()).decode()}"
|
||||||
|
f":{base64.b64encode(code.encode()).decode()}")
|
||||||
|
else:
|
||||||
|
password = f"{static}{code}"
|
||||||
fd, path = tempfile.mkstemp(suffix=".txt")
|
fd, path = tempfile.mkstemp(suffix=".txt")
|
||||||
try:
|
try:
|
||||||
with os.fdopen(fd, "w", newline="\n") as f:
|
with os.fdopen(fd, "w", newline="\n") as f:
|
||||||
f.write(f"{cfg['username']}\n{static}{code}\n")
|
f.write(f"{cfg['username']}\n{password}\n")
|
||||||
subprocess.run([cfg["openvpn_bin"], "--config", cfg["ovpn_config"],
|
subprocess.run([cfg["openvpn_bin"], "--config", cfg["ovpn_config"],
|
||||||
"--auth-user-pass", path])
|
"--auth-user-pass", path])
|
||||||
finally:
|
finally:
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import threading
|
|||||||
import tempfile
|
import tempfile
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
|
import base64
|
||||||
import pyotp
|
import pyotp
|
||||||
import keyring
|
import keyring
|
||||||
import pystray
|
import pystray
|
||||||
@ -32,6 +33,12 @@ DEFAULT_CONFIG = {
|
|||||||
"dev_node": "OpenVPN Data Channel Offload",
|
"dev_node": "OpenVPN Data Channel Offload",
|
||||||
# имя сервиса в keyring, откуда берутся secret (TOTP) и static (пароль).
|
# имя сервиса в keyring, откуда берутся secret (TOTP) и static (пароль).
|
||||||
"keyring_service": "openvpn-totp",
|
"keyring_service": "openvpn-totp",
|
||||||
|
# формат передачи OTP серверу.
|
||||||
|
# False -> пароль = static+code одной строкой (сервер сам отрезает OTP).
|
||||||
|
# True -> формат SCRV1 (static и OTP кодируются раздельно в base64); нужен
|
||||||
|
# для серверов со static-challenge. Саму строку static-challenge при
|
||||||
|
# этом надо УБРАТЬ из .ovpn, иначе openvpn спросит код интерактивно.
|
||||||
|
"static_challenge": False,
|
||||||
# путь к лог-файлу openvpn.
|
# путь к лог-файлу openvpn.
|
||||||
"log_path": "ovpn.log",
|
"log_path": "ovpn.log",
|
||||||
|
|
||||||
@ -153,14 +160,24 @@ def is_running() -> bool:
|
|||||||
return proc is not None and proc.poll() is None
|
return proc is not None and proc.poll() is None
|
||||||
|
|
||||||
|
|
||||||
|
def auth_password(static: str, code: str) -> str:
|
||||||
|
"""Собрать поле пароля для auth-user-pass с учётом режима static_challenge."""
|
||||||
|
if config.get("static_challenge"):
|
||||||
|
s = base64.b64encode(static.encode()).decode()
|
||||||
|
r = base64.b64encode(code.encode()).decode()
|
||||||
|
return f"SCRV1:{s}:{r}"
|
||||||
|
return f"{static}{code}"
|
||||||
|
|
||||||
|
|
||||||
def build_auth_file() -> str:
|
def build_auth_file() -> str:
|
||||||
service = config.get("keyring_service", "openvpn-totp")
|
service = config.get("keyring_service", "openvpn-totp")
|
||||||
secret = keyring.get_password(service, "secret")
|
secret = keyring.get_password(service, "secret")
|
||||||
static = keyring.get_password(service, "static")
|
# static — необязателен: если его нет в keyring, пароль = только TOTP-код.
|
||||||
|
static = keyring.get_password(service, "static") or ""
|
||||||
code = pyotp.TOTP(secret).now()
|
code = pyotp.TOTP(secret).now()
|
||||||
fd, path = tempfile.mkstemp(suffix=".txt")
|
fd, path = tempfile.mkstemp(suffix=".txt")
|
||||||
with os.fdopen(fd, "w", newline="\n") as f:
|
with os.fdopen(fd, "w", newline="\n") as f:
|
||||||
f.write(f"{config['username']}\n{static}{code}\n")
|
f.write(f"{config['username']}\n{auth_password(static, code)}\n")
|
||||||
return path
|
return path
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user