다음을 통해 공유


보안을 위해 템플릿 사용

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

이 문서에서는 템플릿이 Azure Pipelines에 대한 보안을 간소화하는 방법을 설명합니다. 템플릿은 파이프라인의 외부 구조를 정의하고 악성 코드 침입을 방지할 수 있습니다. 템플릿에는 자격 증명 검사와 같은 작업을 수행하는 단계가 자동으로 포함될 수도 있습니다. 팀 또는 조직 내의 여러 파이프라인이 동일한 구조를 공유하는 경우 템플릿을 사용하는 것이 좋습니다.

보호된 리소스에 대한 검사는 Azure Pipelines 의 기본 보안 프레임워크를 형성합니다. 이러한 검사는 파이프라인 구조, 단계 및 작업에 관계없이 적용됩니다. 템플릿을 사용하여 이러한 검사를 적용할 수 있습니다.

템플릿 포함 및 확장

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

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

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

템플릿을 사용하는 extends 경우 템플릿과 최종 파이프라인 모두에서 공통 구성 부분을 수행하는 데 사용할 includes 수도 있습니다. 전체 참조는 템플릿 사용 참조를 참조하세요.

템플릿 확장

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

예를 들어 다음 템플릿 파일의 이름은 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 파이프라인 보안 기능

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

단계 대상

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

예를 들어 네트워크 액세스를 제한하는 것이 좋습니다. 열린 네트워크 액세스가 없으면 사용자 단계는 권한이 없는 원본에서 패키지를 검색하거나 코드와 비밀을 외부 네트워크 위치에 업로드할 수 없습니다.

다음 예제 파이프라인은 컨테이너 내에서 단계를 실행하기 전에 에이전트 호스트에서 단계를 실행합니다.

resources:
  containers:
  - container: builder
    image: mysecurebuildcontainer:latest
steps:
- script: echo This step runs on the agent host, and it could use Docker commands to tear down or limit the container's network
- script: echo This step runs inside the builder container
  target: builder

에이전트 로깅 명령 제한

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

다음 예제 작업은 속성이 target 아티팩트 게시를 허용하지 않도록 에이전트에 지시하기 때문에 실패합니다.

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

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

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

다음 예제 작업은 태스크가 변수 또는 접두사로 접두ok사로 지정된 변수만 설정할 expectedVar 수 있기 때문에 실패합니다.

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

조건부 단계 또는 작업 실행

특정 조건에서만 실행되도록 단계 및 작업을 제한할 수 있습니다. 다음 예제에서 조건은 제한된 코드가 주 분기에 대해서만 빌드되도록 합니다.

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 실행을 방지합니다. 임시 스크립트의 전체 잠금을 위해 차단 BatchScript 하고 ShellScript.

# 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 will be stripped out and not run!
    - bash: echo This step will be stripped out and not run!
    - powershell: echo "This step will be stripped out and not run!"
    - pwsh: echo "This step will be stripped out and not run!"
    - script: echo This step will be stripped out and not run!
    - task: CmdLine@2
      displayName: Test - Will be stripped out
      inputs:
        script: echo This step will be stripped out and not run!
    - task: MyOtherTask@2

형식이 안전한 매개 변수

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

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

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

pool: ${{ parameters.userpool }}
steps:
- script: # ... removed for clarity

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

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

템플릿 단계

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

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 템플릿을 확장하고 승인을 요구합니다. 파이프라인 오류를 보여 주려면 params.yml 대한 참조를 주석으로 처리합니다.

# azure-pipeline.yml

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

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