Dev.to Β· 5 min read

πŸ” A WireGuard GitHub Action That Actually Tells You When the Tunnel Is Dead

πŸ” A WireGuard GitHub Action That Actually Tells You When the Tunnel Is Dead

Your CI job needs to reach something private β€” a staging database in a VPC, an internal registry, a deploy target behind a firewall. The usual answers are bad: whitelist GitHub's entire runner IP range, or run a self-hosted runner just for network access. Better idea: put the runner on your private network for the length of the job. πŸ‘‰ ankurk91/wireguard-action 🧭 What is WireGuard? A modern VPN protocol, and a small one β€” ~4,000 lines of code against OpenVPN's hundreds of thousands. It lives in the Linux kernel (mainline since 5.6), so it's fast. Its crypto isn't configurable, so there's nothing to downgrade. And config is just keys, SSH-style: [Interface] PrivateKey = Address = 10.0.0.2/32 [Peer] PublicKey = AllowedIPs = 0.0.0.0/0 Endpoint = vpn.example.com:51820 That's the whole thing. Ephemeral, keyed, instant to bring up β€” a great fit for CI. 😀 Why another action? I went looking and came away unhappy. Not naming names, but the same problems kept showing up: actions pinned to end-of-life node12/node16, docs that stop at a YAML snippet, huge bundled node_modules running as root, no cleanup step, and tunnel state dumped into job logs. The big one: wg-quick up exits 0 even when your peer is unreachable. It creates the interface, adds the routes, reports success β€” and carries nothing. Your job then fails three steps later with a timeout that looks unrelated, and you burn an afternoon. ✨ Features 🩺 Verifies the handshake. After connecting, it sends real traffic and checks wg show for a completed handshake. Dead tunnel β†’ the step fails here, with a message telling you to check Endpoint, keys, and UDP reachability. Skipped for split tunnels, where there's nothing meaningful to test. 🧼 Cleans up automatically. A post-if: always() step brings the interface down and deletes the config file on success, failure, or cancellation. No disconnect step to remember β€” and no private key left behind on a self-hosted runner. πŸ“¦ Zero dependencies. No node_modules, no bundled dist/. A ten-line node24 shim over readable, linted bash. You can audit the whole thing in five minutes β€” which matters for something running sudo on your runner. πŸ” Opt-in diagnostics. diagnostics: true prints wg show, addresses, routes, and your public IP before and after. Off by default, because that describes your network and job logs reach more people than your secrets do. πŸ“š Real docs. A README and a TROUBLESHOOTING.md written from failures I actually hit β€” including the two that bite everyone: GitHub runners have no IPv6 (strip IPv6 from your config or the tunnel won't start), and AllowedIPs = 0.0.0.0/0 routes the runner's own connection to GitHub through your VPN (if your VPN blocks that, the job hangs). πŸš€ Getting started 1. Grab a client config from your WireGuard server β€” the whole wg0.conf. 2. Add it as a repository secret called WIREGUARD_CONFIG, pasting the entire file. ⚠️ Never commit the config or inline it β€” it holds your private key. 3. Add one step: jobs: deploy: runs-on: ubuntu-latest steps: - name: Connect to WireGuard VPN uses: ankurk91/wireguard-action@v1 with: config: ${{ secrets.WIREGUARD_CONFIG }} # πŸ‘‡ Everything below is routed through the VPN. - name: Talk to something private run: curl -sf http://10.0.0.50:8080/health That's it β€” there is no disconnect step to add. Input Required Default Description config βœ… β€” Full config contents. Always from a secret. interface β€” wg0 Written to /etc/wireguard/.conf. diagnostics β€” false Print tunnel state and routes to the log. Requires an Ubuntu runner and an IPv4-only config. πŸ§ͺ Tested for real Not "I ran it once." CI stands up a real linuxserver/wireguard server in Docker, in its own network namespace, and connects to it β€” a genuine tunnel, no mocks. It then asserts the interface is up, the config is mode 600, both ends agree on the client's public key, ICMP crosses the tunnel, and the byte counters are non-zero. That runs 8Γ— per commit: ubuntu-24.04 and ubuntu-26.04, on x86_64 and ARM, with diagnostics both on and off β€” plus shellcheck and checkbashisms, and a monthly schedule so runner-image drift is caught by CI instead of by you on a Friday. It's also in use in real projects; the handshake check exists because of one. πŸ”¬ The test workflow. ⚑ Performance & πŸ›‘οΈ Security It runs before everything else in your job, so it stays light: no apt-get update on the happy path (it installs from the image's existing indexes and only refreshes if that fails), --no-install-recommends, skips the install entirely if wg-quick is present, and waits for the dpkg lock instead of flaking when unattended-upgrades holds it. The handshake check is bounded to 5s. On the security side: the config is chmod 600 (asserted in CI), the interface input is validated against wg-quick's own character class so it can't escape /etc/wireguard, the key is removed on every exit path, diagnostics are opt-in, and the supply chain is empty. Pin @v1 β€” or a full SHA if your threat model asks for it. πŸ”— Links 🧩 The action Β· README Β· Troubleshooting Β· Issues 🌍 WireGuard Β· whitepaper Β· wg-quick(8) πŸƒ GitHub runner images Β· βš–οΈ MIT ⭐ Star it if it saves you an afternoon.

This is a summary aggregated from Dev.to. Read the complete article on the original site:

Read full article at Dev.to

More Cybersecurity News