Skip to content

Commit

Permalink
add more tests (#23)
Browse files Browse the repository at this point in the history
  • Loading branch information
matheuscscp authored Dec 30, 2023
1 parent 6031973 commit a96757f
Show file tree
Hide file tree
Showing 16 changed files with 562 additions and 191 deletions.
2 changes: 2 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
**/*_test.go
internal/testing/
24 changes: 22 additions & 2 deletions .github/workflows/bootstrap.tf
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

terraform {
backend "gcs" {
bucket = "gke-metadata-server-bootstrap-tf-state"
}
}

locals {
wi_pool_name = google_iam_workload_identity_pool.github_actions.name
gh_sub_prefix = "repo:matheuscscp/gke-metadata-server:environment"
Expand Down Expand Up @@ -149,8 +155,22 @@ resource "google_service_account_iam_member" "pull_request_workload_identity_use
member = "${local.wi_member_prefix}:pull-request"
}

resource "google_storage_bucket_iam_member" "pull_request_cluster_issuer_admin" {
resource "google_service_account" "release" {
project = google_project.gke_metadata_server.name
account_id = "release"
}

resource "google_service_account_iam_member" "release_workload_identity_user" {
service_account_id = google_service_account.release.name
role = "roles/iam.workloadIdentityUser"
member = "${local.wi_member_prefix}:release"
}

resource "google_storage_bucket_iam_binding" "cluster_issuer_admins" {
bucket = "gke-metadata-server-issuer-test"
role = "roles/storage.objectAdmin"
member = google_service_account.pull_request.member
members = [
google_service_account.pull_request.member,
google_service_account.release.member,
]
}
2 changes: 1 addition & 1 deletion .github/workflows/pull-request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
on: [pull_request]

concurrency:
group: pull-request
group: continuous-integration
cancel-in-progress: false

jobs:
Expand Down
66 changes: 66 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# MIT License
#
# Copyright (c) 2023 Matheus Pimenta
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

on:
push:
branches: [main]

concurrency:
group: continuous-integration
cancel-in-progress: false

jobs:
release:
runs-on: ubuntu-latest
environment: release
permissions:
contents: read
id-token: write
packages: write
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v4
with:
go-version: 1.21.5
- run: make tidy
- name: Fail if make tidy produced changes
run: |
if [ -n "$(git diff --name-only)" ] || [ -n "$(git status --porcelain | grep '??')" ]; then
git status
echo ""
echo "The command 'make tidy' produced differences."
echo "Please remember to run it locally before committing your changes."
echo ""
exit 1
fi
- uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- run: make ci-push
- uses: google-github-actions/auth@v2
with:
project_id: gke-metadata-server
workload_identity_provider: projects/637293746831/locations/global/workloadIdentityPools/github-actions/providers/github-actions
service_account: [email protected]
- run: make ci-cluster ci-test
2 changes: 0 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,6 @@ RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-w -s" -o gke-metadata-server \

FROM alpine:3.19.0

# Copy the binary from the builder stage
COPY --from=builder /app/gke-metadata-server .

# Set the binary as the entry point of the container
ENTRYPOINT ["./gke-metadata-server"]
18 changes: 2 additions & 16 deletions Dockerfile.test
Original file line number Diff line number Diff line change
Expand Up @@ -20,21 +20,7 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

FROM google/cloud-sdk:457.0.0-slim

# Install Go (adjust version as needed)
ENV GO_VERSION=1.21.5
RUN apt-get update && apt-get install -y --no-install-recommends wget && \
wget https://golang.org/dl/go${GO_VERSION}.linux-amd64.tar.gz && \
tar -C /usr/local -xzf go${GO_VERSION}.linux-amd64.tar.gz && \
rm go${GO_VERSION}.linux-amd64.tar.gz && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*

# Set environment variables for Go
ENV PATH="/usr/local/go/bin:${PATH}"
ENV GOPATH="/go"
ENV PATH="$GOPATH/bin:$PATH"
FROM golang:1.21.5-alpine3.19

WORKDIR /app

Expand All @@ -44,4 +30,4 @@ RUN go mod download
COPY ./cmd/ ./cmd/
COPY ./internal/ ./internal/

CMD [ "go", "test", "./..." ]
CMD [ "go", "test", "-v", "./..." ]
51 changes: 44 additions & 7 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,26 @@ CI_IMAGE=ghcr.io/matheuscscp/gke-metadata-server/container:ci
.PHONY: test
test:
@if [ "${IMAGE}" == "" ]; then echo "IMAGE variable is required."; exit -1; fi
sed 's|GKE_METADATA_SERVER_IMAGE|${IMAGE}|g' k8s/test.yaml | tee >(kubectl --context kind-kind apply -f -)
sleep 120
kubectl --context kind-kind -n default logs test -c test -f
kubectl --context kind-kind -n kube-system describe $$(kubectl --context kind-kind -n kube-system get po -o name | grep gke)
kubectl --context kind-kind -n default describe po test
kubectl --context kind-kind -n kube-system logs ds/gke-metadata-server | jq
kubectl --context kind-kind -n default logs test -c gke-metadata-proxy | jq
sed 's|<GKE_METADATA_SERVER_IMAGE>|${IMAGE}|g' k8s/test.yaml | tee >(kubectl --context kind-kind apply -f -)
@while : ; do \
sleep 5; \
EXIT_CODE_1=$$(kubectl --context kind-kind -n default get po test -o jsonpath='{.status.containerStatuses[1].state.terminated.exitCode}'); \
EXIT_CODE_2=$$(kubectl --context kind-kind -n default get po test -o jsonpath='{.status.containerStatuses[2].state.terminated.exitCode}'); \
if [ -n "$$EXIT_CODE_1" ] && [ -n "$$EXIT_CODE_2" ]; then \
break; \
fi; \
done; \
echo "Container 'test' exited with code $$EXIT_CODE_1"; \
echo "Container 'test-gcloud' exited with code $$EXIT_CODE_2"; \
kubectl --context kind-kind -n default logs test -c test -f; \
kubectl --context kind-kind -n default logs test -c test-gcloud -f; \
kubectl --context kind-kind -n kube-system describe $$(kubectl --context kind-kind -n kube-system get po -o name | grep gke); \
kubectl --context kind-kind -n default describe po test; \
kubectl --context kind-kind -n kube-system logs ds/gke-metadata-server | jq; \
kubectl --context kind-kind -n default logs test -c gke-metadata-proxy | jq; \
if [ "$$EXIT_CODE_1" != "0" ] || [ "$$EXIT_CODE_2" != "0" ]; then \
exit 1; \
fi

.PHONY: dev-test
dev-test:
Expand All @@ -65,7 +78,9 @@ push:
@if [ "${IMAGE}" == "" ]; then echo "IMAGE variable is required."; exit -1; fi
docker build . -t ${IMAGE}
docker push ${IMAGE}
mv .dockerignore .dockerignore.ignore
docker build . -t ${IMAGE}-test -f Dockerfile.test
mv .dockerignore.ignore .dockerignore
docker push ${IMAGE}-test

.PHONY: dev-push
Expand Down Expand Up @@ -107,6 +122,28 @@ helm-upgrade:
helm-diff:
helm -n kube-system diff upgrade gke-metadata-server helm/gke-metadata-server/ -f k8s/dev-helm-values.yaml

.PHONY: update-branch
update-branch:
git add .
git stash
git fetch --prune --all --force --tags
git update-ref refs/heads/main origin/main
git rebase main
git stash pop

.PHONY: drop-branch
drop-branch:
if [ $$(git status --porcelain=v1 2>/dev/null | wc -l) -ne 0 ]; then \
git status; \
echo ""; \
echo "Are you sure? You have uncommitted changes, consider using scripts/update-branch.sh."; \
exit 1; \
fi

git fetch --prune --all --force --tags
git update-ref refs/heads/main origin/main
BRANCH=$$(git branch --show-current); git checkout main; git branch -D $$BRANCH

.PHONY: bootstrap
bootstrap:
cd .github/workflows/ && terraform init && terraform apply
51 changes: 36 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,21 @@ GKE clusters with GKE Workload Identity enabled. See how GKE Workload Identity

**Disclaimer 1:** This project was not created by Google or by anybody related to Google.
Google enterprise support is not available. **Use this tool at your own risk.**
*(Please feel free to open issues for reporting bugs or vulnerabilities, or for asking questions.)*
*(But please do feel free to open issues for reporting bugs or vulnerabilities, or for asking
questions, help or requesting new features. Feel also free to
[contribute](https://github.com/matheuscscp/gke-metadata-server/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22).)*

**Disclaimer 2:** This tool is *not necessary* for using GCP Workload Identity Federation inside
non-GKE Kubernetes clusters. This is just a facilitator. Kubernetes and GCP Workload Identity
Federation work together by themselves. This tool just makes it so your Pods need less
configuration to use Workload Identity Federation (some level of configuration is still required,
but at least they don't require any particular, per-deployment input, e.g. like the full
Workload Identity Pool Provider resource name), and the integration is closer to how native
GKE Workload Identity works (but still not perfect, as the Service Account impersonation IAM
settings are still slightly different).
but, for example, the full Workload Identity Provider resource name is hidden from the
client Pods and kept only as part of the emulator, see a full example at
[`./k8s/test.yaml`](./k8s/test.yaml)), and the integration is closer to how native GKE Workload
Identity works (but still not perfect, as the impersonation IAM settings are still slightly
different, see the
[Configure GCP Workload Identity Federation for Kubernetes](#configure-gcp-workload-identity-federation-for-kubernetes)
section below).

## Limitations and Caveats

Expand Down Expand Up @@ -67,7 +72,7 @@ Steps:
2. Configure Kubernetes ServiceAccount OIDC Discovery
3. Configure GCP Workload Identity Federation for Kubernetes
4. Deploy `gke-metadata-server` in your cluster
5. (Optional) Verify Supply Chain Security
5. (Optional) Verify Supply Chain Authenticity

### Configure Kubernetes DNS

Expand All @@ -86,7 +91,7 @@ for adding custom cluster-level DNS entries.

Adding an entry to CoreDNS does not work seamlessly for all cases. Depending on how the
application code resolves DNS, the Pod-level DNS configuration mentioned in the link
above may be the only feasible choice. See an example at [`./k8s/test.yaml`](./k8s/test.yaml).
above may be the only feasible choice.

*(Google's Go libraries target the `169.254.169.254` IP address directly. If you are running mostly
Go applications *authenticating through Google's Go libraries* then this DNS configuration may not
Expand Down Expand Up @@ -118,8 +123,12 @@ obtained via Workload Identity Federation for this GitHub repository
Alternatively, this is how you could retrieve these two JSON documents from inside a Pod using `curl`:

```bash
curl -s --cacert /var/run/secrets/kubernetes.io/serviceaccount/ca.crt -H "Authorization: Bearer $(cat /var/run/secrets/kubernetes.io/serviceaccount/token)" "https://kubernetes.default.svc.cluster.local/.well-known/openid-configuration"
curl -s --cacert /var/run/secrets/kubernetes.io/serviceaccount/ca.crt -H "Authorization: Bearer $(cat /var/run/secrets/kubernetes.io/serviceaccount/token)" "https://kubernetes.default.svc.cluster.local/openid/v1/jwks"
curl -s --cacert /var/run/secrets/kubernetes.io/serviceaccount/ca.crt \
-H "Authorization: Bearer $(cat /var/run/secrets/kubernetes.io/serviceaccount/token)" \
"https://kubernetes.default.svc.cluster.local/.well-known/openid-configuration"
curl -s --cacert /var/run/secrets/kubernetes.io/serviceaccount/ca.crt \
-H "Authorization: Bearer $(cat /var/run/secrets/kubernetes.io/serviceaccount/token)" \
"https://kubernetes.default.svc.cluster.local/openid/v1/jwks"
```

Copy the outputs of the two `curl` commands above into files named respectively `openid-config.json`
Expand Down Expand Up @@ -233,21 +242,33 @@ configured and `gke-metadata-server` is properly deployed in your cluster, you'r

### Deploy `gke-metadata-server` in your cluster

A Helm Chart is available in the following Helm OCI Repositories:
A Helm Chart is available in the following [Helm OCI Repositories](https://helm.sh/docs/topics/registries/):

1. `matheuscscp/gke-metadata-server-helm` (Docker Hub)
2. `ghcr.io/matheuscscp/gke-metadata-server/helm` (GitHub Container Registry)
1. `matheuscscp/gke-metadata-server:helm-{tag}` (Docker Hub)
2. `ghcr.io/matheuscscp/gke-metadata-server/helm:{tag}` (GitHub Container Registry)

See the Helm values API at [`./helm/gke-metadata-server/values.yaml`](./helm/gke-metadata-server/values.yaml).

Alternatively, you can write your own Kubernetes manifests and consume only the container images:

1. `matheuscscp/gke-metadata-server` (Docker Hub)
2. `ghcr.io/matheuscscp/gke-metadata-server/container` (GitHub Container Registry)
1. `matheuscscp/gke-metadata-server:container-{tag}` (Docker Hub)
2. `ghcr.io/matheuscscp/gke-metadata-server/container:{tag}` (GitHub Container Registry)

### (Optional) Verify Supply Chain Security
The value of `{tag}` is either a SemVer version or `latest`. Please **DO NOT USE** `dev` and `ci` tags,
as they are not signed and do not represent official releases.

### (Optional) Verify Supply Chain Authenticity

Here's how you should validate the authenticity of the `gke-metadata-server` images... WIP

* Container Image Repositories: `WIP`
* Helm OCI Repositories: `WIP`

# General Notes

Only a single long-lived secret was necessary for setting up the CI infrastructure of this project:
`DOCKER_HUB_ACCESS_TOKEN` 👀

Everything else in this repository runs using GCP Workload Identity Federation or the `GITHUB_TOKEN`, both
the automatic tests testing the Workload Identity Federation features, and the GitHub Workflows themselves
for performing ancillary CI tasks. All the tokens involved in these cases are short-lived.
4 changes: 3 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ go 1.21.5

require (
cloud.google.com/go/storage v1.36.0
github.com/coreos/go-oidc/v3 v3.9.0
github.com/golang-jwt/jwt/v5 v5.2.0
github.com/google/uuid v1.5.0
github.com/prometheus/client_golang v1.18.0
Expand All @@ -28,6 +29,7 @@ require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/go-jose/go-jose/v3 v3.0.1 // indirect
github.com/go-logr/logr v1.3.0 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-openapi/jsonpointer v0.19.6 // indirect
Expand Down Expand Up @@ -70,7 +72,7 @@ require (
golang.org/x/text v0.14.0 // indirect
golang.org/x/time v0.5.0 // indirect
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/appengine v1.6.8 // indirect
google.golang.org/genproto v0.0.0-20231120223509-83a465c0220f // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20231120223509-83a465c0220f // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20231127180814-3a041ad873d4 // indirect
Expand Down
Loading

0 comments on commit a96757f

Please sign in to comment.