메인 콘텐츠로 건너뛰기

Documentation Index

Fetch the complete documentation index at: https://docs.crewai.com/llms.txt

Use this file to discover all available pages before exploring further.

개요

이 가이드는 클라우드 공급자에서 로테이션된 시크릿이 바로 다음 자동화 kickoff에서 적용됨을 검증하는 방법을 보여줍니다 — 재배포 없음, 워커 재시작 없음. Workload Identity 기반 자격 증명(AWS, GCP, Azure)을 구성한 경우에만 관련됩니다. 정적 자격 증명 배포는 로테이션 후 재배포가 필요합니다. 여기에서는 확인할 것이 없습니다. 아래 레시피는 하나의 도구, 하나의 에이전트, 하나의 작업으로 구성된 작은 자체 포함된 Crew를 사용합니다. Crew 프롬프트는 시크릿 값을 절대 참조하지 않으며 — 대신 도구가 os.environ에서 이를 읽고 본 것의 SHA-256 fingerprint를 보고합니다. 클라우드 공급자에서 시크릿을 로테이션하고, 다시 kickoff하면 fingerprint가 변경됩니다.
원시 값이 아닌 fingerprint를 사용하는 이유? 원시 시크릿을 LLM 출력과 트레이스 로그에 넣는 것은 유출 벡터입니다. fingerprint는 실제 값을 관찰 가능한 곳에 쓰지 않고도 “값이 변경되었다”는 것을 확인하기에 충분합니다.

사전 준비 사항

이 검증을 실행하기 전에:
  • WI 기반 Secret Provider Credential이 구성되어 있어야 합니다(AWS, GCP, Azure).
  • Secret = true, 키 API_KEY(또는 원하는 이름 — 아래 도구를 일치시키도록 조정)로 클라우드 공급자의 시크릿을 참조하는 배포의 환경 변수.
  • 클라우드 공급자에서 시크릿 값을 업데이트할 방법(CLI 액세스 또는 클라우드 콘솔).
  • HTTP를 통해 배포를 kickoff할 방법(curl, Postman, 또는 CrewAI Platform의 Run 탭).

1단계 — 검증 Crew 스캐폴딩

새 Crew 프로젝트를 만듭니다. CrewAI CLI가 구조를 스캐폴딩합니다:
crewai create crew rotation_verifier --skip_provider
cd rotation_verifier

2단계 — Credential Echo 도구 추가

src/rotation_verifier/tools/custom_tool.py를 시크릿 기반 환경 변수를 읽고 fingerprint를 반환하는 도구로 교체합니다:
src/rotation_verifier/tools/credential_echo_tool.py
"""Tool that verifies a runtime-injected secret without leaking the value.

Reads the secret-backed env var (populated by the workload-identity
secrets manager at kickoff time) and returns a stable fingerprint. Never
echo raw credential values into LLM output or logs in production code —
the fingerprint alone is sufficient to confirm rotation worked.
"""

from __future__ import annotations

import hashlib
import os

from crewai.tools import BaseTool


# Match the deployment environment variable's `key` field.
ENV_VAR_NAME = "API_KEY"


class CredentialEchoTool(BaseTool):
    name: str = "credential_echo"
    description: str = (
        "Read the API credential from the worker's environment and return a "
        "fingerprint summary. Use this exactly once when asked to verify the "
        "current credential. Takes no arguments."
    )

    def _run(self) -> str:
        value = os.environ.get(ENV_VAR_NAME)
        if not value:
            return (
                f"ERROR: {ENV_VAR_NAME} env var is not set. The workload-"
                "identity secret fetch did not run, or the deployment is "
                "missing the secret-backed env var."
            )
        fingerprint = hashlib.sha256(value.encode()).hexdigest()[:12]
        return f"Authenticated. credential.fingerprint=sha256:{fingerprint}"

3단계 — 기본 에이전트 및 작업 구성 교체

Crew에는 하나의 에이전트와 하나의 작업이 있습니다 — 둘 다 시크릿 값을 절대 언급하지 않는 설명을 가지므로, 작업 키가 로테이션 전반에 걸쳐 안정적으로 유지됩니다.
src/rotation_verifier/config/agents.yaml
credential_checker:
  role: >
    Credential Verifier
  goal: >
    Confirm that the workload-identity-backed secret reached this worker
    process and report a fingerprint of the current value.
  backstory: >
    You are a no-nonsense reliability engineer responsible for verifying
    that secrets fetched at runtime via workload identity are present
    and fresh. You always use the credential_echo tool exactly once and
    report the result verbatim — you never make up values.
src/rotation_verifier/config/tasks.yaml
verify_credential_task:
  description: >
    Use the credential_echo tool to read the runtime-injected credential
    and produce a one-line confirmation. The current year is {current_year}
    (use it only in the timestamp; do not transform the credential output).
  expected_output: >
    A single line in the form:
    "[{current_year}] <credential_echo tool's exact output>"
  agent: credential_checker

4단계 — Crew 클래스 연결

src/rotation_verifier/crew.py
from crewai import Agent, Crew, Process, Task
from crewai.project import CrewBase, agent, crew, task
from crewai.agents.agent_builder.base_agent import BaseAgent

from rotation_verifier.tools.credential_echo_tool import CredentialEchoTool


@CrewBase
class RotationVerifierCrew():
    """Single-task crew that verifies a workload-identity-backed secret
    was successfully fetched at runtime.

    Rotate the underlying secret in the cloud provider, kickoff again, and
    the credential fingerprint in the agent's report changes — without any
    re-deploy, worker restart, or input change. The crew prompt itself
    never references the secret value.
    """

    agents: list[BaseAgent]
    tasks: list[Task]

    @agent
    def credential_checker(self) -> Agent:
        return Agent(
            config=self.agents_config["credential_checker"],
            tools=[CredentialEchoTool()],
            verbose=True,
        )

    @task
    def verify_credential_task(self) -> Task:
        return Task(config=self.tasks_config["verify_credential_task"])

    @crew
    def crew(self) -> Crew:
        return Crew(
            agents=self.agents,
            tasks=self.tasks,
            process=Process.sequential,
            verbose=True,
        )

5단계 — 배포 및 시크릿 환경 변수 구성

다른 Crew와 마찬가지로 이 Crew를 CrewAI Platform에 배포합니다. 그런 다음 배포의 Environment Variables 페이지에서:
  • Key: API_KEY (도구의 ENV_VAR_NAME과 일치해야 함)
  • Value Source: AWS WI 또는 GCP WI에서 설정한 WI 기반 자격 증명
  • Secret Name: 클라우드 공급자의 Secret Manager에 있는 시크릿 이름

6단계 — 첫 번째 Kickoff 실행

<DEPLOYMENT_AUTH_TOKEN><DEPLOYMENT_HOST>를 배포의 Run 탭에 있는 값으로 교체합니다.
curl -m 60 \
  -H "Authorization: Bearer <DEPLOYMENT_AUTH_TOKEN>" \
  -H "Content-Type: application/json" \
  -X POST https://<DEPLOYMENT_HOST>/kickoff \
  -d '{"inputs":{"current_year":"2026"}}'
kickoff가 완료되면(몇 초), 에이전트의 출력을 확인합니다. 다음과 같이 표시됩니다:
[2026] Authenticated. credential.fingerprint=sha256:004421b993c9
fingerprint를 기록합니다. 그 해시는 클라우드 공급자에 현재 있는 어떤 시크릿 값과 고유하게 연결되어 있습니다.

7단계 — 클라우드 공급자에서 시크릿 로테이션

aws secretsmanager update-secret \
  --region <REGION> \
  --secret-id <SECRET_NAME> \
  --secret-string "rotated value"

8단계 — 두 번째 Kickoff 실행 및 비교

curl -m 60 \
  -H "Authorization: Bearer <DEPLOYMENT_AUTH_TOKEN>" \
  -H "Content-Type: application/json" \
  -X POST https://<DEPLOYMENT_HOST>/kickoff \
  -d '{"inputs":{"current_year":"2026"}}'
이제 에이전트의 출력은 다른 fingerprint를 보여줍니다:
[2026] Authenticated. credential.fingerprint=sha256:e2fc89848f72
이는 재배포, 워커 재시작 또는 기타 운영자 작업 없이 실행 중인 배포에서 로테이션이 적용되었음을 증명합니다.

이것이 검증하는 것 — 그리고 검증하지 않는 것

검증하는 것:
  • CrewAI Platform에서 WI OIDC 토큰 발급이 작동합니다.
  • 클라우드 측 신뢰(AWS의 IAM OIDC 공급자, GCP의 Workload Identity Pool, Azure의 Federated Identity Credential)가 토큰을 수락합니다.
  • 클라우드 측 ID(IAM Role / GCP 서비스 계정 / Entra App Registration)가 시크릿을 읽을 수 있는 액세스 권한을 가집니다.
  • 시크릿 값이 kickoff 시점에 워커 프로세스의 os.environ에 도달합니다.
  • 후속 로테이션이 다음 kickoff에 전파됩니다.
검증하지 않는 것:
  • 실제 프로덕션 Crew가 로테이션을 우아하게 처리하는지 — 예를 들어, 시작 시 환경 변수를 한 번만 읽는 장기 실행 작업은 작업이 끝날 때까지 이전 값을 계속 사용합니다. 적절히 계획하세요: 모듈 임포트 시가 아닌 사용 시점에 시크릿을 읽으세요.

왜 프롬프트에서 직접 시크릿을 참조하지 않나요?

더 간단해 보이는 데모는 시크릿 값을 작업 설명에 직접 넣고(예: “{api_key}에 대해 조사”) 프롬프트를 검사하는 것입니다. 그렇게 하지 마세요. 두 가지 이유:
  1. LLM 호출 트레이스와 공급자 측 로그에 시크릿이 유출됩니다. 트레이스 액세스가 있는 모든 사람이 읽을 수 있습니다.
  2. 모든 kickoff에서 작업 설명이 변경됩니다. CrewAI Platform은 설명의 MD5 해시로 작업을 식별합니다. 로테이션되는 값은 kickoff마다 해시가 변경되어 배포 시간 → 런타임 작업 매핑이 깨집니다. 증상: 작업 레코드가 무한정 pending_run으로 표시되거나 다중 작업 Crew의 일부 작업만 등록됩니다.
이 가이드의 도구 기반 패턴은 두 문제를 모두 회피합니다: 프롬프트는 정적이고, 도구가 런타임에 환경 변수를 읽으며, 값의 fingerprint만 LLM에 도달합니다.

다음 단계

  • Secrets Manager 개요로 돌아가기
  • 검증되면 검증 Crew를 삭제합니다. 실제 Crew는 동일한 패턴을 따라야 합니다: 시크릿은 도구 내부의 os.environ을 통해 액세스되며, 절대 프롬프트에 치환되지 않습니다.