Consul’s ServiceDefaults Custom Resource Definition (CRD) lets you configure default behavior for services without touching their individual configurations.
Let’s see how this works by setting up a simple mesh and then applying ServiceDefaults.
Imagine we have two services, frontend and backend, running in our Kubernetes cluster.
apiVersion: v1
kind: Service
metadata:
name: frontend
spec:
ports:
- port: 80
targetPort: 8080
name: http
---
apiVersion: v1
kind: Service
metadata:
name: backend
spec:
ports:
- port: 8080
targetPort: 8080
name: http
And their corresponding deployments (simplified):
apiVersion: apps/v1
kind: Deployment
metadata:
name: frontend-deployment
spec:
replicas: 1
selector:
matchLabels:
app: frontend
template:
metadata:
labels:
app: frontend
spec:
containers:
- name: frontend
image: nginx:latest # Replace with your actual frontend image
ports:
- containerPort: 8080
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: backend-deployment
spec:
replicas: 1
selector:
matchLabels:
app: backend
template:
metadata:
labels:
app: backend
spec:
containers:
- name: backend
image: nginx:latest # Replace with your actual backend image
ports:
- containerPort: 8080
With Consul and its Kubernetes integration set up, these services will be automatically registered. Now, let’s say we want to enforce mTLS for all communication originating from the frontend service by default. Without ServiceDefaults, you’d typically configure this in the frontend’s mesh configuration.
Here’s how ServiceDefaults changes that. We create a ServiceDefaults CRD:
apiVersion: consul.hashicorp.com/v1alpha1
kind: ServiceDefaults
metadata:
name: frontend
spec:
destinationDefault:
enableIntentions: true
tls:
mode: enabled
When you apply this YAML, Consul’s Dataplane will observe this ServiceDefaults resource. For any service named frontend in Consul’s catalog, its sidecar proxy will automatically configure itself to require TLS for incoming connections from other services, and enforce intentions. The enableIntentions: true ensures that even with TLS enabled, standard Consul intentions are still enforced. This means that even if a frontend sidecar is configured to accept TLS, an intention must still explicitly allow traffic from the source service.
The mode: enabled specifically tells the sidecar proxy to require TLS for incoming connections. If a client tries to connect to frontend without TLS, the connection will be dropped.
This is incredibly powerful because you don’t need to modify the frontend deployment or its configuration. The ServiceDefaults CRD acts as a central policy engine. You can also set defaults for all services if you omit the metadata.name or use a wildcard if supported by your Consul version, though explicitly naming services is generally preferred for clarity and control.
For example, to set a default for all services in a namespace:
apiVersion: consul.hashicorp.com/v1alpha1
kind: ServiceDefaults
metadata:
name: "*" # Applies to all services in the namespace
spec:
destinationDefault:
tls:
mode: permit_unencrypted
Here, permit_unencrypted means the frontend sidecar will allow unencrypted connections but will attempt to upgrade to TLS if the client supports it. This is a more lenient default, often used during migration.
The core problem ServiceDefaults solves is the operational overhead of managing security policies for each individual service. Instead of configuring mTLS on a per-service basis, you define a global or per-service default that the sidecar proxies automatically pick up. This significantly reduces configuration drift and simplifies security posture management.
The ServiceDefaults CRD is specifically about configuring the destination side of a connection. It dictates how the service itself behaves when other services try to connect to it. This is distinct from ServiceResolver or ServiceRouter CRDs, which control how a service finds and connects to other services.
A common pitfall is misunderstanding the scope of destinationDefault. It configures the proxy for the service named in the CRD’s metadata.name when it is the destination of traffic. It does not affect how that service initiates connections to other services. For controlling outbound connections, you’d look at ServiceDefaults’s upstreamDefaults or other CRDs like ServiceResolver.
If you were to forget to apply a ServiceDefaults resource that explicitly enabled TLS, and later tried to enforce intentions, you might see errors indicating that intentions are not being enforced or that connections are succeeding without proper encryption, even though you expected them to be secured.
The next concept you’ll likely explore is how to manage ServiceDefaults across different namespaces, or how to combine ServiceDefaults with ServiceIntentions for fine-grained access control.