fdm/deploy/switch-domain.py

80 lines
3.5 KiB
Python

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