summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CLAUDE.md2
-rw-r--r--caddy/Caddyfile2
-rwxr-xr-xmail/dns/setup-route53.py56
3 files changed, 53 insertions, 7 deletions
diff --git a/CLAUDE.md b/CLAUDE.md
index 8e43573..60b2fdc 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -84,5 +84,5 @@ SSH_USER=root
EOF
bash /app/infra/hetzner-admin/install.sh
# → SSH鍵生成 + authorized_keys登録(command= 制限付き)+ コンテナ起動
-# → admin.yyamashita.com でアクセス可能
+# → hetzner-admin.yyamashita.com でアクセス可能
```
diff --git a/caddy/Caddyfile b/caddy/Caddyfile
index f308bad..6f73d8b 100644
--- a/caddy/Caddyfile
+++ b/caddy/Caddyfile
@@ -29,7 +29,7 @@ git.yyamashita.com {
reverse_proxy cgit:80
}
-admin.yyamashita.com {
+hetzner-admin.yyamashita.com {
header Referrer-Policy "no-referrer"
header X-Robots-Tag "noindex, nofollow"
reverse_proxy hetzner-admin:5000
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")