linuxのrootをSSHの証明書認証を使って共有し CERT IDで個々のユーザーを識別する

Linuxのユーザーの管理で何かより良い方法がないかと検索していたら Scalable and secure access with SSH - Facebook CodeSSHの証明書認証を使った方法が紹介されていたので検証してみました。


上記の記事をざっくりまとめると
  1. rootを共通アカウントとして使用する
  2. 証明書認証のCERT IDを使用し個々のユーザーを識別する
  3. ログは全て集約しているので簡単に分析できる
  4. AuthorizedPrincipalsFileを使ってアクセス制御を行う
    といった感じでしょうか。

証明書認証には以下の理由からHashicorpのVaultを使ってみることにしました。
  1. 署名に必要な秘密鍵を公開しなくて済むので、ユーザーに公開鍵への署名を任せられます。
  2. ldap auth methodを使いActive Directoryに認証を任せると、token_display_name(今回の場合はldap-usernameという形式)がCERT IDにセットされるようになり、ユーザーがCERT IDを変更することが出来なくなります。
  3. Policyで証明書発行時に必要なroleへのアクセスを制限可能になります。

以下を参考に環境を構築しておきます。

Signed SSH Certificates - SSH - Secrets Engines - Vault by HashiCorp

LDAP - Auth Methods - Vault by HashiCorp


ttlの変更

tokenのttlが長すぎなので短くしておきます。

[root@localhost ~]# vault read sys/auth/ldap/tune
Key                  Value
---                  -----
default_lease_ttl    768h
force_no_cache       false
max_lease_ttl        768h
token_type           default-service
[root@localhost ~]# vault write sys/auth/ldap/tune default_lease_ttl=10m max_lease_ttl=30m
Success! Data written to: sys/auth/ldap/tune
[root@localhost ~]# vault read sys/auth/ldap/tune
Key                  Value
---                  -----
default_lease_ttl    10m
force_no_cache       false
max_lease_ttl        30m
token_type           default-service
[root@localhost ~]#

roleの作成

rootというロールを作成し登録します。allow_user_key_idsをtrueにするとCERT IDをユーザーが更新できてしまうのでfalseにしておきます。
allow_usersにはリモートログイン先のユーザー名を指定します。defaultのままだと"*"となりリモートからログイン可能なユーザー全てを指定出来てしまうので注意が必要です。
有効期限は1時間としました。

[root@localhost ~]# vault write ssh-client-signer/roles/root -<<"EOH"
  {
     "allow_user_certificates": true,
     "allow_user_key_ids": "false",
     "allowed_users": "root",
     "default_extensions": [
      {
         "permit-pty": ""
       }
     ],
     "key_type": "ca",
     "default_user": "root",
     "ttl": "1h"
   }
EOH
Success! Data written to: ssh-client-signer/roles/root
[root@localhost vault.d]# vault read ssh-client-signer/roles/root
Key                         Value
---                         -----
allow_bare_domains          false
allow_host_certificates     false
allow_subdomains            false
allow_user_certificates     true
allow_user_key_ids          false
allowed_critical_options    n/a
allowed_domains             n/a
allowed_extensions          n/a
allowed_users               root
default_critical_options    map[]
default_extensions          map[permit-pty:]
default_user                root
key_bits                    0
key_id_format               n/a
key_type                    ca
max_ttl                     0s
ttl                         1h
[root@localhost vault.d]# 

ポリシーの割り当て

上で作成したロールに対するポリシーを設定し、Active Directoryのmnishikizawaユーザーにポリシーを割り当てます。

[root@localhost ~]# vault policy write ssh-user-root -<<"EOH"
> path "ssh-client-signer/sign/root"
> {
>   capabilities = ["update"]
> }
> EOH
Success! Uploaded policy: ssh-user-root
[root@localhost vault.d]# vault policy read ssh-user-root
# Write ssh certificates
path "ssh-client-signer/sign/root"
{
  capabilities = ["update"]
}
[root@localhost vault.d]# vault write auth/ldap/users/mnishikizawa policies=ssh-user-root
Success! Data written to: auth/ldap/users/mnishikizawa
[root@localhost vault.d]# vault read auth/ldap/users/mnishikizawa
Key         Value
---         -----
groups      n/a
policies    [ssh-user-root]

証明書の発行

Active Directoryで認証を行い証明書を発行してからrootでSSHログインしてみます。

[vagrant@localhost ~]$ vault login -method=ldap username=mnishikizawa
Password (will be hidden):
Success! You are now authenticated. The token information displayed below
is already stored in the token helper. You do NOT need to run "vault login"
again. Future Vault requests will automatically use this token.

Key                    Value
---                    -----
token                  s.n0SrSqKIK5LxXyg3eGkCrxX6
token_accessor         WzMbzaSQGrD0MRjoKceF0fEO
token_duration         10m
token_renewable        true
token_policies         ["default" "ssh-user-root"]
identity_policies      []
policies               ["default" "ssh-user-root"]
token_meta_username    mnishikizawa
[vagrant@localhost ~]$ vault token lookup
Key                 Value
---                 -----
accessor            WzMbzaSQGrD0MRjoKceF0fEO
creation_time       1549593890
creation_ttl        10m
display_name        ldap-mnishikizawa
entity_id           935ade7e-d2f6-5cc1-9e27-6490a7f38441
expire_time         2019-02-08T02:54:50.066232798Z
explicit_max_ttl    0s
id                  s.n0SrSqKIK5LxXyg3eGkCrxX6
issue_time          2019-02-08T02:44:50.066232489Z
meta                map[username:mnishikizawa]
num_uses            0
orphan              true
path                auth/ldap/login/mnishikizawa
policies            [default ssh-user-root]
renewable           true
ttl                 9m58s
type                service
[vagrant@localhost ~]$ vault write -field=signed_key ssh-client-signer/sign/root public_key=@$HOME/.ssh/id_rsa.pub > ~/.ssh/id_rsa-cert.pub
[vagrant@localhost ~]$ ssh-keygen -Lf .ssh/id_rsa-cert.pub
.ssh/id_rsa-cert.pub:
        Type: ssh-rsa-cert-v01@openssh.com user certificate
        Public key: RSA-CERT SHA256:3c7XiI1ULfNbrCqk1icCG0KtuEeew4bBbi1I6gO5mSM
        Signing CA: RSA SHA256:4LJnUxaq/OiEIRd1TuEjEhyoV/J73Daep/VNVaQMmvc
        Key ID: "vault-ldap-mnishikizawa-ddced7888d542df35bac2aa4d627021b42adb8479ec386c16e2d48ea03b99923"
        Serial: 2420534201198412456
        Valid: from 2019-02-20T01:33:06 to 2019-02-20T02:33:36
        Principals:
                root
        Critical Options: (none)
        Extensions:
                permit-pty
[vagrant@localhost ~]$ ssh root@localhost
Last login: Wed Feb 20 01:34:35 2019
[root@localhost ~]# tail /var/log/secure
Feb 20 01:34:48 localhost sshd[9396]: Accepted publickey for root from ::1 port 47374 ssh2: RSA-CERT ID vault-ldap-mnishikizawa-ddced7888d542df35bac2aa4d627021b42adb8479ec386c16e2d48ea03b99923 (serial 2420534201198412456) CA RSA SHA256:4LJnUxaq/OiEIRd1TuEjEhyoV/J73Daep/VNVaQMmvc

secureログには
RSA-CERT ID vault-ldap-mnishikizawa-xxxxxx
が記録されrootを共有アカウントとしても個々のユーザーを識別できそうです。


principalsを使ったアクセス制限

AuthorizedPrincipalsFileを使って、設定されたprincipalのみアクセス可能なのか確認してみます。

SSH - Secrets Engines - HTTP API - Vault by HashiCorp

・ default_user (string: "")
For the CA type, if you wish this to be a valid principal, it must also be in allowed_users.

この説明通りdefault_userとallowed_usersに同じ値を設定します。

[root@localhost ~]# vault write ssh-client-signer/roles/root -<<"EOH"
> {
>      "allow_user_certificates": true,
>       "allow_user_key_ids": "false",
>       "allowed_users": "root,zone-webservers,zone-databases",
>       "default_extensions": [
>        {
>           "permit-pty": ""
>         }
>       ],
>       "key_type": "ca",
>       "default_user": "root,zone-webservers,zone-databases",
>       "ttl": "1h"
>     }
> EOH
Success! Data written to: ssh-client-signer/roles/root
[root@localhost ~]# vault read ssh-client-signer/roles/root
Key                         Value
---                         -----
allow_bare_domains          false
allow_host_certificates     false
allow_subdomains            false
allow_user_certificates     true
allow_user_key_ids          false
allowed_critical_options    n/a
allowed_domains             n/a
allowed_extensions          n/a
allowed_users               root,zone-webservers,zone-databases
default_critical_options    map[]
default_extensions          map[permit-pty:]
default_user                root,zone-webservers,zone-databases
key_bits                    0
key_id_format               n/a
key_type                    ca
max_ttl                     0s
ttl                         1h
[root@localhost ~]#

principalsがある場合とない場合
[vagrant@localhost ~]$ ssh-keygen -Lf .ssh/id_rsa-cert.pub
.ssh/id_rsa-cert.pub:
        Type: ssh-rsa-cert-v01@openssh.com user certificate
        Public key: RSA-CERT SHA256:3c7XiI1ULfNbrCqk1icCG0KtuEeew4bBbi1I6gO5mSM
        Signing CA: RSA SHA256:4LJnUxaq/OiEIRd1TuEjEhyoV/J73Daep/VNVaQMmvc
        Key ID: "vault-ldap-mnishikizawa-ddced7888d542df35bac2aa4d627021b42adb8479ec386c16e2d48ea03b99923"
        Serial: 13444262937296389838
        Valid: from 2019-02-20T05:16:42 to 2019-02-20T06:17:12
        Principals:
                root
                zone-databases
                zone-webservers
        Critical Options: (none)
        Extensions:
                permit-pty
[vagrant@localhost ~]$ sudo cat /etc/ssh/auth_principals/root
zone-webservers
zone-databases
[vagrant@localhost ~]$ ssh root@localhost
Last login: Wed Feb 20 05:31:38 2019 from ::1
[root@localhost ~]# tail /var/log/secure
Feb 20 05:32:33 localhost sshd[11072]: Accepted publickey for root from ::1 port 47498 
ssh2: RSA-CERT ID vault-ldap-mnishikizawa-ddced7888d542df35bac2aa4d627021b42adb8479ec386c16e2d48ea03b99923 (serial 13444262937296389838) CA RSA SHA256:4LJnUxaq/OiEIRd1TuEjEhyoV/J73Daep/VNVaQMmvc
[root@localhost ~]# logout
Connection to localhost closed.
[vagrant@localhost ~]$ sudo cat /etc/ssh/auth_principals/root
root-everywhere
[vagrant@localhost ~]$ ssh root@localhost
no such identity: /home/vagrant/.ssh/id_ed25519: No such file or directory
Permission denied (publickey,gssapi-keyex,gssapi-with-mic).
[vagrant@localhost ~]$ sudo tail /var/log/secure
Feb 20 05:33:38 localhost sshd[11135]: error: Certificate does not contain an authorized principal
Feb 20 05:33:38 localhost sshd[11135]: Connection closed by ::1 port 47500 [preauth]
[vagrant@localhost ~]$

期待通りの動作ですがzone-webservers,zone-databasesというユーザーが存在した場合、そのユーザーでログインできてしまうのが残念です。


まとめ
  • SSHアカウントを共有してもCERT IDで個々のユーザーを識別できる。
  • ユーザーをサーバー毎に個別に作成しなくてすむ。
  • secureログ、auditログの分析基盤があればアカウントを共有しても個々のユーザーのログイン情報を分析可能となり運用も楽になると思う。

参考

SSH - Secrets Engines - HTTP API - Vault by HashiCorp

OpenSSHの認証に証明書を使う方法|ConoHa VPSサポート

VaultのPolicyを使った運用 - Carpe Diem