


보안을 약하게 만들지 않고 Ansible 인프라 자동화를 작성하고 검토하기
AI에게 Ansible playbook 같은 인프라 자동화를 작성하거나 고치게 할 때 쓴다. 모든 lint와 보안 검사를 경고 없이 통과시키고, 비밀번호와 키를 저장소에 두지 않으며, 네트워크는 가장 아래 계층부터 살피고, 테스트가 버그를 잡는지 증명하고, 실제로 실행한 검사만 보고한다.
이 AI 규칙을 사용하려면
## 보안과 안정성을 우선하고, 요청받은 것만 바꾸기
인프라 자동화를 보안 핵심 코드로 다룬다. 작은 구성 오류도 개인정보 보호, 접근 제어, 라우팅 또는 서비스 가용성을 드러나지 않게 약화할 수 있다.
- 기능 수보다 안정성, 편의성보다 보안, 영리함보다 명확성을 우선한다.
- 요청된 문제만 정확히 해결한다. 무관한 수정과 추측성 기능은 별도 작업으로 분리한다.
- 변경 완료를 선언하기 전에 영향을 받는 자동화를 실행해 가정을 검증한다.
- 새로운 종속성을 쉽게 추가하지 않는다. 모든 종속성은 공격 표면과 유지보수 부담을 늘린다.
- 새로운 추상화나 구성 옵션을 도입하기 전에 저장소의 기존 패턴을 따른다.
- 새 옵션은 각각 추가 테스트 범위로 간주하고, 사용자에게 입증된 필요가 있을 때만 추가한다.
- 보존된 상태가 결함을 숨길 수 있다면 동작 테스트에 새 배포를 사용한다.
## 모든 lint와 보안 검사를 경고 없이 통과하기
검토 전에 저장소의 전체 로컬 품질 게이트를 실행한다. 품질 게이트에는 구성 lint, YAML lint, 언어 lint, shell 분석, 정적 보안 분석, playbook 구문 검사, 관련 단위 테스트가 포함되어야 한다.
지속적 통합에서는 경고를 하나도 허용하지 않는다. 보안에 민감한 자동화에서는 겉보기에 사소한 지적이 손상된 template, 안전하지 않은 file permission 또는 반복 소음에 묻힌 실제 회귀를 나타낼 수 있다.
다음 순서로 지적 사항을 해결한다.
1. 근본 문제를 수정한다.
2. 규칙이 해당 저장소에 적합하지 않다면 담당 도구 구성의 allowlist에 추가하고 이유를 설명한다.
3. inline suppression은 최후의 수단으로만 사용하며 정확한 규칙 이름과 예외 근거를 기록한다.
지적 사항을 영구 경고 목록으로 옮기지 않는다. 규칙은 통과시키거나 장기간 추적할 수 있는 근거와 함께 명시적으로 건너뛰어야 한다.
일반적인 필수 검사에는 다음이 포함된다.
- YAML 문서를 `---`로 시작하고 parser가 요구하면 의미가 모호한 workflow key를 따옴표로 묶는다.
- 광범위한 `ignore_errors`보다 `failed_when` 같은 명시적인 실패 조건을 우선한다.
- Jinja2 spacing을 일관되게 유지한다.
- `umask`에 의존하지 않고 모든 file 또는 directory task에 `mode`를 지정한다.
- shell 변수를 따옴표로 묶고 엄격한 shell error handling을 활성화한다.
- 정적 분석 suppression을 지름길이 아니라 검토 가능한 예외로 다룬다.
변경 사항을 제출하기 전에 차이를 검사하고 실행한 검사를 기록하며, 지속적 통합이 동일한 필수 검증을 수행하는지 확인한다.
## 입력을 일찍 확인하고, 비밀번호와 키를 저장소에 두지 않기
비용이 크거나 상태를 변경하는 작업 전에 입력을 검증한다. 빈 collection, 누락된 구성, 잘못된 address, 호환되지 않는 version, permission 불일치를 신뢰 가능한 가장 이른 지점에서 거부한다.
- file과 directory mode를 명시적으로 설정한다.
- permission, authentication, cryptography 또는 security boundary 오류가 발생하면 실패시킨다. 경고는 실제로 선택적인 동작에만 사용한다.
- 실패한 조건과 안전한 수정 조치를 제시해 오류를 실행 가능하게 만든다.
- 최소 권한을 적용해 permission, account, 보존 credential, process capability를 최소화한다.
- 안전한 기본값, 강력한 cryptography, 엄격한 firewall rule, 최소 logging, 최소 data retention을 사용한다.
- password, private key, access token 또는 provider credential을 source control에 절대 저장하지 않는다.
- 확인된 security vulnerability에 신속히 대응하고 업데이트된 동작을 검증한다.
- 문서화되지 않은 workaround를 피한다. workaround가 존재하는 이유와 상위 issue를 기록해 유지보수 담당자가 나중에 안전하게 사용을 종료할 수 있게 한다.
task, conditional, provider path 또는 module을 추가하기 전에 codebase에서 확립된 pattern을 검색한다. 적합하지 않다는 증거가 없다면 기존 control flow를 재사용한다.
## Ansible loop와 Jinja2 template의 흔한 실수 막기
기존 iteration을 기계적으로 `loop`로 교체하지 않는다. 일부 기존 iteration 형식은 중첩 list를 평탄화하지만 `loop`는 구조를 유지한다. 결과 item shape을 검증하고 예상한 file 또는 resource가 생성되는지 확인한다.
- 기존 list를 다른 list로 감싸지 말고 `loop`에 직접 전달한다.
- 이전 구문이 여러 input을 평탄화했다면 list를 명시적으로 결합한다.
- path 변수 끝에 slash를 두지 않아 separator 중복을 방지한다.
- 읽기 전용 command와 handler에는 `changed_when: false`를 설정해 state report를 정확하게 유지한다.
- command 실패가 명시적으로 예상되고 안전할 때만 `failed_when: false`를 사용한다. 무관한 실패를 숨기지 않는다.
Jinja2 native evaluation에서는 다음을 지킨다.
- 조건에는 `"true"`와 `"false"` 같은 string이 아니라 실제 boolean이 필요하다.
- lookup에 변수를 직접 전달하고 template expression 안에 다른 template expression을 중첩하지 않는다.
- lookup이 file content를 반환하면 JSON text를 명시적으로 parse한다.
- falsy value option을 활성화하지 않으면 `default()`가 빈 string을 대체하지 않는다는 점을 기억한다.
- template이 생성하는 복잡한 list comprehension을 일반 automation loop와 명시적 누적으로 교체한다.
- boolean predicate에는 test를 사용하고 data transformation에는 filter를 사용한다.
automation framework의 실제 filter 또는 충실한 mock으로 template을 테스트한다. 구문 유효성만으로는 native mode 동작을 입증할 수 없다.
## 네트워크 서비스 문제를 수신 대기 포트부터 한 층씩 살피기
모든 연결 증상을 routing failure로 가정하지 말고 가장 낮은 불확실 계층부터 network automation을 debug한다.
1. 예상한 TCP와 UDP socket이 의도한 address와 port에서 listen 중인지 확인한다.
2. service와 socket unit 상태에서 구성 경고를 확인한다.
3. 필요한 kernel network setting을 검증한다.
4. 구성된 resolver를 대상으로 name resolution을 직접 테스트한다.
5. 그 후에만 firewall, address translation, policy routing rule을 조사한다.
socket activation service에서는 service 구성만이 아니라 socket unit을 구성한다. 필요하면 상속된 listener를 비우고 stream과 datagram listener를 모두 정의하며 변경 후 socket을 재시작한다. socket을 비활성화하면 service dependency가 손상될 수 있다.
platform 차이를 의도적으로 고려한다.
- firewall backend는 rule 순서를 바꿀 수 있다. automation이 기대하는 동작의 backend를 고정하거나 검증한다.
- 여러 network 연결이 있는 host는 address translation을 위해 명시적인 outbound interface가 필요할 수 있다.
- 요구 사항을 충족한다면 불필요하게 복잡한 source translation보다 interface를 지정한 단순 masquerading을 우선한다.
- 설치된 tool version을 감지한 후 조건부로 compatibility flag를 추가한다.
- host와 port 구문에서 요구하는 경우 IPv6 endpoint를 bracket으로 묶는다.
- routing 또는 translation rule을 재설계하기 전에 name resolution을 확인한다.
network rule을 단순하게 유지하고 task result에서 성공을 추론하지 말고 packet flow를 테스트한다.
## 회귀 테스트가 버그가 다시 생기면 실제로 실패하는지 증명하기
회귀 테스트는 방지한다고 주장하는 결함을 실제로 감지할 수 있음을 보여야 한다.
1. 실패 사례에 집중한 테스트를 작성한다.
2. 일시적으로 결함을 도입하거나 관련 조건을 변이시킨다.
3. 새 테스트가 의도한 이유로 실패하는지 확인한다.
4. 올바른 구현을 복원하고 테스트가 통과하는지 확인한다.
5. 테스트가 방지하는 정확한 회귀를 문서화한다.
동작이 template, file path, permission, firewall rule 또는 collection shape에 따라 달라지면 생성 text만이 아니라 관찰 가능한 결과를 테스트한다. 상황에 따라 생성된 file, 실제 mode, parse된 value, listener state, packet flow 또는 render 결과를 검증한다.
변경으로 path나 동작이 달라지면 기존 테스트를 업데이트한다. 실패 감지 능력이 입증될 때까지 통과 중인 테스트가 의미 있다고 가정하지 않는다.
## 질문 없이 배포되게 하고, 실제로 한 검사만 보고하기
지속적 통합 또는 agent용 automation은 완전히 비대화형인 실행 경로를 지원해야 한다.
- 승인된 parameter 또는 environment variable로 해당 변수가 미리 정의되면 모든 prompt를 우회한다.
- 핵심 변수 전체 목록과 각 type, default, purpose를 문서화한다.
- provider 전용 credential을 일반 구성과 분리하고 승인된 secret channel을 통해서만 받는다.
- local 또는 영향이 큰 deployment mode에는 명시적인 confirmation variable을 요구한다.
- headless run이 input 부족으로 멈추지 않도록 optional feature에 명시적인 boolean value를 제공한다.
- 여러 interface와 address가 있는 대표적 조건의 clean environment에서 비대화형 경로를 테스트한다.
완료 전에 모든 linter, syntax check, 관련 unit test, 새 deployment 또는 동등한 integration test를 실행한다. 무관한 수정, 근거 없는 dependency, 숨겨진 suppression, 누락된 file mode, secret, 문서화되지 않은 동작이 없는지 차이를 검토한다. 수집한 정확한 증거를 보고하고 정적 검사만 실행했다면 deployment 성공을 주장하지 않는다.
