セキュリティにテンプレートを使用する
Azure DevOps Services | Azure DevOps Server 2022 | Azure DevOps Server 2020
この記事では、テンプレートを使用して Azure Pipelines のセキュリティを効率化する方法について説明します。 テンプレートは、パイプラインの外部構造を定義し、悪意のあるコード侵入を防ぐのに役立ちます。 テンプレートには、資格情報のスキャンなどのタスクを実行する手順を自動的に含めることもできます。 チームまたは組織内の複数のパイプラインが同じ構造を共有している場合は、テンプレートの使用を検討してください。
保護されたリソースをチェック Azure Pipelines の基本的なセキュリティ フレームワークを形成します。 これらのチェックは、パイプラインの構造、ステージ、ジョブに関係なく適用されます。 テンプレートを使用して、これらのチェックを適用できます。
テンプレートを含める、拡張する
Azure Pipelines には、および extends テンプレートが用意されています。
テンプレートには、C++ の
#include
と同様に、テンプレートを参照する外部ファイルに直接テンプレートのコードが含まれます。 次のパイプラインの例では、steps
セクションにinclude-npm-steps.yml テンプレートを挿入します。steps: - template: templates/include-npm-steps.yml
テンプレートを拡張して、パイプラインの外部構造を定義し、対象を絞ったカスタマイズのための特定のポイントを提供します。 C++ のコンテキストでは、
extends
テンプレートは継承に似ています。
extends
テンプレートを使用する場合は、テンプレートと最終的なパイプラインの両方でincludes
を使用して、一般的な構成を行うこともできます。 完全なリファレンスについては、 Template 使用法のリファレンスを参照してください。
テンプレートを拡張する
最も安全なパイプラインの場合は、まず拡張テンプレートを使用します。 これらのテンプレートは、パイプラインの外部構造を定義し、悪意のあるコードがパイプラインに侵入するのを防ぎます。
たとえば、次のテンプレート ファイルの名前は 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
ログ コマンドを使用して、設定可能な変数を明示的に宣言できます。 空のリストを指定すると、すべての変数設定は許可されません。
次の例のタスクは、 expectedVar
変数または ok
のプレフィックスが付いた変数のみを設定できるため、失敗します。
- 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
、 powershell
、 pwsh
、および script
が実行されないようにします。 アドホック スクリプトの完全なロックダウンのために、 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'