LabHub
배우기 러닝패스 코스

コンテナセキュリティ

環境変数とファイルマウント、何が違うのか

LabHub 에서 이어서 보기

한국어 원문으로 표시합니다.

이 실습은 진짜 VM 에서 돕니다

이 상자는 파드가 아니라 KubeVirt 가 띄운 가상머신입니다. 리눅스 커널이 따로 돌고 systemd 가 실제로 서비스를 관리하며, docker 는 흉내가 아니라 진짜 도커 엔진입니다. docker run 으로 띄운 컨테이너는 실제로 프로세스가 되고 docker execdocker logs 도 그대로 동작합니다.

예전에는 이 실습이 파드 안에서 돌았습니다. 커널 권한을 전부 내려놓은 상자라 컨테이너를 띄우는 단계가 막혀 있었고, 그래서 이미지 아카이브를 직접 풀어 보는 우회로 배웠습니다. 이제 우회가 필요 없습니다.

알아 둘 것이 둘 있습니다.

목표

같은 시크릿을 환경변수와 파일 마운트 두 방식으로 전달해 보고, docker inspect 노출 여부와 재시작 없는 로테이션 가능 여부를 직접 재어 비교표로 정리합니다. 마지막에는 간단한 시크릿 탐지 스크립트를 작성합니다.

왜 중요한가

환경변수는 저장 위치가 아니라 전달 방식입니다. /proc/<pid>/environ 은 프로세스가 사는 동안 계속 읽히고, 컨테이너 환경변수는 데몬에 접근할 수 있는 누구에게나 docker inspect 로 그대로 보입니다. 그래서 ".env 를 로드하고 지웠으니 괜찮다"는 안심은 근거가 없습니다. 더 중요한 것은 로테이션입니다. 환경변수는 프로세스 시작 시점에 고정되므로 값을 바꾸려면 반드시 재시작이 필요하고, 파일 마운트는 같은 inode 를 덮어쓰면 재시작 없이 새 값이 보입니다. 이 차이가 사고 대응 시간을 가릅니다 — 자격증명이 공개됐다고 가정할 때 폐기하고 교체해 서비스를 정상화하는 데 몇 분이 걸리는지가 곧 그 팀의 시크릿 관리 수준입니다.

단계

  1. /root/sec3 디렉터리를 만들고 /root/sec3/app.envAPI_KEY=labhub-env-key-v1 한 줄을 적은 뒤, 파일 권한을 600 으로 바꿉니다.
  2. alpine:3.20 으로 sec3-env 컨테이너를 --env-file /root/sec3/app.env 를 써서 백그라운드 실행합니다. 컨테이너의 API_KEY 가 파일의 값과 같아야 합니다.
  3. docker inspect sec3-env 로 읽어낸 API_KEY 값을 그대로 /root/sec3/leak-proof.txt 에 저장합니다.
  4. /root/sec3/secret.txtlabhub-file-key-v1 을 적고, 그 파일을 sec3-file 컨테이너의 /run/secrets/api_key 경로에 읽기 전용으로 바인드 마운트해 백그라운드 실행합니다. 마운트 원본은 정확히 /root/sec3/secret.txt 여야 합니다.
  5. sec3-file 컨테이너의 환경변수에 API_KEY 도, 시크릿 값 자체도 들어 있지 않아야 합니다.
  6. 컨테이너를 재시작하지 않은 채 /root/sec3/secret.txt 의 내용을 labhub-rotated-v2 로 덮어씁니다. sec3-file 이 새 값을 보게 되고, sec3-env 의 환경변수 값은 옛 값 그대로여야 합니다. 그리고 /root/sec3/rotate.md 에 어느 방식이 재시작을 필요로 하는지 결론을 적습니다(재시작 또는 restart 라는 말이 들어가야 합니다).
  7. 실행 권한이 있는 /root/sec3/find-secrets.sh 를 작성합니다. 첫 번째 인자로 받은 디렉터리를 뒤져서 AKIA 로 시작하는 액세스 키나 PRIVATE KEY 블록이 있으면 0 이 아닌 종료코드로 끝나면서 어느 파일에서 찾았는지 경로를 출력하고, 아무것도 없으면 종료코드 0 으로 끝나야 합니다.
  8. /root/sec3/secrets.md 에 환경변수 방식과 파일 마운트 방식을 비교하는 내용을 적고, 다음 네 줄을 정확히 이 형식으로 포함시킵니다.
    • env_visible_in_inspect=yes
    • mount_visible_in_inspect=no
    • env_needs_restart=yes
    • mount_needs_restart=no

참고

시크릿 파일과 권한 600

/root/sec3 디렉터리를 만들고 /root/sec3/app.envAPI_KEY=labhub-env-key-v1 한 줄을 적은 뒤, 파일 권한을 600 으로 바꿉니다.

값 자체보다 파일 권한이 채점 대상입니다. 소유자만 읽고 쓸 수 있어야 하며 그룹과 기타 사용자에게는 아무 권한도 없어야 합니다. 파일을 만든 뒤 권한을 바꾸는 순서로 하세요.

env-file 로 주입하기

alpine:3.20 으로 sec3-env 컨테이너를 --env-file /root/sec3/app.env 를 써서 백그라운드 실행합니다. 컨테이너의 API_KEY 가 파일의 값과 같아야 합니다.

값을 명령줄에 직접 쓰면 셸 히스토리와 프로세스 목록에 남습니다. 파일에서 읽어 주입하는 옵션을 쓰세요. 채점이 컨테이너의 환경변수 값과 파일 값을 대조하므로 컨테이너는 떠 있어야 합니다.

환경변수는 inspect 에 그대로 보인다

docker inspect sec3-env 로 읽어낸 API_KEY 값을 그대로 /root/sec3/leak-proof.txt 에 저장합니다.

컨테이너를 만든 사람이 아니어도 데몬에 접근할 수 있으면 이 값을 읽습니다. 추측해서 적지 말고 실제로 docker inspect 로 뽑아낸 값을 그대로 파일에 남기세요.

파일로 마운트하기

/root/sec3/secret.txtlabhub-file-key-v1 을 적고, 그 파일을 sec3-file 컨테이너의 /run/secrets/api_key 경로에 읽기 전용으로 바인드 마운트해 백그라운드 실행합니다. 마운트 원본은 정확히 /root/sec3/secret.txt 여야 합니다.

호스트 파일 하나를 컨테이너의 특정 경로에 그대로 붙이는 방식입니다. 마운트 원본 경로와 대상 경로가 채점 대상이므로 정확히 맞춰야 하고, 시크릿이므로 읽기 전용으로 붙여야 합니다.

환경변수에는 남기지 않기

sec3-file 컨테이너의 환경변수에 API_KEY 도, 시크릿 값 자체도 들어 있지 않아야 합니다.

파일 마운트로 바꿨다면 같은 값을 환경변수로도 넘길 이유가 없습니다. 둘 다 주면 파일 마운트의 이점이 사라집니다 — inspect 에 다시 보이기 때문입니다. 4단계 컨테이너에 환경변수가 섞이지 않았는지 확인하세요.

재시작 없이 로테이션하기

컨테이너를 재시작하지 않은 채 /root/sec3/secret.txt 의 내용을 labhub-rotated-v2 로 덮어씁니다. sec3-file 이 새 값을 보게 되고, sec3-env 의 환경변수 값은 옛 값 그대로여야 합니다. 그리고 /root/sec3/rotate.md 에 어느 방식이 재시작을 필요로 하는지 결론을 적습니다(재시작 또는 restart 라는 말이 들어가야 합니다).

바인드 마운트는 inode 를 따라갑니다. 파일을 제자리에서 덮어쓰면(리다이렉션) 컨테이너가 새 값을 즉시 보지만, 파일을 지우고 새로 만드는 방식(일부 편집기나 sed -i)은 inode 가 바뀌어 마운트가 옛 파일을 계속 가리킵니다. 그리고 환경변수 쪽은 절대 바뀌면 안 됩니다 — 그것이 이 단계의 대조군입니다.

시크릿 탐지 스크립트

실행 권한이 있는 /root/sec3/find-secrets.sh 를 작성합니다. 첫 번째 인자로 받은 디렉터리를 뒤져서 AKIA 로 시작하는 액세스 키나 PRIVATE KEY 블록이 있으면 0 이 아닌 종료코드로 끝나면서 어느 파일에서 찾았는지 경로를 출력하고, 아무것도 없으면 종료코드 0 으로 끝나야 합니다.

디렉터리를 재귀적으로 훑어 두 가지 형태를 찾습니다 — AKIA 로 시작하는 액세스 키와 PRIVATE KEY 블록 헤더. 깨끗하면 0, 찾으면 0 이 아닌 값으로 끝나야 하고 어느 파일인지 경로를 출력해야 합니다. 이런 규칙이 형태가 뚜렷한 값에만 잘 듣는다는 한계도 같이 생각해 보세요.

전달 방식 비교표

/root/sec3/secrets.md 에 환경변수 방식과 파일 마운트 방식을 비교하는 내용을 적고, 다음 네 줄을 정확히 이 형식으로 포함시킵니다.

네 줄의 값은 앞 단계에서 직접 확인한 결과와 일치해야 합니다. 3단계에서 무엇이 보였는지, 5단계에서 무엇이 안 보였는지, 6단계에서 어느 쪽이 재시작을 요구했는지 되짚어 적으세요. 그리고 본문에는 환경변수 방식과 마운트 방식이 모두 언급돼야 합니다.