4.18. 非 root ユーザーとして Buildah を使用したコンテナーイメージのビルド

コンテナーで root ユーザーとして Pipelines を実行すると、コンテナープロセスとホストが他の悪意のあるリソースにさらされる可能性があります。コンテナー内の特定の root 以外のユーザーとしてワークロードを実行すると、このタイプの露出を減らすことができます。非 root ユーザーとして Buildah を使用してコンテナーイメージのビルドを実行するには、次の手順を実行します。

  • カスタムサービスアカウント (SA) とセキュリティーコンテキスト制約 (SCC) を定義します。
  • ID が 1000build ユーザーを使用するように Buildah を設定します。
  • カスタム設定マップを使用してタスクの実行を開始するか、パイプラインの実行と統合します。

4.18.1. カスタムサービスアカウントとセキュリティーコンテキストの制約の設定

デフォルトの pipeline SA では、namespace の範囲外のユーザー ID を使用できます。デフォルト SA への依存を減らすために、ユーザー ID 1000build ユーザーに必要なクラスターロールとロールバインディングを使用して、カスタム SA と SCC を定義できます。

重要

現時点で、Buildah がコンテナー内で正常に実行されるためには、allowPrivilegeEscalation 設定を有効にする必要があります。この設定により、Buildah は非 root ユーザーとして実行するときに SETUID および SETGID 機能を活用できます。

手順

  • 必要なクラスターロールとロールバインディングを使用して、カスタム SA と SCC を作成します。

    例: 使用される ID が 1000 のカスタム SA および SCC

    apiVersion: v1
    kind: ServiceAccount
    metadata:
      name: pipelines-sa-userid-1000 1
    ---
    kind: SecurityContextConstraints
    metadata:
      annotations:
      name: pipelines-scc-userid-1000 2
    allowHostDirVolumePlugin: false
    allowHostIPC: false
    allowHostNetwork: false
    allowHostPID: false
    allowHostPorts: false
    allowPrivilegeEscalation: true 3
    allowPrivilegedContainer: false
    allowedCapabilities: null
    apiVersion: security.openshift.io/v1
    defaultAddCapabilities: null
    fsGroup:
      type: MustRunAs
    groups:
    - system:cluster-admins
    priority: 10
    readOnlyRootFilesystem: false
    requiredDropCapabilities:
    - MKNOD
    - KILL
    runAsUser: 4
      type: MustRunAs
      uid: 1000
    seLinuxContext:
      type: MustRunAs
    supplementalGroups:
      type: RunAsAny
    users: []
    volumes:
    - configMap
    - downwardAPI
    - emptyDir
    - persistentVolumeClaim
    - projected
    - secret
    ---
    apiVersion: rbac.authorization.k8s.io/v1
    kind: ClusterRole
    metadata:
      name: pipelines-scc-userid-1000-clusterrole 5
    rules:
    - apiGroups:
      - security.openshift.io
      resourceNames:
      - pipelines-scc-userid-1000
      resources:
      - securitycontextconstraints
      verbs:
      - use
    ---
    apiVersion: rbac.authorization.k8s.io/v1
    kind: RoleBinding
    metadata:
      name: pipelines-scc-userid-1000-rolebinding 6
    roleRef:
      apiGroup: rbac.authorization.k8s.io
      kind: ClusterRole
      name: pipelines-scc-userid-1000-clusterrole
    subjects:
    - kind: ServiceAccount
      name: pipelines-sa-userid-1000

1
カスタム SA を定義します。
2
runAsUser フィールドを変更して、制限された権限に基づいて作成されたカスタム SCC を定義します。
3
現時点で、Buildah がコンテナー内で正常に実行されるためには、allowPrivilegeEscalation 設定を有効にする必要があります。この設定により、Buildah は非 root ユーザーとして実行するときに SETUID および SETGID 機能を活用できます。
4
カスタム SA を介してカスタム SCC にアタッチされた Pod を、ユーザー ID が 1000 として実行されるように制限します。
5
カスタム SCC を使用するクラスターロールを定義します。
6
カスタム SCC を使用するクラスターロールをカスタム SA にバインドします。

4.18.2. build ユーザーを使用するための Buildah の設定

ユーザー ID が 1000build ユーザーを使用する Buildah タスクを定義できます。

手順

  1. buildah クラスタータスクのコピーを通常のタスクとして作成します。

    $ oc get clustertask buildah -o yaml | yq '. |= (del .metadata |= with_entries(select(.key == "name" )))' | yq '.kind="Task"' | yq '.metadata.name="buildah-as-user"' | oc create -f -
  2. コピーした buildah タスクを編集します。

    $ oc edit task buildah-as-user

    例: build ユーザーで変更された Buildah タスク

    apiVersion: tekton.dev/v1beta1
    kind: Task
    metadata:
      name: buildah-as-user
    spec:
      description: >-
        Buildah task builds source into a container image and
        then pushes it to a container registry.
        Buildah Task builds source into a container image using Project Atomic's
        Buildah build tool.It uses Buildah's support for building from Dockerfiles,
        using its buildah bud command.This command executes the directives in the
        Dockerfile to assemble a container image, then pushes that image to a
        container registry.
      params:
      - name: IMAGE
        description: Reference of the image buildah will produce.
      - name: BUILDER_IMAGE
        description: The location of the buildah builder image.
        default: registry.redhat.io/rhel8/buildah@sha256:99cae35f40c7ec050fed3765b2b27e0b8bbea2aa2da7c16408e2ca13c60ff8ee
      - name: STORAGE_DRIVER
        description: Set buildah storage driver
        default: vfs
      - name: DOCKERFILE
        description: Path to the Dockerfile to build.
        default: ./Dockerfile
      - name: CONTEXT
        description: Path to the directory to use as context.
        default: .
      - name: TLSVERIFY
        description: Verify the TLS on the registry endpoint (for push/pull to a non-TLS registry)
        default: "true"
      - name: FORMAT
        description: The format of the built container, oci or docker
        default: "oci"
      - name: BUILD_EXTRA_ARGS
        description: Extra parameters passed for the build command when building images.
        default: ""
      - description: Extra parameters passed for the push command when pushing images.
        name: PUSH_EXTRA_ARGS
        type: string
        default: ""
      - description: Skip pushing the built image
        name: SKIP_PUSH
        type: string
        default: "false"
      results:
      - description: Digest of the image just built.
        name: IMAGE_DIGEST
        type: string
      workspaces:
      - name: source
      steps:
      - name: build
        securityContext:
          runAsUser: 1000 1
        image: $(params.BUILDER_IMAGE)
        workingDir: $(workspaces.source.path)
        script: |
          echo "Running as USER ID `id`" 2
          buildah --storage-driver=$(params.STORAGE_DRIVER) bud \
            $(params.BUILD_EXTRA_ARGS) --format=$(params.FORMAT) \
            --tls-verify=$(params.TLSVERIFY) --no-cache \
            -f $(params.DOCKERFILE) -t $(params.IMAGE) $(params.CONTEXT)
          [[ "$(params.SKIP_PUSH)" == "true" ]] && echo "Push skipped" && exit 0
          buildah --storage-driver=$(params.STORAGE_DRIVER) push \
            $(params.PUSH_EXTRA_ARGS) --tls-verify=$(params.TLSVERIFY) \
            --digestfile $(workspaces.source.path)/image-digest $(params.IMAGE) \
            docker://$(params.IMAGE)
          cat $(workspaces.source.path)/image-digest | tee /tekton/results/IMAGE_DIGEST
        volumeMounts:
        - name: varlibcontainers
          mountPath: /home/build/.local/share/containers 3
      volumes:
      - name: varlibcontainers
        emptyDir: {}

    1
    Buildah イメージの build ユーザーに対応し、明示的に ID が 1000 のユーザーとして指定してコンテナーを実行します。
    2
    ユーザー ID を表示して、プロセスがユーザー ID 1000 として実行されていることを確認します。
    3
    必要に応じて、ボリュームマウントのパスを変更できます。

4.18.3. カスタムの config map を使用したタスク実行またはパイプライン実行の開始

カスタム Buildah クラスタータスクを定義したら、ユーザー ID が 1000build ユーザーとしてイメージをビルドする TaskRun オブジェクトを作成できます。さらに、TaskRun オブジェクトを PipelineRun オブジェクトの一部として統合できます。

手順

  1. カスタム ConfigMap および Dockerfile オブジェクトを使用して TaskRun オブジェクトを作成します。

    例: Buildah をユーザー ID 1000 として実行するタスク実行

    apiVersion: v1
    data:
      Dockerfile: |
        ARG BASE_IMG=registry.access.redhat.com/ubi8/ubi
        FROM $BASE_IMG AS buildah-runner
        RUN dnf -y update && \
            dnf -y install git && \
            dnf clean all
        CMD git
    kind: ConfigMap
    metadata:
      name: dockerfile 1
    ---
    apiVersion: tekton.dev/v1beta1
    kind: TaskRun
    metadata:
      name: buildah-as-user-1000
    spec:
      serviceAccountName: pipelines-sa-userid-1000 2
      params:
      - name: IMAGE
        value: image-registry.openshift-image-registry.svc:5000/test/buildahuser
      taskRef:
        kind: Task
        name: buildah-as-user
      workspaces:
      - configMap:
          name: dockerfile 3
        name: source

    1
    Dockerfile を使用してソースを取得するなどの事前タスクはなく、タスクの実行に焦点が置かれているため、config map を使用します。
    2
    作成したサービスアカウントの名前。
    3
    buildah-as-user タスクのソースワークスペースとして config map をマウントします。
  2. (オプション) パイプラインと対応するパイプライン実行を作成します。

    例: パイプラインと対応するパイプラインの実行

    apiVersion: tekton.dev/v1beta1
    kind: Pipeline
    metadata:
      name: pipeline-buildah-as-user-1000
    spec:
      params:
      - name: IMAGE
      - name: URL
      workspaces:
      - name: shared-workspace
      - name: sslcertdir
        optional: true
      tasks:
      - name: fetch-repository 1
        taskRef:
          name: git-clone
          kind: ClusterTask
        workspaces:
        - name: output
          workspace: shared-workspace
        params:
        - name: url
          value: $(params.URL)
        - name: subdirectory
          value: ""
        - name: deleteExisting
          value: "true"
      - name: buildah
        taskRef:
          name: buildah-as-user 2
        runAfter:
        - fetch-repository
        workspaces:
        - name: source
          workspace: shared-workspace
        - name: sslcertdir
          workspace: sslcertdir
        params:
        - name: IMAGE
          value: $(params.IMAGE)
    ---
    apiVersion: tekton.dev/v1beta1
    kind: PipelineRun
    metadata:
      name: pipelinerun-buildah-as-user-1000
    spec:
      taskRunSpecs:
        - pipelineTaskName: buildah
          taskServiceAccountName: pipelines-sa-userid-1000 3
      params:
      - name: URL
        value: https://github.com/openshift/pipelines-vote-api
      - name: IMAGE
        value: image-registry.openshift-image-registry.svc:5000/test/buildahuser
      pipelineRef:
        name: pipeline-buildah-as-user-1000
      workspaces:
      - name: shared-workspace 4
        volumeClaimTemplate:
          spec:
            accessModes:
              - ReadWriteOnce
            resources:
              requests:
                storage: 100Mi

    1
    git-clone クラスタータスクを使用して、Dockerfile を含むソースを取得し、変更された Buildah タスクを使用してそれをビルドします。
    2
    変更された Buildah タスクを参照してください。
    3
    Buildah タスク用に作成したサービスアカウントを使用します。
    4
    コントローラーによって自動的に作成される永続ボリューム要求 (PVC) を使用して、git-clone タスクと変更された Buildah タスクの間でデータを共有します。
  3. タスクの実行またはパイプラインの実行を開始します。

4.18.4. 非特権ビルドの制限

非特権ビルドのプロセスは、ほとんどの Dockerfile オブジェクトで機能します。ただし、ビルドが失敗する原因となる既知の制限がいくつかあります。

  • --mount=type=cache オプションの使用は、必要となる権限の問題が原因で失敗する場合があります。詳細は、この記事 を参照してください。
  • --mount=type=secret オプションの使用は失敗します。リソースのマウントには、カスタム SCC によって提供されない追加の機能が必要になるためです。