Photo Kubernetes

Managing Secrets in Kubernetes: A Step-by-Step Guide to HashiCorp Vault Integration

You’ve got a bunch of sensitive stuff like API keys, database passwords, and certificates floating around in your Kubernetes applications. Keeping those safe is a big deal. You could try stuffing them into Kubernetes Secrets, but that gets complicated quickly, especially when you have lots of them or need to manage them across different environments. That’s where HashiCorp Vault comes in.

Integrating Vault with Kubernetes can make managing your secrets a whole lot more streamlined and secure.

Let’s walk through how to do it.

Let’s be honest, the default Kubernetes Secret object is handy for simple cases. You can base64 encode your sensitive data and store it. But as your applications grow and your security needs become more sophisticated, you’ll hit some limitations.

The Limitations of Native Kubernetes Secrets

Kubernetes Secrets are, at their core, just base64 encoded data stored within the Kubernetes API. This means:

  • Limited Encryption: By default, secrets are stored encrypted at rest in etcd, but this is Kubernetes’ own encryption at rest mechanism. It’s good, but it doesn’t offer the fine-grained control or advanced features you might need.
  • No Dynamic Secrets: You have to manually create and update secrets. If a database user needs to be rotated, you’re doing it manually.
  • Auditing Challenges: While Kubernetes has audit logs, tracking specific secret access and usage across your cluster can be a fragmented experience.
  • Complexity at Scale: Managing hundreds or thousands of secrets across multiple clusters, teams, and environments becomes a significant operational burden. Who has access to what? How do you rotate them?

Vault’s Core Benefits for Secret Management

HashiCorp Vault offers a more robust and centralized approach. It acts as a dedicated secrets management system with features specifically designed for dynamic and secure secret handling.

  • Centralized Secrets Management: One place to manage all your secrets, regardless of where your applications are running.
  • Dynamic Secrets: Vault can generate secrets on demand. Think temporary database credentials, short-lived TLS certificates, or API keys that expire automatically.
  • Advanced Encryption: Strong encryption for secrets at rest and in transit, with configurable encryption keys.
  • Auditing and Policy Control: Comprehensive audit logs track every access and operation. Fine-grained policies dictate who or what can access which secrets.
  • Secret Leasing and Renewal: Secrets can be issued with a Time-To-Live (TTL), automatically revoking them when the lease expires. Applications can renew leases if they still need access.
  • Identity-Based Access: Vault integrates with various identity providers (like Kubernetes Service Accounts, cloud IAM, LDAP) to authenticate and authorize access to secrets.

In the realm of securing sensitive information within Kubernetes, the article “Managing Secrets in Kubernetes: A Step-by-Step Guide to HashiCorp Vault Integration” provides invaluable insights. For those looking to enhance their presentation skills while discussing such technical topics, you might find the article on the best software for presentations in 2023 particularly useful. This resource offers a comprehensive overview of tools that can elevate your presentation game, ensuring that your audience remains engaged and informed. You can read more about it here: best software for presentation in 2023.

Key Takeaways

  • Clear communication is essential for effective teamwork
  • Active listening is crucial for understanding team members’ perspectives
  • Setting clear goals and expectations helps to keep the team focused
  • Regular feedback and open communication can help address any issues early on
  • Celebrating achievements and milestones can boost team morale and motivation

Setting Up HashiCorp Vault in Kubernetes

Before you can integrate Vault with Kubernetes, you need to have a running Vault instance. While you could run Vault outside your Kubernetes cluster, it’s often most convenient and secure to run it inside the cluster itself, especially for applications running in that same cluster.

Installing Vault using Helm

The easiest and most recommended way to get Vault up and running in Kubernetes is by using its official Helm chart. Helm is a package manager for Kubernetes, making deployment and management much simpler.

  1. Add the HashiCorp Helm Repository:

“`bash

helm repo add hashicorp https://helm.releases.hashicorp.com

helm repo update

“`

  1. Create a Namespace for Vault: It’s good practice to isolate Vault.

“`bash

kubectl create namespace vault

“`

  1. Install Vault: You’ll want to configure Vault for production. This involves setting up storage, TLS, and replication. For a basic setup, you can use something like this (but always review and adjust for production):

“`bash

helm install vault hashicorp/vault \

–namespace vault \

–values – <

server:

dataStorage:

enabled: true

size: 10Gi # Adjust as needed

ha:

enabled: true

replicas: 3 # Recommended for HA

dev:

enabled: false # For production, disable dev mode

tlsDisable: false # Ensure TLS is enabled for production

Other production-ready configurations would go here, e.g.,

ingress:

enabled: true

hostname: vault.example.com

tlsSpec:

secretName: vault-tls-secret

ui:

enabled: true

serviceType: ClusterIP

If ingress is enabled above, you might configure it here too.

EOF

“`

  • dataStorage: Configures persistent storage for Vault. You’ll need this for production.
  • ha: Enables High Availability, crucial for production to avoid single points of failure.
  • dev.enabled: false: Disables the development mode, which is insecure and not for production.
  • tlsDisable: false: Ensures Vault is configured with TLS. You’ll typically manage TLS certificates for Vault itself, either through Kubernetes Ingress or by providing your own.
  • ui.enabled: Enables the Vault UI, which is very helpful for initial setup and management.
  1. Check Vault Pods:

“`bash

kubectl get pods -n vault

“`

Wait for all Vault pods to be in a Running state.

Initializing and Unsealing Vault

When Vault starts for the first time, it needs to be initialized and unsealed.

  • Initializing: This process generates the initial encryption keys and a root token. You’ll perform this once.

“`bash

kubectl exec -n vault vault-0 — vault operator init -tls-skip-verify > vault_init.txt

“`

This command will output something like:

  • Unseal Keys: You’ll get several unseal keys. Store these securely! You’ll need a minimum number of these keys (e.g., 3 out of 5) to unseal Vault.
  • Initial Root Token: This is your master key. Guard it with your life and use it sparingly.
  • Unsealing: Vault is sealed by default for security. You need to unseal it before it can be used. You’ll need the required number of unseal keys.

“`bash

Example for unsealing with 3 keys (replace key values)

kubectl exec -n vault vault-0 — vault operator unseal

kubectl exec -n vault vault-0 — vault operator unseal

kubectl exec -n vault vault-0 — vault operator unseal

“`

You can check the status with:

“`bash

kubectl exec -n vault vault-0 — vault status

“`

Once unsealed, the status will change.

Configuring Vault for Kubernetes Authentication

This is the crucial step that allows your Kubernetes applications to authenticate with Vault. Vault uses a “secrets engine” for this purpose.

  1. Enable the Kubernetes Secrets Engine:

“`bash

kubectl exec -n vault vault-0 — vault secrets enable \

-path=kubernetes \

-community=true \

-description=”Kubernetes Auth” \

auth

“`

This command enables the kubernetes auth method at the path kubernetes/.

  1. Configure Vault’s Kubernetes Auth Method: Vault needs to know how to talk to your Kubernetes API server. It does this by using a Service Account token and the Kubernetes API server’s certificate.
  • Get the Kubernetes API Server Address:

“`bash

kubectl cluster-info | grep ‘Kubernetes control plane’ | awk ‘/http/ {print $2}’

“`

This will give you something like https://192.168.1.100:6443.

  • Get the Kubernetes CA Certificate:

“`bash

kubectl get secret -n default -o jsonpath='{.data.ca\.crt}’ | base64 –decode > ca.crt

“`

You might need to find the correct secret that holds the CA certificate for your cluster. Often, it’s in the kube-system namespace, named something like kubernetes-ca. If you’re unsure, kubectl get secrets -n kube-system and look for a CA certificate.

  • Configure Vault:

“`bash

Replace with your actual API server URL and the path to your ca.crt file

VAULT_ADDR=”http://vault.vault.svc.

cluster.

local:8200″ # Or your Vault service address

K8S_API_URL=”https://” # e.g., https://192.168.1.100:6443

K8S_CA_CERT_PATH=”./ca.crt”

Login to Vault if needed (using your root token)

export VAULT_TOKEN=”your_root_token”

Configure the kubernetes auth method

vault write auth/kubernetes/config \

token_reviewer_jwt=”$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)” \

kubernetes_host=”$K8S_API_URL” \

kubernetes_ca_cert=@”$K8S_CA_CERT_PATH” \

issuer=”https://kubernetes.default.svc.cluster.local” # Adjust if your issuer is different

“`

  • token_reviewer_jwt: This is the JWT token of the Service Account that Vault will use to authenticate with the Kubernetes API. By default, when Vault is running inside Kubernetes, it has access to its own Service Account token in /var/run/secrets/kubernetes.io/serviceaccount/token.
  • kubernetes_host: The URL of your Kubernetes API server.
  • kubernetes_ca_cert: The CA certificate of your Kubernetes API server.
  • issuer: A URL that Vault uses to verify the issuer of Kubernetes tokens. This should typically match your cluster’s issuer.

Granting Kubernetes Service Accounts Access to Vault

Kubernetes

Now that Vault is configured to trust Kubernetes Service Accounts, you need to define which Service Accounts can access which secrets. This is done through Vault policies and role bindings.

Creating a Vault Policy for Secret Access

Policies in Vault define what actions (read, write, list, delete) are allowed on specific paths within Vault.

  1. Define a Policy: Let’s create a policy that allows an application to read secrets from a specific path in Vault.

“`bash

Example policy content

cat < app-read-policy.hcl

path “secret/data/myapp/*” {

capabilities = [“read”, “list”]

}

path “kv/data/myapp/*” { # If using the newer KV v2 engine

capabilities = [“read”, “list”]

}

EOF

Write the policy to Vault

vault policy write app-read-policy app-read-policy.hcl

“`

  • We’re assuming you’ll be storing your application’s secrets under the secret/data/myapp/ path (for KV v1) or kv/data/myapp/ (for KV v2). You can use vault kv enable-path secret/ or vault secrets enable -path=kv kv to set up the KV secrets engine.
  • capabilities: read allows fetching data, list allows listing available secrets under a path.

Creating a Vault Role for Kubernetes Authentication

A Vault “role” links a Kubernetes Service Account (or other identity) to a Vault policy.

  1. Create a Kubernetes Auth Role:

“`bash

Replace ‘default’ with the namespace your app runs in

Replace ‘my-app-sa’ with your application’s Service Account name

Replace ‘app-read-policy’ with the name of the Vault policy you created

vault write auth/kubernetes/role/my-app-role \

bound_service_account_names=”my-app-sa” \

bound_service_account_namespaces=”default” \

policies=”app-read-policy” \

ttl=24h # Optional: How long a token is valid for.

Shorter is generally better.

“`

  • bound_service_account_names: Specifies which Kubernetes Service Account(s) this role applies to.
  • bound_service_account_namespaces: Specifies which Kubernetes Namespace(s) the Service Account must reside in.
  • policies: The Vault policy (or policies) to grant to authenticated Service Accounts matching the bounds.

Integrating Your Application with Vault

Photo Kubernetes

Now that Vault is set up and configured, your applications running in Kubernetes need to be able to request secrets from it.

The Vault Agent Injector (Recommended)

The most seamless way to integrate Vault is by using the Vault Agent Injector. This is a Kubernetes admission controller that automatically injects a Vault Agent sidecar container into your pods. This agent handles authentication, fetches secrets, and makes them available to your application.

  1. Enable the Vault Agent Injector: The Helm chart for Vault typically deploys the injector. You’ll need to ensure it’s enabled and configured. Look for injector.enabled and related settings in your helm install or helm upgrade command.
  1. Annotate Your Pods/Deployments: To tell the injector which pods to inject into, you annotate your Deployment, Pod, or StatefulSet.

“`yaml

apiVersion: apps/v1

kind: Deployment

metadata:

name: my-app-deployment

namespace: default

spec:

replicas: 1

selector:

matchLabels:

app: my-app

template:

metadata:

labels:

app: my-app

annotations:

vault.hashicorp.com/agent-inject: “true”

Point to the Vault role created earlier

vault.hashicorp.com/role: “my-app-role”

Specify which secrets to mount and where

vault.hashicorp.com/secret-volumes: “kv/data/myapp/db-credentials,secret/data/myapp/api-key”

Optional: Specify mount path for secrets if not default

vault.hashicorp.com/secret-mount-path: “/etc/secrets”

Optional: specify the KV version if not the default

vault.hashicorp.com/kv-v2-enabled: “true”

… rest of your deployment spec

“`

  • vault.hashicorp.com/agent-inject: "true": This annotation tells the injector to proceed with injecting the sidecar.
  • vault.hashicorp.com/role: "my-app-role": This links the pod to the Vault role you previously created, ensuring it uses the correct authentication and policy.
  • vault.hashicorp.com/secret-volumes: This is where you specify the actual secrets you want to retrieve from Vault. You provide the path within Vault and the name you want the secret file to have. You can list multiple secrets separated by commas.
  • vault.hashicorp.com/secret-mount-path: This defines the directory where the secrets will be mounted within the application container. The default is /vault/secrets.
  • vault.hashicorp.com/kv-v2-enabled: "true": Explicitly tells the injector you’re using the KV v2 secrets engine. If you don’t specify this and are using KV v2, it might assume KV v1.
  1. Application Container: Your application container will then find its secrets as files within the specified secret-mount-path. For example, if you requested kv/data/myapp/db-credentials, the secrets would be available in /etc/secrets/db-credentials (or /vault/secrets/db-credentials if you didn’t specify a custom mount path).

Manually Fetching Secrets (Less Recommended for Production)

While the injector is preferred, you can also manually fetch secrets from within your application using the Vault API or a Vault client library.

  1. Configure Kubernetes Service Account for Vault Authentication:

You need to create a Service Account in Kubernetes and then bind it to a Vault role as described earlier.

“`bash

In Kubernetes

kubectl create serviceaccount my-app-sa -n default

“`

Then, create the Vault role binding as shown in the previous section.

  1. Provide Vault Address to Your Application: Your application needs to know where to find the Vault server. This is often done via environment variables.
  1. Authenticate with Vault: Your application will need to get its Kubernetes Service Account token and use it to authenticate with Vault.

“`go

// Example using HashiCorp Vault Go Client

import (

“io/ioutil”

“os”

vaultapi “github.com/hashicorp/vault/api”

)

// …

vaultAddr := os.Getenv(“VAULT_ADDR”) // e.g., “http://vault.vault.svc.cluster.local:8200”

client, err := vaultapi.NewClient(&vaultapi.Config{Address: vaultAddr})

if err != nil {

// handle error

}

// Read Kubernetes Service Account token

k8sToken, err := ioutil.ReadFile(“/var/run/secrets/kubernetes.io/serviceaccount/token”)

if err != nil {

// handle error

}

// Authenticate using Kubernetes auth method

authPath := “auth/kubernetes/login”

loginData := map[string]interface{}{

“jwt”: string(k8sToken),

“role”: “my-app-role”, // The Vault role name

}

resp, err := client.Logical().Write(authPath, loginData)

if err != nil {

// handle error

}

// Get the Vault token from the response

clientToken := resp.Auth.ClientToken

client.SetToken(clientToken)

// Now you can use the client to read secrets

“`

  1. Read Secrets: Once authenticated, you can read secrets from Vault.

“`go

// Example reading a secret

secret, err := client.Logical().Read(“secret/data/myapp/db-credentials”)

if err != nil {

// handle error

}

// Access secret data, e.g., secret.Data[“data”][“username”]

“`

In the realm of Kubernetes security, managing sensitive information is crucial, and integrating HashiCorp Vault can significantly enhance your approach. For those looking to explore additional tools that can aid in managing secrets effectively, you might find it helpful to read about the best free software for voice recording, which can be a useful resource for capturing important meetings or discussions related to your Kubernetes projects. You can check it out here. This combination of tools can streamline your workflow while ensuring that your secrets remain secure.

Managing Secrets Lifecycle and Rotation

“`html

Metrics Value
Number of Secrets Managed 500
Number of Kubernetes Clusters Integrated 10
Number of Vault Policies Defined 20
Number of Access Control Rules Implemented 100

“`

Vault isn’t just about storing secrets; it’s about managing their entire lifecycle.

Dynamic Secrets Generation

This is where Vault truly shines. Instead of storing static credentials, Vault can generate them on demand.

  1. Enable Dynamic Secrets Engines: Vault has specific engines for generating dynamic secrets, such as:
  • Database Secrets Engine: For generating temporary database credentials.
  • AWS Secrets Engine: For generating temporary IAM user credentials.
  • PKI Secrets Engine: For generating short-lived TLS certificates.
  1. Configure Dynamic Secret Roles: You create roles within these engines that define the parameters for generated secrets (e.g., database username format, password complexity, lease duration, allowed roles/permissions).
  • Example (PostgreSQL Database):

“`bash

Assuming you have the database secrets engine enabled at ‘database’

vault write database/config/my-pg-db \

plugin_name=”postgresql-database-plugin” \

allowed_roles=”my-app-db-role” \

connection_urls=”postgresql://vault:vault-password@pg-host:5432/mydatabase” \

username=”vault” \

password=”vault-password”

vault write database/roles/my-app-db-role \

db_name=”my-pg-db” \

creation_statements=”CREATE USER {{name}} WITH PASSWORD ‘{{password}}’ VALID UNTIL ‘{{ttl}}’; GRANT CONNECT ON DATABASE mydatabase TO {{name}};” \

default_ttl=”10m” \

max_ttl=”30m”

“`

This configures Vault to talk to your PostgreSQL instance and defines a role that can create users with specific permissions and TTLs.

  1. Application Integration: Your application authenticates with Vault (as shown previously) and then requests a credential for the my-app-db-role. Vault generates a username and password, grants it a lease, and returns it. The application uses these credentials to connect to the database. When the lease expires, Vault automatically revokes the credential. The application can also renew the lease if it needs to continue using the credential.

Secret Rotation and Renewal

  • Automatic Rotation: For dynamic secrets, rotation is handled by Vault based on the defined TTLs and renewal policies.
  • Manual Rotation: For static secrets (those not generated dynamically), you’ll still need a process for rotation. Vault’s audit logs and policies can help enforce rotation requirements, but the actual secret update might need an external automation or manual intervention.
  • Application Renewal: When using the Vault Agent Injector or client libraries, applications can be programmed to detect when a secret’s lease is nearing expiration and request a renewal from Vault, preventing unexpected access failures.

Advanced Considerations and Best Practices

As you move beyond basic integration, there are several advanced topics and best practices to keep in mind.

Centralized Auditing and Monitoring

  • Vault Audit Devices: Configure Vault to send audit logs to various destinations (file, syslog, Kafka, Splunk, etc.). This is crucial for compliance and security investigations.
  • Kubernetes Audit Logs: Complement Vault’s logs with Kubernetes audit logs to correlate access attempts and understand the full picture of what’s happening in your cluster.
  • Monitoring Vault: Set up Prometheus metrics for Vault and monitor its health, performance, and key statistics.

Multi-Cluster and Multi-Datacenter Deployments

  • Vault Replication: For high availability across multiple data centers or regions, configure Vault replication. This ensures that secrets are available even if one datacenter is down.
  • Kubernetes Multi-Cluster: When managing secrets across multiple Kubernetes clusters, each cluster can authenticate with a single, replicated Vault instance. The Vault Agent Injector and Kubernetes auth method work well in this scenario.

Secure Storage Backend for Vault

  • Production Storage: For production, always use a robust storage backend for Vault, such as Consul, integrated storage (Raft), or cloud provider-specific storage solutions. Avoid insecure storage.
  • Encryption at Rest: Ensure your storage backend is also configured with encryption at rest, adding another layer of security.

Least Privilege Principle

  • Fine-Grained Policies: Always apply the principle of least privilege. Grant only the necessary permissions to Service Accounts and roles. Avoid using broad wildcards in Vault policies unless absolutely necessary.
  • Dedicated Roles: Create specific roles for each application or team, rather than a single super-role.

Managing Vault Itself

  • Root Token Security: Secure your Vault root token. Store it offline in a password manager or secure vault. Use it only for initial setup and critical administration tasks.
  • Unseal Key Management: Distribute unseal keys securely among trusted individuals or systems. Consider automated unsealing solutions if your operational model supports it.
  • Regular Backups: Regularly back up your Vault data and ensure you have a tested recovery procedure.

Integrating HashiCorp Vault with Kubernetes provides a powerful and secure way to manage your sensitive credentials. By following these steps, you can move from basic Kubernetes Secrets to a dynamic, auditable, and highly secure secrets management system that scales with your applications. Remember that security is an ongoing process, so regularly review your Vault configuration, policies, and audit logs.

FAQs

What is Kubernetes?

Kubernetes is an open-source platform designed to automate deploying, scaling, and operating application containers.

What is HashiCorp Vault?

HashiCorp Vault is a tool for securely accessing and managing secrets. It provides a centralized place to store sensitive data such as API keys, passwords, and certificates.

Why integrate HashiCorp Vault with Kubernetes?

Integrating HashiCorp Vault with Kubernetes allows for secure management and distribution of secrets within a Kubernetes environment. This integration helps to ensure that sensitive data is protected and accessed only by authorized applications and users.

What are the benefits of managing secrets in Kubernetes with HashiCorp Vault?

Managing secrets in Kubernetes with HashiCorp Vault provides benefits such as centralized secret management, dynamic secret generation, audit logging, and fine-grained access control. This helps to enhance security and compliance within Kubernetes deployments.

What are the steps for integrating HashiCorp Vault with Kubernetes?

The steps for integrating HashiCorp Vault with Kubernetes typically involve installing and configuring the Vault Kubernetes Auth method, creating policies and roles for Kubernetes service accounts, and enabling applications running in Kubernetes to access secrets from Vault.

Tags: No tags