보안 이야기

Vault - Signed SSH certificates를 이용한 서버 접근

지기(ZIGI) 2025. 7. 13. 10:05

HashiCorp Vault는 민감한 자격 증명, API 키, 암호화 키 등을 안전하게 저장하고 접근 제어를 제공하는 비밀 관리 시스템입니다. 그중에서도 SSH Secrets Engine은 SSH 접속을 보다 안전하게 제어할 수 있도록 지원하는 기능으로, OTP 방식 또는 SSH 인증서 서명 방식으로 서버 접근을 중앙에서 관리할 수 있습니다.

이번 포스팅에서는 Vault에 SSH Secrets Engine을 SSH 인증서 방식으로 서버에 접근하는 방법에 대한 내용을 다룹니다.


Vault 설치 

 - Vault Server, SSH Server, SSH Client 공통 - Vault 1.20.0(Latest) 설치

 - Ubuntu 24.04 기준

wget -O - https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg

echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(grep -oP '(?<=UBUNTU_CODENAME=).*' /etc/os-release || lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list

sudo apt update && sudo apt install vault

 


Vault Server 설정

 

데이터 디렉토리 생성

mkdir -p /opt/vault/data

 

서버 환경 설정 파일 작성( /etc/vault.d/config.hcl )

  • Vault 웹 UI 활성화(ui) 및 Vault를 외부에서 호출 가능한 API 주소(api_addr) 설정
  • Storage는 Vault의 데이터 저장 방식이고, 여기에서는 /opt/vault/data 디렉토리에 저장
  • listener는 TCP listener에서 사용하는 서비스 Port 및 Vault 서버가 어떤 IP 주소로 요청을 받을지 지정 
    tls-disable은 tls를 비화성화 하는 것이고, 실제 상용에서는 활성화해서 사용 필요.
ui            = true
api_addr      = "http://서비스IP:8200"

storage "file" {
  path = "/opt/vault/data"
}

listener "tcp" {
  address       = "0.0.0.0:8200"
  tls_disable = 1
}

 

 

Vault 서버 실행

  • Vault 서버를 위에서 작성한 환경 설정을 통해서 서버 구동
  • 본 포스팅에서는 백그라운드로 실행하는 것으로 진행.
sudo nohup vault server -config=/etc/vault.d/config.hcl > sudo /var/log/vault.log 2>&1 &

 

 

Vault 서버 초기화

  • Vault 서버 최초 초기화(Initalize)
  • Vault Unseal Key와 초기 Root Token(초기 관리자 접근을 위한 토큰) 생성
  • Unseal Key와 관련하여, -key-shares, -key-threshold 옵션 사용 가능하며, 옵션은 내용은 다음과 같습니다. 
    • -key-shares : 생성하는 Unseal 키 수 (default 5)
    • -key-threshold : 생성된  Unseal 키 중에서, Vault를 Unseal 하는 데 사용하는 키 수 (default 3)
export VAULT_ADDR="http://서비스IP:8200" # 실행 전 환경 변수 선언

#기본 실행
vault operator init

#옵션 사용 시
vault operator init -key-shares=5 -key-threshold=3

 

 

 

Vault 서버 잠금 해제

  • Vault init 이후, sealed 상태로, Vault 사용을 위해서 unseal을 수행
  • unseal은 -key-threshold에 선언된 만큼 진행해야 하면, unseal된 수량은 'Unseal Progress'에서 확인 가능
  • 별도 옵션이 없을 경우, -key-threshold=3 으로 되어서, 3번의 unseal 수행
  • unseal이 완료되면, 'Sealed'가 'true'에서 'false'로 변경 됨. 
vault operator unseal
#실행 후, unseal key를 입력

 

Unseal 1차 수행
Unseal 3차 수행 : 완료(sealed : false)

 

Vault 서버 로그인

  • Vault 사용을 위한 로그인 수행
  • 별도 로그인 대신에, VAULT_TOKEN 환경 변수에 token 값을 선언해서 사용하는 것도 가능
vault login [root token]

 

 

Vault Seret Engint 마운트
  • secrets enable로 Secret Engine을 활성화합니다.
  • -path 옵션을 이용해서, 마운트 지점을 설정하는 데, 이는 SSH Secret Engine이 사용할 논리 경로
  • -path 옵션을 사용해서, 서로 다른 설정을 여러 번 마운트 가능
    예를 들어서, 이번 포스팅에서 다루는 SSH 인증서 방식과 다음 포스팅에서 다룰 OTP 인증서 방식을 각각 구성 가능
vault secrets enable -path=ssh-client-signer ssh

 

SSH Secret Engine의 CA 키 생성

  - Vault가 내부적으로 사용할 SSH 인증서 서명용 키쌍 (private/public) 을 자동으로 생성

  - 이 키는 Vault가 클라이언트의 공개 키를 받아 SSH 인증서 형식으로 서명할 때 사용

  - Vault는 자체적으로 SSH CA 역할을 수행하게 되며,
    이후 발급된 인증서는 대상 서버의 TrustedUserCAKeys에 등록된 공개 키와 비교되어 접근을 제어

vault write ssh-client-signer/config/ca generate_signing_key=true

 

SSH CA 공개 키를 조회하기 위한 정책 설정

  • ssh-client-signer/config/ca 경로의 리소스(공개 키 포함)에 대해 읽기 권한만 허용
#policy.hcl
path "ssh-client-signer/config/ca" {
  capabilities = ["read"]
}

 

정책을 Vault에 등록
vault policy write ssh-pubkey-read policy.hcl

 

SSH Public Key Read를 위한 Token 생성(SSH Server에서 사용)
  • 앞서 만든 ssh-pubkey-read 정책을 받은 전용 토큰 생성
  • 이 토큰을 사용해서, SSH Server가 Vault에서 서명된 CA 공개키를 읽을 수 있음
vault token create -policy=ssh-pubkey-read

 

SSH-Client가 Vault에 CA 사인 요청을 할 수 있도록 설정
  • 클라이언트가 제출한 공개 키를 받아 인증서(서명된 키)로 발급해주는 기능을 제공
  • 기본 계정을 zigi로 설정하고, 인증서에 대한 TTL을 5분으로 지정
vault write ssh-client-signer/roles/my-role -<<"EOH"
{
  "algorithm_signer": "rsa-sha2-256",
  "allow_user_certificates": true,
  "allowed_users": "*",
  "allowed_extensions": "permit-pty,permit-port-forwarding",
  "default_extensions": [
    {
      "permit-pty": ""
    }
  ],
  "key_type": "ca",
  "default_user": "zigi",
  "ttl": "5m0s"
}
EOH

 

 

SSH 인증서 서명을 위한 정책 정의

  • SSH Client가 앞서 생성한 역할(role)을 통해 인증서 서명을 요청하기 위한 sign 경로의 update 권한 정의
#sign-policy.hcl
path "ssh-client-signer/sign/my-role" {
  capabilities = ["update"]
}

 

정책을 Vault에 등록용헌

vault policy write require-ssh-sign sign-policy.hcl

 

 
인증서 서명을 위한 Token 생성(SSH Client에서 사용)
  • 앞서 만든 require-ssh-sign 정책을 받은 전용 토큰 생성
  • 이 토큰을 사용해서, SSH Client가 Vault에 인증서 서명 요청
vault token create -policy=require-ssh-sign

 

 

 


SSH Server 설정

Vault에서 서명한 SSH 인증서를 신뢰하기 위해서, Vault의 CA 공개키를 가져와서 등록

Vault Server 환경 변수 설정
  • VAULT Token 값은 'SSH Public Key Read를 위한 Token 생성(SSH Server에서 사용)' 에서 확인한 Token 값 사용
export VAULT_ADDR=http://Vault_Serer_IP:8200
export VAULT_TOKEN="SSH Public Key Read Token"

설정 확인
env | grep VAULT

 

CA의 PublicKey를 SSH-Server에 추가
  • Vault에서 Public Key를 SSH 서버로 가져와서, SSH 서버에 저장
vault read -field=public_key ssh-client-signer/config/ca | sudo tee /etc/ssh/trusted-user-ca-keys.pem > /dev/null

 

 

Public Key 경로를 SSH 구성 파일에 TrustedUserCAKeys 옵션으로 추가

  • OpenSSH에서 서명된 인증서를 신뢰하기 위한 CA 공개키로 지정
# /etc/ssh/sshd_config
TrustedUserCAKeys /etc/ssh/trusted-user-ca-keys.pem

 

SSH 서비스 재기동

  • TrustedUserCAKeys 옵션 적용을 위해서 SSH 데몬 재기동
sudo systemctl restart ssh

 

 


SSH Client 설정

SSH 클라이언트는 자신의 공개키를 Vault에 제출하여 서명된 인증서를 발급받고, 이를 통해 SSH 서버에 안전하게 로그인합니다.

Vault Server 환경 변수 설정
  •  
  • VAULT Token 값은 ' 인증서 서명을 위한 Token 생성(SSH Client에서 사용) ' 단계에서 생성한 Token.
export VAULT_ADDR=http://Vault_Serer_IP:8200
export VAULT_TOKEN="require-ssh-sign Token"

설정 확인
env | grep VAULT

 

SSH Key 생성 (혹은 기존 Key 사용 가능) 

  • Vault에 제출할 Public Key 생성
ssh-keygen -t rsa

 

Vault에 Public Key Sign 요청

  • 생성한 Public Key를 Vault에 제출하고, 기존에 생성한 역할(role) 기반으로 서명된 SSH 인증서 발급
vault write -field=signed_key ssh-client-signer/sign/my-role \
  public_key=@./.ssh/id_rsa.pub > ./.ssh/id_rsa-cert.pub

 

 SSH Server에 SSH 접속

  • -i 옵션으로 앞서 생성한 Private Key를 지정해서, SSH Server에 접속
  • OpenSSH가 같은 경로에 앞서 발급 받은 SSH 인증서를 함께 사용
  • SSH Server에서는 TrustedUserCAKeys 값을 기준으로 서명된 인증서를 신뢰
ssh -i ~/.ssh/id_rsa username@SSH_Server_IP