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()