Infograb logo
머신 ID 문제 해결 가이드

봇이 "생성 불일치"로 인해 인증서를 갱신하지 못했습니다.

증상

봇은 다음과 같은 오류를 기록합니다:

ERROR: renewable cert generation mismatch: stored=3, presented=2

봇의 이후 연결 시도는 다음과 같은 오류를 볼 수 있습니다:

ERROR: failed direct dial to auth server: auth API: access denied [00]
"\tauth API: access denied [00], failed dial to auth server through reverse tunnel: Get \"https://teleport.cluster.local/v2/configuration/name\": Get \"https://example.com:3025/webapi/find\": x509: cannot validate certificate for example.com because it doesn't contain any IP SANs"
"\tGet \"https://teleport.cluster.local/v2/configuration/name\": Get \"https://example.com:3025/webapi/find\": x509: cannot validate certificate for example.com because it doesn't contain any IP SANs"

특히 auth API: access denied 메시지를 주목하십시오.

자체 호스팅된 Teleport 배포에서는 Teleport Auth Service가 추가적인 맥락을 제공할 수 있습니다:

[AUTH]      WARN lock targeting User:"bot-example" is in force: The bot user "bot-example" has been locked due to a certificate generation mismatch, possibly indicating a stolen certificate. auth/apiserver.go:224

설명

토큰 기반 조인을 사용하는 머신 ID는 인증서 생성 카운터를 사용하여 잠재적으로 도난당한 갱신 가능한 인증서를 감지합니다. 봇이 새로운 갱신 가능한 인증서를 가져올 때마다 Auth Service는 카운터를 증가시켜 백엔드에 저장하고 인증서에 카운터의 사본을 포함합니다.

봇 인증서에 포함된 카운터가 Teleport의 Auth Server에 저장된 카운터와 일치하지 않으면 갱신이 실패하고 봇 사용자는 자동으로 잠금됩니다.

갱신 가능한 인증서는 기본적으로 봇의 내부 데이터 디렉터리인 /var/lib/teleport/bot에만 저장됩니다. 여러 봇이 동일한 내부 데이터 디렉터리를 사용하여 시작되거나 이 내부 데이터가 여러 봇 인스턴스 간에 공유되는 경우 실수로 이를 유발할 수 있습니다.

또한, 봇이 새롭게 갱신된 인증서를 저장하지 못하고(예: 파일 시스템 오류로 인해) 충돌하는 경우 오래된 인증서를 사용하여 갱신을 시도하고 잠금을 트리거합니다.

해결 방법

봇의 잠금을 해제하기 전에 위에서 설명한 두 가지 시나리오 중 하나가 해당되는지 확인하십시오. 인증서가 도난당한 경우 해결해야 할 보안 문제가 있을 수 있습니다.

그렇지 않다면 먼저 내부 데이터 디렉터리를 사용하는 봇 인스턴스가 하나만 있는지 확인하십시오. 여러 봇을 단일 시스템에서 실행할 수 있지만 각각에 대해 별도의 데이터 디렉터리를 구성해야 합니다.

또한 내부 데이터가 공유 NFS 볼륨을 통해 다른 노드와 공유되지 않도록 하십시오. 노드 간에 인증서를 공유하려면 내부 데이터 디렉터리(기본적으로 /var/lib/teleport/bot)가 아닌 대상 디렉터리(보통 /opt/machine-id)에서만 콘텐츠를 복사하거나 공유하십시오.

근본 원인을 해결한 후에는 잠긴 봇을 재설정하기 위해 다음 단계를 따르십시오:

  1. 봇 사용자에 대한 잠금을 제거합니다.
  2. 봇의 생성 카운터를 삭제하고 봇을 재생성하여 재설정합니다.

잠금을 제거하려면 먼저 봇 사용자를 대상으로 하는 잠금을 찾아서 제거하십시오:

tctl get locks
kind: lockmetadata: id: 1658359514703080513 name: 5cee949f-5203-4f3b-9805-dac35d798a16spec: message: The bot user "bot-example" has been locked due to a certificate generation mismatch, possibly indicating a stolen certificate. target: user: bot-exampleversion: v2
tctl rm lock/5cee949f-5203-4f3b-9805-dac35d798a16

다음으로, 봇을 삭제하고 새로 생성하여 생성 카운터를 재설정하십시오:

tctl bots rm example

tctl bots add example --roles=foo,bar

마지막으로, 새 토큰으로 봇을 재구성하고 다시 시작하십시오. 봇은 새 토큰을 감지하고 내부 데이터 디렉터리를 자동으로 재설정합니다.

tbot가 시작 시 "bad certificate error"를 표시합니다.

증상

tbot 프로세스를 재시작하면 다음과 같은 로그가 출력됩니다:

INFO [TBOT]      Successfully loaded bot identity, valid: after=2022-07-21T21:49:26Z, before=2022-07-21T22:50:26Z, duration=1h1m0s | kind=tls, renewable=true, disallow-reissue=false, roles=[bot-test], principals=[-teleport-internal-join], generation=2 tbot/tbot.go:281
ERRO [TBOT]      Identity has expired. The renewal is likely to fail. (expires: 2022-07-21T22:50:26Z, current time: 2022-07-25T20:18:33Z) tbot/tbot.go:415
WARN [TBOT]      Note: onboarding config ignored as identity was loaded from persistent storage tbot/tbot.go:288
ERRO [TBOT]      Failed to resolve tunnel address Get "https://auth.example.com:3025/webapi/find": x509: cannot validate certificate for auth.example.com because it doesn't contain any IP SANs reversetunnel/transport.go:90
ERRO [TBOT]      Failed to resolve tunnel address Get "https://auth.example.com:3025/webapi/find": x509: cannot validate certificate for auth.example.com because it doesn't contain any IP SANs reversetunnel/transport.go:90
ERROR: failed direct dial to auth server: Get "https://teleport.cluster.local/v2/configuration/name": remote error: tls: bad certificate
"\tGet \"https://teleport.cluster.local/v2/configuration/name\": remote error: tls: bad certificate, failed dial to auth server through reverse tunnel: Get \"https://teleport.cluster.local/v2/configuration/name\": Get \"https://auth.example.com:3025/webapi/find\": x509: cannot validate certificate for auth.example.com because it doesn't contain any IP SANs"
"\tGet \"https://teleport.cluster.local/v2/configuration/name\": Get \"https://auth.example.com:3025/webapi/find\": x509: cannot validate certificate for auth.example.com because it doesn't contain any IP SANs"

특히, "Identity has expired. The renewal is likely to fail."라는 로그 라인을 주목하십시오.

설명

토큰으로 조인된 봇은 인증서가 만료되면 Teleport Auth Service에 다시 인증할 수 없습니다. AWS IAM 조인과 달리 토큰 기반 조인에서 토큰은 한 번만 사용될 수 있으므로 봇의 내부 인증서가 만료되면 연결할 수 없습니다.

봇의 신원이 만료되면 Auth Service에서 봇과 관련된 특정 매개변수를 재설정하고 새 조인 토큰을 발급해야 합니다. 이를 수행하는 가장 간단한 방법은 봇을 제거하고 재생성하여 모든 서버 측 데이터를 정리하고 새 조인 토큰을 발급받는 것입니다.

해결 방법

봇을 제거하고 원하는 대로 이름과 역할 목록을 변경하여 재생성하십시오:

tctl bots rm example
tctl bots add example --roles=access

생성된 조인 토큰을 기존 봇 구성에 복사하십시오—--token CLI 플래그 또는 tbot.yamlonboarding.token 매개변수에서—그리고 봇을 다시 시작하십시오. 봇은 새 토큰을 감지하고 정상적으로 클러스터에 재가입할 것입니다.

SSH 연결이 ssh: handshake failed: ssh: unable to authenticate로 실패합니다.

증상

노드에 SSH로 연결을 시도할 때 다음과 같은 오류로 연결이 실패합니다:

ssh -F /opt/machine-id/ssh_config bob@node.example.com
ERROR: ssh: handshake failed: ssh: unable to authenticate, attempted methods [none publickey], no supported methods remain
ERROR: unable to execute tshexecuting `tsh proxy`exit status 1
kex_exchange_identification: Connection closed by remote hostConnection closed by UNKNOWN port 65535

특히 ssh: unable to authenticate 메시지를 주목하십시오.

설명

이것은 SSH 인증서에 사용자로 나열되지 않은 사용자로 노드에 로그인하려고 할 때 발생할 수 있습니다.

tbot 로그를 확인하고 일치하는 출력을 위한 대리 인증서가 갱신할 때의 로그 메시지를 찾아 이를 확인할 수 있습니다.

다음 예에서 /opt/machine-id의 신원에 나열된 유일한 주체는 access 역할을 통해 alice입니다:

INFO [TBOT]      Successfully renewed impersonated certificates for directory /opt/machine-id, valid: after=2022-07-21T21:49:26Z, before=2022-07-21T22:50:26Z, duration=1h1m0s | kind=tls, renewable=false, disallow-reissue=true, roles=[access], principals=[alice -teleport-internal-join], generation=0 tbot/renew.go:630

그러나 SSH 명령은 bob으로 로그인하려고 시도했습니다.

해결 방법

봇 신원이 요청된 사용자로 로그인할 수 있도록 하려면 다음과 같은 작업을 수행해야 합니다:

  • SSH 명령을 허용된 사용자로 변경
  • access 역할을 수정하여 alice 주체를 허용
  • bob 주체를 통해 로그인을 허용하는 역할 추가

역할이 추가되거나 수정된 경우, 변경 사항이 적용되도록 인증서를 갱신해야 합니다. 봇은 기본적으로 20분 후에 인증서를 자동으로 갱신하지만 tbot 프로세스를 재시작하거나 재로드 신호를 보내 즉시 갱신을 트리거할 수 있습니다:

# systemd를 사용하는 경우 프로세스를 재시작할 수 있습니다:

systemctl restart machine-id

# 또는 직접 `tbot`에 재로드 신호를 보낼 수 있습니다:

pkill -sigusr1 tbot

데이터베이스 요청이 database "example" not found, 그러나 데이터베이스가 존재합니다.

증상

데이터베이스 접근 인증서를 요청할 때 다음과 같은 오류로 인증서 요청이 실패합니다:

ERROR: Failed to generate impersonated certs for directory /opt/machine-id: database "example" not found
database "example" not found

그러나 데이터베이스는 존재하며 일반 사용자가 tsh를 통해 확인할 수 있습니다:

tsh db ls
Name Description Allowed Users Labels Connect---------- ----------- ------------- ------- -------example [alice] env=dev

설명

일반 Teleport 사용자와 달리 머신 ID 봇 사용자는 최소한의 Teleport RBAC 권한만 부여받으며, 기본적으로 역할을 통해 권한이 부여되지 않는 한 데이터베이스를 보거나 나열할 수 없습니다.

해결 방법

머신 ID 데이터베이스 접근 가이드에 따라 오류에 나열된 출력에 데이터베이스 권한을 제공하는 역할이 부여되었는지 확인하십시오.

예를 들어, 다음 예에서 역할의 rules 섹션을 확인하십시오:

kind: role
version: v5
metadata:
  name: machine-id-db
spec:
  allow:
    db_labels:
      '*': '*'
    db_names: [example]
    db_users: [alice]
    rules:
      - resources: [db_server, db]
        verbs: [read, list]

봇에게 최소한 이러한 RBAC 규칙을 부여하는 역할이 할당되었는지 확인하십시오. 필요하다면 tctl을 사용하여 봇 역할을 검사하여 필요한 rules가 부여되어 있는지 확인할 수 있습니다:

tctl get role/machine-id-db

역할에 데이터베이스 권한이 누락된 경우 다음과 같이 수정할 수 있습니다:

# 역할을 로컬 파일에 저장

tctl get role/machine-id-db > db-role.yaml

# 필요에 따라 역할을 수정

nano db-role.yaml

# 수정된 복사본으로 기존 역할을 교체

tctl create -f db-role.yaml

기본적으로 출력(예: /opt/machine-id)은 tctl bots add --roles=...를 통해 봇에 제공된 모든 역할이 부여되지만, tbot.yamlroles: ... 매개변수를 사용하여 이러한 역할의 하위 집합만 부여할 수 있습니다.

권한이 사라진 경우 tbot.yaml에서 데이터베이스 역할을 요청하는지 확인하십시오. 기본 동작에 의존하거나 roles: ... 목록에 역할을 추가하여 요청할 수 있습니다.

문제가 해결되면 tbot 클라이언트를 다시 시작하거나 새 역할이 적용되도록 재로드하십시오.

봇이 처음에 역할이 부여되지 않았다면 가장 간단한 해결책은 봇을 삭제하고 재생성하는 것입니다. --roles=... 플래그에 역할을 포함하도록 하십시오:

tctl bots rm example
tctl bots add example --roles=foo,bar,machine-id-db

대상 kubernetes_secret: identity-output는 exec 플러그인 모드에서 디렉터리여야 합니다.

기본적으로 Kubernetes ID를 출력할 때, tbot은 항상 최신 버전의 자격 증명을 제공하기 위해 Kubernetes exec 플러그인을 사용합니다.

그러나 Kubernetes 시크릿에 Kubernetes ID를 출력할 때는 exec 플러그인의 사용을 비활성화하는 것이 중요합니다. 따라서 정적 kubeconfig 파일을 작성하여 장착된 단기 자격 증명을 쓰도록 disable_exec_plugin: true를 추가해야 합니다:

outputs:
  - type: kubernetes
    # 자격 증명을 부여할 Kubernetes 클러스터의 이름을 지정하십시오.
    kubernetes_cluster: example-k8s-cluster
    # Kubernetes 시크릿에 Kubernetes ID를 출력할 때 필수입니다.
    disable_exec_plugin: true
    destination:
      type: kubernetes_secret
      # 이 가이드에서는 identity-output이 시크릿 이름으로 사용됩니다.
      # 이 이름을 사용자 지정할 수 있습니다. 여러 출력을 동일한
      # 대상으로 공유할 수 없습니다.
      name: identity-output

disable_exec_plugin 플래그를 추가하지 않으면 경고가 표시되며: Destination kubernetes_secret: identity-output must be a directory in exec plugin mode.

Teleport 원문 보기