Giter Club home page Giter Club logo

gtoken's Introduction

Docker Pulls Docker Pulls

Securely access AWS Services from GKE cluster

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 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) blog post.

gtoken tool

The gtoken tool can get Google Cloud ID token when running with under GCP Service Account (for example, GKE Pod with Workload Identity).

gtoken command syntax

NAME:
   gtoken - generate ID token with current Google Cloud service account

USAGE:
   gtoken [global options] command [command options] [arguments...]

COMMANDS:
   help, h  Shows a list of commands or help for one command

GLOBAL OPTIONS:
   --refresh      auto refresh ID token before it expires (default: true)
   --file value   write ID token into file (stdout, if not specified)
   --help, -h     show help (default: false)
   --version, -v  print the version

gtoken-webhook Kubernetes webhook

The gtoken-webhook is a Kubernetes mutating admission webhook, that mutates any K8s Pod running under specially annotated Kubernetes Service Account (see details below).

gtoken-webhook mutation

The gtoken-webhook injects a gtoken initContainer into a target Pod and an additional gtoken sidekick container (to refresh an ID OIDC token a moment before expiration), mounts token volume and injects three AWS-specific environment variables. The gtoken container generates a valid GCP OIDC ID Token and writes it to the token volume.

Injected AWS environment variables:

  • AWS_WEB_IDENTITY_TOKEN_FILE - the path to the web identity token file (OIDC ID token)
  • AWS_ROLE_ARN - the ARN of the role to assume by Pod containers
  • AWS_ROLE_SESSION_NAME - the name applied to this assume-role session

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:
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
[...]
      args:
      [...]
      - --tls-cert-file=/etc/webhook/certs/cert.pem
      - --tls-private-key-file=/etc/webhook/certs/key.pem
      volumeMounts:
      - name: webhook-certs
        mountPath: /etc/webhook/certs
        readOnly: true
[...]
   volumes:
   - name: webhook-certs
     secret:
       secretName: gtoken-webhook-certs

The most important thing to remember is to set the corresponding CA certificate later in the webhook configuration, so the apiserver will know that it should be accepted. For now, we’ll reuse the script originally written by the Istio team to generate a certificate signing request. Then we’ll send the request to the Kubernetes API, fetch the certificate, and create the required secret from the result.

First, run webhook-create-signed-cert.sh script and check if the secret holding the certificate and key has been created:

./deployment/webhook-create-signed-cert.sh

creating certs in tmpdir /var/folders/vl/gxsw2kf13jsf7s8xrqzcybb00000gp/T/tmp.xsatrckI71
Generating RSA private key, 2048 bit long modulus
.........................+++
....................+++
e is 65537 (0x10001)
certificatesigningrequest.certificates.k8s.io/gtoken-webhook-svc.gtoken created
NAME                         AGE   REQUESTOR              CONDITION
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 script to generate a self-signed certificate.

Export CA Bundle as environment variable:

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:

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:

kubectl create -f deployment/deployment.yaml

kubectl create -f deployment/service.yaml

configure mutating admission webhook

Now that our webhook server is running, it can accept requests from the apiserver. However, we should create some configuration resources in Kubernetes first. Let’s start with our validating webhook, then we’ll configure the mutating webhook later. If you take a look at the webhook configuration, you’ll notice that it contains a placeholder for CA_BUNDLE:

[...]
      service:
        name: gtoken-webhook-svc
        namespace: gtoken
        path: "/pods"
      caBundle: ${CA_BUNDLE}
[...]

There is a small script that substitutes the CA_BUNDLE placeholder in the configuration with this CA. Run this command before creating the validating webhook configuration:

cat ./deployment/mutatingwebhook.yaml | ./deployment/webhook-patch-ca-bundle.sh > ./deployment/mutatingwebhook-bundle.yaml

Create mutating webhook configuration:

kubectl create -f deployment/mutatingwebhook-bundle.yaml

configure RBAC for gtoken-webhook

Define RBAC permission for webhook service account:

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

Configuration Flow

Flow variables

  • 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 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 enabled:

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

or update an existing cluster:

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

GCP: Configure GCP Service Account

Create Google Cloud Service Account:

# create GCP Service Account
gcloud iam service-accounts create ${GSA_NAME}

# get GCP SA UID to be used for AWS Role with Google OIDC Web Identity
GSA_ID=$(gcloud iam service-accounts describe --format json ${GSA_NAME}@${PROJECT_ID}.iam.gserviceaccount.com  | jq -r '.uniqueId')

Update GSA_NAME Google Service Account with following roles:

  • roles/iam.workloadIdentityUser - impersonate service accounts from GKE Workloads
  • roles/iam.serviceAccountTokenCreator - impersonate service accounts to create OAuth2 access tokens, sign blobs, or sign JWTs
gcloud projects add-iam-policy-binding ${PROJECT_ID} \
  --member serviceAccount:${GSA_NAME}@${PROJECT_ID}.iam.gserviceaccount.com \
  --role roles/iam.serviceAccountTokenCreator

gcloud iam service-accounts add-iam-policy-binding \
  --role roles/iam.workloadIdentityUser \
  --member "serviceAccount:${PROJECT_ID}.svc.id.goog[${K8S_NAMESPACE}/${KSA_NAME}]" \
  ${GSA_NAME}@${PROJECT_ID}.iam.gserviceaccount.com

AWS: Create AWS IAM Role with Google OIDC Web Identity

# prepare role trust policy document for Google OIDC provider
cat > gcp-trust-policy.json << EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Federated": "accounts.google.com"
      },
      "Action": "sts:AssumeRoleWithWebIdentity",
      "Condition": {
        "StringEquals": {
          "accounts.google.com:sub": "${GSA_ID}"
        }
      }
    }
  ]
}
EOF

# create AWS IAM Rome with Google Web Identity
aws iam create-role --role-name ${AWS_ROLE_NAME} --assume-role-policy-document file://gcp-trust-policy.json

# assign AWS role desired policies
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 ${AWS_ROLE_NAME} --query Role.Arn --output text)

GKE: Kubernetes Service Account

Create K8s namespace:

kubectl create namespace ${K8S_NAMESPACE}

Create K8s Service Account:

kubectl create serviceaccount --namespace ${K8S_NAMESPACE} ${KSA_NAME}

Annotate K8s Service Account with GKE Workload Identity (GCP Service Account email)

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:

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

Run demo

Run a new K8s Pod with K8s ${KSA_NAME} Service Account:

# run a pod (with AWS CLI onboard) in interactive mod
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

# the output should look similar to below
{
    "UserId": "AROA9GB4GPRFFXVHNSLCK:gtoken-webhook-gyaashbbeeqhpvfw",
    "Account": "906385953612",
    "Arn": "arn:aws:sts::906385953612:assumed-role/bucket-full-gtoken/gtoken-webhook-gyaashbbeeqhpvfw"
}

External references

I've borrowed an initial mutating admission webhook code and deployment guide from banzaicloud/admission-webhook-example repository. Big thanks to Banzai Cloud team!

gtoken's People

Contributors

alexei-led avatar jefimm avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

gtoken's Issues

Small typo in README

Hey I notice we use KSA_NAMESPACE parameter in README just one time, then we proceed using K8S_NAMESPACE nomenclature, so I guess it is a typo?

Anyway, I can't PR to it, just reporting what I found while following the tutorial.

BTW, great tool :)

kubernetes api compatibility

The following warnings were issues with recent deployment attempt

./deployment/webhook-create-signed-cert.sh:
Warning: certificates.k8s.io/v1beta1 CertificateSigningRequest is deprecated in v1.19+, unavailable in v1.22+; use certificates.k8s.io/v1 CertificateSigningRequest

kubectl create -f deployment/mutatingwebhook-bundle.yaml
Warning: admissionregistration.k8s.io/v1beta1 MutatingWebhookConfiguration is deprecated in v1.16+, unavailable in v1.22+; use admissionregistration.k8s.io/v1 MutatingWebhookConfiguration

Pod cannot be terminated

I create a pod with below config:

############

testpod.yaml

############
apiVersion: v1
kind: Pod
metadata:
name: test-job
spec:
serviceAccountName: saname
restartPolicy: Never
containers:
- name: test-pod
image: ubuntu
command:
- /bin/sleep
- "10"
resources:
limits:
cpu: 100m
memory: 128Mi
requests:
cpu: 100m
memory: 128Mi

############

CMD: kubectl -f testpod.yaml

After the sleep command finished, it does not terminated,

CMD: kubectl get pod
test-job 1/2 Running 0 5m26s

CMD: kubectl describe pod test-job
.
.
Containers:
test-pod:
Container ID: docker://d6a95538e748e9585af2e11f62d2f9f65bc7a7c5cb01d01357985cb0e7fd56f3
Image: ubuntu
Image ID: docker-pullable://ubuntu@sha256:c95a8e48bf88e9849f3e0f723d9f49fa12c5a00cfc6e60d2bc99d87555295e4c
Port:
Host Port:
Command:
/bin/sleep
10
State: Terminated
Reason: Completed
Exit Code: 0
Started: Tue, 15 Dec 2020 11:18:59 +0800
Finished: Tue, 15 Dec 2020 11:19:09 +0800
Ready: False
Restart Count: 0
.
.
.
update-gcp-id-token:
Container ID: docker://88ec093929b3c648b032448d17de5f46fb6be9bdcc5e78247acac1a16cf9dcbc
Image: doitintl/gtoken:latest
Image ID: docker-pullable://doitintl/gtoken@sha256:cb9647b375f579e378e957ed80dfa6259667987cf87835b10b125dc5b175b31d
Port:
Host Port:
Command:
/gtoken
--file=/var/run/secrets/aws/token/gtoken
--refresh=true
State: Running
Started: Tue, 15 Dec 2020 11:19:00 +0800
Ready: True
Restart Count: 0

#########

It seems like the update-gcp-id-token does not terminated.
Is it possible to terminate update-gcp-id-token after test-pod fininshed?

Thank you!

CronJob doesn't finish

Hey !
I would like use gtoken in cronjob.
It's work but po doesn't finish.
It still stay with "status: Not Ready" because after po execution, update-gcp-id-token restart :

Normal Started 3m26s kubelet Started container check-certif
Normal Pulling 3m26s kubelet Pulling image "doitintl/gtoken:latest"
Normal Pulled 3m26s kubelet Successfully pulled image "doitintl/gtoken:latest" in 248.821135ms (248.835925ms including waiting)
Normal Created 3m26s kubelet Created container update-gcp-id-token
Normal Started 3m26s kubelet Started container update-gcp-id-token

I can't stop "update-gcp-id-token" for cronjob please ?

Thanks !

Inquiry: access AWS ECR

Hi,

Your gtoken approach works like a charm for access to S3. But I was wondering if you have tried/confirmed it with other AWS services, for example ECR to pull images?

Kind regards,

Eric Van Steenbergen

implementation issue

I've deployed gtoken as mentioned in read me. But I couldn't achieve to access aws resource. Actually, webhook doesn't run properly. I couldn't see any log in webhook pod. there is only following logs
time="2023-03-24T09:26:19Z" level=info msg="listening on https://:8443"

when I tried aws command in test pod I saw the following error.
Unable to locate credentials. You can configure credentials by running "aws configure".

Also, I couldn't see any environment variables related with AWS in test pod after creation. I expect webhook adds some aws related variables after pod creation.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.