Re-use username for UID if it exists

This commit is contained in:
Quentin McGaw
2020-12-27 00:36:39 +00:00
parent 38e713fea2
commit 2dc674559e
19 changed files with 52 additions and 48 deletions

View File

@@ -118,7 +118,8 @@ func _main(background context.Context, args []string) int { //nolint:gocognit,go
// Should never change // Should never change
uid, gid := allSettings.System.UID, allSettings.System.GID 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 { if err != nil {
logger.Error(err) logger.Error(err)
return 1 return 1
@@ -217,7 +218,7 @@ func _main(background context.Context, args []string) int { //nolint:gocognit,go
go collectStreamLines(ctx, streamMerger, logger, signalTunnelReady) 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) ovpnConf, firewallConf, routingConf, logger, httpClient, fileManager, streamMerger, cancel)
wg.Add(1) wg.Add(1)
// wait for restartOpenvpn // 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 // wait for updaterLooper.Restart() or its ticket launched with RunRestartTicker
go updaterLooper.Run(ctx, wg) 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) wg.Add(1)
// wait for unboundLooper.Restart or its ticker launched with RunRestartTicker // wait for unboundLooper.Restart or its ticker launched with RunRestartTicker
go unboundLooper.Run(ctx, wg, signalDNSReady) go unboundLooper.Run(ctx, wg, signalDNSReady)

View File

@@ -7,7 +7,7 @@ import (
) )
type Configurator interface { type Configurator interface {
CreateUser(username string, uid int) error CreateUser(username string, uid int) (createdUsername string, err error)
} }
type configurator struct { type configurator struct {

View File

@@ -6,34 +6,34 @@ import (
) )
// CreateUser creates a user in Alpine with the given UID. // 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) UIDStr := fmt.Sprintf("%d", uid)
u, err := c.lookupUID(UIDStr) u, err := c.lookupUID(UIDStr)
_, unknownUID := err.(user.UnknownUserIdError) _, unknownUID := err.(user.UnknownUserIdError)
if err != nil && !unknownUID { if err != nil && !unknownUID {
return fmt.Errorf("cannot create user: %w", err) return "", fmt.Errorf("cannot create user: %w", err)
} else if u != nil { } else if u != nil {
if u.Username == username { 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) u, err = c.lookupUser(username)
_, unknownUsername := err.(user.UnknownUserError) _, unknownUsername := err.(user.UnknownUserError)
if err != nil && !unknownUsername { if err != nil && !unknownUsername {
return fmt.Errorf("cannot create user: %w", err) return "", fmt.Errorf("cannot create user: %w", err)
} else if u != nil { } 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) username, u.Uid, uid)
} }
passwd, err := c.fileManager.ReadFile("/etc/passwd") passwd, err := c.fileManager.ReadFile("/etc/passwd")
if err != nil { 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))...) 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 { 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
} }

View File

@@ -71,8 +71,7 @@ func OpenvpnConfig() error {
lines := providerConf.BuildConf( lines := providerConf.BuildConf(
connection, connection,
allSettings.OpenVPN.Verbosity, allSettings.OpenVPN.Verbosity,
allSettings.System.UID, "nonroortuser",
allSettings.System.GID,
allSettings.OpenVPN.Root, allSettings.OpenVPN.Root,
allSettings.OpenVPN.Cipher, allSettings.OpenVPN.Cipher,
allSettings.OpenVPN.Auth, allSettings.OpenVPN.Auth,

View File

@@ -14,9 +14,10 @@ import (
"github.com/qdm12/golibs/network" "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") 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 { for _, warning := range warnings {
c.logger.Warn(warning) 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. // 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) ( client network.Client, logger logging.Logger) (
lines []string, warnings []error) { lines []string, warnings []error) {
doIPv6 := "no" doIPv6 := "no"
@@ -69,7 +70,7 @@ func generateUnboundConf(ctx context.Context, settings settings.DNS,
"interface": "0.0.0.0", "interface": "0.0.0.0",
"port": "53", "port": "53",
// Other // Other
"username": "\"nonrootuser\"", "username": fmt.Sprintf("%q", username),
} }
// Block lists // Block lists

View File

@@ -41,7 +41,7 @@ func Test_generateUnboundConf(t *testing.T) {
logger := mock_logging.NewMockLogger(mockCtrl) logger := mock_logging.NewMockLogger(mockCtrl)
logger.EXPECT().Info("%d hostnames blocked overall", 2).Times(1) logger.EXPECT().Info("%d hostnames blocked overall", 2).Times(1)
logger.EXPECT().Info("%d IP addresses blocked overall", 3).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) require.Len(t, warnings, 0)
expected := ` expected := `
server: server:

View File

@@ -15,7 +15,7 @@ import (
type Configurator interface { type Configurator interface {
DownloadRootHints(ctx context.Context, uid, gid int) error DownloadRootHints(ctx context.Context, uid, gid int) error
DownloadRootKey(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) UseDNSInternally(IP net.IP)
UseDNSSystemWide(ip net.IP, keepNameserver bool) error UseDNSSystemWide(ip net.IP, keepNameserver bool) error
Start(ctx context.Context, logLevel uint8) (stdout io.ReadCloser, waitFn func() error, err error) Start(ctx context.Context, logLevel uint8) (stdout io.ReadCloser, waitFn func() error, err error)

View File

@@ -27,6 +27,7 @@ type looper struct {
conf Configurator conf Configurator
logger logging.Logger logger logging.Logger
streamMerger command.StreamMerger streamMerger command.StreamMerger
username string
uid int uid int
gid int gid int
loopLock sync.Mutex loopLock sync.Mutex
@@ -40,7 +41,7 @@ type looper struct {
} }
func NewLooper(conf Configurator, settings settings.DNS, logger logging.Logger, 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{ return &looper{
state: state{ state: state{
status: constants.Stopped, status: constants.Stopped,
@@ -48,6 +49,7 @@ func NewLooper(conf Configurator, settings settings.DNS, logger logging.Logger,
}, },
conf: conf, conf: conf,
logger: logger.WithPrefix("dns over tls: "), logger: logger.WithPrefix("dns over tls: "),
username: username,
uid: uid, uid: uid,
gid: gid, gid: gid,
streamMerger: streamMerger, streamMerger: streamMerger,
@@ -292,7 +294,7 @@ func (l *looper) updateFiles(ctx context.Context) (err error) {
return err return err
} }
settings := l.GetSettings() 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 err
} }
return nil return nil

View File

@@ -33,8 +33,9 @@ type Looper interface {
type looper struct { type looper struct {
state state state state
// Fixed parameters // Fixed parameters
uid int username string
gid int uid int
gid int
// Configurators // Configurators
conf Configurator conf Configurator
fw firewall.Configurator fw firewall.Configurator
@@ -54,7 +55,7 @@ type looper struct {
} }
func NewLooper(settings settings.OpenVPN, 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, conf Configurator, fw firewall.Configurator, routing routing.Routing,
logger logging.Logger, client *http.Client, fileManager files.FileManager, logger logging.Logger, client *http.Client, fileManager files.FileManager,
streamMerger command.StreamMerger, cancel context.CancelFunc) Looper { streamMerger command.StreamMerger, cancel context.CancelFunc) Looper {
@@ -64,6 +65,7 @@ func NewLooper(settings settings.OpenVPN,
settings: settings, settings: settings,
allServers: allServers, allServers: allServers,
}, },
username: username,
uid: uid, uid: uid,
gid: gid, gid: gid,
conf: conf, conf: conf,
@@ -107,8 +109,7 @@ func (l *looper) Run(ctx context.Context, wg *sync.WaitGroup) {
lines := providerConf.BuildConf( lines := providerConf.BuildConf(
connection, connection,
settings.Verbosity, settings.Verbosity,
l.uid, l.username,
l.gid,
settings.Root, settings.Root,
settings.Cipher, settings.Cipher,
settings.Auth, settings.Auth,

View File

@@ -62,8 +62,8 @@ func (c *cyberghost) GetOpenVPNConnection(selection models.ServerSelection) (
return pickRandomConnection(connections, c.randSource), nil return pickRandomConnection(connections, c.randSource), nil
} }
func (c *cyberghost) BuildConf(connection models.OpenVPNConnection, verbosity, func (c *cyberghost) BuildConf(connection models.OpenVPNConnection, verbosity int,
uid, gid int, root bool, cipher, auth string, extras models.ExtraConfigOptions) (lines []string) { username string, root bool, cipher, auth string, extras models.ExtraConfigOptions) (lines []string) {
if len(cipher) == 0 { if len(cipher) == 0 {
cipher = aes256cbc 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") lines = append(lines, "ncp-ciphers AES-256-GCM:AES-256-CBC:AES-128-GCM")
} }
if !root { if !root {
lines = append(lines, "user nonrootuser") lines = append(lines, "user "+username)
} }
lines = append(lines, []string{ lines = append(lines, []string{
"<ca>", "<ca>",

View File

@@ -73,7 +73,7 @@ func (m *mullvad) GetOpenVPNConnection(selection models.ServerSelection) (
} }
func (m *mullvad) BuildConf(connection models.OpenVPNConnection, 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 { if len(cipher) == 0 {
cipher = aes256cbc cipher = aes256cbc
} }
@@ -114,7 +114,7 @@ func (m *mullvad) BuildConf(connection models.OpenVPNConnection,
lines = append(lines, `pull-filter ignore "ifconfig-ipv6"`) lines = append(lines, `pull-filter ignore "ifconfig-ipv6"`)
} }
if !root { if !root {
lines = append(lines, "user nonrootuser") lines = append(lines, "user "+username)
} }
lines = append(lines, []string{ lines = append(lines, []string{
"<ca>", "<ca>",

View File

@@ -78,7 +78,7 @@ func (n *nordvpn) GetOpenVPNConnection(selection models.ServerSelection) (
return pickRandomConnection(connections, n.randSource), nil 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) { cipher, auth string, extras models.ExtraConfigOptions) (lines []string) {
if len(cipher) == 0 { if len(cipher) == 0 {
cipher = aes256cbc cipher = aes256cbc
@@ -121,7 +121,7 @@ func (n *nordvpn) BuildConf(connection models.OpenVPNConnection, verbosity, uid,
fmt.Sprintf("auth %s", auth), fmt.Sprintf("auth %s", auth),
} }
if !root { if !root {
lines = append(lines, "user nonrootuser") lines = append(lines, "user "+username)
} }
lines = append(lines, []string{ lines = append(lines, []string{
"<ca>", "<ca>",

View File

@@ -109,7 +109,7 @@ func (p *pia) GetOpenVPNConnection(selection models.ServerSelection) (
return connection, nil 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) { cipher, auth string, extras models.ExtraConfigOptions) (lines []string) {
var X509CRL, certificate string var X509CRL, certificate string
var defaultCipher, defaultAuth string var defaultCipher, defaultAuth string
@@ -161,7 +161,7 @@ func (p *pia) BuildConf(connection models.OpenVPNConnection, verbosity, uid, gid
lines = append(lines, "ncp-disable") lines = append(lines, "ncp-disable")
} }
if !root { if !root {
lines = append(lines, "user nonrootuser") lines = append(lines, "user "+username)
} }
lines = append(lines, []string{ lines = append(lines, []string{
"<crl-verify>", "<crl-verify>",

View File

@@ -70,7 +70,7 @@ func (s *privado) GetOpenVPNConnection(selection models.ServerSelection) (
return pickRandomConnection(connections, s.randSource), nil 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) { cipher, auth string, extras models.ExtraConfigOptions) (lines []string) {
if len(cipher) == 0 { if len(cipher) == 0 {
cipher = aes256cbc cipher = aes256cbc
@@ -104,7 +104,7 @@ func (s *privado) BuildConf(connection models.OpenVPNConnection, verbosity, uid,
fmt.Sprintf("auth %s", auth), fmt.Sprintf("auth %s", auth),
} }
if !root { if !root {
lines = append(lines, "user nonrootuser") lines = append(lines, "user "+username)
} }
lines = append(lines, []string{ lines = append(lines, []string{
"<ca>", "<ca>",

View File

@@ -15,7 +15,7 @@ import (
// Provider contains methods to read and modify the openvpn configuration to connect as a client. // Provider contains methods to read and modify the openvpn configuration to connect as a client.
type Provider interface { type Provider interface {
GetOpenVPNConnection(selection models.ServerSelection) (connection models.OpenVPNConnection, err error) 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) root bool, cipher, auth string, extras models.ExtraConfigOptions) (lines []string)
PortForward(ctx context.Context, client *http.Client, PortForward(ctx context.Context, client *http.Client,
fileManager files.FileManager, pfLogger logging.Logger, gateway net.IP, fw firewall.Configurator, fileManager files.FileManager, pfLogger logging.Logger, gateway net.IP, fw firewall.Configurator,

View File

@@ -72,7 +72,7 @@ func (p *purevpn) GetOpenVPNConnection(selection models.ServerSelection) (
return pickRandomConnection(connections, p.randSource), nil 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) { cipher, auth string, extras models.ExtraConfigOptions) (lines []string) {
if len(cipher) == 0 { if len(cipher) == 0 {
cipher = aes256cbc cipher = aes256cbc
@@ -108,7 +108,7 @@ func (p *purevpn) BuildConf(connection models.OpenVPNConnection, verbosity, uid,
fmt.Sprintf("cipher %s", cipher), fmt.Sprintf("cipher %s", cipher),
} }
if !root { if !root {
lines = append(lines, "user nonrootuser") lines = append(lines, "user "+username)
} }
lines = append(lines, []string{ lines = append(lines, []string{
"<ca>", "<ca>",

View File

@@ -73,7 +73,7 @@ func (s *surfshark) GetOpenVPNConnection(selection models.ServerSelection) (
return pickRandomConnection(connections, s.randSource), nil 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) { cipher, auth string, extras models.ExtraConfigOptions) (lines []string) {
if len(cipher) == 0 { if len(cipher) == 0 {
cipher = aes256cbc cipher = aes256cbc
@@ -117,7 +117,7 @@ func (s *surfshark) BuildConf(connection models.OpenVPNConnection, verbosity, ui
fmt.Sprintf("auth %s", auth), fmt.Sprintf("auth %s", auth),
} }
if !root { if !root {
lines = append(lines, "user nonrootuser") lines = append(lines, "user "+username)
} }
lines = append(lines, []string{ lines = append(lines, []string{
"<ca>", "<ca>",

View File

@@ -69,7 +69,7 @@ func (v *vyprvpn) GetOpenVPNConnection(selection models.ServerSelection) (
return pickRandomConnection(connections, v.randSource), nil 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) { root bool, cipher, auth string, extras models.ExtraConfigOptions) (lines []string) {
if len(cipher) == 0 { if len(cipher) == 0 {
cipher = aes256cbc cipher = aes256cbc
@@ -106,7 +106,7 @@ func (v *vyprvpn) BuildConf(connection models.OpenVPNConnection, verbosity, uid,
fmt.Sprintf("auth %s", auth), fmt.Sprintf("auth %s", auth),
} }
if !root { if !root {
lines = append(lines, "user nonrootuser") lines = append(lines, "user "+username)
} }
lines = append(lines, []string{ lines = append(lines, []string{
"<ca>", "<ca>",

View File

@@ -72,7 +72,7 @@ func (w *windscribe) GetOpenVPNConnection(selection models.ServerSelection) (con
return pickRandomConnection(connections, w.randSource), nil 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) { root bool, cipher, auth string, extras models.ExtraConfigOptions) (lines []string) {
if len(cipher) == 0 { if len(cipher) == 0 {
cipher = aes256cbc 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") lines = append(lines, "ncp-ciphers AES-256-GCM:AES-256-CBC:AES-128-GCM")
} }
if !root { if !root {
lines = append(lines, "user nonrootuser") lines = append(lines, "user "+username)
} }
lines = append(lines, []string{ lines = append(lines, []string{
"<ca>", "<ca>",