{"id":13416246,"url":"https://github.com/flavioaiello/swarm-router","last_synced_at":"2025-09-06T04:34:51.587Z","repository":{"id":48664771,"uuid":"52256370","full_name":"flavioaiello/swarm-router","owner":"flavioaiello","description":"Scalable stateless «zero config» service-name ingress for docker swarm mode with a fresh more secure approach","archived":false,"fork":false,"pushed_at":"2023-07-14T12:27:05.000Z","size":1408,"stargazers_count":73,"open_issues_count":0,"forks_count":12,"subscribers_count":4,"default_branch":"main","last_synced_at":"2025-09-04T19:43:24.218Z","etag":null,"topics":["automation","discovery","docker","docker-swarm","docker-swarm-mode","encryption","golang","haproxy","haproxy-docker","ingress-controller","ingress-haproxy","reverse-proxy","stack","swarm","swarm-router","tls","tls-encryption"],"latest_commit_sha":null,"homepage":"https://hub.docker.com/r/flavioaiello/swarm-router/","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/flavioaiello.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":"FUNDING.yml","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},"funding":{"github":"flavioaiello"}},"created_at":"2016-02-22T07:48:34.000Z","updated_at":"2025-05-28T17:47:54.000Z","dependencies_parsed_at":"2024-04-10T05:38:29.314Z","dependency_job_id":"a3d329c0-ff49-4068-88e6-8f32f452bdf5","html_url":"https://github.com/flavioaiello/swarm-router","commit_stats":{"total_commits":265,"total_committers":3,"mean_commits":88.33333333333333,"dds":0.07169811320754715,"last_synced_commit":"5f7781d3cdc132b49da102397e3b8f662a38c0b5"},"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/flavioaiello/swarm-router","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/flavioaiello%2Fswarm-router","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/flavioaiello%2Fswarm-router/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/flavioaiello%2Fswarm-router/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/flavioaiello%2Fswarm-router/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/flavioaiello","download_url":"https://codeload.github.com/flavioaiello/swarm-router/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/flavioaiello%2Fswarm-router/sbom","scorecard":{"id":402821,"data":{"date":"2025-08-11","repo":{"name":"github.com/flavioaiello/swarm-router","commit":"5f7781d3cdc132b49da102397e3b8f662a38c0b5"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3,"checks":[{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Code-Review","score":0,"reason":"Found 0/30 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"SAST","score":0,"reason":"no SAST tool detected","details":["Warn: no pull requests merged into dev branch"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Pinned-Dependencies","score":3,"reason":"dependency not pinned by hash detected -- score normalized to 3","details":["Warn: containerImage not pinned by hash: Dockerfile:2","Warn: containerImage not pinned by hash: Dockerfile:14: pin your Docker image by updating haproxy:1.9.2-alpine to haproxy:1.9.2-alpine@sha256:ecd4c2d5017ee7719b638a36ea3cf3e5f7f02054011699331e6a59d26db704b4","Info:   1 out of   1 goCommand dependencies pinned","Info:   0 out of   2 containerImage dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'main'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}}]},"last_synced_at":"2025-08-18T20:28:51.392Z","repository_id":48664771,"created_at":"2025-08-18T20:28:51.392Z","updated_at":"2025-08-18T20:28:51.392Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":273740855,"owners_count":25159434,"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","status":"online","status_checked_at":"2025-09-05T02:00:09.113Z","response_time":402,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["automation","discovery","docker","docker-swarm","docker-swarm-mode","encryption","golang","haproxy","haproxy-docker","ingress-controller","ingress-haproxy","reverse-proxy","stack","swarm","swarm-router","tls","tls-encryption"],"created_at":"2024-07-30T21:00:55.985Z","updated_at":"2025-09-06T04:34:51.563Z","avatar_url":"https://github.com/flavioaiello.png","language":"Go","funding_links":["https://github.com/sponsors/flavioaiello"],"categories":["Container Operations","Networking \u0026 Proxies","Go"],"sub_categories":["Reverse Proxy"],"readme":"[![Docker Pulls](https://img.shields.io/docker/pulls/flavioaiello/swarm-router.svg)](https://hub.docker.com/r/flavioaiello/swarm-router/)\n[![Docker Automation](\nhttps://img.shields.io/docker/automated/flavioaiello/swarm-router.svg)](https://hub.docker.com/r/flavioaiello/swarm-router/)\n[![Go Report](\nhttps://goreportcard.com/badge/github.com/flavioaiello/swarm-router)](https://goreportcard.com/report/github.com/flavioaiello/swarm-router)\n\n# Swarm-Router\nThis is the «zero config» ingress router for Docker swarm mode deployments, based on the mature and superior haproxy library and a little of golang offering unique advantages:\n- Zero-copy using tcp splice syscall for real gbps throughput at very low cpu\n- No root privileges required\n- No docker socket mount required for service discovery\n- No external dependencies\n\n## Scope\nSolves common docker swarm mode requirements:\n- Port overlapping due to service name publishing \n- Claim based service discovery\n- HTTP service forwarding\n- TLS service offloading eg. termination and forwarding\n- TLS service passthrough\n- Stackable as swarm or stack edge\n\n## Docker Swarm\nBuilt for docker swarm mode `docker swarm init` ingress networking: Service discovery is based on claim resolution. Just define your service name urls as network alias names. Due to swarm lacking dns `SRV` support, port discovery is done by automatic port enumeration based on a default port list.\n\n## Mode 1 - Ingress routing\nSimply get started having a swarm-router up and running. Now attach and define your app urls. The according inner port will be discoverd automaticly.\n```\ndocker stack deploy -c swarm.yml swarm\ndocker stack deploy -c app.yml app\n```\nNow the endpoints below should be reachable:\n- http://app.localtest.me\n\n## Mode 2 - Ingress routing with isolated stacks\nDeploying the same stack multiple times, eg. for development, testing and production, the service names collission can be avoided only by an additional router per stack. The according inner service name and port will be discoverd automaticly \n\n![Stack isolation](https://github.com/flavioaiello/swarm-router/blob/main/swarm-router.png?raw=true)\n\n```\ndocker stack deploy -c swarm.yml swarm\ndocker stack deploy -c testing.yml testing\ndocker stack deploy -c production.yml production\n```\nNow the endpoints below should be reachable:\n\nTesting:\n- http://service.testing.localtest.me\n- http://api.testing.localtest.me\n\nProduction:\n- http://service.localtest.me\n- http://api.localtest.me\n\nThe inner communication of a stack can now be done with service shortnames eg. the service could reach simply a database using db as hostname. This makes portability of stages even simpler.\n\n## Override port discovery\nSwarm-router does port discovery based on a default port list:\n```\nDEFAULT_BACKEND_PORTS=80 443 8000 8080 8443 9000\n```\nAlternatively port ovveride based on url `startswith` is possible:\n```\nOVERRIDE_BACKEND_PORTS=myapp:6457 myotherapp:7465\n```\n\n## Certificates\nWhen TLS offloading comes into action, according fullchain certificates containing the private key should be provisioned on `/certs` host volume mount as `service.com.pem`. Preferably this one should be mounted using docker secrets.\n\n## TLS Mutual Authentication\nTLS mutual authentication can simply be enabled by adding space separated fqdn service names to the `BACKENDS_VERIFY_TLS` environment variable and the CA to the /certs/ directory. \n\n## Performance\nThis one is built for high throughput and little CPU usage. Haproxy implements zero-copy and tcp-splicing based TCP handling. Even with golang now supporting [splicing](https://github.com/golang/go/issues/10948), haproxy is ways more superior in terms of cpu consumption and latency.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fflavioaiello%2Fswarm-router","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fflavioaiello%2Fswarm-router","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fflavioaiello%2Fswarm-router/lists"}