diff --git a/scripts/gateway-admin-proxy/deploy.py b/scripts/gateway-admin-proxy/deploy.py index f40d6d4..021130a 100644 --- a/scripts/gateway-admin-proxy/deploy.py +++ b/scripts/gateway-admin-proxy/deploy.py @@ -38,6 +38,16 @@ def remove_legacy_wildcards(text): return text +def update_legacy_fuc_certificate(text, directory): + for directive, filename in [('ssl_certificate', 'fullchain.pem'), ('ssl_certificate_key', 'privkey.pem')]: + old = '/etc/letsencrypt/live/tomshell.com.de/' + filename + pattern = r'(?m)^(\s*' + directive + r'\s+)' + re.escape(old) + r'(\s*;)' + text, count = re.subn(pattern, lambda m: m[1] + directory + '/' + filename + m[2], text) + if count != 1: + raise RuntimeError('Expected exactly one old fuc certificate directive; refusing ambiguous edit') + return text + + def main(): parser = argparse.ArgumentParser() parser.add_argument('role', choices=['gateway', 'white']) @@ -45,6 +55,7 @@ def main(): parser.add_argument('--skip-legacy', action='store_true') parser.add_argument('--legacy-certificate', default='/etc/white-proxy-letsencrypt/live/tomshell.com.de') parser.add_argument('--expected-legacy-sha256') + parser.add_argument('--expected-fuc-sha256') parser.add_argument('--white-ip', default='104.168.30.40') args = parser.parse_args() ipaddress.ip_address(args.white_ip) @@ -86,9 +97,19 @@ def main(): if not args.expected_legacy_sha256 or sha(current) != args.expected_legacy_sha256: parser.error('Legacy file needs an exact current hash before modifying wildcard blocks') changes[legacy_file] = remove_legacy_wildcards(current.decode()).encode() + fuc_file = pathlib.Path('/etc/nginx/conf.d/fu.tomshell.my.conf') + current = fuc_file.read_bytes() + if str(fuc_file) in old_state['files']: + if sha(current) != old_state['files'][str(fuc_file)]: + parser.error('Legacy fuc file changed after previous deployment') + changes[fuc_file] = current + else: + if not args.expected_fuc_sha256 or sha(current) != args.expected_fuc_sha256: + parser.error('Legacy fuc certificate update needs an exact current file hash') + changes[fuc_file] = update_legacy_fuc_certificate(current.decode(), args.legacy_certificate).encode() if args.stage == 'https': for path, data in changes.items(): - if path.name == 'master.tomshell.my.conf': + if path.name in ('master.tomshell.my.conf', 'fu.tomshell.my.conf'): continue for cert in re.findall(rb'(?m)^\s*ssl_certificate\s+([^;]+);', data): subprocess.run(['openssl', 'x509', '-in', cert.decode(), '-checkend', '86400', '-noout'], @@ -100,7 +121,7 @@ def main(): parser.error('Legacy proxy requires root and wildcard SANs, not a single-host certificate') for path, data in changes.items(): if path.exists() and str(path) not in old_state['files']: - if path.name != 'master.tomshell.my.conf': + if path.name not in ('master.tomshell.my.conf', 'fu.tomshell.my.conf'): raise RuntimeError('Unmanaged file already exists: ' + str(path)) elif path.exists() and sha(path.read_bytes()) != old_state['files'].get(str(path)): raise RuntimeError('Managed file changed outside this deployment: ' + str(path)) diff --git a/scripts/gateway-admin-proxy/render.py b/scripts/gateway-admin-proxy/render.py index cad5cbb..fb3e6c8 100644 --- a/scripts/gateway-admin-proxy/render.py +++ b/scripts/gateway-admin-proxy/render.py @@ -58,6 +58,28 @@ def http_server(names, redirect=True): ''' +def body_rewrites(source, alias, key): + # Rewrite URL hosts, preserving plain brand names such as DP-LINK.NET. + rules = f''' sub_filter_once off; + sub_filter_types text/css application/javascript application/json; + sub_filter 'https://$gateway_admin_origin_host' 'https://$host'; + sub_filter 'http://$gateway_admin_origin_host' 'https://$host'; + sub_filter '//$gateway_admin_origin_host' '//$host'; +''' + if key in ('new', 'old'): + rules += f''' sub_filter 'https://{source}' 'https://{alias}'; + sub_filter 'http://{source}' 'https://{alias}'; + sub_filter '//{source}' '//{alias}'; + sub_filter '.{source}/' '.{alias}/'; +''' + elif key == 'fuc-site': + rules += ''' sub_filter 'https://福云.com' 'https://fuc.tomtojapan.online'; + sub_filter 'http://福云.com' 'https://fuc.tomtojapan.online'; + sub_filter '//福云.com' '//fuc.tomtojapan.online'; +''' + return rules + + def https_server(names, certificate, content, cloudflare=False, white_ip=None, key_name='privatekey.pem'): trusted = f' include {CF_TRUST};\n' if cloudflare else '' if white_ip: @@ -155,17 +177,13 @@ map "$host|$http_referer" $gateway_admin_referer { extras = f''' proxy_redirect ~^https?://([a-z0-9-]+\\.)?{source_regex}(/.*)$ https://$1{alias}$2; proxy_cookie_domain {source} {alias}; ''' - elif key == 'fuc-site': - extras = " sub_filter '福云.com' 'fuc.tomtojapan.online';\n" location = f''' resolver 1.1.1.1 1.0.0.1 valid=300s ipv6=off; resolver_timeout 5s; if ($gateway_admin_origin_host = '') {{ return 444; }} location / {{ {proxy_lines('$gateway_admin_origin_host', '$gateway_admin_origin_host', '$gateway_admin_origin_host', alias)}{extras} proxy_set_header Accept-Encoding ""; - sub_filter_once off; - sub_filter_types text/css application/javascript application/json; - sub_filter '{source}' '{alias}'; +{body_rewrites(source, alias, key)} }} ''' data += https_server(names, cert, location, cloudflare=cf, diff --git a/scripts/gateway-admin-proxy/verify-maps.py b/scripts/gateway-admin-proxy/verify-maps.py index 0e102cd..4aaca4c 100644 --- a/scripts/gateway-admin-proxy/verify-maps.py +++ b/scripts/gateway-admin-proxy/verify-maps.py @@ -22,6 +22,23 @@ with tempfile.TemporaryDirectory(prefix='gateway-proxy-maps-') as folder: sock.bind(('127.0.0.1', 0)) port = sock.getsockname()[1] maps = render.render_white('https', '/unused')['00-gateway-admin-proxy-maps.conf'] + bodies = [ + ('dpl', 'dpl.tomtojapan.online', 'dp-link.net', 'dpl.tomtojapan.online', 'dpl-site', 'DP-LINK.NET'), + ('fuc', 'fuc.tomtojapan.online', 'xn--9kqy16h.com', 'fuc.tomtojapan.online', 'fuc-site', '福云.com'), + ('new', 'getway-dpl.tomshell.online', 'gemcook.net', 'tomshell.online', 'new', 'GEMCOOK.NET'), + ('old', 'nasa.tomshell.com.de', 'diaodu.us', 'tomshell.com.de', 'old', 'DIAODU.US'), + ] + locations = '' + for name, host, source, alias, key, brand in bodies: + upstream = ('getway-dpl.gemcook.net' if key == 'new' else + 'nasa.diaodu.us' if key == 'old' else source) + body = f'