아티팩트 정책 검사

Azure DevOps Services

아티팩트 정책은 프로덕션과 같은 중요한 환경에 배포하기 전에 적용됩니다. 이러한 정책은 지정된 파이프라인 실행의 모든 배포 가능한 아티팩트에 대해 평가되며 아티팩트가 준수하지 않는 경우 배포를 차단합니다. 아티팩트 평가를 위해 검사 추가하려면 사용자 지정 정책을 구성해야 합니다. 이 가이드에서는 사용자 지정 정책을 만드는 방법을 설명합니다.

참고

현재 지원되는 아티팩트 형식은 컨테이너 이미지 및 Kubernetes 환경에 대한 것입니다.

필수 구성 요소

읽기 쉽고 쓰기 쉬운 정책을 정의하려면 Rego를 사용합니다.

Rego 쿼리 언어를 숙지합니다. 기본 사항이 수행됩니다.

JSON과 같은 구조화된 문서 모델을 지원하기 위해 Rego는 Datalog를 확장합니다. Rego 쿼리는 OPA에 저장된 데이터에 대한 어설션입니다. 이러한 쿼리를 사용하여 시스템의 예상 상태를 위반하는 데이터 인스턴스를 열거하는 정책을 정의할 수 있습니다.

사용자 지정 정책 만들기

다음은 공유되는 샘플 정책입니다. 요구 사항에 따라 고유한 정책 집합을 빌드할 수 있습니다.

특정 프로젝트/파이프라인 확인

이 정책은 이미지가 Azure Pipelines 및 Pipeline-foo에 의해 빌드되었는지 확인합니다. 이렇게 하려면 파이프라인 정의가 이름 필드를 다음과 같이 재정의해야 합니다. AzureDevOps_$(BuildDefinitionName)_$(Date:yyyyMMdd)$(Rev:.r). 파이프라인 실행 이름 지정에 대한 자세한 내용은 여기를 참조하세요.

allowedBuilder := "AzureDevOps_pipeline-foo"

checkBuilder[errors] {
    trace("Check if images are built by Azure Pipelines")
    resourceUri := values[index].build.resourceUri    
    image := fetchImage(resourceUri)
    builder := values[index].build.build.provenance.builderVersion
    trace(sprintf("%s: builder", [builder]))
    not startswith(builder, "allowedBuilder")
    errors := sprintf("%s: image not built by Azure Pipeline [%s]", [image,builder])
}

fetchRegistry(uri) = reg {
    out := regex.find_n("//.*/", uri, 1)
    reg = trim(out[0], "/")
}

fetchImage(uri) = img {
    out := regex.find_n("/.*@", uri, 1)
    img := trim(out[0], "/@")
}

허용된 레지스트리 확인

이 정책은 이미지가 허용된 레지스트리에서만 있는지 확인합니다.

allowlist = {
 "gcr.io/myrepo",
 "raireg1.azurecr.io"
}

checkregistries[errors] {
    trace(sprintf("Allowed registries: %s", [concat(", ", allowlist)]))
    resourceUri := values[index].image.resourceUri
    registry := fetchRegistry(resourceUri)
    image := fetchImage(resourceUri)
    not allowlist[registry]
    errors := sprintf("%s: source registry not permitted", [image]) 
}

fetchRegistry(uri) = reg {
    out := regex.find_n("//.*/", uri, 1)
    reg = trim(out[0], "/")
}

fetchImage(uri) = img {
    out := regex.find_n("/.*@", uri, 1)
    img := trim(out[0], "/@")
}

금지된 포트 확인

이 정책은 컨테이너 이미지에 노출되는 금지된 포트를 확인합니다.

forbiddenPorts = {
    "80",
    "22"
}

checkExposedPorts[errors] {
    trace(sprintf("Checking for forbidden exposed ports: %s", [concat(", ", forbiddenPorts)]))
    layerInfos := values[index].image.image.layerInfo
    layerInfos[x].directive == "EXPOSE"
    resourceUri := values[index].image.resourceUri
    image := fetchImage(resourceUri)
    ports := layerInfos[x].arguments
    trace(sprintf("exposed ports: %s", [ports]))
    forbiddenPorts[ports]
    errors := sprintf("%s: image exposes forbidden port %s", [image,ports])
}

fetchRegistry(uri) = reg {
    out := regex.find_n("//.*/", uri, 1)
    reg = trim(out[0], "/")
}

fetchImage(uri) = img {
    out := regex.find_n("/.*@", uri, 1)
    img := trim(out[0], "/@")
}

이전 배포 확인

이 정책은 검사가 구성된 특정 환경/리소스에 배포되기 전에 이미지가 하나 이상의 환경에 미리 배포되었는지 확인합니다.

predeployedEnvironments = {
    "env/resource1",
    "env2/resource3"
}

checkDeployedEnvironments[errors] {
    trace(sprintf("Checking if the image has been pre-deployed to one of: [%s]", [concat(", ", predeployedEnvironments)]))
    deployments := values[index].deployment
    deployedAddress := deployments[i].deployment.address
    trace(sprintf("deployed to : %s",[deployedAddress]))
    resourceUri := deployments[i].resourceUri
    image := fetchImage(resourceUri)
    not predeployedEnvironments[deployedAddress]
    trace(sprintf("%s: fails pre-deployed environment condition. found %s", [image,deployedAddress]))
    errors := sprintf("image %s fails pre-deployed environment condition. found %s", [image,deployedAddress])
}

fetchRegistry(uri) = reg {
    out := regex.find_n("//.*/", uri, 1)
    reg = trim(out[0], "/")
}

fetchImage(uri) = img {
    out := regex.find_n("/.*@", uri, 1)
    img := trim(out[0], "/@")
}