의존성 해석용 사설 레지스트리
소스 스캔은 클론한 저장소에서 cdxgen을 실행하고, cdxgen은 빌드가 하는 것과
같은 방식으로 mvn·npm·pip를 호출해 프로젝트의 의존성 트리를 해석합니다. 이
의존성 중 일부가 사설 레지스트리(사내 Nexus/Artifactory 미러, 범위가 지정된 npm
레지스트리, 사설 PyPI 인덱스)에 있다면 워커가 그 레지스트리의 자격증명을 가지고
있어야 합니다. 없으면 해석이 중간에 실패하고, 스캔이 만든 SBOM에는 접근하지 못한
의존성이 빠집니다.
이 기능은 사설 레지스트리와 다른 기능입니다. 그쪽(ER3)은 컨테이너 스캔에서 Trivy가 컨테이너 이미지를 pull할 때 인증합니다. 이 문서는 소스 스캔에서 cdxgen이 프로젝트 의존성을 해석할 때 인증하는 방법을 다룹니다. 두 자격증명은 저장 방식도, 쓰이는 도구도 다릅니다.
규칙
다음 네 파일 중 필요한 것만 한 디렉터리 아래에 둡니다.
| 파일 | 생태계 |
|---|---|
settings.xml | Maven |
.npmrc | npm |
pip.conf | pip |
.netrc | 범용 HTTP/Git 기본 인증. 예를 들어 cdxgen의 Go 분석기가 호출하는 git/go list가 접근하는 사설 Go 모듈 VCS 호스트 |
네 개를 다 둘 필요는 없습니다. cdxgen은 나머지가 없어도 개의치 않습니다. 워커는 이
디렉터리를 읽기 전용으로 /etc/trusca/registry라는 고정 경로에 마운트합니다.
경로를 설정 가능하게 두지 않고 고정한 이유는 임의 파일 읽기로 이어지지 않게
하기 위해서입니다. 프로젝트나 스캔 요청의 어떤 값도 이 경로에 무엇이 마운트되는지
정하지 못하고, 오직 운영자의 배포 설정만 정합니다.
파일 내용 예시
settings.xml (Maven)
Maven은 특정 저장소 조회에 어떤 <server> 자격증명을 보낼지 저장소의 <url>이
아니라 <id>로 판단합니다. 스캔 대상 프로젝트의 pom.xml은 원하는 <id>와
<url>을 가진 <repository>를 얼마든지 선언할 수 있고, 그 <id>가 여러분의
settings.xml에 있는 <server>의 id와 우연히 겹치면 Maven은 선언된 저장소와
실제 저장소를 구분하지 못하고 그 자격증명을 그쪽으로 보냅니다. 이런 이유로
<server>만 두는 방식에 기대면 안 됩니다. 대신 <mirror mirrorOf="*">로 모든
조회를 신뢰하는 URL 하나에 고정하세요. 이렇게 하면 프로젝트가 선언한
<repository>는 무시되고, <id>가 가로챌 대상 자체가 남지 않습니다.
<settings>
<mirrors>
<mirror>
<id>internal-mirror</id>
<mirrorOf>*</mirrorOf>
<url>https://nexus.internal.example.com/repository/maven-public/</url>
</mirror>
</mirrors>
<servers>
<server>
<id>internal-mirror</id>
<username>svc</username>
<password>S3cret</password>
</server>
</servers>
</settings>
mirror 없이 <server>만 두면 어떤 문제가 생기는지는 아래 "알려진 한계"를
참고하세요.
.npmrc (npm)
자격증명은 //호스트/경로/:_authToken= 형태로 정확한 레지스트리 호스트에
범위를 지정하세요. 범위를 지정하지 않은 _auth나 _authToken 줄은 이번
설치에서 npm이 조회하는 모든 레지스트리에 적용됩니다. 스캔 대상 프로젝트의
package.json이 publishConfig.registry 필드로 지정한 레지스트리도
포함되므로, 워커에 마운트하는 자격증명에는 범위를 지정하지 않은 형태를 절대
쓰지 마세요.
@myorg:registry=https://registry.internal.example.com/
//registry.internal.example.com/:_authToken=${NPM_TOKEN}
파일 넣기
Docker Compose
.env에 REGISTRY_CONFIG_HOST_PATH를 설정하고(기본값 ./secrets/registry) 그
경로에 파일을 둡니다.
mkdir -p ./secrets/registry
cp /path/to/settings.xml ./secrets/registry/settings.xml
cp /path/to/.npmrc ./secrets/registry/.npmrc
docker-compose.yml은 이 디렉터리를 스캔 파이프라인 워커(프로덕션은
worker-scan, dev는 celery-worker)에 읽기 전용으로 이미 마운트해 둡니다.
디렉터리가 없거나 비어 있어도 문제없습니다. 이 저장소의 cosign 키 마운트와 같은
방식입니다.
.netrc는 가리키는 환경변수가 없는 유일한 파일이라(아래 참고) 별도의 명시적
마운트가 한 줄 더 필요합니다. docker-compose.yml과 docker-compose.dev.yml
모두 디렉터리 마운트 바로 옆에 이 줄을 주석으로 넣어 두었습니다. 파일을 만든
뒤에만 주석을 해제하세요.
- ${REGISTRY_CONFIG_HOST_PATH:-./secrets/registry}/.netrc:/home/trustedoss/.netrc:ro
호스트에 파일이 없는 채로 주석만 해제하면 아무 일도 일어나지 않는 게 아니라 문제가 생깁니다. Docker는 없는 바인드 마운트 원본을 빈 디렉터리로 만들어 버리고, 그 디렉터리가 해당 경로를 파일로 기대하는 모든 것을 가립니다. 파일을 먼저 만드세요.
Helm
Secret을 직접 만들고, 차트가 스캔 워커에만 마운트하게 합니다.
kubectl create secret generic trustedoss-registry-config \
--from-file=settings.xml=./settings.xml \
--from-file=.npmrc=./.npmrc
worker:
scan:
extraVolumes:
- name: registry-config
secret:
secretName: trustedoss-registry-config
extraVolumeMounts:
- name: registry-config
mountPath: /etc/trusca/registry
readOnly: true
extraEnv:
MVN_ARGS: "--settings /etc/trusca/registry/settings.xml"
NPM_CONFIG_USERCONFIG: /etc/trusca/registry/.npmrc
PIP_CONFIG_FILE: /etc/trusca/registry/pip.conf
worker.scan.*은 스캔 파이프라인 워커에만 적용됩니다.
사설 인증기관에 쓰는, 백엔드·beat·워커 둘 다에 적용되는
차트 전체용 env.extraVolumes/env.extraEnv와는 다릅니다. cdxgen을 실행하지
않는 파드가 레지스트리 자격증명을 읽을 이유는 없습니다.
.netrc는 같은 Secret을 subPath를 지정해 한 번 더 마운트해서
/etc/trusca/registry가 아니라 $HOME/.netrc(이 이미지의 HOME은
/home/trustedoss)에 오도록 합니다.
extraVolumeMounts:
- name: registry-config
mountPath: /home/trustedoss/.netrc
subPath: .netrc
readOnly: true
도구가 그 파일을 보게 하기
디렉터리를 마운트하는 것만으로는 아무것도 바뀌지 않습니다.
/etc/trusca/registry를 저절로 읽는 도구는 없습니다. settings.xml,
.npmrc, pip.conf는 각각 해당 도구에게 어디를 볼지 알려 주는 변수가 따로
필요합니다.
| 변수 | 읽는 주체 | 효과 |
|---|---|---|
MVN_ARGS | cdxgen 자체의 Maven 호출 | mvn 명령줄에 붙는 추가 인자입니다. --settings /etc/trusca/registry/settings.xml로 설정하세요. |
NPM_CONFIG_USERCONFIG | npm(표준 npm 환경변수) | 대체 .npmrc 경로. |
PIP_CONFIG_FILE | pip(표준 pip 환경변수) | 대체 pip.conf 경로. |
필요한 만큼 .env에 추가합니다.
MVN_ARGS=--settings /etc/trusca/registry/settings.xml
NPM_CONFIG_USERCONFIG=/etc/trusca/registry/.npmrc
PIP_CONFIG_FILE=/etc/trusca/registry/pip.conf
MAVEN_SETTINGS라는 환경변수는 존재하지 않고, 설정해도 아무 효과가 없습니다.
Maven 자체는 ~/.m2/settings.xml이나 -s/--settings 명령줄 플래그만 읽습니다.
환경변수 형태는 없습니다. MVN_ARGS는 cdxgen이 대신 실행하는 mvn 호출에 추가
인자를 넘기는 cdxgen 자체의 방식이고, --settings <경로>