From cba6442bdedf52704bc0daa15ebdf3ebafcd6489 Mon Sep 17 00:00:00 2001 From: xuans Date: Sat, 19 Sep 2026 09:58:33 +0800 Subject: [PATCH] =?UTF-8?q?=E5=88=87=E6=8D=A2=E5=85=AC=E7=BD=91=E5=9F=9F?= =?UTF-8?q?=E5=90=8D=E4=B8=BAcdis.cn=E5=B9=B6=E9=85=8D=E7=BD=AE=E7=94=9F?= =?UTF-8?q?=E4=BA=A7HTTPS=E5=8F=8D=E4=BB=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- deploy/check-access.py | 6 +-- deploy/nginx-cdis-http.conf | 11 +++++ deploy/nginx-cdis-https.conf | 25 ++++++++++++ deploy/switch-domain.py | 79 ++++++++++++++++++++++++++++++++++++ deploy/upgrade.py | 10 ++--- 5 files changed, 123 insertions(+), 8 deletions(-) create mode 100644 deploy/nginx-cdis-http.conf create mode 100644 deploy/nginx-cdis-https.conf create mode 100644 deploy/switch-domain.py diff --git a/deploy/check-access.py b/deploy/check-access.py index 178cdad..12312a5 100644 --- a/deploy/check-access.py +++ b/deploy/check-access.py @@ -10,9 +10,9 @@ try: commands = [ "systemctl is-active fdm-studio; ss -ltnp '( sport = :9999 )'", "grep -R -n -E 'listen|server_name' /etc/nginx/conf.d /etc/nginx/sites-enabled 2>/dev/null", - "curl -sS -I --resolve odacm.cn:443:127.0.0.1 https://odacm.cn; curl -sS -I http://odacm.cn", - "openssl x509 -in /etc/letsencrypt/live/odacm.cn/fullchain.pem -noout -issuer -subject -dates", - "tail -n 8 /var/log/nginx/fdm-access.log; tail -n 8 /var/log/nginx/fdm-error.log", + 'curl -sS -I --resolve cdis.cn:443:127.0.0.1 https://cdis.cn; curl -sS -I http://cdis.cn', + 'openssl x509 -in /etc/letsencrypt/live/cdis.cn/fullchain.pem -noout -issuer -subject -dates', + 'tail -n 8 /var/log/nginx/fdm-cdis-access.log; tail -n 8 /var/log/nginx/fdm-cdis-error.log', ] for command in commands: print(command, flush=True) diff --git a/deploy/nginx-cdis-http.conf b/deploy/nginx-cdis-http.conf new file mode 100644 index 0000000..1058b2a --- /dev/null +++ b/deploy/nginx-cdis-http.conf @@ -0,0 +1,11 @@ +server { + listen 80; + listen [::]:80; + server_name cdis.cn; + location ^~ /.well-known/acme-challenge/ { + root /var/lib/letsencrypt; + } + location / { + return 308 https://cdis.cn$request_uri; + } +} diff --git a/deploy/nginx-cdis-https.conf b/deploy/nginx-cdis-https.conf new file mode 100644 index 0000000..0eee43a --- /dev/null +++ b/deploy/nginx-cdis-https.conf @@ -0,0 +1,25 @@ +server { + listen 443 ssl; + listen [::]:443 ssl; + server_name cdis.cn; + ssl_certificate /etc/letsencrypt/live/cdis.cn/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/cdis.cn/privkey.pem; + ssl_protocols TLSv1.2 TLSv1.3; + access_log /var/log/nginx/fdm-cdis-access.log; + error_log /var/log/nginx/fdm-cdis-error.log warn; + + add_header X-Content-Type-Options nosniff always; + add_header Referrer-Policy strict-origin-when-cross-origin always; + + location / { + proxy_pass http://127.0.0.1:9999; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $remote_addr; + proxy_set_header X-Forwarded-Proto https; + proxy_set_header Connection ''; + proxy_connect_timeout 5s; + proxy_read_timeout 60s; + } +} diff --git a/deploy/switch-domain.py b/deploy/switch-domain.py new file mode 100644 index 0000000..6ac910c --- /dev/null +++ b/deploy/switch-domain.py @@ -0,0 +1,79 @@ +import getpass +import json +import time +from datetime import datetime, timezone +from pathlib import Path + +import paramiko +from deploy import dns_request + + +DOMAIN = 'cdis.cn' +HOST = '47.108.78.12' +ROOT = Path(__file__).resolve().parent + + +def main(): + password = getpass.getpass('SSH password: ') + key = getpass.getpass('Aliyun AccessKey ID: ') + secret = getpass.getpass('Aliyun AccessKey secret: ') + client = paramiko.SSHClient() + client.load_host_keys(str(Path.home() / '.ssh' / 'known_hosts')) + client.connect(HOST, username='root', password=password, timeout=20) + + def run(command, timeout=60): + print(command, flush=True) + _, out, err = client.exec_command(command, timeout=timeout) + text = out.read().decode() + err.read().decode() + print(text, flush=True) + if out.channel.recv_exit_status(): + raise RuntimeError('Remote command failed') + return text.strip() + + try: + result = dns_request('DescribeDomainRecords', key, secret, + DomainName=DOMAIN, PageSize='500') + records = [r for r in result.get('DomainRecords', {}).get('Record', []) + if r['RR'] == '@' and r['Type'] in ('A', 'AAAA', 'CNAME')] + print(json.dumps(records, ensure_ascii=True), flush=True) + if any(r['Type'] != 'A' or r['Status'] != 'ENABLE' for r in records): + raise RuntimeError('Unexpected root DNS records; no changes made') + stamp = datetime.now(timezone.utc).strftime('%Y%m%dT%H%M%SZ') + backup = '/var/backups/fdm/domain-' + stamp + run('nginx -t && systemctl is-active fdm-studio') + run(f'install -d -m 755 {backup} /var/lib/letsencrypt/.well-known/acme-challenge') + run(f'cp -a /etc/nginx/conf.d {backup}/') + with client.open_sftp() as sftp: + with sftp.open(backup + '/dns-before.json', 'w') as output: + output.write(json.dumps(records)) + sftp.put(str(ROOT / 'nginx-cdis-http.conf'), '/etc/nginx/conf.d/fdm-cdis.conf') + run('nginx -t && systemctl reload nginx') + for record in records: + if record['Value'] != HOST: + dns_request('UpdateDomainRecord', key, secret, RecordId=record['RecordId'], + RR='@', Type='A', Value=HOST, TTL='600') + if not records: + dns_request('AddDomainRecord', key, secret, DomainName=DOMAIN, + RR='@', Type='A', Value=HOST, TTL='600') + key = secret = None + for attempt in range(30): + answer = run(f'dig +short @223.5.5.5 {DOMAIN} A') + if answer.splitlines() == [HOST]: + break + time.sleep(20) + else: + raise RuntimeError('DNS propagation pending; rerun after propagation') + run(f'certbot certonly --webroot -w /var/lib/letsencrypt -d {DOMAIN} --cert-name {DOMAIN} --non-interactive --agree-tos --register-unsafely-without-email --server https://acme-v02.api.letsencrypt.org/directory', timeout=420) + with client.open_sftp() as sftp: + sftp.put(str(ROOT / 'nginx-cdis-https.conf'), '/etc/nginx/conf.d/fdm-cdis-ssl.conf') + run('nginx -t && systemctl reload nginx') + run(f'curl -fsS --resolve {DOMAIN}:443:127.0.0.1 https://{DOMAIN}/healthz') + run(f'openssl x509 -in /etc/letsencrypt/live/{DOMAIN}/fullchain.pem -noout -issuer -subject -dates') + run('systemctl is-enabled certbot.timer || true') + print('Domain switch complete', flush=True) + finally: + client.close() + + +if __name__ == '__main__': + main() diff --git a/deploy/upgrade.py b/deploy/upgrade.py index df19f81..f0ccd87 100644 --- a/deploy/upgrade.py +++ b/deploy/upgrade.py @@ -29,7 +29,7 @@ try: stamp = datetime.now(timezone.utc).strftime("%Y%m%dT%H%M%SZ") release = f"/opt/fdm/releases/{stamp}" backup = f"/var/backups/fdm/{stamp}" - run(f"install -d -m 755 {release} {backup} && cp -a /etc/nginx/conf.d/fdm-odacm.conf {backup}/") + run(f'install -d -m 755 {release} {backup} && cp -a /etc/nginx/conf.d/fdm-cdis.conf {backup}/') sftp = client.open_sftp() binary = root / "artifacts/fdm-studio" sftp.put(str(binary), release + "/fdm-studio") @@ -37,16 +37,16 @@ try: expected = hashlib.sha256(binary.read_bytes()).hexdigest() run(f"echo '{expected} {release}/fdm-studio' | sha256sum -c -") sftp.put(str(root / "deploy/fdm-studio.service"), "/etc/systemd/system/fdm-studio.service") - sftp.put(str(root / "deploy/nginx-program-http.conf"), release + "/nginx-http.conf") + sftp.put(str(root / 'deploy/nginx-cdis-http.conf'), release + '/nginx-http.conf') run("systemctl daemon-reload") run(f"systemd-run --unit=fdm-candidate-{stamp.lower()} --property=DynamicUser=yes {release}/fdm-studio -listen 127.0.0.1:19999") run("sleep 2 && curl -fsS http://127.0.0.1:19999/healthz") run(f"systemctl stop fdm-candidate-{stamp.lower()}") switched = True run(f"ln -sfn {release} /opt/fdm/current") - run(f"cp {release}/nginx-http.conf /etc/nginx/conf.d/fdm-odacm.conf && nginx -t && systemctl reload nginx") + run(f'cp {release}/nginx-http.conf /etc/nginx/conf.d/fdm-cdis.conf && nginx -t && systemctl reload nginx') run("sleep 3 && systemctl enable fdm-studio && systemctl restart fdm-studio && sleep 3 && systemctl is-active fdm-studio") - run("curl -fsS http://127.0.0.1:9999/healthz && curl -fsS --resolve odacm.cn:443:127.0.0.1 https://odacm.cn/healthz") + run('curl -fsS http://127.0.0.1:9999/healthz && curl -fsS --resolve cdis.cn:443:127.0.0.1 https://cdis.cn/healthz') run("systemctl is-enabled certbot.timer || true") sftp.close() print("部署完成:" + release, flush=True) @@ -56,7 +56,7 @@ except Exception: run("systemctl stop fdm-studio || true") if previous: run(f"ln -sfn {previous} /opt/fdm/current && systemctl start fdm-studio") - run(f"cp {backup}/fdm-odacm.conf /etc/nginx/conf.d/fdm-odacm.conf && nginx -t && systemctl reload nginx") + run(f'cp {backup}/fdm-cdis.conf /etc/nginx/conf.d/fdm-cdis.conf && nginx -t && systemctl reload nginx') raise finally: client.close()