📌

【OPKSSH】SSHのシングルサインオンを、OPKSSHとEntra IDで実現する

2025/04/06に公開

前置き

2025年3月に、Cloudflare社がOPKSSHのオープンソース化を発表 しました。物凄くざっくり説明すると、既存のOIDC・OpenSSHの仕様を上手く応用して、手動のSSH鍵管理を不要にしたプロダクトです。技術仕様については、前述のCloudflare社ブログを読んでください。

OPKSSH(github)設定例 を読むと、Entra IDはデフォルト設定を利用すれば簡単に実現できそうに見えます。しかしこれは 個人用Microsoftアカウント の設定であり、多くの方が期待している「組織用テナントでのSSO」を実現するには、一手間加える必要があります。

2025年4月時点で「加えるべき一手間」に関する情報がどこにもなかったので、花見を楽しんだ後の時間で確かめました。

事前準備

IdP(Microsoft Entra ID テナント)

  • テナント作成済の場合、アプリケーション管理者 以上の権限を持つアカウントを用意してください。
  • テナント未作成の場合、Microsoft Entra のプランと価格 から作成してください。プランは『Microsoft Entra ID P1(2025年4月現在、1ユーザ 899円+消費税/月)』で大丈夫です。(最初の1か月は無料)

SSH Server(RHEL9.5)

  • 任意のLinuxサーバを用意します。今回は RHEL9.5 を使用しました。
  • 2025年4月時点のサポート対象は Ubuntu・CentOS のみですが、sshd_configAuthorizedKeysCommandAuthorizedKeysCommandUser が使えるOpenSSHがインストールされていれば、問題なく動作するはずです。(少なくとも RHEL9.5 では動作しました)

SSH Client(Windows11)

任意の Linux・Windows・Mac クライアントを用意します。今回は Windows11 を使用しました。

設定手順

IdP(Microsoft Entra ID テナント)

アプリの新規作成

  1. Azure Portal にサインイン。
  2. Microsoft Entra ID > 管理 > アプリの登録 > 新規登録 をクリック。
  3. 以下項目を入力し 登録 をクリック。
項目名 入力値
名前 OpenPubKey SSH を入力(任意の名前でOK)
アカウントの種類 この組織ディレクトリのみに含まれるアカウント を選択
リダイレクトURI - プラットフォーム パブリック クライアント/ネイティブ(モバイルとデスクトップ) を選択
リダイレクトURI - URI http://localhost:3000/login-callback を入力

Entra ID:アプリの新規作成

  1. 概要 から ディレクトリ(テナント)IDアプリケーション(クライアント)ID をコピーしておく。

テナントID・クライアントIDの取得

SSH Server(RHEL9.5)

install.sh を参考に、実運用向けにアレンジしました。

1. ユーザ・グループ作成

  • グループ

    groupadd --system opksshuser
    
  • ユーザ

    useradd -r -M -s /sbin/nologin -g opksshuser opksshuser
    

2. インストール

  • バイナリダウンロード

    wget -q --show-progress -O /usr/local/bin/opkssh https://github.com/openpubkey/opkssh/releases/latest/download/opkssh-linux-amd64
    
  • 権限設定

    chmod 755 /usr/local/bin/opkssh && chown root:opksshuser /usr/local/bin/opkssh
    
  • コンテキスト設定

    restorecon /usr/local/bin/opkssh
    

3. SELinux設定

  • TE(Type Enforcement)ファイル作成

    cat << 'EOF' > /tmp/opkssh.te
    module opkssh 1.0;
    
    
    require {
            type sshd_t;
            type var_log_t;
            type ssh_exec_t;
            type http_port_t;
            type sudo_exec_t;
            class file { append execute execute_no_trans open read map };
            class tcp_socket name_connect;
    }
    
    
    # We need to allow the AuthorizedKeysCommand opkssh process launched by sshd to:
    
    # 1. Make TCP connections to ports labeled http_port_t. This is so opkssh can download the public keys of the OpenID providers.
    allow sshd_t http_port_t:tcp_socket name_connect;
    
    # 2. Needed to allow opkssh to call `ssh -V` to determine if the version is supported by opkssh
    allow sshd_t ssh_exec_t:file { execute execute_no_trans open read map };
    
    # 3. Needed to allow opkssh to call `sudo opkssh readhome` to read the policy file in the user's home directory
    allow sshd_t sudo_exec_t:file { execute execute_no_trans open read map };
    
    # 4. Needed to allow opkssh to write to its log file
    allow sshd_t var_log_t:file { open append };
    EOF
    
  • モジュールファイルの作成

    checkmodule -M -m -o /tmp/opkssh.mod /tmp/opkssh.te
    
  • ポリシーパッケージの作成

    semodule_package -o /tmp/opkssh.pp -m /tmp/opkssh.mod
    
  • カーネルへのパッケージ読み込み(設定反映)

    semodule -i /tmp/opkssh.pp
    
  • 不要ファイルの削除

    rm -f /tmp/opkssh.*
    

4. sudo設定

  • 定義作成

    cat << EOF > /etc/sudoers.d/opkssh
    opksshuser ALL=(ALL) NOPASSWD: /usr/local/bin/opkssh readhome *
    EOF
    
  • 権限設定

    chmod 440 /etc/sudoers.d/opkssh
    

5. ログ設定

  • 空ログファイル作成

    touch /var/log/opkssh.log && chown root:opksshuser /var/log/opkssh.log && chmod 660 /var/log/opkssh.log
    
  • ログローテ設定作成

    cat << EOF > /etc/logrotate.d/opkssh
    /var/log/opkssh.log
    {
        daily 
        ifempty
        compress
        rotate 7
        dateext
        dateformat _%Y-%m-%d
        missingok
    }
    EOF
    
  • ログローテ設定確認

    logrotate -d /etc/logrotate.d/opkssh ; echo $?
    

6. OPKSSH設定

作成したアプリのテナントID・クライアントIDを、あらかじめ変数に設定しておきます。

TENANT_ID="aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"
CLIENT_ID="bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb"

/etc/opk/providers

認証を許可するIdPを定義します。詳細は OPKSSH - /etc/opk/providers をご確認ください。

  • ディレクトリ作成(存在しない場合)

    mkdir -m 755 /etc/opk
    
  • 定義作成

    cat << EOF > /etc/opk/providers
    https://login.microsoftonline.com/${TENANT_ID}/v2.0 ${CLIENT_ID} 24h
    EOF
    
  • 権限設定

    chown root:opksshuser /etc/opk/providers && chmod 640 /etc/opk/providers
    

/etc/opk/auth_id

Linuxユーザ を引き継げる EntraIDユーザ(UPN) を定義します。所謂認可の設定です。詳細は OPKSSH - /etc/opk/auth_id をご確認ください。

  • ディレクトリ作成(存在しない場合)

    mkdir -m 755 /etc/opk
    
  • 定義作成

    cat << EOF > /etc/opk/auth_id
    maihai maihai@example.jp https://login.microsoftonline.com/${TENANT_ID}/v2.0
    EOF
    
  • 権限設定

    chown root:opksshuser /etc/opk/auth_id && chmod 640 /etc/opk/auth_id
    

/etc/skel/.opk/auth_id

今回は ~/.opk/auth_id を使用しませんが、ファイルが存在しないと /var/log/opkssh.log で以下エラーを毎回吐きます。出力が気になる方は、以下ディレクトリとファイルを追加します。

2025/04/06 16:24:09 warning: failed to load user policy: failed to read user policy file /home/xxxx/.opk/auth_id: error reading xxxx home policy using command /usr/bin/sudo -n /usr/local/bin/opkssh readhome xxxx got output Failed to read user's home policy file: failed to open /home/xxxx/.opk/auth_id, open /home/xxxx/.opk/auth_id: no such file or directory
Error: failed to open /home/xxxx/.opk/auth_id, open /home/xxxx/.opk/auth_id: no such file or directory and err exit status 1
  • ディレクトリ作成

    mkdir -m 755 /etc/skel/.opk
    
  • ファイル作成

    touch /etc/skel/.opk/auth_id && chmod 640 /etc/skel/.opk/auth_id
    

OpenSSH設定

今回は グループ sso_entra に所属するユーザ のみ、Entra IDでのSSOを許可します。

  • グループ作成

    groupadd sso_entra
    
  • 対象ユーザのグループ追加

    usermod -aG sso_entra ユーザ名
    
  • 定義作成

    cat << EOF > /etc/ssh/sshd_config.d/05-opkssh.conf
    Match Group sso_entra
      AllowUsers *
      PubkeyAuthentication yes
      AuthorizedKeysCommand /usr/local/bin/opkssh verify %u %k %t
      AuthorizedKeysCommandUser opksshuser
    EOF
    
  • 定義確認

    sshd -T -C user=ユーザ名 \
    | grep -i -e AllowUsers -e PubkeyAuthentication -e AuthorizedKeysCommand
    
  • sshd再起動

    systemctl restart sshd
    

SSH Client(Windows11)

コマンドラインは、すべてPowerShellで実行します。(管理者権限不要)

1. インストール

OPKSSHは、管理者権限不要でインストールできます。

winget install openpubkey.opkssh

2. Powershellプロファイルの編集

opkssh login 実行時のオプションを、あらかじめプロファイルに定義しておきます。

  • プロファイル新規作成(存在しない場合)

    New-Item -ItemType File -Path $PROFILE -Force
    
  • プロファイルのパス確認

    $PROFILE
    
  • プロファイルの編集(以下を追加)

    $tenant_id = "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"
    $client_id = "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb"
    $issuer = "https://login.microsoftonline.com/$tenant_id/v2.0"
    $provider = "$issuer,$client_id"
    

3. 既存鍵ペアの退避

OPKSSHは、IdP(Entra ID)ログイン成功後に以下ファイル名で鍵ペアを生成します。
上書きされたくない場合は退避してください。

  • 秘密鍵 : ~/.ssh/id_ecdsa
  • 公開鍵 : ~/.ssh/id_ecdsa.pub

ログイン手順

1. Entra ID

  1. SSH Client(Windows11)でPowershellを開き、以下コマンドを実行する。

    opkssh login --provider $provider
    
  2. Entra IDのログイン画面が自動で開くので、自分のユーザでログインする。

    Entra ID:ログイン

  3. 組織の代理として同意する にチェックを入れ、承諾 をクリックする。(使用する権限は後述)

    Entra ID:アクセス許可同意

  4. You may now close this window が表示されたら、ブラウザを閉じる。

    Entra ID:ログイン成功

2. SSH

通常のSSHログインと同様、以下コマンドでログインします。

ssh ユーザ名@xxx.xxx.xxx.xxx

補足

Entra IDで要求されるアクセス許可

OPKSSHのSSOは、以下のアクセス許可を要求します。

Entra ID:アクセス許可一覧

アクセス制御について

実運用では、OPKSSH・Entra IDアプリ双方でアクセス制御を行うべきです。
OPKSSHの認可設定に不備があっても、Entra IDで認証自体を拒否できます。

OPKSSH

/etc/opk/auth_id で、ユーザの認可を制限します。

Entra ID

セキュリティグループ(メーリス)単位 で、ユーザの認証を制限します。

  1. Microsoft Entra ID > 管理 > エンタープライズアプリケーション > 作成したアプリ をクリック。
  2. 管理 > ユーザとグループ をクリック。
  3. ユーザーまたはグループの追加 をクリックし、対象グループを追加する。

Entra ID認証時に、リダイレクトエラーが発生した場合

AADSTS50011: 要求で指定されたリダイレクト URI が一致しません を参考に、正しいリダイレクトURIを確認してください。

Discussion