From 5b4aee69b4c6bf295bc44aad6171e17a0d80e63c Mon Sep 17 00:00:00 2001 From: yyamashita Date: Sat, 11 Jul 2026 20:35:09 +0900 Subject: Dedupe Caddyfile with snippets, sync docs with reality, add tests/check.sh - Caddyfile: extract repeated header blocks into (noindex)/(crawler_bypass) snippets (adapted JSON verified byte-identical to previous config) - CLAUDE.md: add mosquitone-admin/microblog to repos table; document that proxy auth is disabled (PROXY_NO_AUTH=1) with JP-only IP filter instead - Deploy.md/caddy/README.md: reflect all-service deploy and forward proxy - tests/check.sh: shell/python syntax, repos.txt<->hooks<->sessions.txt consistency, app.py COMMANDS vs run-command.sh whitelist, secrets check Co-Authored-By: Claude Opus 4.7 --- .gitignore | 1 + CLAUDE.md | 17 +++++--- Deploy.md | 4 +- caddy/Caddyfile | 32 ++++++++-------- caddy/README.md | 13 +++++-- tests/check.sh | 117 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 156 insertions(+), 28 deletions(-) create mode 100755 tests/check.sh diff --git a/.gitignore b/.gitignore index fd755d9..1c6531b 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ .DS_Store hetzner-admin/id_rsa hetzner-admin/id_rsa.pub +__pycache__/ diff --git a/CLAUDE.md b/CLAUDE.md index 3a5188f..1ab0691 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -33,10 +33,15 @@ server/ サーバー共通設定(authorized_keys, requirements.md) | hetzner-infra | /app/infra | | tokyo-livehouse-events | /app/tokyo-livehouse-events | | whois-band | /app/whois-band | +| mosquitone-admin | /home/yyamashita/workspaces/repos/mosquitone-admin | +| microblog | /app/microblog | ## よく使うコマンド ```bash +# 整合性チェック(push 前に実行推奨) +bash tests/check.sh + # Caddy 設定変更 vi caddy/Caddyfile && git push origin master @@ -112,12 +117,11 @@ docker compose -f /app/infra/vpn/docker-compose.yml restart # Proxy 初回セットアップ(Caddy に統合済み。追加作業なし) # → push するだけで Caddy が proxy.yyamashita.com の証明書を自動取得 # → proxy.yyamashita.com:80 (HTTP) / :443 (HTTPS) でフォワードプロキシとして動作 -# → 認証は VPN と同じ users.conf を共有 +# → 認証は無効(PROXY_NO_AUTH=1)。代わりに日本国内 IP のみ許可(ipdeny.com の JP CIDR リスト) -# Proxy ユーザー追加・変更(VPN と同じ users.conf を編集) -ssh root@ -vi /app/infra/vpn/users.conf -docker compose -f /app/infra/caddy/docker-compose.yml restart # Caddy 再起動で反映 +# Proxy 認証を有効に戻す場合 +vi caddy/docker-compose.yml # PROXY_NO_AUTH=1 を削除(VPN と同じ users.conf の Basic 認証に戻る) +git push origin master # DNS 登録(初回のみ。setup-route53.py に proxy が含まれている) ssh root@ @@ -147,4 +151,5 @@ python3 /app/infra/mail/dns/setup-route53.py - HTTP プロキシ: `proxy.yyamashita.com:80` - HTTPS プロキシ: `proxy.yyamashita.com:443` -- 認証: Basic 認証(VPN と同じユーザー名/パスワード) +- 認証: なし(`PROXY_NO_AUTH=1`)。日本国内 IP からのみ接続可能 + (有効化すると VPN と同じ `users.conf` の Basic 認証になる) diff --git a/Deploy.md b/Deploy.md index dfa517b..9797459 100644 --- a/Deploy.md +++ b/Deploy.md @@ -17,9 +17,11 @@ ssh root@ 'bash /app/infra/git/install.sh' ## 通常デプロイ ```bash -git push origin master # Caddy 設定・cgit コンテナ更新(自動再ビルド含む) +git push origin master # caddy / cgit / mail / hetzner-admin / vpn を再デプロイ(自動再ビルド含む) ``` +push 前に `bash tests/check.sh` で整合性チェックを実行できる。 + ## フック / リポジトリ定義を変更した場合 ```bash diff --git a/caddy/Caddyfile b/caddy/Caddyfile index d5624c5..90abecb 100644 --- a/caddy/Caddyfile +++ b/caddy/Caddyfile @@ -2,46 +2,45 @@ order forward_proxy before redir } -golive.yyamashita.com { +(noindex) { header Referrer-Policy "no-referrer" header X-Robots-Tag "noindex, nofollow" +} +(crawler_bypass) { @crawler_bypass header X-Crawler-Access "f9a3c1e847b2d056" header @crawler_bypass -X-Robots-Tag +} +golive.yyamashita.com { + import noindex + import crawler_bypass reverse_proxy tokyo-app:3000 } whoisband.yyamashita.com { - header Referrer-Policy "no-referrer" - header X-Robots-Tag "noindex, nofollow" - - @crawler_bypass header X-Crawler-Access "f9a3c1e847b2d056" - header @crawler_bypass -X-Robots-Tag - + import noindex + import crawler_bypass reverse_proxy whois-app:3000 } + code.yyamashita.com { - header Referrer-Policy "no-referrer" - header X-Robots-Tag "noindex, nofollow" + import noindex reverse_proxy 172.19.0.1:8080 } git.yyamashita.com { - header Referrer-Policy "no-referrer" - header X-Robots-Tag "noindex, nofollow" + import noindex reverse_proxy cgit:80 } hetzner-admin.yyamashita.com { - header Referrer-Policy "no-referrer" - header X-Robots-Tag "noindex, nofollow" + import noindex reverse_proxy hetzner-admin:5000 } blog.yyamashita.com { - header Referrer-Policy "no-referrer" - header X-Robots-Tag "noindex, nofollow" + import noindex reverse_proxy microblog-app:3000 } @@ -51,8 +50,7 @@ vpn.yyamashita.com { tls { key_type rsa4096 } - header Referrer-Policy "no-referrer" - header X-Robots-Tag "noindex, nofollow" + import noindex route /ca.pem { header Content-Type "application/x-pem-file" header Content-Disposition "attachment; filename=\"vpn-ca.pem\"" diff --git a/caddy/README.md b/caddy/README.md index 2faf232..f3222a4 100644 --- a/caddy/README.md +++ b/caddy/README.md @@ -1,11 +1,16 @@ # caddy -Caddy によるリバースプロキシ設定。`git push` で自動デプロイされる。 +Caddy によるリバースプロキシ + フォワードプロキシ設定。`git push` で自動デプロイされる。 | ファイル | 内容 | |---|---| -| `Caddyfile` | ホスト別ルーティング定義 | -| `docker-compose.yml` | Caddy コンテナ定義 | +| `Caddyfile` | ホスト別ルーティング定義(共通ヘッダーはスニペット `(noindex)` / `(crawler_bypass)`) | +| `Dockerfile` | forwardproxy プラグイン入りの Caddy を xcaddy でビルド | +| `entrypoint.sh` | 起動時に `/tmp/proxy_forward.caddy` を生成(Basic 認証 + 日本 IP 限定フィルタ) | +| `docker-compose.yml` | Caddy コンテナ定義(`PROXY_NO_AUTH=1` でプロキシ認証を無効化中) | | `deploy.sh` | post-receive フックから呼ばれるデプロイスクリプト | -新しいホストを追加する場合は `Caddyfile` を編集して push する。 +- 新しいホストを追加する場合は `Caddyfile` を編集して push する。 +- フォワードプロキシは `proxy.yyamashita.com:80/443`。認証は無効(`PROXY_NO_AUTH=1`)で、 + 代わりに ipdeny.com の JP CIDR リストで日本国内 IP のみ許可している。 +- `entrypoint.sh` の変更はイメージ再ビルドが必要(`deploy.sh` の `--build` で push だけで反映される)。 diff --git a/tests/check.sh b/tests/check.sh new file mode 100755 index 0000000..99add68 --- /dev/null +++ b/tests/check.sh @@ -0,0 +1,117 @@ +#!/usr/bin/env bash +# リポジトリの整合性チェック。push 前に実行する: bash tests/check.sh +set -uo pipefail +cd "$(dirname "$0")/.." + +FAIL=0 +ok() { echo " ok: $*"; } +ng() { echo " NG: $*"; FAIL=1; } + +echo "=== シェルスクリプト構文 ===" +while IFS= read -r f; do + if bash -n "$f" 2>/dev/null; then + ok "$f" + else + ng "$f" + bash -n "$f" || true + fi +done < <(find . -name '*.sh' -not -path './.git/*' | sort) +while IFS= read -r f; do + if bash -n "$f" 2>/dev/null; then + ok "$f" + else + ng "$f" + bash -n "$f" || true + fi +done < <(find git/hooks -type f -name 'post-receive' | sort) + +echo "" +echo "=== Python 構文 ===" +for f in hetzner-admin/app.py mail/dns/setup-route53.py; do + if python3 -m py_compile "$f" 2>/dev/null; then + ok "$f" + else + ng "$f" + python3 -m py_compile "$f" || true + fi +done + +echo "" +echo "=== repos.txt と git/hooks/ の整合性 ===" +mapfile -t repos < <(grep -vE '^\s*(#|$)' git/repos.txt | cut -d: -f1) +for repo in "${repos[@]}"; do + if [[ -f "git/hooks/$repo/post-receive" ]]; then + ok "repos.txt: $repo → hooks あり" + else + ng "repos.txt: $repo のフックがない (git/hooks/$repo/post-receive)" + fi +done +for dir in git/hooks/*/; do + name="$(basename "$dir")" + if printf '%s\n' "${repos[@]}" | grep -qx "$name"; then + ok "hooks: $name → repos.txt に定義あり" + else + ng "hooks: $name が repos.txt に定義されていない" + fi +done + +echo "" +echo "=== sessions.txt のリポジトリが repos.txt に存在するか ===" +while IFS=: read -r user repo; do + [[ "$user" =~ ^#.*$ || -z "$user" ]] && continue + if printf '%s\n' "${repos[@]}" | grep -qx "$repo"; then + ok "sessions.txt: $user:$repo" + else + ng "sessions.txt: $repo が repos.txt に定義されていない" + fi +done < claude/sessions.txt + +echo "" +echo "=== hetzner-admin: app.py の COMMANDS が run-command.sh で許可されているか ===" +while IFS= read -r cmd; do + if grep -qF "\"$cmd\")" hetzner-admin/run-command.sh; then + ok "$cmd" + else + ng "run-command.sh のホワイトリストにない: $cmd" + fi +done < <(python3 - <<'EOF' +import ast +tree = ast.parse(open("hetzner-admin/app.py").read()) +for node in ast.walk(tree): + if isinstance(node, ast.Assign) and getattr(node.targets[0], "id", "") == "COMMANDS": + for elt in node.value.elts: + print(elt.elts[1].value) +EOF +) + +echo "" +echo "=== 秘密ファイルがコミットされていないか ===" +for name in users.conf id_rsa id_rsa.pub .env; do + regex="(^|/)$(printf '%s' "$name" | sed 's/\./\\./g')\$" + if git ls-files | grep -qE "$regex"; then + ng "コミットされている: $(git ls-files | grep -E "$regex")" + else + ok "$name は未コミット" + fi +done + +echo "" +echo "=== Caddyfile 検証(docker が使える場合のみ) ===" +if docker info >/dev/null 2>&1 && docker image inspect caddy-caddy:latest >/dev/null 2>&1; then + if docker run --rm -v "$PWD/caddy:/etc/caddy:ro" caddy-caddy:latest \ + caddy validate --config /etc/caddy/Caddyfile --adapter caddyfile >/dev/null 2>&1; then + ok "Caddyfile valid" + else + ng "Caddyfile が無効" + fi +else + echo " skip: docker にアクセスできない(root で実行すると検証される)" +fi + +echo "" +if [[ "$FAIL" -eq 0 ]]; then + echo "All checks passed." +else + echo "FAILED: 上記の NG を修正してください。" + exit 1 +fi -- cgit v1.2.3