Skip to content

Commit

Permalink
Merge pull request Place1#134 from DasSkelett/feature/block-inter-dev…
Browse files Browse the repository at this point in the history
…ice-traffic
  • Loading branch information
DasSkelett authored Mar 24, 2022
2 parents 04a37c7 + aa88ab7 commit 2d97464
Show file tree
Hide file tree
Showing 4 changed files with 78 additions and 36 deletions.
17 changes: 14 additions & 3 deletions cmd/serve/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,13 @@ func Register(app *kingpin.Application) *servecmd {
cli.Flag("wireguard-interface", "Set the wireguard interface name").Default("wg0").Envar("WG_WIREGUARD_INTERFACE").StringVar(&cmd.AppConfig.WireGuard.Interface)
cli.Flag("wireguard-private-key", "Wireguard private key").Envar("WG_WIREGUARD_PRIVATE_KEY").StringVar(&cmd.AppConfig.WireGuard.PrivateKey)
cli.Flag("wireguard-port", "The port that the Wireguard server will listen on").Envar("WG_WIREGUARD_PORT").Default("51820").IntVar(&cmd.AppConfig.WireGuard.Port)
cli.Flag("vpn-allowed-ips", "A list of networks that VPN clients will be allowed to connect to via the VPN").Envar("WG_VPN_ALLOWED_IPS").Default("0.0.0.0/0", "::/0").StringsVar(&cmd.AppConfig.VPN.AllowedIPs)
cli.Flag("vpn-cidr", "The network CIDR for the VPN").Envar("WG_VPN_CIDR").Default("10.44.0.0/24").StringVar(&cmd.AppConfig.VPN.CIDR)
cli.Flag("vpn-cidrv6", "The IPv6 network CIDR for the VPN").Envar("WG_VPN_CIDRV6").Default("fd48:4c4:7aa9::/64").StringVar(&cmd.AppConfig.VPN.CIDRv6)
cli.Flag("vpn-gateway-interface", "The gateway network interface (i.e. eth0)").Envar("WG_VPN_GATEWAY_INTERFACE").Default(detectDefaultInterface()).StringVar(&cmd.AppConfig.VPN.GatewayInterface)
cli.Flag("vpn-nat44-enabled", "Enable or disable NAT of IPv6 traffic leaving through the gateway").Envar("WG_IPV4_NAT_ENABLED").Default("true").BoolVar(&cmd.AppConfig.VPN.NAT44)
cli.Flag("vpn-nat66-enabled", "Enable or disable NAT of IPv6 traffic leaving through the gateway").Envar("WG_IPV6_NAT_ENABLED").Default("true").BoolVar(&cmd.AppConfig.VPN.NAT66)
cli.Flag("vpn-gateway-interface", "The gateway network interface (i.e. eth0)").Envar("WG_VPN_GATEWAY_INTERFACE").Default(detectDefaultInterface()).StringVar(&cmd.AppConfig.VPN.GatewayInterface)
cli.Flag("vpn-allowed-ips", "A list of networks that VPN clients will be allowed to connect to via the VPN").Envar("WG_VPN_ALLOWED_IPS").Default("0.0.0.0/0", "::/0").StringsVar(&cmd.AppConfig.VPN.AllowedIPs)
cli.Flag("vpn-client-isolation", "Block or allow traffic between client devices").Envar("WG_VPN_CLIENT_ISOLATION").Default("false").BoolVar(&cmd.AppConfig.VPN.ClientIsolation)
cli.Flag("dns-enabled", "Enable or disable the embedded dns proxy server (useful for development)").Envar("WG_DNS_ENABLED").Default("true").BoolVar(&cmd.AppConfig.DNS.Enabled)
cli.Flag("dns-upstream", "An upstream DNS server to proxy DNS traffic to. Defaults to resolvconf with Cloudflare DNS as fallback").Envar("WG_DNS_UPSTREAM").StringsVar(&cmd.AppConfig.DNS.Upstream)
cli.Flag("dns-domain", "A domain to serve configured device names authoritatively").Envar("WG_DNS_DOMAIN").StringVar(&cmd.AppConfig.DNS.Domain)
Expand Down Expand Up @@ -141,7 +142,17 @@ func (cmd *servecmd) Run() {

logrus.Infof("wireguard VPN network is %s", network.StringJoinIPNets(vpnip, vpnipv6))

if err := network.ConfigureForwarding(conf.VPN.GatewayInterface, conf.VPN.CIDR, conf.VPN.CIDRv6, conf.VPN.NAT44, conf.VPN.NAT66, conf.VPN.AllowedIPs); err != nil {
options := network.ForwardingOptions{
GatewayIface: conf.VPN.GatewayInterface,
CIDR: conf.VPN.CIDR,
CIDRv6: conf.VPN.CIDRv6,
NAT44: conf.VPN.NAT44,
NAT66: conf.VPN.NAT66,
ClientIsolation: conf.VPN.ClientIsolation,
AllowedIPs: conf.VPN.AllowedIPs,
}

if err := network.ConfigureForwarding(options); err != nil {
logrus.Error(err)
return
}
Expand Down
2 changes: 2 additions & 0 deletions docs/2-configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ Here's what you can configure:
| `WG_VPN_CIDR` | `--vpn-cidr` | `vpn.cidr` | | `10.44.0.0/24` | The VPN IPv4 network range. VPN clients will be assigned IP addresses in this range. Set to `0` to disable IPv4. |
| `WG_IPV4_NAT_ENABLED` | `--vpn-nat44-enabled` | `vpn.nat44` | | `true` | Disables NAT for IPv4 |
| `WG_IPV6_NAT_ENABLED` | `--vpn-nat66-enabled` | `vpn.nat66` | | `true` | Disables NAT for IPv6 |
| `WG_VPN_CLIENT_ISOLATION` | `--vpn-client-isolation` | `vpn.clientIsolation` | | `false` | BLock or allow traffic between client devices (client isolation) |
| `WG_VPN_CIDRV6` | `--vpn-cidrv6` | `vpn.cidrv6` | | `fd48:4c4:7aa9::/64` | The VPN IPv6 network range. VPN clients will be assigned IP addresses in this range. Set to `0` to disable IPv6. |
| `WG_VPN_GATEWAY_INTERFACE` | `--vpn-gateway-interface` | `vpn.gatewayInterface` | | _default gateway interface (e.g. eth0)_ | The VPN gateway interface. VPN client traffic will be forwarded to this interface. |
| `WG_VPN_ALLOWED_IPS` | `--vpn-allowed-ips` | `vpn.allowedIPs` | | `0.0.0.0/0, ::/0` | Allowed IPs that clients may route through this VPN. This will be set in the client's WireGuard connection file and routing is also enforced by the server using iptables. |
Expand All @@ -54,5 +55,6 @@ wireguard:
privateKey: "<some-key>"
dns:
upstream:
- "2001:4860:4860::8888"
- "8.8.8.8"
```
25 changes: 14 additions & 11 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,12 @@ type AppConfig struct {
} `yaml:"wireguard"`
// Configure VPN related settings (networking)
VPN struct {
// The "AllowedIPs" for VPN clients.
// This value will be included in client config
// files and in server-side iptable rules
// to enforce network access.
// defaults to ["0.0.0.0/0", "::/0"]
AllowedIPs []string `yaml:"allowedIPs"`
// CIDR configures a network address space
// that client (WireGuard peers) will be allocated
// an IP address from
Expand All @@ -64,6 +70,11 @@ type AppConfig struct {
// an IP address from
// defaults to fd48:4c4:7aa9::/64
CIDRv6 string `yaml:"cidrv6"`
// GatewayInterface will be used in iptable forwarding
// rules that send VPN traffic from clients to this interface
// Most use-cases will want this interface to have access
// to the outside internet
GatewayInterface string `yaml:"gatewayInterface"`
// NAT44 configures whether IPv4 traffic leaving
// through the GatewayInterface should be masqueraded
// defaults to true
Expand All @@ -73,17 +84,9 @@ type AppConfig struct {
// masqueraded like IPv4 traffic
// defaults to true
NAT66 bool `yaml:"nat66"`
// GatewayInterface will be used in iptable forwarding
// rules that send VPN traffic from clients to this interface
// Most use-cases will want this interface to have access
// to the outside internet
GatewayInterface string `yaml:"gatewayInterface"`
// The "AllowedIPs" for VPN clients.
// This value will be included in client config
// files and in server-side iptable rules
// to enforce network access.
// defaults to ["0.0.0.0/0", "::/0"]
AllowedIPs []string `yaml:"allowedIPs"`
// ClientIsolation configures whether traffic between client devices will be blocked or allowed
// defaults to false
ClientIsolation bool `yaml:"clientIsolation"`
} `yaml:"vpn"`
// Configure the embedded DNS server
DNS struct {
Expand Down
70 changes: 48 additions & 22 deletions internal/network/network.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,14 +65,25 @@ func SplitAddresses(addresses string) []string {
return split
}

func ConfigureForwarding(gatewayIface string, cidr, cidrv6 string, nat44, nat66 bool, allowedIPs []string) error {
// ForwardingOptions contains all options used for configuring the firewall rules
type ForwardingOptions struct {
GatewayIface string
CIDR, CIDRv6 string
NAT44, NAT66 bool
ClientIsolation bool
AllowedIPs []string
allowedIPv4s []string
allowedIPv6s []string
}

func ConfigureForwarding(options ForwardingOptions) error {
// Networking configuration (iptables) configuration
// to ensure that traffic from clients of the wireguard interface
// is sent to the provided network interface
allowedIPv4s := make([]string, 0, len(allowedIPs)/2)
allowedIPv6s := make([]string, 0, len(allowedIPs)/2)
allowedIPv4s := make([]string, 0, len(options.AllowedIPs)/2)
allowedIPv6s := make([]string, 0, len(options.AllowedIPs)/2)

for _, allowedCIDR := range allowedIPs {
for _, allowedCIDR := range options.AllowedIPs {
parsedAddress, parsedNetwork, err := net.ParseCIDR(allowedCIDR)
if err != nil {
return errors.Wrap(err, "invalid cidr in AllowedIPs")
Expand All @@ -86,21 +97,23 @@ func ConfigureForwarding(gatewayIface string, cidr, cidrv6 string, nat44, nat66
allowedIPv6s = append(allowedIPv6s, parsedNetwork.String())
}
}
options.allowedIPv4s = allowedIPv4s
options.allowedIPv6s = allowedIPv6s

if cidr != "" {
if err := configureForwardingv4(gatewayIface, cidr, nat44, allowedIPv4s); err != nil {
if options.CIDR != "" {
if err := configureForwardingv4(options); err != nil {
return err
}
}
if cidrv6 != "" {
if err := configureForwardingv6(gatewayIface, cidrv6, nat66, allowedIPv6s); err != nil {
if options.CIDRv6 != "" {
if err := configureForwardingv6(options); err != nil {
return err
}
}
return nil
}

func configureForwardingv4(gatewayIface string, cidr string, nat44 bool, allowedIPs []string) error {
func configureForwardingv4(options ForwardingOptions) error {
ipt, err := iptables.NewWithProtocol(iptables.ProtocolIPv4)
if err != nil {
return errors.Wrap(err, "failed to init iptables")
Expand Down Expand Up @@ -128,27 +141,34 @@ func configureForwardingv4(gatewayIface string, cidr string, nat44 bool, allowed
return errors.Wrap(err, "failed to append POSTROUTING rule to nat chain")
}

for _, allowedCIDR := range allowedIPs {
if err := ipt.AppendUnique("filter", "WG_ACCESS_SERVER_FORWARD", "-s", cidr, "-d", allowedCIDR, "-j", "ACCEPT"); err != nil {
if options.ClientIsolation {
// Reject inter-device traffic
if err := ipt.AppendUnique("filter", "WG_ACCESS_SERVER_FORWARD", "-s", options.CIDR, "-d", options.CIDR, "-j", "REJECT"); err != nil {
return errors.Wrap(err, "failed to set ip tables rule")
}
}
// Accept client traffic for given allowed ips
for _, allowedCIDR := range options.allowedIPv4s {
if err := ipt.AppendUnique("filter", "WG_ACCESS_SERVER_FORWARD", "-s", options.CIDR, "-d", allowedCIDR, "-j", "ACCEPT"); err != nil {
return errors.Wrap(err, "failed to set ip tables rule")
}
}
// And reject everything else
if err := ipt.AppendUnique("filter", "WG_ACCESS_SERVER_FORWARD", "-s", cidr, "-j", "REJECT"); err != nil {
if err := ipt.AppendUnique("filter", "WG_ACCESS_SERVER_FORWARD", "-s", options.CIDR, "-j", "REJECT"); err != nil {
return errors.Wrap(err, "failed to set ip tables rule")
}

if gatewayIface != "" {
if nat44 {
if err := ipt.AppendUnique("nat", "WG_ACCESS_SERVER_POSTROUTING", "-s", cidr, "-o", gatewayIface, "-j", "MASQUERADE"); err != nil {
if options.GatewayIface != "" {
if options.NAT44 {
if err := ipt.AppendUnique("nat", "WG_ACCESS_SERVER_POSTROUTING", "-s", options.CIDR, "-o", options.GatewayIface, "-j", "MASQUERADE"); err != nil {
return errors.Wrap(err, "failed to set ip tables rule")
}
}
}
return nil
}

func configureForwardingv6(gatewayIface string, cidrv6 string, nat66 bool, allowedIPs []string) error {
func configureForwardingv6(options ForwardingOptions) error {
ipt, err := iptables.NewWithProtocol(iptables.ProtocolIPv6)
if err != nil {
return errors.Wrap(err, "failed to init ip6tables")
Expand All @@ -174,20 +194,26 @@ func configureForwardingv6(gatewayIface string, cidrv6 string, nat66 bool, allow
return errors.Wrap(err, "failed to append POSTROUTING rule to nat chain")
}

if options.ClientIsolation {
// Reject inter-device traffic
if err := ipt.AppendUnique("filter", "WG_ACCESS_SERVER_FORWARD", "-s", options.CIDRv6, "-d", options.CIDRv6, "-j", "REJECT"); err != nil {
return errors.Wrap(err, "failed to set ip tables rule")
}
}
// Accept client traffic for given allowed ips
for _, allowedCIDR := range allowedIPs {
if err := ipt.AppendUnique("filter", "WG_ACCESS_SERVER_FORWARD", "-s", cidrv6, "-d", allowedCIDR, "-j", "ACCEPT"); err != nil {
for _, allowedCIDR := range options.allowedIPv6s {
if err := ipt.AppendUnique("filter", "WG_ACCESS_SERVER_FORWARD", "-s", options.CIDRv6, "-d", allowedCIDR, "-j", "ACCEPT"); err != nil {
return errors.Wrap(err, "failed to set ip tables rule")
}
}
// And reject everything else
if err := ipt.AppendUnique("filter", "WG_ACCESS_SERVER_FORWARD", "-s", cidrv6, "-j", "REJECT"); err != nil {
if err := ipt.AppendUnique("filter", "WG_ACCESS_SERVER_FORWARD", "-s", options.CIDRv6, "-j", "REJECT"); err != nil {
return errors.Wrap(err, "failed to set ip tables rule")
}

if gatewayIface != "" {
if nat66 {
if err := ipt.AppendUnique("nat", "WG_ACCESS_SERVER_POSTROUTING", "-s", cidrv6, "-o", gatewayIface, "-j", "MASQUERADE"); err != nil {
if options.GatewayIface != "" {
if options.NAT66 {
if err := ipt.AppendUnique("nat", "WG_ACCESS_SERVER_POSTROUTING", "-s", options.CIDRv6, "-o", options.GatewayIface, "-j", "MASQUERADE"); err != nil {
return errors.Wrap(err, "failed to set ip tables rule")
}
}
Expand Down

0 comments on commit 2d97464

Please sign in to comment.