서명된 SSH 인증서

서명된 SSH 인증서 (Signed SSH Certificates)

서명된 SSH 인증서는 설정 복잡성과 플랫폼 독립성 측면에서 가장 간단하고 강력한 방식이에요. Vault의 강력한 CA 기능과 OpenSSH에 내장된 기능을 활용하면, 클라이언트는 자신의 로컬 SSH 키를 사용해 대상 호스트에 SSH로 접속할 수 있어요.

서명된 SSH 인증서를 사용할 때 SSH CA 서명 키가 시크릿 엔진의 마운트에 생성되거나 구성돼요. 이 키는 다른 SSH 키에 서명하는 데 사용됩니다.

이 섹션에서 "클라이언트(client)"는 SSH 작업을 수행하는 사람 또는 머신을 가리키고, "호스트(host)"는 대상 머신을 가리켜요. 헷갈린다면 "client"를 "user"로 바꿔 생각하세요.

이 페이지에서는 이 시크릿 엔진의 빠른 시작을 보여 드릴게요. 모든 경로에 대한 자세한 문서는 시크릿 엔진을 마운트한 후 vault path-help를 사용하세요.

출처: 문서

본문

클라이언트 키 서명 (Client key signing)

클라이언트가 SSH 키 서명을 요청하기 전에 Vault SSH 시크릿 엔진이 구성되어 있어야 해요. 보통 Vault 관리자나 보안 팀이 이 단계들을 수행해요. Chef, Puppet, Ansible, Salt 같은 설정 관리 도구로 이 작업을 자동화하는 것도 가능해요.

서명 키 및 역할 구성

다음 단계들은 Vault 관리자, 보안 팀 또는 설정 관리 도구가 사전에 수행해요.

1. 시크릿 엔진을 마운트합니다. Vault의 모든 시크릿 엔진처럼 SSH 시크릿 엔진도 사용 전에 마운트되어야 해요.

$ vault secrets enable -path=ssh-client-signer ssh
Successfully mounted 'ssh' at 'ssh-client-signer'!

이렇게 하면 SSH 시크릿 엔진이 "ssh-client-signer" 경로에 활성화돼요. 같은 시크릿 엔진을 다른 -path 인자로 여러 번 마운트할 수 있어요. "ssh-client-signer"라는 이름은 특별하지 않아요 — 어떤 이름이든 될 수 있지만, 이 문서에서는 "ssh-client-signer"를 가정할게요.

2. /config/ca 엔드포인트를 사용해 클라이언트 키 서명용 CA로 Vault를 구성합니다. 내부 CA가 없다면 Vault가 키 쌍을 생성해 줄 수 있어요.

$ vault write ssh-client-signer/config/ca generate_signing_key=true
Key             Value
---             -----
public_key      ssh-rsa AAAAB3NzaC1yc2EA...

이미 키 쌍이 있다면 페이로드의 일부로 공개·개인 키 부분을 지정하세요.

$ vault write ssh-client-signer/config/ca \
    private_key="..." \
    public_key="..."

관리 키를 사용한다면 관리 키 이름이나 ID를 지정하세요.

    $ vault write ssh-client-signer/config/ca \
    managed_key_name="..." \

관리 키에 대한 자세한 내용은 관리 키 페이지를 참고하세요.

생성되었든 업로드되었든 관계없이, 클라이언트 서명자 공개 키는 /public_key 엔드포인트의 API나 CLI(다음 단계 참고)로 접근할 수 있어요.

3. 공개 키를 모든 대상 호스트의 SSH 구성에 추가합니다. 이 과정은 수동이거나 설정 관리 도구로 자동화할 수 있어요. 공개 키는 API로 접근할 수 있으며 인증이 필요하지 않아요.

$ curl -o /etc/ssh/trusted-user-ca-keys.pem http://127.0.0.1:8200/v1/ssh-client-signer/public_key
$ vault read -field=public_key ssh-client-signer/config/ca > /etc/ssh/trusted-user-ca-keys.pem

공개 키 내용이 저장된 경로를 SSH 구성 파일에 TrustedUserCAKeys 옵션으로 추가해요.

# /etc/ssh/sshd_config
# ...
TrustedUserCAKeys /etc/ssh/trusted-user-ca-keys.pem

변경 사항을 반영하려면 SSH 서비스를 재시작하세요.

4. 클라이언트 키 서명용 이름 있는 Vault 역할을 만듭니다.

중요 참고: Vault 1.9 이전에는, 역할에서 "allowed_extensions"가 비어 있거나 지정되지 않으면 Vault가 관대한 기본값을 가정했어요. 역할에 할당된 사용자라면 누구든 Vault 서버에 대한 인증서 요청의 일부로 임의의 확장 값을 지정할 수 있게요. 이는 보안에 중요한 정보에 extensions 필드에 의존하는 제3자 시스템에 상당한 영향을 미칠 수 있어요. 그런 경우 템플릿을 사용해 기본 확장을 지정하고, 필드가 비어 있거나 설정되지 않았다면 "allowed_extensions"를 임의의 비어 있지 않은 문자열로 명시적으로 설정하는 것을 고려하세요.

SSH 인증서 기능의 일부 구현 방식 때문에 옵션이 맵으로 전달돼요. 다음 예시는 permit-pty 확장을 인증서에 추가하고, 사용자가 인증서를 요청할 때 permit-ptypermit-port-forwarding에 자신의 값을 지정할 수 있게 해 줘요.

$ 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": "ubuntu",
  "ttl": "30m0s"
}
EOH

클라이언트 SSH 인증

다음 단계들은 Vault가 관리하는 머신에 인증하려는 클라이언트(사용자)가 수행해요. 이 명령들은 보통 클라이언트의 로컬 워크스테이션에서 실행돼요.

1. SSH 공개 키를 찾거나 생성합니다. 보통 ~/.ssh/id_rsa.pub예요. SSH 키 쌍이 없다면 생성하세요.

$ ssh-keygen -t rsa -C "[email protected]"

2. Vault에 공개 키 서명을 요청합니다. 이 파일은 보통 .pub로 끝나고 내용이 ssh-rsa ...로 시작해요.

$ vault write ssh-client-signer/sign/my-role \
    public_key=@$HOME/.ssh/id_rsa.pub

Key             Value
---             -----
serial_number   c73f26d2340276aa
signed_key      [email protected] AAAAHHNzaC1...

결과에는 시리얼과 서명된 키가 포함돼요. 이 서명된 키는 또 다른 공개 키예요.

서명 옵션을 커스터마이즈하려면 JSON 페이로드를 사용하세요.

$ vault write ssh-client-signer/sign/my-role -<<"EOH"
{
  "public_key": "ssh-rsa AAA...",
  "valid_principals": "my-user",
  "key_id": "custom-prefix",
  "extensions": {
    "permit-pty": "",
    "permit-port-forwarding": ""
  }
}
EOH

3. 결과 서명된 공개 키를 디스크에 저장합니다. 필요에 따라 권한을 제한하세요.

$ vault write -field=signed_key ssh-client-signer/sign/my-role \
    public_key=@$HOME/.ssh/id_rsa.pub > signed-cert.pub

인증서를 SSH 키 쌍 바로 옆에 저장한다면 이름을 -cert.pub로 접미사 붙이세요(~/.ssh/id_rsa-cert.pub). 이 명명 체계로 OpenSSH는 인증 중 자동으로 사용해요.

4. (선택) 활성화된 확장, 프린시펄, 서명된 키의 메타데이터를 봅니다.

$ ssh-keygen -Lf ~/.ssh/signed-cert.pub

5. 서명된 키로 호스트 머신에 SSH 접속합니다. SSH 호출 인증으로 Vault의 서명된 공개 키 해당 개인 키를 모두 제공해야 해요.

$ ssh -i signed-cert.pub -i ~/.ssh/id_rsa [email protected]

호스트 키 서명 (Host key signing)

보안 레이어를 하나 더 추가하기 위해 호스트 키 서명을 활성화하는 것을 권장해요. 이는 클라이언트 키 서명과 함께 사용되어 추가 무결성 레이어를 제공해요. 활성화되면 SSH 에이전트가 SSH를 시도하기 전에 대상 호스트가 유효하고 신뢰할 수 있는지 검증해요. 이렇게 하면 사용자가 실수로 관리되지 않거나 악의적인 머신에 SSH 접속할 확률을 줄여줘요.

서명 키 구성

1. 시크릿 엔진을 마운트합니다. 최대 보안을 위해 클라이언트 서명자와 다른 경로에 마운트하세요.

$ vault secrets enable -path=ssh-host-signer ssh
Successfully mounted 'ssh' at 'ssh-host-signer'!

2. /config/ca 엔드포인트를 사용해 호스트 키 서명용 CA로 Vault를 구성합니다. 내부 CA가 없다면 Vault가 키 쌍을 생성해 줄 수 있어요.

$ vault write ssh-host-signer/config/ca generate_signing_key=true
Key             Value
---             -----
public_key      ssh-rsa AAAAB3NzaC1yc2EA...

이미 키 쌍이 있다면 페이로드의 일부로 공개·개인 키 부분을 지정하세요.

$ vault write ssh-host-signer/config/ca \
    private_key="..." \
    public_key="..."

생성되었든 업로드되었든 관계없이, 호스트 서명자 공개 키는 /public_key 엔드포인트의 API로 접근할 수 있어요.

3. 호스트 키 인증서 TTL을 확장합니다.

$ vault secrets tune -max-lease-ttl=87600h ssh-host-signer

4. 호스트 키 서명용 역할을 만듭니다. 허용 도메인 목록을 채우고, allow_bare_domains를 설정하거나 둘 다 하세요.

$ vault write ssh-host-signer/roles/hostrole \
    key_type=ca \
    algorithm_signer=rsa-sha2-256 \
    ttl=87600h \
    allow_host_certificates=true \
    allowed_domains="localdomain,example.com" \
    allow_subdomains=true

5. 호스트의 SSH 공개 키에 서명합니다.

$ vault write ssh-host-signer/sign/hostrole \
    cert_type=host \
    public_key=@/etc/ssh/ssh_host_rsa_key.pub
Key             Value
---             -----
serial_number   3746eb17371540d9
signed_key      [email protected] AAAAHHNzaC1y...

6. 결과 서명된 인증서를 호스트 머신의 SSH 구성의 HostCertificate로 설정합니다.

$ vault write -field=signed_key ssh-host-signer/sign/hostrole \
    cert_type=host \
    public_key=@/etc/ssh/ssh_host_rsa_key.pub > /etc/ssh/ssh_host_rsa_key-cert.pub

인증서 권한을 0640으로 설정하세요.

$ chmod 0640 /etc/ssh/ssh_host_rsa_key-cert.pub

호스트 키와 호스트 인증서를 SSH 구성 파일에 추가해요.

# /etc/ssh/sshd_config
# ...

# For client keys
TrustedUserCAKeys /etc/ssh/trusted-user-ca-keys.pem

# For host keys
HostKey /etc/ssh/ssh_host_rsa_key
HostCertificate /etc/ssh/ssh_host_rsa_key-cert.pub

변경 사항을 반영하려면 SSH 서비스를 재시작하세요.

클라이언트 측 호스트 검증

1. 대상 머신의 호스트 서명을 검증할 호스트 서명 CA 공개 키를 가져옵니다.

$ curl http://127.0.0.1:8200/v1/ssh-host-signer/public_key
$ vault read -field=public_key ssh-host-signer/config/ca

2. 결과 공개 키를 known_hosts 파일에 authority로 추가합니다.

# ~/.ssh/known_hosts
@cert-authority *.example.com ssh-rsa AAAAB3NzaC1yc2EAAA...

3. 평소처럼 대상 머신에 SSH 접속합니다.

문제 해결 (Troubleshooting)

이 유형의 키 서명을 처음 구성할 때 VERBOSE SSH 로깅을 활성화해 로그의 오류를 주석으로 표시하는 데 도움을 주세요.

# /etc/ssh/sshd_config
# ...
LogLevel VERBOSE

변경 후 SSH를 재시작하세요.

기본적으로 SSH는 /var/log/auth.log에 로그를 남기지만, 다른 많은 것도 그렇게 해요. SSH 로그만 추출하려면 다음을 사용하세요.

$ tail -f /var/log/auth.log | grep --line-buffered "sshd"

호스트에 연결할 수 없으면 SSH 서버 로그가 안내와 통찰을 제공할 수 있어요.

이름이 나열된 프린시펄이 아님 (Name is not a listed principal)

auth.log에 다음 메시지가 표시되면:

# /var/log/auth.log
key_cert_check_authority: invalid certificate
Certificate invalid: name is not a listed principal

인증서가 시스템에 인증하기 위한 나열된 프린시펄로 사용자 이름을 허용하지 않는 것이에요. 이것은 대부분 OpenSSH 버그(알려진 이슈 참고) 때문이에요. 이 버그는 allowed_users 옵션 값 "*"를 존중하지 않아요. 이 문제를 우회하는 방법은 다음과 같아요.

  1. 역할에서 default_user를 설정하세요. 항상 같은 사용자로 인증한다면 역할의 default_user를 대상 머신에 SSH 접속하는 사용자 이름으로 설정하세요.
$ vault write ssh/roles/my-role -<<"EOH"
{
  "default_user": "YOUR_USER",
  // ...
}
EOH
  1. 서명 중에 valid_principals를 설정하세요. 여러 사용자가 Vault를 통해 SSH에 인증할 수 있는 상황에서는, 키 서명 중 유효 프린시펄 목록에 현재 사용자 이름을 포함하도록 설정하세요.
$ vault write ssh-client-signer/sign/my-role -<<"EOH"
{
  "valid_principals": "my-user"
  // ...
}
EOH

로그인 후 프롬프트 없음 (No prompt after login)

호스트 머신에 인증한 후 프롬프트가 보이지 않으면 서명된 인증서에 permit-pty 확장이 없을 수 있어요. 이 확장을 서명된 인증서에 추가하는 두 가지 방법이 있어요.

  • 역할 생성의 일부로
$ vault write ssh-client-signer/roles/my-role -<<"EOH"
{
  "default_extensions": {
    "permit-pty": ""
  }
  // ...
}
EOH
  • 서명 작업 자체의 일부로
$ vault write ssh-client-signer/sign/my-role -<<"EOH"
{
  "extensions": {
    "permit-pty": ""
  }
  // ...
}
EOH

포트 포워딩 없음 (No port forwarding)

게스트에서 호스트로의 포트 포워딩이 작동하지 않으면 서명된 인증서에 permit-port-forwarding 확장이 없을 수 있어요. 역할 생성이나 서명 과정의 일부로 확장을 추가해 포트 포워딩을 활성화하세요. 예시는 로그인 후 프롬프트 없음을 참고하세요.

{
  "default_extensions": {
    "permit-port-forwarding": ""
  }
}

X11 포워딩 없음 (No x11 forwarding)

게스트에서 호스트로의 X11 포워딩이 작동하지 않으면 서명된 인증서에 permit-X11-forwarding 확장이 없을 수 있어요. 역할 생성이나 서명 과정의 일부로 확장을 추가해 X11 포워딩을 활성화하세요. 예시는 로그인 후 프롬프트 없음을 참고하세요.

{
  "default_extensions": {
    "permit-X11-forwarding": ""
  }
}

에이전트 포워딩 없음 (No agent forwarding)

게스트에서 호스트로의 에이전트 포워딩이 작동하지 않으면 서명된 인증서에 permit-agent-forwarding 확장이 없을 수 있어요. 역할 생성이나 서명 과정의 일부로 확장을 추가해 에이전트 포워딩을 활성화하세요. 예시는 로그인 후 프롬프트 없음을 참고하세요.

{
  "default_extensions": {
    "permit-agent-forwarding": ""
  }
}

키 주석 (Key comments)

키에 주석 속성(comment attributes)을 보존하는 데 필요한 추가 단계가 있으며, 주석이 필요하다면 고려해야 해요. 개인·공개 키에 주석이 적용될 수 있는데, 예를 들어 ssh-keygen-C 파라미터와 함께 사용하면 다음처럼 돼요.

ssh-keygen -C "...Comments" -N "" -t rsa -b 4096 -f host-ca

주석이 포함된 적응된 키 값은 아래에 보여 주는 Vault CLI 및 API 단계대로 키 관련 파라미터와 함께 제공되어야 해요.

# Using CLI:
vault secrets enable -path=hosts-ca ssh
KEY_PRI=$(cat ~/.ssh/id_rsa | sed -z 's/\n/\\n/g')
KEY_PUB=$(cat ~/.ssh/id_rsa.pub | sed -z 's/\n/\\n/g')
# Create / update keypair in Vault
vault write ssh-client-signer/config/ca \
  generate_signing_key=false \
  private_key="${KEY_PRI}" \
  public_key="${KEY_PUB}"
# Using API:
curl -X POST -H "X-Vault-Token: ..." -d '{"type":"ssh"}' http://127.0.0.1:8200/v1/sys/mounts/hosts-ca
KEY_PRI=$(cat ~/.ssh/id_rsa | sed -z 's/\n/\\n/g')
KEY_PUB=$(cat ~/.ssh/id_rsa.pub | sed -z 's/\n/\\n/g')
tee payload.json <<EOF
{
  "generate_signing_key" : false,
  "private_key"          : "${KEY_PRI}",
  "public_key"           : "${KEY_PUB}"
}
EOF
# Create / update keypair in Vault
curl -X POST -H "X-Vault-Token: ..." -d @payload.json http://127.0.0.1:8200/v1/hosts-ca/config/ca

중요: Vault가 복호화할 수 없으므로 개인 키 비밀번호를 추가하지 마세요. 정상적으로 업로드된 것을 확인한 후 즉시 호스트에서 키 쌍과 payload.json을 파기하세요.

알려진 이슈 (Known issues)

  • SELinux 시행 시스템에서는 SSH 데몬이 읽을 수 있도록 관련 유형을 조정해야 할 수 있어요. 예를 들어 서명된 호스트 인증서를 sshd_key_t 유형으로 조정하세요.
  • 일부 SSH 버전에서 다음 오류가 발생할 수 있어요.
no separate private key for certificate

OpenSSH 7.2에서 도입되고 7.5에서 수정된 버그예요. 자세한 내용은 OpenSSH bug 2617을 참고하세요.

  • 일부 SSH 버전에서 대상 호스트에 다음 오류가 발생할 수 있어요.
userauth_pubkey: certificate signature algorithm ssh-rsa: signature algorithm not supported [preauth]

해결책은 /etc/ssh/sshd_config에 다음 줄을 추가하는 것이에요.

CASignatureAlgorithms ^ssh-rsa

ssh-rsa 알고리즘은 OpenSSH 8.2에서 더 이상 지원되지 않아요.

API

SSH 시크릿 엔진은 완전한 HTTP API를 제공해요. 자세한 내용은 SSH 시크릿 엔진 API 문서를 참고해 주세요.

더 알아보기 (Learn more)