소개
노출된 지원 키는 폐기되었습니다. 서비스는 사용 가능하지만 요약 작업의 비공개 설정에 이전 값이 남아 있어 인증하지 못합니다. 대기 중인 daily 묶음의 요약이 아직 필요합니다.
권한 범위를 배우고 대체 키를 발급한 뒤 worker의 비공개 설정을 수정하여 실제 업무 실행을 완료합니다. 고객 디렉터리 접근이 여전히 금지되고 무관한 모니터가 정상인지도 테스트합니다. 이 실습은 차단 후 상태의 독립 환경을 제공하며 이전 VM을 재사용하지 않습니다.
읽기 전용 자격 증명 테스트
이 단계에서는 무효한 자격 증명과 유효하지만 권한이 부족한 자격 증명을 구분합니다. 새 환경은 차단이 끝난 상태입니다. key_support_old는 폐기되었고 작업은 이전 비밀값을 사용하며 daily는 미처리 상태입니다. 이전 실습 파일이나 VM은 필요하지 않습니다. 키와 운영자 토큰은 이 VM에 속합니다. 이전 환경의 자격 증명을 재사용하지 말고 여기서 값을 가져오세요.
Workstation은 /home/labex/project/repository의 유지보수 터미널입니다. Integration은 /home/labex/project에서 keyworker로 실행됩니다. Key Manager는 제공자의 유지보수 UI입니다. 로컬 교육 API는 http://127.0.0.1:8000에 있고 가상 동작은 이 과정에 한정됩니다.
**권한 범위(scope)**는 이름이 붙은 작업을 수행할 권한입니다. 최소 권한은 작업에 필요한 동작만 허용하고 무관한 동작은 사용할 수 없게 유지하는 것입니다. 요약 프로그램은 티켓을 읽고 요약을 게시하지만 고객 디렉터리는 읽지 않습니다.
| 작업 | 필요한 권한 범위 | 이 작업에 필요한가요? |
|---|---|---|
GET /v1/tickets |
tickets:read |
예: 입력 |
POST /v1/summaries |
summaries:write |
예: 출력 |
GET /v1/customers |
customers:read |
아니요 |
GET /v1/health |
health:read |
아니요: 별도 모니터 |
GET /v1/results |
tickets:read |
수신한 요약 보기 |
권한 실패 알아보기
준비된 읽기 전용 데모 키는 유효하지만 게시 권한이 없습니다. Workstation에서 이 키로 게시 요청을 보냅니다. curl -X POST는 쓰기 작업을 선택하고 -H는 헤더, -d는 JSON 본문을 제공합니다. -s는 진행을 숨기고 -w는 상태를 출력합니다. 제공자가 본문을 검사하기 전에 권한을 거부하므로 빈 객체 {}만으로 충분합니다. Content-Type: application/json 헤더는 본문이 JSON 형식임을 서비스에 알려줍니다.
curl -s -w '\nHTTP %{http_code}\n' -X POST -H 'Authorization: Bearer lab_demo_read_only' -H 'Content-Type: application/json' -d '{}' http://127.0.0.1:8000/v1/summaries
{"error":"Missing scope","required":"summaries:write"}
HTTP 403
이 API에서 403은 인증은 되었지만 필요한 권한 범위가 없다는 뜻입니다. 자격 증명 자체를 거부하는 401과 다릅니다. 읽기 전용 키로 읽기와 쓰기 작업을 완료할 수 없고, 전체 접근 키는 권한이 과도합니다. 대체 키에는 표의 업무 권한 두 개가 필요합니다.
전용 대체 키 발급하기
이 단계에서는 앞서 선택한 권한 범위 두 개로 전용 대체 키를 발급합니다. 비밀값은 다음 단계에서 비공개로 저장합니다. 이름은 식별용 라벨이며 권한을 결정하는 것은 scope입니다.
전용 자격 증명 발급하기
Workstation에서 파일을 출력하는 cat으로 운영자 토큰을 표시합니다.
cat /home/labex/.config/keylab/operator-token
Key Manager의 Operator token에 값을 입력하고 Sign in, Open Key Manager를 선택합니다. 운영자 인증은 애플리케이션 키와 분리됩니다. key_support_old가 이미 Revoked인지 확인합니다.
Issue a key 아래 Name에 Support summary replacement를 입력합니다. tickets:read와 summaries:write만 선택하고 Issue key를 누릅니다. 비밀이 아닌 키 ID와 Secret shown once가 나타납니다. 페이지를 나가기 전에 lab_key_로 시작하는 비밀값을 복사하세요. 제공자는 복구 가능한 비밀값 대신 해시를 저장합니다. 해시는 단방향 지문으로, 저장한 원본을 표시하지 않고 제공된 값을 비교할 수 있게 합니다.
값을 잃어버리면 미사용 키를 ID로 폐기하고 새로 발급합니다. 노출된 값을 재사용하거나 복사 오류를 해결하려고 권한을 넓히지 마세요.
비공개 런타임 설정 저장
이 단계에서는 대체 값을 서비스 계정의 비공개 런타임 파일에 저장하고 접근 권한을 확인합니다. 아래 명령은 모두 Integration에서 실행하세요.
런타임 설정은 프로그램 실행 시 읽는 파일입니다. 이 worker는 호출마다 /home/labex/project/runtime/job.ini를 읽으므로 재시작할 필요가 없습니다. 저장소 예제는 다른 파일입니다. Integration에서 현재 설정을 봅니다.
cat /home/labex/project/runtime/job.ini
[integration]
api_key = lab_key_support_exposed
비밀값을 저장소나 셸 명령 이력에 넣지 마세요. Bash의 read는 프롬프트에서 값을 받습니다. -r은 문자를 그대로 유지하고 -s는 입력을 숨기며 -p는 안내를 출력합니다. API_KEY는 임시 셸 변수입니다. Integration에서 실행하고 프롬프트에 새 비밀값을 붙여 넣은 뒤 Enter를 누릅니다. 입력은 보이지 않습니다.
read -r -s -p 'Replacement key: ' API_KEY
printf는 형식이 지정된 텍스트를 씁니다. \n은 줄바꿈, %s는 변수값 삽입, 큰따옴표는 값을 인수 하나로 유지하는 역할이며 >는 지정 파일을 덮어씁니다. 기존 비공개 파일은 현재 터미널 계정인 keyworker 소유입니다.
printf '[integration]\napi_key = %s\n' "$API_KEY" > /home/labex/project/runtime/job.ini
chmod로 접근 권한을 설정합니다. 모드 600은 소유자에게만 읽기와 쓰기를 허용하며 그룹과 다른 계정은 접근할 수 없습니다.
chmod 600 /home/labex/project/runtime/job.ini
ls -l은 비밀값을 출력하지 않고 소유자와 권한을 보여줍니다.
ls -l /home/labex/project/runtime/job.ini
-rw-------와 소유자 keyworker를 확인합니다. 크기와 날짜는 달라질 수 있습니다. 셸 변수만으로 파일이 바뀌지는 않습니다. 런타임 파일을 저장해야 다음 worker 호출이 새 자격 증명을 사용합니다.
이 Integration 터미널을 열어 두세요. API_KEY는 현재 셸에만 속하며 모든 터미널에서 사용할 수 있는 것은 아닙니다. 다음 단계에서 독립 요청에 사용한 뒤 삭제합니다. 터미널을 닫거나 재연결했다면 위의 cat 명령으로 job.ini의 현재 비밀값을 보고 이 단계를 반복하세요.
업무 실행 완료 및 검증
이 단계에서는 대기 중인 daily를 처리하고 허용 및 금지 동작을 모두 테스트합니다. worker는 준비되어 있으므로 /opt/keylab/worker.py를 변경하지 마세요. 자격 증명을 복구하는 과제이지 인증을 우회하도록 프로그램을 고치는 과제가 아닙니다.
Integration에서 지정 묶음을 실행합니다.
python3 /opt/keylab/worker.py daily
Read batch: daily (3 items)
{"batch":"daily","count":3,"subjects":["Reset password","Invoice copy","Delivery question"]}
Received batch: daily
Receiving key ID: ...
수신 ID는 Key Manager의 대체 키 ID와 같아야 합니다. 401이면 저장한 비밀값이 활성 대체 키의 값인지 확인하고, 403이면 표와 권한을 비교합니다. 필요하면 권한이 잘못된 대체 키를 폐기하고 올바르게 발급하여 비공개 파일을 수정한 뒤 재실행합니다. 같은 묶음을 다시 실행하면 수신 기록을 대체하며 요약을 중복 생성하지 않습니다.
실제 수신 서비스 살펴보기
같은 Integration 터미널을 계속 사용합니다. 이전 단계의 API_KEY 변수가 아직 있으므로 한 번만 표시되는 비밀값을 다시 입력할 필요가 없습니다.
읽기 전용 결과 엔드포인트를 사용합니다. 파이프라인 |는 응답을 다음 프로그램에 전달합니다. python3 -m json.tool은 Python JSON 포매터를 실행해 읽기 좋은 들여쓰기를 만듭니다.
curl -s -H "Authorization: Bearer $API_KEY" http://127.0.0.1:8000/v1/results | python3 -m json.tool
daily, payload의 제목 세 개, 대체 키의 key_id를 찾습니다. 로컬 성공 파일이 아니라 수신 API의 기록입니다. 백엔드는 원본 데이터를 읽은 자격 증명도 기록합니다.
이제 금지된 고객 디렉터리 요청을 테스트합니다.
curl -s -w '\nHTTP %{http_code}\n' -H "Authorization: Bearer $API_KEY" http://127.0.0.1:8000/v1/customers
{"error":"Missing scope","required":"customers:read"}
HTTP 403
예상한 거부는 새 키의 제한을 확인합니다. 임시 셸 변수가 더 이상 필요하지 않으면 unset으로 삭제하세요. 저장한 런타임 파일은 바뀌지 않습니다.
unset API_KEY
Workstation에서 이전 키가 여전히 사용 불가이고 모니터가 정상인지 확인합니다.
curl -s -w '\nHTTP %{http_code}\n' -H 'Authorization: Bearer lab_key_support_exposed' http://127.0.0.1:8000/v1/tickets
{"error":"Invalid or revoked key"}
HTTP 401
curl -s -w '\nHTTP %{http_code}\n' -H 'Authorization: Bearer lab_key_monitor_only' http://127.0.0.1:8000/v1/health
{"status":"ok","integration":"monitor"}
HTTP 200
마지막으로 git status --short는 변경되거나 추적되지 않은 저장소 파일을 간단히 나열합니다.
git status --short
안내대로 진행했다면 비공개 런타임 파일만 바꿨으므로 출력이 없습니다. 대체 값을 예제나 커밋에 넣지 마세요. 첫 실습처럼 눈에 보이는 줄이 사라져도 이력에 비밀값이 남을 수 있습니다.
요약
인증 실패와 권한 부족을 구분하고 읽기·쓰기 권한 조합을 선택했으며, 한 번만 표시되는 비밀값을 비공개로 저장하고 대기 중인 요약을 완료했습니다. 수신 기록, 이전 키 거부, 고객 접근 금지, 모니터 가용성을 확인했습니다. 올바른 교체는 정상 업무를 복구하고 노출된 자격 증명의 접근을 제거합니다.



