0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

プロキシ環境下のSLES on EC2で `smt-ec2.susecloud.net` が名前解決できなかったときの対応 - SUSEリポジトリのIPアドレスを動的に取得してSquidにhostsを設定する

0
Posted at

はじめに

お疲れ様です。矢儀 @yuki_ink です。

SUSE Linux Enterprise Server (SLES) をEC2で起動してプロキシの設定をしたら、smt-ec2.susecloud.net が名前解決できなくて、SUSEのリポジトリ(Update Server)にアクセスできずに zypper でパッケージをインストールできなかった!! という事象に遭遇しました。

プロキシ(Squid)側の設定をいじって解決に至ったので、備忘を残しておきます。

同じ問題への先行事例として、Azure 上の SLES で Squid の hosts_file を使って smt-azure.susecloud.net を通した @ytamu2 さんの記事があります。
単一 IP を hosts にベタ書きする最小構成に加え、SLES 側で SUSEConnect --cleanup から registercloudguest --force-new までを実行し、再登録する手順が具体的に示されています。
トラブルシューティングの際に大変ありがたく、参考にさせていただきました。

本記事は、こちらの記事に、以下の要素を付け足したものです。

  • コンテナでSquidをホストしているときの対応ノウハウ
  • Update ServerのIPアドレスを動的に取得してhostsに反映する仕組み

SUSE Public Cloud 更新インフラの仕様

まず前提として、SUSE Linux Enterprise Server (SLES) の PAYG イメージは、AWS・Azure・GCP のいずれで起動しても、SUSE が運用する Update Infrastructure に自動登録される仕組みになっています。
登録の流れは二段構えで、まずインスタンスが Region Server(世界各地に少数配置された情報提供サーバ)へ HTTPS で問い合わせ、自リージョン向けの Update Server の IP アドレスを受け取ります。
続いて、そのうち最初に応答した Update Server の IP をインスタンス自身の /etc/hostssmt-<プロバイダ>.susecloud.net として書き込み、以降 zypper などのクライアントはその hosts エントリを頼りに Update Server へ接続します。

詳細は公式の「Accessing the Public Cloud Update Infrastructure via a Proxy」をご確認ください。

なお FQDN 側に残っている smt- プレフィックスは、SUSE が以前提供していた SMT (Subscription Management Tool) の名残で、指しているサーバの実体は現行呼称の「Update Server」と同じもののようです。
この記事でも「Update Server」で表記を統一します。

ここで重要なのは、smt-ec2.susecloud.net をはじめとする Update Server の FQDN がグローバル DNS には登録されていないという点です。
名前解決はあくまで各インスタンスのローカル hosts で完結する設計になっており、外部の DNS リゾルバに問い合わせても解決できません。

何という仕様・・・😅

プロキシ構成で発生するエラー

SLES をプロキシ経由の構成に置くと、HTTPS リクエストの宛先ホスト名解決の担当がインスタンスからプロキシへ移ります。
これはSLESの仕様というより、プロキシを利用する上での必然です。

クライアントが CONNECT smt-ec2.susecloud.net:443 を送ると、プロキシは自身の DNS リゾルバでホスト名を引こうとしますが、公開 DNS には存在しないので当然引けません。
Squid の場合、このとき返るのが ERR_DNS_FAIL で、クライアント側には HTTP 503 Service Unavailable として観測されます。

SLES 内部の /etc/hosts にいくら正しい IP が並んでいても、プロキシがその内容を知らない以上、名前解決は成功しません。

プロキシ側で名前解決を通す ― Squid の hosts_file ディレクティブと SUSE 公式 JSON

本題です。

プロキシ(今回は Squid)に hosts ファイルを参照させ、そこに Update Server の IP を書き込みます。
Squid には hosts_file という設定ディレクティブがあり、任意のパスを指定できます。
DNS 応答よりも先に hosts の内容を評価するので、公開 DNS で引けないホスト名でもここに書けば解決可能になります。

SUSE は全リージョンの Update Server の一覧をJSONで公開しています。
AWS 向けはこちらの URL で、この中から typesmtregion にリージョン名(東京なら ap-northeast-1)が入るエントリを拾えばよいことになります。

GCP: https://susepubliccloudinfo.suse.com/v1/google/servers.json
Azure: https://susepubliccloudinfo.suse.com/v1/microsoft/servers.json
AWS: https://susepubliccloudinfo.suse.com/v1/amazon/servers.json

(出典)Accessing the Public Cloud Update Infrastructure via a Proxy

この JSON を使ってコンテナ起動時に hosts を生成しておけば、もし SUSE 側で Update Server の IP が追加・変更されても、その都度追いつき対応をする必要はなくなります。

やってみる

CloudShellのDockerコンテナでSquidを動かして、検証してみます。

ディレクトリと 4 ファイルの準備

作業ディレクトリを作り、Dockerfile・squid.conf・whitelist.txt・entrypoint.sh の4つを置きます。

mkdir -p ~/squid-suse && cd ~/squid-suse

Dockerfile は amazonlinux:2023-minimal をベースに squid を入れ、非 root(squid ユーザ)で動かすようにしています。

以下の点を踏まえて、hosts ファイルは /etc/hosts ではなく、/etc/squid/hosts としてSquid用に定義するようにして、かつ entrypoint.sh でファイルごと生成する形にしました。

  • Docker自体の仕組みとして、コンテナのhostsファイルはコンテナ起動のタイミングで決定されるから
  • コンテナが決定したhostsをroot権限でいじりたくなかったから
    ※やろうと思えばできるみたいです(参考:Fargate+Dockerで名前解決リクエストに挑んだ話
# Dockerfile
FROM public.ecr.aws/amazonlinux/amazonlinux:2023-minimal

ENV TZ=Asia/Tokyo \
    SQUID_CONF_PATH=/etc/squid/squid.conf \
    WHITELIST_DIR=/etc/squid/whitelist \
    SQUID_HOSTS_FILE=/etc/squid/hosts \
    SUSE_SERVERS_URL=https://susepubliccloudinfo.suse.com/v1/amazon/servers.json \
    AWS_REGION=ap-northeast-1  # 必要に応じてリージョンを修正してください!!!

RUN dnf update -y && \
    dnf install -y squid tzdata jq && \
    ln -sf /usr/share/zoneinfo/Asia/Tokyo /etc/localtime && \
    dnf clean all && \
    rm -rf /var/cache/dnf

RUN mkdir -p /etc/squid /var/log/squid /var/spool/squid /run/squid ${WHITELIST_DIR} && \
    chown -R squid:squid /etc/squid /var/log/squid /var/spool/squid /run/squid ${WHITELIST_DIR} && \
    chmod 750 /etc/squid /var/log/squid /var/spool/squid /run/squid ${WHITELIST_DIR}

COPY --chown=squid:squid --chmod=640 squid.conf     ${SQUID_CONF_PATH}
COPY --chown=squid:squid --chmod=640 whitelist.txt  ${WHITELIST_DIR}/whitelist.txt
COPY --chown=squid:squid --chmod=750 entrypoint.sh  /usr/local/bin/entrypoint.sh

USER squid

EXPOSE 3128

ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]

squid.conf は最小構成にします。
hosts_file/etc/squid/hosts に向けているのが今回の要点で、ホワイトリストは別ファイルで管理します。

squid.conf
# squid.conf
http_port 3128

acl SSL_ports  port 443
acl CONNECT    method CONNECT
acl whitelisted_domains dstdomain "/etc/squid/whitelist/whitelist.txt"

http_access allow CONNECT SSL_ports whitelisted_domains
http_access deny all

# 非 root で書ける squid 所有パスを hosts_file として指定
hosts_file /etc/squid/hosts

access_log stdio:/dev/stdout squid
cache_log  stdio:/dev/stderr

pid_filename /run/squid/squid.pid
coredump_dir /var/spool/squid

whitelist.txt は検証目的で、以下の一行で。

whitelist.txt
smt-ec2.susecloud.net

続いての entrypoint.sh が、本記事の肝の部分です!
SUSE の servers.json から AWS_REGION に一致する Update Server のエントリを jq で抽出し、/etc/squid/hosts を生成してから Squid を起動します。
squid ユーザで実行される前提で、書き込み先はすべて squid 所有のパスに閉じています。

entrypoint.sh
#!/bin/sh
# entrypoint.sh
set -eu

echo "[entrypoint] fetching Update Servers for region=${AWS_REGION}"

HOSTS_LINES="$(
  curl -fsSL "${SUSE_SERVERS_URL}" \
    | jq -r --arg r "${AWS_REGION}" \
        '.servers[] | select(.type=="smt" and .region==$r) | "\(.ip)\t\(.name)"'
)"

if [ -z "${HOSTS_LINES}" ]; then
  echo "[entrypoint] ERROR: no Update Server entries found for region=${AWS_REGION}" >&2
  exit 1
fi

{
  echo "# auto-generated at $(date -Iseconds) from ${SUSE_SERVERS_URL}"
  echo "${HOSTS_LINES}"
} > "${SQUID_HOSTS_FILE}"

echo "[entrypoint] ${SQUID_HOSTS_FILE}:"
cat "${SQUID_HOSTS_FILE}"


echo "[entrypoint] Cleaning up any existing PID files"
PID_FILE="/run/squid/squid.pid"
if [ -f "$PID_FILE" ]; then
  echo "[entrypoint] Removing existing PID file: $PID_FILE"
  rm -f "$PID_FILE"
fi

echo "[entrypoint] Initializing Squid cache"
/usr/sbin/squid -z

echo "[entrypoint] Cleaning up PID file after cache initialization"
[ -f "$PID_FILE" ] && rm -f "$PID_FILE"

echo "[entrypoint] Starting Squid in no-daemon mode"
exec /usr/sbin/squid -N -d 1

ビルドと起動

CloudShell 上でそのままビルドできます。

docker build -t squid-suse:local .
docker run -d --name squid-suse -p 3128:3128 squid-suse:local

servers.json から抽出された東京リージョンの Update Server の IP が /etc/squid/hosts に流し込まれていることを確認します。

docker exec squid-suse cat /etc/squid/hosts

このように出力されました。大丈夫そう!!!

# auto-generated at 2026-07-08T13:08:29+09:00 from https://susepubliccloudinfo.suse.com/v1/amazon/servers.json
54.248.86.233   smt-ec2.susecloud.net
54.248.226.128  smt-ec2.susecloud.net
54.248.240.93   smt-ec2.susecloud.net

プロキシ経由での名前解決確認

動作確認してみます。

まずは、CloudShell から プロキシを指定せずに curl を打ちます。
CloudShell では hosts の設定はしていません。

curl -v https://smt-ec2.susecloud.net

当然、名前解決でエラーになりました。

* Could not resolve host: smt-ec2.susecloud.net
* Store negative name resolve for smt-ec2.susecloud.net:443
* shutting down connection #0
curl: (6) Could not resolve host: smt-ec2.susecloud.net

続いて CloudShell から Docker で起動した Squid に向けて curl を打ちます。
-x で明示プロキシを指定し、ERR_DNS_FAIL にならず TLS ハンドシェイクまで到達することを確認したいところです。

curl -v -x http://localhost:3128 https://smt-ec2.susecloud.net

Update Server がクライアント証明書を要求しますので、SLES 以外からの curl では最終的にエラーになります。
ここで確認したいのはあくまでプロキシ側で名前解決が通ったかどうかで、次のログ行が出ていれば hosts が効いていることになります。

* Establish HTTP proxy tunnel to smt-ec2.susecloud.net:443
> CONNECT smt-ec2.susecloud.net:443 HTTP/1.1
> Host: smt-ec2.susecloud.net:443
> User-Agent: curl/8.17.0
> Proxy-Connection: Keep-Alive
> 
< HTTP/1.1 200 Connection established
< 
* CONNECT phase completed
* CONNECT tunnel established, response 200

entrypoint.sh の hosts 生成部分をコメントアウトし、再ビルドしてコンテナを起動すると、同じ curl コマンドが HTTP/1.1 503 Service Unavailable を返し、Squid のログに ERR_DNS_FAIL が出ます。

ECS Fargate に載せる場合

こちらの記事で実装例まで踏み込んでいますので、参考にしてください!

留意点

servers.json から取ってくる方式でも、IP の変更検知はコンテナ再起動タイミングでしか行われません。
SUSE の Update Server 追加はそう頻繁ではありませんが(頻繁にあってたまるか!!)、長期稼働することが想定される環境では cron や sidecar で日次で JSON を取り直し、差分があれば squid -k reconfigure で無停止反映するとより堅牢になります。

また、MITM 型プロキシを噛ませていると、Region Server との TLS ハンドシェイクで証明書エラーになり、そもそも Update Server の IP を取得できずに詰まる仕様のようです。
SUSE のクライアント証明書を用いた TLS をプロキシで終端させない設定(該当ホストは TLS 透過とか)を入れる必要があります。

What happens if communication fails along the way as described above? The update infrastructure is designed using certificate pinning. This will cause problems with MITM devices that intercept the pinned certificates and replace them with their own. The MITM device will need to exempt the update infrastructure IPs from SSL inspection to prevent these problems.

(出典)Accessing the Public Cloud Update Infrastructure via a Proxy

終わりに

SUSEがCDNを用意すれば、こんな対応不要なのでは???

今後に期待しましょう😓

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?