Zero-Trust Microservices
Build a zero-trust service mesh where every internal service-to-service call is authenticated via short-lived JWTs, policies control which services can call which endpoints, and lateral movement is impossible. Go SDK.
Zero-Trust Microservices
What you'll build: A three-service mesh (Orders → Inventory → Payments) where every
inter-service call carries a signed service JWT. No service trusts another based on network
position alone. Rego policy controls which service can call which endpoint. A compromised
Orders service cannot call Payments directly. Go SDK.
The Story
Shopstream runs an e-commerce platform. After a pen-test found that a compromised orders-service
could directly call payments-service with no auth (both on the same VPC subnet), they adopted
zero-trust. Now every service call is authenticated as if it came from the internet.
Zero-trust rules:
orders-servicecan callinventory-service— read-onlyorders-servicecan callpayments-service— onlyPOST /chargewithorder_idheaderinventory-servicecannot callpayments-service— ever- External user tokens are different from internal service tokens — same validator, different claims
- Short-lived tokens (5 minute TTL) — compromise window is minimal
Architecture
User Browser
│ user JWT (sub, roles, tenant_id)
▼
┌─────────────────┐
│ orders-service │ :8081
│ CoreSDK auth │ ← validates user JWT
│ │ │
│ ▼ │
│ business logic │
│ │ │
│ service JWT │ ← mints short-lived internal token
│ │ │
└───────┼─────────┘
│ ┌─────────────────────┐
├───────────────────►│ inventory-service │ :8082
│ svc JWT │ CoreSDK auth │
│ │ policy: orders→inv │
│ └─────────────────────┘
│
│ ┌─────────────────────┐
└───────────────────►│ payments-service │ :8083
svc JWT │ CoreSDK auth │
+ order_id header │ policy: orders→pmt │
└─────────────────────┘Prerequisites
go get github.com/coresdk-dev/sdk-go
go get github.com/golang-jwt/jwt/v5Quickstart
git clone https://github.com/coresdk-dev/examples
cd examples/go/zero-trust-mesh
go run ./...Code Walkthrough
Step 1 — Service identity JWT
Each service has a keypair. On startup it mints a short-lived service token:
package serviceauth
import (
"time"
"github.com/golang-jwt/jwt/v5"
)
type ServiceClaims struct {
ServiceName string `json:"service_name"`
AllowedTargets []string `json:"allowed_targets"`
jwt.RegisteredClaims
}
// MintServiceToken creates a 5-minute service-to-service JWT.
// In production: use a secrets manager or SPIFFE/SVID.
func MintServiceToken(serviceName string, targets []string, signingKey []byte) (string, error) {
claims := ServiceClaims{
ServiceName: serviceName,
AllowedTargets: targets,
RegisteredClaims: jwt.RegisteredClaims{
Subject: serviceName,
Issuer: "shopstream-internal",
IssuedAt: jwt.NewNumericDate(time.Now()),
ExpiresAt: jwt.NewNumericDate(time.Now().Add(5 * time.Minute)),
},
}
return jwt.NewWithClaims(jwt.SigningMethodHS256, claims).SignedString(signingKey)
}Step 2 — Every service validates callers
Each service runs the same CoreSDK middleware:
func ServiceAuthMiddleware(sdk *coresdk.SDK, allowedCallers []string) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
token := strings.TrimPrefix(r.Header.Get("Authorization"), "Bearer ")
claims, err := sdk.Authorize(r.Context(), token)
if err != nil {
writeProblem(w, 401, "Unauthorized", "Service JWT required.")
return
}
// Check caller is in the allowed list for this service
callerService := claims.Subject
allowed := false
for _, a := range allowedCallers {
if a == callerService {
allowed = true
break
}
}
if !allowed {
writeProblem(w, 403, "Forbidden",
fmt.Sprintf("Service '%s' is not permitted to call this endpoint.", callerService))
return
}
ctx := context.WithValue(r.Context(), "caller_service", callerService)
ctx = context.WithValue(ctx, "claims", claims)
next.ServeHTTP(w, r.WithContext(ctx))
})
}
}Step 3 — Inventory service: only orders can call
// inventory-service/main.go
func main() {
sdk, _ := coresdk.FromEnv()
mux := http.NewServeMux()
// Only orders-service can call inventory — enforced in middleware
protected := ServiceAuthMiddleware(sdk, []string{"orders-service"})
mux.Handle("/inventory/check", protected(http.HandlerFunc(checkInventory)))
mux.Handle("/inventory/reserve", protected(http.HandlerFunc(reserveStock)))
http.ListenAndServe(":8082", mux)
}
func checkInventory(w http.ResponseWriter, r *http.Request) {
caller := r.Context().Value("caller_service").(string)
productID := r.URL.Query().Get("product_id")
// inventory-service trusts orders-service but still validates the data
stock := db.GetStock(productID)
log.Printf("inventory.check: caller=%s product=%s stock=%d", caller, productID, stock)
json.NewEncoder(w).Encode(map[string]any{
"product_id": productID,
"available": stock > 0,
"quantity": stock,
})
}Step 4 — Payments service: stricter rules via Rego
// payments-service/main.go
func main() {
sdk, _ := coresdk.FromEnv()
// Two layers: middleware (identity) + policy (intent)
mux := http.NewServeMux()
mux.HandleFunc("/charge", func(w http.ResponseWriter, r *http.Request) {
token := strings.TrimPrefix(r.Header.Get("Authorization"), "Bearer ")
claims, err := sdk.Authorize(r.Context(), token)
if err != nil {
writeProblem(w, 401, "Unauthorized", err.Error())
return
}
// Policy: only orders-service can charge, and must provide order_id
orderID := r.Header.Get("X-Order-ID")
allowed, err := sdk.EvaluatePolicy(r.Context(), "payments.allow_charge", map[string]any{
"caller_service": claims.Subject,
"order_id": orderID,
"method": r.Method,
"amount_cents": parseAmount(r),
})
if err != nil || !allowed {
writeProblem(w, 403, "Forbidden",
"Policy denied: only orders-service may charge with a valid order_id.")
return
}
result := processCharge(orderID, parseAmount(r))
json.NewEncoder(w).Encode(result)
})
http.ListenAndServe(":8083", mux)
}policies/payments.rego:
package payments
default allow_charge := false
allow_charge if {
input.caller_service == "orders-service"
input.order_id != ""
input.method == "POST"
input.amount_cents > 0
input.amount_cents <= 100_000_00 # $100,000 max per charge
}Step 5 — Orders service: orchestrates the flow
// orders-service/main.go
func PlaceOrder(w http.ResponseWriter, r *http.Request) {
// 1. Validate incoming user JWT
userClaims := r.Context().Value("user_claims").(*coresdk.Claims)
// 2. Mint a short-lived service token for downstream calls
svcToken, err := serviceauth.MintServiceToken(
"orders-service",
[]string{"inventory-service", "payments-service"},
serviceSigningKey,
)
if err != nil {
writeProblem(w, 500, "Internal Error", "Could not mint service token.")
return
}
svcHeader := "Bearer " + svcToken
// 3. Check inventory (orders → inventory, allowed by policy)
inv, err := callService("http://inventory-service:8082/inventory/check?product_id="+productID,
svcHeader, nil)
if err != nil || !inv["available"].(bool) {
writeProblem(w, 409, "Out of Stock", "Product is not available.")
return
}
// 4. Reserve stock
callService("http://inventory-service:8082/inventory/reserve",
svcHeader, map[string]any{"product_id": productID, "qty": 1})
// 5. Charge (orders → payments, allowed by policy with X-Order-ID)
orderID := uuid.New().String()
charge, err := callServiceWithHeader(
"http://payments-service:8083/charge",
svcHeader,
"X-Order-ID", orderID,
map[string]any{"amount_cents": req.AmountCents},
)
if err != nil {
writeProblem(w, 402, "Payment Failed", "Charge was declined.")
return
}
json.NewEncoder(w).Encode(map[string]any{
"order_id": orderID,
"status": "confirmed",
"charge": charge,
"user": userClaims.Subject,
})
}What Zero-Trust Prevents
| Attack | How it's blocked |
|---|---|
Compromised inventory-service calls payments-service | Rego policy: only orders-service can charge |
| Stolen service token reused after 5 min | JWT TTL: exp enforced by sidecar |
orders-service calls payments-service without order_id | Rego: input.order_id != "" required |
| External actor spoofs service identity | Service signing key never leaves that service's pod |
| Network-adjacent service bypasses auth | Every call validated — no implicit trust by subnet |
Comparison: Network Trust vs Zero-Trust
# Before: network trust (VPC-only)
# Any service on the subnet can call any other service
curl http://payments-service/charge -d '{"amount":50000}' # works! 😱
# After: zero-trust with CoreSDK
# Must carry a valid service JWT signed by orders-service's key
# Must pass Rego policy check
# Token expires in 5 minutes
curl http://payments-service/charge \
-H "Authorization: Bearer <orders-service-jwt>" \
-H "X-Order-ID: order-001" \
-d '{"amount":50000}' # only works from orders-serviceFull Source
→ examples/go/zero-trust-mesh/
cd examples/go/zero-trust-mesh
go run ./...
# Starts all three services on :8081, :8082, :8083SaaS Billing Webhooks with Tenant Scoping
Build SecurePay — a Stripe webhook processor that validates signatures, scopes events to tenants, gates feature access via feature flags, and emits structured audit trails. Python + FastAPI.
Multi-Tenant RAG System
Build AskAcme — a production-ready RAG API serving multiple enterprise customers from one deployment, with CoreSDK enforcing JWT auth, tenant isolation, RBAC, and PII gating.