ip and mtu updates (linux only)

This commit is contained in:
Christoph Haas 2020-12-18 21:54:57 +01:00
parent a95fe42efe
commit 10defaa2ba
7 changed files with 163 additions and 6 deletions

View File

@ -85,7 +85,7 @@ A detailed description for using this software with a raspberry pi can be found
## What is out of scope
* Generation or application of any `iptables` or `nftables` rules
* Setting up or changing IP-addresses of the WireGuard interface
* Setting up or changing IP-addresses of the WireGuard interface on operating systems other than linux
## Application stack

1
go.mod
View File

@ -10,6 +10,7 @@ require (
github.com/gorilla/sessions v1.2.1 // indirect
github.com/jordan-wright/email v4.0.1-0.20200917010138-e1c00e156980+incompatible
github.com/kelseyhightower/envconfig v1.4.0
github.com/milosgajdos/tenus v0.0.3
github.com/sirupsen/logrus v1.7.0
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
github.com/tatsushid/go-fastping v0.0.0-20160109021039-d7bb493dee3e

View File

@ -4,6 +4,7 @@ import (
"errors"
"os"
"reflect"
"runtime"
"github.com/h44z/wg-portal/internal/wireguard"
@ -91,6 +92,7 @@ func NewConfig() *Config {
cfg.LDAP.BindPass = "SuperSecret"
cfg.WG.DeviceName = "wg0"
cfg.WG.WireGuardConfig = "/etc/wireguard/wg0.conf"
cfg.WG.ManageIPAddresses = true
cfg.AdminLdapGroup = "CN=WireGuardAdmins,OU=_O_IT,DC=COMPANY,DC=LOCAL"
cfg.Email.Host = "127.0.0.1"
cfg.Email.Port = 25
@ -109,5 +111,10 @@ func NewConfig() *Config {
log.Warnf("unable to load environment config: %v", err)
}
if cfg.WG.ManageIPAddresses && runtime.GOOS != "linux" {
log.Warnf("Managing IP addresses only works on linux! Feature disabled.")
cfg.WG.ManageIPAddresses = false
}
return cfg
}

View File

@ -82,8 +82,24 @@ func (s *Server) PostAdminEditInterface(c *gin.Context) {
return
}
// Update interface IP address
if s.config.WG.ManageIPAddresses {
if err := s.wg.SetIPAddress(formDevice.IPs); err != nil {
_ = s.updateFormInSession(c, formDevice)
s.setFlashMessage(c, "Failed to update ip address: "+err.Error(), "danger")
c.Redirect(http.StatusSeeOther, "/admin/device/edit?formerr=update")
}
if err := s.wg.SetMTU(formDevice.Mtu); err != nil {
_ = s.updateFormInSession(c, formDevice)
s.setFlashMessage(c, "Failed to update MTU: "+err.Error(), "danger")
c.Redirect(http.StatusSeeOther, "/admin/device/edit?formerr=update")
}
}
s.setFlashMessage(c, "Changes applied successfully!", "success")
s.setFlashMessage(c, "WireGuard must be restarted to apply ip changes.", "warning")
if !s.config.WG.ManageIPAddresses {
s.setFlashMessage(c, "WireGuard must be restarted to apply ip changes.", "warning")
}
c.Redirect(http.StatusSeeOther, "/admin/device/edit")
}

View File

@ -319,6 +319,18 @@ func (u *UserManager) InitFromCurrentInterface() error {
log.Errorf("failed to init user-manager from device: %v", err)
return err
}
var ipAddresses []string
var mtu int
if u.wg.Cfg.ManageIPAddresses {
if ipAddresses, err = u.wg.GetIPAddress(); err != nil {
log.Errorf("failed to init user-manager from device: %v", err)
return err
}
if mtu, err = u.wg.GetMTU(); err != nil {
log.Errorf("failed to init user-manager from device: %v", err)
return err
}
}
// Check if entries already exist in database, if not create them
for _, peer := range peers {
@ -326,7 +338,7 @@ func (u *UserManager) InitFromCurrentInterface() error {
return err
}
}
if err := u.validateOrCreateDevice(*device); err != nil {
if err := u.validateOrCreateDevice(*device, ipAddresses, mtu); err != nil {
return err
}
@ -366,7 +378,7 @@ func (u *UserManager) validateOrCreateUserForPeer(peer wgtypes.Peer) error {
return nil
}
func (u *UserManager) validateOrCreateDevice(dev wgtypes.Device) error {
func (u *UserManager) validateOrCreateDevice(dev wgtypes.Device, ipAddresses []string, mtu int) error {
device := Device{}
u.db.Where("device_name = ?", dev.Name).FirstOrInit(&device)
@ -377,6 +389,8 @@ func (u *UserManager) validateOrCreateDevice(dev wgtypes.Device) error {
device.ListenPort = dev.ListenPort
device.Mtu = 0
device.PersistentKeepalive = 16 // Default
device.IPsStr = strings.Join(ipAddresses, ", ")
device.Mtu = mtu
res := u.db.Create(&device)
if res.Error != nil {

View File

@ -1,6 +1,7 @@
package wireguard
type Config struct {
DeviceName string `yaml:"device" envconfig:"WG_DEVICE"`
WireGuardConfig string `yaml:"configFile" envconfig:"WG_CONFIG_FILE"` // optional, if set, updates will be written to this file
DeviceName string `yaml:"device" envconfig:"WG_DEVICE"`
WireGuardConfig string `yaml:"configFile" envconfig:"WG_CONFIG_FILE"` // optional, if set, updates will be written to this file
ManageIPAddresses bool `yaml:"manageIPAddresses" envconfig:"MANAGE_IPS"` // handle ip-address setup of interface
}

118
internal/wireguard/net.go Normal file
View File

@ -0,0 +1,118 @@
package wireguard
import (
"fmt"
"net"
"github.com/milosgajdos/tenus"
)
func (m *Manager) GetIPAddress() ([]string, error) {
wgInterface, err := tenus.NewLinkFrom(m.Cfg.DeviceName)
if err != nil {
return nil, fmt.Errorf("could not retrieve WireGuard interface %s: %w", m.Cfg.DeviceName, err)
}
// Get golang net.interface
iface := wgInterface.NetInterface()
if iface == nil { // Not sure if this check is really necessary
return nil, fmt.Errorf("could not retrieve WireGuard net.interface: %w", err)
}
addrs, err := iface.Addrs()
if err != nil {
return nil, fmt.Errorf("could not retrieve WireGuard ip addresses: %w", err)
}
ipAddresses := make([]string, 0, len(addrs))
for _, addr := range addrs {
var ip net.IP
var mask net.IPMask
switch v := addr.(type) {
case *net.IPNet:
ip = v.IP
mask = v.Mask
case *net.IPAddr:
ip = v.IP
mask = ip.DefaultMask()
}
if ip == nil {
continue // something is wrong?
}
maskSize, _ := mask.Size()
cidr := fmt.Sprintf("%s/%d", ip.String(), maskSize)
ipAddresses = append(ipAddresses, cidr)
}
return ipAddresses, nil
}
func (m *Manager) SetIPAddress(cidrs []string) error {
wgInterface, err := tenus.NewLinkFrom(m.Cfg.DeviceName)
if err != nil {
return fmt.Errorf("could not retrieve WireGuard interface %s: %w", m.Cfg.DeviceName, err)
}
// First remove existing IP addresses
existingIPs, err := m.GetIPAddress()
if err != nil {
return err
}
for _, cidr := range existingIPs {
wgIp, wgIpNet, err := net.ParseCIDR(cidr)
if err != nil {
return fmt.Errorf("unable to parse cidr %s: %w", cidr, err)
}
if err := wgInterface.UnsetLinkIp(wgIp, wgIpNet); err != nil {
return fmt.Errorf("failed to unset ip %s: %w", cidr, err)
}
}
// Next set new IP adrresses
for _, cidr := range cidrs {
wgIp, wgIpNet, err := net.ParseCIDR(cidr)
if err != nil {
return fmt.Errorf("unable to parse cidr %s: %w", cidr, err)
}
if err := wgInterface.SetLinkIp(wgIp, wgIpNet); err != nil {
return fmt.Errorf("failed to set ip %s: %w", cidr, err)
}
}
return nil
}
func (m *Manager) GetMTU() (int, error) {
wgInterface, err := tenus.NewLinkFrom(m.Cfg.DeviceName)
if err != nil {
return 0, fmt.Errorf("could not retrieve WireGuard interface %s: %w", m.Cfg.DeviceName, err)
}
// Get golang net.interface
iface := wgInterface.NetInterface()
if iface == nil { // Not sure if this check is really necessary
return 0, fmt.Errorf("could not retrieve WireGuard net.interface: %w", err)
}
return iface.MTU, nil
}
func (m *Manager) SetMTU(mtu int) error {
wgInterface, err := tenus.NewLinkFrom(m.Cfg.DeviceName)
if err != nil {
return fmt.Errorf("could not retrieve WireGuard interface %s: %w", m.Cfg.DeviceName, err)
}
if mtu == 0 {
mtu = 1420 // WireGuard default MTU
}
if err := wgInterface.SetLinkMTU(mtu); err != nil {
return fmt.Errorf("could not set MTU on interface %s: %w", m.Cfg.DeviceName, err)
}
return nil
}