Implement zero trust network architecture where every request is authenticated and authorized regardless of network location. Covers identity-based access, micro-segmentation, mutual TLS, policy engines, and the migration path from perimeter-based security.
Zero Trust Networking: Security Beyond the Perimeter
In a world where work happens across clouds, devices, and networks, Zero Trust Networking ensures that every request to a service is authenticated, authorized, and encrypted—regardless of the user’s location, device, or network. It matters when you need to enforce consistent security across hybrid infrastructure: when developers deploy services to multiple clouds, when users access internal apps from home or on the road, and when the network perimeter has dissolved into a mesh of endpoints and APIs.
To begin securing a service with Zero Trust, the first step is to define a trust boundary around each resource. This involves configuring identity-based access control for every endpoint, ensuring that no request is trusted simply because it arrives from the internal network.
Every service must require mutual TLS (mTLS) between client and server. The client presents a certificate signed by a trusted certificate authority (CA), and the server verifies it before accepting the connection. The server also presents its own certificate, which the client validates.
A typical service configuration in a gRPC server using Envoy as a sidecar proxy includes the following settings:
static_resources:
listeners:
- name: http
address:
socket_address:
address: 0.0.0.0
port_value: 8080
filter_chains:
- filters:
- name: envoy.filters.network.http_connection_manager
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
stat_prefix: ingress
route_config:
name: local_route
virtual_hosts:
- name: backend
domains: ["*"]
routes:
- match:
prefix: "/"
route:
cluster: backend_service
http_filters:
- name: envoy.filters.http.router
transport_socket:
name: envoy.transport_sockets.tls
typed_config:
"@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext
sni: "backend.example.com"
common_tls_context:
tls_certificates:
- certificate_chain:
inline_string: |
-----BEGIN CERTIFICATE-----
MIID... (full server cert)
-----END CERTIFICATE-----
private_key:
inline_string: |
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC...
-----END PRIVATE KEY-----
validation_context:
trusted_ca:
inline_string: |
-----BEGIN CERTIFICATE-----
MIID... (CA cert used to sign client certs)
-----END CERTIFICATE-----
verification_mode: SUBJECT_ALTERNATIVE_NAME
The verification_mode: SUBJECT_ALTERNATIVE_NAME ensures that the client’s certificate must include the service’s hostname (or IP) in its SAN field. A client that presents a certificate without a SAN entry for backend.example.com fails to authenticate.
Clients must also send a X-Forwarded-For header and a Authorization: Bearer <token> header. The bearer token is a JWT issued by an identity provider (IdP), containing claims such as sub, email, groups, and iss.
The server validates the JWT using a public key (JWKS) endpoint at https://idp.example.com/.well-known/jwks.json. If the signature is invalid, the server returns a 401 Unauthorized with a body:
{
"error": "invalid_token",
"error_description": "The access token is invalid or expired",
"error_uri": "https://idp.example.com/docs/errors#invalid-token"
}
A common failure occurs when the client’s certificate is valid but the subject field does not match the expected iss (issuer) claim. For example, a client with iss: https://auth.acme.com but subject: auth.acme.com fails to authenticate if the server expects iss to match subject exactly.
Once a connection is established, the system must verify the identity of both the device and the user. Devices are validated based on attestation, which includes verifying the device’s integrity, configuration, and runtime state.
A device attestation report is sent in the initial handshake. The report is a JSON object signed with the device’s private key:
{
"device_id": "d123456789",
"platform": "linux",
"os_version": "Ubuntu 22.04",
"boot_time": "2023-07-14T10:00:00Z",
"tpm_measurement": [
{
"pcr": 0,
"hash": "a1b2c3d4e5f6..."
},
{
"pcr": 1,
"hash": "f6e5d4c3b2a1..."
}
],
"installed_packages": [
"nginx",
"python3",
"git",
"docker"
],
"trust_level": "high"
}
The server validates the report by verifying the signature using the device’s public key, which is stored in a registry. The registry is a simple HTTP API:
GET /devices/d123456789/public-key HTTP/1.1
Host: device-registry.example.com
Accept: application/json
{
"device_id": "d123456789",
"public_key": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA...",
"created_at": "2023-07-14T09:00:00Z"
}
The server then validates the tpm_measurement values against a known good state. If the pcr values do not match the expected baseline, the device is marked as untrusted.
A device is considered trusted if:
tpm_measurement values match a predefined baseline.boot_time is within 15 minutes of the expected boot time.installed_packages list contains all required packages, and no unexpected ones.If any check fails, the server returns a 403 Forbidden with a detailed payload:
{
"error": "device_untrusted",
"details": [
{
"check": "tpm_measurement",
"pcr": 0,
"expected": "a1b2c3d4e5f6...",
"actual": "a1b2c3d4e5f7...",
"reason": "PCR 0 mismatch: expected a1b2c3d4e5f6, got a1b2c3d4e5f7"
},
{
"check": "installed_packages",
"missing": ["nginx"],
"unexpected": ["vim", "htop"]
}
]
}
The system fails silently when tpm_measurement is sent but boot_time is missing. The server assumes the device is trusted if boot_time is present, but it does not validate the boot_time value—it only checks for its existence.
With identity and device trust established, the system enforces access policies based on the full context of the request. This includes user attributes, device posture, network location, and time of day.
Access decisions are made using a policy engine that evaluates a set of rules. Each rule is a JSON object with conditions and actions. A policy might look like:
{
"id": "access-to-financial-data",
"description": "Allow users in finance team to access financial data",
"conditions": [
{
"type": "claim",
"name": "groups",
"values": ["finance", "executive"],
"match": "any"
},
{
"type": "device",
"property": "trust_level",
"value": "high"
},
{
"type": "time",
"start": "09:00",
"end": "17:00"
},
{
"type": "network",
"source": "10.0.0.0/8",
"location": "office"
}
],
"actions": [
{
"type": "grant",
"permissions": ["read", "write"],
"duration": "4h"
},
{
"type": "require",
"factor": "mfa"
}
],
"priority": 100
}
The policy engine evaluates all active policies and selects the highest-priority one that matches the request context. Policies are stored in a database and exposed via a REST API:
POST /policies/evaluate HTTP/1.1
Host: policy-engine.example.com
Content-Type: application/json
{
"user": {
"email": "[email protected]",
"groups": ["finance", "devops"],
"iss": "https://auth.acme.com"
},
"device": {
"device_id": "d123456789",
"trust_level": "high",
"location": "office",
"network": "10.0.0.12"
},
"timestamp": "2023-07-14T11:30:00Z"
}
The engine responds with:
{
"policy_id": "access-to-financial-data",
"matched": true,
"reason": "User in finance group, device trusted, within office hours",
"grant": {
"permissions": ["read", "write"],
"expires_at": "2023-07-14T15:30:00Z"
},
"requirements": [
{
"factor": "mfa",
"required": true,
"reason": "MFA required for finance data access"
}
]
}
If a user requests access to a service at 18:00, the policy engine returns matched: false because the time condition fails. The user is granted only the default access, and the system logs a warning: Policy access-to-financial-data not matched due to time window.
In a microservices architecture, services must communicate securely with one another. Zero Trust requires that every service-to-service call is authenticated, encrypted, and audited.
Services use a service mesh such as Istio or Linkerd, but the configuration is standardized across platforms. Each service is assigned a unique identity in the form of a service account, and all communication is secured using mTLS.
To configure service-to-service communication in Istio, a DestinationRule and a PeerAuthentication policy are used:
apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
name: default
spec:
mtls:
mode: STRICT
selector:
matchLabels:
app: backend
apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
name: backend
spec:
host: backend.example.com
trafficPolicy:
tls:
mode: ISTIO_MUTUAL
loadBalancer:
simple: ROUND_ROBIN
The mode: STRICT in PeerAuthentication means that all incoming connections must be mTLS, and the server must validate the client’s certificate. The ISTIO_MUTUAL mode in DestinationRule configures Istio to use the service account’s certificate as the client identity.
Each service logs every request with a X-Request-ID header, and the service mesh captures the full request context in a distributed trace. A typical trace includes:
http.request_id: UUID of the request.http.user_agent: Client software and version.http.x_forwarded_for: Original client IP.grpc.status_code: gRPC status code.mesh.service: The service that handled the request.When a service fails, the trace shows the entire path from client to final service, including the identity of each service involved.
A common failure is a mismatch between the service_account and the subject field in the client certificate. For example, a service calling inventory-service with a service account inventory@prod but the client certificate has subject: inventory-service.prod fails to authenticate, even though the names are similar. The server logs:
Error: mTLS handshake failed
- Client certificate subject mismatch
- Expected: inventory@prod
- Actual: inventory-service.prod
Trust is not a one-time event. The system continuously monitors the state of users, devices, and services. Every 10 minutes, a health check runs across all services.
A health check is a periodic request to a /health endpoint that returns a JSON response:
{
"status": "ok",
"timestamp": "2023-07-14T11:40:00Z",
"checks": [
{
"name": "mTLS_validation",
"status": "pass",
"details": {
"client_cert_valid": true,
"server_cert_valid": true,
"session_reused": true
}
},
{
"name": "device_attestation",
"status": "fail",
"details": {
"last_updated": "2023-07-14T10:30:00Z",
"required_interval": "60m",
"missing": ["inventory-service", "billing-service"]
}
}
],
"metrics": {
"requests_per_second": 123,
"error_rate": 0.02,
"latency_p95": 320
}
}
If the device_attestation check fails, the system triggers a revalidation of the device’s attestation report. The device must re-upload its report within 5 minutes, or it is marked as degraded.
The system also revalidates the user’s JWT token every 15 minutes. The token is refreshed using a refresh_token flow:
POST /oauth2/token HTTP/1.1
Host: auth.acme.com
Content-Type: application/x-www-form-urlencoded
grant_type=refresh_token&refresh_token=rt123456789
The server responds with a new JWT and a new refresh token:
{
"access_token": "at123456789",
"expires_in": 3600,
"refresh_token": "rt987654321",
"refresh_token_expires_in": 2592000
}
If the refresh fails—due to network issues or an expired refresh token—the client must reauthenticate using the full OAuth2 flow.
Failure modes include:
401 but does not include the WWW-Authenticate header with a challenge.Zero Trust Networking is not a single configuration but a continuous process of trust validation. Every request is an opportunity to verify identity, device state, and context. The system succeeds when a user can access a service from any device at any time, with the assurance that the service knows who they are, what device they’re using, and what they’re allowed to do—without requiring a static network perimeter.
This page was rewritten on 18 October 2026. It replaced a templated version that was not specific to its own title. How these pages are written and checked. If anything here is wrong, tell us at [email protected] and we will correct it.
We use cookies for analytics (Google Analytics) and advertising (Google AdSense) to improve your experience and support free content. Privacy Policy