{"id":13819956,"url":"https://github.com/capnspacehook/whalewall","last_synced_at":"2025-07-05T05:03:09.759Z","repository":{"id":61624181,"uuid":"528519990","full_name":"capnspacehook/whalewall","owner":"capnspacehook","description":"Automate management of firewall rules for Docker containers","archived":false,"fork":false,"pushed_at":"2024-12-23T16:06:29.000Z","size":324,"stargazers_count":283,"open_issues_count":19,"forks_count":6,"subscribers_count":8,"default_branch":"master","last_synced_at":"2025-04-03T23:09:25.946Z","etag":null,"topics":["docker","firewall","golang","security"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/capnspacehook.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-08-24T17:10:00.000Z","updated_at":"2025-04-02T19:56:04.000Z","dependencies_parsed_at":"2024-01-04T20:27:17.177Z","dependency_job_id":"8f7a34d6-6ab8-45c0-8f19-21c520115578","html_url":"https://github.com/capnspacehook/whalewall","commit_stats":null,"previous_names":[],"tags_count":6,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/capnspacehook%2Fwhalewall","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/capnspacehook%2Fwhalewall/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/capnspacehook%2Fwhalewall/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/capnspacehook%2Fwhalewall/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/capnspacehook","download_url":"https://codeload.github.com/capnspacehook/whalewall/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248625497,"owners_count":21135513,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["docker","firewall","golang","security"],"created_at":"2024-08-04T08:00:55.463Z","updated_at":"2025-04-12T19:46:25.783Z","avatar_url":"https://github.com/capnspacehook.png","language":"Go","funding_links":[],"categories":["Go"],"sub_categories":[],"readme":"# whalewall\n\nAutomate management of firewall rules for Docker containers.\n\n## Requirements\n\nLinux with a recent kernel, around 5.10 or newer.\n\n## Purpose\n\nDocker by default creates iptables rules to handle container traffic that override almost all user-set\nrules. There are two main ways to get around this:\n\n1. Prevent Docker from creating any iptables rules by setting `\"iptables\": false` in `/etc/docker/daemon.json`\n    - This is the nuclear approach. It will break most networking for containers, and require that\n    you manage iptables for containers manually, which can be a very involved process.\n2. Add rules to the `DOCKER-USER` iptables chain\n    - Docker ensures that rules in this chain are processed *before* any rules Docker creates.\n\nAdding rules to the `DOCKER-USER` chain is what whalewall does to avoid managing more firewall rules\nthan it needs to. You may be wondering if whalewall is necessary, after all it is very easy to add\nfirewall rules to the `DOCKER-USER` chain yourself. Well, Docker containers and networks are ephemeral,\nmeaning every time a container or network is destroyed and recreated, the IP address and subnet\nrespectively will be randomized. Whalewall takes care of creating or deleting rules when containers\nare created or killed, which would be very tedious and error-prone manually. Finally, as well as\nmanaging firewall rules to limit traffic to and from localhost and external interfaces, whalewall\ncan also enforce container network isolation by limiting traffic between containers.\n\n## Mechanism\n\nWhalewall listens for Docker container `start` and `die` events and creates or deletes\n[nftables](https://wiki.nftables.org/wiki-nftables/index.php/What_is_nftables%3F)\nrules appropriately. Why is nftables used instead of iptables? A few reasons:\n\n- nftables can be configured programmatically unlike iptables, removing the need for whalewall to\nexecute any binaries\n- nftables allows for first-class sets and maps in firewall rules which can greatly speed up\ntraffic matching in the kernel\n- In most distros, iptables rules are translated to nftables rules under the hood, making iptables\nrules compatible with nftables rules\n\nWhalewall stores details of containers it is managing rules for in a SQLite database. If containers\nare started or stopped while whalewall isn't running, whalewall will compare currently running\ncontainers to what was last saved to the database and create/delete firewall rules appropriately.\n\n## Security\n\nWhalewall needs the `NET_ADMIN` capability to manage nftables rules. It also needs to be a member\nof the `docker` group in order to use `/var/run/docker/docker.sock` to receive events from the\nlocal Docker daemon.\n\nTo reduce attack surface, [landlock](https://docs.kernel.org/userspace-api/landlock.html) and\n[seccomp](https://docs.kernel.org/next/userspace-api/seccomp_filter.html) are leveraged to ensure\nonly files and syscalls required by whalewall can be accessed and called respectively. This vastly\nlimits what whalewall is able to do in the event an attacker is able to execute code in the context\nof its process. However, this will not prevent said attacker from taking advantage of the Docker\nsocket whalewall has access to which can trivially lead to privilege escalation.\n\n## Installation\n\n### Docker image\n\nDownload the Docker image:\n\n```sh\ndocker pull ghcr.io/capnspacehook/whalewall:0.2.0\n```\n\nEnsure whalewall is given necessary permissions, and that it is using `host` network mode. This\nallows the whalewall container to modify host firewall rules.\n\nExample Docker compose file:\n\n```yaml\nversion: \"3\"\nservices:\n  whalewall:\n    cap_add: \n      - NET_ADMIN\n    image: ghcr.io/capnspacehook/whalewall\n    network_mode: host\n    volumes:\n      - whalewall_data:/data\n      - /var/run/docker.sock:/var/run/docker.sock:ro\n\nvolumes:\n  whalewall_data:\n```\n\n### Binary install\n\nIf you want to run whalewall natively, download a release binary.\n\nOr if you want to compile from source, assuming you have Go 1.19 installed:\n\n```sh\ngo install github.com/capnspacehook/whalewall/cmd/whalewall@latest\n```\n\nAfter installing whalewall, grant it required permissions by running:\n\n```sh\n# this must be run first, it will erase any set capabilities\nchgrp docker whalewall\nsetcap 'cap_net_admin=+ep' whalewall\n\n```\n\n## Configuration\n\nWhalewall uses Docker labels for configuration:\n\n- `whalewall.enabled` is used to enable or disable firewall rules for a container. If this label is\nnot present and set to `true` for a container, whalewall will not create any firewall rules for it.\n- `whalewall.rules` specifies the firewall rules for a container. If this label is not specified but\n`whalewall.enabled=true` is, no traffic will be allowed to or from the container (unless another\ncontainer has an output rule for this container).\n\nThe contents of the `whalewall.rules` label is a yaml config.\n\nWhalewall creates rules with a default drop policy, meaning any traffic not explicitly allowed will\nbe dropped.\n\n### Example\n\nBelow is an example Docker compose file that configures [Miniflux](https://github.com/miniflux/v2),\na feed reader. Miniflux needs to connect to a Postgresql database to store state and make outbound\nHTTPS connections to fetch articles, so that's only what is allowed.\n\n```yaml\nversion: \"3\"\nservices:\n  miniflux:\n    depends_on:\n      - miniflux_db\n    environment:\n      - DATABASE_URL=postgres://miniflux:secret@miniflux_db/miniflux?sslmode=disable\n      - RUN_MIGRATIONS=1\n      - CREATE_ADMIN=1\n      - ADMIN_USERNAME=admin\n      - ADMIN_PASSWORD=password\n    image: miniflux/miniflux:latest\n    labels:\n      whalewall.enabled: true\n      whalewall.rules: |\n        mapped_ports:\n          # allow traffic to port 80 from localhost\n          localhost:\n            allow: true\n          # allow traffic to port 80 from LAN\n          external:\n            allow: true\n            ips:\n              - \"192.168.1.0/24\"\n        output:\n          # allow postgres connections\n          - network: default\n            container: miniflux_db\n            proto: tcp\n            dst_ports:\n              - 5432\n          # allow DNS requests\n          - log_prefix: \"dns\"\n            proto: udp\n            dst_ports:\n              - 53\n          # allow HTTPS requests\n          - log_prefix: \"https\"\n            proto: tcp\n            dst_ports:\n              - 443\n    ports:\n      - \"80:8080/tcp\"\n\n  miniflux_db:\n    environment:\n      - POSTGRES_USER=miniflux\n      - POSTGRES_PASSWORD=secret\n    image: postgres:alpine\n    labels:\n      # no rules specified, drop all traffic\n      whalewall.enabled: true\n```\n\nNote to make this Docker compose config as concise as possible, best practices were not followed.\nThis is merely intended to be an example of whalewall rules, not how to setup Miniflux securely.\n\n### Rules config reference\n\n```yaml\n# controls traffic from localhost or external networks to a container on mapped ports\nmapped_ports:\n  # controls traffic from localhost\n  localhost:\n    # required; allow traffic from localhost or not \n    allow: false\n    # optional; log new inbound traffic that this rule will match\n    log_prefix: \"\"\n    # optional; settings that allow you to filter traffic further if desired\n    verdict:\n      # optional; a chain to jump to after matching traffic. This applies to new and established\n      # inbound traffic, and established outbound traffic \n      chain: \"\"\n      # optional; the userspace nfqueue to send new outbound packets to\n      queue: 0\n      # optional; the userspace nfqueue to send established inbound packets to. Required if\n      # 'output_est_queue' is set\n      input_est_queue: 0\n      # optional; the userspace nfqueue to send established inbound packets to. Required if\n      # 'input_est_queue' is set\n      output_est_queue: 0\n  # controls traffic from external networks (from any non-loopback network interface)\n  external:\n    # required; allow external traffic or not\n    allow: false\n    # optional; log new inbound traffic that this rule will match\n    log_prefix: \"\"\n    # optional; a list of IP addresses, CIDRs, or ranges of IP addresses to allow traffic from\n    ips: []\n    # optional; settings that allow you to filter traffic further if desired\n    verdict:\n      # optional; a chain to jump to after matching traffic. This applies to new and established\n      # inbound traffic, and established outbound traffic \n      chain: \"\"\n      # optional; the userspace nfqueue to send new outbound packets to\n      queue: 0\n      # optional; the userspace nfqueue to send established inbound packets to. Required if\n      # 'output_est_queue' is set\n      input_est_queue: 0\n      # optional; the userspace nfqueue to send established inbound packets to. Required if\n      # 'input_est_queue' is set\n      output_est_queue: 0\n# controls traffic from a container to localhost, another container, or the internet\noutput:\n    # optional; log new outbound traffic that this rule will match\n  - log_prefix: \"\"\n    # optional; a Docker network traffic will be allowed out of. If unset, will default to all \n    # networks the container is a member of. Required if 'container' is set\n    network: \"\"\n    # optional; a list of IP addresses, CIDRs, or ranges of IP addresses to allow traffic to\n    ips: []\n    # optional; a container to allow traffic to. This can be either the name of the container or\n    # the service name of the container is docker compose is used\n    container: \"\"\n    # required; either 'tcp' or 'udp'\n    proto: \"\"\n    # optional; a list of source ports to allow traffic to. Can be a single port or a\n    # range of ports.\n    src_ports: []\n    # optional; a list of destination ports to allow traffic to. Can be a single port or a\n    # range of ports.\n    dst_ports: []\n    # optional; settings that allow you to filter traffic further if desired\n    verdict:\n      # optional; a chain to jump to after matching traffic. This applies to new and established\n      # inbound traffic, and established outbound traffic \n      chain: \"\"\n      # optional; the userspace nfqueue to send new outbound packets to\n      queue: 0\n      # optional; the userspace nfqueue to send established inbound packets to. Required if\n      # 'output_est_queue' is set\n      input_est_queue: 0\n      # optional; the userspace nfqueue to send established inbound packets to. Required if\n      # 'input_est_queue' is set\n      output_est_queue: 0\n```\n\nPort and IP ranges are inclusive. Examples:\n\n- `4000-5000` will match all ports between and including port 4000 and port 5000\n- `1.1.1.1-2.2.2.2` will match all IPs between and including 1.1.1.1 and 2.2.2.2\n\n### Docker environmental variables\n\nWhalewall accepts several environmental variables that can be used to configure how it connects to a Docker server:\n\n- `DOCKER_HOST` to set the URL to the Docker server.\n- `DOCKER_API_VERSION` to set the version of the Docker API to use, leave empty for latest.\n- `DOCKER_CERT_PATH` to specify the directory from which to load the TLS certificates (ca.pem, cert.pem, key.pem).\n- `DOCKER_TLS_VERIFY` to enable or disable TLS verification (off by default).\n\n### Tips\n\n- Logged traffic is sent to the kernel log file, typically `/var/log/kern.log` for Debian based distros\nand `/var/log/messages` for RHEL based distros\n- If you want a container to only be allowed outbound access on a port to localhost, use the IP\nof the `docker0` network interface, which is often `172.17.0.1`\n- If no Docker networks are explicitly created, use the `default` network when creating container to\ncontainer rules\n\n## Verifying releases\n\nStarting from v0.2.0, all Docker images and binary checksum files are signed. You can verify\nimages or released binaries to ensure they were not tampered with in transit.\n\nVerifying Docker images or binaries both require [`cosign`](https://github.com/sigstore/cosign).\n\n### Verifying Docker images\n\nSimply check the signature of the image with `cosign`:\n\n```sh\ncosign verify ghcr.io/capnspacehook/whalewall:\u003cversion\u003e | jq\n```\n\nYou can verify the image was built by Github Actions by inspecting the `Issuer` and `Subject` fields of the output.\n\n### Verifying binaries\n\nDownload the checksums file, certificate, signature and the archive to the same directory.\n\nExtract the binary from the archive, verify the checksums file and verify the contents of the binary:\n\n```sh\ntar xfs whalewall_\u003cversion\u003e_linux_amd64.tar.gz\ncosign verify-blob --certificate checksums.txt.crt --signature checksums.txt.sig checksums.txt\nsha256sum -c checksums.txt\n```\n\n### Reproducing released binaries\n\nYou can also reproduce the released binaries to verify that they were built from this unmodified\nsource code. Verifying binaries requires [`gorepro`](https://github.com/capnspacehook/gorepro).\n\nFirst, download the release archive and extract it. Clone this repro and go into it.\n\nInstall `gorepro` and run it on the extracted release binary. `gorepro` will tell you if reproducing\nthe binary was successful. Don't worry about checking out the correct tag or commit, gorepro will\nhandle that for you.\n\nIf you don't trust `gorepro` you can run it again additionally passing the `-d` flag. This will\nprint the commands `gorepro` generated to reproduce the release binary. You can run the printed commands\nand verify for yourself that the reproduced binary is bit for bit identical to the released one.\n\n```sh\ntar fxs whalewall_\u003cversion\u003e_linux_amd64.tar.gz\ngit clone https://github.com/capnspacehook/whalewall whalewall-src\ncd whalewall-src\n\n# reproduce binary\ngo install github.com/capnspacehook/gorepro@latest\ngorepro -b=\"-ldflags=-s -w -X main.version \u003cversion\u003e\" ../whalewall\n\n# reproduce by manually running command from gorepro\nBUILD_CMD=\"$(gorepro -d -b='-ldflags=-s -w -X main.version \u003cversion\u003e' ../whalewall)\"\necho \"$BUILD_CMD\"\n\"$BUILD_CMD\"\nsha256sum whalewall whalewall.repro\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcapnspacehook%2Fwhalewall","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcapnspacehook%2Fwhalewall","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcapnspacehook%2Fwhalewall/lists"}