다음을 통해 공유


보안을 위한 템플릿

Azure DevOps Services | Azure DevOps Server | Azure DevOps Server 2022 | Azure DevOps Server 2020

Azure Pipelines 템플릿을 사용하면 YAML 파이프라인에서 재사용 가능한 콘텐츠, 논리 및 매개 변수를 정의할 수 있습니다. 이 문서에서는 템플릿이 다음을 통해 파이프라인 보안을 강화하는 데 어떻게 도움이 되는지 설명합니다.

  • 악의적인 코드 침입을 방지하기 위해 파이프라인의 외부 구조를 정의합니다.
  • 자격 증명 검사와 같은 작업을 수행하는 단계를 자동으로 포함합니다.
  • Azure Pipelines의 기본 보안 프레임워크를 구성하고 모든 파이프라인 구조 및 구성 요소 에 적용되는 보호된 리소스에 대한 검사를 적용하는 데 도움을 줍니다.

이 문서는 Azure Pipelines에 대한 보안 조치를 구현하는 데 도움이 되는 시리즈의 일부입니다. 자세한 내용은 보안 Azure Pipelines를 참조하세요.

필수 조건

범주 요구 사항
Azure DevOps - Azure DevOps를 안전하고 안전한 Azure Pipelines로 만들기 에서 권장 사항을 구현 합니다.
- YAML 및 Azure Pipelines에 대한 기본 지식 자세한 내용은 첫 번째 파이프라인 만들기를 참조하세요.
권한 - 파이프라인 사용 권한을 수정하려면: 프로젝트 관리자 그룹의 구성원입니다.
- 조직 권한을 수정하려면 : 프로젝트 컬렉션 관리자 그룹의 구성원입니다.

템플릿 포함 및 확장

Azure Pipelines는 템플릿을 포함하고확장합니다.

  • 템플릿에는 includes C++에서와 유사하게 #include 템플릿을 참조하는 템플릿의 코드가 외부 파일에 직접 포함됩니다. 다음 예제 파이프라인은 include-npm-steps.yml 템플릿을 섹션에 삽입합니다 steps .

      steps:
      - template: templates/include-npm-steps.yml 
    
  • 템플릿은 extends 파이프라인의 외부 구조를 정의하고 대상 사용자 지정에 대한 특정 지점을 제공합니다. C++ extends 의 컨텍스트에서 템플릿은 상속과 유사합니다.

템플릿 extends을 사용할 때, 공통 구성 부분을 템플릿과 최종 파이프라인 모두에서 수행하기 위해 includes을(를) 사용할 수도 있습니다. 자세한 내용은 파이프라인에서 YAML 템플릿을 사용하여 재사용 가능하고 안전한 프로세스를 참조하세요.

템플릿 확장

가장 안전한 파이프라인의 경우 먼저 템플릿을 사용합니다 extends . 이러한 템플릿은 파이프라인의 외부 구조를 정의하고 악성 코드 침입을 방지합니다.

다음 예제에서는 template.yml 템플릿 파일을 보여줍니다.

parameters:
- name: usersteps
  type: stepList
  default: []
steps:
- ${{ each step in parameters.usersteps }}:
  - ${{ step }}

다음 예제 파이프라인은 template.yml 템플릿을 확장합니다.

# azure-pipelines.yml
resources:
  repositories:
  - repository: templates
    type: git
    name: MyProject/MyTemplates
    ref: refs/tags/v1

extends:
  template: template.yml@templates
  parameters:
    usersteps:
    - script: echo This is my first step
    - script: echo This is my second step

템플릿을 설정할 extends 때 주요 변경 내용이 기존 파이프라인에 영향을 주지 않도록 특정 Git 분기 또는 태그에 고정하는 것이 좋습니다. 앞의 예제에서는 이 기능을 사용합니다.

파이프라인 보안 기능

YAML 파이프라인 구문에는 몇 가지 기본 제공 보호 기능이 포함되어 있습니다. Extends 템플릿은 파이프라인 보안을 강화하기 위해 그 사용을 강제할 수 있습니다. 다음 제한 사항을 구현할 수 있습니다.

단계 대상

호스트가 아닌 컨테이너에서 실행되도록 지정된 단계를 제한할 수 있습니다. 컨테이너의 단계는 에이전트 호스트에 액세스할 수 없으므로 에이전트 구성을 수정하거나 나중에 실행하기 위해 악성 코드를 남길 수 없습니다.

예를 들어 컨테이너에서 사용자 단계를 실행하여 네트워크에 액세스하지 못하도록 할 수 있으므로 권한이 없는 소스에서 패키지를 검색하거나 코드와 비밀을 외부 위치에 업로드할 수 없습니다.

다음 예제 파이프라인은 잠재적으로 호스트 네트워크를 변경할 수 있는 에이전트 호스트에서 단계를 실행한 다음, 네트워크 액세스를 제한하는 컨테이너 내의 단계를 실행합니다.

resources:
  containers:
  - container: builder
    image: mysecurebuildcontainer:latest
steps:
- script: echo This step runs on the agent host
- script: echo This step runs inside the builder container
  target: builder

형식이 안전한 매개 변수

파이프라인을 실행하기 전에 템플릿과 해당 매개 변수가 상수로 변환됩니다. 템플릿 매개 변수는 입력 매개 변수의 형식 안전성을 향상시킬 수 있습니다.

다음 예제 템플릿에서 매개 변수는 문자열을 허용하는 대신 특정 선택 항목을 열거하여 사용 가능한 파이프라인 풀 옵션을 제한합니다.

# template.yml
parameters:
- name: userpool
  type: string
  default: Azure Pipelines
  values:
  - Azure Pipelines
  - private-pool-1
  - private-pool-2

pool: ${{ parameters.userpool }}
steps:
- script: echo Hello world

템플릿을 확장하려면 파이프라인에서 사용 가능한 풀 선택 항목 중 하나를 지정해야 합니다.

# azure-pipelines.yml
extends:
  template: template.yml
  parameters:
    userpool: private-pool-1

에이전트 로깅 명령 제한

사용자는 표준 출력에 인쇄되는 특수 형식의 문자열인 로깅 명령을 사용하여 서비스를 요청합니다. 로깅 명령이 사용자 단계에 제공하는 서비스를 제한할 수 있습니다. 모드에서는 restricted 아티팩트 업로드 및 테스트 결과 연결과 같은 대부분의 에이전트 서비스를 로깅 명령에 사용할 수 없습니다.

속성 target는 에이전트에게 아티팩트 게시를 제한하도록 지시하므로, 다음 예제에서 아티팩트 게시 작업이 실패합니다.

- task: PublishBuildArtifacts@1
  inputs:
    artifactName: myartifacts
  target:
    commands: restricted

로깅 명령의 변수

setvariable 명령은 restricted 모드에서 계속 허용되므로 REST API를 통해 검색된 열린 이슈와 같이 사용자가 제공한 데이터를 출력하는 작업은 삽입 공격에 취약할 수 있습니다. 악의적인 사용자 콘텐츠는 후속 작업으로 내보내는 변수를 환경 변수로 설정하고 에이전트 호스트를 손상시킬 수 있습니다.

이 위험을 완화하기 위해 로깅 명령을 사용하여 설정할 수 있는 변수를 명시적으로 선언할 setvariable 수 있습니다. 빈 목록을 settableVariables지정하면 모든 변수 설정이 허용되지 않습니다.

예제에서는 settableVariablesexpectedVar로 제한하고, ok로 시작하는 변수를 모두 제한합니다. 태스크가 호출 BadVar된 다른 변수를 설정하려고 하므로 실패합니다.

- task: PowerShell@2
  target:
    commands: restricted
    settableVariables:
    - expectedVar
    - ok*
  inputs:
    targetType: 'inline'
    script: |
      Write-Host "##vso[task.setvariable variable=BadVar]myValue"

조건부 단계 또는 작업 실행

특정 조건에서만 실행되도록 단계 및 작업을 제한할 수 있습니다. 다음 예제는 제한된 코드가 main 브랜치에 대해서만 빌드되도록 보장합니다.

jobs:
- job: buildNormal
  steps:
  - script: echo Building the normal, unsensitive part
- ${{ if eq(variables['Build.SourceBranchName'], 'refs/heads/main') }}:
  - job: buildMainOnly
    steps:
    - script: echo Building the restricted part that only builds for main branch

구문 수정

Azure Pipelines 템플릿에는 YAML 구문을 반복하고 수정할 수 있는 유연성이 있습니다. 반복을 사용하여 특정 YAML 보안 기능을 적용할 수 있습니다.

템플릿은 승인된 작업만 실행할 수 있도록 사용자 단계를 다시 작성할 수도 있습니다. 예를 들어 템플릿은 인라인 스크립트 실행을 방지할 수 있습니다.

다음 예제 템플릿은 스크립트 단계 유형bash, powershellpwshscript 실행을 방지합니다. 스크립트를 완전히 잠그기 위해 BatchScriptShellScript를 차단할 수 있습니다.

# template.yml
parameters:
- name: usersteps
  type: stepList
  default: []
steps:
- ${{ each step in parameters.usersteps }}:
  - ${{ if not(or(startsWith(step.task, 'Bash'),startsWith(step.task, 'CmdLine'),startsWith(step.task, 'PowerShell'))) }}:  
    - ${{ step }}
  # The following lines replace tasks like Bash@3, CmdLine@2, PowerShell@2
  - ${{ else }}:  
    - ${{ each pair in step }}:
        ${{ if eq(pair.key, 'inputs') }}:
          inputs:
            ${{ each attribute in pair.value }}:
              ${{ if eq(attribute.key, 'script') }}:
                script: echo "Script removed by template"
              ${{ else }}:
                ${{ attribute.key }}: ${{ attribute.value }}
        ${{ elseif ne(pair.key, 'displayName') }}:
          ${{ pair.key }}: ${{ pair.value }}

          displayName: 'Disabled by template: ${{ step.displayName }}'

이전 템플릿을 확장하는 다음 예제 파이프라인에서는 스크립트 단계가 제거되고 실행되지 않습니다.

# azure-pipelines.yml
extends:
  template: template.yml
  parameters:
    usersteps:
    - task: MyTask@1
    - script: echo This step is stripped out and not run
    - bash: echo This step is stripped out and not run
    - powershell: echo "This step is stripped out and not run"
    - pwsh: echo "This step is stripped out and not run"
    - script: echo This step is stripped out and not run
    - task: CmdLine@2
      displayName: Test - stripped out
      inputs:
        script: echo This step is stripped out and not run
    - task: MyOtherTask@2

템플릿 단계

템플릿은 자격 증명 검사 또는 정적 코드 검사를 수행하는 등 파이프라인의 단계를 자동으로 포함할 수 있습니다. 다음 템플릿은 모든 작업의 사용자 단계 전후에 단계를 삽입합니다.

parameters:
  jobs: []

jobs:
- ${{ each job in parameters.jobs }}: 
  - ${{ each pair in job }}:  
      ${{ if ne(pair.key, 'steps') }}:
        ${{ pair.key }}: ${{ pair.value }}
    steps:                            
    - task: CredScan@1 
    - ${{ job.steps }} 
    - task: PublishMyTelemetry@1 
      condition: always()

템플릿 적용

보안 메커니즘으로서의 템플릿의 효율성은 적용에 의존합니다. 템플릿 사용을 적용하기 위한 주요 제어 지점은 보호된 리소스입니다.

승인을 구성하고 에이전트 풀 또는 다른 보호된 리소스(예: 리포지토리)에 대한 검사를 구성할 수 있습니다. 예를 들어 리포지토리 리소스 검사 추가를 참조 하세요.

필수 템플릿

특정 템플릿의 사용을 적용하려면 리소스에 대한 서비스 연결에 필요한 템플릿 검사를 구성합니다. 이 검사는 파이프라인이 템플릿에서 확장되는 경우에만 적용됩니다.

파이프라인 작업을 볼 때 검사 상태를 모니터링할 수 있습니다. 파이프라인이 필요한 템플릿에서 확장되지 않으면 검사에 실패합니다.

실패한 승인 확인을 보여 주는 스크린샷.

필요한 템플릿을 사용하면 확인이 통과합니다.

통과된 승인 확인을 보여 주는 스크린샷.

예를 들어 다음 params.yml 템플릿을 확장하는 파이프라인에서 참조해야 합니다.

# params.yml
parameters:
- name: yesNo 
  type: boolean
  default: false
- name: image
  displayName: Pool Image
  type: string
  default: ubuntu-latest
  values:
  - windows-latest
  - ubuntu-latest
  - macOS-latest

steps:
- script: echo ${{ parameters.yesNo }}
- script: echo ${{ parameters.image }}

다음 예제 파이프라인은 params.yml 템플릿을 확장하고 승인을 요구합니다. 파이프라인 오류를 보여 주려면 extends 대한 참조를 주석으로 처리합니다.

# azure-pipeline.yml

resources:
 containers:
     - container: my-container
       endpoint: my-service-connection
       image: mycontainerimages

extends:
    template: params.yml
    parameters:
        yesNo: true
        image: 'windows-latest'