mirror of
https://github.com/BagelHole/DevOps-Security-Agent-Skills.git
synced 2026-08-22 12:49:53 +02:00
V2
This commit is contained in:
@@ -11,76 +11,425 @@ metadata:
|
||||
|
||||
Implement "never trust, always verify" security model.
|
||||
|
||||
## When to Use This Skill
|
||||
|
||||
Use this skill when:
|
||||
- Replacing traditional perimeter-based VPN access models
|
||||
- Implementing BeyondCorp-style access to internal applications
|
||||
- Securing multi-cloud or hybrid-cloud environments
|
||||
- Enforcing identity-based access for every service interaction
|
||||
- Meeting compliance requirements for continuous verification and least privilege
|
||||
- Adopting micro-segmentation for Kubernetes or cloud workloads
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Identity provider (IdP) supporting OIDC/SAML (Okta, Azure AD, Google Workspace)
|
||||
- Service mesh or proxy infrastructure (Istio, Envoy, Cloudflare Access)
|
||||
- Device management/MDM solution for device posture checks
|
||||
- Kubernetes cluster for workload-level examples
|
||||
- Understanding of mTLS, RBAC, and network policies
|
||||
|
||||
## Core Principles
|
||||
|
||||
```yaml
|
||||
zero_trust_principles:
|
||||
- Verify explicitly (authenticate all access)
|
||||
- Least privilege access
|
||||
- Assume breach (micro-segmentation)
|
||||
- Continuous validation
|
||||
- End-to-end encryption
|
||||
verify_explicitly:
|
||||
description: "Authenticate and authorize every access request"
|
||||
controls:
|
||||
- Strong multi-factor authentication
|
||||
- Identity-aware proxy for all applications
|
||||
- Service-to-service mTLS
|
||||
- API token validation on every request
|
||||
|
||||
least_privilege:
|
||||
description: "Grant minimum access needed for the task"
|
||||
controls:
|
||||
- Just-in-time (JIT) access provisioning
|
||||
- Time-bounded access grants
|
||||
- Role-based access with fine-grained permissions
|
||||
- Regular access reviews and certification
|
||||
|
||||
assume_breach:
|
||||
description: "Design systems expecting compromise has occurred"
|
||||
controls:
|
||||
- Micro-segmentation between all services
|
||||
- End-to-end encryption (data in transit and at rest)
|
||||
- Continuous monitoring and anomaly detection
|
||||
- Blast radius containment
|
||||
```
|
||||
|
||||
## Identity-Based Access
|
||||
## BeyondCorp Implementation
|
||||
|
||||
### Cloudflare Access Configuration
|
||||
|
||||
```bash
|
||||
# Create an Access application for an internal service
|
||||
curl -X POST "https://api.cloudflare.com/client/v4/accounts/${ACCOUNT_ID}/access/apps" \
|
||||
-H "Authorization: Bearer ${CF_TOKEN}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"name": "Internal Dashboard",
|
||||
"domain": "dashboard.internal.example.com",
|
||||
"type": "self_hosted",
|
||||
"session_duration": "12h",
|
||||
"auto_redirect_to_identity": true,
|
||||
"allowed_idps": ["google-workspace-idp-id"]
|
||||
}'
|
||||
|
||||
# Create an Access policy
|
||||
curl -X POST "https://api.cloudflare.com/client/v4/accounts/${ACCOUNT_ID}/access/apps/${APP_ID}/policies" \
|
||||
-H "Authorization: Bearer ${CF_TOKEN}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"name": "Engineering team access",
|
||||
"decision": "allow",
|
||||
"include": [
|
||||
{ "group": { "id": "engineering-group-id" } }
|
||||
],
|
||||
"require": [
|
||||
{ "login_method": { "id": "google-workspace-idp-id" } }
|
||||
],
|
||||
"exclude": [
|
||||
{ "geo": { "country_code": "KP" } }
|
||||
]
|
||||
}'
|
||||
|
||||
# Create a device posture rule
|
||||
curl -X POST "https://api.cloudflare.com/client/v4/accounts/${ACCOUNT_ID}/devices/posture" \
|
||||
-H "Authorization: Bearer ${CF_TOKEN}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"name": "Require disk encryption",
|
||||
"type": "disk_encryption",
|
||||
"match": { "platform": "linux" },
|
||||
"schedule": "1h",
|
||||
"input": { "requireAll": true }
|
||||
}'
|
||||
```
|
||||
|
||||
### Cloudflare Access Terraform
|
||||
|
||||
```hcl
|
||||
resource "cloudflare_access_application" "dashboard" {
|
||||
account_id = var.cloudflare_account_id
|
||||
name = "Internal Dashboard"
|
||||
domain = "dashboard.internal.example.com"
|
||||
type = "self_hosted"
|
||||
session_duration = "12h"
|
||||
|
||||
auto_redirect_to_identity = true
|
||||
}
|
||||
|
||||
resource "cloudflare_access_policy" "engineering" {
|
||||
account_id = var.cloudflare_account_id
|
||||
application_id = cloudflare_access_application.dashboard.id
|
||||
name = "Engineering team"
|
||||
precedence = 1
|
||||
decision = "allow"
|
||||
|
||||
include {
|
||||
group = [cloudflare_access_group.engineering.id]
|
||||
}
|
||||
|
||||
require {
|
||||
login_method = [var.google_idp_id]
|
||||
}
|
||||
}
|
||||
|
||||
resource "cloudflare_access_group" "engineering" {
|
||||
account_id = var.cloudflare_account_id
|
||||
name = "Engineering"
|
||||
|
||||
include {
|
||||
email_domain = ["example.com"]
|
||||
}
|
||||
|
||||
require {
|
||||
group = ["engineering@example.com"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Identity-Aware Proxy with OAuth2 Proxy
|
||||
|
||||
```yaml
|
||||
# Service mesh mTLS
|
||||
# oauth2-proxy deployment for protecting internal services
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: oauth2-proxy
|
||||
namespace: auth
|
||||
spec:
|
||||
replicas: 2
|
||||
selector:
|
||||
matchLabels:
|
||||
app: oauth2-proxy
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: oauth2-proxy
|
||||
spec:
|
||||
containers:
|
||||
- name: oauth2-proxy
|
||||
image: quay.io/oauth2-proxy/oauth2-proxy:v7.6.0
|
||||
args:
|
||||
- --provider=oidc
|
||||
- --oidc-issuer-url=https://accounts.google.com
|
||||
- --client-id=$(CLIENT_ID)
|
||||
- --client-secret=$(CLIENT_SECRET)
|
||||
- --email-domain=example.com
|
||||
- --upstream=http://internal-service.default.svc:8080
|
||||
- --http-address=0.0.0.0:4180
|
||||
- --cookie-secret=$(COOKIE_SECRET)
|
||||
- --cookie-secure=true
|
||||
- --cookie-httponly=true
|
||||
- --cookie-samesite=lax
|
||||
- --set-xauthrequest=true
|
||||
- --pass-access-token=true
|
||||
- --skip-provider-button=true
|
||||
- --session-store-type=redis
|
||||
- --redis-connection-url=redis://redis.auth.svc:6379
|
||||
env:
|
||||
- name: CLIENT_ID
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: oauth2-proxy
|
||||
key: client-id
|
||||
- name: CLIENT_SECRET
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: oauth2-proxy
|
||||
key: client-secret
|
||||
- name: COOKIE_SECRET
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: oauth2-proxy
|
||||
key: cookie-secret
|
||||
ports:
|
||||
- containerPort: 4180
|
||||
---
|
||||
# Ingress routing through oauth2-proxy
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: internal-service
|
||||
annotations:
|
||||
nginx.ingress.kubernetes.io/auth-url: "https://auth.example.com/oauth2/auth"
|
||||
nginx.ingress.kubernetes.io/auth-signin: "https://auth.example.com/oauth2/start?rd=$scheme://$host$request_uri"
|
||||
nginx.ingress.kubernetes.io/auth-response-headers: "X-Auth-Request-User,X-Auth-Request-Email"
|
||||
spec:
|
||||
rules:
|
||||
- host: dashboard.example.com
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: internal-service
|
||||
port:
|
||||
number: 8080
|
||||
```
|
||||
|
||||
## Service Mesh mTLS (Istio)
|
||||
|
||||
```yaml
|
||||
# Enforce strict mTLS across the mesh
|
||||
apiVersion: security.istio.io/v1beta1
|
||||
kind: PeerAuthentication
|
||||
metadata:
|
||||
name: default
|
||||
namespace: istio-system
|
||||
spec:
|
||||
mtls:
|
||||
mode: STRICT
|
||||
---
|
||||
# Authorization policy: frontend can call backend
|
||||
apiVersion: security.istio.io/v1beta1
|
||||
kind: AuthorizationPolicy
|
||||
metadata:
|
||||
name: frontend-to-backend
|
||||
name: backend-access
|
||||
namespace: default
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app: backend
|
||||
action: ALLOW
|
||||
rules:
|
||||
- from:
|
||||
- source:
|
||||
principals: ["cluster.local/ns/default/sa/frontend"]
|
||||
- from:
|
||||
- source:
|
||||
principals: ["cluster.local/ns/default/sa/frontend"]
|
||||
to:
|
||||
- operation:
|
||||
methods: ["GET", "POST"]
|
||||
paths: ["/api/*"]
|
||||
---
|
||||
# Default deny all in namespace
|
||||
apiVersion: security.istio.io/v1beta1
|
||||
kind: AuthorizationPolicy
|
||||
metadata:
|
||||
name: deny-all
|
||||
namespace: production
|
||||
spec: {}
|
||||
```
|
||||
|
||||
## Network Segmentation
|
||||
## Micro-Segmentation with Kubernetes Network Policies
|
||||
|
||||
```yaml
|
||||
# Kubernetes Network Policy
|
||||
# Default deny all traffic in namespace
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: NetworkPolicy
|
||||
metadata:
|
||||
name: deny-all
|
||||
name: default-deny-all
|
||||
namespace: production
|
||||
spec:
|
||||
podSelector: {}
|
||||
policyTypes:
|
||||
- Ingress
|
||||
- Egress
|
||||
- Ingress
|
||||
- Egress
|
||||
---
|
||||
# Allow DNS resolution for all pods
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: NetworkPolicy
|
||||
metadata:
|
||||
name: allow-dns
|
||||
namespace: production
|
||||
spec:
|
||||
podSelector: {}
|
||||
policyTypes:
|
||||
- Egress
|
||||
egress:
|
||||
- to: []
|
||||
ports:
|
||||
- protocol: UDP
|
||||
port: 53
|
||||
- protocol: TCP
|
||||
port: 53
|
||||
---
|
||||
# Frontend: allow ingress from ingress controller, egress to backend
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: NetworkPolicy
|
||||
metadata:
|
||||
name: frontend-policy
|
||||
namespace: production
|
||||
spec:
|
||||
podSelector:
|
||||
matchLabels:
|
||||
app: frontend
|
||||
policyTypes:
|
||||
- Ingress
|
||||
- Egress
|
||||
ingress:
|
||||
- from:
|
||||
- namespaceSelector:
|
||||
matchLabels:
|
||||
name: ingress-nginx
|
||||
ports:
|
||||
- protocol: TCP
|
||||
port: 8080
|
||||
egress:
|
||||
- to:
|
||||
- podSelector:
|
||||
matchLabels:
|
||||
app: backend
|
||||
ports:
|
||||
- protocol: TCP
|
||||
port: 8080
|
||||
---
|
||||
# Database: allow from backend only, no egress
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: NetworkPolicy
|
||||
metadata:
|
||||
name: database-policy
|
||||
namespace: production
|
||||
spec:
|
||||
podSelector:
|
||||
matchLabels:
|
||||
app: database
|
||||
policyTypes:
|
||||
- Ingress
|
||||
- Egress
|
||||
ingress:
|
||||
- from:
|
||||
- podSelector:
|
||||
matchLabels:
|
||||
app: backend
|
||||
ports:
|
||||
- protocol: TCP
|
||||
port: 5432
|
||||
```
|
||||
|
||||
## OPA Policy for Access Decisions
|
||||
|
||||
```rego
|
||||
# policy.rego - Zero trust access decision
|
||||
package zerotrust.access
|
||||
|
||||
import rego.v1
|
||||
|
||||
default allow := false
|
||||
|
||||
allow if {
|
||||
identity_verified
|
||||
device_compliant
|
||||
authorized_for_resource
|
||||
risk_acceptable
|
||||
}
|
||||
|
||||
identity_verified if {
|
||||
input.identity.authenticated == true
|
||||
input.identity.mfa_verified == true
|
||||
time.now_ns() < input.identity.session_expires_ns
|
||||
}
|
||||
|
||||
device_compliant if {
|
||||
input.device.encryption_enabled == true
|
||||
input.device.os_updated == true
|
||||
input.device.firewall_enabled == true
|
||||
input.device.certificate_valid == true
|
||||
}
|
||||
|
||||
authorized_for_resource if {
|
||||
some role in input.identity.roles
|
||||
some permission in data.role_permissions[role]
|
||||
permission == input.resource.required_permission
|
||||
}
|
||||
|
||||
risk_acceptable if {
|
||||
input.risk.score < 70
|
||||
not input.risk.active_threat
|
||||
}
|
||||
|
||||
step_up_required if {
|
||||
input.risk.score >= 50
|
||||
input.risk.score < 70
|
||||
not input.identity.recent_mfa
|
||||
}
|
||||
```
|
||||
|
||||
## Implementation Steps
|
||||
|
||||
1. Identify sensitive resources
|
||||
2. Map access patterns
|
||||
3. Implement strong authentication
|
||||
4. Apply micro-segmentation
|
||||
5. Enable logging and monitoring
|
||||
6. Continuous verification
|
||||
1. **Inventory assets and data flows** - Map every application, service, and data store
|
||||
2. **Deploy identity provider** - Centralize authentication with SSO and MFA
|
||||
3. **Implement identity-aware proxy** - Route all access through authentication layer
|
||||
4. **Enable mTLS for service mesh** - Encrypt and authenticate all service communication
|
||||
5. **Apply network policies** - Default deny with explicit allow rules
|
||||
6. **Add device posture checks** - Verify device compliance before granting access
|
||||
7. **Deploy continuous monitoring** - Log and analyze all access decisions
|
||||
8. **Iterate and refine** - Review policies based on monitoring data
|
||||
|
||||
## Best Practices
|
||||
## Troubleshooting
|
||||
|
||||
- Identity-aware proxies
|
||||
- Device trust verification
|
||||
- Context-based access
|
||||
- Encrypted communications
|
||||
- Continuous monitoring
|
||||
| Problem | Cause | Solution |
|
||||
|---------|-------|----------|
|
||||
| Users cannot access internal apps | Identity provider misconfigured | Verify OIDC/SAML settings; check redirect URIs |
|
||||
| mTLS connections failing | Certificate expired or wrong CA | Check cert expiry with `istioctl proxy-config secret`; verify CA chain |
|
||||
| Network policy blocking legitimate traffic | Missing egress or ingress rule | Use `kubectl describe networkpolicy`; verify pod labels match selectors |
|
||||
| Device posture check fails | MDM agent not reporting | Verify device agent is running; check compliance dashboard |
|
||||
| OAuth2 proxy returns 403 | User email domain not in allow-list | Add domain to `--email-domain` flag or update group membership |
|
||||
|
||||
## Related Skills
|
||||
|
||||
- [service-mesh](../../../infrastructure/networking/service-mesh/) - mTLS implementation
|
||||
- [kubernetes-hardening](../../hardening/kubernetes-hardening/) - K8s security
|
||||
- [vpn-setup](../vpn-setup/) - Traditional VPN (contrast with zero trust)
|
||||
|
||||
Reference in New Issue
Block a user