Back
Featured image of post Prometheusにクライアント認証をかける

Prometheusにクライアント認証をかける

特定の相手だけがメトリクスを見られるようにしたい

やったことのまとめ

Prometheusクライアント認証をかけてみた.

Prometheus単独でクライアント認証を行う方法と,
クライアント認証をかけたnginxでリバースプロキシする方法をそれぞれ試した.

Prometheus, nginxはすべてDocker Desktop for Macで動かした.

Prometheus単独でクライアント認証する
Prometheus単独でクライアント認証する

Prometheus+nginxでクライアント認証する
Prometheus+nginxでクライアント認証する

つかうもの

やったこと

各種秘密鍵と証明書の作成

まずはクライアント認証に必要なサーバーとクライアントの秘密鍵+証明書をOpenSSLで作成する.

# nginxコンテナを起動してOpenSSLを使う
$ docker container run --rm -it -v="$PWD:/workdir" -w="/workdir" --entrypoint=/bin/bash nginx:1.21.0

# サーバー用のオレオレ証明書を作成
# ホスト名はprometheus.hogehoge.comとしてSANsの設定もしておく(Chromeで開くため)
$ openssl genpkey -algorithm RSA -out server-private-key.pem
$ openssl req -x509 -key server-private-key.pem -out server-cert.pem -addext 'subjectAltName = DNS:prometheus.hogehoge.com'
Country Name (2 letter code) [AU]:
State or Province Name (full name) [Some-State]:
Locality Name (eg, city) []:
Organization Name (eg, company) [Internet Widgits Pty Ltd]:
Organizational Unit Name (eg, section) []:
Common Name (e.g. server FQDN or YOUR name) []:prometheus.hogehoge.com
Email Address []:

# クライアント認証局の証明書を作成(オレオレ認証局)
$ openssl genpkey -algorithm RSA -out client-ca-private-key.pem
$ openssl req -x509 -key client-ca-private-key.pem -out client-ca-cert.pem -days 365
Country Name (2 letter code) [AU]:
State or Province Name (full name) [Some-State]:
Locality Name (eg, city) []:
Organization Name (eg, company) [Internet Widgits Pty Ltd]:
Organizational Unit Name (eg, section) []:
Common Name (e.g. server FQDN or YOUR name) []:client-ca
Email Address []:

# 署名のための準備
$ mkdir -p demoCA/newcerts && touch demoCA/index.txt
$ echo 01 > ./demoCA/serial

# クライアント証明書を作成(クライアント認証局で署名する)
# PKCS#12形式のものも作成しておく
$ openssl genpkey -algorithm RSA -out client-private-key.pem
$ openssl req -new -key client-private-key.pem -out client-csr.pem
Country Name (2 letter code) [AU]:
State or Province Name (full name) [Some-State]:
Locality Name (eg, city) []:
Organization Name (eg, company) [Internet Widgits Pty Ltd]:
Organizational Unit Name (eg, section) []:
Common Name (e.g. server FQDN or YOUR name) []:client
Email Address []:
A challenge password []:
An optional company name []:

$ openssl ca -in client-csr.pem -out client-cert.pem -keyfile client-ca-private-key.pem -cert client-ca-cert.pem -days 365
Sign the certificate? [y/n]:y
1 out of 1 certificate requests certified, commit? [y/n]y

$ openssl pkcs12 -export -clcerts -in client-cert.pem -inkey client-private-key.pem -out client-cert.p12
Enter Export Password: fugafuga
Verifying - Enter Export Password: fugafuga

# コンテナから出ると鍵と証明書が作成されている
$ exit
$ ls -1
client-ca-cert.pem
client-ca-private-key.pem
client-cert.p12
client-cert.pem
client-csr.pem
client-private-key.pem
demoCA
server-cert.pem
server-private-key.pem

今回は鍵と証明書をたくさん使っていて後で混乱しそうなのでここに内容をまとめておく.

  • サーバー認証局の証明書(server-cert.pem)
    • サーバー証明書に署名した認証局の証明書
    • 今回はオレオレ証明書なのでサーバー証明書と同一だが, ちゃんとした認証局(CA)によってサーバー証明書を発行した場合は認証局の証明書を使う.
  • サーバー認証局の秘密鍵(server-private-key.pem)
    • サーバー証明書に署名した認証局の秘密鍵
    • 今回はオレオレ証明書なのでサーバー秘密鍵と同一
    • このあとは使わない
  • サーバー証明書(server-cert.pem)
    • サーバー(Prometheus)の身元を証明する証明書. 今回はオレオレ証明書を使う.
    • ホスト名はprometheus.hogehoge.comとした(オレオレ証明書なので任意の名前)
  • サーバー秘密鍵(server-private-key.pem)
    • サーバー(Prometheus)に持たせる秘密鍵
  • クライアント認証局の証明書(client-ca-cert.pem)
    • クライアント証明書に署名した認証局の証明書
  • クライアント認証局の秘密鍵(client-ca-private-key.pem)
    • クライアント証明書に署名した認証局の秘密鍵
    • このあとは使わない
  • クライアント証明書(client-cert.pem, client-cert.p12)
    • クライアントの身元を証明する証明書
    • クライアント(Prometheus, Grafana, User)ごとに分けてもいいけど, 面倒なので全部同じものを使う
  • クライアント秘密鍵(client-private-key.pem)
    • クライアントに持たせる秘密鍵
  • クライアントのCSR(client-csr.pem)
    • クライアント証明書を発行するときに使ったCSR
    • このあとは使わない

ついでにブラウザでも動作確認できるように, Macでサーバー証明書を信頼する設定とクライアント証明書を持たせる設定を行う.

Finderからclient-cert.p12server-cert.pemをそれぞれダブルクリックして,
証明書をキーチェーンアクセスで信頼するように設定する.

.p12を開くとパスワードを求められるので, 作成時に指定したもの(fugafuga)を入力する
.p12を開くとパスワードを求められるので, 作成時に指定したもの(fugafuga)を入力する

クライアント証明書の信頼設定を"常に信頼"に変えておく
クライアント証明書の信頼設定を"常に信頼"に変えておく

サーバー証明書も同様に設定
サーバー証明書も同様に設定

最終的なキーチェーンアクセスの状態
最終的なキーチェーンアクセスの状態

最後に, 今回はサーバー証明書で実在しないドメイン(prometheus.hogehoge.com)を指定しているので,
Macの/etc/hostsに名前解決の設定を記述しておく.

# /etc/hostsにprometheus.hogehoge.com(localhostに飛ばす)を追加
$ echo "127.0.0.1 prometheus.hogehoge.com" | sudo tee -a /etc/hosts

以上で証明書とか秘密鍵の準備は完了.

Prometheus単独でクライアント認証をかける

Prometheus単独でクライアント認証する
Prometheus単独でクライアント認証する

Prometheus自体にクライアント認証の仕組みがあるので1,
サーバー証明書サーバー秘密鍵(HTTPS化してサーバーの身元を証明するのに使用),
クライアント認証局の証明書(クライアントの身元を確認するのに使用)を持たせる.

クライアント認証の設定はweb-config.ymlに記述する.

Prometheus自身のメトリクスを取得する際にもクライアント認証を突破する必要があるので,
クライアント証明書クライアント秘密鍵(クライアントの身元を証明するのに使用),
サーバー認証局の証明書(HTTPSサーバーの身元を確認するのに使用)も持たせることにする.
(Prometheus自身のメトリクスを取得しない場合は不要)

クライアント認証を突破するための設定はprometheus.ymlscrape_configs2に記述する.

あとは指定のパスにそれぞれの秘密鍵, 証明書ファイルを配置する.
今回はDockerで起動するのでパスを指定してマウントしてあげれば良い.

以上で準備ができたので, いよいよDockerで起動する.

# ディレクトリの状態(使わないものは消している)
$ ls -1
client-ca-cert.pem
client-cert.pem
client-private-key.pem
docker-compose.yml
prometheus.yml
server-cert.pem
server-private-key.pem
web-config.yml

# コンテナを起動する
$ docker compose up -d --force-recreate --remove-orphans

動作確認のため,
Chromeで https://prometheus.hogehoge.com を開く.

クライアント証明書を要求されるので"OK"を選択
クライアント証明書を要求されるので"OK"を選択

この画面が出たらMacにログインしているユーザー名とパスワードを入力する
この画面が出たらMacにログインしているユーザー名とパスワードを入力する

クライアント認証に成功. サーバー証明書も有効になっている
クライアント認証に成功. サーバー証明書も有効になっている

Macにクライアント証明書をもたせたため, 問題なく閲覧できた.

今度はクライアント証明書を使わずに https://prometheus.hogehoge.com を開いてみる.

クライアント証明書を信頼しない設定に戻す
クライアント証明書を信頼しない設定に戻す

今度はここで"キャンセル"を選択
今度はここで"キャンセル"を選択

クライアント認証に失敗したので閲覧できない
クライアント認証に失敗したので閲覧できない

要求されたクライアント証明書を提示しなかったため, 閲覧ができない.
以上でPrometheusにクライアント認証がかかっていることを確認できた.

# コンテナを終了しておく
$ docker compose down

Prometheus+nginxでクライアント認証をかける

Prometheus+nginxでクライアント認証する
Prometheus+nginxでクライアント認証する

Prometheusの前段にnginx(リバースプロキシ)を用意して, そこでクライアント認証をかけることもできる.

nginxサーバー証明書サーバー秘密鍵(HTTPSサーバーの身元を証明するのに使用),
クライアント認証局の証明書(クライアントの身元を確認するのに使用)を持たせる.

今回はHTTPS化と認証まわりの設定をhttps.confに記述する.
今回はクライアント認証を行うポート(443)と別に認証をかけないポート(44433)も用意してみる.

この場合Prometheus自体にはクライアント認証をかけないのでweb-config.ymlは不要,
さらに自身のメトリクスを取得する場合でもクライアント証明書クライアント秘密鍵は不要となる.
(したがってprometheus.ymlも自分で作らずデフォルトのものを使用する)

あとは各種ファイルをnginxのコンテナにマウントして起動する.
(先程とは異なり, Prometheusにファイルを持たせないなど設定が変わっていることに注意)

# ディレクトリの状態(使わないものを消している)
$ ls -1
client-ca-cert.pem
docker-compose.yml
https.conf
server-cert.pem
server-private-key.pem

# コンテナを起動する
$ docker compose up -d --force-recreate --remove-orphans

Chromeでhttps://prometheus.hogehoge.comを開く.

クライアント証明書を要求されるが"キャンセル"を選択
クライアント証明書を要求されるが"キャンセル"を選択

nginxのクライアント認証失敗画面に遷移する
nginxのクライアント認証失敗画面に遷移する

クライアント証明書を提示しなかったので認証に失敗した.
(クライアント証明書を要求された際に"OK"を選択すると認証に成功する)

この状態で次はhttps://prometheus.hogehoge.com:44433を開く.

クライアント認証がかかっていないのでそのまま開ける. サーバー証明書も有効
クライアント認証がかかっていないのでそのまま開ける. サーバー証明書も有効

44433ポートにはクライアント認証をかけていないため, そのまま開くことができた.

こんな感じの出し分けをPrometheus単独でやるのは難しいが,
nginxを使うと比較的カンタンに実現できるし, Prometheus自身のメトリクスを取得するためにクライアント証明書などを持たせる必要もなくなる.

クライアント認証以外の認証方式も設定しやすいので,
個人的にはこちらのほうがすき.

# コンテナを終了しておく
$ docker compose down

おわり

Prometheusにクライアント認証をかけてみた.

セキュリティ要件でPrometheusへのアクセスに認証が必要な場合は,
基本は前段のnginxで認証設定をかけてクライアント側(Grafanaとか)に認証情報を持たせてアクセスするのが良いと思う.

おまけ

箱より袋派なねこ
箱より袋派なねこ

Built with Hugo
Theme Stack designed by Jimmy