Repository navigation
Conversation
…tworks lib/container.sh runs containers for modules under fixed rules: image pinned by digest, no new privileges, cap_drop ALL, read-only root, memory and PID limits, no engine socket except for Portainer. It also checks Compose files, installs the pinned Trivy release and manages the container firewall. libexec/lss-container-firewall keeps published container ports behind the lss firewall rules: an own nftables forward chain drops redirected connections to container networks unless fw_allow opened the port. fw_trust accepts host traffic from networks such as the k3s pod network. Refs #5 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: VectoDE <system.bot.discord.utility@gmail.com>
docker installs Docker CE from download.docker.com (pinned keys for the apt and dnf repositories) on Debian 12/13, Ubuntu 22.04/24.04/26.04 and the RHEL 9/10 family, and docker.io from the distribution where Docker builds nothing. daemon.json follows CIS Docker section 2: icc off, live-restore, local log driver with limits, no-new-privileges, userns-remap (option), no userland proxy, default ulimits, SELinux labels on RHEL, default seccomp. podman uses the distribution packages with process and log limits. Both install the container firewall and Trivy. AIDE skips the container data directories. Refs #5 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: VectoDE <system.bot.discord.utility@gmail.com>
k3s runs a pinned, checksum-verified binary (v1.36.5+k3s1, amd64 and arm64) as a systemd unit with /etc/rancher/k3s/config.yaml after the k3s CIS hardening guide: encrypted secrets, protect-kernel-defaults with the kernel values the kubelet expects, kubeconfig mode 0600, Pod Security admission, audit log, optional Traefik. On RHEL with SELinux k3s-selinux comes as a pinned package. The pod and service networks are trusted, the API port follows LSS_K3S_API_ACCESS. portainer runs Portainer CE from a pinned digest on 127.0.0.1:9443 or the WireGuard network, with the admin password from the secrets and a data volume. It is the only container that gets the engine socket. Optionally below /portainer/ on the admin port of the web server. Refs #5 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: VectoDE <system.bot.discord.utility@gmail.com>
Host paths of bind mounts were checked only as written, so a symbolic link to the engine socket, /etc or /root passed. Both the path as written and its realpath are now checked, and a path that cannot be resolved stops the run. All of /etc is denied except /etc/lss/containers/NAME for container NAME (Portainer's admin password moves there), as are /var/lib/lss*, /var/log/lss*, /run/systemd and /run/dbus. The Compose check covers the same rules: bind sources go through the same path check, and privileged, cap_add ALL or denied capabilities, unconfined security options, devices and host or container namespaces are refused. First unit tests for lib/container.sh. Refs #5 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: VectoDE <system.bot.discord.utility@gmail.com>
…oad ranges fw_trust accepted traffic by source address alone, so a neighbour on the LAN could spoof a pod address and reach every port of the host. It now takes NETWORK INTERFACE: ufw allows "in on INTERFACE from NETWORK", firewalld binds the interface to its trusted zone. The registry records both, removal stays symmetric and tolerates rules that are already gone. Networks are validated properly (address bytes, prefix ranges) and refused when broader than /8 (IPv4) or /32 (IPv6) or when they contain the address of the default route. A failed ufw allow stops the run. k3s trusts its pod network on cni0 only. Refs #5 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: VectoDE <system.bot.discord.utility@gmail.com>
When docker or podman is installed, the guard scans every image with the pinned Trivy for HIGH and CRITICAL vulnerabilities and reports one finding per affected image with the numbers. Podman images are saved to an archive first, so no socket is needed. A scan that fails is a note. Refs #5 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: VectoDE <system.bot.discord.utility@gmail.com>
…le metadata Unit tests for the port list and reload of the container firewall, its setup, verify and removal, the nftables rule set, engine detection, Compose projects, Trivy upkeep, the metadata and conflicts of the four container modules and the pinned download checksums. Refs #5 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: VectoDE <system.bot.discord.utility@gmail.com>
k8s-node is minimal-secure plus k3s. profiles/all.conf gains docker and portainer; k3s stays out because it conflicts with docker and is covered by k8s-node. Refs #5 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: VectoDE <system.bot.discord.utility@gmail.com>
New container group podman (storage on a volume, not overlayfs). New VM runs on every supported system including Ubuntu 22.04, 24.04 and 26.04 cloud images: firewall with docker and portainer, then firewall with k3s. Profile k8s-node on Debian 13 and AlmaLinux 10. The Lynis minimums of the profiles all and k8s-node are estimates until the first runs. Refs #5 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: VectoDE <system.bot.discord.utility@gmail.com>
README: container module list and section, settings examples, the k8s-node profile, resource rows to be filled by the integration runs. hardening.md: rows for docker, podman, the container rules, the container firewall, portainer, k3s and the Trivy scan with their CIS Docker and k3s CIS guide areas, plus the deliberate deviations and what is not covered. Architecture and module guide describe the container firewall, fw_trust and the container helpers. Changelog entries. Refs #5 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: VectoDE <system.bot.discord.utility@gmail.com>
Quote the Trivy severity list (SC2054), keep escaped newlines in printf formats and test strings, and wrap the long list of rejected networks. Refs #5 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: VectoDE <system.bot.discord.utility@gmail.com>
Refs #5 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: VectoDE <system.bot.discord.utility@gmail.com>
…en a run fails Refs #5 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: VectoDE <system.bot.discord.utility@gmail.com>
Docker runs with user namespace remapping, so a new volume belongs to the remapped root. Portainer runs in the host user namespace as the real root without capabilities and stopped with 'mkdir /data/certs: permission denied'. Refs #5 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: VectoDE <system.bot.discord.utility@gmail.com>
Refs #5 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: VectoDE <system.bot.discord.utility@gmail.com>
…sockets dockerd holds the published ports of the containers itself, so Portainer on 127.0.0.1:9443 made the check report a TCP API. The check now reads dockerd's command line and the hosts in daemon.json. Refs #5 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: VectoDE <system.bot.discord.utility@gmail.com>
Refs #5 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
AlmaLinux 10 (RHEL 10 kernel) has no addrtype xtables match, so dockerd fails to add its NAT rules. There dockerd now writes nftables rules itself, and lss enables IP forwarding, which that backend leaves to the system. Refs #5 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: VectoDE <system.bot.discord.utility@gmail.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: VectoDE <system.bot.discord.utility@gmail.com>
With the nftables backend dockerd sets no FORWARD drop policy, so the forwarding lss enables for it would route between every interface. The container firewall now drops forwarding that involves no container network while that sysctl file exists. Forwarding goes on only after the rule is loaded and goes off on removal before the firewall goes. Refs #5 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: VectoDE <system.bot.discord.utility@gmail.com>
icc=false and the missing userland proxy need the bridge netfilter hooks; dockerd loads br_netfilter itself only for its iptables rules. Refs #5 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: VectoDE <system.bot.discord.utility@gmail.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: VectoDE <system.bot.discord.utility@gmail.com>
…tables The AlmaLinux 10 cloud image has only kernel-modules-core, which lacks br_netfilter and xt_addrtype. Installing kernel-modules for the running kernel lets dockerd keep its iptables rules, so the experimental nftables backend, the forwarding sysctl and its firewall guard go away again. Refs #5 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: VectoDE <system.bot.discord.utility@gmail.com>
Signed-off-by: VectoDE <system.bot.discord.utility@gmail.com>
xt_addrtype and br_netfilter are in kernel-modules-extra there, which cloud images leave out and iptables no longer pulls in (docker/docker-ce-packaging#1340). The check now loads the modules instead of only resolving them. Refs #5 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: VectoDE <system.bot.discord.utility@gmail.com>
Signed-off-by: VectoDE <system.bot.discord.utility@gmail.com>
Signed-off-by: VectoDE <system.bot.discord.utility@gmail.com>
The encryption controller can report later than the API on a busy host; the profile run on AlmaLinux 10 failed now and then right after start. The check now waits up to 60 seconds and logs the status (key names and hash state, no key material) when it still fails. Refs #6 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: VectoDE <system.bot.discord.utility@gmail.com>
Signed-off-by: VectoDE <system.bot.discord.utility@gmail.com>
Signed-off-by: VectoDE <system.bot.discord.utility@gmail.com> # Conflicts: # .github/workflows/integration.yml # tests/integration/vm-images.conf
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



Stage 3 of lss 2.0 (#5): Docker, Podman, k3s and Portainer.
docker(Docker CE from download.docker.com, the distribution package where needed) andpodman(conflict), hardened daemon settings (no-new-privileges, icc off, live-restore, userns-remap, log limits).inet lss_containerslets a redirected connection through only to ports a module opened withfw_allow.lib/container.sh: containers pinned by digest, cap_drop ALL, read-only root, memory and PID limits, no host namespaces, no engine socket (Portainer is the one documented exception); host paths are checked as written and after following symbolic links. Compose files are checked against the same rules.k3swith secrets encryption, Pod Security restricted, audit log and CIS kernel parameters; SELinux policy on the RHEL family. Trusted networks are bound to their interface.portaineron 127.0.0.1 or the WireGuard network.k8s-node;allgains docker and portainer.Not run on a real machine before this pull request; the CI runs are the first test.
Refs #5
🤖 Generated with Claude Code