SPIFFE, developed by AT&T and Bell Labs and maintained by CNCF, stands as an open-source solution. It operates as a standardized framework dedicated to the secure identification and fortification of communication channels among diverse application services.

Introduction

In an era where data breaches and cyberattacks dominate headlines (Outdated password exposed, leaked passwords on GitHub), traditional password-based authentication methods increasingly fail to provide the security and convenience that today’s dynamic, multi-cloud environments demand. Enter SPIFFE, the Secure Production Identity Framework for Everyone, a revolutionary solution that not only addresses the shortcomings of passwords but also unlocks a new era of identity management in complex, distributed systems.

As businesses expand their operations across multiple cloud platforms, maintaining consistent and robust authentication mechanisms becomes all the more daunting. The complexities of managing access across various clouds, services, and applications while safeguarding sensitive data demand a paradigm shift. This is where going “passwordless” emerges as a beacon of innovation, promising enhanced security, streamlined user experiences, and simplified administration.

However, the current approach of relying on passwords and keys for authentication presents its own set of challenges. Rotating passwords and keys in a timely manner often becomes an overlooked practice due to factors like limited time, budget constraints, lack of established procedures, or simply being forgotten. Moreover, the usage of weak passwords is all too common, undermining the very foundation of security.

What’s more, passwords and keys are frequently shared through emails, chat platforms, and even unintentionally committed to version control systems. They might find their way onto employees’ devices, creating vulnerabilities that can be exploited by malicious actors. The need for a more secure, systematic, and foolproof identity management solution is evident.

In the dynamic landscape of distributed systems, SPIFFE stands as a powerful solution that revolutionizes identity management. By discarding the vulnerabilities associated with conventional password-based methods, SPIFFE offers a robust and secure approach. It ensures reliable authentication without the risks of easily compromised passwords and keys. In doing so, SPIFFE not only bridges security gaps but also propels us towards an authentication landscape that is more efficient, dependable, and poised for the future.

This blog post will explain how to install SPIFFE on your on-premises Kubernetes cluster. Additionally, we will deploy an application that will establish a connection with an S3 bucket.

How does it work

Authentication with SPIFFE relies on mutual Transport Layer Security (mTLS) protocol. mTLS is a form of secure communication that ensures both parties—client and server—authenticate each other using digital certificates, enhancing the security of data exchanged over the network.

So, SPIFFE will generate a CA certificate that we’ll utilize to establish trust and verify the certificates created by SPIFFE. It’s crucial to upload this CA certificate to our AWS account, ensuring that AWS recognizes and trusts all incoming requests from our cluster.

SPIFFE generates short-lived certificates upon application startup for making requests. To maintain connection stability, SPIFFE periodically renews these certificates before expiration, ensuring seamless communication.

As our pod initializes, SPIFFE comes into play by mounting certificates within the pod. These certificates serve a pivotal role in enabling secure calls to AWS via mutual TLS (mTLS). During these calls, our pod aims to acquire AWS credentials, which it can access internally. However, just like the certificates, these AWS credentials follow a short-lived lifecycle. The remarkable aspect here is that SPIFFE is responsible for automatically renewing these credentials. This autonomous renewal process ensures that our AWS credentials remain up-to-date and effective, seamlessly aligning with SPIFFE’s ethos of continual security enhancement.

An example of a certificate. Verify spiffe installation

Install cert-manager

Installing cert-manager on your Kubernetes cluster before installing SPIFFE is a crucial step. cert-manager is a widely used tool that automates the management of TLS certificates within Kubernetes.

Integrating SPIFFE into your Kubernetes cluster relies on secure communication channels established through TLS certificates. cert-manager simplifies the management and issuance of these certificates, ensuring that the identities and connections established by SPIFFE are trustworthy and adequately encrypted.

Create a namespace for cert-manager

We begin by creating a namespace where we will deploy cert-manager and SPIFFE.
Use the following two commands to create the namespaces and switch to the desired namespace:

kubectl create namespace cert-manager
kubectl config set-context --current --namespace=cert-manager

Add jetstack

In order to install cert-manager and SPIFFE, we can make use of the Helm package manager and install these Helm charts on our cluster. To achieve this, we need to fetch the Jetstack repository locally.
Use the following commands to achieve this:

helm repo add jetstack https://charts.jetstack.io
helm repo update

Deploy cert-manager

Now that we’ve added Jetstack as a helm repository, we can proceed to install cert-manager on our Kubernetes cluster.
Use the following commands to install cert-manager:

kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.11.0/cert-manager.crds.yaml
helm upgrade --install cert-manager jetstack/cert-manager --version v1.11.0 --set prometheus.enabled=false

Install SPIFFE

Deploy SPIFFE

Now that cert-manager is installed, we can proceed to the installation of SPIFFE.
To do this, use the following command:

Please note: for the trusted domain, you could use a different name; I suggest using the name of your Kubernetes cluster. You will need this domain name later when setting up AWS policies.

helm upgrade --install cert-manager-csi-driver-spiffe jetstack/cert-manager-csi-driver-spiffe --wait \
--set image.tag=aws \
--set image.repository.driver=ghcr.io/joshvanl/cert-manager-csi-driver \
--set image.repository.approver=ghcr.io/joshvanl/cert-manager-csi-driver-approver \
--set "app.logLevel=1" \
--set "app.trustDomain=demo.home.cluster" \
--set "app.approver.signerName=clusterissuers.cert-manager.io/csi-driver-spiffe-ca" \
--set "app.issuer.name=csi-driver-spiffe-ca" \
--set "app.issuer.kind=ClusterIssuer" \
--set "app.issuer.group=cert-manager.io"

Configure SPIFFE

To obtain and renew certificates automatically, we use the following ClusterIssuer.
Deploy it using the following command:

kubectl apply -f https://raw.githubusercontent.com/nicholasM95/spiffe-demo/main/setup/clusterissuer.yaml

Verify installation

Verify if all pods have started and check whether the secret csi-driver-spiffe-ca has been created.

Verify spiffe installation

Configure AWS

Create the s3 bucket

Create a S3 bucket with a relevant and easy name and upload a text file named ‘hello.txt’. Our application will read this file later.

Create S3 bucket

Create a trusted anchor

In the context of AWS, a trusted anchor might involve setting up a trust relationship with an external identity provider or certificate authority. This ensures that the SPIFFE identities generated within the Kubernetes cluster can be verified and trusted by AWS services when defining policies, access controls, or other security-related configurations.

To achieve this, we need a CA certificate that is utilized by SPIFFE on our Kubernetes cluster. Use the following command to obtain the CA certificate:

kubectl get secret csi-driver-spiffe-ca -o jsonpath='{.data}'

Copy the value of ca.crt and decode it using base64. Example:

echo 'copied-value' | base64 -d

Navigate to the IAM section within AWS and access the “Roles” tab. Scroll down and choose “Roles Anywhere.” Ensure that you are still within the desired region. Generate a trust anchor. Opt for the external certificate bundle option. Paste the decoded value of ca.crt from the secret.

Create Trust anchor

Create a trusted profile

A trusted profile establishes trust, defines access controls, and enables secure interaction between SPIFFE-issued identities within a Kubernetes cluster and AWS resources. It ensures a smooth and secure integration between these two environments while maintaining a solid security posture.

We require a policy that allows our application, operating in the ‘spiffe’ namespace with service account ‘file-read-app’, to gain read access to files within our designated bucket, all within the ‘demo.home.cluster’ cluster.

{
    "Version": "2012-10-17",
	  "Statement": [
		    {
			      "Action": "s3:GetObject",
			      "Condition": {
				        "StringLike": {
					          "aws:PrincipalTag/x509SAN/URI": "spiffe://demo.home.cluster/ns/spiffe/sa/file-read-app"
				        }
			      },
			      "Effect": "Allow",
			      "Resource": "arn:aws:s3:::<BUCKET_NAME>/*",
			      "Sid": ""
		    }
	  ]
}

Create a new role named demo_k8s_read, establish a trust relationship, and append the previously mentioned policy to the permission policy.
Don’t forget to change the Arn source to your trust anchor.

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "",
            "Effect": "Allow",
            "Principal": {
                "Service": "rolesanywhere.amazonaws.com"
            },
            "Action": [
                "sts:TagSession",
                "sts:SetSourceIdentity",
                "sts:AssumeRole"
            ],
            "Condition": {
                "StringLike": {
                    "aws:PrincipalTag/x509SAN/URI": "spiffe://demo.home.cluster/ns/*/sa/*"
                },
                "ArnEquals": {
                    "aws:SourceArn": "arn:aws:rolesanywhere:eu-west-1:930970667460:trust-anchor/6eac6a56-97c3-439f-9074-f33c576ab08d"
                }
            }
        }
    ]
}

Go back to the “Roles Anywhere” page and create a new trust profile, again check your region. Use the role you created before.

Create Trust profile

Deploy application

Create a namespace for our application

We begin by creating a namespace where we will deploy our application.
Use the following two commands to create the namespaces and switch to the desired namespace:

kubectl create namespace spiffe
kubectl config set-context --current --namespace=spiffe

Deploy service account

We require a service account named ‘file-read-app’ for our application. Create a new file named ‘serviceaccount.yaml’ with the following content.

apiVersion: v1
kind: ServiceAccount
metadata:
  name: file-read-app

Deploy service account.

kubectl apply -f serviceaccount.yaml

Deploy role

We require a role named ‘file-read-app’ for our application. Create a new file named ‘role.yaml’ with the following content.

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: file-read-app
rules:
  - apiGroups: ["cert-manager.io"]
    resources: ["certificaterequests"]
    verbs: ["create"]

Deploy role.

kubectl apply -f role.yaml

Deploy role binding

We require a role binding named ‘file-read-app’ for our application. Create a new file named ‘rolebinding.yaml’ with the following content.

apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: file-read-app
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: file-read-app
subjects:
  - kind: ServiceAccount
    name: file-read-app

Deploy role binding.

kubectl apply -f rolebinding.yaml

Deploy application

Create a new file named ‘deployment.yaml’ with the following content. If necessary, update the environment variables. Fill in the three AWS resources with the one you have generated.

SPIFFE will mount files in the volume we created here, SPIFFE would create a certificate to communicate with AWS. After the certificate is generated, SPIFFE will use this to retrieve AWS credentials and mount them.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: demo-spiffe
spec:
  replicas: 1
  selector:
    matchLabels:
      app.kubernetes.io/name: demospiffe
  template:
    metadata:
      labels:
        app.kubernetes.io/name: demospiffe
    spec:
      serviceAccount: file-read-app
      containers:
        - name: demospiffe
          image: nicholas95/spiffedemo:dev-1
          imagePullPolicy: Always
          env:
            - name: AWS_REGION
              value: eu-west-1
            - name: AWS_BUCKET
              value: <BUCKET_NAME>
          volumeMounts:
            - mountPath: /root/.aws
              name: spiffe
      volumes:
        - csi:
            driver: spiffe.csi.cert-manager.io
            readOnly: true
            volumeAttributes:
              aws.spiffe.csi.cert-manager.io/enable: "true"
              aws.spiffe.csi.cert-manager.io/role: arn:aws:iam::930970667460:role/demo_k8s_read
              aws.spiffe.csi.cert-manager.io/trust-anchor: arn:aws:rolesanywhere:eu-west-1:930970667460:trust-anchor/1bf58d4e-35c6-465c-a05c-1bdcce05054b
              aws.spiffe.csi.cert-manager.io/trust-profile: arn:aws:rolesanywhere:eu-west-1:930970667460:profile/fa6c37f9-f64a-4bdf-b0f6-c39ec5282aa4
          name: spiffe

Deploy application.

kubectl apply -f deployment.yaml

Test our application

When your pod is started, you can open a shell into it using the following command and install curl. Once curl is installed, you can send a GET request to ‘/file/hello.txt’, the response will be the content of the file in the S3 bucket.

kubectl exec -it <pod-name> -- /bin/sh
apk add curl
curl http://localhost:8080/file/hello.txt
exit

We’ve also included a file watcher to monitor the renewal of credentials and certificates.

Filewatcher logging

Conclusion

AWS’s IRSA has been the go-to method for granting permissions to Kubernetes pods running on Amazon EKS. However, as the landscape of distributed systems evolves, SPIFFE offers a fresh perspective on securing these connections.

By setting up our connection with S3 in this manner, we eliminate the need to manage access keys and secret keys ourselves. As a result, the likelihood of our keys being leaked is minimized. Additionally, both our certificates and AWS credentials have a short lifespan.

Furthermore, we are relieved from the task of key rotation as this process occurs automatically.

In essence, SPIFFE’s focus on secure identity issuance and management can be applied to a variety of scenarios where authenticated and secure communication is crucial. Its flexibility and adaptability make it valuable across a spectrum of modern distributed and cloud-native computing environments.

SPIFFE becomes especially valuable when dealing with complex scenarios such as hybrid and multi-cloud setups. In the realm of security, SPIFFE shines by offering a passwordless solution that not only enhances protection but also eliminates the vulnerabilities and complexities associated with traditional passwords and keys.

Extra

Curious about establishing a connection to an AWS database without using passwords? This post explains how to connect to an RDS from EKS without needing a password. Since we’ve already set up SPIFFE, you can also achieve this from your on-premises, azure, gcloud, … environment.

In this repository, you’ll find all the resources that were used to create this blog post.

Nicholas is a Java Developer with a passion for security and an enthusiasm for exploring new technologies.