内部サービス向けの TLS 証明書:正しく設定する方法

2026/07/09 23:57

内部サービス向けの TLS 証明書:正しく設定する方法

RSS: https://news.ycombinator.com/rss

要約

Japanese Translation:

本テキストは、予約済みトップレベルドメイン(TLD)や自己署名証明書を用いた内部 HTTP サービスの代替として、「スプリット・ホライズン DNS」を採用する安全なアプローチを提唱する。このアーキテクチャでは、クライアントが外部からアクセスするか、NetBird のカスタムゾーンによって管理される VPN を経由して接続かに応じて、単一の公開ドメインを異なる IP アドレスに解決させる。これにより、自己署名証明書ではなく、Let's Encrypt などの信頼された公開 CA(認証局)の使用が可能になる。重要なセキュリティ構成要素として、内部システムに至る前に全てのパブリックインターネットトラフィックを厳格にブロックする Web アプリケーションファイアウォール(WAF)が設定されている。本構成では WAF を nginx にて実装し、VPN インターフェースにバインドしている。信頼された CA の導入と内部サービスへの露出の回避、およびメインサーバーでのポート 80 コンフィグの不要を実現するため、

acme.sh
を使用し、
--standalone
フラグで
http-01
チャレンジャーを介して証明書を発行する方式が採用されている。自動化は、証明書を更新するための日次 cron ジョブと、新しい証明書を nginx コンフィグに同期する bash スクリプト(root 権限不要での動作を実現するために
setcap
を活用)によって実現されている。この構成により、Subject Alternative Names (SANs) を用いて複数のサブドメインを単一の証明書下で保護することが可能になる。

本文

スプリットホライゾン DNS と WAF を用いた、内部サービス向けの安全な TLS 設定手法

クリックベイト臭い内容かもしれませんが、重要な技術的なテーマです。以下に「なぜこれが正解なのか」を説明し、実装手順を解説します。

コンテキスト:HTTP サービスのホスト例

外部サービスと内部サービスを両方ホストするサーバーの例を考えます。

  • 内部サービス: VPN 接続が必要(例:Grafana)。
  • アクセス方法:
    1. ICANN で予約されたプライベート TLD を使う(例:
      .internal
    2. 所有するパブリックドメインを使う(例:
      tuxnet.dev

ここでは、内部 IP アドレス

10.0.1.10
でアクセス可能な Grafana を使用して解説します。

検討すべき選択肢と課題

① プライベート TLD(
.internal
)を使用する方法

  • 実装: DNS レコードを作成し、ドメインを内部 IP に解決する。
  • メリット: チュートリアルやドキュメントが非常に多い。
  • デメリット:
    • HTTP クライアントに自己署名証明書を検証させなければならない
    • 利用者に TLS エラーを無視させる提案が必要になる(非推奨)。

② パブリックドメイン(
tuxnet.dev
)とスプリットホライゾン DNS を使用する方法

  • 実装:
    • パブリック DNS リゾルバからはパブリック IP に解決。
    • VPN 接続クライアントからは内部 IP に解決。
  • メリット:
    • **パブリック CA(Let's Encrypt や ZeroSSL)**を使用できるため、信頼性の高い証明書を発行可能。
  • デメリット:
    • **WAF(Web アプリケーションファイアウォール)**の導入が必要。

結論: 全クライアントに自己署名証明書を読み込ませる手法よりも、WAF を集中管理する手法の方が実装が容易で安全です。

構成要素と要件

以下のような構成を実装します:

  • VPN リゾルバ: NetBird(スプリットホライゾン DNS の管理を担当)
  • ACME クライアント: acme.sh(証明書の発行・自動更新)
  • リバースプロキシ/WAF: Nginx

注:ここでは NetBird のカスタムゾーン機能を活用し、サーバー側がパブリックリゾルバを使用するように制御します。サーバー自体は例外として内部 IP に解決されます。

ACME.sh で証明書を発行する

http-01 チャALLENGE を採用して、以下のコマンドで証明書を取得します:

acme.sh --issue -d grafana.tuxnet.dev --server letsencrypt --standalone
  • スタンドアロンのモード (
    --standalone
    )
    :
    • Nginx がポート 80 に常時リスニングする必要はありません。
    • acme.sh だけがポート 80 を一時的に活用して課題を完了します。

Nginx の設定

以下の設定ファイルを使用します(※

our-server.netbird.cloud
は VPN ネットワークインターフェースまたは IP アドレスへ置き換えてください)。

upstream grafana {
    server localhost:3000;
}

map $http_upgrade $connection_upgrade {
    default upgrade;
    '' close;
}

server {
    listen our-server.netbird.cloud:443 ssl;
    server_name grafana.tuxnet.dev;
    http2 on;

    ssl_certificate     /etc/ssl/certs/grafana.tuxnet.dev.crt;
    ssl_certificate_key /etc/ssl/private/grafana.tuxnet.dev.key;

    access_log /var/log/nginx/grafana.tuxnet.dev.access.log main;
    error_log  /var/log/nginx/grafana.tuxnet.dev.error.log warn;

    location / {
        proxy_pass http://grafana;
        proxy_set_header Host $host;
    }
  
    # Grafana のライブ WebSocket 接続の代理化
    location /api/live/ {
        proxy_pass http://grafana;
        proxy_http_version 1.1;
        proxy_set_header   Upgrade    $http_upgrade;
        proxy_set_header   Connection $connection_upgrade;
        proxy_set_header   Host       $host;
    }
}

重要な設定ポイント

  • listen our-server.netbird.cloud:443 ssl;
    :
    • サーバーのVPN ネットワークインターフェースにバインドしています。
    • これにより、パブリックインターネットから送信される
      grafana.tuxnet.dev
      へのトラフィックはすべて拒否(ブロック)されます

これにより、スプリットホライゾン DNS と WAF の2 つのセキュリティ層が構築され、回避が困難な防御ラインとなります。

証明書の自動更新設定(Cron Job)

acme.sh には

--cron
フラグがあり、証明書の自動更新を制御できます。 Nginx の設定にある証明書ファイルを更新し、サービス再起動する必要があります。

以下のスクリプトを日次 cron ジョブとして実行します:

main() {
    refresh_certs
    sync_api_tuxnet_dev_certs
    sync_internal_tuxnet_dev_certs
    reload_nginx
}

refresh_certs() {
    setcap CAP_NET_BIND_SERVICE=+ep /usr/bin/socat1
    sudo -u acmesh /home/acmesh/acme.sh/acme.sh --cron
    setcap -r /usr/bin/socat1
}

sync_api_tuxnet_dev_certs() {
    local green=$(get_checksum "$API_SRC_KEY")
    local blue=$(get_checksum "$API_DST_KEY")
    local key_allowed_group=www-data

    if [[ "$green" != "$blue" ]]; then
        sync_certs "$API_SRC_KEY" "$API_DST_KEY" "$API_SRC_CERT" "$API_DST_CERT" "$key_allowed_group"
    fi
}

sync_internal_tuxnet_dev_certs() {
    local green=$(get_checksum "$INTERNAL_SRC_KEY")
    local blue=$(get_checksum "$INTERNAL_DST_KEY")
    local key_allowed_group=www-data

    if [[ "$green" != "$blue" ]]; then
        sync_certs "$INTERNAL_SRC_KEY" "$INTERNAL_DST_KEY" "$INTERNAL_SRC_CERT" "$INTERNAL_DST_CERT" "$key_allowed_group"
    fi
}

reload_nginx() {
    nginx -t
    systemctl reload nginx
}

get_checksum() {
    sha256sum "$1" | cut -d' ' -f1
}

sync_certs() {
    local src_key="$1"
    local dst_key="$2"
    local src_cert="$3"
    local dst_cert="$4"
    local key_allowed_group="$5"

    cp -v "$src_key" "$dst_key"
    logger "synced $dst_key"

    cp -v "$src_cert" "$dst_cert"
    logger "synced $dst_cert"

    chown root:${key_allowed_group} "$dst_key"
    chown root:ssl-cert "$dst_cert"
    chmod 640 "$dst_key" "$dst_cert"
}

main "$@"

補足:
setcap CAP_NET_BIND_SERVICE=+ep
について

  • acme.sh はスタンドアロンモードでポート 80 を使用しますが、root ユーザーとして実行したくない場合があります。
  • ポート 80 は「特権ポート」のため通常はルート以外がバインドできません。
  • この Cap がそれを可能にします。

これで、サービスが内部・外部を問わず安全な TLS 通信が可能になります。

ボーナス:SANs と CNAME を使用した拡張運用

複数の内部サービス(Grafana、Analytics など)がある場合、個別の証明書を作成するのは手間です。 その代わりに TLS SAN (Subject Alternative Name) を活用します。

ワイルドカード証明書 vs. TLS SAN

  • ワイルドカード: セキュリティ懸念から避けるのが無難です。
  • SAN 付き単一証明書: CN とともに、複数のサブドメインを指定できます。

これにより、以下の DNS レコード構成で 1 つの証明書を使用可能です:

# A レコード
internal.tuxnet.dev -> 10.0.1.x (内部 IP)

# CNAME レコード
grafana.tuxnet.dev -> internal.tuxnet.dev
analytics.tuxnet.dev -> internal.tuxnet.dev

SAN を含む証明書の発行例

1 つの ACME コマンドで複数のドメインを指定します:

acme.sh --issue -d internal.tuxnet.dev -d grafana.tuxnet.dev -d analytics.tuxnet.dev \
         --server letsencrypt --standalone

確認結果:

echo | openssl s_client -connect internal.tuxnet.dev:443 2>/dev/null | openssl x509 -noout -subject -ext subjectAltName
subject=CN=internal.tuxnet.dev
X509v3 Subject Alternative Name: 
    DNS:analytics.tuxnet.dev, DNS:grafana.tuxnet.dev, DNS:internal.tuxnet.dev

Nginx 設定では、異なる

server
ブロック間でこの同じ証明書ファイルを共有・再利用します。

まとめ

  • スプリットホライゾン DNSにより、内部/外部の IP を適切に分離。
  • **WAF(リバースプロキシ)**で不要なトラフィックをフィルタリングし、セキュリティ層を追加。
  • ACME プロトコルを活用して、自動化された無料証明書管理を実現。

この構成により、ダウンストリームの HTTP クライアントに影響を与えずに、内部サービスに対して安全な TLS 環境を構築できます。すべて無料です!

同じ日のほかのニュース

一覧に戻る →

2026/07/09 17:05

Show HN:私の低速な PC で GLM 5.2 を動かしている

## Japanese Translation: **Colibrì**は、ランタイム依存関係ゼロの純 C 言語で書かれた軽量な C エンジンであり、約 25 GB の RAM を搭載する汎用ハードウェア上で、740 億パラメータの GLM-5.2 モデルをローカルに実行することを可能にする。これは、ディスクストリーミング技術を採用することで実現しており、メモリ内に密度の高いコア(〜17B パラメータ)と頻繁に使用されるエキスパートのみを保持し、残りの約 21,504 のルーティング済みエキスパートをディスクからストリーミング処理する。このエンジンは、DeepSeek-V3 スタイルのルーティング、MLA アテンション、圧縮された KV キャッシュ、そして Native MTP 推論的デコード(int8 専用ヘッド)を含む GLM-5.2 の MoE アーキテクチャをネイティブでサポートしている。AVX2 インタージェンタートド積の最適化済みカーネルを採用し、使用パターンを学習して自動的にメモリピン固定を行う自動メモリ管理システムを活用することで、初期のコールドスタート後(最初の推論時にトークンあたり約 11 GB のディスク読み込み)のレイテンシを最小化している。プロジェクトには純 C ランタイムと、コンバージョン専用で Python のみが必要となるオフライン FP8→int4 変換スクリプトが含まれており、Linux/WSL2 および設定可能な環境パラメータに対応する。Apache 2.0(エンジン用)と MIT(ウェイト用)のハイブリッドライセンスの下にあり、研究者が消費者向け NVMe ドライブまたは高エンドハードウェア上においてローカルで最先端モデルを実行できるよう、完全なポートabilityを備えている。システムの性能に応じて、約 0.1 トークン/秒から 15 トークン/秒以上の速度を実現する。 (注:改善されたバージョンは、元のサマリーの流れを保ちつつ、量子化、推論的デコード、コールドスタートのメカニズム、ライセンスの詳細に関する不足していた技術的な specifics を統合しています。)

2026/07/09 20:03

欧州議会で Chat Control 1.0 が承認される

## Japanese Translation: 欧州議会は、Instagram、iCloud、Gmail などの主要プラットフォームにおける私的通信の無許可的大規模スキャンを 2028 年まで、あるいは恒久的な合意に至るまでの間を認める暫定規則を承認した。投票結果は賛成 276 票で可決されたが、絶対多数 361 票には届かなかった。一方、スキャン対象を司法手続きにおける被疑者だけに限定する修正案(322 対 255 の支持率)も、同様に絶対多数に達しなかったため採択されなかった。その結果、Meta や Apple など大規模な米国技術企業は当局から無許可でダイレクトメッセージのスキャンを義務付けられることとなり、WhatsApp のようにエンドツーエンド暗号化が実施されているアプリは除外されることとなった。データによると、この大規模監視は虐待通報の 36% しか説明しておらず(2022 年以降の米国事例において報告件数は 50% も減少しており、これは暗号化強化によるものとして示唆されている)、残りの大部分の通報は公開投稿やクラウドストレージから出ている。市民権利活動家や性暴力被害者を含む批判者は、この法律を「滑稽なもの」と糾弾し、それは真摯な児童保護よりもむしろビッグテックのデータ利益を追求するものだとしている。また、Meta が生成した通報のほとんどは、すでに企業が把握していた材料に過ぎないという発見にも言及している。恒久版のための協議は立法による立滞の解消後、9 月に再開予定となっており、この措置が安全性を損なう一方で実証的な保護的成果をもたらしていないと考える活動家からは激しい反対運動が生じている。

2026/07/09 21:48

Show HN: 18語

## Japanese Translation: ## 日本語訳: 提供された要約は、重要な点を明確かつ忠実に反映しているため、改訂の必要はありません。 ## Text to translate: The provided summary is clear and faithful to the key points, so no revision is necessary.