@intentius/chant-lexicon-k8s 0.0.15 → 0.0.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/integrity.json +4 -3
- package/dist/manifest.json +1 -1
- package/dist/rules/latest-image-tag.ts +121 -0
- package/dist/rules/missing-resource-limits.ts +111 -0
- package/package.json +1 -1
- package/src/composites/agic-ingress.ts +0 -1
- package/src/composites/aks-external-dns-agent.ts +199 -0
- package/src/composites/azure-monitor-collector.ts +2 -2
- package/src/composites/composites.test.ts +359 -0
- package/src/composites/gce-ingress.ts +143 -0
- package/src/composites/gke-external-dns-agent.ts +175 -0
- package/src/composites/gke-fluent-bit-agent.ts +219 -0
- package/src/composites/gke-otel-collector.ts +229 -0
- package/src/composites/index.ts +12 -0
- package/src/index.ts +14 -0
- package/src/lint/rules/latest-image-tag.ts +121 -0
- package/src/lint/rules/missing-resource-limits.ts +111 -0
- package/src/lint/rules/rules.test.ts +192 -0
- package/src/plugin.ts +125 -208
- package/src/skills/chant-k8s-aks.md +146 -0
- package/src/skills/chant-k8s-gke.md +191 -0
- package/src/skills/kubernetes-patterns.md +183 -0
- package/src/skills/kubernetes-security.md +237 -0
- /package/{dist → src}/skills/chant-k8s-eks.md +0 -0
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
---
|
|
2
|
+
skill: kubernetes-security
|
|
3
|
+
description: Kubernetes pod security, image scanning, network policies, and secrets management
|
|
4
|
+
user-invocable: true
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Kubernetes Security Patterns
|
|
8
|
+
|
|
9
|
+
## Pod Security
|
|
10
|
+
|
|
11
|
+
### Security Context (container-level)
|
|
12
|
+
|
|
13
|
+
All Deployment-based composites accept `securityContext` for hardened containers:
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
import { WebApp } from "@intentius/chant-lexicon-k8s";
|
|
17
|
+
|
|
18
|
+
const { deployment, service } = WebApp({
|
|
19
|
+
name: "api",
|
|
20
|
+
image: "api:1.0",
|
|
21
|
+
port: 8080,
|
|
22
|
+
securityContext: {
|
|
23
|
+
runAsNonRoot: true,
|
|
24
|
+
runAsUser: 1000,
|
|
25
|
+
readOnlyRootFilesystem: true,
|
|
26
|
+
allowPrivilegeEscalation: false,
|
|
27
|
+
capabilities: { drop: ["ALL"] },
|
|
28
|
+
},
|
|
29
|
+
});
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### Pod Security Standards
|
|
33
|
+
|
|
34
|
+
Kubernetes enforces three levels via Pod Security Admission:
|
|
35
|
+
|
|
36
|
+
| Level | What it blocks | When to use |
|
|
37
|
+
|-------|---------------|-------------|
|
|
38
|
+
| `privileged` | Nothing | System namespaces only |
|
|
39
|
+
| `baseline` | hostNetwork, hostPID, privileged containers | Development |
|
|
40
|
+
| `restricted` | Non-root, no capabilities, read-only root FS | Production |
|
|
41
|
+
|
|
42
|
+
Apply to a namespace:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
kubectl label namespace prod pod-security.kubernetes.io/enforce=restricted
|
|
46
|
+
kubectl label namespace prod pod-security.kubernetes.io/warn=restricted
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Post-Synth Security Checks
|
|
50
|
+
|
|
51
|
+
chant catches security issues at build time:
|
|
52
|
+
|
|
53
|
+
| Check | What it detects |
|
|
54
|
+
|-------|----------------|
|
|
55
|
+
| WK8005 | Secrets exposed in environment variables |
|
|
56
|
+
| WK8006 | `latest` image tags (non-deterministic) |
|
|
57
|
+
| WK8041 | API keys or tokens in plain text |
|
|
58
|
+
| WK8042 | Hardcoded passwords in container env |
|
|
59
|
+
| WK8201 | Missing resource limits (CPU/memory) |
|
|
60
|
+
| WK8202 | Privileged containers |
|
|
61
|
+
| WK8203 | Host namespace sharing (hostPID/hostNetwork) |
|
|
62
|
+
| WK8204 | Writable root filesystem |
|
|
63
|
+
| WK8205 | Containers running as root |
|
|
64
|
+
|
|
65
|
+
## Image Security
|
|
66
|
+
|
|
67
|
+
### Pin Image Digests
|
|
68
|
+
|
|
69
|
+
Use image digests instead of tags for immutability:
|
|
70
|
+
|
|
71
|
+
```typescript
|
|
72
|
+
const { deployment } = WebApp({
|
|
73
|
+
name: "api",
|
|
74
|
+
image: "api@sha256:abc123def456...",
|
|
75
|
+
port: 8080,
|
|
76
|
+
});
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Private Registry with imagePullSecrets
|
|
80
|
+
|
|
81
|
+
```typescript
|
|
82
|
+
import { Deployment, Secret } from "@intentius/chant-lexicon-k8s";
|
|
83
|
+
|
|
84
|
+
export const registryCreds = new Secret({
|
|
85
|
+
metadata: { name: "registry-creds" },
|
|
86
|
+
type: "kubernetes.io/dockerconfigjson",
|
|
87
|
+
data: { ".dockerconfigjson": "${DOCKER_CONFIG_JSON}" },
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
export const deployment = new Deployment({
|
|
91
|
+
spec: {
|
|
92
|
+
template: {
|
|
93
|
+
spec: {
|
|
94
|
+
imagePullSecrets: [{ name: "registry-creds" }],
|
|
95
|
+
containers: [{ name: "app", image: "private.registry.io/app:1.0" }],
|
|
96
|
+
},
|
|
97
|
+
},
|
|
98
|
+
},
|
|
99
|
+
});
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### Image Policy
|
|
103
|
+
|
|
104
|
+
Block unsigned or unscanned images with admission controllers:
|
|
105
|
+
- **Kyverno**: policy-based, Kubernetes-native
|
|
106
|
+
- **OPA/Gatekeeper**: Rego-based policies
|
|
107
|
+
- **Sigstore/Cosign**: image signature verification
|
|
108
|
+
|
|
109
|
+
## Network Policies
|
|
110
|
+
|
|
111
|
+
### Default Deny All
|
|
112
|
+
|
|
113
|
+
Start with deny-all and add explicit allows:
|
|
114
|
+
|
|
115
|
+
```typescript
|
|
116
|
+
import { NamespaceEnv } from "@intentius/chant-lexicon-k8s";
|
|
117
|
+
|
|
118
|
+
const ns = NamespaceEnv({
|
|
119
|
+
name: "prod",
|
|
120
|
+
defaultDenyIngress: true,
|
|
121
|
+
defaultDenyEgress: true,
|
|
122
|
+
});
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### Allow Specific Traffic
|
|
126
|
+
|
|
127
|
+
```typescript
|
|
128
|
+
import { NetworkIsolatedApp } from "@intentius/chant-lexicon-k8s";
|
|
129
|
+
|
|
130
|
+
const app = NetworkIsolatedApp({
|
|
131
|
+
name: "api",
|
|
132
|
+
image: "api:1.0",
|
|
133
|
+
port: 8080,
|
|
134
|
+
namespace: "prod",
|
|
135
|
+
allowIngressFrom: [
|
|
136
|
+
{ podSelector: { "app.kubernetes.io/name": "gateway" } },
|
|
137
|
+
{ namespaceSelector: { "kubernetes.io/metadata.name": "monitoring" } },
|
|
138
|
+
],
|
|
139
|
+
allowEgressTo: [
|
|
140
|
+
{ podSelector: { "app.kubernetes.io/name": "postgres" }, ports: [{ port: 5432 }] },
|
|
141
|
+
{ ipBlock: { cidr: "10.0.0.0/8" }, ports: [{ port: 443 }] },
|
|
142
|
+
],
|
|
143
|
+
});
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### Allow DNS Egress
|
|
147
|
+
|
|
148
|
+
Most pods need DNS. Always allow egress to kube-dns:
|
|
149
|
+
|
|
150
|
+
```yaml
|
|
151
|
+
egress:
|
|
152
|
+
- to:
|
|
153
|
+
- namespaceSelector:
|
|
154
|
+
matchLabels:
|
|
155
|
+
kubernetes.io/metadata.name: kube-system
|
|
156
|
+
ports:
|
|
157
|
+
- port: 53
|
|
158
|
+
protocol: UDP
|
|
159
|
+
- port: 53
|
|
160
|
+
protocol: TCP
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
## Secrets Management
|
|
164
|
+
|
|
165
|
+
### External Secrets Operator
|
|
166
|
+
|
|
167
|
+
Sync secrets from external providers (AWS Secrets Manager, Vault, etc.):
|
|
168
|
+
|
|
169
|
+
```yaml
|
|
170
|
+
apiVersion: external-secrets.io/v1beta1
|
|
171
|
+
kind: ExternalSecret
|
|
172
|
+
metadata:
|
|
173
|
+
name: app-secrets
|
|
174
|
+
spec:
|
|
175
|
+
refreshInterval: 1h
|
|
176
|
+
secretStoreRef:
|
|
177
|
+
name: aws-secrets
|
|
178
|
+
kind: ClusterSecretStore
|
|
179
|
+
target:
|
|
180
|
+
name: app-secrets
|
|
181
|
+
data:
|
|
182
|
+
- secretKey: db-password
|
|
183
|
+
remoteRef:
|
|
184
|
+
key: prod/db-password
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
### Sealed Secrets
|
|
188
|
+
|
|
189
|
+
Encrypt secrets for safe storage in Git:
|
|
190
|
+
|
|
191
|
+
```bash
|
|
192
|
+
kubeseal --format yaml < secret.yaml > sealed-secret.yaml
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
### Secret Rotation
|
|
196
|
+
|
|
197
|
+
Use the External Secrets Operator `refreshInterval` or Reloader to restart pods on secret changes:
|
|
198
|
+
|
|
199
|
+
```bash
|
|
200
|
+
kubectl annotate deployment api reloader.stakater.com/auto="true"
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
## RBAC Hardening
|
|
204
|
+
|
|
205
|
+
### Audit RBAC Permissions
|
|
206
|
+
|
|
207
|
+
```bash
|
|
208
|
+
# Check what a ServiceAccount can do
|
|
209
|
+
kubectl auth can-i --list --as=system:serviceaccount:prod:api-sa
|
|
210
|
+
|
|
211
|
+
# Check specific permission
|
|
212
|
+
kubectl auth can-i create pods --as=system:serviceaccount:prod:api-sa
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
### Avoid Cluster-Admin
|
|
216
|
+
|
|
217
|
+
Never bind `cluster-admin` to application ServiceAccounts. Use namespace-scoped Roles with minimal verbs:
|
|
218
|
+
|
|
219
|
+
```typescript
|
|
220
|
+
import { WorkerPool } from "@intentius/chant-lexicon-k8s";
|
|
221
|
+
|
|
222
|
+
const worker = WorkerPool({
|
|
223
|
+
name: "processor",
|
|
224
|
+
image: "processor:1.0",
|
|
225
|
+
rbacRules: [
|
|
226
|
+
{ apiGroups: [""], resources: ["configmaps"], verbs: ["get"] },
|
|
227
|
+
],
|
|
228
|
+
});
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
### Service Account Token Projection
|
|
232
|
+
|
|
233
|
+
Disable auto-mounting of SA tokens when not needed:
|
|
234
|
+
|
|
235
|
+
```yaml
|
|
236
|
+
automountServiceAccountToken: false
|
|
237
|
+
```
|
|
File without changes
|