首页 > 解决方案 > Jenkins SSH 主机/用户证书工作流程

问题描述

每次 Ansible 阶段运行时如何设置 SSH 证书签名过程?我有使用 Terraform 创建 VM 然后运行 ​​Ansible 的管道。在 cloud-init 阶段,会创建和签署主机密钥,并配置公共用户 CA 密钥。客户端/主机 CA 权限在 HashiCorp Vault 中配置。因此,此时,无论配置什么 VM,我都可以通过 SSH 连接到它们中的每一个,就像我@cert-authority *.example.com ecdsa-sha2-nistp521 AAAAB3NzaC1yc2EAAA.../etc/ssh/ssh_known_hosts. 我需要做的就是创建新密钥并对其进行签名,因为我的 TTL 很短。但这在 Jenkins 中不起作用。Jenkins 将所有 SSH 密钥存储在下面/var/lib/jenkins/.ssh,默认情况下没有任何内容。如果只是出于测试原因,我确实将我的个人用户密钥和证书 + ssh 配置文件复制到/var/lib/jenkins/.ssh,那么Jenkins就可以愉快的运行Ansible了。但是,每次我在我的 infra repo 中进行一些提交时,我都无法生成、签名和复制 jenkins 密钥。创建长寿证书也不好闻。

SSH 证书签名和轮换的惯用工作流程是什么?

标签: jenkinssshansible

解决方案


我想到了其他解决方案。

创建 bash 脚本/etc/vault/sign-jenkins-cert.sh

#cat <<EOT >> /etc/vault/sign-jenkins-cert.sh
#!/bin/bash

set -eu -o pipefail

VAULT_ADDR='https://vault.example.com'
SSH_PUB_KEY_PATH='/var/lib/jenkins/.ssh/id_ecdsa.pub'
SSH_CERT_PATH='/var/lib/jenkins/.ssh/id_ecdsa-cert.pub'
ROLE_ID='<jenkins-role-id>'  # At cloud-init/kickstart stage this should be baked in
SECRET_ID='<jenkins-secret-id>'  # At cloud-init/kickstart stage this should be baked in

main () {
  local VAULT_TOKEN=$(vault_signin "${ROLE_ID}" "${SECRET_ID}")
  local SSH_PUB_KEY=$(cat "${SSH_PUB_KEY_PATH}")

  sign_ssh_cert "${VAULT_TOKEN}" "${SSH_PUB_KEY}" "${SSH_CERT_PATH}"

  chmod 0640 "${SSH_CERT_PATH}"
}

vault_signin () {
  local ROLE_ID=$1
  local SECRET_ID=$2

  local RES=$(curl -s --request POST \
    --data '{"role_id": "'"${ROLE_ID}"'", "secret_id": "'"${SECRET_ID}"'"}' \
    ${VAULT_ADDR}/v1/auth/approle/login | jq -r .auth.client_token)

  local RT=$?
  if [ "$RT" == "0" ]; then
    echo $RES
  else
    echo "Login with role $ROLE_ID failed. RT:$RT $RES"
    echo ""
  fi
}

sign_ssh_cert () {
  local VAULT_TOKEN="$1";
  local PUB_KEY="$2";
  local CERT_PATH="$3";

  curl -s \
    --header "X-Vault-Token: ${VAULT_TOKEN}" \
    --request POST \
    --data '{"public_key": "'"${PUB_KEY}"'", "cert_type": "user"}' \
    ${VAULT_ADDR}/v1/ssh-client-signer/sign/clientrole | jq -r .data.signed_key > "${CERT_PATH}"
}

main "$@"; exit
#EOT

然后将权限设置为0644和 root 所有权。

sudo chown root. /etc/vault/sign-jenkins-cert.sh && sudo chmod 0644 /etc/vault/sign-jenkins-cert.sh

然后创建 Systemd 单元/etc/systemd/system/sign-jenkins-certificate.service

#cat <<EOT >> /etc/systemd/system/sign-jenkins-certificate.service
[Unit]
Description=Sign a new host cert on boot, then daily
[Service]
ExecStart=/bin/sh /etc/vault/sign-jenkins-cert.sh
Restart=on-failure
RestartSec=20
Type=forking
#EOT

还要设置权限0644和 root 所有权。

然后创建定时器单元/etc/systemd/system/sign-jenkins-certificate.timer

#cat <<EOT >> /etc/systemd/system/sign-jenkins-certificate.timer
[Unit]
Description=Sign a new host cert on boot, then daily
[Timer]
OnCalendar=daily
Persistent=true
Unit=sign-jenkins-certificate.service
[Install]
WantedBy=timers.target
#EOT

https://www.freedesktop.org/software/systemd/man/systemd.time.html#Calendar%20Events

核实

systemd-analyze verify /etc/systemd/system/sign-jenkins-certificate.timer

启用和启动计时器

systemctl enable sign-jenkins-certificate.timer && \
systemctl start sign-jenkins-certificate.timer && \
systemctl status sign-jenkins-certificate.timer

对于那些坐在强化代理后面的人,请确保您的代理不会阻止curlVault ACL 中的用户代理。

该解决方案使用 Vault AppRole 进行身份验证,使用 Systemd 运行签名服务,Jenkins 主机上不需要 Vault 代理。

这可能是有效的解决方案。但也许有更好的东西?


推荐阅读