1. 記事一覧 >
  2. ブログ記事
category logo

oauth2-proxyで既存アプリにAzure AD OpenID Connect認証機能を追加

(更新) (公開)

はじめに

oauth2-proxy で既存アプリに Azure AD(Microsoft Entra ID) OpenID Connect 認証機能を追加しました。


過去記事で、SimpleSAMLphp, go-oidc と同じようなことをやってきましたが、今回の構成は、プロキシ型の認証になります。
プロキシ型の特徴として、既存の Web アプリケーションの実装を変更しないまま認証機能を実現できるということがあります。
プロキシ型の認証は、アプリケーションに認証ロジックを組み込むことが困難な場合や、認証機能を分離したい場合に有用です。


構成は、公式サイトhttps://oauth2-proxy.github.io/oauth2-proxy/)Architecture の図の左側です。

Architecture


今回、簡単なアプリケーション実装(PHP で phpinfo() を実行するだけ。) ~
Azure AD(Microsoft Entra ID)設定 ~
oauth2-proxy 設定 ~
OpenID Connect 認証を通過したらアプリにアクセス
まで全手順を紹介していきます。
なお、oauth2-proxy は、VSCode で F5 で起動してデバッグできるようにします。


Azure AD(Azure Active Directory)は、Microsoft Entra ID に名称が変わりましたが、この記事では、Azure AD 表記のままでいきます。

手順は、OS(Ubuntu22) インストール & SSH 接続成功直後からスタートとします。

今回の手順では、Nginx & oauth2-proxy と Nginx & Web アプリケーション(PHP) は同一サーバーに同居します。

同一サーバーに同居 図

【検証環境】

Ubuntu 22.04.3 LTS

Nginx 1.18.0

PHP 8.3.1

oauth2-proxy V7.5.1


Web アプリケーション環境構築

今回、Web アプリケーションサーバーの URI は、

http://webapps-php.example.com/info.php とします。

root で作業するため、sudo は省略しています。

# apt update
# add-apt-repository ppa:ondrej/php -y
# apt update
# apt -y install php8.3 php8.3-gd php8.3-mbstring php8.3-common php8.3-curl
# php -v
PHP 8.3.1 (cli) (built: Dec 21 2023 20:12:13) (NTS)
# apt -y remove apache2-*
# apt install php-fpm -y
# apt install nginx -y
# nginx -v
nginx version: nginx/1.18.0 (Ubuntu)
# vi /etc/nginx/fastcgi_params
/etc/nginx/fastcgi_params
fastcgi_param  SCRIPT_NAME        $fastcgi_script_name;
fastcgi_param  SCRIPT_FILENAME    $document_root$fastcgi_script_name;
fastcgi_param  SCRIPT_NAME        $fastcgi_script_name;
# mkdir -p /opt/webapps/php
# chown -R www-data: /opt/webapps
# mkdir -p /var/log/webapps/php
# vi /opt/webapps/php/info.php
/opt/webapps/php/info.php
<?php
phpinfo();
# vi /etc/nginx/conf.d/webapps-info.conf
/etc/nginx/conf.d/webapps-info.conf
server  # サーバーブロックの開始
{
  listen 80;  # サーバーが待ち受けるポート番号(HTTPのデフォルトポートは80)

  server_name webapps-php.example.com;  # サーバーの名前(ドメイン名)

  access_log /var/log/webapps/php/access.log;  # アクセスログの出力先
  error_log /var/log/webapps/php/error.log;  # エラーログの出力先

  proxy_buffer_size 8k;  # プロキシバッファのサイズ(8キロバイト)

  root /opt/webapps/php;  # サーバーのルートディレクトリ

  location /  # ルートディレクトリに対する設定
  {
    index index.html index.htm index.php;  # デフォルトで使用するインデックスファイル
  }

  location ~ [^/]\.php(/|$)  # .phpで終わるリクエストに対する設定
  {
    fastcgi_split_path_info ^(.+?\.php)(/.*)$;  # パス情報を分割する正規表現

    if (!-f $document_root$fastcgi_script_name)  # ファイルが存在しない場合
    {
      return 404;  # 404エラーを返す
    }

    client_max_body_size 100m;  # クライアントからの最大ボディサイズ(100メガバイト)

    fastcgi_param HTTP_PROXY "";  # HTTP_PROXYパラメータを空に設定(セキュリティ対策)

    fastcgi_pass unix:/run/php/php8.3-fpm.sock;  # FastCGIサーバーへのパス
    fastcgi_index index.php;  # FastCGIサーバーのインデックスファイル

    include fastcgi_params;  # FastCGIパラメータのインクルード
  }
}

# vi /etc/php/8.3/fpm/php.ini
date.timezone = Asia/Tokyo
# vi /etc/hosts
192.168.11.9 webapps-php.example.com

今回、Web アプリケーションサーバーの IP アドレスは、192.168.11.9 とします。

# systemctl restart nginx
# systemctl restart php8.3-fpm

ひとまず、http://webapps-php.example.com/info.php にアクセスして、単体で動作しているか確認します。

単体で動作しているか確認

本来であれば、直接アクセス不可の場所に構築されているはずですが、ここでは簡略化のため、このまま進めます。(この記事の最後まで実施しても :80 直接アクセスの場合、認証無しになります。)

単体で動作しているか確認 図


golang-go インストール

Go 言語をインストールします。

# apt update -y
# apt install golang-go -y
# go version
go version go1.18.1 linux/amd64

Go 言語をインストール 図


oauth2-proxy 設置

/opt/oauth2-proxy を配置します。

# apt install git -y
# cd /opt
# git clone https://github.com/oauth2-proxy/oauth2-proxy.git

oauth2-proxy 設置 図


Azure AD - アプリの登録

OP(Azure AD)側の設定を行います。
Azure ポータルから、Microsoft Entra ID に移動して、アプリの登録 をクリックします。 Microsoft Entra ID


アプリの登録


+新規作成 をクリックします。

アプリの登録 +新規作成


アプリ情報を入力し、登録 をクリックします。
名前oauth2-proxy(任意です。)
サポートされているアカウントの種類この組織ディレクトリのみに含まれるアカウント (<テナント名> のみ - シングル テナント)
リダイレクト URI (省略可能)Web https://webapps-php.example.com/oauth2/callback

アプリ情報を入力


概要 をクリックして、
アプリケーション (クライアント) ID から client_id(後で行う oauth2-proxy 設定項目)を確認しておきます。

アプリケーション (クライアント) ID


証明書とシークレット をクリックし、+新しいクライアント シークレット をクリックします。 新しいクライアント シークレット


説明有効期限 を任意の値に設定し、追加 をクリックします。 クライアント シークレット追加


ここで出てくる の文字列が client_secret(後で行う oauth2-proxy 設定項目)の文字列になります。(シークレット ID の方ではありません。)
二度と表示されないため、ここで、メモっておきます。

クライアント シークレットの値


グループ読み取りアクセス許可を追加します。

公式ドキュメントhttps://oauth2-proxy.github.io/oauth2-proxy/docs/configuration/oauth_provider/#azure-auth-provider)に指示がある手順です。

必須ではないかもしれません。今回の手順では、これを省略しても動作しました。

API アクセス許可 をクリックし、Microsoft Graph をクリックします。

API アクセス許可 Microsoft Graph


Group.Read.All にチェックを入れて、アクセス許可の更新 をクリックします。

Group.Read.Allにチェック アクセス許可の更新


***に管理者の同意を与えます をクリックして、はい をクリックします。

***に管理者の同意を与えます


***に管理者の同意を与えます はい

公式ドキュメントhttps://oauth2-proxy.github.io/oauth2-proxy/docs/configuration/oauth_provider/#azure-auth-provider)に指示がある手順です。

これを行わないと以下のエラーになります。

管理者の承認が必要

には、組織内のリソースへのアクセス許可が必要です。このアクセス許可を付与できるのは管理者のみです。アプリケーションを使用するには、まず管理者に依頼してこのアプリにアクセス許可を付与してください。

管理者の承認が必要


v2.0 Azure Auth エンドポイントを使用する予定がある場合は、マニフェスト をクリックして、マニフェストページに移動し、
"accessTokenAcceptedVersion": 2
をアプリ登録マニフェストファイルに設定し、保存 をクリックします。

accessTokenAcceptedVersion=2

公式ドキュメントhttps://oauth2-proxy.github.io/oauth2-proxy/docs/configuration/oauth_provider/#azure-auth-provider)に指示がある手順です。

今回の手順では、必須ではありません。


Azure AD - アプリの登録 図


VSCode 拡張機能 Go インストール

SSH Remote 機能により、oauth2-proxy の Ubuntu に接続し、/opt/oauth2-proxy を開きます。

SSH Remote 機能の手順に関しては、別記事「Alpine Linux をインストールして VS Code Remote SSH してみた」を参考にしてください。

/opt/oauth2-proxy を開く


拡張機能 Go をインストールします。

拡張機能 Go をインストール


拡張機能 Go が必要とする gopls をインストールします。

# go install -v golang.org/x/tools/gopls@latest

gopls(Go Please/ゴプルス)は、Go 言語が公式にサポートしている Language Server です。これは、エディタや統合開発環境(IDE)がソースコードを解析し、コード補完やシンボルのリネームなどの機能を提供するためのツールです。

インストールが必要とメッセージが表示される場合、Install をクリックしても同じことです。

gopls(Go Please/ゴプルス)Install をクリック


VSCode 拡張機能 Go インストール 図


Nginx oauth2-proxy 用設定追加

Nginx から oauth2-proxy へ通信が行われるように設定します。

# mkdir -p /var/log/webapps/oauth2-proxy

自己署名証明書と秘密鍵を作成し、配置します。

# openssl genrsa -out ca.key 2048
# openssl req -new -key ca.key -out ca.csr
Country Name (2 letter code) [AU]:JP
State or Province Name (full name) [Some-State]:Aichi
Locality Name (eg, city) []:Toyota
Organization Name (eg, company) [Internet Widgits Pty Ltd]:
Organizational Unit Name (eg, section) []:
Common Name (e.g. server FQDN or YOUR name) []:webapps-php.example.com
Email Address []:

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:
# echo "subjectAltName=DNS:*.example.com,IP:192.168.11.9" > san.txt
# openssl x509 -req -days 365 -in ca.csr -signkey ca.key -out ca.crt -extfile san.txt
Signature ok
subject=C = JP, ST = Aichi, L = Toyota, O = Default Company Ltd, CN = webapp.example.com
Getting Private key
# mkdir -p /etc/pki/tls/certs
# mkdir /etc/pki/tls/private
# mv ca.crt /etc/pki/tls/certs/oauth2-proxy.crt
# mv ca.key /etc/pki/tls/private/oauth2-proxy.key
# mv ca.csr /etc/pki/tls/private/oauth2-proxy.csr

Nginx の設定を追加します。

# vi /etc/nginx/conf.d/oauth2-proxy.conf
/etc/nginx/conf.d/oauth2-proxy.conf
server # サーバーブロックの開始。このブロック内の設定は、特定のサーバーまたは仮想ホストに適用されます。
{
  listen 443 ssl; # サーバーが443ポートでSSL接続をリッスンするように指示します。
  ssl_certificate /etc/pki/tls/certs/oauth2-proxy.crt; # SSL証明書のパスを指定します。
  ssl_certificate_key /etc/pki/tls/private/oauth2-proxy.key; # SSL証明書の秘密鍵のパスを指定します。
  server_name _; # サーバー名を指定します。ここではワイルドカード(_)が使用されています。
  access_log /var/log/webapps/oauth2-proxy/access.log; # アクセスログの出力先を指定します。
  error_log /var/log/webapps/oauth2-proxy/error.log; # エラーログの出力先を指定します。

  proxy_buffer_size 8k; # プロキシバッファのサイズを8KBに設定します。
  resolver 127.0.0.53 ipv6=off; # DNSリゾルバとして127.0.0.53を使用し、IPv6を無効にします。

  location / # ロケーションブロックの開始。このブロック内の設定は、特定のURLパターンに適用されます。
  {
    auth_request /oauth2/auth; # 認証リクエストを指定したURLに送信します。
    error_page 401 = /oauth2/sign_in?rd=$request_uri; # 401エラー(未認証)が発生した場合のリダイレクト先を指定します。
    index index.html index.htm index.php; # ディレクトリインデックスとして使用するファイルを指定します。
    proxy_pass http://$host; # リクエストを指定したプロキシサーバーに転送します。
    #proxy_ssl_verify off; # プロキシサーバーのSSL証明書の検証を無効にします(コメントアウトされています)。
  }

  location /oauth2/ { # /oauth2/ ロケーションブロックの開始。
    proxy_pass http://127.0.0.1:4180; # リクエストを指定したoauth2-proxyサーバーに転送します。
    proxy_set_header Host $host; # プロキシリクエストのHostヘッダーを設定します。
    proxy_set_header X-Real-IP $remote_addr; # プロキシリクエストのX-Real-IPヘッダーを設定します。
    proxy_set_header X-Scheme $scheme; # プロキシリクエストのX-Schemeヘッダーを設定します。
  }

  location /oauth2/auth { # /oauth2/auth ロケーションブロックの開始。
    proxy_pass http://127.0.0.1:4180; # リクエストを指定したoauth2-proxyサーバーに転送します。
    proxy_set_header Host $host; # プロキシリクエストのHostヘッダーを設定します。
    proxy_set_header X-Real-IP $remote_addr; # プロキシリクエストのX-Real-IPヘッダーを設定します。
    proxy_set_header X-Scheme $scheme; # プロキシリクエストのX-Schemeヘッダーを設定します。
    proxy_set_header Content-Length ""; # プロキシリクエストのContent-Lengthヘッダーを空に設定します。
    proxy_pass_request_body off; # プロキシリクエストのボディの転送を無効にします。
  }
}

resolver 127.0.0.53 ipv6=off; の部分は、/etc/hosts を読み取る設定です。

今回の場合、webapps-php.example.com の名前解決のために必要です。

error_page 401 = /oauth2/sign_in?rd=$request_uri;

?rb=の部分は、認証通過後、元々アクセスしに来た URI に戻るために必要です。

これが無いと、以下の例のように / に戻ります。

例:

https://webapps-php.example.com/info.php

認証通過

https://webapps-php.example.com


Nginx の設定を反映します。

# systemctl restart nginx

Nginx oauth2-proxy 用設定追加 図


VSCode でデバッグ起動環境作成

今回、oauth2-proxy (の main.go)を F5(デバッグモード) で起動します。 起動するための準備になります。

main.go を開いて、 実行とデバッグlaunch.json ファイルを作成します をクリックします。

launch.json ファイルを作成します


Go: Launch Package をクリックします。

Go: Launch Package


launch.json が作成されたら準備完了です。

launch.json


とりあえず、main.go に戻って、F5 キーを押して起動します。
The "dlv" command is not available. Run "go install -v github.com/go-delve/delve/cmd/dlv@latest" to install.
と表示されるため、
Install をクリックします。

dlv Install

ここでインストールされる dlv(delve) は、Go 言語のデバッガです。単体でも機能しますが、VSCode と連携して VSCode で Go 言語のデバッグが可能になります。

go install -v github.com/go-delve/delve/cmd/dlv@latest でインストールしても構いません。

インストールし終えたら、F5 で起動!
といきたいところですが、今回の環境の場合、以下のエラーになり、起動できません。
Build Error: go build -o /opt/oauth2-proxy/__debug_bin2712808634 -gcflags all=-N -l .
# google.golang.org/grpc
/root/go/pkg/mod/google.golang.org/grpc@v1.58.3/server.go:2096:14: undefined: atomic.Int64
note: module requires Go 1.19 (exit status 2)

Go のバージョンが低すぎるため、ビルドエラーです。


Go 更新

Go を最新版に更新します。

# add-apt-repository ppa:longsleep/golang-backports -y
# apt update
# apt install golang-go -y
# go version
go version go1.21.4 linux/amd64

VSCode で
Tools (gopls, dlv) need recompiling to work with go version go1.21.4 linux/amd64
と表示されるため、
Update tools をクリックします。

Update tools


再び、F5 で main.go を起動します。
ビルドエラーは解消して、起動しようとするところまで進みますが、設定を全く指定していないため、以下のエラーになります。
[2024/01/04 21:07:24] [main.go:54] invalid configuration:
missing setting: cookie-secret
provider missing setting: client-id
missing setting: client-secret or client-secret-file
missing setting for email validation: email-domain or authenticated-emails-file required.
use email-domain=* to authorize all email addresses
Process 12927 has exited with status 1
Detaching


F5 で main.go を起動 図


oauth2-proxy 設定

oauth2-proxy の設定を行います。


起動オプションで設定もできますが、今回は、設定ファイルで設定します。


そのためには、まず、
--config=/opt/oauth2-proxy/oauth2-proxy.cfg オプションありで oauth-proxy を起動して、設定ファイル /opt/oauth2-proxy/oauth2-proxy.cfg を読み込むようにします。
オプションを付けてデバッグ起動したいので、launch.json に
"args": ["--config=/opt/oauth2-proxy/oauth2-proxy.cfg"],
を追加して、以下のようにします。

/opt/oauth2-proxy/.vscode/launch.json
{
    // IntelliSense を使用して利用可能な属性を学べます。
    // 既存の属性の説明をホバーして表示します。
    // 詳細情報は次を確認してください: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Launch Package",
            "type": "go",
            "request": "launch",
            "mode": "auto",
            "program": "${fileDirname}",
            "args": ["--config=/opt/oauth2-proxy/oauth2-proxy.cfg"],
        }
    ]
}

oauth2-proxy.cfg を編集します。

# head -c 24 /dev/urandom | base64
6s1wlAH00x80vPOftMiBwCzcEeyr23y4
# vi /opt/oauth2-proxy/oauth2-proxy.cfg
/opt/oauth2-proxy/oauth2-proxy.cfg
# Azure ADを使用して認証を行います。
provider = "azure"
# OAuth2 ProxyがHTTP/HTTPSクライアントを待ち受けるアドレスとポートを指定します。
http_address = "127.0.0.1:4180"
# 許可されるEメールのドメインを指定します。ここではワイルドカードが使われており、どのドメインのEメールアドレスも許可されます。
email_domains = ["*"]
# OAuth2 プロバイダに要求するスコープを指定します。"openid" は OpenID Connect フローを使用することを意味します。
scope = "openid"
# OAuth2プロキシが発行するCookieのシードに使われる値を指定します。
cookie_secret = "6s1wlAH00x80vPOftMiBwCzcEeyr23y4"
# この行はコメントアウトされていますが、もし有効化された場合、CookieがHTTPS接続でのみ送信されることを指定します。
#cookie_secure = false
# この行もコメントアウトされていますが、もし有効化された場合、セッションCookieが最小限の情報のみを含むようになります。
#session_cookie_minimal = true
# OpenID ConnectプロバイダのURLを指定します。
oidc_issuer_url = "https://login.microsoftonline.com/<Azure AD ディレクトリ (テナント) ID>/v2.0"
# OAuth2プロバイダに登録したクライアントのIDを指定します。
client_id = "<Azure AD アプリケーション (クライアント) ID>"
# OAuth2プロバイダに登録したクライアントシークレットを指定します。
client_secret = "<Azure AD 証明書とシークレットの「値」>"
# OAuth2プロバイダからの認証応答を受け取るためのリダイレクトURLを指定します。
redirect_url = "https://webapps-php.example.com/oauth2/callback"
# Azure AD のテナントIDを指定します。
azure_tenant = "<Azure AD ディレクトリ (テナント) ID>"
# プロバイダの選択ボタンをスキップするかどうかを指定します。true が指定されている場合、ユーザーは直接プロバイダのログインページにリダイレクトされます。
skip_provider_button = true

cookie_secret は、OAuth2 Proxy が発行する Cookie のシード(初期値)として使われます。

24 バイトのランダムなバイト列を base64 エンコードしたものを設定しています。

長すぎると、以下のエラーになります。

[2023/12/12 20:17:11] [main.go:54] invalid configuration:

cookie_secret must be 16, 24, or 32 bytes to create an AES cipher, but is 44 bytes

Process 11727 has exited with status 1

Detaching

起動時引数でも、同様の設定ができますが、設定項目の名称と異なるため、注意が必要です。

例:

設定の場合:email_domains

引数の場合:email-domain


oauth2-proxy 設定 図


動作確認

F5 で起動します。

F5 で main.go を起動成功

起動しました!


https://webapps-php.example.com/info.php にアクセスします。

info.phpにアクセス


Azure AD認証1


Azure AD認証2


info.php画面


成功!ヨシっ!

loading...