{"id":22057139,"url":"https://github.com/capnspacehook/egress-eddie","last_synced_at":"2025-05-12T16:04:12.462Z","repository":{"id":38086657,"uuid":"422296031","full_name":"capnspacehook/egress-eddie","owner":"capnspacehook","description":"Hostname filtering for arbitrary network protocols","archived":false,"fork":false,"pushed_at":"2024-05-24T08:29:30.000Z","size":306,"stargazers_count":21,"open_issues_count":5,"forks_count":1,"subscribers_count":3,"default_branch":"master","last_synced_at":"2024-06-19T05:58:34.226Z","etag":null,"topics":["dns","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":"2021-10-28T17:27:01.000Z","updated_at":"2024-06-03T18:46:24.000Z","dependencies_parsed_at":"2024-05-06T09:46:08.775Z","dependency_job_id":"725b6952-5af7-41fb-b521-ab6255f58ccb","html_url":"https://github.com/capnspacehook/egress-eddie","commit_stats":null,"previous_names":[],"tags_count":17,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/capnspacehook%2Fegress-eddie","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/capnspacehook%2Fegress-eddie/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/capnspacehook%2Fegress-eddie/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/capnspacehook%2Fegress-eddie/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/capnspacehook","download_url":"https://codeload.github.com/capnspacehook/egress-eddie/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":227370654,"owners_count":17770706,"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":["dns","firewall","golang","security"],"created_at":"2024-11-30T16:16:25.502Z","updated_at":"2024-11-30T16:16:26.050Z","avatar_url":"https://github.com/capnspacehook.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# egress-eddie\n\n![Tests](https://github.com/capnspacehook/egress-eddie/actions/workflows/test.yml/badge.svg)\n\n`go install github.com/capnspacehook/egress-eddie/cmd/egress-eddie@latest`\n\n## Purpose\n\nEgress Eddie is a simple tool designed to do one thing: filter outbound traffic by hostname on Linux.\nIptables and nftables both only let you filter by IP address, generally if you want to filter\nby hostname you need a proxy for the specific protocol you're trying to filter. But Egress Eddie\nallows you to filter all TCP and UDP traffic by hostname, regardless of the protocol being used\non top.\n\nFiltering by hostname can make it exceedingly difficult for both malware to phone home and misbehaving\nsoftware to send unwanted telemetry. Combined with strong egress firewall rules, Egress Eddie can\nact as a failsafe, preventing attackers that are able to execute code on your machine from exfiltrating\ndata or interactively taking control.\n\n## How it works\n\nEgress Eddie utilizes nfqueue to intercept configured packets from iptables or nftables. It then filters\nDNS requests, only allowing requests for allowed hostnames. DNS responses to those requests are tracked,\nand only the IP addresses or hostnames present in DNS responses are allowed outbound for a configurable \namount of time.\n\n## Details\n\nAll DNS requests that are sent to Egress Eddie are filtered to make sure the questions contain allowed\nhostnames, and all DNS responses are also filtered in the same way. Additionally, only DNS responses from\nan established connection are accepted, but DNS requests from either a new or established connections\nare accepted.\n\nA DNS message is allowed if all of the questions in that message have an explicitly allowed hostname as\na suffix. For example, if `google.com` is an allowed hostname, DNS requests for\n`blog.google.com`, `groups.google.com`, and `google.com` would all be allowed.\n\nAccepted DNS answers of type `A` and `AAAA` cause the contained IPs to be allowed. DNS answers of type\n`CNAME`, `SRV`, `MX` and `NS` cause the contained hostnames to be allowed to be queried. All other accepted\nDNS answer types are passed through to the sender with no action taken by Egress Eddie.\n\nNormal traffic is only parsed up to the network layer (`IPv4` or `IPv6`). The source and destination\nIP addresses are inspected to ensure they match IPs returned from accepted DNS answers.\n\n## Security\n\nEgress Eddie leverages `seccomp` to ensure that it will only use a handful of syscalls (default 28)\nwith filtered arguments. This makes it very difficult for an attacker to do anything of value if\nthey are somehow able to execute code in the context of a running Egress Eddie process.\n\n## Permissions required\n\nAfter building, give the binary necessary capabilities:\n\n```bash\nsetcap 'cap_net_admin=+ep' egress-eddie\n```\n\nSpecial permissions are needed to interface with nfqueue. \n\nAlternatively, you *could* run Egress Eddie as root, though that is **not recommended** from a security standpoint.\n\n# Configuration\n\nEgress Eddie requires both iptables rules that send appropriate packets to Egress Eddie for\ninspection, and to be configured to look for those packets.\n\n## Iptables rules:\n\nThe requirements for iptables rules are pretty simple. Egress Eddie requires 3 sets of\nrules: sending DNS responses, sending DNS requests, and sending traffic to Egress Eddie.\n\n### Sending DNS responses\n\nFirst, you'll need to add a rule that sends all DNS responses to Egress Eddie. This can be\naccomplished as so:\n\n```bash\n# filter all DNS responses\niptables -A INPUT -p udp --sport 53 -m state --state ESTABLISHED,RELATED -j NFQUEUE --queue-num 1\n```\n\nNote here that only UDP traffic over port 53 is sent to Egress Eddie, but DNS traffic can\nbe sent over TCP as well. Additional rules are omitted for brevity.\n\nSending only established traffic isn't required, but it is recommended as starting a DNS\nconversation with a response doesn't make any sense. \n\nThis rule only needs to be added once, regardless of how many different types of traffic\nyou want to filter.\n\n### Sending DNS requests\n\nNext, you'll need to add a rule that sends DNS requests to Egress Eddie. You can either\nsend all DNS requests and filter all hostnames at once, or send specific DNS requests\nso that you can more granularly filter by hostname. For example, you can filter outbound\ntraffic by the user who created the connection in iptables, allowing you to filter DNS\nrequests differently depending on who sent it.\n\n```bash\n# filter all DNS requests\niptables -A OUTPUT -p udp --dport 53 -j NFQUEUE --queue-num 1000\n\n# OR\n\n# filter DNS requests from a specific user, in this case admin\niptables -A OUTPUT -m owner --uid-owner admin -p udp --dport 53 -j NFQUEUE --queue-num 1000\n```\n\n### Sending traffic\n\nFinally, you'll need to add a rule that sends the actual traffic you want to filter to\nEgress Eddie. As before, you can send all traffic, or traffic from certain\nusers. The following example rules will filter HTTP traffic:\n\n```bash\n# filter HTTP requests\niptables -A OUTPUT -p tcp --dport 80 -m state --state NEW -j NFQUEUE --queue-num 1001\n\n# OR\n\n# filter HTTP requests from a specific user, in this case admin\niptables -A OUTPUT -p tcp --dport 80 -m owner --uid-owner admin -m state --state NEW -j NFQUEUE --queue-num 1001\n```\n\nNotice how only new packets are being sent to Egress Eddie. This is purely for performance\nreasons. You could send new and established HTTP packets for Egress Eddie to inspect,\nbut that would have needless overhead; if the first packet is going to an allowed IP, all\nfollowing packets in the same connection will also go to that allowed IP and can be safely\nallowed.\n\n## Config file\n\nThe various options in the config file mostly boil down to telling Egress Eddie which nfqueue\nnumbers to open and use. Here's a simple config that only allows traffic to `github.com`, \nusing the same nfqueue numbers that were set in iptables rules above:\n\n```toml\ninboundDNSQueue.ipv4 = 1\n\n[[filters]]\nname = \"example\"\ndnsQueue.ipv4 = 1000\ntrafficQueue.ipv4 = 1001\nallowAnswersFor = \"5m\"\nallowedHostnames = [\n    \"github.com\",\n]\n```\n\nIf you are filtering `IPv6` traffic and using ip6tables, set `inboundDNSQueue.ipv6`,\n`dnsQueue.ipv6`, and `trafficQueue.ipv6`.\n\nNext we create a filter, setting the nfqueue numbers used for DNS requests and traffic\nthat we want filtered. The `name` of each filter is simply an identifier that will allow\nyou to more easily read or search through Egress Eddie's logs.\n\n`allowAnswersFor` controls how long IPs and hostnames returned\nfrom DNS responses are allowed for. The syntax for specifying a duration is the \n[Go duration syntax](https://pkg.go.dev/time#ParseDuration).\n\nFinally `allowedHostnames` controls the hostnames that are allowed, which here is just `github.com`.\n\n### Allowing all hostnames\n\nThere may be situations where you want to filter the hostnames of a specific user or type\nof traffic, but allow other users or types of traffic flow unrestricted. I like to allow\nthe root user to have unrestricted HTTP/S access for example, as if someone compromises the\nroot account, then all other bets are off.\n\nTo accomplish this, set `allowAllHostnames = true` and don't set both `trafficQueue` and\n`allowedHostnames`. Because all DNS responses must be inspected by Egress Eddie in order for it to\nfunction properly, all DNS requests must go through Egress Eddie as well.\n\n## Example\n\nHere's an example that ties everything mentioned above together. It allows `apt` to access\nthe standard Debian repositories, the `dev` user to pull Go modules, and the `root` user\nto have unrestricted DNS traffic.\n\niptables rules:\n\n```bash\n# filter all DNS responses\niptables -A INPUT -p udp --sport 53 -j NFQUEUE --queue-num 1\n\n# filter DNS requests from apt\niptables -A OUTPUT -p udp --dport 53 -m owner --uid-owner _apt -j NFQUEUE --queue-num 1000\n# filter HTTP/S requests from apt\niptables -A OUTPUT -p tcp --dport 80 -m owner --uid-owner _apt -m state --state NEW -j NFQUEUE --queue-num 1001\niptables -A OUTPUT -p tcp --dport 443 -m owner --uid-owner _apt -m state --state NEW -j NFQUEUE --queue-num 1001\n\n# filter DNS requests from the dev user\niptables -A OUTPUT -p udp --dport 53 -m owner --uid-owner dev -j NFQUEUE --queue-num 2000\n# filter HTTP/S requests from the dev user\niptables -A OUTPUT -p tcp --dport 80 -m owner --uid-owner dev -m state --state NEW -j NFQUEUE --queue-num 2001\niptables -A OUTPUT -p tcp --dport 443 -m owner --uid-owner dev -m state --state NEW -j NFQUEUE --queue-num 2001\n\n# allow all DNS requests from the root user\niptables -A OUTPUT -p udp --dport 53 -m owner --uid-owner root -j NFQUEUE --queue-num 3000\n```\n\nconfig file:\n\n```toml\ninboundDNSQueue.ipv4 = 1\n\n# filter apt updating\n[[filters]]\nname = \"apt updating\"\ndnsQueue.ipv4 = 1000\ntrafficQueue.ipv4 = 1001\nallowAnswersFor = \"30m\"\nallowedHostnames = [\n    \"deb.debian.org\",\n    \"security.debian.org\",\n]\n\n# filter go module traffic\n[[filters]]\nname = \"go modules\"\ndnsQueue.ipv4 = 2000\ntrafficQueue.ipv4 = 2001\nallowAnswersFor = \"5m\"\nallowedHostnames = [\n    \"proxy.golang.org\",\n    \"sum.golang.org\",\n]\n\n# allow all root DNS requests\n[[filters]]\nname = \"root allow all\"\ndnsQueue.ipv4 = 3000\nallowAllHostnames = true\n```\n\n## Verifying releases\n\nStarting from v1.1.1, binary checksum files are signed. You can verify\nreleased binaries to ensure they were not tampered with in transit.\n\nVerifying binaries requires [`cosign`](https://github.com/sigstore/cosign).\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 egress-eddie_\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 egress-eddie_\u003cversion\u003e_linux_amd64.tar.gz\ngit clone https://github.com/capnspacehook/egress-eddie egress-eddie-src\ncd egress-eddie-src\n\n# reproduce binary\ngo install github.com/capnspacehook/gorepro@latest\ngorepro -b=\"-ldflags=-s -w -X main.version \u003cversion\u003e\" ../egress-eddie\n\n# reproduce by manually running command from gorepro\nBUILD_CMD=\"$(gorepro -d -b='-ldflags=-s -w -X main.version \u003cversion\u003e' ../egress-eddie)\"\necho \"$BUILD_CMD\"\n\"$BUILD_CMD\"\nsha256sum egress-eddie egress-eddie.repro\n```\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcapnspacehook%2Fegress-eddie","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcapnspacehook%2Fegress-eddie","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcapnspacehook%2Fegress-eddie/lists"}