Linux Cluster Account Management

目前部门的生产和开发测试机器大部分都在自维护的公有云环境,由于公司缺少可用的LDAP或Kerberos服务,我们在服务器账号上使用了两种管理策略:

  • 生产集群:工作账号+临时证书

  • 开发集群:个人账号+个人证书/密码

配合安全组的隔离,我们确保用户只能通过受控的渠道可以登录内网机器。跳板机禁止root用户登录,只接受证书验证,用户权限最小化;开发机和生产环境服务器同样只接受证书登录,但开发机为了让用户使用 sudo 会增加个人密码的配置。开发集群通过Ansible和管理平台创建用户账号和同步个人证书密码,生产集群部署 Vault 来完成临时证书的签发管理。

design

开发集群

运维或管理平台负责将用户证书及密码同步到开发机,视情况授予 sudo 权限,开发用户通过本地SSH配置上加入对应跳板机设置,实现本地直接通过域名(内网域名,需配合内网DNS)登录目标主机:

Host *.cn-zhangjiakou.codeb2cc.com
    ProxyJump bastion.cn-zhangjiakou
    User XXX
    IdentityFile ~/.ssh/id_rsa
  
Host bastion.cn-zhangjiakou
    Hostname A.B.C.D
    User XXX
    IdentityFile ~/.ssh/id_rsa

建议跳板机上不保存用户本地证书,通过 ForwardAgent 的方式登录,具体实现方式根据客户端系统及SSH工具有所不同,Linux环境下可参考:

$ eval "$(ssh-agent)"         # 启动 ssh-agent 并设置 SSH_AUTH_SOCK SSH_AGENT_PID 环境变量
$ ssh-add ~/.ssh/id_rsa       # 添加本地密钥
$ ssh HOST

生产集群

生产集群从业务延续性及运维角度出发,原则上不创建个人账号,根据具体需求创建工作账号。用户需要登陆到生产集群时,通过在跳板机上获取的临时证书(对跳板机上的用户证书签名,非用户本地证书)进行身份验证,临时证书由机房的 Vault 服务提供,大致的部署流程:

  • 生产集群主机上部署从Vault获得的SSH CA证书,配置 TrustedUserCAKeysAuthorizedPrincipalsFile

  • 跳板机上为root用户(或其他负责签发的账号)登录Vault,配置每日任务(本地cron或中控远程执行)刷新 Vault Token

  • 跳板机上为用户创建SSH证书,如 id_rsa 和 id_rsa.pub

  • 跳板机上部署签名脚本(/root/vault-sign),在PAM配置中(/etc/pam.d/sshd)加入登录触发操作:

session    optional     pam_exec.so seteuid /root/vault-sign

用户登录过程:

  • 用户通过本地证书登录跳板机,触发 /root/vault-sign 脚本执行,脚本根据用户身份(本地硬编码静态配置或调用管理平台接口)决定是否向Vault申请临时证书,并在申请中指定登录的工作账号、用户ID及用途(Principals)。若决定授权,签发证书 id_rsa-cert.pub 到用户默认目录下

  • 用户通过指定工作账号的方式登录到生产集群主机:ssh role@host

  • 目标机器根据证书有效期和证书Principals决定是否登录成功

操作审计

虽然用户都是通过一个工作账号登陆到主机,但由于可以在证书中设置用户ID,因此系统日志中对会话有明确的记录:“Accepted publickey for root from IP port PORT ssh2: RSA-CERT ID XXX (serial XXX) CA RSA SHA256:XXX”,用户的行为可以被追溯审计。

临时授权/部分授权

跳板机上的 /root/vault-sign 脚本集中管理了向哪些用户签发临时证书,临时授权可以通过脚本中更新临时用户名单或远程接口获取的方式实现 跳板机上签发证书时指定临时或独立的Principals,在部分授权机器的 AuthorizedPrincipalsFile 文件中加入该Principals,即可实现某工作账号的部分主机授权。如证书签发root用户,Principal指定 temporary-use ,只在限定机器上的 AuthorizedPrincipalsFile 目录下root文件中加入 temporary-use ,则用户只可以通过该证书登录限定机器的root账号。

实例

一个静态管理的签名脚本例子:

#!/bin/sh

# /etc/pam.d/sshd
# session    optional     pam_exec.so seteuid /root/vault-sign

export VAULT_ADDR="https://vault.cn-zhangjiakou.codeb2cc.com"
export PATH=/usr/sbin:$PATH

read -r -d '' AS_ROOT <<-"EOT"
alice
bob
EOT

read -r -d '' TEMP_ROOT <<-"EOT"
cindy
EOT

# PAM_TYPE: Use this to detect user login or logout
# PAM_USER: Which user be login
# PAM_RHOST: Remote user ip
# PAM_SERVICE
# PAM_TTY
if [ $PAM_TYPE == 'open_session' ]; then
    # Principal: as-root
    for USER in $AS_ROOT; do
        if [[ $USER == "${PAM_USER}" ]] && [[ -f "/home/$PAM_USER/.ssh/id_rsa.pub" ]]; then
            vault write -field=signed_key ssh/sign/ops \
                public_key=@/home/$PAM_USER/.ssh/id_rsa.pub \
                valid_principals=as-root \
                key_id=$PAM_USER > /home/$PAM_USER/.ssh/id_rsa-cert.pub
            if [ $? -eq 0 ]; then
                chown `id -u $PAM_USER`:`id -g $PAM_USER` /home/$PAM_USER/.ssh/id_rsa-cert.pub
            fi
            break
        fi
    done

    # Principal: temp-root
    for USER in $TEMP_ROOT; do
        if [[ $USER == "${PAM_USER}" ]] && [[ -f "/home/$PAM_USER/.ssh/id_rsa.pub" ]]; then
            vault write -field=signed_key ssh/sign/ops \
                public_key=@/home/$PAM_USER/.ssh/id_rsa.pub \
                valid_principals=temp-root \
                key_id=$PAM_USER > /home/$PAM_USER/.ssh/id_rsa-cert.pub
            if [ $? -eq 0 ]; then
                chown `id -u $PAM_USER`:`id -g $PAM_USER` /home/$PAM_USER/.ssh/id_rsa-cert.pub
            fi
            break
        fi
    done
elif [ $PAM_TYPE == 'close_session' ]; then
	# Revoke key maybe?
	:
fi