Skip to content
This repository has been archived by the owner on Jun 27, 2024. It is now read-only.

Pulling in updates from original #8

Draft
wants to merge 28 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
5bba84e
update some libraries
alexei-led Aug 6, 2021
8f5e6e3
handle no service account case
jefimm Nov 29, 2021
88c7a61
Merge pull request #8 from jefimm/master
alexei-led Mar 30, 2022
3a87589
fix github actions; update K8s versions
alexei-led Mar 30, 2022
f7612c1
update go packages
alexei-led Mar 30, 2022
e0273ef
Merge branch 'master' into update/build
alexei-led Mar 30, 2022
2449063
Merge pull request #10 from doitintl/update/build
alexei-led Mar 30, 2022
82972a6
remove arm platform
alexei-led Mar 30, 2022
f4afab3
increase linter timeout; update go ver
alexei-led Mar 30, 2022
b4b8162
update README [skip ci]
alexei-led Mar 30, 2022
93cb17d
update README [skip ci]
alexei-led Mar 30, 2022
e149eb9
fix README flow
alexei-led Mar 30, 2022
2e3ba03
update README [skip ci]
alexei-led Mar 30, 2022
68ccd1f
update README [skip ci]
alexei-led Mar 30, 2022
3d6790c
update README [skip ci]
alexei-led Mar 30, 2022
2e6d108
update README [skip ci]
alexei-led Mar 30, 2022
e8826f5
update README [skip ci]
alexei-led Mar 30, 2022
fe1c6be
update kube webhook packate (return AdmissionReview)
alexei-led Mar 30, 2022
b7113d7
update README [skip ci]
alexei-led Mar 30, 2022
d25a3d6
log sa email
alexei-led Mar 30, 2022
c00b855
update example
alexei-led Mar 30, 2022
3284dec
deploy on GKE autopilot
alexei-led Apr 3, 2022
496422c
Merge pull request #11 from doitintl/gke-autopilot
alexei-led Apr 3, 2022
c1d48ee
[skip ci] fix spell
alexei-led Nov 2, 2022
ce3fb3c
[skip ci] fix spell
alexei-led Nov 2, 2022
5113e22
scope webhook to namespace; skip namespaces with admission.gtoken/ign…
alexei-led Nov 6, 2022
2e9e2b8
update readme
alexei-led Nov 6, 2022
710e8ca
Merge pull request #16 from doitintl/feature/namespaced
alexei-led Nov 6, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 62 additions & 30 deletions .github/workflows/build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,38 +10,70 @@ on:
jobs:

build:
runs-on: ubuntu-18.04

runs-on: ubuntu-latest
if: "!contains(github.event.head_commit.message, '[skip ci]')"

steps:
- uses: actions/checkout@v1
- name: Checkout code
uses: actions/checkout@v2

- name: decide on tag
run: |
image_tag=$(echo $GITHUB_REF | sed -e 's/refs\/heads\///g' -e 's/refs\/tags\///g' | sed -e 's/master/latest/g')
echo "${image_tag}" > ./cmd/gtoken/.version
echo "${image_tag}" > ./cmd/gtoken-webhook/.version
echo "::debug::decide on image tag ${image_tag}"
echo "::set-env name=IMAGE_TAG::${image_tag}"

- name: build image
env:
DOCKER_BUILDKIT: 1
DOCKER_REPOSITORY: ${{ secrets.DOCKER_REPOSITORY }}
- name: Decide on tag
id: tagger
run: |
docker build --tag ${DOCKER_REPOSITORY}/gtoken:${IMAGE_TAG} cmd/gtoken
docker build --tag ${DOCKER_REPOSITORY}/gtoken:${IMAGE_TAG}-alpine --target certs cmd/gtoken
docker build --tag ${DOCKER_REPOSITORY}/gtoken-webhook:${IMAGE_TAG} cmd/gtoken-webhook

- name: push image
env:
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
DOCKER_REPOSITORY: ${{ secrets.DOCKER_REPOSITORY }}
run: |
echo ${DOCKER_PASSWORD} | docker login -u ${DOCKER_USERNAME} --password-stdin
docker push ${DOCKER_REPOSITORY}/gtoken:${IMAGE_TAG}
docker push ${DOCKER_REPOSITORY}/gtoken:${IMAGE_TAG}-alpine
docker push ${DOCKER_REPOSITORY}/gtoken-webhook:${IMAGE_TAG}
docker logout
tag=$(echo "${{ github.ref }}" | sed -e 's/^refs\/heads\///g' -e 's/^refs\/tags\///g' -e 's/^refs\/pull\///g' -e 's/\/merge$//g' | sed -e 's/master/latest/g')
echo "::set-output name=tag::${tag}"
echo "::debug::docker image tag ${tag}"

- name: Login to DockerHub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKER_ACCOUNT }}
password: ${{ secrets.DOCKER_TOKEN }}

- name: Set up QEMU
uses: docker/setup-qemu-action@v1

- name: Set up Docker buildx
id: buildx
uses: docker/setup-buildx-action@v1

- name: Cache Docker layers
uses: actions/cache@v2
id: cache
with:
path: /tmp/.buildx-cache
key: ${{ runner.os }}-buildx-${{ github.sha }}
restore-keys: |
${{ runner.os }}-buildx-

- name: Build and push gtoken Docker image
if: github.event_name != 'pull_request'
uses: docker/build-push-action@v2
with:
context: cmd/gtoken
cache-from: type=local,src=/tmp/.buildx-cache
cache-to: type=local,dest=/tmp/.buildx-cache
push: true
tags: ${{ secrets.DOCKER_REPOSITORY }}/gtoken:${{ steps.tagger.outputs.tag }}

- name: Build and push gtoken:alpine Docker image
if: github.event_name != 'pull_request'
uses: docker/build-push-action@v2
with:
context: cmd/gtoken
target: certs
cache-from: type=local,src=/tmp/.buildx-cache
cache-to: type=local,dest=/tmp/.buildx-cache
push: true
tags: ${{ secrets.DOCKER_REPOSITORY }}/gtoken:${{ steps.tagger.outputs.tag }}-alpine

- name: Build and push gtoken-webhook Docker image
if: github.event_name != 'pull_request'
uses: docker/build-push-action@v2
with:
context: cmd/gtoken-webhook
cache-from: type=local,src=/tmp/.buildx-cache
cache-to: type=local,dest=/tmp/.buildx-cache
push: true
tags: ${{ secrets.DOCKER_REPOSITORY }}/gtoken-webhook:${{ steps.tagger.outputs.tag }}
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,5 @@
**/debug
**/debug.test

gcp-trust-policy.json

79 changes: 59 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

Ever wanted to access AWS services from Google Kubernetes cluster (GKE) without using AWS IAM credentials?

This solution can help you to get and exchange Google OIDC token for temporary AWS IAM security credentials are generated by AWS STS service. This approach allows you to access AWS services form GKE cluster without pre-generated long-living AWS credentials.
This solution can help you to get and exchange Google OIDC token for temporary AWS IAM security credentials are generated by AWS STS service. This approach allows you to access AWS services form a GKE cluster without pre-generated long-living AWS credentials.

Read more about this solution on DoiT [Securely Access AWS Services from Google Kubernetes Engine (GKE)](https://blog.doit-intl.com/securely-access-aws-from-gke-dba1c6dbccba?source=friends_link&sk=779821ca975ddb312916e1be732c637f) blog post.

Expand Down Expand Up @@ -47,8 +47,20 @@ Injected AWS environment variables:

The AWS SDK will automatically make the corresponding `AssumeRoleWithWebIdentity` calls to AWS STS on your behalf. It will handle in memory caching as well as refreshing credentials as needed.

### skip injection

The `gtoken-webhook` can be configured to skip injection for all Pods in the specific Namespace by adding the `admission.gtoken/ignore` label to the Namespace.

## `gtoken-webhook` deployment

1. Create a new `gtoken` namespace:

```sh
kubectl create -f deployment/namespace.yaml
```

```

1. To deploy the `gtoken-webhook` server, we need to create a webhook service and a deployment in our Kubernetes cluster. It’s pretty straightforward, except one thing, which is the server’s TLS configuration. If you’d care to examine the [deployment.yaml](https://github.com/doitintl/gtoken/blob/master/deployment/deployment.yaml) file, you’ll find that the certificate and corresponding private key files are read from command line arguments, and that the path to these files comes from a volume mount that points to a Kubernetes secret:

```yaml
Expand Down Expand Up @@ -80,13 +92,29 @@ Generating RSA private key, 2048 bit long modulus
.........................+++
....................+++
e is 65537 (0x10001)
certificatesigningrequest.certificates.k8s.io/gtoken-webhook-svc.default created
certificatesigningrequest.certificates.k8s.io/gtoken-webhook-svc.gtoken created
NAME AGE REQUESTOR CONDITION
gtoken-webhook-svc.default 1s [email protected] Pending
certificatesigningrequest.certificates.k8s.io/gtoken-webhook-svc.default approved
gtoken-webhook-svc.gtoken 1s [email protected] Pending
certificatesigningrequest.certificates.k8s.io/gtoken-webhook-svc.gtoken approved
secret/gtoken-webhook-certs configured
```

**Note** For the GKE Autopilot, run the [webhook-create-self-signed-cert.sh](https://github.com/doitintl/gtoken/blob/master/deployment/webhook-create-self-signed-cert.sh) script to generate a self-signed certificate.

Export CA Bundle as environment variable:

```sh
export CA_BUNDLE=[output value of the previous script "Encoded CA:"]
```

Then, we’ll create the webhook service and deployment.

First, create a Kubernetes Service Account to be used with the `gtoken-webhook`:

```sh
kubectl create -f deployment/service-account.yaml
```

Once the secret is created, we can create deployment and service. These are standard Kubernetes deployment and service resources. Up until this point we’ve produced nothing but an HTTP server that’s accepting requests through a service on port 443:

```sh
Expand All @@ -103,7 +131,7 @@ Now that our webhook server is running, it can accept requests from the `apiserv
[...]
service:
name: gtoken-webhook-svc
namespace: default
namespace: gtoken
path: "/pods"
caBundle: ${CA_BUNDLE}
[...]
Expand All @@ -123,19 +151,13 @@ kubectl create -f deployment/mutatingwebhook-bundle.yaml

### configure RBAC for gtoken-webhook

Create Kubernetes Service Account to be used with `gtoken-webhook`:

```sh
kubectl create -f deployment/service-account.yaml
```

Define RBAC permission for webhook service account:

```sh
# create a cluster role
kubectl create -f deployment/clusterrole.yaml
# define a cluster role binding
kubectl create 0f deployment/clusterrolebinding.yaml
kubectl create -f deployment/clusterrolebinding.yaml
```

## Configuration Flow
Expand All @@ -144,26 +166,31 @@ kubectl create 0f deployment/clusterrolebinding.yaml

- `PROJECT_ID` - GCP project ID
- `CLUSTER_NAME` - GKE cluster name
- `CLUSTER_ZONE` - GKE cluster zone
- `GSA_NAME` - Google Cloud Service Account name (choose any)
- `GSA_ID` - Google Cloud Service Account unique ID (generated by Google)
- `KSA_NAME` - Kubernetes Service Account name (choose any)
- `KSA_NAMESPACE` - Kubernetes namespace
- `AWS_ROLE_NAME` - AWS IAM role name (choose any)
- `AWS_POLICY_NAME` - an AWS IAM policy to assign to IAM role
- `AWS_POLICY_NAME` - an **existing** AWS IAM policy to assign to IAM role
- `AWS_ROLE_ARN` - AWS IAM Role ARN identifier (generated by AWS)

### GCP: Enable GKE Workload Identity

Create a new GKE cluster with [Workload Identity](https://cloud.google.com/kubernetes-engine/docs/how-to/workload-identity) enabled:

```sh
gcloud beta container clusters create ${CLUSTER_NAME} --identity-namespace=${PROJECT_ID}.svc.id.goog
gcloud container clusters create ${CLUSTER_NAME} \
--zone=${CLUSTER_ZONE} \
--workload-pool=${PROJECT_ID}.svc.id.goog
```

or update an existing cluster:

```sh
gcloud beta container clusters update ${CLUSTER_NAME} --identity-namespace=${PROJECT_ID}.svc.id.goog
gcloud container clusters update ${CLUSTER_NAME} \
--zone=${CLUSTER_ZONE} \
--workload-pool=${PROJECT_ID}.svc.id.goog
```

### GCP: Configure GCP Service Account
Expand Down Expand Up @@ -210,7 +237,7 @@ cat > gcp-trust-policy.json << EOF
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"accounts.google.com:sub": "${GSA_SA}"
"accounts.google.com:sub": "${GSA_ID}"
}
}
}
Expand All @@ -225,7 +252,7 @@ aws iam create-role --role-name ${AWS_ROLE_NAME} --assume-role-policy-document f
aws iam attach-role-policy --role-name ${AWS_ROLE_NAME} --policy-arn arn:aws:iam::aws:policy/${AWS_POLICY_NAME}

# get AWS Role ARN to be used in K8s SA annotation
AWS_ROLE_ARN=$(aws iam get-role --role-name ${ROLE_NAME} --query Role.Arn --output text)
AWS_ROLE_ARN=$(aws iam get-role --role-name ${AWS_ROLE_NAME} --query Role.Arn --output text)
```

### GKE: Kubernetes Service Account
Expand All @@ -245,15 +272,15 @@ kubectl create serviceaccount --namespace ${K8S_NAMESPACE} ${KSA_NAME}
Annotate K8s Service Account with GKE Workload Identity (GCP Service Account email)

```sh
kubectl annotate serviceaccount --namespace ${K8S_NAMESPACE} ${KSA_NAME}
kubectl annotate serviceaccount --namespace ${K8S_NAMESPACE} ${KSA_NAME} \
iam.gke.io/gcp-service-account=${GSA_NAME}@${PROJECT_ID}.iam.gserviceaccount.com

```

Annotate K8s Service Account with AWS Role ARN:

```sh
kubectl annotate serviceaccount --namespace ${K8S_NAMESPACE} ${KSA_NAME}
kubectl annotate serviceaccount --namespace ${K8S_NAMESPACE} ${KSA_NAME} \
amazonaws.com/role-arn=${AWS_ROLE_ARN}
```

Expand All @@ -263,7 +290,19 @@ Run a new K8s Pod with K8s ${KSA_NAME} Service Account:

```sh
# run a pod (with AWS CLI onboard) in interactive mod
kubectl run -it --rm --generator=run-pod/v1 --image mikesir87/aws-cli --serviceaccount ${KSA_NAME} test-pod
cat << EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: test-pod
namespace: ${K8S_NAMESPACE}
spec:
serviceAccountName: ${KSA_NAME}
containers:
- name: test-pod
image: mikesir87/aws-cli
command: ["tail", "-f", "/dev/null"]
EOF

# in Pod shell: check AWS assumed role
aws sts get-caller-identity
Expand Down
17 changes: 16 additions & 1 deletion cmd/gtoken-webhook/.golangci.yaml
Original file line number Diff line number Diff line change
@@ -1,3 +1,18 @@
run:
# which dirs to skip
skip-dirs:
- mocks
# Timeout for analysis, e.g. 30s, 5m.
# Default: 1m
timeout: 5m
# Exit code when at least one issue was found.
# Default: 1
issues-exit-code: 2
# Include test files or not.
# Default: true
tests: false


linters-settings:
govet:
check-shadowing: true
Expand All @@ -15,7 +30,7 @@ linters-settings:
misspell:
locale: US
lll:
line-length: 140
line-length: 160
goimports:
local-prefixes: github.com/golangci/golangci-lint
gocritic:
Expand Down
6 changes: 3 additions & 3 deletions cmd/gtoken-webhook/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@
#
# ----- Go Builder Image ------
#
FROM golang:1.14-alpine AS builder
FROM golang:1.17-alpine AS builder

# curl git bash
RUN apk add --no-cache curl git bash make
COPY --from=golangci/golangci-lint:v1.24-alpine /usr/bin/golangci-lint /usr/bin
COPY --from=golangci/golangci-lint:v1.45-alpine /usr/bin/golangci-lint /usr/bin

#
# ----- Build and Test Image -----
Expand All @@ -32,7 +32,7 @@ RUN make
#
# ------ get latest CA certificates
#
FROM alpine:3.11 as certs
FROM alpine:3.15 as certs
RUN apk --update add ca-certificates


Expand Down
Loading