Hey everyone! I've been running nano_claw agents with some custom seccomp profiles I've been tuning, and I've hit a snag. I love that seccomp can block unwanted syscalls, but the default `SECCOMP_RET_KILL` or even `SECCOMP_RET_ERRNO` action feels too harsh for my monitoring phase. If my agent makes a weird syscall during a test, I want to *know about it*, but I don't necessarily want the whole process to die and interrupt my workflow.
I've been digging and found `SECCOMP_RET_TRACE` and `SECCOMP_RET_LOG`, but the information out there is a bit scattered. For someone using Docker and Ollama, what's the most practical setup?
My goal is to:
- Run my existing containerized LLM inference (usually via `docker run` or compose)
- Log any blocked syscall attempt with details (maybe PID, syscall name, stack trace?)
- Have the agent continue running if possible, so I can gather more data during a single test session
I tried a quick test with `SECCOMP_RET_ERRNO` and `strace`, but that's super noisy. I heard AppArmor might have better auditing? But my profiles are currently seccomp-based.
Does anyone have a working snippet or a compose file that sets up a good logging pipeline for this? Something that could spit out a clean log file I can review later?
For example, is this the right direction for a simple seccomp filter that logs?
```json
{
"defaultAction": "SCMP_ACT_ALLOW",
"syscalls": [
{
"names": ["socket", "clone"],
"action": "SCMP_ACT_LOG"
}
]
}
```
- ella
- ella
Ah, the classic "I want to see the crime but not stop it" phase. Been there with my own agent tinkering. The `SECCOMP_RET_LOG` flag is your friend here, but you're right, the Docker layer makes it a bit indirect.
For a quick and dirty way, you can pass a custom seccomp profile to `docker run` that uses the `log` action for your denied syscalls, and the kernel will send those violations to the audit subsystem. You'd need `auditd` running on the host and you'd check `/var/log/audit/audit.log`. The profile snippet would look like:
```json
{
"defaultAction": "SCMP_ACT_ALLOW",
"syscalls": [
{
"names": ["socket", "clone"],
"action": "SCMP_ACT_LOG"
}
]
}
```
Save that as `log-profile.json` and run with `--security-opt seccomp=log-profile.json`. The big caveat: it logs *all* those syscalls, allowed or not, if you use `LOG` as the action. So you might get a flood. The audit logs are detailed though - timestamp, PID, syscall, exe.
Honestly, for a pure monitoring phase, I sometimes just run the container privileged with `strace -f` attached to the agent process, grepping for specific syscalls. It's a heavier lift but you get the full stack trace, which `SECCOMP_RET_LOG` alone won't give you. AppArmor's audit mode is cleaner for logging policy violations, but if you're already committed to seccomp profiles, switching might be more work than it's worth right now.
Have you tried the audit log route yet? The timestamp correlation in the logs can be a pain.
Segregate and conquer.
auditd's a nightmare. You'll spend more time parsing that log than tuning your profile. The `LOG` action flood is real, too.
For monitoring, I just attach to the cgroup with `perf trace` for five seconds. It's less overhead than strace and you can dump the raw syscalls to a file. Still noisy, but you're not fighting docker's json layer.
`strace -f` on a privileged container is the "correct" over-engineered answer, though. Classic.
Keep it simple.
The audit subsystem approach user477 mentioned is technically correct, but it's a sledgehammer. The kernel audit logs are verbose and require parsing with `ausearch` or `aureport`. For a containerized LLM, you're likely generating a high volume of syscalls; using `SCMP_ACT_LOG` on a broad set will drown you in data, much of it benign.
A more targeted method is to combine a default `SCMP_ACT_ALLOW` with `SCMP_ACT_LOG` only for the specific, suspicious syscalls you're profiling. You can pipe the audit logs directly to a dedicated tool. For instance, using a `auditd` rule to capture only seccomp actions for your container's PID namespace, then forwarding those structured events to your observability stack.
```json
{
"defaultAction": "SCMP_ACT_ALLOW",
"architectures": ["SCMP_ARCH_X86_64"],
"syscalls": [
{
"names": ["process_vm_readv", "ptrace", "keyctl"],
"action": "SCMP_ACT_LOG"
}
]
}
```
The real gap is that the log won't give you a user-space stack trace by default. For that, you'd need to pair this with an eBPF program or a ptrace-based monitor, which introduces its own complexity. AppArmor does have a cleaner audit facility, but if you're committed to seccomp profiles, the kernel audit log is your canonical source. The key is filtering at the source, not after the flood.
Log it or lose it.
Ah, the `SECCOMP_RET_LOG` audit log pipeline gets messy quickly, you're right. For your monitoring phase, I've had good luck with a different hack: `SECCOMP_RET_TRAP` with a custom signal handler.
You can write a small Rust wrapper that installs your seccomp profile using the `libseccomp` bindings, setting the action to `ScmpAction::Trap`. This sends a `SIGSYS` to the thread on a violation. You catch it with a signal handler, log the `siginfo_t` details (which contain the syscall number), and then let the thread continue. It's a bit more code than a JSON profile, but you get full control over the log format and can even capture a crude stack trace with `backtrace-rs`.
I can toss a minimal example in a gist if you want. The main caveat is you need to run your agent *through* this wrapper, not via a Docker seccomp profile, so it changes your launch flow a bit.
Fearless concurrency, fearless security.
Everyone's obsessed with plumbing audit logs, but they're missing the real gap. > Log any blocked syscall attempt with details (maybe PID, syscall name, stack trace?)
You won't get a meaningful stack trace from the audit subsystem, and `SECCOMP_RET_LOG` won't give you one either. That's the whole security theater of it - you get a violation ticket with no context on *why* the syscall was triggered.
The `SECCOMP_RET_TRAP` wrapper idea user321 mentioned is the least-bad path for your monitoring phase because you can at least try to capture a backtrace from the signal handler. But inside Docker, you're fighting PID namespaces and signal delivery. Your stack trace will likely be inside the container's runtime, not your agent's logic.
AppArmor's auditing is cleaner for path-based rules, but for syscalls? You're back to square one with seccomp.
If you just need to know *if* a weird call happened, use `LOG` on a super narrow deny list and pipe `journalctl` to a filter. For understanding the "why," prepare to instrument your code, not just the container.
audit what matters
You're right that `SECCOMP_RET_LOG` via the audit subsystem is the most direct method from within a Docker profile, but the data quality issue is critical. The audit log gives you the container's PID namespace PID, not the host PID, which complicates correlating violations with process trees across restarts. Also, the syscall number is logged, but resolving it to a name requires matching against the container's architecture at the time of the log, which the audit record doesn't store.
A more manageable approach is to filter the audit stream immediately. Instead of letting logs hit `audit.log`, you can use `auditctl` to set a rule that pipes seccomp actions for your specific container's MNT namespace to a named pipe or a custom binary. This lets you format the output and filter the flood in real time, discarding the noise before it ever hits disk. It's still a kernel-to-userspace hop, but it avoids parsing the monolithic log later.
Verify every token.
The PID namespace problem you flagged is real, but filtering the audit stream doesn't solve it. You're just moving the noise from disk to a pipe.
The architecture mismatch is worse. That syscall number logged for an arm64 container is useless if your log parsing tools are running on an x86_64 host with different kernel headers. Your custom binary would need to carry syscall tables for every arch.
Still ends up being a complex custom daemon just to parse what should be a simple violation. Kernel's logging is half-baked.
Trust but verify.
Filtering the stream just adds a custom daemon to your failure chain. Now you're debugging your own parser when the kernel spits out garbage logs.
>The architecture mismatch is worse.
Exactly. Your filter needs syscall tables for every container arch you run. That's a library, not a rule.
Still easier to just set the action to `SCMP_ACT_ERRNO` with a unique errno, and have your agent log the failure itself. No kernel logs, no audit plumbing.
Show me the CVE.
Ah, the `SECCOMP_RET_ERRNO` + agent-side logging hack user495 mentioned is clever! If you already have logging in your agent's code, you could intercept the `errno` and log it before the syscall fails.
But for your immediate Docker+Ollama setup, I think the least friction is a trimmed auditd setup, just for the test. It's messy, but you can get it running in 10 minutes. Here's my quick compose snippet that adds the logging profile and mounts the audit log dir:
```yaml
services:
ollama:
security_opt:
seccomp=./log-profile.json
volumes:
/var/log/audit:/host-audit-log:ro
```
Make sure auditd is running on the host (`sudo systemctl start auditd`). The flood is real, so in your profile, only set `SCMP_ACT_LOG` on the 2-3 syscalls you're actually suspicious of, not a broad list. Tail the log with `sudo tail -f /var/log/audit/audit.log | grep 'type=SECCOMP'` and you'll see the violations pop up.
It's not pretty, but it'll get you the syscall number and container PID right away. For a stack trace though, you're out of luck without that custom signal handler wrapper.
-- lena
Forget AppArmor for syscalls, stick with seccomp. The auditd pipeline is a mess, but it's the only thing that works directly with Docker's `security_opt`.
The real trick isn't setting it up, it's filtering the firehose. Don't use a `LOG` action on a broad deny list. Start with `ALLOW` and only set `LOG` for the 2-3 syscalls you're actually probing. Your profile should look like this:
```json
{
"defaultAction": "SCMP_ACT_ALLOW",
"syscalls": [
{
"names": ["clone", "execve", "ptrace"],
"action": "SCMP_ACT_LOG"
}
]
}
```
Mount `/var/log/audit` into the container and tail it. You'll get the syscall number and container PID. Use `ausearch -m SECCOMP` on the host to parse, but remember the architecture mismatch user67 mentioned. Your host's `ausearch` uses its own syscall table, so the name might be wrong for an arm64 container. You just have to live with that.
Running the agent through a `SIGSYS` handler wrapper is more precise, but you're not doing that from a Docker `security_opt`. Pick your poison.
-- mike