Safe Read-Only Agent Access to Production Linux Hosts
Bottom line
A production agent account can be made strongly constrained and read-oriented using standard Linux, OpenSSH, systemd, AppArmor, sudoers, and AWS controls. The goal shouldn't be “arbitrary shell that's 100% harmless,” because arbitrary local execution can still harm availability or confidentiality. The goal should be: direct production introspection with bounded blast radius.
The highest-risk area is container access. Direct access to /var/run/docker.sock or membership in the docker group is effectively root-level control. Container introspection should instead be exposed through narrowly allowed sudo commands such as docker ps, docker inspect, docker logs --tail, docker top, and docker stats --no-stream.
Recommended security model
Use a dedicated account:
agent-ro
The account should have:
- No password login
- No sudo group membership
- No Docker group membership
- No LXD group membership
- No direct Docker or Podman socket access
- No write access to application or system paths
- Cgroup/systemd resource limits
- AppArmor confinement
- SSH forwarding disabled
- Audited access
This gives the agent direct production visibility while avoiding broad production control.
Why normal read-only shell access is not enough
A normal unprivileged Linux user may be unable to modify root-owned files, but it can often still:
- Consume CPU
- Consume memory
- Create many processes
- Perform expensive disk reads
- Scan internal networks
- Read secrets available to its UID or groups
- Access logs or process environments containing credentials
So read-only access must cover more than file permissions. It must constrain:
filesystem writes
process count
CPU
memory
network egress
privileged groups
container runtime access
log and secret visibility
Core controls
1. OpenSSH restrictions
For the production read-only user, disable pivot features:
Match User agent-ro
AllowTcpForwarding no
AllowAgentForwarding no
X11Forwarding no
PermitTunnel no
GatewayPorts no
PermitUserEnvironment no
PermitTTY yes
PasswordAuthentication no
If the agent doesn't need an interactive TTY, disable it too:
PermitTTY no
2. No privileged groups
Don't add agent-ro to:
sudo
docker
lxd
adm
root
systemd-journal
Only add log-reading groups if the confidentiality risk is accepted.
3. systemd/cgroup limits
Apply limits to the user slice:
# /etc/systemd/system/user-<uid>.slice.d/agent-ro.conf
[Slice]
CPUQuota=20%
MemoryMax=512M
TasksMax=128
IOWeight=10
Reload systemd:
sudo systemctl daemon-reload
Also consider PAM limits:
# /etc/security/limits.d/agent-ro.conf
agent-ro hard nproc 128
agent-ro hard nofile 1024
agent-ro hard core 0
4. AppArmor confinement
Use AppArmor to enforce mandatory access control beyond Unix permissions.
Desired policy shape:
allow read:
/etc/**
/usr/**
/lib/**
/proc/**
/sys/**
selected /var/log/**
selected application config paths
deny write:
/**
deny read:
/etc/shadow
/root/**
/home/*/.ssh/**
**/*.pem
**/*.key
**/.env
credential files
deny network:
outbound network except SSH session itself, if possible
AppArmor should be developed in complain mode first, then moved to enforce mode after testing.
Container introspection
Do not grant Docker socket access
Don't allow:
agent-ro in docker group
agent-ro access to /var/run/docker.sock
agent-ro access to /run/podman/podman.sock
agent-ro access to /run/user/*/podman/podman.sock
Docker daemon access isn't read-only by default. A user who can access the Docker socket can usually create privileged containers, mount host filesystems, exec into containers, or otherwise gain host-level control.
Use sudoers allowlists instead
The safer system-provided pattern is:
agent-ro cannot run docker directly
agent-ro can run selected docker commands through sudo
root accesses docker.sock
sudoers decides which commands are allowed
Example:
Defaults:agent-ro !authenticate
Defaults:agent-ro env_reset
Defaults:agent-ro log_output
Cmnd_Alias DOCKER_RO = \
/usr/bin/docker ps *, \
/usr/bin/docker inspect *, \
/usr/bin/docker logs --tail 5000 *, \
/usr/bin/docker top *, \
/usr/bin/docker stats --no-stream *, \
/usr/bin/docker port *, \
/usr/bin/docker images *, \
/usr/bin/docker network inspect *, \
/usr/bin/docker volume inspect *
agent-ro ALL=(root) NOPASSWD: DOCKER_RO
Podman can follow the same pattern:
Cmnd_Alias PODMAN_RO = \
/usr/bin/podman ps *, \
/usr/bin/podman inspect *, \
/usr/bin/podman logs --tail 5000 *, \
/usr/bin/podman top *, \
/usr/bin/podman stats --no-stream *, \
/usr/bin/podman port *, \
/usr/bin/podman images *, \
/usr/bin/podman network inspect *, \
/usr/bin/podman volume inspect *
agent-ro ALL=(root) NOPASSWD: PODMAN_RO
Commands to avoid
Don't allow:
docker run
docker exec
docker cp
docker build
docker pull
docker push
docker compose
docker start
docker stop
docker restart
docker kill
docker rm
docker system prune
docker volume rm
docker network create
docker network rm
podman run
podman exec
podman cp
podman mount
podman unshare
podman start
podman stop
podman restart
podman kill
podman rm
docker exec and podman exec are especially important to block. They aren't read-only introspection; they're arbitrary command execution inside a production container.
AWS access path
For EC2 production access, prefer AWS Systems Manager Session Manager over inbound SSH when possible.
Benefits:
- No public SSH port required
- IAM-based access control
- Centralized access revocation
- CloudTrail session-start/session-end events
- Optional CloudWatch/S3 session logs
Important caveat: AWS states that session logging isn't available for Session Manager sessions that tunnel SSH or port forwarding, because Session Manager only carries encrypted tunnel data in those cases.
What this design allows
The agent can inspect:
- OS version and kernel
- Packages
- Services
- Systemd unit state
- Logs within policy
- Network listeners
- Routes
- Disk usage
- Mounts
- Process lists
- Container metadata
- Container logs
- Container resource usage
- Container port mappings
- Container image names and IDs
- Docker/Podman networks and volumes metadata
What this design intentionally blocks
The agent can't:
- Become root
- Restart services
- Modify config
- Write application files
- Mutate containers
- Exec into containers
- Create new containers
- Access raw Docker control socket
- Use SSH forwarding as a pivot
- Freely scan internal networks
- Consume unlimited host resources
Residual risks
This model is safer, not magical.
Remaining risks include:
- Secrets exposed in readable logs or configs
- Sensitive environment variables visible through allowed commands
- Sudoers wildcard mistakes
- AppArmor profile gaps
- Expensive but allowed read operations
- Docker inspect exposing secrets in env or labels
- Logs containing credentials or customer data
So the design should be paired with:
- Log redaction
- Secret hygiene
- Least-privilege file ACLs
- Short-lived access
- Audit logs
- Budget/resource alarms
- Periodic access review
Practical recommendation
For direct production access without a custom gateway, the best pattern is:
restricted production user
+ OpenSSH restrictions
+ AppArmor confinement
+ systemd/cgroup limits
+ no Docker socket access
+ sudoers allowlist for container read commands
+ AWS/IAM audit trail
This gives an AI agent meaningful production introspection while avoiding the most dangerous mistake: treating Docker or Podman control-plane access as read-only.
Sources
- Ubuntu AppArmor documentation: https://documentation.ubuntu.com/security/security-features/privilege-restriction/apparmor/
- Ubuntu Server AppArmor guide: https://ubuntu.com/server/docs/how-to/security/apparmor/
- Linux kernel AppArmor documentation: https://www.kernel.org/doc/html/latest/admin-guide/LSM/apparmor.html
- Systemd resource control: https://www.freedesktop.org/software/systemd/man/latest/systemd.resource-control.html
- Systemd user slices: https://www.freedesktop.org/software/systemd/man/latest/[email protected]
- OpenSSH sshd_config manual: https://raw.githubusercontent.com/openssh/openssh-portable/master/sshd_config.5
- Docker Linux post-install docs: https://docs.docker.com/engine/install/linux-postinstall/
- Docker Engine security docs: https://docs.docker.com/engine/security/
- Docker authorization plugin docs: https://docs.docker.com/engine/extend/plugins_authorization/
- Podman documentation: https://docs.podman.io/en/latest/markdown/podman.1.html
- Podman commands: https://docs.podman.io/en/stable/Commands.html
- AWS Session Manager documentation: https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager.html