Skip to content

Commit 469193a

Browse files
committed
fix: move active configs to /etc/wireguard
1 parent 13a1a53 commit 469193a

8 files changed

Lines changed: 172 additions & 9 deletions

File tree

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ The client is in experimental mode, so it is important to know which parts of th
1111
- Configuration files in user's config directory:
1212
- Session info: `~/.config/mbvpn/config.yml`
1313
- Machine ID: `~/.config/mbvpn/machine-id`
14-
- WireGuard configurations: `~/.config/mbvpn/servers/*.conf`
14+
- WireGuard configuration storage: `~/.config/mbvpn/servers/*.conf`
15+
- Active WireGuard configurations are copied to `/etc/wireguard/*.conf` during connection
1516
- `logout` command removes the configuration files but keeps WireGuard interfaces in the system
1617

1718
## Installation

pkg/config/config_test.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,13 @@ func (m *MockDirectoryProvider) GetServersFile() (string, error) {
4848
return "", fmt.Errorf("not implemented")
4949
}
5050

51+
func (m *MockDirectoryProvider) GetWireguardDir() (string, error) {
52+
if m.ConfigDirError {
53+
return "", fmt.Errorf("simulated error")
54+
}
55+
return "", fmt.Errorf("not implemented")
56+
}
57+
5158
// Helper function to setup a test environment
5259
func setupTestConfig(t *testing.T) (*YamlConfigProvider, func()) {
5360
// Create a temporary directory

pkg/config/directory.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"fmt"
55
"os"
66
"path/filepath"
7+
"strings"
78
)
89

910
// DirectoryProvider defines the interface for getting application directories and file paths.
@@ -16,6 +17,9 @@ type DirectoryProvider interface {
1617
// GetServersDir returns the servers directory (e.g., ~/.config/mbvpn/servers)
1718
GetServersDir() (string, error)
1819

20+
// GetWireguardDir returns the system WireGuard directory (/etc/wireguard)
21+
GetWireguardDir() (string, error)
22+
1923
// GetConfigFile returns the config file path (e.g., ~/.config/mbvpn/config.yml)
2024
GetConfigFile() (string, error)
2125

@@ -61,6 +65,16 @@ func (d *directoryProvider) GetServersDir() (string, error) {
6165
return filepath.Join(configDir, "servers"), nil
6266
}
6367

68+
// GetWireguardDir returns the system WireGuard directory
69+
func (d *directoryProvider) GetWireguardDir() (string, error) {
70+
// For test environments (temp directories), use test structure
71+
if strings.Contains(d.baseDir, os.TempDir()) {
72+
return filepath.Join(d.baseDir, "wireguard"), nil
73+
}
74+
// For production, use standard WireGuard location
75+
return "/etc/wireguard", nil
76+
}
77+
6478
// GetConfigFile returns the config file path
6579
func (d *directoryProvider) GetConfigFile() (string, error) {
6680
configDir, err := d.GetConfigDir()

pkg/config/directory_test.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,21 @@ func TestTestDirectoryProvider_Integration(t *testing.T) {
179179
}
180180
}
181181

182+
func TestDefaultDirectoryProvider_GetWireguardDir(t *testing.T) {
183+
tempDir := t.TempDir()
184+
provider := NewDirectoryProvider(tempDir)
185+
186+
wireguardDir, err := provider.GetWireguardDir()
187+
if err != nil {
188+
t.Fatalf("GetWireguardDir should succeed: %v", err)
189+
}
190+
191+
expectedDir := filepath.Join(tempDir, "wireguard")
192+
if wireguardDir != expectedDir {
193+
t.Errorf("Expected wireguard dir '%s', got '%s'", expectedDir, wireguardDir)
194+
}
195+
}
196+
182197
func TestDirectoryProvider_Interface(t *testing.T) {
183198
// Verify the implementation satisfies the interface
184199
var _ DirectoryProvider = &directoryProvider{}

pkg/console/console.go

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -99,13 +99,20 @@ func sanitizeWgConfigPath(cfgPath string, dirProvider config.DirectoryProvider)
9999
return "", fmt.Errorf("config path is not a regular file")
100100
}
101101

102-
// Ensure it's within the expected config directory
102+
// Check if it's within the expected config directory OR system wireguard directory
103103
expectedBase, err := dirProvider.GetServersDir()
104104
if err != nil {
105105
return "", fmt.Errorf("failed to get servers directory: %w", err)
106106
}
107-
if !strings.HasPrefix(absPath, expectedBase) {
108-
return "", fmt.Errorf("config file must be within %s", expectedBase)
107+
108+
wireguardBase, wgErr := dirProvider.GetWireguardDir()
109+
if wgErr != nil {
110+
return "", fmt.Errorf("failed to get wireguard directory: %w", wgErr)
111+
}
112+
113+
// Path must be in either user config directory or system wireguard directory
114+
if !strings.HasPrefix(absPath, expectedBase) && !strings.HasPrefix(absPath, wireguardBase) {
115+
return "", fmt.Errorf("config file must be within %s or %s", expectedBase, wireguardBase)
109116
}
110117

111118
// Verify file extension (WireGuard configs must be .conf)

pkg/servers/servers_test.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,13 @@ func (m *mockDirectoryProvider) GetServersFile() (string, error) {
245245
return "", nil
246246
}
247247

248+
func (m *mockDirectoryProvider) GetWireguardDir() (string, error) {
249+
if m.shouldError {
250+
return "", os.ErrPermission
251+
}
252+
return "", nil
253+
}
254+
248255
func TestDefaultServerStorage_Get(t *testing.T) {
249256
dirProvider, cleanup := setupTestDir(t)
250257
defer cleanup()

pkg/vpn/vpn.go

Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -268,8 +268,14 @@ func (vpn *DefaultVpn) Connect(cfg string) error {
268268
return errors.NewVPNError("write config", err)
269269
}
270270

271-
output.PrintMsg(fmt.Sprintf("Calling 'wg-quick up %s'", cfgPath), output.MsgOutput)
272-
err = console.WgUp(cfgPath, vpn.dirProvider)
271+
// Copy config to /etc/wireguard for wg-quick to use
272+
systemCfgPath, err := vpn.copyConfigToSystem(cfgPath, cfgName)
273+
if err != nil {
274+
return errors.NewVPNError("copy config to system", err)
275+
}
276+
277+
output.PrintMsg(fmt.Sprintf("Calling 'wg-quick up %s'", systemCfgPath), output.MsgOutput)
278+
err = console.WgUp(systemCfgPath, vpn.dirProvider)
273279
if err != nil {
274280
return errors.NewVPNError("connect", err)
275281
}
@@ -297,12 +303,13 @@ func (vpn *DefaultVpn) Disconnect(cfg string) error {
297303

298304
output.PrintMsg(fmt.Sprintf("Disconnecting from %s...", cfg), output.MsgOutput)
299305

300-
cfgDir, err := vpn.dirProvider.GetServersDir()
306+
wireguardDir, err := vpn.dirProvider.GetWireguardDir()
301307
if err != nil {
302-
return errors.NewConfigError("get servers directory", err)
308+
return errors.NewConfigError("get wireguard directory", err)
303309
}
304310

305-
err = console.WgDown(filepath.Join(cfgDir, cfg+".conf"), vpn.dirProvider)
311+
systemCfgPath := filepath.Join(wireguardDir, cfg+".conf")
312+
err = console.WgDown(systemCfgPath, vpn.dirProvider)
306313
if err != nil {
307314
return errors.NewVPNError("disconnect", err)
308315
}
@@ -417,3 +424,31 @@ func (vpn *DefaultVpn) saveWgConfig(serverName string, configContent string) (st
417424
filePath := filepath.Join(configDir, serverName+".conf")
418425
return filePath, os.WriteFile(filePath, []byte(configContent), 0o600)
419426
}
427+
428+
// copyConfigToSystem copies a config file from user directory to /etc/wireguard
429+
// This requires sudo permissions and is called during connection operations.
430+
func (vpn *DefaultVpn) copyConfigToSystem(userConfigPath, serverName string) (string, error) {
431+
wireguardDir, err := vpn.dirProvider.GetWireguardDir()
432+
if err != nil {
433+
return "", fmt.Errorf("failed to get wireguard directory: %w", err)
434+
}
435+
436+
// Ensure /etc/wireguard exists with proper permissions
437+
if err := os.MkdirAll(wireguardDir, 0o700); err != nil {
438+
return "", fmt.Errorf("failed to create wireguard directory: %w", err)
439+
}
440+
441+
// Read source config
442+
configData, err := os.ReadFile(userConfigPath)
443+
if err != nil {
444+
return "", fmt.Errorf("failed to read config: %w", err)
445+
}
446+
447+
// Write to system location
448+
systemPath := filepath.Join(wireguardDir, serverName+".conf")
449+
if err := os.WriteFile(systemPath, configData, 0o600); err != nil {
450+
return "", fmt.Errorf("failed to write system config: %w", err)
451+
}
452+
453+
return systemPath, nil
454+
}

pkg/vpn/vpn_test.go

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,13 @@ func (m *mockDirectoryProvider) GetServersFile() (string, error) {
6262
return "", nil
6363
}
6464

65+
func (m *mockDirectoryProvider) GetWireguardDir() (string, error) {
66+
if m.shouldError {
67+
return "", os.ErrPermission
68+
}
69+
return "", nil
70+
}
71+
6572
func TestGetCountryFlag(t *testing.T) {
6673
tests := []struct {
6774
name string
@@ -596,6 +603,76 @@ func TestWriteConfig_Integration(t *testing.T) {
596603
}
597604
}
598605

606+
func TestCopyConfigToSystem(t *testing.T) {
607+
dirProvider, cleanup := setupTestDir(t)
608+
defer cleanup()
609+
610+
vpn := &DefaultVpn{dirProvider: dirProvider}
611+
612+
// First create a config file in user directory
613+
server := remote.Server{
614+
Hostname: "test.example.com",
615+
IPv4AddrIn: "203.0.113.1",
616+
PublicKey: "server-public-key",
617+
}
618+
619+
userConfigPath, err := vpn.writeConfig("test-server", server, "private-key", "10.0.0.1/32", "2001:db8::1/128")
620+
if err != nil {
621+
t.Fatalf("writeConfig should succeed: %v", err)
622+
}
623+
624+
// Now copy to system directory
625+
systemPath, err := vpn.copyConfigToSystem(userConfigPath, "test-server")
626+
if err != nil {
627+
t.Fatalf("copyConfigToSystem should succeed: %v", err)
628+
}
629+
630+
// Verify system config file exists
631+
if _, err := os.Stat(systemPath); os.IsNotExist(err) {
632+
t.Error("System config file should be created")
633+
}
634+
635+
// Verify file contents match
636+
userContent, err := os.ReadFile(userConfigPath)
637+
if err != nil {
638+
t.Fatalf("Failed to read user config file: %v", err)
639+
}
640+
641+
systemContent, err := os.ReadFile(systemPath)
642+
if err != nil {
643+
t.Fatalf("Failed to read system config file: %v", err)
644+
}
645+
646+
if string(userContent) != string(systemContent) {
647+
t.Error("User and system config files should have identical content")
648+
}
649+
650+
// Verify file permissions
651+
fileInfo, err := os.Stat(systemPath)
652+
if err != nil {
653+
t.Fatalf("Failed to stat system config file: %v", err)
654+
}
655+
656+
if fileInfo.Mode().Perm() != 0o600 {
657+
t.Errorf("System config file should have 0600 permissions, got %o", fileInfo.Mode().Perm())
658+
}
659+
660+
// Verify directory permissions
661+
wireguardDir, err := dirProvider.GetWireguardDir()
662+
if err != nil {
663+
t.Fatalf("GetWireguardDir should succeed: %v", err)
664+
}
665+
666+
dirInfo, err := os.Stat(wireguardDir)
667+
if err != nil {
668+
t.Fatalf("Failed to stat wireguard directory: %v", err)
669+
}
670+
671+
if dirInfo.Mode().Perm() != 0o700 {
672+
t.Errorf("Wireguard directory should have 0700 permissions, got %o", dirInfo.Mode().Perm())
673+
}
674+
}
675+
599676
// Additional test functions to improve coverage
600677
func TestGetConnectedServers_EmptyInput(t *testing.T) {
601678
// Test with empty string to cover error paths

0 commit comments

Comments
 (0)