diff --git a/cmd/gluetun/main.go b/cmd/gluetun/main.go index d38eef49..2f3677c3 100644 --- a/cmd/gluetun/main.go +++ b/cmd/gluetun/main.go @@ -118,7 +118,8 @@ func _main(background context.Context, args []string) int { //nolint:gocognit,go // Should never change uid, gid := allSettings.System.UID, allSettings.System.GID - err = alpineConf.CreateUser("nonrootuser", uid) + const defaultUsername = "nonrootuser" + nonRootUsername, err := alpineConf.CreateUser(defaultUsername, uid) if err != nil { logger.Error(err) return 1 @@ -217,7 +218,7 @@ func _main(background context.Context, args []string) int { //nolint:gocognit,go go collectStreamLines(ctx, streamMerger, logger, signalTunnelReady) - openvpnLooper := openvpn.NewLooper(allSettings.OpenVPN, uid, gid, allServers, + openvpnLooper := openvpn.NewLooper(allSettings.OpenVPN, nonRootUsername, uid, gid, allServers, ovpnConf, firewallConf, routingConf, logger, httpClient, fileManager, streamMerger, cancel) wg.Add(1) // wait for restartOpenvpn @@ -229,7 +230,7 @@ func _main(background context.Context, args []string) int { //nolint:gocognit,go // wait for updaterLooper.Restart() or its ticket launched with RunRestartTicker go updaterLooper.Run(ctx, wg) - unboundLooper := dns.NewLooper(dnsConf, allSettings.DNS, logger, streamMerger, uid, gid) + unboundLooper := dns.NewLooper(dnsConf, allSettings.DNS, logger, streamMerger, nonRootUsername, uid, gid) wg.Add(1) // wait for unboundLooper.Restart or its ticker launched with RunRestartTicker go unboundLooper.Run(ctx, wg, signalDNSReady) diff --git a/internal/alpine/alpine.go b/internal/alpine/alpine.go index 97c6ce9d..3c3e3fbe 100644 --- a/internal/alpine/alpine.go +++ b/internal/alpine/alpine.go @@ -7,7 +7,7 @@ import ( ) type Configurator interface { - CreateUser(username string, uid int) error + CreateUser(username string, uid int) (createdUsername string, err error) } type configurator struct { diff --git a/internal/alpine/users.go b/internal/alpine/users.go index d3e90c3e..9ec3ed2d 100644 --- a/internal/alpine/users.go +++ b/internal/alpine/users.go @@ -6,34 +6,34 @@ import ( ) // CreateUser creates a user in Alpine with the given UID. -func (c *configurator) CreateUser(username string, uid int) error { +func (c *configurator) CreateUser(username string, uid int) (createdUsername string, err error) { UIDStr := fmt.Sprintf("%d", uid) u, err := c.lookupUID(UIDStr) _, unknownUID := err.(user.UnknownUserIdError) if err != nil && !unknownUID { - return fmt.Errorf("cannot create user: %w", err) + return "", fmt.Errorf("cannot create user: %w", err) } else if u != nil { if u.Username == username { - return nil + return "", nil } - return fmt.Errorf("user with ID %d exists with username %q instead of %q", uid, u.Username, username) + return u.Username, nil } u, err = c.lookupUser(username) _, unknownUsername := err.(user.UnknownUserError) if err != nil && !unknownUsername { - return fmt.Errorf("cannot create user: %w", err) + return "", fmt.Errorf("cannot create user: %w", err) } else if u != nil { - return fmt.Errorf("cannot create user: user with name %s already exists for ID %s instead of %d", + return "", fmt.Errorf("cannot create user: user with name %s already exists for ID %s instead of %d", username, u.Uid, uid) } passwd, err := c.fileManager.ReadFile("/etc/passwd") if err != nil { - return fmt.Errorf("cannot create user: %w", err) + return "", fmt.Errorf("cannot create user: %w", err) } passwd = append(passwd, []byte(fmt.Sprintf("%s:x:%d:::/dev/null:/sbin/nologin\n", username, uid))...) if err := c.fileManager.WriteToFile("/etc/passwd", passwd); err != nil { - return fmt.Errorf("cannot create user: %w", err) + return "", fmt.Errorf("cannot create user: %w", err) } - return nil + return username, nil } diff --git a/internal/cli/cli.go b/internal/cli/cli.go index 4cf31868..c50f3b02 100644 --- a/internal/cli/cli.go +++ b/internal/cli/cli.go @@ -71,8 +71,7 @@ func OpenvpnConfig() error { lines := providerConf.BuildConf( connection, allSettings.OpenVPN.Verbosity, - allSettings.System.UID, - allSettings.System.GID, + "nonroortuser", allSettings.OpenVPN.Root, allSettings.OpenVPN.Cipher, allSettings.OpenVPN.Auth, diff --git a/internal/dns/conf.go b/internal/dns/conf.go index 5abd441a..8da871f3 100644 --- a/internal/dns/conf.go +++ b/internal/dns/conf.go @@ -14,9 +14,10 @@ import ( "github.com/qdm12/golibs/network" ) -func (c *configurator) MakeUnboundConf(ctx context.Context, settings settings.DNS, uid, gid int) (err error) { +func (c *configurator) MakeUnboundConf(ctx context.Context, settings settings.DNS, + username string, uid, gid int) (err error) { c.logger.Info("generating Unbound configuration") - lines, warnings := generateUnboundConf(ctx, settings, c.client, c.logger) + lines, warnings := generateUnboundConf(ctx, settings, username, c.client, c.logger) for _, warning := range warnings { c.logger.Warn(warning) } @@ -28,7 +29,7 @@ func (c *configurator) MakeUnboundConf(ctx context.Context, settings settings.DN } // MakeUnboundConf generates an Unbound configuration from the user provided settings. -func generateUnboundConf(ctx context.Context, settings settings.DNS, +func generateUnboundConf(ctx context.Context, settings settings.DNS, username string, client network.Client, logger logging.Logger) ( lines []string, warnings []error) { doIPv6 := "no" @@ -69,7 +70,7 @@ func generateUnboundConf(ctx context.Context, settings settings.DNS, "interface": "0.0.0.0", "port": "53", // Other - "username": "\"nonrootuser\"", + "username": fmt.Sprintf("%q", username), } // Block lists diff --git a/internal/dns/conf_test.go b/internal/dns/conf_test.go index c4f6582f..3b3e0380 100644 --- a/internal/dns/conf_test.go +++ b/internal/dns/conf_test.go @@ -41,7 +41,7 @@ func Test_generateUnboundConf(t *testing.T) { logger := mock_logging.NewMockLogger(mockCtrl) logger.EXPECT().Info("%d hostnames blocked overall", 2).Times(1) logger.EXPECT().Info("%d IP addresses blocked overall", 3).Times(1) - lines, warnings := generateUnboundConf(ctx, settings, client, logger) + lines, warnings := generateUnboundConf(ctx, settings, "nonrootuser", client, logger) require.Len(t, warnings, 0) expected := ` server: diff --git a/internal/dns/dns.go b/internal/dns/dns.go index 97dd4be6..4e3da2bc 100644 --- a/internal/dns/dns.go +++ b/internal/dns/dns.go @@ -15,7 +15,7 @@ import ( type Configurator interface { DownloadRootHints(ctx context.Context, uid, gid int) error DownloadRootKey(ctx context.Context, uid, gid int) error - MakeUnboundConf(ctx context.Context, settings settings.DNS, uid, gid int) (err error) + MakeUnboundConf(ctx context.Context, settings settings.DNS, username string, uid, gid int) (err error) UseDNSInternally(IP net.IP) UseDNSSystemWide(ip net.IP, keepNameserver bool) error Start(ctx context.Context, logLevel uint8) (stdout io.ReadCloser, waitFn func() error, err error) diff --git a/internal/dns/loop.go b/internal/dns/loop.go index fc29ef30..fe2eefdf 100644 --- a/internal/dns/loop.go +++ b/internal/dns/loop.go @@ -27,6 +27,7 @@ type looper struct { conf Configurator logger logging.Logger streamMerger command.StreamMerger + username string uid int gid int loopLock sync.Mutex @@ -40,7 +41,7 @@ type looper struct { } func NewLooper(conf Configurator, settings settings.DNS, logger logging.Logger, - streamMerger command.StreamMerger, uid, gid int) Looper { + streamMerger command.StreamMerger, username string, uid, gid int) Looper { return &looper{ state: state{ status: constants.Stopped, @@ -48,6 +49,7 @@ func NewLooper(conf Configurator, settings settings.DNS, logger logging.Logger, }, conf: conf, logger: logger.WithPrefix("dns over tls: "), + username: username, uid: uid, gid: gid, streamMerger: streamMerger, @@ -292,7 +294,7 @@ func (l *looper) updateFiles(ctx context.Context) (err error) { return err } settings := l.GetSettings() - if err := l.conf.MakeUnboundConf(ctx, settings, l.uid, l.gid); err != nil { + if err := l.conf.MakeUnboundConf(ctx, settings, l.username, l.uid, l.gid); err != nil { return err } return nil diff --git a/internal/openvpn/loop.go b/internal/openvpn/loop.go index 76fd2230..ee415ffd 100644 --- a/internal/openvpn/loop.go +++ b/internal/openvpn/loop.go @@ -33,8 +33,9 @@ type Looper interface { type looper struct { state state // Fixed parameters - uid int - gid int + username string + uid int + gid int // Configurators conf Configurator fw firewall.Configurator @@ -54,7 +55,7 @@ type looper struct { } func NewLooper(settings settings.OpenVPN, - uid, gid int, allServers models.AllServers, + username string, uid, gid int, allServers models.AllServers, conf Configurator, fw firewall.Configurator, routing routing.Routing, logger logging.Logger, client *http.Client, fileManager files.FileManager, streamMerger command.StreamMerger, cancel context.CancelFunc) Looper { @@ -64,6 +65,7 @@ func NewLooper(settings settings.OpenVPN, settings: settings, allServers: allServers, }, + username: username, uid: uid, gid: gid, conf: conf, @@ -107,8 +109,7 @@ func (l *looper) Run(ctx context.Context, wg *sync.WaitGroup) { lines := providerConf.BuildConf( connection, settings.Verbosity, - l.uid, - l.gid, + l.username, settings.Root, settings.Cipher, settings.Auth, diff --git a/internal/provider/cyberghost.go b/internal/provider/cyberghost.go index 11ee1295..af0e0f27 100644 --- a/internal/provider/cyberghost.go +++ b/internal/provider/cyberghost.go @@ -62,8 +62,8 @@ func (c *cyberghost) GetOpenVPNConnection(selection models.ServerSelection) ( return pickRandomConnection(connections, c.randSource), nil } -func (c *cyberghost) BuildConf(connection models.OpenVPNConnection, verbosity, - uid, gid int, root bool, cipher, auth string, extras models.ExtraConfigOptions) (lines []string) { +func (c *cyberghost) BuildConf(connection models.OpenVPNConnection, verbosity int, + username string, root bool, cipher, auth string, extras models.ExtraConfigOptions) (lines []string) { if len(cipher) == 0 { cipher = aes256cbc } @@ -105,7 +105,7 @@ func (c *cyberghost) BuildConf(connection models.OpenVPNConnection, verbosity, lines = append(lines, "ncp-ciphers AES-256-GCM:AES-256-CBC:AES-128-GCM") } if !root { - lines = append(lines, "user nonrootuser") + lines = append(lines, "user "+username) } lines = append(lines, []string{ "", diff --git a/internal/provider/mullvad.go b/internal/provider/mullvad.go index 212b6eda..7d4227ec 100644 --- a/internal/provider/mullvad.go +++ b/internal/provider/mullvad.go @@ -73,7 +73,7 @@ func (m *mullvad) GetOpenVPNConnection(selection models.ServerSelection) ( } func (m *mullvad) BuildConf(connection models.OpenVPNConnection, - verbosity, uid, gid int, root bool, cipher, auth string, extras models.ExtraConfigOptions) (lines []string) { + verbosity int, username string, root bool, cipher, auth string, extras models.ExtraConfigOptions) (lines []string) { if len(cipher) == 0 { cipher = aes256cbc } @@ -114,7 +114,7 @@ func (m *mullvad) BuildConf(connection models.OpenVPNConnection, lines = append(lines, `pull-filter ignore "ifconfig-ipv6"`) } if !root { - lines = append(lines, "user nonrootuser") + lines = append(lines, "user "+username) } lines = append(lines, []string{ "", diff --git a/internal/provider/nordvpn.go b/internal/provider/nordvpn.go index d9299437..1b94127d 100644 --- a/internal/provider/nordvpn.go +++ b/internal/provider/nordvpn.go @@ -78,7 +78,7 @@ func (n *nordvpn) GetOpenVPNConnection(selection models.ServerSelection) ( return pickRandomConnection(connections, n.randSource), nil } -func (n *nordvpn) BuildConf(connection models.OpenVPNConnection, verbosity, uid, gid int, root bool, +func (n *nordvpn) BuildConf(connection models.OpenVPNConnection, verbosity int, username string, root bool, cipher, auth string, extras models.ExtraConfigOptions) (lines []string) { if len(cipher) == 0 { cipher = aes256cbc @@ -121,7 +121,7 @@ func (n *nordvpn) BuildConf(connection models.OpenVPNConnection, verbosity, uid, fmt.Sprintf("auth %s", auth), } if !root { - lines = append(lines, "user nonrootuser") + lines = append(lines, "user "+username) } lines = append(lines, []string{ "", diff --git a/internal/provider/piav4.go b/internal/provider/piav4.go index 0ee6d534..dd1d4d5e 100644 --- a/internal/provider/piav4.go +++ b/internal/provider/piav4.go @@ -109,7 +109,7 @@ func (p *pia) GetOpenVPNConnection(selection models.ServerSelection) ( return connection, nil } -func (p *pia) BuildConf(connection models.OpenVPNConnection, verbosity, uid, gid int, root bool, +func (p *pia) BuildConf(connection models.OpenVPNConnection, verbosity int, username string, root bool, cipher, auth string, extras models.ExtraConfigOptions) (lines []string) { var X509CRL, certificate string var defaultCipher, defaultAuth string @@ -161,7 +161,7 @@ func (p *pia) BuildConf(connection models.OpenVPNConnection, verbosity, uid, gid lines = append(lines, "ncp-disable") } if !root { - lines = append(lines, "user nonrootuser") + lines = append(lines, "user "+username) } lines = append(lines, []string{ "", diff --git a/internal/provider/privado.go b/internal/provider/privado.go index d4b6eb17..7461848d 100644 --- a/internal/provider/privado.go +++ b/internal/provider/privado.go @@ -70,7 +70,7 @@ func (s *privado) GetOpenVPNConnection(selection models.ServerSelection) ( return pickRandomConnection(connections, s.randSource), nil } -func (s *privado) BuildConf(connection models.OpenVPNConnection, verbosity, uid, gid int, root bool, +func (s *privado) BuildConf(connection models.OpenVPNConnection, verbosity int, username string, root bool, cipher, auth string, extras models.ExtraConfigOptions) (lines []string) { if len(cipher) == 0 { cipher = aes256cbc @@ -104,7 +104,7 @@ func (s *privado) BuildConf(connection models.OpenVPNConnection, verbosity, uid, fmt.Sprintf("auth %s", auth), } if !root { - lines = append(lines, "user nonrootuser") + lines = append(lines, "user "+username) } lines = append(lines, []string{ "", diff --git a/internal/provider/provider.go b/internal/provider/provider.go index 832ce892..ef76c2b1 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -15,7 +15,7 @@ import ( // Provider contains methods to read and modify the openvpn configuration to connect as a client. type Provider interface { GetOpenVPNConnection(selection models.ServerSelection) (connection models.OpenVPNConnection, err error) - BuildConf(connection models.OpenVPNConnection, verbosity, uid, gid int, + BuildConf(connection models.OpenVPNConnection, verbosity int, username string, root bool, cipher, auth string, extras models.ExtraConfigOptions) (lines []string) PortForward(ctx context.Context, client *http.Client, fileManager files.FileManager, pfLogger logging.Logger, gateway net.IP, fw firewall.Configurator, diff --git a/internal/provider/purevpn.go b/internal/provider/purevpn.go index 36cf3907..addead66 100644 --- a/internal/provider/purevpn.go +++ b/internal/provider/purevpn.go @@ -72,7 +72,7 @@ func (p *purevpn) GetOpenVPNConnection(selection models.ServerSelection) ( return pickRandomConnection(connections, p.randSource), nil } -func (p *purevpn) BuildConf(connection models.OpenVPNConnection, verbosity, uid, gid int, root bool, +func (p *purevpn) BuildConf(connection models.OpenVPNConnection, verbosity int, username string, root bool, cipher, auth string, extras models.ExtraConfigOptions) (lines []string) { if len(cipher) == 0 { cipher = aes256cbc @@ -108,7 +108,7 @@ func (p *purevpn) BuildConf(connection models.OpenVPNConnection, verbosity, uid, fmt.Sprintf("cipher %s", cipher), } if !root { - lines = append(lines, "user nonrootuser") + lines = append(lines, "user "+username) } lines = append(lines, []string{ "", diff --git a/internal/provider/surfshark.go b/internal/provider/surfshark.go index a31e9044..951fac2d 100644 --- a/internal/provider/surfshark.go +++ b/internal/provider/surfshark.go @@ -73,7 +73,7 @@ func (s *surfshark) GetOpenVPNConnection(selection models.ServerSelection) ( return pickRandomConnection(connections, s.randSource), nil } -func (s *surfshark) BuildConf(connection models.OpenVPNConnection, verbosity, uid, gid int, root bool, +func (s *surfshark) BuildConf(connection models.OpenVPNConnection, verbosity int, username string, root bool, cipher, auth string, extras models.ExtraConfigOptions) (lines []string) { if len(cipher) == 0 { cipher = aes256cbc @@ -117,7 +117,7 @@ func (s *surfshark) BuildConf(connection models.OpenVPNConnection, verbosity, ui fmt.Sprintf("auth %s", auth), } if !root { - lines = append(lines, "user nonrootuser") + lines = append(lines, "user "+username) } lines = append(lines, []string{ "", diff --git a/internal/provider/vyprvpn.go b/internal/provider/vyprvpn.go index 414fdffb..22cb0392 100644 --- a/internal/provider/vyprvpn.go +++ b/internal/provider/vyprvpn.go @@ -69,7 +69,7 @@ func (v *vyprvpn) GetOpenVPNConnection(selection models.ServerSelection) ( return pickRandomConnection(connections, v.randSource), nil } -func (v *vyprvpn) BuildConf(connection models.OpenVPNConnection, verbosity, uid, gid int, +func (v *vyprvpn) BuildConf(connection models.OpenVPNConnection, verbosity int, username string, root bool, cipher, auth string, extras models.ExtraConfigOptions) (lines []string) { if len(cipher) == 0 { cipher = aes256cbc @@ -106,7 +106,7 @@ func (v *vyprvpn) BuildConf(connection models.OpenVPNConnection, verbosity, uid, fmt.Sprintf("auth %s", auth), } if !root { - lines = append(lines, "user nonrootuser") + lines = append(lines, "user "+username) } lines = append(lines, []string{ "", diff --git a/internal/provider/windscribe.go b/internal/provider/windscribe.go index c2a80a57..11ae0eb3 100644 --- a/internal/provider/windscribe.go +++ b/internal/provider/windscribe.go @@ -72,7 +72,7 @@ func (w *windscribe) GetOpenVPNConnection(selection models.ServerSelection) (con return pickRandomConnection(connections, w.randSource), nil } -func (w *windscribe) BuildConf(connection models.OpenVPNConnection, verbosity, uid, gid int, +func (w *windscribe) BuildConf(connection models.OpenVPNConnection, verbosity int, username string, root bool, cipher, auth string, extras models.ExtraConfigOptions) (lines []string) { if len(cipher) == 0 { cipher = aes256cbc @@ -111,7 +111,7 @@ func (w *windscribe) BuildConf(connection models.OpenVPNConnection, verbosity, u lines = append(lines, "ncp-ciphers AES-256-GCM:AES-256-CBC:AES-128-GCM") } if !root { - lines = append(lines, "user nonrootuser") + lines = append(lines, "user "+username) } lines = append(lines, []string{ "",