Skip to content

Commit 63bcdaf

Browse files
SecAI-Hubclaude
andcommitted
Add advanced process isolation with seccomp-bpf, Landlock, and systemd hardening (M16)
Per-service seccomp JSON profiles (default-deny, explicit syscall allowlists), Landlock LSM filesystem access policies with graceful degradation on older kernels, LimitCORE=0 and MemoryDenyWriteExecute on all applicable services. Fully hardened diffusion and search-mediator units. 31 validation tests. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent b9a43cb commit 63bcdaf

23 files changed

Lines changed: 1925 additions & 9 deletions

files/system/etc/secure-ai/config/appliance.yaml

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,41 @@ monitoring:
6666
# On mismatch: model is quarantined, removed from manifest, workers restarted.
6767
integrity_interval: 15 # minutes (5, 15, 30, or 60)
6868

69+
# Audit log chain verification interval (minutes). Walks every hash-chained
70+
# audit log and verifies the chain is intact. On break: snapshots the broken
71+
# log and raises a CRITICAL alert.
72+
audit_interval: 30 # minutes
73+
74+
vault:
75+
# Auto-lock timeout in minutes. After this period of inactivity the vault
76+
# is automatically locked (unmounted + LUKS closed), stopping all AI services.
77+
# Set to 0 to disable auto-lock (not recommended).
78+
auto_lock_timeout: 30
79+
# How often the watchdog checks for inactivity (seconds).
80+
check_interval: 30
81+
82+
auth:
83+
# Session timeout in minutes. After this period of inactivity the user
84+
# must re-authenticate. Setting to 0 disables timeout (not recommended).
85+
session_timeout: 30
86+
# Maximum failed login attempts before lockout.
87+
max_failed_attempts: 5
88+
# Initial lockout duration in seconds after max_failed_attempts.
89+
lockout_duration: 60
90+
# Escalated lockout after 15 failed attempts (seconds).
91+
escalated_lockout: 900
92+
93+
sandbox:
94+
# Per-service process isolation (M16).
95+
# seccomp: JSON profiles in /etc/secure-ai/seccomp/ define allowed syscalls.
96+
# landlock: YAML policy in /etc/secure-ai/policy/landlock.yaml restricts filesystem access.
97+
# Both are enabled by default. If Landlock is unavailable (kernel < 5.13),
98+
# enforcement is skipped with a warning.
99+
seccomp_enabled: true
100+
landlock_enabled: true
101+
# Core dump prevention across all services (LimitCORE=0).
102+
core_dumps_disabled: true
103+
69104
logging:
70105
level: "info"
71106
store_raw_prompts: false
Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
# Landlock LSM Filesystem Access Policies
2+
#
3+
# Per-service filesystem access allowlists. Each service is restricted to
4+
# only the paths listed here. Access types:
5+
# ro = read-only (LANDLOCK_ACCESS_FS_READ_FILE + READ_DIR)
6+
# rw = read-write (all read + WRITE_FILE + REMOVE_FILE + MAKE_REG + MAKE_DIR)
7+
# exe = execute (LANDLOCK_ACCESS_FS_EXECUTE)
8+
#
9+
# These policies are applied by the landlock-apply helper at service start.
10+
# If Landlock is not available (kernel < 5.13), the policies are skipped
11+
# with a warning in the audit log.
12+
13+
version: 1
14+
15+
services:
16+
inference:
17+
description: "Inference worker — read-only model access, no output writes"
18+
paths:
19+
- path: /var/lib/secure-ai/registry
20+
access: ro
21+
- path: /etc/secure-ai
22+
access: ro
23+
- path: /usr/bin/llama-server
24+
access: exe
25+
- path: /usr/lib
26+
access: ro
27+
- path: /usr/lib64
28+
access: ro
29+
- path: /tmp
30+
access: rw
31+
32+
diffusion:
33+
description: "Diffusion worker — model read, output write"
34+
paths:
35+
- path: /var/lib/secure-ai/registry
36+
access: ro
37+
- path: /var/lib/secure-ai/vault/outputs
38+
access: rw
39+
- path: /etc/secure-ai
40+
access: ro
41+
- path: /usr/bin/python3
42+
access: exe
43+
- path: /usr/lib
44+
access: ro
45+
- path: /usr/lib64
46+
access: ro
47+
- path: /opt/secure-ai/services
48+
access: ro
49+
- path: /tmp
50+
access: rw
51+
52+
registry:
53+
description: "Trusted registry — manifest + model store read-write, config read-only"
54+
paths:
55+
- path: /var/lib/secure-ai/registry
56+
access: rw
57+
- path: /var/lib/secure-ai/logs
58+
access: rw
59+
- path: /etc/secure-ai
60+
access: ro
61+
- path: /usr/libexec/secure-ai/registry
62+
access: exe
63+
- path: /usr/lib
64+
access: ro
65+
- path: /usr/lib64
66+
access: ro
67+
- path: /tmp
68+
access: rw
69+
70+
ui:
71+
description: "Web UI — quarantine write, auth write, logs write, config read-only"
72+
paths:
73+
- path: /var/lib/secure-ai/quarantine
74+
access: rw
75+
- path: /var/lib/secure-ai/auth
76+
access: rw
77+
- path: /var/lib/secure-ai/logs
78+
access: rw
79+
- path: /run/secure-ai
80+
access: rw
81+
- path: /etc/secure-ai
82+
access: ro
83+
- path: /usr/libexec/secure-ai/ui
84+
access: exe
85+
- path: /usr/bin/python3
86+
access: exe
87+
- path: /usr/lib
88+
access: ro
89+
- path: /usr/lib64
90+
access: ro
91+
- path: /opt/secure-ai/services
92+
access: ro
93+
- path: /tmp
94+
access: rw
95+
96+
tool_firewall:
97+
description: "Tool firewall — policy read, vault read-only, audit log write"
98+
paths:
99+
- path: /var/lib/secure-ai/vault
100+
access: ro
101+
- path: /var/lib/secure-ai/logs
102+
access: rw
103+
- path: /etc/secure-ai
104+
access: ro
105+
- path: /usr/libexec/secure-ai/tool-firewall
106+
access: exe
107+
- path: /usr/lib
108+
access: ro
109+
- path: /usr/lib64
110+
access: ro
111+
- path: /tmp
112+
access: rw
113+
114+
quarantine:
115+
description: "Quarantine watcher — quarantine rw, registry rw, logs rw"
116+
paths:
117+
- path: /var/lib/secure-ai/quarantine
118+
access: rw
119+
- path: /var/lib/secure-ai/registry
120+
access: rw
121+
- path: /var/lib/secure-ai/logs
122+
access: rw
123+
- path: /etc/secure-ai
124+
access: ro
125+
- path: /usr/bin/python3
126+
access: exe
127+
- path: /usr/bin/llama-server
128+
access: exe
129+
- path: /usr/bin/sha256sum
130+
access: exe
131+
- path: /usr/bin/cosign
132+
access: exe
133+
- path: /usr/lib
134+
access: ro
135+
- path: /usr/lib64
136+
access: ro
137+
- path: /opt/secure-ai/services
138+
access: ro
139+
- path: /tmp
140+
access: rw
141+
142+
airlock:
143+
description: "Airlock — policy read, logs write, outbound network (not restricted by Landlock)"
144+
paths:
145+
- path: /var/lib/secure-ai/logs
146+
access: rw
147+
- path: /etc/secure-ai
148+
access: ro
149+
- path: /usr/libexec/secure-ai/airlock
150+
access: exe
151+
- path: /usr/lib
152+
access: ro
153+
- path: /usr/lib64
154+
access: ro
155+
- path: /tmp
156+
access: rw
157+
158+
search_mediator:
159+
description: "Search mediator — config read, logs write"
160+
paths:
161+
- path: /var/lib/secure-ai/logs
162+
access: rw
163+
- path: /etc/secure-ai
164+
access: ro
165+
- path: /usr/libexec/secure-ai/search-mediator
166+
access: exe
167+
- path: /usr/bin/python3
168+
access: exe
169+
- path: /usr/lib
170+
access: ro
171+
- path: /usr/lib64
172+
access: ro
173+
- path: /opt/secure-ai/services
174+
access: ro
175+
- path: /tmp
176+
access: rw
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
{
2+
"_comment": "Seccomp profile for airlock (Go binary). ONLY service with outbound network. No exec, no raw device access, file write only to audit logs.",
3+
"defaultAction": "SCMP_ACT_ERRNO",
4+
"defaultErrnoRet": 1,
5+
"archMap": [
6+
{ "architecture": "SCMP_ARCH_X86_64", "subArchitectures": ["SCMP_ARCH_X86"] },
7+
{ "architecture": "SCMP_ARCH_AARCH64", "subArchitectures": [] }
8+
],
9+
"syscalls": [
10+
{
11+
"_comment": "Core process lifecycle",
12+
"names": [
13+
"read", "write", "close", "fstat", "lstat", "stat", "newfstatat",
14+
"lseek", "mmap", "mprotect", "munmap", "brk", "mremap",
15+
"rt_sigaction", "rt_sigprocmask", "rt_sigreturn",
16+
"pread64", "pwrite64", "readv", "writev",
17+
"access", "pipe", "pipe2", "dup", "dup2", "dup3",
18+
"getpid", "getuid", "getgid", "geteuid", "getegid",
19+
"getppid", "gettid", "getdents64",
20+
"clone", "clone3", "wait4",
21+
"exit", "exit_group",
22+
"futex", "futex_waitv",
23+
"set_robust_list", "get_robust_list",
24+
"nanosleep", "clock_nanosleep", "clock_gettime", "clock_getres",
25+
"gettimeofday",
26+
"sched_yield", "sched_getaffinity",
27+
"set_tid_address",
28+
"arch_prctl", "prctl",
29+
"getrandom", "rseq"
30+
],
31+
"action": "SCMP_ACT_ALLOW"
32+
},
33+
{
34+
"_comment": "File operations (policy read, audit log write)",
35+
"names": [
36+
"open", "openat", "openat2", "fcntl",
37+
"fadvise64",
38+
"ftruncate", "fsync", "fdatasync",
39+
"readlink", "readlinkat",
40+
"statx", "statfs", "fstatfs",
41+
"getcwd", "umask"
42+
],
43+
"action": "SCMP_ACT_ALLOW"
44+
},
45+
{
46+
"_comment": "Event polling (Go runtime)",
47+
"names": [
48+
"epoll_create", "epoll_create1", "epoll_ctl", "epoll_wait", "epoll_pwait",
49+
"poll", "ppoll", "select", "pselect6",
50+
"eventfd", "eventfd2",
51+
"timerfd_create", "timerfd_settime", "timerfd_gettime"
52+
],
53+
"action": "SCMP_ACT_ALLOW"
54+
},
55+
{
56+
"_comment": "Network — outbound (HTTPS to allowlisted URLs) + localhost (:8490)",
57+
"names": [
58+
"socket", "bind", "listen", "accept", "accept4",
59+
"connect", "getsockopt", "setsockopt",
60+
"getsockname", "getpeername",
61+
"sendto", "recvfrom", "sendmsg", "recvmsg",
62+
"shutdown"
63+
],
64+
"action": "SCMP_ACT_ALLOW"
65+
},
66+
{
67+
"_comment": "Go runtime internals",
68+
"names": [
69+
"madvise", "sigaltstack",
70+
"tgkill", "tkill",
71+
"set_thread_area", "get_thread_area"
72+
],
73+
"action": "SCMP_ACT_ALLOW"
74+
}
75+
]
76+
}
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
{
2+
"_comment": "Seccomp profile for diffusion worker (Python). Same as inference (GPU ioctl) plus image/video write syscalls. No outbound network.",
3+
"defaultAction": "SCMP_ACT_ERRNO",
4+
"defaultErrnoRet": 1,
5+
"archMap": [
6+
{ "architecture": "SCMP_ARCH_X86_64", "subArchitectures": ["SCMP_ARCH_X86"] },
7+
{ "architecture": "SCMP_ARCH_AARCH64", "subArchitectures": [] }
8+
],
9+
"syscalls": [
10+
{
11+
"_comment": "Core process lifecycle",
12+
"names": [
13+
"read", "write", "close", "fstat", "lstat", "stat", "newfstatat",
14+
"lseek", "mmap", "mprotect", "munmap", "brk", "mremap",
15+
"rt_sigaction", "rt_sigprocmask", "rt_sigreturn",
16+
"pread64", "pwrite64", "readv", "writev",
17+
"access", "pipe", "pipe2", "dup", "dup2", "dup3",
18+
"getpid", "getuid", "getgid", "geteuid", "getegid",
19+
"getppid", "gettid", "getdents64",
20+
"clone", "clone3", "wait4", "waitid",
21+
"exit", "exit_group",
22+
"futex", "futex_waitv",
23+
"set_robust_list", "get_robust_list",
24+
"nanosleep", "clock_nanosleep", "clock_gettime", "clock_getres",
25+
"gettimeofday",
26+
"sched_yield", "sched_getaffinity", "sched_setaffinity",
27+
"set_tid_address",
28+
"arch_prctl", "prctl",
29+
"getrandom", "rseq"
30+
],
31+
"action": "SCMP_ACT_ALLOW"
32+
},
33+
{
34+
"_comment": "File operations (model read + output write)",
35+
"names": [
36+
"open", "openat", "openat2", "fcntl",
37+
"fadvise64", "fallocate",
38+
"rename", "renameat", "renameat2",
39+
"unlink", "unlinkat",
40+
"mkdir", "mkdirat",
41+
"ftruncate", "fsync", "fdatasync",
42+
"readlink", "readlinkat",
43+
"statx", "statfs", "fstatfs",
44+
"getcwd", "umask", "fchmod", "fchmodat",
45+
"copy_file_range", "sendfile"
46+
],
47+
"action": "SCMP_ACT_ALLOW"
48+
},
49+
{
50+
"_comment": "GPU ioctl — required for CUDA/ROCm/Vulkan",
51+
"names": ["ioctl"],
52+
"action": "SCMP_ACT_ALLOW"
53+
},
54+
{
55+
"_comment": "Memory-mapped I/O for model loading",
56+
"names": ["madvise", "mincore", "msync"],
57+
"action": "SCMP_ACT_ALLOW"
58+
},
59+
{
60+
"_comment": "Event polling",
61+
"names": [
62+
"epoll_create", "epoll_create1", "epoll_ctl", "epoll_wait", "epoll_pwait",
63+
"poll", "ppoll", "select", "pselect6",
64+
"eventfd", "eventfd2",
65+
"timerfd_create", "timerfd_settime", "timerfd_gettime"
66+
],
67+
"action": "SCMP_ACT_ALLOW"
68+
},
69+
{
70+
"_comment": "Localhost socket for health check only (bound by PrivateNetwork)",
71+
"names": [
72+
"socket", "bind", "listen", "accept", "accept4",
73+
"connect", "getsockopt", "setsockopt",
74+
"getsockname", "getpeername",
75+
"sendto", "recvfrom", "sendmsg", "recvmsg",
76+
"shutdown"
77+
],
78+
"action": "SCMP_ACT_ALLOW"
79+
},
80+
{
81+
"_comment": "Thread management + Python runtime",
82+
"names": [
83+
"set_thread_area", "get_thread_area",
84+
"tgkill", "tkill",
85+
"sigaltstack"
86+
],
87+
"action": "SCMP_ACT_ALLOW"
88+
}
89+
]
90+
}

0 commit comments

Comments
 (0)