train-k8s-container-mitre 2.1.0 → 2.2.0
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.
- checksums.yaml +4 -4
- data/.release-please-manifest.json +1 -1
- data/ARCHITECTURE.md +546 -0
- data/BEADS-BOARD.md +104 -0
- data/CHANGELOG.md +22 -0
- data/CONTRIBUTING.md +125 -36
- data/DEVELOPMENT.md +32 -34
- data/README.md +16 -4
- data/data/train-k8s-container-scan-results v1.31.json +1 -0
- data/data/train-k8s-container-scan-results v2.json +1 -0
- data/lib/train-k8s-container/version.rb +1 -1
- metadata +6 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 8ef90f67ca076b6179acb73cdf55cbccc46c574353fa6840a9383bf619a12b03
|
|
4
|
+
data.tar.gz: 45a05c0359f9b79f1c80dca78db72bb902b15ba4cf36fa50cdd644d2ac4ca394
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 5ffda135781a1949734ff7e604b8561569687a5787d48508c429df4bb247b99f9de69336aa44b5da243fe63b0838f1cee0a3bebffdb2beb3aeee88f8a6bdbd74
|
|
7
|
+
data.tar.gz: 237d9179ecdd74b3826de69299b07dfdcf6d001c5684f229344613def553832b1453e51ce418f73997c724ac9bf01ec2b8183476a311049340139db62fe9fa5c
|
data/ARCHITECTURE.md
ADDED
|
@@ -0,0 +1,546 @@
|
|
|
1
|
+
# Architecture Overview
|
|
2
|
+
|
|
3
|
+
This document provides a technical overview of the train-k8s-container plugin architecture.
|
|
4
|
+
|
|
5
|
+
## High-Level Architecture
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
┌─────────────────────────────────────────────────────────────────────────────┐
|
|
9
|
+
│ InSpec / Cinc Auditor │
|
|
10
|
+
│ │
|
|
11
|
+
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
|
|
12
|
+
│ │ Profile │───▶│ Resources │───▶│ Train │───▶│ Transport │ │
|
|
13
|
+
│ │ Controls │ │ (file, user │ │ Framework │ │ Plugin │ │
|
|
14
|
+
│ └─────────────┘ │ package..) │ └─────────────┘ └──────┬──────┘ │
|
|
15
|
+
└──────────────────────────────────────────────────────────────────┼─────────┘
|
|
16
|
+
│
|
|
17
|
+
┌────────────────────────────────────┘
|
|
18
|
+
▼
|
|
19
|
+
┌─────────────────────────────────────────────────────────────────────────────┐
|
|
20
|
+
│ train-k8s-container Plugin │
|
|
21
|
+
│ │
|
|
22
|
+
│ ┌───────────────────────────────────────────────────────────────────────┐ │
|
|
23
|
+
│ │ Connection │ │
|
|
24
|
+
│ │ • URI parsing (k8s-container://namespace/pod/container) │ │
|
|
25
|
+
│ │ • Parameter validation │ │
|
|
26
|
+
│ │ • File operations via Train::File::Remote::Linux │ │
|
|
27
|
+
│ └───────────────────────────────────┬───────────────────────────────────┘ │
|
|
28
|
+
│ │ │
|
|
29
|
+
│ ┌────────────────────────────┼────────────────────────────┐ │
|
|
30
|
+
│ ▼ ▼ ▼ │
|
|
31
|
+
│ ┌─────────────────┐ ┌─────────────────────┐ ┌─────────────────┐ │
|
|
32
|
+
│ │ ShellDetector │ │ KubectlExecClient │ │ Platform │ │
|
|
33
|
+
│ │ │ │ │ │ │ │
|
|
34
|
+
│ │ • OS detection │ │ • Command execution │ │ • Detect+Context│ │
|
|
35
|
+
│ │ • Shell probing │ │ • PTY sessions │ │ • OS families │ │
|
|
36
|
+
│ │ • Linux family │ │ • Retry handling │ │ • Kubernetes │ │
|
|
37
|
+
│ └────────┬────────┘ └──────────┬──────────┘ │ context │ │
|
|
38
|
+
│ │ │ └─────────────────┘ │
|
|
39
|
+
│ │ ┌──────────┴──────────┐ │
|
|
40
|
+
│ │ ▼ ▼ │
|
|
41
|
+
│ │ ┌─────────────────┐ ┌─────────────────┐ │
|
|
42
|
+
│ │ │ SessionManager │ │ ResultProcessor │ │
|
|
43
|
+
│ │ │ (Singleton) │ │ │ │
|
|
44
|
+
│ │ │ │ │ • ANSI cleanup │ │
|
|
45
|
+
│ │ │ • Connection │ │ • Exit codes │ │
|
|
46
|
+
│ │ │ pooling │ │ • Error detect │ │
|
|
47
|
+
│ │ │ • Thread-safe │ └─────────────────┘ │
|
|
48
|
+
│ │ └────────┬────────┘ │
|
|
49
|
+
│ │ │ │
|
|
50
|
+
│ │ ▼ │
|
|
51
|
+
│ │ ┌─────────────────┐ │
|
|
52
|
+
│ │ │ PtySession │ │
|
|
53
|
+
│ │ │ │ │
|
|
54
|
+
│ │ │ • Persistent │ │
|
|
55
|
+
│ │ │ shell session │ │
|
|
56
|
+
│ │ │ • Command queue │ │
|
|
57
|
+
│ │ └─────────────────┘ │
|
|
58
|
+
│ │ │
|
|
59
|
+
│ ┌────────┴──────────────────────────────────────────────────────────┐ │
|
|
60
|
+
│ │ Support Modules │ │
|
|
61
|
+
│ │ │ │
|
|
62
|
+
│ │ ┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐ │ │
|
|
63
|
+
│ │ │ KubectlCommand │ │ KubernetesName │ │ AnsiSanitizer │ │ │
|
|
64
|
+
│ │ │ Builder │ │ Validator │ │ │ │ │
|
|
65
|
+
│ │ │ │ │ │ │ • CVE-2021-25743 │ │ │
|
|
66
|
+
│ │ │ • Shell escaping │ │ • RFC 1123 │ │ mitigation │ │ │
|
|
67
|
+
│ │ │ • Windows/Unix │ │ • Injection │ │ • ANSI stripping │ │ │
|
|
68
|
+
│ │ └──────────────────┘ │ prevention │ └──────────────────┘ │ │
|
|
69
|
+
│ │ └──────────────────┘ │ │
|
|
70
|
+
│ └────────────────────────────────────────────────────────────────────┘ │
|
|
71
|
+
└─────────────────────────────────────────────────────────────────────────────┘
|
|
72
|
+
│
|
|
73
|
+
▼
|
|
74
|
+
┌─────────────────────────────────────────────────────────────────────────────┐
|
|
75
|
+
│ kubectl exec │
|
|
76
|
+
│ │
|
|
77
|
+
│ kubectl exec --stdin <pod> -n <namespace> -c <container> -- <cmd> │
|
|
78
|
+
└─────────────────────────────────────────────────────────────────────────────┘
|
|
79
|
+
│
|
|
80
|
+
▼
|
|
81
|
+
┌─────────────────────────────────────────────────────────────────────────────┐
|
|
82
|
+
│ Kubernetes Cluster │
|
|
83
|
+
│ │
|
|
84
|
+
│ ┌─────────────────────────────────────────────────────────────────┐ │
|
|
85
|
+
│ │ Target Pod │ │
|
|
86
|
+
│ │ ┌─────────────────────────────────────────────────────────┐ │ │
|
|
87
|
+
│ │ │ Target Container │ │ │
|
|
88
|
+
│ │ │ │ │ │
|
|
89
|
+
│ │ │ /bin/bash, /bin/sh, /bin/ash ──or── distroless │ │ │
|
|
90
|
+
│ │ │ │ │ │
|
|
91
|
+
│ │ └─────────────────────────────────────────────────────────┘ │ │
|
|
92
|
+
│ └─────────────────────────────────────────────────────────────────┘ │
|
|
93
|
+
└─────────────────────────────────────────────────────────────────────────────┘
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Command Execution Flow
|
|
97
|
+
|
|
98
|
+
```
|
|
99
|
+
┌──────────────────────────────────────────────────────────────────────────────┐
|
|
100
|
+
│ Command Execution Flow │
|
|
101
|
+
└──────────────────────────────────────────────────────────────────────────────┘
|
|
102
|
+
|
|
103
|
+
InSpec Control Plugin Components kubectl
|
|
104
|
+
───────────── ───────────────── ───────
|
|
105
|
+
│
|
|
106
|
+
│ command('whoami')
|
|
107
|
+
▼
|
|
108
|
+
┌─────────┐
|
|
109
|
+
│ Control │
|
|
110
|
+
└────┬────┘
|
|
111
|
+
│
|
|
112
|
+
▼
|
|
113
|
+
┌─────────────────┐
|
|
114
|
+
│ Connection │ ─── parse URI, validate params
|
|
115
|
+
└────────┬────────┘
|
|
116
|
+
│
|
|
117
|
+
▼
|
|
118
|
+
┌─────────────────┐
|
|
119
|
+
│ KubectlExec │
|
|
120
|
+
│ Client │
|
|
121
|
+
└────────┬────────┘
|
|
122
|
+
│
|
|
123
|
+
│ Check for existing session
|
|
124
|
+
▼
|
|
125
|
+
┌─────────────────┐ ┌─────────────────┐
|
|
126
|
+
│ SessionManager │────▶│ PtySession │ (if pooled session exists)
|
|
127
|
+
│ (Singleton) │ │ (persistent) │
|
|
128
|
+
└────────┬────────┘ └────────┬────────┘
|
|
129
|
+
│ │
|
|
130
|
+
│ No session? │ Has session?
|
|
131
|
+
│ Create new │ Reuse
|
|
132
|
+
▼ ▼
|
|
133
|
+
┌─────────────────┐ ┌─────────────────┐
|
|
134
|
+
│ KubectlCommand │ │ Send command │
|
|
135
|
+
│ Builder │ │ via PTY pipe │
|
|
136
|
+
└────────┬────────┘ └────────┬────────┘
|
|
137
|
+
│ │
|
|
138
|
+
▼ ▼
|
|
139
|
+
┌─────────────────┐ ┌─────────────────┐
|
|
140
|
+
│ Mixlib:: │ │ Read response │
|
|
141
|
+
│ ShellOut │ │ until marker │
|
|
142
|
+
└────────┬────────┘ └────────┬────────┘
|
|
143
|
+
│ │
|
|
144
|
+
▼ ▼
|
|
145
|
+
┌───────────────────────────────┐
|
|
146
|
+
│ kubectl exec │ ───▶ Container
|
|
147
|
+
└───────────────┬───────────────┘
|
|
148
|
+
│
|
|
149
|
+
▼
|
|
150
|
+
┌─────────────────┐
|
|
151
|
+
│ ResultProcessor │
|
|
152
|
+
│ │
|
|
153
|
+
│ • Parse exit │
|
|
154
|
+
│ code │
|
|
155
|
+
│ • Strip ANSI │
|
|
156
|
+
│ • Detect errors │
|
|
157
|
+
└────────┬────────┘
|
|
158
|
+
│
|
|
159
|
+
▼
|
|
160
|
+
┌─────────────────┐
|
|
161
|
+
│ RetryHandler │ ─── Retry on transient errors
|
|
162
|
+
│ │ (exponential backoff)
|
|
163
|
+
└────────┬────────┘
|
|
164
|
+
│
|
|
165
|
+
▼
|
|
166
|
+
Train::Extras::CommandResult
|
|
167
|
+
(stdout, stderr, exit_code)
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
## File Structure
|
|
171
|
+
|
|
172
|
+
```
|
|
173
|
+
lib/train-k8s-container/
|
|
174
|
+
├── transport.rb # Train plugin registration
|
|
175
|
+
├── connection.rb # Main connection class
|
|
176
|
+
├── kubectl_exec_client.rb # Command execution engine
|
|
177
|
+
├── platform.rb # OS detection (Detect+Context)
|
|
178
|
+
├── version.rb # Version constant
|
|
179
|
+
│
|
|
180
|
+
├── session_manager.rb # Connection pool (Singleton)
|
|
181
|
+
├── pty_session.rb # Persistent PTY shell session
|
|
182
|
+
│
|
|
183
|
+
├── shell_detector.rb # Shell/OS detection
|
|
184
|
+
├── kubectl_command_builder.rb # Command string building
|
|
185
|
+
├── result_processor.rb # Output parsing & validation
|
|
186
|
+
├── retry_handler.rb # Exponential backoff retry
|
|
187
|
+
│
|
|
188
|
+
├── ansi_sanitizer.rb # ANSI escape removal (CVE fix)
|
|
189
|
+
├── kubernetes_name_validator.rb # RFC 1123 validation
|
|
190
|
+
└── errors.rb # Custom error classes
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
## Component Details
|
|
194
|
+
|
|
195
|
+
### Core Components
|
|
196
|
+
|
|
197
|
+
#### Transport (`transport.rb`)
|
|
198
|
+
|
|
199
|
+
Registers the `k8s-container` transport with Train:
|
|
200
|
+
|
|
201
|
+
```ruby
|
|
202
|
+
module TrainPlugins::K8sContainer
|
|
203
|
+
class Transport < Train.plugin(1)
|
|
204
|
+
name 'k8s-container'
|
|
205
|
+
# ...
|
|
206
|
+
end
|
|
207
|
+
end
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
Connection options:
|
|
211
|
+
- `pod` - Target pod name (required)
|
|
212
|
+
- `container_name` - Target container (required)
|
|
213
|
+
- `namespace` - Kubernetes namespace (default: `default`)
|
|
214
|
+
- `kubeconfig` - Path to kubeconfig file
|
|
215
|
+
|
|
216
|
+
#### Connection (`connection.rb`)
|
|
217
|
+
|
|
218
|
+
Main connection handler:
|
|
219
|
+
- Parses URI format: `k8s-container://<namespace>/<pod>/<container>`
|
|
220
|
+
- Validates required parameters (pod, container)
|
|
221
|
+
- Provides `run_command()` for command execution
|
|
222
|
+
- Provides file access via `Train::File::Remote::Linux`
|
|
223
|
+
|
|
224
|
+
#### KubectlExecClient (`kubectl_exec_client.rb`)
|
|
225
|
+
|
|
226
|
+
Command execution engine:
|
|
227
|
+
- Detects available shell via `ShellDetector`
|
|
228
|
+
- Routes commands through `SessionManager` for pooling
|
|
229
|
+
- Falls back to direct execution for distroless containers
|
|
230
|
+
- Integrates `RetryHandler` for transient errors
|
|
231
|
+
|
|
232
|
+
#### Platform (`platform.rb`)
|
|
233
|
+
|
|
234
|
+
OS detection using **Detect+Context** pattern:
|
|
235
|
+
|
|
236
|
+
```ruby
|
|
237
|
+
def platform
|
|
238
|
+
# 1. Use Train's scanner to detect actual OS
|
|
239
|
+
@platform = Train::Platforms::Detect.scan(self)
|
|
240
|
+
|
|
241
|
+
# 2. Add Kubernetes context families
|
|
242
|
+
add_k8s_families(@platform)
|
|
243
|
+
|
|
244
|
+
@platform
|
|
245
|
+
end
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
Result: `os.linux?` returns `true` while `platform.families` includes `kubernetes`, `container`.
|
|
249
|
+
|
|
250
|
+
### Session Management (Connection Pooling)
|
|
251
|
+
|
|
252
|
+
#### SessionManager (`session_manager.rb`)
|
|
253
|
+
|
|
254
|
+
Thread-safe singleton that pools PTY sessions:
|
|
255
|
+
|
|
256
|
+
```ruby
|
|
257
|
+
class SessionManager
|
|
258
|
+
include Singleton
|
|
259
|
+
|
|
260
|
+
# One session per namespace/pod/container
|
|
261
|
+
def get_session(session_key, kubectl_cmd:, shell:, timeout:, logger:)
|
|
262
|
+
@mutex.synchronize do
|
|
263
|
+
unless @sessions[session_key]&.healthy?
|
|
264
|
+
@sessions[session_key] = PtySession.new(...)
|
|
265
|
+
@sessions[session_key].connect
|
|
266
|
+
end
|
|
267
|
+
@sessions[session_key]
|
|
268
|
+
end
|
|
269
|
+
end
|
|
270
|
+
end
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
Benefits:
|
|
274
|
+
- Reuses kubectl exec connections across multiple commands
|
|
275
|
+
- Significantly faster for profiles with many controls
|
|
276
|
+
- Automatic cleanup on session failure or process exit
|
|
277
|
+
|
|
278
|
+
#### PtySession (`pty_session.rb`)
|
|
279
|
+
|
|
280
|
+
Persistent PTY-based shell session:
|
|
281
|
+
|
|
282
|
+
```ruby
|
|
283
|
+
# Instead of spawning kubectl for each command:
|
|
284
|
+
kubectl exec pod -c container -- /bin/sh -c "command1"
|
|
285
|
+
kubectl exec pod -c container -- /bin/sh -c "command2"
|
|
286
|
+
kubectl exec pod -c container -- /bin/sh -c "command3"
|
|
287
|
+
|
|
288
|
+
# Maintains one persistent session:
|
|
289
|
+
kubectl exec pod -c container -- /bin/bash
|
|
290
|
+
> command1
|
|
291
|
+
> command2
|
|
292
|
+
> command3
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
Features:
|
|
296
|
+
- Uses Ruby's `PTY.spawn` for interactive session
|
|
297
|
+
- Sends commands with exit code markers
|
|
298
|
+
- Parses output and extracts real exit codes
|
|
299
|
+
- Health checking via process status
|
|
300
|
+
|
|
301
|
+
### Shell & OS Detection
|
|
302
|
+
|
|
303
|
+
#### ShellDetector (`shell_detector.rb`)
|
|
304
|
+
|
|
305
|
+
Detects available shell in container:
|
|
306
|
+
|
|
307
|
+
```
|
|
308
|
+
Detection Order (Unix):
|
|
309
|
+
1. /bin/bash - Ubuntu, Debian, RHEL
|
|
310
|
+
2. /bin/sh - POSIX standard
|
|
311
|
+
3. /bin/ash - Alpine, BusyBox
|
|
312
|
+
4. /bin/zsh - Less common
|
|
313
|
+
|
|
314
|
+
Detection Order (Windows):
|
|
315
|
+
1. cmd.exe - Command prompt
|
|
316
|
+
2. powershell.exe - PowerShell 5.1
|
|
317
|
+
3. pwsh.exe - PowerShell Core
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
Also detects Linux distribution family from `/etc/os-release`:
|
|
321
|
+
- Debian family: ubuntu, debian, linuxmint, kali
|
|
322
|
+
- RedHat family: rhel, centos, fedora, rocky, almalinux
|
|
323
|
+
- Alpine, Arch, SUSE, Gentoo
|
|
324
|
+
|
|
325
|
+
### Command Building & Processing
|
|
326
|
+
|
|
327
|
+
#### KubectlCommandBuilder (`kubectl_command_builder.rb`)
|
|
328
|
+
|
|
329
|
+
Builds properly escaped kubectl commands:
|
|
330
|
+
|
|
331
|
+
```ruby
|
|
332
|
+
builder = KubectlCommandBuilder.new(
|
|
333
|
+
kubectl_path: '/usr/bin/kubectl',
|
|
334
|
+
pod: 'my-pod',
|
|
335
|
+
namespace: 'default',
|
|
336
|
+
container_name: 'app'
|
|
337
|
+
)
|
|
338
|
+
|
|
339
|
+
# Unix shell
|
|
340
|
+
builder.with_shell('/bin/bash', 'cat /etc/passwd')
|
|
341
|
+
# => "kubectl exec --stdin my-pod -n default -c app -- /bin/bash -c 'cat /etc/passwd'"
|
|
342
|
+
|
|
343
|
+
# Windows shell
|
|
344
|
+
builder.with_windows_shell('cmd.exe', 'dir')
|
|
345
|
+
# => "kubectl exec --stdin my-pod -n default -c app -- cmd.exe /c 'dir'"
|
|
346
|
+
|
|
347
|
+
# Direct binary (distroless)
|
|
348
|
+
builder.direct_binary('cat /etc/os-release')
|
|
349
|
+
# => "kubectl exec --stdin my-pod -n default -c app -- cat /etc/os-release"
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
#### ResultProcessor (`result_processor.rb`)
|
|
353
|
+
|
|
354
|
+
Processes command output:
|
|
355
|
+
|
|
356
|
+
1. **Validation**: Detects connection errors and silent failures
|
|
357
|
+
2. **Sanitization**: Strips ANSI sequences, normalizes line endings
|
|
358
|
+
3. **Exit code parsing**: Extracts real exit code from kubectl messages
|
|
359
|
+
|
|
360
|
+
Connection error patterns detected:
|
|
361
|
+
- `error dialing backend`
|
|
362
|
+
- `connection refused`
|
|
363
|
+
- `pods "name" not found`
|
|
364
|
+
- `Error from server`
|
|
365
|
+
|
|
366
|
+
### Security Components
|
|
367
|
+
|
|
368
|
+
#### AnsiSanitizer (`ansi_sanitizer.rb`)
|
|
369
|
+
|
|
370
|
+
Addresses **CVE-2021-25743** (terminal escape sequence injection):
|
|
371
|
+
|
|
372
|
+
```ruby
|
|
373
|
+
module AnsiSanitizer
|
|
374
|
+
CSI_REGEX = /\e\[([;\d]+)?[A-Za-z]/ # Colors, cursor
|
|
375
|
+
OSC_REGEX = /\e\][^\a]*\a/ # Terminal title
|
|
376
|
+
CURSOR_REGEX = /\e\[A|\e\[C|\e\[K/ # Movement
|
|
377
|
+
|
|
378
|
+
def self.sanitize(text)
|
|
379
|
+
# Remove all ANSI escape sequences
|
|
380
|
+
end
|
|
381
|
+
end
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
#### KubernetesNameValidator (`kubernetes_name_validator.rb`)
|
|
385
|
+
|
|
386
|
+
Validates names per **RFC 1123** DNS subdomain rules:
|
|
387
|
+
|
|
388
|
+
```ruby
|
|
389
|
+
VALID_NAME_REGEX = /\A[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)?\z/
|
|
390
|
+
MAX_NAME_LENGTH = 253
|
|
391
|
+
|
|
392
|
+
def self.validate!(name, resource_type:)
|
|
393
|
+
# Prevents command injection via malformed names
|
|
394
|
+
end
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
### Error Handling
|
|
398
|
+
|
|
399
|
+
#### RetryHandler (`retry_handler.rb`)
|
|
400
|
+
|
|
401
|
+
Exponential backoff for transient errors:
|
|
402
|
+
|
|
403
|
+
```ruby
|
|
404
|
+
RetryHandler.with_retry(max_retries: 3, logger: logger) do
|
|
405
|
+
# Network operation
|
|
406
|
+
end
|
|
407
|
+
|
|
408
|
+
# Retry delays: 1s, 2s, 4s (exponential backoff)
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
Retryable errors:
|
|
412
|
+
- `NetworkError` - Silent failures, timeouts
|
|
413
|
+
- `ConnectionError` - kubectl connection issues
|
|
414
|
+
|
|
415
|
+
#### Custom Errors (`errors.rb`)
|
|
416
|
+
|
|
417
|
+
```ruby
|
|
418
|
+
K8sContainerError < Train::TransportError # Base class
|
|
419
|
+
KubectlNotFoundError # kubectl binary not in PATH
|
|
420
|
+
ContainerNotFoundError # Container doesn't exist in pod
|
|
421
|
+
PodNotFoundError # Pod doesn't exist in namespace
|
|
422
|
+
ShellNotAvailableError # Distroless container, no shell
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
## Platform Detection Deep Dive
|
|
426
|
+
|
|
427
|
+
### Why "Detect + Context" Pattern?
|
|
428
|
+
|
|
429
|
+
When connecting to an **operating system** (container, VM, bare metal), Train must detect the actual OS for InSpec resources to work correctly.
|
|
430
|
+
|
|
431
|
+
**Wrong approach** - `force_platform!('k8s-container')`:
|
|
432
|
+
- Platform name becomes `k8s-container` instead of `ubuntu`
|
|
433
|
+
- `os.linux?` returns `false`
|
|
434
|
+
- Resources like `user`, `package`, `service` fail
|
|
435
|
+
|
|
436
|
+
**Correct approach** - Detect + Context:
|
|
437
|
+
- Detect actual OS: `ubuntu`, `alpine`, `centos`
|
|
438
|
+
- Add context families: `kubernetes`, `container`
|
|
439
|
+
- `os.linux?` returns `true` ✓
|
|
440
|
+
- Resources work correctly ✓
|
|
441
|
+
|
|
442
|
+
### Detection Commands
|
|
443
|
+
|
|
444
|
+
Train's scanner executes these to identify the OS:
|
|
445
|
+
|
|
446
|
+
```bash
|
|
447
|
+
uname -s # "Linux"
|
|
448
|
+
uname -m # Architecture
|
|
449
|
+
cat /etc/os-release # OS identification
|
|
450
|
+
cat /etc/debian_version # Debian family
|
|
451
|
+
cat /etc/alpine-release # Alpine
|
|
452
|
+
cat /etc/redhat-release # RHEL family
|
|
453
|
+
```
|
|
454
|
+
|
|
455
|
+
### Result
|
|
456
|
+
|
|
457
|
+
```bash
|
|
458
|
+
$ cinc-auditor detect -t k8s-container:///my-pod/my-container
|
|
459
|
+
|
|
460
|
+
Name: ubuntu
|
|
461
|
+
Families: debian, linux, unix, os, kubernetes, container
|
|
462
|
+
Release: 22.04
|
|
463
|
+
Arch: aarch64
|
|
464
|
+
```
|
|
465
|
+
|
|
466
|
+
## URI Format
|
|
467
|
+
|
|
468
|
+
```
|
|
469
|
+
k8s-container://<namespace>/<pod>/<container>
|
|
470
|
+
```
|
|
471
|
+
|
|
472
|
+
| Component | Required | Default | Example |
|
|
473
|
+
|-----------|----------|---------|---------|
|
|
474
|
+
| namespace | No | `default` | `production` |
|
|
475
|
+
| pod | Yes | - | `web-app-7d4b8c9f-x2k4m` |
|
|
476
|
+
| container | Yes | - | `nginx` |
|
|
477
|
+
|
|
478
|
+
**Examples:**
|
|
479
|
+
```bash
|
|
480
|
+
# Full URI
|
|
481
|
+
k8s-container://production/web-app/nginx
|
|
482
|
+
|
|
483
|
+
# Default namespace
|
|
484
|
+
k8s-container:///my-pod/my-container
|
|
485
|
+
```
|
|
486
|
+
|
|
487
|
+
## Testing Architecture
|
|
488
|
+
|
|
489
|
+
### Test Structure
|
|
490
|
+
|
|
491
|
+
```
|
|
492
|
+
spec/
|
|
493
|
+
├── train-k8s-container/ # Unit tests (mocked)
|
|
494
|
+
│ ├── connection_spec.rb
|
|
495
|
+
│ ├── kubectl_exec_client_spec.rb
|
|
496
|
+
│ ├── platform_spec.rb
|
|
497
|
+
│ ├── shell_detector_spec.rb
|
|
498
|
+
│ ├── session_manager_spec.rb
|
|
499
|
+
│ └── ...
|
|
500
|
+
│
|
|
501
|
+
└── integration/ # Integration tests (real cluster)
|
|
502
|
+
├── platform_detection_spec.rb
|
|
503
|
+
├── command_execution_spec.rb
|
|
504
|
+
└── ...
|
|
505
|
+
```
|
|
506
|
+
|
|
507
|
+
### CI Matrix
|
|
508
|
+
|
|
509
|
+
| Test Type | Ruby | Kubernetes | Description |
|
|
510
|
+
|-----------|------|------------|-------------|
|
|
511
|
+
| Unit | 3.1, 3.2, 3.3 | N/A | Mocked, fast |
|
|
512
|
+
| Integration | 3.1, 3.2, 3.3 | 1.29, 1.30, 1.31 | Real kind cluster |
|
|
513
|
+
| Pod-to-Pod | 3.3 | 1.30 | Scanner inside cluster |
|
|
514
|
+
|
|
515
|
+
## Key Design Decisions
|
|
516
|
+
|
|
517
|
+
1. **kubectl over Kubernetes Ruby client**
|
|
518
|
+
- Simpler dependency management
|
|
519
|
+
- Leverages user's existing kubeconfig and auth
|
|
520
|
+
- Works with any kubectl-compatible cluster
|
|
521
|
+
|
|
522
|
+
2. **Connection pooling via PTY sessions**
|
|
523
|
+
- Dramatically improves performance for multi-control profiles
|
|
524
|
+
- Single kubectl process instead of one per command
|
|
525
|
+
- Thread-safe singleton pattern
|
|
526
|
+
|
|
527
|
+
3. **Detect+Context over force_platform**
|
|
528
|
+
- Ensures InSpec resources work correctly
|
|
529
|
+
- Provides Kubernetes awareness via family tags
|
|
530
|
+
- Compatible with existing profiles
|
|
531
|
+
|
|
532
|
+
4. **RFC 1123 validation**
|
|
533
|
+
- Prevents command injection via malformed names
|
|
534
|
+
- Enforces Kubernetes naming standards
|
|
535
|
+
|
|
536
|
+
5. **ANSI sanitization (CVE-2021-25743)**
|
|
537
|
+
- Strips terminal escape sequences
|
|
538
|
+
- Prevents injection attacks via kubectl output
|
|
539
|
+
|
|
540
|
+
6. **Cinc Auditor in CI**
|
|
541
|
+
- Open-source, license-free InSpec distribution
|
|
542
|
+
- No Chef license required for testing
|
|
543
|
+
|
|
544
|
+
7. **OIDC trusted publishing**
|
|
545
|
+
- Secure gem publishing without API keys
|
|
546
|
+
- Leverages GitHub Actions identity
|
data/BEADS-BOARD.md
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# train-k8s-container - Project Board
|
|
2
|
+
|
|
3
|
+
**Last Updated:** 2025-12-21
|
|
4
|
+
**Current Version:** 2.0.1
|
|
5
|
+
**RubyGems:** `train-k8s-container-mitre`
|
|
6
|
+
**Branch:** main
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## Current Status: ✅ Released
|
|
11
|
+
|
|
12
|
+
v2.0.1 published to RubyGems and working correctly.
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## Quick Start
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
# Resume Claude session
|
|
20
|
+
claude --continue
|
|
21
|
+
|
|
22
|
+
# Install plugin
|
|
23
|
+
cinc-auditor plugin install train-k8s-container-mitre
|
|
24
|
+
|
|
25
|
+
# Test with running pod
|
|
26
|
+
cinc-auditor detect -t k8s-container://default/test-nginx/test-nginx
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## Active Tasks
|
|
32
|
+
|
|
33
|
+
| Task | Status | Notes |
|
|
34
|
+
|------|--------|-------|
|
|
35
|
+
| Submit PR: Auto-discover train plugins | ⏳ Pending | Plan at `../inspec/PR-AUTO-DISCOVER-TRAIN-PLUGINS.md` |
|
|
36
|
+
| Submit PR: Dynamic os.kubernetes? | ⏳ Pending | Plan at `../inspec/PR-DYNAMIC-OS-FAMILY-METHODS.md` |
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## Test Environment
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
# Available test pods (may need recreation if completed)
|
|
44
|
+
kubectl run test-nginx --image=nginx:alpine --restart=Never
|
|
45
|
+
kubectl run test-ubuntu --image=ubuntu:24.04 --restart=Never -- sleep infinity
|
|
46
|
+
kubectl run test-ubi9 --image=registry.access.redhat.com/ubi9/ubi:latest --restart=Never -- sleep infinity
|
|
47
|
+
|
|
48
|
+
# Check pod status
|
|
49
|
+
kubectl get pods
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## Key Files
|
|
55
|
+
|
|
56
|
+
| File | Purpose |
|
|
57
|
+
|------|---------|
|
|
58
|
+
| `lib/train-k8s-container/transport.rb` | Train.plugin(1) registration |
|
|
59
|
+
| `lib/train-k8s-container/connection.rb` | kubectl exec connection |
|
|
60
|
+
| `lib/train-k8s-container/platform.rb` | OS detection + k8s families |
|
|
61
|
+
| `lib/train-k8s-container-mitre.rb` | Shim for gem name |
|
|
62
|
+
| `.github/workflows/release-please.yml` | Automated version PRs |
|
|
63
|
+
| `.github/workflows/release-tag.yml` | Publish on tag |
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## Release Process
|
|
68
|
+
|
|
69
|
+
1. Commits with conventional prefixes land on `main`
|
|
70
|
+
2. release-please creates a Release PR
|
|
71
|
+
3. Merge PR → tag created automatically
|
|
72
|
+
4. Tag triggers `release-tag.yml` → publishes to RubyGems
|
|
73
|
+
|
|
74
|
+
**Do NOT manually tag** - let release-please handle it.
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## Session History
|
|
79
|
+
|
|
80
|
+
| Date | Session | Summary |
|
|
81
|
+
|------|---------|---------|
|
|
82
|
+
| 2025-12-21 | SESSION-2025-12-21-008.md | Plugin API clarified, tested on 3 OS types |
|
|
83
|
+
| 2025-12-12 | SESSION-2025-12-12-009.md | Diagrams research |
|
|
84
|
+
| 2025-12-05 | SESSION-2025-12-05-007.md | v2.0.1 released to RubyGems |
|
|
85
|
+
| 2025-12-04 | SESSION-2025-12-04-006.md | CI fixed, release automation |
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## Known Issues
|
|
90
|
+
|
|
91
|
+
1. **gem install doesn't show in plugin list** - InSpec bug, PR plan ready
|
|
92
|
+
2. **Test pods expire** - Recreate with `--restart=Never` + `sleep infinity`
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
## Recovery Prompt
|
|
97
|
+
|
|
98
|
+
```
|
|
99
|
+
Continue train-k8s-container from BEADS-BOARD.md
|
|
100
|
+
|
|
101
|
+
Status: v2.0.1 released and working
|
|
102
|
+
Pending: Submit 2 PRs to inspec/inspec upstream
|
|
103
|
+
Test pods: test-nginx, test-ubuntu, test-ubi9 (may need recreation)
|
|
104
|
+
```
|