Code maintenance: OS package for file system

- OS custom internal package for file system interaction
- Remove fileManager external dependency
- Closer API to Go's native API on the OS
- Create directories at startup
- Better testability
- Move Unsetenv to os interface
This commit is contained in:
Quentin McGaw
2020-12-29 00:55:31 +00:00
parent f5366c33bc
commit 73479bab26
43 changed files with 923 additions and 353 deletions

View File

@@ -1,31 +1,68 @@
package openvpn
import (
"io/ioutil"
"os"
"strings"
"github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/golibs/files"
)
// WriteAuthFile writes the OpenVPN auth file to disk with the right permissions.
func (c *configurator) WriteAuthFile(user, password string, uid, gid int) error {
exists, err := c.fileManager.FileExists(string(constants.OpenVPNAuthConf))
if err != nil {
const filepath = string(constants.OpenVPNAuthConf)
file, err := c.os.OpenFile(filepath, os.O_RDONLY, 0)
if err != nil && !os.IsNotExist(err) {
return err
} else if exists {
data, err := c.fileManager.ReadFile(string(constants.OpenVPNAuthConf))
}
if os.IsNotExist(err) {
file, err = c.os.OpenFile(filepath, os.O_WRONLY|os.O_CREATE, 0400)
if err != nil {
return err
}
lines := strings.Split(string(data), "\n")
if len(lines) > 1 && lines[0] == user && lines[1] == password {
return nil
_, err = file.WriteString(user + "\n" + password)
if err != nil {
_ = file.Close()
return err
}
c.logger.Info("username and password changed", constants.OpenVPNAuthConf)
err = file.Chown(uid, gid)
if err != nil {
_ = file.Close()
return err
}
return file.Close()
}
return c.fileManager.WriteLinesToFile(
string(constants.OpenVPNAuthConf),
[]string{user, password},
files.Ownership(uid, gid),
files.Permissions(constants.UserReadPermission))
data, err := ioutil.ReadAll(file)
if err != nil {
_ = file.Close()
return err
}
if err := file.Close(); err != nil {
return err
}
lines := strings.Split(string(data), "\n")
if len(lines) > 1 && lines[0] == user && lines[1] == password {
return nil
}
c.logger.Info("username and password changed in %s", constants.OpenVPNAuthConf)
file, err = c.os.OpenFile(filepath, os.O_TRUNC|os.O_WRONLY, 0400)
if err != nil {
return err
}
_, err = file.WriteString(user + "\n" + password)
if err != nil {
_ = file.Close()
return err
}
err = file.Chown(uid, gid)
if err != nil {
_ = file.Close()
return err
}
return file.Close()
}

View File

@@ -4,17 +4,18 @@ import (
"context"
"net"
"net/http"
"strings"
"sync"
"time"
"github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/gluetun/internal/firewall"
"github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/gluetun/internal/os"
"github.com/qdm12/gluetun/internal/provider"
"github.com/qdm12/gluetun/internal/routing"
"github.com/qdm12/gluetun/internal/settings"
"github.com/qdm12/golibs/command"
"github.com/qdm12/golibs/files"
"github.com/qdm12/golibs/logging"
)
@@ -43,7 +44,7 @@ type looper struct {
// Other objects
logger, pfLogger logging.Logger
client *http.Client
fileManager files.FileManager
openFile os.OpenFileFunc
streamMerger command.StreamMerger
cancel context.CancelFunc
// Internal channels and locks
@@ -57,7 +58,7 @@ type looper struct {
func NewLooper(settings settings.OpenVPN,
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,
logger logging.Logger, client *http.Client, openFile os.OpenFileFunc,
streamMerger command.StreamMerger, cancel context.CancelFunc) Looper {
return &looper{
state: state{
@@ -74,7 +75,7 @@ func NewLooper(settings settings.OpenVPN,
logger: logger.WithPrefix("openvpn: "),
pfLogger: logger.WithPrefix("port forwarding: "),
client: client,
fileManager: fileManager,
openFile: openFile,
streamMerger: streamMerger,
cancel: cancel,
start: make(chan struct{}),
@@ -115,8 +116,8 @@ func (l *looper) Run(ctx context.Context, wg *sync.WaitGroup) {
settings.Auth,
settings.Provider.ExtraConfigOptions,
)
if err := l.fileManager.WriteLinesToFile(string(constants.OpenVPNConf), lines,
files.Ownership(l.uid, l.gid), files.Permissions(constants.UserReadPermission)); err != nil {
if err := writeOpenvpnConf(lines, l.openFile); err != nil {
l.logger.Error(err)
l.cancel()
return
@@ -239,6 +240,22 @@ func (l *looper) portForward(ctx context.Context, wg *sync.WaitGroup,
return settings.Provider.PortForwarding.Filepath
}
providerConf.PortForward(ctx,
client, l.fileManager, l.pfLogger,
client, l.openFile, l.pfLogger,
gateway, l.fw, syncState)
}
func writeOpenvpnConf(lines []string, openFile os.OpenFileFunc) error {
const filepath = string(constants.OpenVPNConf)
file, err := openFile(filepath, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644)
if err != nil {
return err
}
_, err = file.WriteString(strings.Join(lines, "\n"))
if err != nil {
return err
}
if err := file.Close(); err != nil {
return err
}
return nil
}

View File

@@ -3,10 +3,9 @@ package openvpn
import (
"context"
"io"
"os"
"github.com/qdm12/gluetun/internal/os"
"github.com/qdm12/golibs/command"
"github.com/qdm12/golibs/files"
"github.com/qdm12/golibs/logging"
"golang.org/x/sys/unix"
)
@@ -20,21 +19,19 @@ type Configurator interface {
}
type configurator struct {
fileManager files.FileManager
logger logging.Logger
commander command.Commander
openFile func(name string, flag int, perm os.FileMode) (*os.File, error)
mkDev func(major uint32, minor uint32) uint64
mkNod func(path string, mode uint32, dev int) error
logger logging.Logger
commander command.Commander
os os.OS
mkDev func(major uint32, minor uint32) uint64
mkNod func(path string, mode uint32, dev int) error
}
func NewConfigurator(logger logging.Logger, fileManager files.FileManager) Configurator {
func NewConfigurator(logger logging.Logger, os os.OS) Configurator {
return &configurator{
fileManager: fileManager,
logger: logger.WithPrefix("openvpn configurator: "),
commander: command.NewCommander(),
openFile: os.OpenFile,
mkDev: unix.Mkdev,
mkNod: unix.Mknod,
logger: logger.WithPrefix("openvpn configurator: "),
commander: command.NewCommander(),
os: os,
mkDev: unix.Mkdev,
mkNod: unix.Mknod,
}
}

View File

@@ -11,7 +11,7 @@ import (
// CheckTUN checks the tunnel device is present and accessible.
func (c *configurator) CheckTUN() error {
c.logger.Info("checking for device %s", constants.TunnelDevice)
f, err := c.openFile(string(constants.TunnelDevice), os.O_RDWR, 0)
f, err := c.os.OpenFile(string(constants.TunnelDevice), os.O_RDWR, 0)
if err != nil {
return fmt.Errorf("TUN device is not available: %w", err)
}
@@ -23,9 +23,10 @@ func (c *configurator) CheckTUN() error {
func (c *configurator) CreateTUN() error {
c.logger.Info("creating %s", constants.TunnelDevice)
if err := c.fileManager.CreateDir("/dev/net"); err != nil {
if err := c.os.MkdirAll("/dev/net", 0751); err != nil {
return err
}
const (
major = 10
minor = 200
@@ -34,8 +35,17 @@ func (c *configurator) CreateTUN() error {
if err := c.mkNod(string(constants.TunnelDevice), unix.S_IFCHR, int(dev)); err != nil {
return err
}
if err := c.fileManager.SetUserPermissions(string(constants.TunnelDevice), 0666); err != nil {
const filepath = string(constants.TunnelDevice)
file, err := c.os.OpenFile(filepath, os.O_WRONLY, 0666)
if err != nil {
return err
}
return nil
const readWriteAllPerms os.FileMode = 0666
if err := file.Chmod(readWriteAllPerms); err != nil {
_ = file.Close()
return err
}
return file.Close()
}