80 lines
3.5 KiB
Python
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()
|