🪪

OpenSSHの証明書認証を試す

2024/11/03に公開

OpenSSHの証明書認証について

https://docs.redhat.com/ja/documentation/red_hat_enterprise_linux/6/html/deployment_guide/sec-using_openssh_certificate_authentication

ユーザ認証

  • CA秘密鍵で署名されたユーザ証明書(公開鍵)の署名情報を利用して認証する。
  • SSHサーバにはCA公開鍵を登録し、ユーザから送られてきユーザ証明書を検証する。

公開鍵をユーザホームディレクトリ配下の ~/.ssh/authorized_keys に記載しなくてよい。(むしろ置いてはいけない?)

ホスト認証

  • 接続先のSSHサーバが目的のホストであることを認証して詐称を防ぐ技術。
  • SSHサーバにはCA秘密鍵で署名したホストキーをインストール、SSHクライアントのユーザ領域にはCA公開鍵の情報を保管する。
  • SSHサーバから送られてきたホストキーが、信頼しているCA証明書で署名されたものを確認する。

~/.ssh/known_hosts にはSSHサーバのホストキーではなてく、CA公開鍵の情報を記載してやる。
→ 初回SSH接続時に通常だとホストキーのフィンガープリントが表示され「信頼しますか?」と表示されるところの処理が無くなる

テスト環境

サーバ側

Raspberry Pi OS > KVM > Debian12

$ cat /etc/os-release
PRETTY_NAME="Debian GNU/Linux 12 (bookworm)"
NAME="Debian GNU/Linux"
VERSION_ID="12"
VERSION="12 (bookworm)"
VERSION_CODENAME=bookworm
ID=debian
HOME_URL="https://www.debian.org/"
SUPPORT_URL="https://www.debian.org/support"
BUG_REPORT_URL="https://bugs.debian.org/"

$ cat /etc/debian_version
12.0

$ uname -a
Linux debian12 6.1.0-9-arm64 #1 SMP Debian 6.1.27-1 (2023-05-08) aarch64 GNU/Linux

クライアント側

Raspberry Pi OS > Docker > AmazonLinux2023

$ cat /etc/os-release
NAME="Amazon Linux"
VERSION="2023"
ID="amzn"
ID_LIKE="fedora"
VERSION_ID="2023"
PLATFORM_ID="platform:al2023"
PRETTY_NAME="Amazon Linux 2023.5.20240708"
ANSI_COLOR="0;33"
CPE_NAME="cpe:2.3:o:amazon:amazon_linux:2023"
HOME_URL="https://aws.amazon.com/linux/amazon-linux-2023/"
DOCUMENTATION_URL="https://docs.aws.amazon.com/linux/"
SUPPORT_URL="https://aws.amazon.com/premiumsupport/"
BUG_REPORT_URL="https://github.com/amazonlinux/amazon-linux-2023"
VENDOR_NAME="AWS"
VENDOR_URL="https://aws.amazon.com/"
SUPPORT_END="2028-03-15"

$ cat /etc/amazon-linux-release
Amazon Linux release 2023.5.20240708 (Amazon Linux)

$ uname -a
Linux e1c64185cb26 6.6.31+rpt-rpi-2712 #1 SMP PREEMPT Debian 1:6.6.31-1+rpt1 (2024-05-29) aarch64 aarch64 aarch64 GNU/Linux

普通の公開鍵認証

クライアントの鍵ペアを作成

$ ssh-keygen -q -t ed25519
Enter file in which to save the key (/home/user/.ssh/id_ed25519):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:

$ ls -l ~/.ssh/id_ed25519*
-rw------- 1 user user 399 Nov  2 06:13 /home/user/.ssh/id_ed25519
-rw-r--r-- 1 user user  95 Nov  2 06:13 /home/user/.ssh/id_ed25519.pub

公開鍵を ~/.ssh/authorized_keys に追記

$ cat ~/.ssh/id_ed25519.pub | tee -a ~/.ssh/authorized_keys
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHylCHNgAqZNJrunlbek2+PiNNU/9jo/XjiJToRquOOq user@debian12

接続テスト

ssh -i ./id_ed25519 user@192.168.11.120

このときサーバ側では以下のようなログが出る。

# journalctl -u ssh
Nov 02 15:10:41 debian12 sshd[2953]: Accepted publickey for user from 192.168.11.250 port 55696 ssh2: ED25519 SHA256:tPR4rIIfnnh7gHp9sqNnrDGv2nukqx8QwlS2SxTC1Os

パスワード認証を禁止したい場合(証明書認証に失敗したあとパスワード認証にフォールバックしたくない場合)は設定を変更する

# sed -i -e '/#PasswordAuthentication/ s/.*/PasswordAuthentication no/' /etc/ssh/sshd_config

# systemctl restart ssh

証明書認証の準備

CA証明書作成

CA証明書(署名するための鍵ペア)を作成する

$ ssh-keygen -q -t ed25519 -f ca.key
Enter passphrase (empty for no passphrase):
Enter same passphrase again:

$ ls -l ca.key*
-rw------- 1 root root 399 Nov  2 06:06 ca.key
-rw-r--r-- 1 root root  95 Nov  2 06:06 ca.key.pub

CA証明書と言われるが、ただのSSH鍵ペアである。

ユーザ証明書作成

クライアントの公開鍵に署名してユーザ証明書を作成する。

シリアルと有効期限を指定する例

$ ssh-keygen -s ca.key -I certificate-test -n user -z $(date +%Y%m%d%H%M) -V +365d id_ed25519
Signed user key id_ed25519-cert.pub: id "certificate-test" serial 202411020544 for user valid from 2024-11-02T05:43:00 to 2025-11-02T05:44:56
    
$ ssh-keygen -L -f id_ed25519-cert.pub
id_ed25519-cert.pub:
        Type: ssh-ed25519-cert-v01@openssh.com user certificate
        Public key: ED25519-CERT SHA256:tPR4rIIfnnh7gHp9sqNnrDGv2nukqx8QwlS2SxTC1Os
        Signing CA: ED25519 SHA256:p12hRLK8xDAv+ArfcrZS1loLBUXkTC3JRtlRyYZvpwQ (using ssh-ed25519)
        Key ID: "certificate-test"
        Serial: 202411020544
        Valid: from 2024-11-02T05:43:00 to 2025-11-02T05:44:56
        Principals:
                user
        Critical Options: (none)
        Extensions:
                permit-X11-forwarding
                permit-agent-forwarding
                permit-port-forwarding
                permit-pty
                permit-user-rc
  • 証明書なので有効期限は設定したい。
  • 明示的に無効化するにはシリアルの登録が必要
  • ここでは指定していないが、接続元ホストを限定するオプションもあるようだ。

作成した証明書をユーザに配布する。

CA公開鍵の配置

SSHサーバにCA公開鍵を配置する。

# cp ~user/work/ca.key.pub /etc/ssh/ca.key.pub
# chown root: /etc/ssh/ca.key.pub

# echo "TrustedUserCAKeys /etc/ssh/ca.key.pub" | tee -a /etc/ssh/sshd_config
TrustedUserCAKeys /etc/ssh/ca.key.pub

ホスト証明書の作成

SSHホスト公開鍵をCA秘密鍵で署名して、ホスト証明書を作成する。

# ssh-keygen -s ~user/work/ca.key -h -I certificate-test /etc/ssh/ssh_host_ed25519_key.pub
Signed host key /etc/ssh/ssh_host_ed25519_key-cert.pub: id "certificate-test" serial 0 valid forever

# ssh-keygen -L -f /etc/ssh/ssh_host_ed25519_key-cert.pub
/etc/ssh/ssh_host_ed25519_key-cert.pub:
        Type: ssh-ed25519-cert-v01@openssh.com host certificate
        Public key: ED25519-CERT SHA256:RspUz/oxoReoB+85SbuIWjzAl9bfg8cNRzLjxtwEPfQ
        Signing CA: ED25519 SHA256:p12hRLK8xDAv+ArfcrZS1loLBUXkTC3JRtlRyYZvpwQ (using ssh-ed25519)
        Key ID: "certificate-test"
        Serial: 0
        Valid: forever
        Principals: (none)
        Critical Options: (none)
        Extensions: (none)

# echo "HostCertificate /etc/ssh/ssh_host_ed25519_key-cert.pub" | tee -a /etc/ssh/sshd_config
HostCertificate /etc/ssh/ssh_host_ed25519_key-cert.pub

sshd 再起動

# systemctl restart ssh

接続テスト

ユーザ側で信頼するCA公開鍵を設定

cat << EOF >> known_hosts
@cert-authority 192.168.11.120 $(cat ca.key.pub)
EOF

~/.ssh/authorized_keys の退避

~/.ssh/authorized_keys があると、強制的に通常の公開鍵認証になるようだ。
後述のKRLの仕組みも効かなくなるので、もしあれば予め削除する。

接続テスト

接続してみる

$ ssh -v -o UserKnownHostsFile=./known_hosts -i ./id_ed25519 -F /dev/null user@192.168.11.120

ホスト証明書を利用していることを示すデバッグ情報

debug1: Server host certificate: ssh-ed25519-cert-v01@openssh.com SHA256:RspUz/oxoReoB+85SbuIWjzAl9bfg8cNRzLjxtwEPfQ, serial 0 ID "certificate-test" CA ssh-ed25519 SHA256:p12hRLK8xDAv+ArfcrZS1loLBUXkTC3JRtlRyYZvpwQ valid forever
debug1: Host '192.168.11.120' is known and matches the ED25519-CERT host certificate.

ユーザ証明書を利用していることを示すデバッグ情報

debug1: Will attempt key: ./id_ed25519 ED25519-CERT SHA256:tPR4rIIfnnh7gHp9sqNnrDGv2nukqx8QwlS2SxTC1Os explicit
debug1: Offering public key: ./id_ed25519 ED25519-CERT SHA256:tPR4rIIfnnh7gHp9sqNnrDGv2nukqx8QwlS2SxTC1Os explicit
debug1: Server accepts key: ./id_ed25519 ED25519-CERT SHA256:tPR4rIIfnnh7gHp9sqNnrDGv2nukqx8QwlS2SxTC1Os explicit

サーバ側でもユーザ情報で認証されたことを示すログが出力される

# journalctl -u ssh
Nov 02 08:50:55 debian12 sshd[2734]: Accepted publickey for user from 192.168.11.250 port 57364 ssh2: ED25519-CERT SHA256:tPR4rIIfnnh7gHp9sqNnrDGv2nukqx8QwlS2SxTC1Os ID certificate-test (serial 0) CA ED25519 SHA256:p12hRLK8xDAv+ArfcrZS1>

ssh config を記載して利用すると簡単。

config
Host myalias
    HostName 192.168.11.120
    User user
    IdentityFile ./id_ed25519
    UserKnownHostsFile ./known_hosts

ssh config を利用した接続。

ssh -v -F ./config myalias

scp でも利用できる

$ scp -v -F config krl myalias:/tmp

証明書を無効化

無効化すべきユーザ証明書のシリアルのリストを作成する。

krl.list
serial:202411020544

作成したリストに基づいて、KRL(Key Revocation List)ファイルを作成する(※ krl 更新時は -u を指定する)

$ ssh-keygen -k -f krl -s ca.key.pub krl.list
Revoking from krl.list

作成したKRLファイルを確認する

$ ssh-keygen -Q -l -f krl
# KRL version 0
# Generated at 20241102T054916


# CA key ssh-ed25519 SHA256:p12hRLK8xDAv+ArfcrZS1loLBUXkTC3JRtlRyYZvpwQ
serial: 202411020544

$ ssh-keygen -Q -f krl id_ed25519-cert.pub
id_ed25519-cert.pub ((null)): REVOKED

サーバに配置して、sshd を再起動する。

# mv krl /etc/ssh
# echo "RevokedKeys /etc/ssh/krl" | tee -a /etc/ssh/sshd_config
RevokedKeys /etc/ssh/krl

# systemctl restart ssh

無効化したユーザ証明書でアクセスすると接続失敗となる。
サーバ側では以下のログが出る。

# journalctl -u ssh
Nov 03 06:08:20 debian12 sshd[3368]: error: Authentication key ED25519-CERT SHA256:tPR4rIIfnnh7gHp9sqNnrDGv2nukqx8QwlS2SxTC1Os revoked by file /etc/ssh/krl
Nov 03 06:08:20 debian12 sshd[3368]: Connection closed by authenticating user user 192.168.11.250 port 53332 [preauth]
GitHubで編集を提案

Discussion