summaryrefslogtreecommitdiff
path: root/mail
diff options
context:
space:
mode:
authoryyamashita <yyamashita@hetzner.yyamashita.com>2026-06-19 14:03:22 +0900
committeryyamashita <yyamashita@hetzner.yyamashita.com>2026-06-19 14:03:22 +0900
commit6af7cd1d8c32b138a77f9c835df4a3d4624cf69a (patch)
treecb8640f272780a2197cc0c4c03ed36bdb428489f /mail
parent50e887cb22ca375e66f7948033e05f596c9f43f5 (diff)
Rename hetzner-admin domain and use CNAME for web subdomains
- caddy/Caddyfile: admin.yyamashita.com → hetzner-admin.yyamashita.com - mail/dns/setup-route53.py: add A record for primary, CNAME records for all web service subdomains pointing to hetzner.yyamashita.com - CLAUDE.md: update access URL Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Diffstat (limited to 'mail')
-rwxr-xr-xmail/dns/setup-route53.py56
1 files changed, 51 insertions, 5 deletions
diff --git a/mail/dns/setup-route53.py b/mail/dns/setup-route53.py
index a54fc91..c508235 100755
--- a/mail/dns/setup-route53.py
+++ b/mail/dns/setup-route53.py
@@ -29,6 +29,15 @@ SELECTOR = "mail"
PRIMARY = "hetzner.yyamashita.com"
ZONE_DOMAIN = "yyamashita.com"
+# hetzner.yyamashita.com への CNAME を設定するサービスサブドメイン
+WEB_SUBDOMAINS = [
+ "golive",
+ "whoisband",
+ "code",
+ "git",
+ "hetzner-admin",
+]
+
def get_server_ip():
if ip := os.environ.get("SERVER_IP", ""):
@@ -70,6 +79,33 @@ def read_dkim_pubkey(path):
return "".join(parts)
+def a_record(fqdn, ip):
+ name = fqdn if fqdn.endswith(".") else fqdn + "."
+ return {
+ "Action": "UPSERT",
+ "ResourceRecordSet": {
+ "Name": name,
+ "Type": "A",
+ "TTL": 300,
+ "ResourceRecords": [{"Value": ip}],
+ },
+ }
+
+
+def cname_record(fqdn, target):
+ name = fqdn if fqdn.endswith(".") else fqdn + "."
+ target = target if target.endswith(".") else target + "."
+ return {
+ "Action": "UPSERT",
+ "ResourceRecordSet": {
+ "Name": name,
+ "Type": "CNAME",
+ "TTL": 300,
+ "ResourceRecords": [{"Value": target}],
+ },
+ }
+
+
def txt_record(fqdn, raw_value):
chunks = [raw_value[i:i + 255] for i in range(0, len(raw_value), 255)]
txt_value = " ".join(f'"{c}"' for c in chunks)
@@ -117,12 +153,19 @@ def main():
changes = []
- # プライマリドメイン (IP 直指定 SPF)
+ # プライマリドメイン: A レコード
+ changes.append(a_record(PRIMARY, server_ip))
+
+ # Web サービス: CNAME → hetzner.yyamashita.com
+ for sub in WEB_SUBDOMAINS:
+ changes.append(cname_record(f"{sub}.{ZONE_DOMAIN}", PRIMARY))
+
+ # プライマリドメイン: メール認証
changes.append(txt_record(PRIMARY, f"v=spf1 ip4:{server_ip} -all"))
changes.append(txt_record(f"{SELECTOR}._domainkey.{PRIMARY}", dkim_value))
changes.append(txt_record(f"_dmarc.{PRIMARY}", "v=DMARC1; p=none"))
- # サービスドメイン (include SPF)
+ # サービスドメイン: メール認証 (include SPF)
for domain in service_domains:
changes.append(txt_record(domain, f"v=spf1 include:{PRIMARY} -all"))
changes.append(txt_record(f"{SELECTOR}._domainkey.{domain}", dkim_value))
@@ -138,7 +181,7 @@ def main():
client.change_resource_record_sets(
HostedZoneId=zone_id,
ChangeBatch={
- "Comment": "Mail auth records (SPF/DKIM/DMARC) via hetzner-infra",
+ "Comment": "DNS records via hetzner-infra",
"Changes": [c],
},
)
@@ -146,8 +189,8 @@ def main():
ok += 1
except client.exceptions.InvalidChangeBatch as e:
msg = str(e)
- if "CNAME" in msg or "conflicting" in msg:
- print(f" SKIP {rtype} {name} (既存の CNAME と競合)")
+ if "already exists" in msg or "conflicting" in msg:
+ print(f" SKIP {rtype} {name} (競合または既存レコード)")
skipped += 1
else:
raise
@@ -156,6 +199,9 @@ def main():
print(f"完了: {ok} 件成功 / {skipped} 件スキップ")
print()
print("確認コマンド (数分後):")
+ print(f" dig A {PRIMARY} +short")
+ for sub in WEB_SUBDOMAINS:
+ print(f" dig CNAME {sub}.{ZONE_DOMAIN} +short")
for domain in [PRIMARY] + service_domains:
print(f" dig TXT {SELECTOR}._domainkey.{domain} +short")