diff --git a/action/README.md b/action/README.md index f8ebcb8e..6c2db0f2 100644 --- a/action/README.md +++ b/action/README.md @@ -134,18 +134,25 @@ jobs: flux tag artifact $OCI_REPO:$(git rev-parse --short HEAD) --tag staging ``` -Example workflow for publishing Kubernetes manifests bundled as OCI artifacts to Docker Hub: +### Push and sign Kubernetes manifests to container registries + +Example workflow for publishing Kubernetes manifests bundled as OCI artifacts +which are signed with Cosign and GitHub OIDC: ```yaml -name: push-artifact-production +name: push-sign-artifact on: push: - tags: - - '*' + branches: + - 'main' + +permissions: + packages: write # needed for ghcr.io access + id-token: write # needed for keyless signing env: - OCI_REPO: "oci://docker.io/my-org/app-config" + OCI_REPO: "oci://ghcr.io/my-org/manifests/${{ github.event.repository.name }}" jobs: kubernetes: @@ -155,23 +162,24 @@ jobs: uses: actions/checkout@v3 - name: Setup Flux CLI uses: fluxcd/flux2/action@main - - name: Login to Docker Hub + - name: Setup Cosign + uses: sigstore/cosign-installer@main + - name: Login to GHCR uses: docker/login-action@v2 with: - username: ${{ secrets.DOCKER_USERNAME }} - password: ${{ secrets.DOCKER_PASSWORD }} - - name: Generate manifests - run: | - kustomize build ./manifests/production > ./deploy/app.yaml - - name: Push manifests - run: | - flux push artifact $OCI_REPO:$(git tag --points-at HEAD) \ - --path="./deploy" \ - --source="$(git config --get remote.origin.url)" \ - --revision="$(git tag --points-at HEAD)/$(git rev-parse HEAD)" - - name: Deploy manifests to production + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Push and sign manifests run: | - flux tag artifact $OCI_REPO:$(git tag --points-at HEAD) --tag production + digest_url=$(flux push artifact \ + $OCI_REPO:$(git rev-parse --short HEAD) \ + --path="./manifests" \ + --source="$(git config --get remote.origin.url)" \ + --revision="$(git branch --show-current)/$(git rev-parse HEAD)" |\ + jq -r '. | .repository + "@" + .digest') + + cosign sign $digest_url ``` ### End-to-end testing diff --git a/cmd/flux/push_artifact.go b/cmd/flux/push_artifact.go index 6920a41a..6664c201 100644 --- a/cmd/flux/push_artifact.go +++ b/cmd/flux/push_artifact.go @@ -43,13 +43,14 @@ The command can read the credentials from '~/.docker/config.json' but they can a --source="$(git config --get remote.origin.url)" \ --revision="$(git branch --show-current)/$(git rev-parse HEAD)" - # Push and sign artifact with cosgin + # Push and sign artifact with cosign digest_url = $(flux push artifact \ oci://ghcr.io/org/config/app:$(git rev-parse --short HEAD) \ --source="$(git config --get remote.origin.url)" \ --revision="$(git branch --show-current)/$(git rev-parse HEAD)" \ - --path="./path/to/local/manifest.yaml" | \ - jq -r '. | .image + "@" + .digest') + --path="./path/to/local/manifest.yaml" \ + --output json | \ + jq -r '. | .repository + "@" + .digest') cosign sign $digest_url # Push manifests passed into stdin to GHCR @@ -116,7 +117,7 @@ func init() { pushArtifactCmd.Flags().Var(&pushArtifactArgs.provider, "provider", pushArtifactArgs.provider.Description()) pushArtifactCmd.Flags().StringSliceVar(&pushArtifactArgs.ignorePaths, "ignore-paths", excludeOCI, "set paths to ignore in .gitignore format") pushArtifactCmd.Flags().StringVarP(&pushArtifactArgs.output, "output", "o", "", - "the format in which the artifact digest should be printed. can be 'json' or 'yaml'") + "the format in which the artifact digest should be printed, can be 'json' or 'yaml'") pushCmd.AddCommand(pushArtifactCmd) } @@ -207,15 +208,15 @@ func pushArtifactCmdRun(cmd *cobra.Command, args []string) error { } info := struct { - URL string `json:"url"` - Image string `json:"image"` - Tag string `json:"tag"` - Digest string `json:"digest"` + URL string `json:"url"` + Repository string `json:"repository"` + Tag string `json:"tag"` + Digest string `json:"digest"` }{ - URL: fmt.Sprintf("oci://%s", digestURL), - Image: fmt.Sprintf("%s/%s", digest.RegistryStr(), digest.RepositoryStr()), - Tag: tag.TagStr(), - Digest: digest.DigestStr(), + URL: fmt.Sprintf("oci://%s", digestURL), + Repository: digest.Repository.Name(), + Tag: tag.TagStr(), + Digest: digest.DigestStr(), } switch pushArtifactArgs.output {