Secure Communication in Kubernetes with Consul Connect and Vault Agent Injector
Securing Communication in Kubernetes with Consul Connect and Vault Agent Injector In modern cloud-native Kubernetes environments, security is paramount. One of
In modern cloud-native Kubernetes environments, security is paramount. One of the key challenges is ensuring secure communication between microservices while also managing secrets securely. Whether you're running a monolithic application or a set of microservices in Kubernetes, securing inter-service communication and secret management is crucial to prevent unauthorized access and ensure data privacy. Enter HashiCorp Consul Connect combined with HashiCorp Vault Agent Injector, bringing the same powerful service mesh solution from the HashiCorp ecosystem to Kubernetes.
In this blog post, we'll explore how you can secure the communication of your workloads in Kubernetes using HashiCorp Consul Connect service mesh while leveraging Vault Agent Injector for dynamic secret management, providing a direct Kubernetes equivalent to our Nomad examples.
Note: This article demonstrates how to use the same HashiCorp Consul Connect service mesh technology shown in our Secure Communication on HashiCorp Nomad with Consul Connect post, but running in Kubernetes. It also builds upon the secret management concepts from Kubernetes and Vault Agent Injector: Dynamic Secrets Management.
Consul Connect is HashiCorp's service mesh solution that provides secure service-to-service communication in your infrastructure. It achieves this by enabling mutual Transport Layer Security (mTLS) between services. Each service gets its own identity, and Consul enforces that only trusted services can communicate with each other. By using Connect, service communication is authenticated and encrypted, ensuring that the network's security is maintained even in hostile or compromised environments.
While Kubernetes provides excellent orchestration capabilities, it lacks native service mesh features for encrypted communication between services. Consul Connect bridges this gap by bringing HashiCorp's proven service mesh technology to Kubernetes environments. When combined with Vault Agent Injector, you get:
consul.example.com:8500
vault.example.com:8200
Install only the Consul Connect injector components to connect directly to an external Consul cluster without deploying Consul clients:
1# Add HashiCorp Helm repository
2helm repo add hashicorp https://helm.releases.hashicorp.com
3helm repo update
4
5# Create consul-values.yaml for external Consul configuration
6cat > consul-values.yaml <<EOF
7global:
8 name: consul
9 datacenter: dc1
10 # Configure to use external Consul servers directly
11 consulAPITimeout: 5s
12
13# Disable server installation - we're using external Consul
14server:
15 enabled: false
16
17# Disable client - Connect injector will communicate directly with external Consul
18client:
19 enabled: false
20
21# Enable Connect injection for external Consul
22connectInject:
23 enabled: true
24 default: false # We'll explicitly enable per service
25 # Configure injector to use external Consul directly
26 consulNode:
27 meta:
28 pod-name: \${HOSTNAME}
29 node-name: \${NODE_NAME}
30 # External Consul servers
31 k8sAllowNamespaces: ["*"]
32 k8sDenyNamespaces: []
33
34controller:
35 enabled: true
36
37# Disable UI since we're using external Consul
38ui:
39 enabled: false
40
41# Disable DNS since no clients are deployed
42dns:
43 enabled: false
44
45# Configure external Consul connection
46externalServers:
47 enabled: true
48 hosts: ["consul.example.com"]
49 httpsPort: 8501 # Use HTTPS port for secure connection
50 useSystemRoots: true
51EOF
52
53# Install Consul Connect components
54kubectl create namespace consul
55helm install consul hashicorp/consul \
56 --namespace consul \
57 --values consul-values.yaml
58
59# Wait for Connect injector to be ready
60kubectl wait --for=condition=ready pod -l app=consul-connect-injector -n consul --timeout=300s
Install the Vault Agent Injector as demonstrated in our previous posts:
1# Install only the Agent Injector (no Vault server)
2helm install vault-injector hashicorp/vault \
3 --set "global.externalVaultAddr=https://vault.example.com:8200" \
4 --set "injector.enabled=true" \
5 --set "server.enabled=false" \
6 --set "csi.enabled=false"
Set up the same Vault configuration as in our previous Kubernetes posts:
1# Set Vault address environment variable
2export VAULT_ADDR="https://vault.example.com:8200"
3
4# Enable Kubernetes auth method
5vault auth enable kubernetes
6
7# Configure Kubernetes auth
8KUBERNETES_HOST=$(kubectl config view --raw --minify --flatten -o jsonpath='{.clusters[].cluster.server}')
9KUBERNETES_CA_CERT=$(kubectl config view --raw --minify --flatten -o jsonpath='{.clusters[].cluster.certificate-authority-data}' | base64 -d)
10
11# Create service account and configure auth
12kubectl create serviceaccount vault-auth
13kubectl apply -f - <<EOF
14apiVersion: rbac.authorization.k8s.io/v1
15kind: ClusterRoleBinding
16metadata:
17 name: role-tokenreview-binding
18roleRef:
19 apiGroup: rbac.authorization.k8s.io
20 kind: ClusterRole
21 name: system:auth-delegator
22subjects:
23- kind: ServiceAccount
24 name: vault-auth
25 namespace: default
26EOF
27
28TOKEN_REVIEWER_JWT=$(kubectl create token vault-auth)
29
30vault write auth/kubernetes/config \
31 token_reviewer_jwt="$TOKEN_REVIEWER_JWT" \
32 kubernetes_host="$KUBERNETES_HOST" \
33 kubernetes_ca_cert="$KUBERNETES_CA_CERT"
1# Enable database secrets engine
2vault secrets enable -path=dynamic-app/db database
3
4# Configure MySQL connection
5vault write dynamic-app/db/config/mysql \
6 plugin_name=mysql-database-plugin \
7 connection_url="{{username}}:{{password}}@tcp(mysql-server.demo.svc.cluster.local:3306)/" \
8 allowed_roles="*" \
9 username="root" \
10 password="super-duper-password"
11
12# Create dynamic credentials role
13vault write dynamic-app/db/roles/app \
14 db_name=mysql \
15 creation_statements="CREATE USER '{{name}}'@'%' IDENTIFIED BY '{{password}}';GRANT ALL ON my_app.* TO '{{name}}'@'%';" \
16 default_ttl="1h" \
17 max_ttl="24h"
18
19# Enable transit engine for encryption
20vault secrets enable -path=dynamic-app/transit transit
21vault write -f dynamic-app/transit/keys/app
22
23# Create Vault policy
24vault policy write dynamic-app-consul - <<EOF
25path "dynamic-app/db/creds/app" {
26 capabilities = ["read"]
27}
28path "dynamic-app/transit/encrypt/app" {
29 capabilities = ["update"]
30}
31path "dynamic-app/transit/decrypt/app" {
32 capabilities = ["update"]
33}
34EOF
35
36# Create Kubernetes role
37vault write auth/kubernetes/role/dynamic-app-consul \
38 bound_service_account_names=dynamic-app-consul \
39 bound_service_account_namespaces=demo \
40 policies=dynamic-app-consul \
41 ttl=24h
1# Create demo namespace
2kubectl create namespace demo
3
4# Create service accounts
5kubectl create serviceaccount dynamic-app-consul -n demo
6kubectl create serviceaccount mysql-server-consul -n demo
1# mysql-consul-deployment.yaml
2apiVersion: apps/v1
3kind: Deployment
4metadata:
5 name: mysql-server
6 namespace: demo
7 labels:
8 app: mysql-server
9spec:
10 replicas: 1
11 selector:
12 matchLabels:
13 app: mysql-server
14 template:
15 metadata:
16 labels:
17 app: mysql-server
18 annotations:
19 "consul.hashicorp.com/connect-inject": "true"
20 "consul.hashicorp.com/connect-service": "mysql-server"
21 "consul.hashicorp.com/connect-service-port": "3306"
22 spec:
23 serviceAccountName: mysql-server-consul
24 containers:
25 - name: mysql
26 image: mysql:9
27 env:
28 - name: MYSQL_ROOT_PASSWORD
29 value: "super-duper-password"
30 - name: MYSQL_DATABASE
31 value: "my_app"
32 ports:
33 - containerPort: 3306
34 name: mysql
35 resources:
36 requests:
37 cpu: 500m
38 memory: 1Gi
39 limits:
40 cpu: 500m
41 memory: 1Gi
42 readinessProbe:
43 exec:
44 command:
45 - "/bin/bash"
46 - "-c"
47 - "mysqladmin ping -h 127.0.0.1 -u root -p$MYSQL_ROOT_PASSWORD"
48 initialDelaySeconds: 30
49 periodSeconds: 10
50 livenessProbe:
51 exec:
52 command:
53 - "/bin/bash"
54 - "-c"
55 - "mysqladmin ping -h 127.0.0.1 -u root -p$MYSQL_ROOT_PASSWORD"
56 initialDelaySeconds: 60
57 periodSeconds: 10
58
59---
60apiVersion: v1
61kind: Service
62metadata:
63 name: mysql-server
64 namespace: demo
65 labels:
66 app: mysql-server
67spec:
68 ports:
69 - port: 3306
70 name: mysql
71 targetPort: 3306
72 selector:
73 app: mysql-server
1# dynamic-app-consul.yaml
2apiVersion: apps/v1
3kind: Deployment
4metadata:
5 name: dynamic-app
6 namespace: demo
7 labels:
8 app: dynamic-app
9spec:
10 replicas: 1
11 selector:
12 matchLabels:
13 app: dynamic-app
14 template:
15 metadata:
16 labels:
17 app: dynamic-app
18 annotations:
19 # Consul Connect annotations
20 "consul.hashicorp.com/connect-inject": "true"
21 "consul.hashicorp.com/connect-service": "dynamic-app"
22 "consul.hashicorp.com/connect-service-port": "8080"
23 "consul.hashicorp.com/connect-service-upstreams": "mysql-server:3306"
24
25 # Vault Agent Injector annotations
26 "vault.hashicorp.com/agent-inject": "true"
27 "vault.hashicorp.com/agent-inject-status": "update"
28 "vault.hashicorp.com/agent-inject-vault-addr": "https://vault.example.com:8200"
29 "vault.hashicorp.com/role": "dynamic-app-consul"
30 "vault.hashicorp.com/agent-inject-secret-config.ini": "dynamic-app/db/creds/app"
31 "vault.hashicorp.com/agent-inject-template-config.ini": |
32 [DEFAULT]
33 LogLevel = DEBUG
34 Port = 8080
35
36 [DATABASE]
37 Address = 127.0.0.1
38 Port = 3306
39 Database = my_app
40 {{- with secret "dynamic-app/db/creds/app" }}
41 User = {{ .Data.username }}
42 Password = {{ .Data.password }}
43 {{- end }}
44
45 [VAULT]
46 Enabled = True
47 InjectToken = True
48 Namespace =
49 Address = https://vault.example.com:8200
50 KeyPath = dynamic-app/transit
51 KeyName = app
52 spec:
53 serviceAccountName: dynamic-app-consul
54 containers:
55 - name: dynamic-app
56 image: ghcr.io/infralovers/nomad-vault-mysql:1.0.0
57 ports:
58 - containerPort: 8080
59 name: http
60 env:
61 - name: CONFIG_FILE
62 value: "/vault/secrets/config.ini"
63 - name: VAULT_ADDR
64 value: "https://vault.example.com:8200"
65 resources:
66 requests:
67 cpu: 256m
68 memory: 256Mi
69 limits:
70 cpu: 256m
71 memory: 256Mi
72 livenessProbe:
73 httpGet:
74 path: /health
75 port: 8080
76 initialDelaySeconds: 60
77 periodSeconds: 10
78 readinessProbe:
79 httpGet:
80 path: /health
81 port: 8080
82 initialDelaySeconds: 30
83 periodSeconds: 5
84
85---
86apiVersion: v1
87kind: Service
88metadata:
89 name: dynamic-app
90 namespace: demo
91 labels:
92 app: dynamic-app
93spec:
94 ports:
95 - port: 8080
96 name: http
97 targetPort: 8080
98 selector:
99 app: dynamic-app
100
101---
102# Ingress for external access
103apiVersion: networking.k8s.io/v1
104kind: Ingress
105metadata:
106 name: dynamic-app-ingress
107 namespace: demo
108 annotations:
109 nginx.ingress.kubernetes.io/rewrite-target: /
110spec:
111 rules:
112 - host: dynamic-app.local
113 http:
114 paths:
115 - path: /
116 pathType: Prefix
117 backend:
118 service:
119 name: dynamic-app
120 port:
121 number: 8080
Create Consul intentions to control service-to-service communication, just like in the Nomad example. Connect to your external Consul cluster to configure intentions:
1# Set Consul address environment variable
2export CONSUL_HTTP_ADDR="https://consul.example.com:8500"
3
4# Create intention to allow dynamic-app to connect to mysql-server
5consul intention create -allow dynamic-app mysql-server
6
7# Verify intentions
8consul intention list
9
10# Optional: Create more granular intentions with deny-all default
11consul intention create -deny "*" "*"
12consul intention create -allow dynamic-app mysql-server
Alternatively, you can use Kubernetes CRDs for intentions:
1# consul-intentions.yaml
2apiVersion: consul.hashicorp.com/v1alpha1
3kind: ServiceIntentions
4metadata:
5 name: mysql-server-intentions
6 namespace: demo
7spec:
8 destination:
9 name: mysql-server
10 sources:
11 - name: dynamic-app
12 action: allow
This deployment approach uses no Consul clients in Kubernetes, providing several advantages:
The Consul Connect injector operates independently and:
You can customize the Connect proxy configuration using annotations:
1annotations:
2 "consul.hashicorp.com/connect-inject": "true"
3 "consul.hashicorp.com/connect-service": "dynamic-app"
4 "consul.hashicorp.com/connect-service-port": "8080"
5 "consul.hashicorp.com/connect-proxy-cpu-request": "50m"
6 "consul.hashicorp.com/connect-proxy-memory-request": "64Mi"
7 "consul.hashicorp.com/connect-proxy-cpu-limit": "100m"
8 "consul.hashicorp.com/connect-proxy-memory-limit": "128Mi"
9 "consul.hashicorp.com/envoy-extra-args": "--log-level debug"
Consul Connect can integrate with Kubernetes health checks:
1annotations:
2 "consul.hashicorp.com/service-sync": "true"
3 "consul.hashicorp.com/service-port": "8080"
4 "consul.hashicorp.com/connect-service-port": "8080"
Feature | Nomad + Consul Connect | Kubernetes + Consul Connect (Client-less) |
---|---|---|
Service Registration | Automatic via Nomad | Direct API calls to external Consul |
Sidecar Injection | Native Nomad integration | Kubernetes admission controller |
Configuration | Job specification HCL | Kubernetes annotations |
Service Discovery | Consul DNS/API | External Consul API calls |
Health Checks | Nomad + Consul checks | Kubernetes + External Consul API |
Resource Management | Nomad scheduler | Kubernetes scheduler |
Local Consul Agents | Optional with Connect | None required (client-less) |
Using HashiCorp Consul Connect with Vault Agent Injector in Kubernetes provides a familiar and powerful service mesh solution that maintains consistency with your existing HashiCorp infrastructure. This approach is particularly valuable for organizations already using Consul Connect with Nomad who want to extend the same security patterns to Kubernetes workloads.
Key advantages of this approach:
By leveraging Consul Connect in Kubernetes, you can ensure secure, authenticated, and encrypted communication between your services while maintaining the operational patterns and expertise your team has developed with HashiCorp's service mesh technology.
Start enhancing your Kubernetes deployments today by integrating Consul Connect with Vault Agent Injector, and maintain the same level of service mesh security and operational consistency across your entire infrastructure.
You are interested in our courses or you simply have a question that needs answering? You can contact us at anytime! We will do our best to answer all your questions.
Contact us