{"id":23771620,"url":"https://github.com/michurin/netpunch","last_synced_at":"2025-09-05T15:32:40.178Z","repository":{"id":39653800,"uuid":"471663127","full_name":"michurin/netpunch","owner":"michurin","description":"Netpunch is a tool to establish network connections between nodes which are behind NATs and firewalls","archived":false,"fork":false,"pushed_at":"2025-07-12T17:57:14.000Z","size":63,"stargazers_count":6,"open_issues_count":4,"forks_count":0,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-07-12T19:41:44.033Z","etag":null,"topics":["hole-punching","nat-traversal","network","openvpn","peer-to-peer","vpn"],"latest_commit_sha":null,"homepage":"","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/michurin.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,"zenodo":null}},"created_at":"2022-03-19T10:34:03.000Z","updated_at":"2025-07-12T17:57:17.000Z","dependencies_parsed_at":"2024-01-21T09:22:34.584Z","dependency_job_id":"ac7e6873-f6ef-4f35-907e-4bf7651a2a95","html_url":"https://github.com/michurin/netpunch","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/michurin/netpunch","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/michurin%2Fnetpunch","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/michurin%2Fnetpunch/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/michurin%2Fnetpunch/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/michurin%2Fnetpunch/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/michurin","download_url":"https://codeload.github.com/michurin/netpunch/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/michurin%2Fnetpunch/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":273777604,"owners_count":25166374,"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":["hole-punching","nat-traversal","network","openvpn","peer-to-peer","vpn"],"created_at":"2025-01-01T04:20:06.592Z","updated_at":"2025-09-05T15:32:40.160Z","avatar_url":"https://github.com/michurin.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Network hole punching tool\n\n[![build](https://github.com/michurin/netpunch/actions/workflows/ci.yaml/badge.svg)](https://github.com/michurin/netpunch/actions/workflows/ci.yaml)\n[![codecov](https://codecov.io/gh/michurin/netpunch/branch/master/graph/badge.svg?token=XCJIGGU510)](https://codecov.io/gh/michurin/netpunch)\n[![linting](https://img.shields.io/badge/linting-golangci--lint--v2-blue)](https://golangci-lint.run/)\n\nNetpunch is a tool to establishes network connections between nodes which are behind NATs and firewalls.\n\nNetpunch has to be run on one *control* node with public IP address and on two *peer*-nodes from private networks.\nControl node makes private peers able to know their public IPs and ports and setup VPN connection over UDP.\n\nIt is not an unheard solution. It is easy to find a lot of scripts, that are doing the same things.\nHowever, the netpunch provides at least three advantages:\n\n- You do not need root permissions to run it. The running with regular user permissions makes your system securer\n- You do not need additional tools like `tcpdump`, `awk` etc, or additional libs\n- All network interactions are protected by signature. So it is difficult to abuse your control node\n\n## How does it work\n\nHere is a slightly oversimplified flow. Boxes are nodes and network areas, arrows are UDP messages.\n\n```mermaid\nflowchart TB\nsubgraph Internet\n    direction LR\n    subgraph Privat network A\n        A[Peer A\u003cbr/\u003ebehind NATs\u003cbr/\u003eand firewalls]\n    end\n    C[Control node\u003cbr/\u003ewith public IP and\u003cbr/\u003eopenned UDP port]\n    A--\u003e|Step 1:\u003cbr/\u003eAnnonce public address\u003cbr/\u003eof node A|C\n    B--\u003e|Step 2:\u003cbr/\u003eAnnonce public address\u003cbr/\u003eof node B|C\n    C--\u003e|Stop 3:\u003cbr/\u003eObtain public address\u003cbr/\u003eof node A|B\n    B--\u003e|Step 4:\u003cbr/\u003eStart punching to peer A\u003cbr/\u003eby its public address|A\n    A-.-\u003e|Step 5:\u003cbr/\u003eNext try\u003cbr/\u003eof step 1|C\n    C-.-\u003e|Step 6:\u003cbr/\u003eObtain public address\u003cbr/\u003eof peer B|A\n    A-.-\u003e|Step 7:\u003cbr/\u003eStart punching to peer B\u003cbr/\u003eby its public address|B\n    subgraph Privat network B\n        B[Peer B\u003cbr/\u003ebehind NATs\u003cbr/\u003eand firewalls]\n    end\nend\n```\n\nEach peer from private networks reaches public control node. Control node saves\npublic addresses of peers and makes them able to know public addresses of each other.\nSo peers from private networks become able to start punching towards each other.\n\n## Quick start with OpenVPN\n\n### Install binaries\n\n```sh\ngo install -v github.com/michurin/netpunch/cmd/...@latest\n```\n\nFor cross compilation look into [build.sh](build.sh) or just run `./build.sh -h`.\n\n### Setup connection\n\nYou can find all instructions and hints in self-documented script [connection-example.sh](connection-example.sh).\n\nBasically, you have to do three things:\n\n- Start netpunch on control node (see instructions in [connection-example.sh](connection-example.sh)). You don't need to have root permissions to do that. However, you have to have open UDP port. If you don't, you are to be able to tune your firewall settings\n- Start the script like `connection-example.sh` on peer A. Do not forget to generate `secret.key` file, as it shown in [connection-example.sh](connection-example.sh)\n- Start slightly edited `connection-example.sh` on peer B. You need to change A to B and swap IP addresses.\n\n### Setup systemd service for server (control node)\n\nYou do not need root permissions or any extra software to start control node. You can just build binary:\n\n```sh\n./build.sh -b # see ./build.sh -h for more cross-compilation options\nscp netpunch root@${YOUR_REMOTE_NODE_WITH_PUBLIC_IP}:/usr/local/bin\n```\n\nAnd use service-file like this:\n\n```ini\n[Unit]\nDescription=Network hole punching tool server\nDocumentation=https://github.com/michurin/netpunch\nAfter=network.target nss-lookup.target\n\n[Service]\nUser=nobody\nNoNewPrivileges=true\nExecStart=/usr/local/bin/netpunch -secret SECRET -local :10001\nRestart=on-failure\n\n[Install]\nWantedBy=multi-user.target\n```\n\nYou can put this file to `/etc/systemd/system/netpunch.service`.\n\nDo not forget to do something like this:\n\n```sh\nsystemctl daemon-reload\nsystemctl status netpunch\nsystemctl start netpunch\nsystemctl enable netpunch\njournalctl -u netpunch -f\n```\n\nNow you can say on one peer\n\n```sh\n./netpunch -peer a -secret SECRET -local :5000 -remote ${CONTROL_NODE_IP}:10001\n```\n\nand similar command on another peer\n\n```sh\n./netpunch -peer b -secret SECRET -local :5000 -remote ${CONTROL_NODE_IP}:10001\n```\n\n\u003e [!NOTE]\n\u003e It has to be really *different* peer nodes in `CONTROL_NODE`'s perspective.\n\nMore details and instructions for peer nodes setting are in [connection-example.sh](connection-example.sh).\n\n## Development and contribution\n\n### Key ideas\n\nLet me share the key ideas of this project, before you start deep refactoring of it.\n\nI regard this project as *library* `netpunchlib` and CLI tool, that uses this library.\n\nI believe the library is to do what it has to do only. It means:\n\n- It must be safe and free of goroutines leaking and stuff like that\n- It must not produce any side effects, including logging. You still able to do things like logging, using middleware\n- It must have compact interface\n- It must provide full control and hide all internals\n\nI'm thinking of CLI tool and `main.go` as an example of using this library. It shows clean example of\n\n- logging (how to create your custom middleware)\n- initialization\n- shutting down\n\nAnd I wonder to keep it pure and simple. For this reason, for example, I don't use\nthing like [cobra](https://github.com/spf13/cobra).\n\nIf you wish to extremely improve CLI/UI/UX... if you want to extend functionality of CLI tool, to add more abilities...\nwon't you just consider creating your own project and using `netpunchlib` as a part of it? I, for my part,\nwill be happy to help you to adjust `netpunchlib` for your needs.\n\nBut on the contrary, if you find error in library, if you find leaks, misfeatures, inconvenient things,\nif you find typos, if you want to improve scripts like `build.sh`, if you know how to make examples better,\nyour pull requests are welcome! However, if you are thinking about grand redevelopment, please open issues first.\n\n### Local running\n\nTerminal 1: control node\n\n```sh\n./netpunch -secret x -local :7777\n```\n\nTerminal 2: peer A\n\n```sh\n./netpunch -peer a -secret x -local :5000 -remote localhost:7777\n```\n\nTerminal 3: peer B\n\n```sh\n./netpunch -peer b -secret x -local :5001 -remote localhost:7777\n```\n\nYou will get output on terminal 1 (control node):\n\n```\n2022/04/02 17:40:20.562777 [25399] [info] Start in control mode on :7777\n2022/04/02 17:40:22.675092 [25399] [info] read: \"a\" \u003c- 127.0.0.1:5000\n2022/04/02 17:40:24.725055 [25399] [info] read: \"b\" \u003c- 127.0.0.1:5001\n2022/04/02 17:40:24.725102 [25399] [info] write: \"i|a|127.0.0.1:5000\" -\u003e 127.0.0.1:5001\n```\n\nTerminal 2 (peer A):\n\n```\n2022/04/02 17:40:22.672392 [25400] [a] [info] Start in peer mode on :5000 to server at localhost:7777\n2022/04/02 17:40:22.674964 [25400] [a] [info] write: \"a\" -\u003e 127.0.0.1:7777\n2022/04/02 17:40:24.725239 [25400] [a] [info] read: \"x\" \u003c- 127.0.0.1:5001\n2022/04/02 17:40:24.725291 [25400] [a] [info] write: \"y\" -\u003e 127.0.0.1:5001\n2022/04/02 17:40:24.725411 [25400] [a] [info] read: \"z\" \u003c- 127.0.0.1:5001\n2022/04/02 17:40:24.725451 [25400] [a] [info] close: ok\nLADDR/LHOST/LPORT/RADDR/RHOST/RPORT: :5000 n/a 5000 127.0.0.1:5001 127.0.0.1 5001\n```\n\nTerminal 3 (peer B):\n\n```\n2022/04/02 17:40:24.724163 [25401] [b] [info] Start in peer mode on :5001 to server at localhost:7777\n2022/04/02 17:40:24.725012 [25401] [b] [info] write: \"b\" -\u003e 127.0.0.1:7777\n2022/04/02 17:40:24.725135 [25401] [b] [info] read: \"i|a|127.0.0.1:5000\" \u003c- 127.0.0.1:7777\n2022/04/02 17:40:24.725174 [25401] [b] [info] write: \"x\" -\u003e 127.0.0.1:5000\n2022/04/02 17:40:24.725342 [25401] [b] [info] read: \"y\" \u003c- 127.0.0.1:5000\n2022/04/02 17:40:24.725378 [25401] [b] [info] write: \"z\" -\u003e 127.0.0.1:5000\n2022/04/02 17:40:24.775912 [25401] [b] [info] write: \"z\" -\u003e 127.0.0.1:5000\n2022/04/02 17:40:24.826117 [25401] [b] [info] write: \"z\" -\u003e 127.0.0.1:5000\n2022/04/02 17:40:24.876327 [25401] [b] [info] write: \"z\" -\u003e 127.0.0.1:5000\n2022/04/02 17:40:24.927088 [25401] [b] [info] write: \"z\" -\u003e 127.0.0.1:5000\n2022/04/02 17:40:24.927283 [25401] [b] [info] close: ok\nLADDR/LHOST/LPORT/RADDR/RHOST/RPORT: :5001 n/a 5001 127.0.0.1:5000 127.0.0.1 5000\n```\n\nIt is easy to understand this log messages. The first letter shows the type of message:\n- `a` and `b` announce corresponding peer on control host\n- `i` (with additional data) is an information on opposite peer from control node\n- `x` is \"ping\" (can be seen as SYN)\n- `y` is \"pong\" (can be seen as SYN+ACK)\n- `z` is \"close\" (can be seen as ACK)\n\n### Roadmap\n\n- Docs\n- More tests and code coverage\n\n### Known issues\n\n- The same private network: in some cases, netpunch won't work if both peers are sitting behind the same NAT\n- MS Windows: nobody yet knows whether netpunch works on MS Windows. Please, let me know, if you do\n- Not perfect diagnostics in case secrets mismatched: if secrets are mismatched it appears like a fake message with corresponding error. Slightly hackish and ugly\n- Client-client protocol is extremely simple: according to the design you are able to link `a` and `b`, `c` and `d` and so on. However if `a` and `c` have the same address and port (by accident) you are able to connect `a` and `d`. Because client `d` can not distinguish `a` and `c`. It can confusing, but I don't think it gives grounds for ruining of simplicity of code and contract\n\n### Internals\n\n```mermaid\nsequenceDiagram\n    actor A as Node A\u003cbr/\u003e192.168.0.10\n    participant GA as Gateway A\u003cbr/\u003epriv: 192.168.0.1\u003cbr/\u003epub: 1.1.1.1\n    participant S as Server\u003cbr/\u003e4.4.4.4\n    participant GB as Gateway B\u003cbr/\u003epriv: 10.0.0.1\u003cbr/\u003epub: 2.2.2.2\n    actor B as Node B\u003cbr/\u003e10.0.0.111\n\n    A -\u003e\u003e GA: from: 192.168.0.10:1001\u003cbr/\u003eto: 4.4.4.4:1000\n    GA -\u003e\u003e S: from: 1.1.1.1:X\n    S -\u003e\u003e S: store 1.1.1.1:X\u003cbr/\u003e(nothing to response yet)\n\n    B -\u003e\u003e GB: from: 10.0.0.111:1002\u003cbr/\u003eto: 4.4.4.4:1000\n    GB -\u003e\u003e S: from: 2.2.2.2:Y\n    S -\u003e\u003e S: store 2.2.2.2:Y and response \"1.1.1.1:X\"\n    S -\u003e\u003e B: data: \"1.1.1.1:X\"\n    B -\u003e\u003e B: start pinging 1.1.1.1:X from :1002\u003cbr/\u003e(see below)\n\n    A -\u003e\u003e GA: next try\n    GA -\u003e\u003e S: next try\n    S -\u003e\u003e S: now we are able to send \"2.2.2.2:Y\" to Node A\n    S -\u003e\u003e A: data: \"2.2.2.2:Y\"\n    A -\u003e\u003e A: start pinging 2.2.2.2:Y from :1001\n\n    B -X GA: \"PING\" to: \"1.1.1.1:X\"\u003cbr/\u003e(until we get \"PONG\" or \"CLOSE\")\n    A -X GB: \"PING\" to: \"2.2.2.2:Y\"\u003cbr/\u003e(until we get \"PONG\" or \"CLOSE\")\n    B -\u003e\u003e A: \"PING\"\n    A -\u003e\u003e B: \"PONG\" (with retries too)\n    B -\u003e\u003e B: stop listning on\u003cbr/\u003efirst \"PONG\"\n    B -\u003e\u003e A: \"CLOSE\" (with retries)\u003cbr/\u003eFirst \"CLOSE\" stops A\n    A -\u003e\u003e A: stop listning and exit\u003cbr/\u003eafter first \"CLOSE\"\n    B -\u003e\u003e B: exit after emitting all \"CLOSE\"\n```\n\nThere are two phases:\n\n- IP discover, using server with public IP (`4.4.4.4` on diagram) and\n- drilling, using `PING`-`PONG`-`CLOSE` communication\n\nThe PING-PONG-CLOSE approach is very similar to SYN-SYNACK-ACK. The\nfinal phase, when we send all CLOSE packets, is similar to TIME-WAIT.\n\n### Related links\n\n#### Documentation\n\n- [Peer-to-Peer Communication Across Network Address Translators](https://bford.info/pub/net/p2pnat/): fundamental work on P2P drilling\n- [Setup OpenVPN](https://ubuntu.com/server/docs/service-openvpn)\n\n#### Similar projects\n\n- \u003chttps://github.com/yinheli/udppunch\u003e\n- \u003chttps://github.com/malcolmseyd/natpunch-go\u003e\n- \u003chttps://git.zx2c4.com/wireguard-tools/tree/contrib/nat-hole-punching/\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmichurin%2Fnetpunch","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmichurin%2Fnetpunch","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmichurin%2Fnetpunch/lists"}