Apache+mod_auth_openidc+mod_proxyでGrafanaをAuth0(OIDC)認証対応してみた

2023.04.07

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

こんにちは。CX事業本部Delivery部のakkyです。

OSS版Grafanaでユーザー認証を行う方法には、組み込みのユーザーデータベースのほか、OAuth2やLDAPを使うことができます。 今回は、Apacheをリバースプロキシにしてmod_auth_openidcを使ってOIDC認証を行い、Grafanaへユーザー名とロールを渡す方法を検証しました。 IdPにはAuth0を使います。

mod_auth_openidcについては、以前にもブログが書かれていますのでご覧ください。

【Auth0】Auth0+Apache(mod_auth_openidc)でシングルサインオンしてみる

Auth0の設定

新たにアプリケーションを作り、mod_auth_openidcに設定するクライアントキーとクライアントシークレットを取得しておきます。

また、Grafanaのユーザーロールを指定するため、Auth0のuser_metadata.roleをトークンと一緒に返すように設定します。詳しくは以下の記事をご覧ください。

Rulesを利用して、id_tokenのクレームにuser_metadataを追加する

今回は、Actionsに置き換え、Login Flowに以下のようなスクリプトを設定しました。

exports.onExecutePostLogin = async (event, api) => {
  const namespace = 'https://myapp.example.com/';
  api.idToken.setCustomClaim(
		`${namespace}user_metadata_role`, 
		event.user.user_metadata.role
	);		
  return;
};

ユーザーのuser_metadataにroleを追加しておきます。これがないとGrafanaでは閲覧専用のViewerになるようです。

{
  "role": "Admin"
}

Apacheを動かすコンテナの作成

Apacheにmod_auth_openidcをインストールしたコンテナを作ります。次にようなDockerfileを作ります。

FROM docker.io/httpd:bullseye

COPY libapache2-mod-auth-openidc_2.4.13.2-1.bullseye_amd64.deb /root
RUN apt update \
    && apt install -y libcjose0 libhiredis0.14 apache2-api-20120211 apache2-bin \
    && apt clean \
    && rm -rf /var/lib/apt/lists/*
RUN apt install -y /root/libapache2-mod-auth-openidc_2.4.13.2-1.bullseye_amd64.deb
RUN cp /usr/lib/apache2/modules/mod_auth_openidc.so /usr/local/apache2/modules

ビルドする際には、mod_auth_openidcのGithub releaseからlibapache2-mod-auth-openidc_x.x.x.x-x.bullseye_amd64.debをダウンロードしておいてください。 バグや脆弱性の修正などがあるので、最新版を使ってください。

Apacheの設定(httpd.conf)

デフォルトのhttpd.confを元に、mod_proxy、mod_proxy_http、mod_proxy_wstunnel、mod_rewrite、mod_auth_openidcを有効にします。

LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_http_module modules/mod_proxy_http.so
LoadModule proxy_wstunnel_module modules/mod_proxy_wstunnel.so
LoadModule rewrite_module modules/mod_rewrite.so
LoadModule auth_openidc_module modules/mod_auth_openidc.so

mod_auth_openidcの設定は次のようにしました。

OIDCProviderMetadataURL https://xxxxxxxxxxxxxx.xx.auth0.com/.well-known/openid-configuration
OIDCClientID XXXXXXXXXXXXXXXXXXXXXXXX
OIDCClientSecret XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
OIDCProviderUserInfoEndpoint https://xxxxxxxxxxxxxx.xx.auth0.com/userinfo

OIDCRedirectURI http://<コンテナのIPアドレス>:8080/secure/redirect_uri
OIDCCryptoPassphrase <ランダムな文字列を設定する>

OIDCScope "openid profile email"
OIDCRemoteUserClaim email
OIDCResponseType "code"
OIDCStateMaxNumberOfCookies 5 true
OIDCProviderEndSessionEndpoint https://xxxxxxxxxxxxxx.xx.auth0.com/oidc/logout

/secureの内部を認証することにして、/secure/grafanaをGrafanaにリバースプロキシすることにします。

<Location /secure>
   AuthType openid-connect
   Require valid-user
</Location>

<Proxy *>
    AuthType openid-connect
    Require valid-user

    RewriteEngine On
    RewriteRule .* - [E=PROXY_USER:%{LA-U:REMOTE_USER},NS]
    RequestHeader set X-WEBAUTH-USER "%{PROXY_USER}e"
</Proxy>

RequestHeader unset Authorization

RewriteEngine on 
RewriteRule ^/secure/grafana$ /secure/grafana/ [R]

ProxyRequests Off
ProxyPreserveHost On

ProxyPass /secure/grafana/api/live/ ws://localhost:3000/api/live/
ProxyPass /secure/grafana/ http://localhost:3000/

Grafanaの脆弱性対策のため、ProxyPreserveHost OnとしてHostヘッダを付けないとうまく動きませんでした。 https://grafana.com/blog/2022/02/08/grafana-7.5.15-and-8.3.5-released-with-moderate-severity-security-fixes/

Grafanaの設定

Grafana側は、ヘッダで渡された内容を認証情報として使用するように設定します。

上から3行がリバースプロキシを使うための設定で、残りが認証情報プロキシの設定です。

GF_AUTH_PROXY_HEADER_NAMEで認証情報として使うヘッダ名を指定します。 また、GF_AUTH_PROXY_HEADER_NAMEでロールなどの補足情報に使うヘッダ名を指定しています。

Grafanaをコンテナで動かすときには、設定をenv_fileで行うと便利です。次のように設定しました。

GF_SERVER_DOMAIN=<GrafanaのドメインまたはIPアドレス>
GF_SERVER_ROOT_URL=%(protocol)s://%(domain)s:%(http_port)s/secure/grafana/
GF_SERVER_SERVE_FROM_SUB_PATH=true
GF_AUTH_PROXY_ENABLED=true
GF_AUTH_PROXY_HEADER_NAME=OIDC_CLAIM_email
GF_AUTH_PROXY_HEADER_PROPERTY=email
GF_AUTH_PROXY_AUTO_SIGN_UP=true
GF_AUTH_PROXY_SYNC_TTL=60
GF_AUTH_PROXY_WHITELIST=
GF_AUTH_PROXY_HEADERS=Role:OIDC_CLAIM_https---myapp.example.com-user_metadata_role
GF_AUTH_PROXY_ENABLE_LOGIN_TOKEN=false
GF_AUTH_DISABLE_LOGIN_FORM=true
GF_AUTH_SIGNOUT_REDIRECT_URL=http://<GrafanaのドメインまたはIPアドレス>/secure/redirect_uri?logout=<ログアウト後のリダイレクト先をURLエンコードしたアドレス>

今回は実験として手元で動かしているのでユーザー認証をする場所もhttpを使っていますが、実運用時にはhttpsで保護してください。

動作確認

以上の設定でApacheのコンテナとGrafanaのコンテナを動かし、ログインとログアウトがきちんとできることを確認できました。 設定を間違えると、ログインできるのにログアウトできなくなったりします。

GrafanaできちんとRoleが設定されていることも確認できました。

また、ログインには脆弱性が紛れ込みやすいので、運用時には十分検証を行ってください。