cabal-version: 3.0 name: vpn-router version: 0.0.1 synopsis: Switch VPN with web interface for LAN description: vpn-router is a service with the web interface allowing users of a local network to control VPN bypass from their devices. The service is tested with AmneziaVPN 4.8.10. == Motivation #motivation# It is convinient if the whole WiFi network is connected through VPN, but user might not access some resources sometimes. Having two networks deployed might be an option, though destop stations usually connect through the Ethernet cable, and such approch doubles the number of WiFi routers. Hopping between WiFi networks might not be as ergonomic as it should be due to bugs in the connectivity check in Android and Windows. == Installation #installation# There are several ways to install the app: - with conventional Haskell tools directly - nix-build - download the statically link version of from github - nixos module === NixOS module #nixos-module# 1. Copy to @\/etc\/nixos@. 2. Modify @\/etc\/nixos\/configuration.nix@ as follows: > imports = > [ # ... ./hardware-configuration.nix > ./vpn-router.nix > ]; > programs = { > vpn-router = { > # the service will try to detect gateway and dev automatically if not specified > # gateway = "192.168.1.1"; > # dev = "wlp2s0"; > # port = 3000; > enable = true; > }; > }; Update configuration and check the new service: > nixos-rebuild switch > systemctl status "vpn-router.service" Once the service is running open link http:\/\/my-router:3000\/ on device other than the router. There is a simple UI available with a toggle button to control the VPN bypass. +----------------------------------+-----------------------------------+ | on | off | +==================================+===================================+ | | | +----------------------------------+-----------------------------------+ The service can be stopped, because it only adjusts routing options in the Linux kernel, but at every start all settings related to the routing table and the packet mark specified in configuration will be cleared. === Manual configuration #manual-configuration# NixOS module provides a service ready to go, but the standalone binary can launched without configuration under sudo or by a regular user after setting proper to access @ip@ and @iptables@. The nixified version is shipped with these tools, but static elf assumes that host has these networking apps pre-installed. > Usage: vpn-router run [-d|--dev ARG] [-g|--gateway ARG] [-t|--routing-table ARG] > [-m|--packet-mark ARG] [-p|--port PORT] > > launch the service exposed over HTTP > > Available options: > -d,--dev ARG network device name connected to the Internet > (default: "wlp2s0") > -g,--gateway ARG network device name connected to the Internet > (default: 192.168.1.1) > -t,--routing-table ARG routing table id (default: 7) > -m,--packet-mark ARG packet mark (default: 2) > -p,--port PORT HTTP port to listen (default: 3000) > -h,--help Show this help text Default values for gateway and device are dynamically detected. == Development environment #development-environment# HLS should be available inside the dev environment. > $ nix-shell > $ emacs src/VpnRouter/Net.hs & > $ cabal build > $ cabal test > $ nix-build > $ sudo ./result/bin/vpn-router run == Static linking #static-linking# Static is not enabled by default, because GitHub CI job times out. > nix-build --arg staticBuild true > # faster build on beefy machine > nix-build --cores 20 -j 20 --arg staticBuild true homepage: http://github.com/yaitskov/vpn-router license: BSD-3-Clause license-file: LICENSE author: Daniil Iaitskov maintainer: dyaitskov@gmail.com copyright: Daniil Iaitkov 2026 category: System build-type: Simple bug-reports: https://github.com/yaitskov/vpn-router/issues extra-doc-files: changelog.md tested-with: GHC == 9.12.2 source-repository head type: git location: https://github.com/yaitskov/vpn-router.git common base default-language: GHC2024 ghc-options: -Wall default-extensions: DefaultSignatures NoImplicitPrelude OverloadedStrings TemplateHaskell build-depends: base >=4.7 && < 5 , optparse-applicative < 1 , relude >= 1.2.2 && < 2 , tagged < 1 , unliftio < 1 , yesod-core < 1.8 library import: base hs-source-dirs: src exposed-modules: VpnRouter.App VpnRouter.Bash VpnRouter.CmdArgs VpnRouter.CmdRun VpnRouter.Net VpnRouter.Net.Iptables VpnRouter.Net.IpTool VpnRouter.Net.Types VpnRouter.Page VpnRouter.Prelude other-modules: Paths_vpn_router autogen-modules: Paths_vpn_router build-depends: , blaze-markup < 1 , conduit < 2 , conduit-extra < 2 , network < 4 , regex-tdfa < 2 , template-haskell < 3 , trace-embrace < 2 , typelits-printf < 1 , wai < 4 test-suite test import: base type: exitcode-stdio-1.0 main-is: Driver.hs other-modules: VpnRouter.Test.Net Discovery hs-source-dirs: test ghc-options: -Wall -main-is Driver build-depends: , vpn-router , QuickCheck , tasty , tasty-discover , tasty-hunit , tasty-quickcheck executable vpn-router import: base ghc-options: -Wall main-is: VpnRouter.hs hs-source-dirs: app build-depends: , vpn-router