zero-trust-patterns
ゼロトラストセキュリティパターン — マイクロサービス間のmTLS(Istio/SPIFFE)、SPIRE ワークロードアイデンティティ、OPA/Envoy認可、NetworkPolicyのデフォルト拒否設定、短命な認証情報、サービスメッシュセキュリティ、およびKubernetes RBACの強化に対応しています。
description の原文を見る
Zero-Trust security patterns — mTLS between microservices (Istio/SPIFFE), SPIRE workload identity, OPA/Envoy authorization, NetworkPolicy default-deny-all, short-lived credentials, service mesh security, and Kubernetes RBAC hardening.
SKILL.md 本文
ゼロトラスト パターン
有効化するタイミング
- Kubernetes またはクラウド環境でサービス間通信を設計する際
- マイクロサービス間の mTLS を実装する際
- ワークロード識別のため SPIFFE/SPIRE をセットアップする際
- Istio または Linkerd サービスメッシュを構成する際
- Kubernetes NetworkPolicy を作成する際
- 東西トラフィック セキュリティをレビューする際
- BeyondCorp 形式のアクセス制御を構築する際
- 既存クラスタ ネットワーク ポリシーで信頼境界のギャップを監査する際
コア原則 (NIST SP 800-207)
ゼロトラスト アーキテクチャは 4 つの柱で動作します:
- 信頼しない、常に検証する — プライベート ネットワーク内からのトラフィックであっても信頼しません。すべての接続には認証と認可が必要です。
- 明示的な検証 — すべてのリクエストで identity + device + context をチェックします (境界でのチェックだけではありません)。
- 最小権限アクセス — タスク完了に必要な最小限の権限であり、操作にスコープされています。
- 侵害を想定する — 攻撃者がすでにネットワーク内にいる場合、横展開を最小化するよう設計します。
SPIFFE/SPIRE によるサービス識別
SPIFFE (Secure Production Identity Framework for Everyone) は暗号化サービス識別のための標準です。
SVID 形式
spiffe://trust-domain/path/to/workload
# Example:
spiffe://prod.example.com/ns/payments/sa/checkout-service
SPIRE Server + Agent セットアップ
# spire-server.yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: spire-server
namespace: spire
spec:
replicas: 1
selector:
matchLabels:
app: spire-server
template:
spec:
containers:
- name: spire-server
image: ghcr.io/spiffe/spire-server:1.9.0
args:
- -config
- /run/spire/config/server.conf
volumeMounts:
- name: spire-config
mountPath: /run/spire/config
- name: spire-data
mountPath: /run/spire/data
# server.conf
server {
bind_address = "0.0.0.0"
bind_port = "8081"
trust_domain = "prod.example.com"
data_dir = "/run/spire/data"
log_level = "INFO"
# JWT SVIDs for service-to-service auth
jwt_issuer = "https://spire-server.spire.svc.cluster.local"
}
plugins {
DataStore "sql" {
plugin_data {
database_type = "sqlite3"
connection_string = "/run/spire/data/datastore.sqlite3"
}
}
NodeAttestor "k8s_psat" {
plugin_data {
clusters = {
"my-cluster" = {
service_account_allow_list = ["spire:spire-agent"]
}
}
}
}
KeyManager "memory" {
plugin_data {}
}
}
ワークロード登録
# Register a workload (checkout-service in payments namespace)
kubectl exec -n spire spire-server-0 -- \
/opt/spire/bin/spire-server entry create \
-spiffeID spiffe://prod.example.com/ns/payments/sa/checkout-service \
-parentID spiffe://prod.example.com/k8s-node/node1 \
-selector k8s:ns:payments \
-selector k8s:sa:checkout-service
ワークロード API から SVID を取得 (Go)
import (
"github.com/spiffe/go-spiffe/v2/workloadapi"
"github.com/spiffe/go-spiffe/v2/spiffetls/tlsconfig"
)
func newTLSConfig(ctx context.Context) (*tls.Config, error) {
source, err := workloadapi.NewX509Source(ctx)
if err != nil {
return nil, fmt.Errorf("create x509 source: %w", err)
}
// TLS config that automatically rotates certificates
return tlsconfig.MTLSClientConfig(source, source, tlsconfig.AuthorizeAny()), nil
}
Istio サービスメッシュ
インストール
# Install Istio with ambient mode (no sidecars, ztunnel at node level)
istioctl install --set profile=ambient
# Or with sidecar mode (traditional)
istioctl install --set profile=default
# Enable sidecar injection for a namespace
kubectl label namespace payments istio-injection=enabled
ネームスペース全体で厳密な mTLS を実行
# peer-authentication.yaml — enforce mTLS for entire namespace
apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
name: default
namespace: payments
spec:
mtls:
mode: STRICT # Reject all non-mTLS traffic. Never leave on PERMISSIVE in production.
認可ポリシー (デフォルト拒否 + 許可リスト)
# Step 1: Default deny ALL ingress in namespace
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
name: deny-all
namespace: payments
spec: {} # Empty spec = deny all
---
# Step 2: Explicitly allow checkout → payment-processor
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
name: allow-checkout-to-processor
namespace: payments
spec:
selector:
matchLabels:
app: payment-processor
rules:
- from:
- source:
principals:
- "cluster.local/ns/payments/sa/checkout-service" # SPIFFE-based identity
to:
- operation:
methods: ["POST"]
paths: ["/v1/payments"]
トラフィック管理
# VirtualService: retry + timeout
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: payment-processor
namespace: payments
spec:
hosts:
- payment-processor
http:
- retries:
attempts: 3
perTryTimeout: 2s
retryOn: gateway-error,connect-failure,retriable-4xx
timeout: 10s
route:
- destination:
host: payment-processor
可観測性 (ゼロコード)
# Prometheus metrics auto-collected: request_total, request_duration_ms, etc.
# Jaeger traces: every request gets a trace ID automatically
# View live traffic between services
istioctl proxy-config log payment-processor-pod --level debug
# Kiali topology graph
kubectl port-forward svc/kiali -n istio-system 20001:20001
Linkerd (軽量な代替案)
Istio より Linkerd を選択する場合: より小さなチーム、よりシンプルな運用モデル、より高いパフォーマンス、CRD の爆発的増加なし。
# Install Linkerd
linkerd install --crds | kubectl apply -f -
linkerd install | kubectl apply -f -
# Inject into deployment (or annotate namespace)
kubectl annotate namespace payments linkerd.io/inject=enabled
# Verify mTLS is working
linkerd viz tap deploy/checkout-service -n payments
# Traffic tap shows real-time encrypted traffic
linkerd viz edges deployment -n payments
# Linkerd Server (equivalent to Istio AuthorizationPolicy)
apiVersion: policy.linkerd.io/v1beta3
kind: Server
metadata:
name: payment-processor
namespace: payments
spec:
podSelector:
matchLabels:
app: payment-processor
port: 8080
proxyProtocol: HTTP/2
---
apiVersion: policy.linkerd.io/v1beta3
kind: MeshTLSAuthentication
metadata:
name: checkout-service-authn
namespace: payments
spec:
identities:
- "checkout-service.payments.serviceaccount.identity.linkerd.cluster.local"
---
apiVersion: policy.linkerd.io/v1beta3
kind: AuthorizationPolicy
metadata:
name: checkout-to-processor
namespace: payments
spec:
targetRef:
group: policy.linkerd.io
kind: Server
name: payment-processor
requiredAuthenticationRefs:
- name: checkout-service-authn
kind: MeshTLSAuthentication
group: policy.linkerd.io
Kubernetes NetworkPolicy
NetworkPolicy は Layer-3/4 ファイアウォールです — サービスメッシュを使用している場合でも常に使用してください。
パターン: デフォルト拒否、次に許可リスト
# 1. Default deny all ingress AND egress in namespace
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
namespace: payments
spec:
podSelector: {} # applies to ALL pods in namespace
policyTypes:
- Ingress
- Egress
# 2. Allow checkout → payment-processor on port 8080
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-checkout-to-processor
namespace: payments
spec:
podSelector:
matchLabels:
app: payment-processor
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app: checkout-service
ports:
- protocol: TCP
port: 8080
# 3. Allow DNS resolution (required for all pods)
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-dns-egress
namespace: payments
spec:
podSelector: {}
policyTypes:
- Egress
egress:
- ports:
- protocol: UDP
port: 53
- protocol: TCP
port: 53
Cilium — Layer-7 NetworkPolicy (HTTP 対応)
# Block all HTTP except POST /v1/payments
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: l7-payment-processor
namespace: payments
spec:
endpointSelector:
matchLabels:
app: payment-processor
ingress:
- fromEndpoints:
- matchLabels:
app: checkout-service
toPorts:
- ports:
- port: "8080"
protocol: TCP
rules:
http:
- method: POST
path: /v1/payments
東西トラフィック セキュリティ
シークレット: 環境変数に入れない
# WRONG: Secret as environment variable
env:
- name: DB_PASSWORD
value: "supersecret" # visible in pod spec, logs, ps aux
# WRONG: Even from k8s secret (still exposed as env var)
env:
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: db-secret
key: password
# CORRECT: Vault Agent Sidecar (secrets never touch etcd)
annotations:
vault.hashicorp.com/agent-inject: "true"
vault.hashicorp.com/agent-inject-secret-db: "secret/payments/db"
vault.hashicorp.com/role: "checkout-service"
vault.hashicorp.com/agent-inject-template-db: |
{{- with secret "secret/payments/db" -}}
DB_PASSWORD={{ .Data.data.password }}
{{- end }}
# CORRECT: External Secrets Operator (ESO) — syncs from AWS Secrets Manager / GCP Secret Manager
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: db-secret
namespace: payments
spec:
refreshInterval: 1h
secretStoreRef:
name: aws-secrets-manager
kind: ClusterSecretStore
target:
name: db-credentials
creationPolicy: Owner
data:
- secretKey: DB_PASSWORD
remoteRef:
key: prod/payments/db
property: password
Kubernetes Token Volume Projection (オーディエンスバインド、短命)
volumes:
- name: vault-token
projected:
sources:
- serviceAccountToken:
audience: vault # audience-bound, not usable for anything else
expirationSeconds: 3600 # short-lived
path: token
API のゼロトラスト — BeyondCorp パターン
User Request
│
▼
Identity-Aware Proxy (IAP / Verified Access)
│ checks:
│ - Identity (Google/OIDC token)
│ - Device trust (MDM-enrolled, cert-based)
│ - Context (IP reputation, time of day)
│
▼ [Allow or Deny]
Backend Service (no direct internet exposure)
AWS Verified Access の例
# Terraform: AWS Verified Access endpoint
resource "aws_verifiedaccess_endpoint" "internal_api" {
verified_access_group_id = aws_verifiedaccess_group.main.id
endpoint_type = "load-balancer"
attachment_type = "vpc"
load_balancer_options {
load_balancer_arn = aws_lb.internal.arn
port = 443
protocol = "https"
subnet_ids = var.private_subnet_ids
}
security_group_ids = [aws_security_group.verified_access.id]
}
resource "aws_verifiedaccess_trust_provider" "oidc" {
trust_provider_type = "user"
user_trust_provider_type = "oidc"
oidc_options {
issuer = "https://accounts.google.com"
authorization_endpoint = "https://accounts.google.com/o/oauth2/v2/auth"
token_endpoint = "https://oauth2.googleapis.com/token"
client_id = var.oauth_client_id
client_secret = var.oauth_client_secret
scope = "openid email"
}
}
ゼロトラスト チェックリスト
新しいサービスをデプロイする前に:
- サービス識別: SPIFFE SVID または audience バインディング付きの Kubernetes Service Account Token ですか?
- mTLS:
PeerAuthenticationが本番環境でSTRICTに設定されていますか (PERMISSIVEではなく)? - 認可:
deny-allポリシーが存在し、各ルートに明示的な許可リストがありますか? - NetworkPolicy: ネームスペースに
default-deny-allがあり、サービスごとの許可リストがありますか? - シークレット: Vault/ESO に保存されていますか — 環境変数や k8s Secret を env として マウントしていませんか?
- エグレス: 必要な外部エンドポイントのみがホワイトリストに登録されていますか?
- 東西: メッシュ内でサービス間に平文 HTTP がありませんか?
- 可観測性: mTLS テレメトリが Prometheus/Jaeger/Kiali に表示されていますか?
ツール リファレンス
| ツール | 用途 |
|---|---|
SPIRE | SPIFFE リファレンス実装 — ワークロード識別 |
Istio | フル機能のサービスメッシュ — mTLS、トラフィック管理、可観測性 |
Linkerd | 軽量なサービスメッシュ — よりシンプル、より高いパフォーマンス |
Cilium | eBPF ベースの CNI — Layer-7 NetworkPolicy、ID 認識ルーティング |
Vault | シークレット管理 — 動的シークレット、リース ベースの回転 |
External Secrets Operator | AWS/GCP/Azure から k8s へのシークレット同期 |
OPA/Gatekeeper | ポリシー実行 — ゼロトラスト ルールをアドミッション webhook として実行 |
cert-manager | mTLS 用の証明書回転自動化 |
関連するスキル
kubernetes-patterns— ベース k8s パターン (Deployment、Service、Ingress)devsecops-patterns— SAST/DAST/OPA ポリシー自動化auth-patterns— ユーザー認証 (JWT、OAuth2) — サービス識別とは異なりますsupply-chain-security— SBOM、SLSA、成果物信頼の Sigstore
ライセンス: MIT(寛容ライセンスのため全文を引用しています) · 原本リポジトリ
詳細情報
- 作者
- marvinrichter
- リポジトリ
- marvinrichter/clarc
- ライセンス
- MIT
- 最終更新
- 2026/4/27
Source: https://github.com/marvinrichter/clarc / ライセンス: MIT