Wire Hysteria 2 UI with per-server SSL defaults and renew.

Expose domain/email on servers, warn that TCP 80/443 must be free on install, and allow Let's Encrypt re-issue from Hysteria settings.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
orohi
2026-07-26 09:46:20 +03:00
co-authored by Cursor
parent ed5c54cf96
commit 183071f588
17 changed files with 653 additions and 33 deletions
+4
View File
@@ -62,6 +62,10 @@ class BackupManager:
remote_dir = inst_path('/opt/amnezia/telemt')
paths['host'] = [remote_dir]
paths['container'] = [remote_dir]
elif base == 'hysteria':
remote_dir = inst_path('/opt/amnezia/hysteria')
paths['host'] = [remote_dir]
paths['container'] = ['/etc/hysteria']
elif base == 'dns':
paths['host'] = ['/opt/amnezia/dns']
paths['container'] = ['/opt/amnezia/dns']
+39 -8
View File
@@ -1,5 +1,5 @@
"""
Hysteria 2 protocol manager — teddysun/hysteria Docker image.
"""
Hysteria 2 protocol manager — official tobyxdd/hysteria image (apernet/hysteria).
Installs Hysteria with Let's Encrypt TLS for an admin-provided domain
(certbot standalone on port 80), host networking, BBR, and salamander obfs.
@@ -32,7 +32,7 @@ def _rand_token(length=16):
class HysteriaManager:
PROTOCOL = 'hysteria'
CONTAINER_NAME = 'amnezia-hysteria'
IMAGE_NAME = 'teddysun/hysteria:latest'
IMAGE_NAME = 'tobyxdd/hysteria:v2'
CERTBOT_IMAGE = 'certbot/certbot:latest'
BASE_DIR = '/opt/amnezia/hysteria'
DEFAULT_PORT = 8998
@@ -449,7 +449,8 @@ iptables -C INPUT -p udp --dport {port} -j ACCEPT 2>/dev/null || iptables -I INP
f"--network host "
f"--cap-add=NET_ADMIN "
f"-v {_q(self.base_dir)}:/etc/hysteria "
f"{self.IMAGE_NAME}"
f"{self.IMAGE_NAME} "
f"-c /etc/hysteria/server.yaml server"
)
_, err, code = self.ssh.run_sudo_command(run_cmd, timeout=60)
if code != 0:
@@ -475,8 +476,8 @@ iptables -C INPUT -p udp --dport {port} -j ACCEPT 2>/dev/null || iptables -I INP
'protocol': self.protocol,
}
def update_settings(self, port=None):
"""Change listen UDP port and recreate the container."""
def update_settings(self, port=None, domain=None, email=None, renew_ssl=False):
"""Change listen UDP port and/or re-issue Let's Encrypt for domain."""
meta = self._ensure_secrets(self._read_metadata())
if port is None:
port = meta.get('port') or self.DEFAULT_PORT
@@ -486,6 +487,32 @@ iptables -C INPUT -p udp --dport {port} -j ACCEPT 2>/dev/null || iptables -I INP
if port == 80:
return {'status': 'error', 'message': 'Port 80 is reserved for Let\'s Encrypt validation'}
old_domain = (meta.get('domain') or '').strip().lower()
domain_changed = False
try:
if domain is not None and str(domain).strip():
new_domain = self._validate_domain(domain)
domain_changed = new_domain != old_domain
meta['domain'] = new_domain
if email is not None and str(email).strip():
meta['email'] = self._validate_email(email)
except ValueError as e:
return {'status': 'error', 'message': str(e)}
if renew_ssl or domain_changed:
# Domain change or explicit renew → re-issue certificate (needs free TCP 80)
try:
d = meta.get('domain') or ''
e = meta.get('email') or ''
if not d or not e:
return {
'status': 'error',
'message': 'Domain and email are required to issue SSL',
}
self._issue_certificate(d, e)
except Exception as exc:
return {'status': 'error', 'message': str(exc)}
meta['port'] = port
self._write_metadata(meta)
self._write_config_from_clients(port)
@@ -497,13 +524,17 @@ iptables -C INPUT -p udp --dport {port} -j ACCEPT 2>/dev/null || iptables -I INP
'message': str(e),
'port': str(port),
'domain': meta.get('domain'),
'email': meta.get('email'),
}
msg = 'Hysteria settings updated'
if renew_ssl or domain_changed:
msg = 'SSL certificate issued and Hysteria settings updated'
return {
'status': 'success',
'port': str(port),
'domain': meta.get('domain'),
'email': meta.get('email'),
'message': f'Hysteria listening on UDP {port}',
'message': msg,
}
def _reload_container(self):
@@ -539,7 +570,7 @@ iptables -C INPUT -p udp --dport {port} -j ACCEPT 2>/dev/null || iptables -I INP
f"Point A-record to this server and free TCP port 80. {err or out}"
)
# Copy live certs into paths expected by teddysun image
# Copy live certs into paths expected by the server config
copy_script = f"""
set -e
LIVE={_q(self.letsencrypt_dir)}/live/{_q(domain)}