diff --git a/task/apko/0.1/README.md b/task/apko/0.1/README.md new file mode 100644 index 0000000000..aa463f82b9 --- /dev/null +++ b/task/apko/0.1/README.md @@ -0,0 +1,233 @@ +# apko Task + +This Task builds minimal or distroless container images using [apko](https://github.com/chainguard-dev/apko) from Chainguard. + +## Install the Task + +```bash +kubectl apply -f https://api.hub.tekton.dev/v1/resource/tekton/task/apko/0.1/raw +# or +kubectl apply -f https://raw.githubusercontent.com/tektoncd/catalog/main/task/apko/0.1/apko.yaml +``` + +## Parameters + +| Name | Description | Default | +|------|-------------|---------| +| `REGISTRY` | Container registry to push the image to | - | +| `PASSWORD` | Password for the container registry | - | +| `USERNAME` | Username for the container registry | `AWS` | +| `IMAGE` | Name (reference) of the image to build | - | +| `CONFIGFILE` | YAML file that configures the target image | `config.yaml` | +| `CONTEXT` | The build context where apko will search for config files | `./` | +| `EXTRA_ARGS` | Additional arguments to pass to apko | `[]` | +| `BUILDER_IMAGE` | The image on which builds will run | `cgr.dev/chainguard/apko:latest` | + +## Workspaces + +- **source**: Contains the apko configuration file(s) +- **dockerconfig**: (Optional) Contains a Docker `config.json` for authentication + +## Results + +- **IMAGE_DIGEST**: Digest of the built image +- **IMAGE_URL**: Full reference URL of the built image + +## Example Usage + +### Basic Example + +This example builds an image using a config file in the repository root: + +```yaml +apiVersion: tekton.dev/v1beta1 +kind: TaskRun +metadata: + name: apko-example +spec: + taskRef: + name: apko + workspaces: + - name: source + persistentVolumeClaim: + claimName: my-source + params: + - name: REGISTRY + value: ghcr.io/my-org + - name: PASSWORD + valueFrom: + secretKeyRef: + name: registry-credentials + key: password + - name: USERNAME + value: my-username + - name: IMAGE + value: ghcr.io/my-org/my-app:latest + - name: CONFIGFILE + value: apko.yaml +``` + +### Example apko Configuration + +Here's a simple `apko.yaml` configuration: + +```yaml +contents: + repositories: + - https://dl-cdn.alpinelinux.org/alpine/edge/main + packages: + - alpine-baselayout-data + - busybox + - ca-certificates-bundle + - tzdata + +accounts: + groups: + - groupname: nonroot + gid: 65532 + users: + - username: nonroot + uid: 65532 + run-as: 65532 + +entrypoint: + command: /bin/sh - + +archs: + - x86_64 + - arm64 +``` + +### Using Registry Credentials in Tekton + +When working with private registries, you can use Kubernetes secrets to store and manage your credentials securely. Here's how to create a secret for a container registry: + +```bash +# Create a registry secret +kubectl create secret docker-registry regcred \ + --docker-server= \ + --docker-username= \ + --docker-password= \ + --docker-email= +``` + +Then reference it in your TaskRun or Pipeline: + +```yaml +workspaces: + - name: dockerconfig + secret: + secretName: regcred + items: + - key: .dockerconfigjson + path: config.json +``` + +### Cloud Provider Authentication + +#### AWS ECR Authentication + +To authenticate with Amazon ECR and get temporary credentials: + +```bash +# Login to ECR (requires AWS CLI v2) +aws ecr get-login-password --region | docker login --username AWS --password-stdin .dkr.ecr..amazonaws.com + +# Get temporary credentials for Tekton +REGISTRY=".dkr.ecr..amazonaws.com" +TOKEN=$(aws ecr get-authorization-token --output text --query 'authorizationData[].authorizationToken' | base64 -d | cut -d: -f2) + +# Use these values in your TaskRun: +# - REGISTRY: $REGISTRY +# - USERNAME: AWS +# - PASSWORD: $TOKEN +``` + +#### Azure Container Registry (ACR) Authentication + +To authenticate with Azure Container Registry: + +```bash +# Login to ACR (requires Azure CLI) +az login +az acr login --name + +# Get credentials +REGISTRY=".azurecr.io" +TOKEN=$(az acr login --name --expose-token --output tsv --query accessToken) + +# Use these values in your TaskRun: +# - REGISTRY: $REGISTRY +# - USERNAME: 00000000-0000-0000-0000-000000000000 +# - PASSWORD: $TOKEN +``` + +#### Google Container Registry (GCR) Authentication + +For Google Container Registry: + +```bash +# Login to GCR (requires gcloud CLI) +gcloud auth login +gcloud auth configure-docker + +# Get access token +REGISTRY="gcr.io" +TOKEN=$(gcloud auth print-access-token) + +# Use these values in your TaskRun: +# - REGISTRY: $REGISTRY +# - USERNAME: oauth2accesstoken +# - PASSWORD: $TOKEN +``` + +> **Note:** For production environments, consider using: +> - AWS: IAM roles for service accounts (IRSA) +> - Azure: Managed identities +> - GCP: Workload Identity + +### Using with Tekton Pipelines + +Here's how you can use the task in a Pipeline: + +```yaml +apiVersion: tekton.dev/v1beta1 +kind: Pipeline +metadata: + name: build-and-deploy +spec: + workspaces: + - name: source + - name: dockerconfig + tasks: + - name: build-image + taskRef: + name: apko + workspaces: + - name: source + workspace: source + - name: dockerconfig + workspace: dockerconfig + params: + - name: REGISTRY + value: ghcr.io/my-org + - name: PASSWORD + valueFrom: + secretKeyRef: + name: registry-credentials + key: password + - name: USERNAME + value: my-username + - name: IMAGE + value: ghcr.io/my-org/my-app:latest +``` + +## Security + +This task runs with minimal privileges by default, dropping all Linux capabilities and running with a non-root user where possible. The build steps require root access for apko to function correctly. + +## Changelog + +### 0.1 + +- Initial release of the apko task diff --git a/task/apko/0.1/apko.yaml b/task/apko/0.1/apko.yaml new file mode 100644 index 0000000000..efaf22a657 --- /dev/null +++ b/task/apko/0.1/apko.yaml @@ -0,0 +1,170 @@ +apiVersion: tekton.dev/v1beta1 +kind: Task +metadata: + name: apko + labels: + app.kubernetes.io/version: "0.1" + annotations: + tekton.dev/pipelines.minVersion: "0.17.0" + tekton.dev/categories: Image Build + tekton.dev/tags: image-build + tekton.dev/displayName: "Build and upload minimal container image using apko" + tekton.dev/platforms: "linux/amd64" + # Security-related annotations + apparmor.security.beta.kubernetes.io/allowedProfileNames: 'runtime/default' + seccomp.security.alpha.kubernetes.io/allowedProfileNames: 'runtime/default' +spec: + description: >- + This Task builds a minimal Docker image with apko and pushes it to a registry. + + Using the apko build tool, this task builds minimal or even + distroless Docker images from a config file. The build artifacts are automatically + published to the specified registry. + + This Task stores the image name and digest as results, allowing Tekton Chains to pick up + that an image was built & sign it. + params: + - name: REGISTRY + type: string + description: Container registry URL (e.g., ghcr.io, gcr.io, docker.io) + - name: PASSWORD + type: string + description: | + Password or token for the container registry. For better security, use a Kubernetes Secret: + `kubectl create secret generic registry-credentials --from-literal=password=YOUR_PASSWORD` + - name: USERNAME + type: string + default: "AWS" + description: Username for the container registry + - name: IMAGE + description: Name (reference) of the image to build (e.g., my-org/my-app:latest) + - name: CONFIGFILE + description: yaml file that configures the target image. + default: config.yaml + - name: CONTEXT + description: The build context where apko shall search for config files. + default: ./ + - name: EXTRA_ARGS + type: array + default: [] + description: Additional arguments to pass to the apko command + - name: BUILDER_IMAGE + description: The image on which builds will run. Use a specific version tag instead of 'latest' for better security. + default: cgr.dev/chainguard/apko@sha256:5b93802c7962992de6d79e4473bb233cc4104874af013b58ff4a1041378bb622 + - name: CPU_REQUEST + description: CPU request for the build container + default: 500m + - name: CPU_LIMIT + description: CPU limit for the build container + default: 1000m + - name: MEMORY_REQUEST + description: Memory request for the build container + default: 1Gi + - name: MEMORY_LIMIT + description: Memory limit for the build container + default: 2Gi + workspaces: + - name: source + description: Holds the config file and build context + - name: dockerconfig + description: Includes a docker `config.json` + optional: true + mountPath: /root/.docker + results: + - name: IMAGE_DIGEST + description: Digest of the image just built. + - name: IMAGE_URL + description: URL of the image just built. + steps: + - name: login-to-ecr + resources: + requests: + cpu: $(params.CPU_REQUEST) + memory: $(params.MEMORY_REQUEST) + limits: + cpu: $(params.CPU_LIMIT) + memory: $(params.MEMORY_LIMIT) + workingDir: $(workspaces.source.path) + image: $(params.BUILDER_IMAGE) + env: + - name: DOCKER_CONFIG + value: $(workspaces.source.path)/.docker + args: + - login + - --workdir=$(workspaces.source.path)/$(params.CONTEXT) + - --username=$(params.USERNAME) + - --password=$(params.PASSWORD) + - $(params.REGISTRY) + # Security context for the login step + securityContext: + # apko requires root access for package installation and image building operations + runAsUser: 0 + runAsGroup: 65532 + # Prevent privilege escalation and limit capabilities + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + # Don't run in privileged mode + privileged: false + procMount: Default + # Use the container runtime's default seccomp profile + seccompProfile: + type: RuntimeDefault + # Drop all Linux capabilities + capabilities: + drop: + - ALL + + - name: build-and-publish + resources: + requests: + cpu: $(params.CPU_REQUEST) + memory: $(params.MEMORY_REQUEST) + limits: + cpu: $(params.CPU_LIMIT) + memory: $(params.MEMORY_LIMIT) + workingDir: $(workspaces.source.path) + image: $(params.BUILDER_IMAGE) + env: + - name: DOCKER_CONFIG + value: $(workspaces.source.path)/.docker + args: + - publish + - --workdir=$(workspaces.source.path)/$(params.CONTEXT) + - --sbom=false + $(params.EXTRA_ARGS) + - $(params.CONFIGFILE) + - $(params.IMAGE) + # Security context for the build step + securityContext: + # apko requires root access for package installation and image building operations + runAsUser: 0 + runAsGroup: 65532 + # Prevent privilege escalation and limit capabilities + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + # Don't run in privileged mode + privileged: false + procMount: Default + # Use the container runtime's default seccomp profile + seccompProfile: + type: RuntimeDefault + # Drop all Linux capabilities + capabilities: + drop: + - ALL + + - name: write-url + image: public.ecr.aws/docker/library/bash:5.2.15-alpine3.16@sha256:46aef8866a15877c36cf72736f1433e827c648d27dfcc9f1c1f187e8df762be9 + securityContext: + runAsNonRoot: true + runAsUser: 65532 # non-root user + runAsGroup: 65532 + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + capabilities: + drop: + - ALL + script: | + set -euo pipefail + image="$(params.IMAGE)" + echo -n "${image}" | tee "$(results.IMAGE_URL.path)" diff --git a/task/apko/0.1/tests/README.md b/task/apko/0.1/tests/README.md new file mode 100644 index 0000000000..b9239d6d84 --- /dev/null +++ b/task/apko/0.1/tests/README.md @@ -0,0 +1,59 @@ +# apko Task Tests + +This directory contains test files for the apko Task. + +## Prerequisites + +- A Kubernetes cluster with Tekton Pipelines installed +- `kubectl` configured to communicate with your cluster +- Access to a container registry for pushing images + +## Test Files + +- `test-apko-config.yaml`: Contains a ConfigMap with a sample apko configuration +- `apko-test.yaml`: A TaskRun that uses the apko Task + +## Running the Tests + +1. Apply the test configuration: + + ```bash + kubectl apply -f test-apko-config.yaml + ``` + +2. Update the `apko-test.yaml` file with your container registry credentials: + - Set `spec.params[0].value` to your container registry URL + - Set `spec.params[1].value` to your container registry password or token + +3. Apply the TaskRun: + + ```bash + kubectl apply -f apko-test.yaml + ``` + +4. Monitor the TaskRun: + + ```bash + tkn taskrun logs -f apko-test + ``` + +## Test Verification + +After the TaskRun completes successfully, verify: + +1. The image was built and pushed to your container registry +2. The TaskRun outputs the image digest and URL +3. The image can be pulled and runs the specified command + +## Cleanup + +To clean up the test resources: + +```bash +kubectl delete -f test-apko-config.yaml +kubectl delete taskrun apko-test +``` + +## Note + +These tests require access to a container registry and valid credentials. The tests are skipped by default in CI/CD pipelines. diff --git a/task/apko/0.1/tests/apko-test.yaml b/task/apko/0.1/tests/apko-test.yaml new file mode 100644 index 0000000000..5aaadca135 --- /dev/null +++ b/task/apko/0.1/tests/apko-test.yaml @@ -0,0 +1,28 @@ +apiVersion: tekton.dev/v1beta1 +kind: TaskRun +metadata: + name: apko-test + annotations: + # This test requires access to a container registry + tekton.dev/pipelines.skip: "true" +spec: + taskRef: + name: apko + workspaces: + - name: source + configMap: + name: apko-test-config + items: + - key: apko.yaml + path: apko.yaml + params: + - name: REGISTRY + value: "" # Must be set to a valid registry + - name: PASSWORD + value: "" # Must be set to a valid password + - name: USERNAME + value: "testuser" + - name: IMAGE + value: "test-image:latest" + - name: CONFIGFILE + value: "apko.yaml" diff --git a/task/apko/0.1/tests/pre-apply-task-hook.sh b/task/apko/0.1/tests/pre-apply-task-hook.sh new file mode 100644 index 0000000000..4667b397fd --- /dev/null +++ b/task/apko/0.1/tests/pre-apply-task-hook.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +# Add an internal registry as sidecar to the task so we can upload to it directly +# from our tests without having to go to an external registry. +add_sidecar_registry ${TMPF} + diff --git a/task/apko/0.1/tests/test-apko-config.yaml b/task/apko/0.1/tests/test-apko-config.yaml new file mode 100644 index 0000000000..7e46e185a0 --- /dev/null +++ b/task/apko/0.1/tests/test-apko-config.yaml @@ -0,0 +1,31 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: apko-test-config +data: + apko.yaml: | + contents: + repositories: + - https://dl-cdn.alpinelinux.org/alpine/edge/main + packages: + - alpine-baselayout-data + - busybox + - ca-certificates-bundle + - tzdata + + accounts: + groups: + - groupname: nonroot + gid: 65532 + users: + - username: nonroot + uid: 65532 + run-as: 65532 + + entrypoint: + command: /bin/echo + arguments: ["Hello, apko!"] + + archs: + - x86_64 + - arm64