Repository navigation
Expand file tree
/
Copy pathportwing-with-build.yaml
More file actions
213 lines (197 loc) · 8.91 KB
/
Copy pathportwing-with-build.yaml
File metadata and controls
213 lines (197 loc) · 8.91 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
# Sockguard — Portwing with Build Preset
#
# Extends portwing-with-compose.yaml with CLASSIC BUILDER support only —
# `docker compose build` / `up --build` run with DOCKER_BUILDKIT=0 set in the
# client environment. This is NOT Compose-BuildKit support: by default,
# `docker compose build` never calls POST /build at all — it drives dockerd's
# embedded BuildKit over two hijacked, unversioned streams, POST /session and
# POST /grpc (Engine API 1.53 deprecated both; current Buildx 0.36.0 still
# uses them). This preset does not open either endpoint — no
# insecure_accept_opaque_buildkit_tunnels acknowledgment (now deprecated, see
# migration.mdx) and no request_body.buildkit mediation policy (issue #185) —
# so they stay DENIED here. THIS FAILURE IS INTENTIONAL: a default (BuildKit)
# `docker compose build` against this preset will fail with a proxy denial on
# /session or /grpc, not a bug to work around by widening this file. Use
# portwing-with-mediated-build.yaml instead if you want that transport fully
# inspected rather than falling back to classic /build.
#
# To use this preset, set DOCKER_BUILDKIT=0 in the environment that runs
# `docker compose build` / `up --build` (Compose honors it and falls back to
# the classic, single-request-body /build endpoint sockguard can fully
# inspect — Dockerfile RUN-instruction scanning, remote-context denial,
# host-network denial all apply exactly as they do for portainer.yaml's
# unrestricted /build access). See docs/content/docs/security.mdx's supported
# transport matrix for the full breakdown of what each preset covers.
#
# Allows: everything in portwing-with-compose.yaml plus:
# POST /build — classic builder only, all three request-body build
# flags left at their secure default (false below)
#
# Still denied: /session, /grpc, and any moby.buildkit.v1.Control method path
# — see portwing-with-mediated-build.yaml for the fully-mediated BuildKit
# alternative to this preset's classic-builder-only approach. Exec is
# still denied — combine with portwing-with-exec.yaml's request_body.exec
# block manually if you need both build and interactive exec.
insecure_allow_read_exfiltration: true
upstream:
socket: /var/run/docker.sock
log:
level: info
format: json
access_log: true
health:
enabled: true
path: /health
# Response redaction — all three disabled for the drydock passthrough topology.
# See portwing.yaml header for standalone-mode guidance.
#
# deny_verbosity: verbose — detailed denial reasons flow to portwing/drydock
# for operator-visible errors (portwing reads the reason field as of the
# release after 0.8.1). The denial consumer in this topology is the trusted
# Portwing agent / drydock controller, not an untrusted caller, so surfacing
# the detailed reason is the right default here.
response:
redact_mount_paths: false
redact_container_env: false
redact_network_topology: false
deny_verbosity: verbose
# Portwing needs container creation for its orchestration flows. Container-create
# bodies are inspected by default; bind-mounted workloads are denied until their
# host paths are allowlisted here. Image pulls are inspected too; this preset
# explicitly allows any registry so Portwing can track arbitrary images while still
# denying fromSrc imports. The stock "runc" runtime is allowlisted because Portwing
# (like drydock) recreates containers from inspect specs that carry an explicit
# HostConfig.Runtime — without it every recreation is rejected at
# POST /containers/create with 403 "runtime \"runc\" is not allowlisted".
request_body:
container_remove:
allow_force: true
allow_remove_volumes: true
container_create:
allowed_bind_mounts: []
allowed_runtimes:
- runc
image_pull:
allow_all_registries: true
# Classic builder: no remote build contexts (they can't be inspected for
# RUN instructions), no host-network builds, and RUN instructions stay
# denied. All three explicit false here is the secure default; left in
# place rather than omitted so the deny posture is visible at the call site.
build:
allow_remote_context: false
allow_host_network: false
allow_run_instructions: false
rules:
# Health and metadata
- match: { method: GET, path: "/_ping" }
action: allow
- match: { method: HEAD, path: "/_ping" }
action: allow
- match: { method: GET, path: "/version" }
action: allow
- match: { method: GET, path: "/info" }
action: allow
- match: { method: GET, path: "/events" }
action: allow
# Container reads — list, inspect, stats, top, changes, and logs.
# /containers/*/logs is required by Portwing's GetContainerLogs() call.
# /containers/*/archive, /containers/*/export, and /containers/*/attach
# are intentionally omitted — those are bulk-data exfiltration paths that
# Portwing does not use. insecure_allow_read_exfiltration: true (set above)
# satisfies sockguard's startup validator for the logs path.
- match: { method: GET, path: "/containers/json" }
action: allow
- match: { method: GET, path: "/containers/*/json" }
action: allow
- match: { method: GET, path: "/containers/*/logs" }
action: allow
- match: { method: GET, path: "/containers/*/stats" }
action: allow
- match: { method: GET, path: "/containers/*/top" }
action: allow
- match: { method: GET, path: "/containers/*/changes" }
action: allow
# Container lifecycle
- match: { method: POST, path: "/containers/*/start" }
action: allow
- match: { method: POST, path: "/containers/*/stop" }
action: allow
- match: { method: POST, path: "/containers/*/restart" }
action: allow
- match: { method: POST, path: "/containers/*/kill" }
action: allow
- match: { method: POST, path: "/containers/*/rename" }
action: allow
- match: { method: POST, path: "/containers/*/update" }
action: allow
- match: { method: POST, path: "/containers/*/wait" }
action: allow
- match: { method: DELETE, path: "/containers/*" }
action: allow
# Container creation (for orchestration and passthrough to drydock)
- match: { method: POST, path: "/containers/create" }
action: allow
# Image reads — list + inspect + history. Image names can be namespaced
# (linuxserver/qbittorrent:latest, ghcr.io/owner/repo:tag), so the
# middle is matched with `**`. The trailing /json or /history segment
# keeps the exfiltration validator happy by not matching /images/get
# or /images/*/get (raw tarball exfil).
- match: { method: GET, path: "/images/json" }
action: allow
- match: { method: GET, path: "/images/**/json" }
action: allow
- match: { method: GET, path: "/images/**/history" }
action: allow
- match: { method: POST, path: "/images/create" }
action: allow
- match: { method: DELETE, path: "/images/**" }
action: allow
# Classic builder only — see header comment. BuildKit's /session and
# /grpc stay denied by the catch-all rule below.
- match: { method: POST, path: "/build" }
action: allow
# Network read + lifecycle. list/inspect carried over from portwing.yaml;
# create/connect/delete/disconnect are new here so compose can own its
# stack network's full lifecycle, including multi-network service attach.
# Body inspection defaults (deny custom drivers, deny driver opts) still
# apply to POST /networks/create.
- match: { method: GET, path: "/networks" }
action: allow
- match: { method: GET, path: "/networks/*" }
action: allow
- match: { method: POST, path: "/networks/create" }
action: allow
- match: { method: POST, path: "/networks/*/connect" }
action: allow
- match: { method: DELETE, path: "/networks/*" }
action: allow
- match: { method: POST, path: "/networks/*/disconnect" }
action: allow
# Volume read + lifecycle. list/inspect carried over from portwing.yaml;
# create/delete are new here for compose-managed named volumes. Body
# inspection defaults (deny custom drivers, deny driver opts) still apply
# to POST /volumes/create.
- match: { method: GET, path: "/volumes" }
action: allow
- match: { method: GET, path: "/volumes/*" }
action: allow
- match: { method: POST, path: "/volumes/create" }
action: allow
- match: { method: DELETE, path: "/volumes/*" }
action: allow
# Distribution (registry digest checks for image freshness). Image
# references in the URL can be multi-segment (registry/owner/repo:tag).
- match: { method: GET, path: "/distribution/**/json" }
action: allow
# Swarm services — list + inspect. /services/*/logs is intentionally
# omitted to keep the exfiltration validator happy.
- match: { method: GET, path: "/services" }
action: allow
- match: { method: GET, path: "/services/*" }
action: allow
# Deny everything else — including /session and /grpc (BuildKit's opaque
# tunnel, see header comment), exec, secrets, plugins, and raw
# archive/export/attach streams.
- match: { method: "*", path: "/**" }
action: deny
reason: "not allowed by portwing-with-build preset"