From 40342619e727e81d017528271baa8162d438e566 Mon Sep 17 00:00:00 2001 From: "Quentin McGaw (desktop)" Date: Mon, 13 Sep 2021 00:50:20 +0000 Subject: [PATCH] Maint: dynamically set allowed VPN input ports - Feat: allow to change VPN type at runtime - Feat: allow to change interface name at runtime - Maint: Add cleanup method to cleanup VPN loop on a vpn shutdown - Change: allow VPN inputs ports only when tunnel is up --- cmd/gluetun/main.go | 10 +--------- internal/vpn/cleanup.go | 27 +++++++++++++++++++++++++++ internal/vpn/loop.go | 8 +++++--- internal/vpn/portforward.go | 14 ++++---------- internal/vpn/run.go | 13 +++++-------- internal/vpn/tunnelup.go | 7 +++++++ 6 files changed, 49 insertions(+), 30 deletions(-) create mode 100644 internal/vpn/cleanup.go diff --git a/cmd/gluetun/main.go b/cmd/gluetun/main.go index 3793532c..b8a355cc 100644 --- a/cmd/gluetun/main.go +++ b/cmd/gluetun/main.go @@ -292,14 +292,6 @@ func _main(ctx context.Context, buildInfo models.BuildInformation, } } - for _, vpnPort := range allSettings.Firewall.VPNInputPorts { - vpnIntf := allSettings.VPN.VPNInterface() - err = firewallConf.SetAllowedPort(ctx, vpnPort, vpnIntf) - if err != nil { - return err - } - } - for _, port := range allSettings.Firewall.InputPorts { err = firewallConf.SetAllowedPort(ctx, port, defaultInterface) if err != nil { @@ -360,7 +352,7 @@ func _main(ctx context.Context, buildInfo models.BuildInformation, tickersGroupHandler.Add(pubIPTickerHandler) vpnLogger := logger.NewChild(logging.Settings{Prefix: "vpn: "}) - vpnLooper := vpn.NewLoop(allSettings.VPN, + vpnLooper := vpn.NewLoop(allSettings.VPN, allSettings.Firewall.VPNInputPorts, allServers, ovpnConf, netLinker, firewallConf, routingConf, portForwardLooper, cmder, publicIPLooper, unboundLooper, vpnLogger, httpClient, buildInfo, allSettings.VersionInformation) diff --git a/internal/vpn/cleanup.go b/internal/vpn/cleanup.go new file mode 100644 index 00000000..99ed62de --- /dev/null +++ b/internal/vpn/cleanup.go @@ -0,0 +1,27 @@ +package vpn + +import ( + "context" + "time" + + "github.com/qdm12/gluetun/internal/publicip/models" +) + +func (l *Loop) cleanup(ctx context.Context, pfEnabled bool) { + for _, vpnPort := range l.vpnInputPorts { + err := l.fw.RemoveAllowedPort(ctx, vpnPort) + if err != nil { + l.logger.Error("cannot remove allowed input port from firewall: " + err.Error()) + } + } + + l.publicip.SetData(models.IPInfoData{}) // clear public IP address data + + if pfEnabled { + const pfTimeout = 100 * time.Millisecond + err := l.stopPortForwarding(ctx, pfTimeout) + if err != nil { + l.logger.Error("cannot stop port forwarding: " + err.Error()) + } + } +} diff --git a/internal/vpn/loop.go b/internal/vpn/loop.go index 49885870..48c374a6 100644 --- a/internal/vpn/loop.go +++ b/internal/vpn/loop.go @@ -34,8 +34,9 @@ type Loop struct { statusManager loopstate.Manager state state.Manager // Fixed parameters - buildInfo models.BuildInformation - versionInfo bool + buildInfo models.BuildInformation + versionInfo bool + vpnInputPorts []uint16 // TODO make changeable through stateful firewall // Configurators openvpnConf openvpn.Interface netLinker netlink.NetLinker @@ -67,7 +68,7 @@ const ( defaultBackoffTime = 15 * time.Second ) -func NewLoop(vpnSettings configuration.VPN, +func NewLoop(vpnSettings configuration.VPN, vpnInputPorts []uint16, allServers models.AllServers, openvpnConf openvpn.Interface, netLinker netlink.NetLinker, fw firewallConfigurer, routing routing.VPNGetter, portForward portforward.StartStopper, starter command.Starter, @@ -87,6 +88,7 @@ func NewLoop(vpnSettings configuration.VPN, state: state, buildInfo: buildInfo, versionInfo: versionInfo, + vpnInputPorts: vpnInputPorts, openvpnConf: openvpnConf, netLinker: netLinker, fw: fw, diff --git a/internal/vpn/portforward.go b/internal/vpn/portforward.go index c7a6f519..2ff7c139 100644 --- a/internal/vpn/portforward.go +++ b/internal/vpn/portforward.go @@ -40,20 +40,14 @@ func (l *Loop) startPortForwarding(ctx context.Context, data tunnelUpData) (err return nil } -func (l *Loop) stopPortForwarding(ctx context.Context, enabled bool, - timeout time.Duration) { - if !enabled { - return // nothing to stop - } - +func (l *Loop) stopPortForwarding(ctx context.Context, + timeout time.Duration) (err error) { if timeout > 0 { var cancel context.CancelFunc ctx, cancel = context.WithTimeout(ctx, timeout) defer cancel() } - _, err := l.portForward.Stop(ctx) - if err != nil { - l.logger.Error("cannot stop port forwarding: " + err.Error()) - } + _, err = l.portForward.Stop(ctx) + return err } diff --git a/internal/vpn/run.go b/internal/vpn/run.go index 216c81c0..c1d210eb 100644 --- a/internal/vpn/run.go +++ b/internal/vpn/run.go @@ -6,7 +6,6 @@ import ( "github.com/qdm12/gluetun/internal/constants" "github.com/qdm12/gluetun/internal/provider" - "github.com/qdm12/gluetun/internal/publicip/models" "github.com/qdm12/golibs/logging" ) @@ -32,6 +31,7 @@ func (l *Loop) Run(ctx context.Context, done chan<- struct{}) { providerConf := provider.New(settings.Provider.Name, allServers, time.Now) + portForwarding := settings.Provider.PortForwarding.Enabled var vpnRunner vpnRunner var serverName, vpnInterface string var err error @@ -49,7 +49,7 @@ func (l *Loop) Run(ctx context.Context, done chan<- struct{}) { continue } tunnelUpData := tunnelUpData{ - portForwarding: settings.Provider.PortForwarding.Enabled, + portForwarding: portForwarding, serverName: serverName, portForwarder: providerConf, vpnIntf: vpnInterface, @@ -76,9 +76,7 @@ func (l *Loop) Run(ctx context.Context, done chan<- struct{}) { case <-tunnelReady: go l.onTunnelUp(openvpnCtx, tunnelUpData) case <-ctx.Done(): - const pfTimeout = 100 * time.Millisecond - l.stopPortForwarding(context.Background(), - settings.Provider.PortForwarding.Enabled, pfTimeout) + l.cleanup(context.Background(), portForwarding) openvpnCancel() <-waitError close(waitError) @@ -86,8 +84,7 @@ func (l *Loop) Run(ctx context.Context, done chan<- struct{}) { case <-l.stop: l.userTrigger = true l.logger.Info("stopping") - l.publicip.SetData(models.IPInfoData{}) // clear public IP address data - l.stopPortForwarding(ctx, settings.Provider.PortForwarding.Enabled, 0) + l.cleanup(context.Background(), portForwarding) openvpnCancel() <-waitError // do not close waitError or the waitError @@ -102,7 +99,7 @@ func (l *Loop) Run(ctx context.Context, done chan<- struct{}) { l.statusManager.Lock() // prevent SetStatus from running in parallel - l.stopPortForwarding(ctx, settings.Provider.PortForwarding.Enabled, 0) + l.cleanup(context.Background(), portForwarding) openvpnCancel() l.statusManager.SetStatus(constants.Crashed) l.logAndWait(ctx, err) diff --git a/internal/vpn/tunnelup.go b/internal/vpn/tunnelup.go index 87556a69..1cf38086 100644 --- a/internal/vpn/tunnelup.go +++ b/internal/vpn/tunnelup.go @@ -19,6 +19,13 @@ type tunnelUpData struct { func (l *Loop) onTunnelUp(ctx context.Context, data tunnelUpData) { l.client.CloseIdleConnections() + for _, vpnPort := range l.vpnInputPorts { + err := l.fw.SetAllowedPort(ctx, vpnPort, data.vpnIntf) + if err != nil { + l.logger.Error("cannot allow input port through firewall: " + err.Error()) + } + } + if l.dnsLooper.GetSettings().Enabled { _, _ = l.dnsLooper.ApplyStatus(ctx, constants.Running) }