{"id":42923294,"url":"https://github.com/fps/0vpn","last_synced_at":"2026-01-30T18:02:46.794Z","repository":{"id":47263881,"uuid":"515988844","full_name":"fps/0vpn","owner":"fps","description":"An experiment in making a wireguard VPN setup super easy (almost 0-conf)","archived":false,"fork":false,"pushed_at":"2024-05-13T18:12:37.000Z","size":115,"stargazers_count":5,"open_issues_count":2,"forks_count":0,"subscribers_count":3,"default_branch":"master","last_synced_at":"2024-05-13T19:33:09.966Z","etag":null,"topics":["linux","vpn","wireguard"],"latest_commit_sha":null,"homepage":"","language":"Shell","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/fps.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-07-20T13:15:29.000Z","updated_at":"2024-05-13T18:12:41.000Z","dependencies_parsed_at":"2024-04-22T13:03:05.903Z","dependency_job_id":"aae7c7b3-ac25-40ef-bd58-9c9f2bb06a3e","html_url":"https://github.com/fps/0vpn","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/fps/0vpn","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fps%2F0vpn","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fps%2F0vpn/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fps%2F0vpn/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fps%2F0vpn/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/fps","download_url":"https://codeload.github.com/fps/0vpn/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fps%2F0vpn/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28917033,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-30T16:37:38.804Z","status":"ssl_error","status_checked_at":"2026-01-30T16:37:37.878Z","response_time":66,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"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":["linux","vpn","wireguard"],"created_at":"2026-01-30T18:02:46.181Z","updated_at":"2026-01-30T18:02:46.789Z","avatar_url":"https://github.com/fps.png","language":"Shell","funding_links":[],"categories":[],"sub_categories":[],"readme":"# 0vpn\n\nAn experiment in making a wireguard VPN setup super easy (almost 0-conf). The only supported topology right now is the server-and-clients (a.k.a. root-and-leafs, hub-and-spokes) topology. This is mainly useful if you just need a VPN for your personal machines and you have a cheap server available on the internet (the smallest virtual private server (VPS) should be fine.) Since all your machines are under your control it is easy to have a shared secret on all of them. This shared secret is a private key from which all other privae and public keys are derived via mixing in names.\n\n## How does it work?\n\nWe give up security for convenience:\n\n* There is a central \"master\" private key from which all other keys are derived mixing in the nodes' names. This master key can (optionally) be derived from a password/phrase using \u003ccode\u003e0vpn-tool key-from-password \u003e keyfile\u003c/code\u003e, which reads the password from \u003ccode\u003estdin\u003c/code\u003e.\n* Clients can dynamically announce the wish to partake in the VPN by sending their node name and their (derived) public key. The server listens on an extra UDP port for these announcements.\n* The server can rederive the client's key and check for the correctness of the transmitted public key.\n* If those check out the client's public key is added to the peers list.\n* The VPN uses IPv6 exclusively. \n* The network prefix is derived from the private key by hashing with \u003ccode\u003esha256sum\u003c/code\u003e.\n* IP addresses are derived from the node's names via hashing with \u003ccode\u003esha256sum\u003c/code\u003e as well. So the names must be unique. If \n* For non linux clients that only have a \"vanilla\" wireguard \"app\" we provide ready to use .cfg files (these are written to the TMPDIR path reported during startup of \u003ccode\u003e0vpn-root\u003c/code\u003e and can be used with the equivalents of wg-quick.)\n* We run an instance of dnsmasq on the server which resolves clients in the \"internal\" TLD (the TLD can be configured as well).\n\n# Requirements\n\n## On the server\n\n* netcat-openbsd (it won't work with the traditional netcat)\n* dnsmasq\n* wireguard-tools\n* bash\n* core utilities like cat, kill, mktemp, etc..\n\n## On dynamic clients\n\n* wireguard-tools\n* bash\n* core utilities like cat, kill, mktemp, etc..\n* systemd-resolved \n\n## On static clients\n\n* wireguard in some form or other (wireguard-android, wireguard-windows)\n\n# Howto\n\n## General setup\n\nBuild the helper tool on all machines that want to take part in dynamic client setup (this includes AT LEAST the server):\n\n\u003cpre\u003ego build\u003c/pre\u003e\n\nThis should have produced a binary called \u003ccode\u003e0vpn-tool\u003c/code\u003e \n\nGenerate a master private key:\n\n\u003cpre\u003ewg genkey \u003e private\u003c/pre\u003e\n\nGuard this key carefully. It is used to derive all other private keys.\n\nAlternatively derive the master private key from a password:\n\n\u003cpre\u003e./0vpn-tool key-from-password \u003e private\u003c/pre\u003e\n\nEnter the password.\n\n## On the server\n\n### Running\n\nIn the simplest case, just run:\n\n\u003cpre\u003e0vpn-server --keyfile [file] --server-name [name] --server-hostname [hostname] --static-clients \"client_a client_b\"\u003c/pre\u003e\n\nwhere:\n\nNote that this requires privileges to create and configure the wireguard device.\n\nThe script will have created config files for every static leaf. In the case of the example config that would be \u003ccode\u003eclient_a.cfg\u003c/code\u003e and \u003ccode\u003eclient_b.cfg\u003c/code\u003e. You can generate a QR code and display it in the terminal with\n\n\u003cpre\u003e0vpn-show-qr client_a.cfg\u003c/pre\u003e\n\nOn android you can just create a new tunnel directly from this QR code and things should work.\n\nNote that this requires privileges to configure the wireguard device.\nNote that for this to work you need the openbsd netcat version, as it's much less broken than the default debian installed netcat (PR's welcome to make this a proper daemon.)\n\n## On each dynamic client\n\n### Running\n\n\u003cpre\u003e0vpn-client --keyfile [file] --server-name [name] --server-hostname [hostname] --client-name [name]\u003c/pre\u003e\n\nNote that the value of the \u003ccode\u003e--server-name\u003c/code\u003e option has to be identical to the one passed to the server.\n\n## All client and server options:\n\n\u003cpre\u003e\nCommon available options:\n  --server-hostname [hostname]     (default: \"localhost\")\n  --server-name [name]             (default: \"ogfx100\")\n  --wireguard-port [port]          (default: \"4242\")\n  --announce-port [port]           (default: \"4243\")\n  --dns-port [port]                (default: \"53\")\n  --keyfile [filename]             (default: \"key\")\n  --tld [domain-name]              (default: \"internal\")\n  --wireguard-device [device-name] (default: \"wg0\")\n\nServer-only options:\n  --static-clients [list of names] (default: \"\")\n\nClient-only options:\n  --client-name [name]             (default: \"ogfx100\")\n  --announce-interval [seconds]    (default: \"30\")\n\u003c/pre\u003e\n\nNote that some options like \u003ccode\u003e--server-name\u003c/code\u003e and \u003ccode\u003e--client-name\u003c/code\u003e are set to \u003ccode\u003e$(hostname --short)\u003c/code\u003e per default (in this case \u003ccode\u003eogfx100\u003c/code\u003e). \n\n# Done\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffps%2F0vpn","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffps%2F0vpn","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffps%2F0vpn/lists"}