-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathfaq.html
More file actions
291 lines (255 loc) · 14.5 KB
/
Copy pathfaq.html
File metadata and controls
291 lines (255 loc) · 14.5 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
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
---
layout: default
title: "FAQ"
description: "Answers to the questions people ask most about Sandlock: running inside Docker, the pidfd_getfd seccomp gate, CAP_SYS_PTRACE, and no-supervisor mode."
permalink: /faq.html
---
<style>
.faq-list {
display: flex;
flex-direction: column;
gap: var(--space-10);
}
.faq-item {
scroll-margin-top: 96px;
}
.faq-item + .faq-item {
padding-top: var(--space-10);
border-top: 1px solid var(--gray-200);
}
.faq-q {
position: relative;
padding-left: 2.75rem;
font-size: var(--text-lg);
font-weight: 700;
line-height: var(--leading-snug);
color: var(--gray-900);
margin-bottom: var(--space-4);
}
.faq-q .marker {
position: absolute;
left: 0;
top: 0.35em;
font-family: var(--font-mono);
font-size: var(--text-xs);
font-weight: 600;
color: var(--accent-600);
letter-spacing: 0.1em;
}
.faq-q code.inline {
font-size: 0.82em;
font-weight: 600;
background: var(--gray-100);
padding: 2px 6px;
}
.faq-a {
max-width: 76ch;
padding-left: 2.75rem;
}
.faq-a p {
font-size: var(--text-base);
line-height: var(--leading-relaxed);
color: var(--gray-600);
margin-bottom: var(--space-4);
}
.faq-a p:last-child {
margin-bottom: 0;
}
.faq-a strong {
color: var(--gray-900);
font-weight: 600;
}
.faq-a a {
color: var(--accent-700);
text-decoration: underline;
text-decoration-color: rgba(201, 161, 62, 0.35);
text-underline-offset: 2px;
}
.faq-a a:hover {
color: var(--accent-600);
text-decoration-color: var(--accent-500);
}
.faq-a ul {
list-style: none;
display: flex;
flex-direction: column;
gap: var(--space-3);
margin-bottom: var(--space-4);
}
.faq-a li {
font-size: var(--text-base);
line-height: var(--leading-relaxed);
color: var(--gray-600);
padding-left: var(--space-5);
position: relative;
}
.faq-a li::before {
content: '';
position: absolute;
left: 0;
top: 0.6em;
width: 6px;
height: 6px;
border-radius: 50%;
background: var(--accent-500);
}
.faq-item .code-block {
margin-left: 2.75rem;
}
@media (max-width: 720px) {
.faq-q {
padding-left: 0;
}
.faq-q .marker {
position: static;
display: block;
margin-bottom: var(--space-2);
}
.faq-a,
.faq-item .code-block {
padding-left: 0;
margin-left: 0;
}
}
</style>
<section class="hero hero-page">
<div class="hero-container">
<div class="hero-badge">
<i data-lucide="info"></i>
<span>FAQ</span>
</div>
<h1>Frequently Asked Questions</h1>
<p class="subtitle">Questions that come up often enough on the issue tracker to be worth answering once, in public, with the exact commands.</p>
</div>
</section>
<section class="section">
<div class="section-container narrow">
<div class="section-header left">
<div class="section-badge">
<i data-lucide="container"></i>
<span>Containers</span>
</div>
<h2 class="section-title">Running Sandlock Inside Docker</h2>
<p class="section-subtitle">Sandlock is built to stand in for the container, so the first question is whether you need one at all. If you do, the runtime's default seccomp profile gets a vote before Sandlock does, and that is the single most common startup failure.</p>
</div>
<div class="faq-list">
<div class="faq-item" id="why-nest">
<h3 class="faq-q"><span class="marker">Q1</span>Sandlock is meant to replace the container. Isn't running it inside one redundant?</h3>
<div class="faq-a">
<p>Often, yes. Sandlock exists so that confining a process needs no image, no runtime, no daemon, and no root. Starting a container in order to run a sandbox that did not require one is paying twice: two policy layers to keep in sync, and the container's own startup cost in front of a sandbox that takes about five milliseconds.</p>
<p>What people usually want from the container in this situation is the <strong>filesystem</strong> rather than the isolation, and Sandlock can give them that on its own:</p>
<ul>
<li><code class="inline">--image python:3.12-slim</code> takes a local Docker image and uses its flattened rootfs directly. The daemon is asked to export the image once and the result is cached; the workload itself runs as an ordinary Sandlock sandbox, with no container around it.</li>
<li><code class="inline">--chroot ./rootfs</code> does the same for a directory you built yourself, unprivileged and with no bind mounts.</li>
<li><a href="/docs/oci.html"><code class="inline">sandlock-oci</code></a> speaks the OCI runtime interface, so containerd, CRI-O, and Kubernetes can run your existing images through Sandlock as a namespace-less, cgroup-less low-level runtime instead of through <code class="inline">runc</code>.</li>
</ul>
<p>Two cases where nesting is genuinely worth it: <strong>you do not get to choose</strong>, because a CI system or an internal platform hands you a container and Sandlock has to live inside it; or <strong>you want the layers</strong>, so that a Sandlock policy mistake still lands inside a container boundary. Both are reasonable, and the rest of this section is how to make them work.</p>
</div>
</div>
<div class="faq-item" id="docker-stock">
<h3 class="faq-q"><span class="marker">Q2</span>Can I run Sandlock in a stock Docker container, with no capabilities added and no seccomp changes?</h3>
<div class="faq-a">
<p><strong>No.</strong> Sandlock's supervisor duplicates the child's file descriptors with <code class="inline">pidfd_getfd</code>, and Docker's default seccomp profile only permits that syscall when the container holds <code class="inline">CAP_SYS_PTRACE</code>. A stock container therefore needs one of two things: <code class="inline">--cap-add SYS_PTRACE</code>, or a modified profile that allows <code class="inline">pidfd_getfd</code> explicitly.</p>
<p>This is a gate in Docker's policy, not a kernel restriction. There is no need for <code class="inline">--privileged</code> or <code class="inline">seccomp=unconfined</code>: the kernel is willing, the runtime's profile is not. See <a href="https://github.com/moby/moby/issues/45622" target="_blank" rel="noopener noreferrer">moby/moby#45622</a> for the upstream discussion of that profile entry.</p>
</div>
</div>
<div class="faq-item" id="pidfd-getfd">
<h3 class="faq-q"><span class="marker">Q3</span>Why does <code class="inline">sandlock run</code> fail on <code class="inline">pidfd_getfd</code> when <code class="inline">sandlock check</code> says the host is fine?</h3>
<div class="faq-a">
<p>Because the two report on different things. <code class="inline">sandlock check</code> inspects what the <em>kernel</em> supports: the Landlock ABI level, seccomp user notification, and the protections built on them. All of that is genuinely present inside the container, so the check passes.</p>
<p>The seccomp profile the container runtime installed around your process is a separate layer, and it is the one that refuses the call. The result is a run that dies at startup with <code class="inline">pidfd_getfd: Operation not permitted</code> on a host that looks fully supported. Treat a successful <code class="inline">check</code> inside a container as a statement about the kernel only.</p>
</div>
</div>
<div class="faq-item" id="recommended">
<h3 class="faq-q"><span class="marker">Q4</span>What is the recommended way to run Sandlock from inside Docker?</h3>
<div class="faq-a">
<p>Grant the container the capability the supervisor needs, and nothing more:</p>
</div>
<div class="code-block">
<div class="code-block-header">
<span class="code-block-title">Grant the capability the supervisor needs</span>
<button class="code-copy" type="button" aria-label="Copy code"><span class="code-copy-label">Copy</span></button>
</div>
<pre><code><span class="p">$</span> <span class="k">docker run</span> --cap-add SYS_PTRACE <span class="s"><image></span> sandlock run ...</code></pre>
</div>
<div class="faq-a">
<p>No privileged container, no <code class="inline">seccomp=unconfined</code>, no custom profile to maintain. The capability is held by the container, not handed to the workload: what the workload may do is still decided by the Sandlock policy you wrote.</p>
</div>
</div>
<div class="faq-item" id="give-it-back">
<h3 class="faq-q"><span class="marker">Q5</span>Adding <code class="inline">SYS_PTRACE</code> feels insecure. Can I take the privilege back?</h3>
<div class="faq-a">
<p>Yes, on the Sandlock side. The container now holds a capability that could, in principle, be used to inspect or manipulate other processes, so deny the syscalls that capability unlocks to the sandboxed workload with <code class="inline">--extra-deny-syscall</code>. The supervisor runs outside the sandbox and keeps its own use of <code class="inline">pidfd_getfd</code>; only the child is affected.</p>
</div>
<div class="code-block">
<div class="code-block-header">
<span class="code-block-title">Hand the privilege back at the sandbox boundary</span>
<button class="code-copy" type="button" aria-label="Copy code"><span class="code-copy-label">Copy</span></button>
</div>
<pre><code><span class="p">$</span> <span class="k">docker run</span> --rm --cap-add SYS_PTRACE \
-v /usr/local/bin/sandlock:/usr/local/bin/sandlock:ro \
-v /tmp/your_program.py:/tmp/your_program.py:ro \
python:3.12-slim <span class="k">sandlock run</span> \
--extra-deny-syscall ptrace \
--extra-deny-syscall pidfd_getfd \
--extra-deny-syscall process_vm_readv \
--extra-deny-syscall process_vm_writev \
-r / -w /tmp -- python3 /tmp/your_program.py</code></pre>
</div>
<div class="faq-a">
<p>The four denials sit on top of the default blocklist, which always applies and cannot be turned off. <code class="inline">-r /</code> makes the whole container filesystem readable, which is convenient for a one-off and worth narrowing to the paths the program actually needs once you know them.</p>
</div>
</div>
<div class="faq-item" id="no-supervisor">
<h3 class="faq-q"><span class="marker">Q6</span>Is there a way to avoid the capability entirely?</h3>
<div class="faq-a">
<p>Yes, at a cost. <code class="inline">--no-supervisor</code> never calls <code class="inline">pidfd_getfd</code>, so it runs under Docker's default profile as is. What you get is layers one and two only: Landlock plus a kernel-only deny filter, with no supervisor process.</p>
<p>What you give up is everything the supervisor provides: IP allowlisting, HTTP ACLs, resource limits, copy-on-write, chroot mediation, <code class="inline">/proc</code> virtualization, and custom handlers. If your policy is purely filesystem grants and syscall denials, that trade is fine. If it depends on any runtime decision, add the capability instead. See <a href="/docs/architecture.html">Architecture</a> for what each layer covers.</p>
</div>
</div>
<div class="faq-item" id="other-runtimes">
<h3 class="faq-q"><span class="marker">Q7</span>Does this apply to Podman and Kubernetes too?</h3>
<div class="faq-a">
<p>Any runtime that applies a seccomp profile derived from Docker's inherits the same gate, so <code class="inline">podman run --cap-add SYS_PTRACE</code> is the equivalent fix.</p>
<p>Kubernetes pods run seccomp-unconfined unless the pod selects <code class="inline">RuntimeDefault</code> or the kubelet was started with that as the default. Where the runtime default is in force, add the capability in the container's security context:</p>
</div>
<div class="code-block">
<div class="code-block-header">
<span class="code-block-title">Pod spec under a RuntimeDefault seccomp profile</span>
<button class="code-copy" type="button" aria-label="Copy code"><span class="code-copy-label">Copy</span></button>
</div>
<pre><code><span class="k">securityContext</span>:
<span class="k">capabilities</span>:
<span class="k">add</span>: [<span class="s">"SYS_PTRACE"</span>]</code></pre>
</div>
<div class="faq-a">
<p>Rather than reason about which profile a given cluster ended up with, run one <code class="inline">sandlock run</code> and look at the result. The failure is immediate, unambiguous, and names the syscall.</p>
</div>
</div>
</div>
</div>
</section>
<section class="section section-dark">
<div class="section-container narrow">
<div class="section-header">
<div class="section-badge">
<i data-lucide="messages-square"></i>
<span>Not answered here</span>
</div>
<h2 class="section-title">Ask on the Issue Tracker</h2>
<p class="section-subtitle">Questions asked in the open get answered in the open, and the good ones end up on this page.</p>
</div>
<div class="text-center">
<div class="button-group">
<a href="https://github.com/multikernel/sandlock/issues" target="_blank" rel="noopener noreferrer" class="btn btn-primary">
<i data-lucide="github"></i>
Open an Issue
</a>
<a href="/docs/getting-started.html" class="btn btn-secondary">
<i data-lucide="book-open"></i>
Read the Docs
</a>
</div>
</div>
</div>
</section>