|
@@ -3,6 +3,7 @@ package vpn
|
|
|
import (
|
|
import (
|
|
|
"bufio"
|
|
"bufio"
|
|
|
"encoding/json"
|
|
"encoding/json"
|
|
|
|
|
+ "errors"
|
|
|
"fmt"
|
|
"fmt"
|
|
|
"log"
|
|
"log"
|
|
|
"net"
|
|
"net"
|
|
@@ -234,12 +235,32 @@ func (m *PPPServerManager) loadAll() {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
func (m *PPPServerManager) save(filename string, v any) error {
|
|
func (m *PPPServerManager) save(filename string, v any) error {
|
|
|
- _ = os.MkdirAll(m.baseDir, 0755)
|
|
|
|
|
|
|
+ if err := os.MkdirAll(m.baseDir, 0755); err != nil {
|
|
|
|
|
+ return err
|
|
|
|
|
+ }
|
|
|
data, err := json.MarshalIndent(v, "", " ")
|
|
data, err := json.MarshalIndent(v, "", " ")
|
|
|
if err != nil {
|
|
if err != nil {
|
|
|
return err
|
|
return err
|
|
|
}
|
|
}
|
|
|
- return os.WriteFile(filepath.Join(m.baseDir, filename), data, 0644)
|
|
|
|
|
|
|
+ f, err := os.CreateTemp(m.baseDir, ".ppp-*.tmp")
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ return err
|
|
|
|
|
+ }
|
|
|
|
|
+ name := f.Name()
|
|
|
|
|
+ defer os.Remove(name)
|
|
|
|
|
+ if err = f.Chmod(0600); err == nil {
|
|
|
|
|
+ _, err = f.Write(data)
|
|
|
|
|
+ }
|
|
|
|
|
+ if err == nil {
|
|
|
|
|
+ err = f.Sync()
|
|
|
|
|
+ }
|
|
|
|
|
+ if closeErr := f.Close(); err == nil {
|
|
|
|
|
+ err = closeErr
|
|
|
|
|
+ }
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ return err
|
|
|
|
|
+ }
|
|
|
|
|
+ return os.Rename(name, filepath.Join(m.baseDir, filename))
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// ======================== PPP Profiles CRUD ========================
|
|
// ======================== PPP Profiles CRUD ========================
|
|
@@ -295,16 +316,25 @@ func (m *PPPServerManager) SaveProfile(p *PPPProfile) error {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Re-apply servers if their configured DefaultProfile was updated
|
|
// Re-apply servers if their configured DefaultProfile was updated
|
|
|
|
|
+ var applyErrors []error
|
|
|
if m.l2tpServer.Enabled && m.l2tpServer.DefaultProfile == p.Name {
|
|
if m.l2tpServer.Enabled && m.l2tpServer.DefaultProfile == p.Name {
|
|
|
- _ = m.applyL2TPServer()
|
|
|
|
|
|
|
+ if err := m.applyL2TPServer(); err != nil {
|
|
|
|
|
+ applyErrors = append(applyErrors, fmt.Errorf("L2TP: %w", err))
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
if m.pptpServer.Enabled && m.pptpServer.DefaultProfile == p.Name {
|
|
if m.pptpServer.Enabled && m.pptpServer.DefaultProfile == p.Name {
|
|
|
- _ = m.applyPPTPServer()
|
|
|
|
|
|
|
+ if err := m.applyPPTPServer(); err != nil {
|
|
|
|
|
+ applyErrors = append(applyErrors, fmt.Errorf("PPTP: %w", err))
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
if m.pppoeServer.Enabled && m.pppoeServer.DefaultProfile == p.Name {
|
|
if m.pppoeServer.Enabled && m.pppoeServer.DefaultProfile == p.Name {
|
|
|
- _ = m.applyPPPoEServer()
|
|
|
|
|
|
|
+ if err := m.applyPPPoEServer(); err != nil {
|
|
|
|
|
+ applyErrors = append(applyErrors, fmt.Errorf("PPPoE: %w", err))
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ if err := errors.Join(applyErrors...); err != nil {
|
|
|
|
|
+ return fmt.Errorf("profile saved but server reapply failed: %w", err)
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
return nil
|
|
return nil
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -341,10 +371,43 @@ func (m *PPPServerManager) ListSecrets() []*PPPSecret {
|
|
|
return list
|
|
return list
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+func (m *PPPServerManager) snapshotSecretsLocked() map[string]*PPPSecret {
|
|
|
|
|
+ before := make(map[string]*PPPSecret, len(m.secrets))
|
|
|
|
|
+ for id, secret := range m.secrets {
|
|
|
|
|
+ copy := *secret
|
|
|
|
|
+ before[id] = ©
|
|
|
|
|
+ }
|
|
|
|
|
+ return before
|
|
|
|
|
+}
|
|
|
|
|
+func (m *PPPServerManager) saveSecretsLocked() error {
|
|
|
|
|
+ list := make([]*PPPSecret, 0, len(m.secrets))
|
|
|
|
|
+ for _, secret := range m.secrets {
|
|
|
|
|
+ list = append(list, secret)
|
|
|
|
|
+ }
|
|
|
|
|
+ return m.save("ppp_secrets.json", list)
|
|
|
|
|
+}
|
|
|
|
|
+func (m *PPPServerManager) commitSecretsLocked(before map[string]*PPPSecret) error {
|
|
|
|
|
+ if err := m.saveSecretsLocked(); err != nil {
|
|
|
|
|
+ m.secrets = before
|
|
|
|
|
+ return fmt.Errorf("saving PPP secrets: %w", err)
|
|
|
|
|
+ }
|
|
|
|
|
+ if err := m.syncSecretsToSystemLocked(); err != nil {
|
|
|
|
|
+ m.secrets = before
|
|
|
|
|
+ saveErr := m.saveSecretsLocked()
|
|
|
|
|
+ if saveErr != nil {
|
|
|
|
|
+ saveErr = fmt.Errorf("restoring saved PPP secrets: %w", saveErr)
|
|
|
|
|
+ }
|
|
|
|
|
+ syncErr := m.syncSecretsToSystemLocked()
|
|
|
|
|
+ if syncErr != nil {
|
|
|
|
|
+ syncErr = fmt.Errorf("reapplying previous PPP secrets: %w", syncErr)
|
|
|
|
|
+ }
|
|
|
|
|
+ return errors.Join(fmt.Errorf("applying PPP secrets: %w", err), saveErr, syncErr)
|
|
|
|
|
+ }
|
|
|
|
|
+ return nil
|
|
|
|
|
+}
|
|
|
func (m *PPPServerManager) SaveSecret(s *PPPSecret) error {
|
|
func (m *PPPServerManager) SaveSecret(s *PPPSecret) error {
|
|
|
m.mu.Lock()
|
|
m.mu.Lock()
|
|
|
defer m.mu.Unlock()
|
|
defer m.mu.Unlock()
|
|
|
-
|
|
|
|
|
if s.Name == "" || s.Password == "" {
|
|
if s.Name == "" || s.Password == "" {
|
|
|
return fmt.Errorf("username and password are required")
|
|
return fmt.Errorf("username and password are required")
|
|
|
}
|
|
}
|
|
@@ -357,49 +420,31 @@ func (m *PPPServerManager) SaveSecret(s *PPPSecret) error {
|
|
|
if s.ID == "" {
|
|
if s.ID == "" {
|
|
|
s.ID = fmt.Sprintf("secret-%d", time.Now().UnixNano())
|
|
s.ID = fmt.Sprintf("secret-%d", time.Now().UnixNano())
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
- m.secrets[s.ID] = s
|
|
|
|
|
-
|
|
|
|
|
- var list []*PPPSecret
|
|
|
|
|
- for _, sc := range m.secrets {
|
|
|
|
|
- list = append(list, sc)
|
|
|
|
|
- }
|
|
|
|
|
- if err := m.save("ppp_secrets.json", list); err != nil {
|
|
|
|
|
- return err
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- return m.syncSecretsToSystemLocked()
|
|
|
|
|
|
|
+ before := m.snapshotSecretsLocked()
|
|
|
|
|
+ copy := *s
|
|
|
|
|
+ m.secrets[s.ID] = ©
|
|
|
|
|
+ return m.commitSecretsLocked(before)
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
func (m *PPPServerManager) SetSecretState(id string, disabled bool) error {
|
|
func (m *PPPServerManager) SetSecretState(id string, disabled bool) error {
|
|
|
m.mu.Lock()
|
|
m.mu.Lock()
|
|
|
defer m.mu.Unlock()
|
|
defer m.mu.Unlock()
|
|
|
-
|
|
|
|
|
- s, ok := m.secrets[id]
|
|
|
|
|
|
|
+ secret, ok := m.secrets[id]
|
|
|
if !ok {
|
|
if !ok {
|
|
|
return fmt.Errorf("secret not found")
|
|
return fmt.Errorf("secret not found")
|
|
|
}
|
|
}
|
|
|
- s.Disabled = disabled
|
|
|
|
|
-
|
|
|
|
|
- var list []*PPPSecret
|
|
|
|
|
- for _, sc := range m.secrets {
|
|
|
|
|
- list = append(list, sc)
|
|
|
|
|
- }
|
|
|
|
|
- _ = m.save("ppp_secrets.json", list)
|
|
|
|
|
- return m.syncSecretsToSystemLocked()
|
|
|
|
|
|
|
+ before := m.snapshotSecretsLocked()
|
|
|
|
|
+ secret.Disabled = disabled
|
|
|
|
|
+ return m.commitSecretsLocked(before)
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
func (m *PPPServerManager) DeleteSecret(id string) error {
|
|
func (m *PPPServerManager) DeleteSecret(id string) error {
|
|
|
m.mu.Lock()
|
|
m.mu.Lock()
|
|
|
defer m.mu.Unlock()
|
|
defer m.mu.Unlock()
|
|
|
-
|
|
|
|
|
- delete(m.secrets, id)
|
|
|
|
|
- var list []*PPPSecret
|
|
|
|
|
- for _, sc := range m.secrets {
|
|
|
|
|
- list = append(list, sc)
|
|
|
|
|
|
|
+ if _, ok := m.secrets[id]; !ok {
|
|
|
|
|
+ return fmt.Errorf("secret not found")
|
|
|
}
|
|
}
|
|
|
- _ = m.save("ppp_secrets.json", list)
|
|
|
|
|
- return m.syncSecretsToSystemLocked()
|
|
|
|
|
|
|
+ before := m.snapshotSecretsLocked()
|
|
|
|
|
+ delete(m.secrets, id)
|
|
|
|
|
+ return m.commitSecretsLocked(before)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Helper IP math utilities for dynamic pool allocation per profile
|
|
// Helper IP math utilities for dynamic pool allocation per profile
|
|
@@ -545,6 +590,8 @@ func (m *PPPServerManager) SetPPPoEServer(cfg PPPoEServerConfig) error {
|
|
|
m.mu.Lock()
|
|
m.mu.Lock()
|
|
|
defer m.mu.Unlock()
|
|
defer m.mu.Unlock()
|
|
|
|
|
|
|
|
|
|
+ previous := m.pppoeServer
|
|
|
|
|
+
|
|
|
m.pppoeServer.Enabled = cfg.Enabled
|
|
m.pppoeServer.Enabled = cfg.Enabled
|
|
|
if cfg.ServiceName != "" {
|
|
if cfg.ServiceName != "" {
|
|
|
m.pppoeServer.ServiceName = cfg.ServiceName
|
|
m.pppoeServer.ServiceName = cfg.ServiceName
|
|
@@ -563,8 +610,14 @@ func (m *PPPServerManager) SetPPPoEServer(cfg PPPoEServerConfig) error {
|
|
|
m.pppoeServer.MaxMRU = cfg.MaxMRU
|
|
m.pppoeServer.MaxMRU = cfg.MaxMRU
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- _ = m.save("pppoe_server.json", m.pppoeServer)
|
|
|
|
|
- return m.applyPPPoEServer()
|
|
|
|
|
|
|
+ if err := m.save("pppoe_server.json", m.pppoeServer); err != nil {
|
|
|
|
|
+ m.pppoeServer = previous
|
|
|
|
|
+ return fmt.Errorf("saving PPPoE configuration: %w", err)
|
|
|
|
|
+ }
|
|
|
|
|
+ if err := m.applyPPPoEServer(); err != nil {
|
|
|
|
|
+ return fmt.Errorf("PPPoE configuration saved but could not be applied: %w", err)
|
|
|
|
|
+ }
|
|
|
|
|
+ return nil
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
func (m *PPPServerManager) applyPPPoEServer() error {
|
|
func (m *PPPServerManager) applyPPPoEServer() error {
|
|
@@ -623,7 +676,10 @@ default-mru
|
|
|
default-asyncmap
|
|
default-asyncmap
|
|
|
proxyarp
|
|
proxyarp
|
|
|
`, dns1, dns2)
|
|
`, dns1, dns2)
|
|
|
- _ = os.WriteFile("/etc/ppp/pppoe-server-options", []byte(opts), 0644)
|
|
|
|
|
|
|
+ if err := os.WriteFile("/etc/ppp/pppoe-server-options", []byte(opts), 0644); err != nil {
|
|
|
|
|
+ m.pppoeServer.LastError = err.Error()
|
|
|
|
|
+ return fmt.Errorf("writing PPPoE options: %w", err)
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
// Launch pppoe-server
|
|
// Launch pppoe-server
|
|
|
// pppoe-server -I <if> -L <local_ip> -R <start_ip> -N <count> -S <service> -O /etc/ppp/pppoe-server-options -X <pidfile>
|
|
// pppoe-server -I <if> -L <local_ip> -R <start_ip> -N <count> -S <service> -O /etc/ppp/pppoe-server-options -X <pidfile>
|
|
@@ -649,6 +705,10 @@ proxyarp
|
|
|
|
|
|
|
|
time.Sleep(300 * time.Millisecond)
|
|
time.Sleep(300 * time.Millisecond)
|
|
|
m.pppoeServer.Running = m.isProcessRunning(m.pppoePIDFile)
|
|
m.pppoeServer.Running = m.isProcessRunning(m.pppoePIDFile)
|
|
|
|
|
+ if !m.pppoeServer.Running {
|
|
|
|
|
+ m.pppoeServer.LastError = "PPPoE server did not become active"
|
|
|
|
|
+ return errors.New(m.pppoeServer.LastError)
|
|
|
|
|
+ }
|
|
|
m.pppoeServer.LastError = ""
|
|
m.pppoeServer.LastError = ""
|
|
|
return nil
|
|
return nil
|
|
|
}
|
|
}
|
|
@@ -681,6 +741,8 @@ func (m *PPPServerManager) SetPPTPServer(cfg PPTPServerConfig) error {
|
|
|
m.mu.Lock()
|
|
m.mu.Lock()
|
|
|
defer m.mu.Unlock()
|
|
defer m.mu.Unlock()
|
|
|
|
|
|
|
|
|
|
+ previous := m.pptpServer
|
|
|
|
|
+
|
|
|
m.pptpServer.Enabled = cfg.Enabled
|
|
m.pptpServer.Enabled = cfg.Enabled
|
|
|
if cfg.Port > 0 {
|
|
if cfg.Port > 0 {
|
|
|
m.pptpServer.Port = cfg.Port
|
|
m.pptpServer.Port = cfg.Port
|
|
@@ -690,13 +752,22 @@ func (m *PPPServerManager) SetPPTPServer(cfg PPTPServerConfig) error {
|
|
|
}
|
|
}
|
|
|
m.pptpServer.RequireMPPE = cfg.RequireMPPE
|
|
m.pptpServer.RequireMPPE = cfg.RequireMPPE
|
|
|
|
|
|
|
|
- _ = m.save("pptp_server.json", m.pptpServer)
|
|
|
|
|
- return m.applyPPTPServer()
|
|
|
|
|
|
|
+ if err := m.save("pptp_server.json", m.pptpServer); err != nil {
|
|
|
|
|
+ m.pptpServer = previous
|
|
|
|
|
+ return fmt.Errorf("saving PPTP configuration: %w", err)
|
|
|
|
|
+ }
|
|
|
|
|
+ if err := m.applyPPTPServer(); err != nil {
|
|
|
|
|
+ return fmt.Errorf("PPTP configuration saved but could not be applied: %w", err)
|
|
|
|
|
+ }
|
|
|
|
|
+ return nil
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
func (m *PPPServerManager) applyPPTPServer() error {
|
|
func (m *PPPServerManager) applyPPTPServer() error {
|
|
|
if !m.pptpServer.Enabled {
|
|
if !m.pptpServer.Enabled {
|
|
|
- _ = exec.Command("systemctl", "stop", "pptpd").Run()
|
|
|
|
|
|
|
+ if out, err := exec.Command("systemctl", "stop", "pptpd").CombinedOutput(); err != nil {
|
|
|
|
|
+ m.pptpServer.LastError = strings.TrimSpace(string(out))
|
|
|
|
|
+ return fmt.Errorf("stopping pptpd: %s: %w", m.pptpServer.LastError, err)
|
|
|
|
|
+ }
|
|
|
m.pptpServer.Running = false
|
|
m.pptpServer.Running = false
|
|
|
m.pptpServer.LastError = ""
|
|
m.pptpServer.LastError = ""
|
|
|
return nil
|
|
return nil
|
|
@@ -724,7 +795,10 @@ delegate
|
|
|
localip %s
|
|
localip %s
|
|
|
remoteip %s
|
|
remoteip %s
|
|
|
`, localIP, remotePool)
|
|
`, localIP, remotePool)
|
|
|
- _ = os.WriteFile("/etc/pptpd.conf", []byte(pptpdConf), 0644)
|
|
|
|
|
|
|
+ if err := os.WriteFile("/etc/pptpd.conf", []byte(pptpdConf), 0644); err != nil {
|
|
|
|
|
+ m.pptpServer.LastError = err.Error()
|
|
|
|
|
+ return fmt.Errorf("writing /etc/pptpd.conf: %w", err)
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
// Write /etc/ppp/pptpd-options
|
|
// Write /etc/ppp/pptpd-options
|
|
|
dns1 := "1.1.1.1"
|
|
dns1 := "1.1.1.1"
|
|
@@ -760,13 +834,23 @@ novj
|
|
|
novjccomp
|
|
novjccomp
|
|
|
nologfd
|
|
nologfd
|
|
|
`, mppeRule, dns1, dns2)
|
|
`, mppeRule, dns1, dns2)
|
|
|
- _ = os.WriteFile("/etc/ppp/pptpd-options", []byte(pptpdOpts), 0644)
|
|
|
|
|
|
|
+ if err := os.WriteFile("/etc/ppp/pptpd-options", []byte(pptpdOpts), 0644); err != nil {
|
|
|
|
|
+ m.pptpServer.LastError = err.Error()
|
|
|
|
|
+ return fmt.Errorf("writing /etc/ppp/pptpd-options: %w", err)
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- _ = exec.Command("systemctl", "restart", "pptpd").Run()
|
|
|
|
|
|
|
+ if out, err := exec.Command("systemctl", "restart", "pptpd").CombinedOutput(); err != nil {
|
|
|
|
|
+ m.pptpServer.LastError = strings.TrimSpace(string(out))
|
|
|
|
|
+ return fmt.Errorf("restarting pptpd: %s: %w", m.pptpServer.LastError, err)
|
|
|
|
|
+ }
|
|
|
time.Sleep(300 * time.Millisecond)
|
|
time.Sleep(300 * time.Millisecond)
|
|
|
|
|
|
|
|
out, _ := exec.Command("systemctl", "is-active", "pptpd").Output()
|
|
out, _ := exec.Command("systemctl", "is-active", "pptpd").Output()
|
|
|
m.pptpServer.Running = strings.TrimSpace(string(out)) == "active"
|
|
m.pptpServer.Running = strings.TrimSpace(string(out)) == "active"
|
|
|
|
|
+ if !m.pptpServer.Running {
|
|
|
|
|
+ m.pptpServer.LastError = "pptpd is not active"
|
|
|
|
|
+ return errors.New(m.pptpServer.LastError)
|
|
|
|
|
+ }
|
|
|
m.pptpServer.LastError = ""
|
|
m.pptpServer.LastError = ""
|
|
|
return nil
|
|
return nil
|
|
|
}
|
|
}
|
|
@@ -786,6 +870,8 @@ func (m *PPPServerManager) SetL2TPServer(cfg L2TPServerConfig) error {
|
|
|
m.mu.Lock()
|
|
m.mu.Lock()
|
|
|
defer m.mu.Unlock()
|
|
defer m.mu.Unlock()
|
|
|
|
|
|
|
|
|
|
+ previous := m.l2tpServer
|
|
|
|
|
+
|
|
|
m.l2tpServer.Enabled = cfg.Enabled
|
|
m.l2tpServer.Enabled = cfg.Enabled
|
|
|
if cfg.Port > 0 {
|
|
if cfg.Port > 0 {
|
|
|
m.l2tpServer.Port = cfg.Port
|
|
m.l2tpServer.Port = cfg.Port
|
|
@@ -798,15 +884,27 @@ func (m *PPPServerManager) SetL2TPServer(cfg L2TPServerConfig) error {
|
|
|
}
|
|
}
|
|
|
m.l2tpServer.IPSecSecret = cfg.IPSecSecret
|
|
m.l2tpServer.IPSecSecret = cfg.IPSecSecret
|
|
|
|
|
|
|
|
- _ = m.save("l2tp_server.json", m.l2tpServer)
|
|
|
|
|
- return m.applyL2TPServer()
|
|
|
|
|
|
|
+ if err := m.save("l2tp_server.json", m.l2tpServer); err != nil {
|
|
|
|
|
+ m.l2tpServer = previous
|
|
|
|
|
+ return fmt.Errorf("saving L2TP configuration: %w", err)
|
|
|
|
|
+ }
|
|
|
|
|
+ if err := m.applyL2TPServer(); err != nil {
|
|
|
|
|
+ return fmt.Errorf("L2TP configuration saved but could not be applied: %w", err)
|
|
|
|
|
+ }
|
|
|
|
|
+ return nil
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
func (m *PPPServerManager) applyL2TPServer() error {
|
|
func (m *PPPServerManager) applyL2TPServer() error {
|
|
|
if !m.l2tpServer.Enabled {
|
|
if !m.l2tpServer.Enabled {
|
|
|
// When disabled, don't stop xl2tpd completely as client may need it, just reload without LNS
|
|
// When disabled, don't stop xl2tpd completely as client may need it, just reload without LNS
|
|
|
- _ = os.WriteFile("/etc/ppp/options.xl2tpd.server", []byte("# disabled\n"), 0644)
|
|
|
|
|
- _ = exec.Command("systemctl", "reload-or-restart", "xl2tpd").Run()
|
|
|
|
|
|
|
+ if err := os.WriteFile("/etc/ppp/options.xl2tpd.server", []byte("# disabled\n"), 0644); err != nil {
|
|
|
|
|
+ m.l2tpServer.LastError = err.Error()
|
|
|
|
|
+ return fmt.Errorf("writing L2TP options: %w", err)
|
|
|
|
|
+ }
|
|
|
|
|
+ if out, err := exec.Command("systemctl", "reload-or-restart", "xl2tpd").CombinedOutput(); err != nil {
|
|
|
|
|
+ m.l2tpServer.LastError = strings.TrimSpace(string(out))
|
|
|
|
|
+ return fmt.Errorf("reloading xl2tpd: %s: %w", m.l2tpServer.LastError, err)
|
|
|
|
|
+ }
|
|
|
m.l2tpServer.Running = false
|
|
m.l2tpServer.Running = false
|
|
|
m.l2tpServer.LastError = ""
|
|
m.l2tpServer.LastError = ""
|
|
|
return nil
|
|
return nil
|
|
@@ -832,7 +930,10 @@ func (m *PPPServerManager) applyL2TPServer() error {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Write /etc/xl2tpd/xl2tpd.conf with [lns default]
|
|
// Write /etc/xl2tpd/xl2tpd.conf with [lns default]
|
|
|
- _ = os.MkdirAll("/etc/xl2tpd", 0755)
|
|
|
|
|
|
|
+ if err := os.MkdirAll("/etc/xl2tpd", 0755); err != nil {
|
|
|
|
|
+ m.l2tpServer.LastError = err.Error()
|
|
|
|
|
+ return fmt.Errorf("creating L2TP directory: %w", err)
|
|
|
|
|
+ }
|
|
|
xl2tpdConf := fmt.Sprintf(`[global]
|
|
xl2tpdConf := fmt.Sprintf(`[global]
|
|
|
port = %d
|
|
port = %d
|
|
|
|
|
|
|
@@ -846,7 +947,10 @@ name = l2tpd
|
|
|
pppoptfile = /etc/ppp/options.xl2tpd.server
|
|
pppoptfile = /etc/ppp/options.xl2tpd.server
|
|
|
length bit = yes
|
|
length bit = yes
|
|
|
`, m.l2tpServer.Port, remotePool, localIP)
|
|
`, m.l2tpServer.Port, remotePool, localIP)
|
|
|
- _ = os.WriteFile("/etc/xl2tpd/xl2tpd.conf", []byte(xl2tpdConf), 0644)
|
|
|
|
|
|
|
+ if err := os.WriteFile("/etc/xl2tpd/xl2tpd.conf", []byte(xl2tpdConf), 0644); err != nil {
|
|
|
|
|
+ m.l2tpServer.LastError = err.Error()
|
|
|
|
|
+ return fmt.Errorf("writing /etc/xl2tpd/xl2tpd.conf: %w", err)
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
// Write /etc/ppp/options.xl2tpd.server
|
|
// Write /etc/ppp/options.xl2tpd.server
|
|
|
dns1 := "1.1.1.1"
|
|
dns1 := "1.1.1.1"
|
|
@@ -872,7 +976,10 @@ mru 1400
|
|
|
proxyarp
|
|
proxyarp
|
|
|
nodefaultroute
|
|
nodefaultroute
|
|
|
`, dns1, dns2)
|
|
`, dns1, dns2)
|
|
|
- _ = os.WriteFile("/etc/ppp/options.xl2tpd.server", []byte(opts), 0644)
|
|
|
|
|
|
|
+ if err := os.WriteFile("/etc/ppp/options.xl2tpd.server", []byte(opts), 0644); err != nil {
|
|
|
|
|
+ m.l2tpServer.LastError = err.Error()
|
|
|
|
|
+ return fmt.Errorf("writing /etc/ppp/options.xl2tpd.server: %w", err)
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
// Clean restart of xl2tpd to avoid stale PID locks
|
|
// Clean restart of xl2tpd to avoid stale PID locks
|
|
|
_ = exec.Command("systemctl", "stop", "xl2tpd").Run()
|
|
_ = exec.Command("systemctl", "stop", "xl2tpd").Run()
|
|
@@ -880,11 +987,18 @@ nodefaultroute
|
|
|
_ = exec.Command("pkill", "-9", "-x", "xl2tpd").Run()
|
|
_ = exec.Command("pkill", "-9", "-x", "xl2tpd").Run()
|
|
|
_ = os.Remove("/var/run/xl2tpd.pid")
|
|
_ = os.Remove("/var/run/xl2tpd.pid")
|
|
|
_ = os.Remove("/run/xl2tpd.pid")
|
|
_ = os.Remove("/run/xl2tpd.pid")
|
|
|
- _ = exec.Command("systemctl", "start", "xl2tpd").Run()
|
|
|
|
|
|
|
+ if out, err := exec.Command("systemctl", "start", "xl2tpd").CombinedOutput(); err != nil {
|
|
|
|
|
+ m.l2tpServer.LastError = strings.TrimSpace(string(out))
|
|
|
|
|
+ return fmt.Errorf("starting xl2tpd: %s: %w", m.l2tpServer.LastError, err)
|
|
|
|
|
+ }
|
|
|
time.Sleep(300 * time.Millisecond)
|
|
time.Sleep(300 * time.Millisecond)
|
|
|
|
|
|
|
|
out, _ := exec.Command("systemctl", "is-active", "xl2tpd").Output()
|
|
out, _ := exec.Command("systemctl", "is-active", "xl2tpd").Output()
|
|
|
m.l2tpServer.Running = strings.TrimSpace(string(out)) == "active"
|
|
m.l2tpServer.Running = strings.TrimSpace(string(out)) == "active"
|
|
|
|
|
+ if !m.l2tpServer.Running {
|
|
|
|
|
+ m.l2tpServer.LastError = "xl2tpd is not active"
|
|
|
|
|
+ return errors.New(m.l2tpServer.LastError)
|
|
|
|
|
+ }
|
|
|
m.l2tpServer.LastError = ""
|
|
m.l2tpServer.LastError = ""
|
|
|
return nil
|
|
return nil
|
|
|
}
|
|
}
|
|
@@ -904,6 +1018,8 @@ func (m *PPPServerManager) SetIPSecServer(cfg IPSecServerConfig) error {
|
|
|
m.mu.Lock()
|
|
m.mu.Lock()
|
|
|
defer m.mu.Unlock()
|
|
defer m.mu.Unlock()
|
|
|
|
|
|
|
|
|
|
+ previous := m.ipsecServer
|
|
|
|
|
+
|
|
|
m.ipsecServer.Enabled = cfg.Enabled
|
|
m.ipsecServer.Enabled = cfg.Enabled
|
|
|
if cfg.PoolAddress != "" {
|
|
if cfg.PoolAddress != "" {
|
|
|
m.ipsecServer.PoolAddress = cfg.PoolAddress
|
|
m.ipsecServer.PoolAddress = cfg.PoolAddress
|
|
@@ -918,21 +1034,42 @@ func (m *PPPServerManager) SetIPSecServer(cfg IPSecServerConfig) error {
|
|
|
m.ipsecServer.DNSServers = cfg.DNSServers
|
|
m.ipsecServer.DNSServers = cfg.DNSServers
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- _ = m.save("ipsec_server.json", m.ipsecServer)
|
|
|
|
|
- return m.applyIPSecServer()
|
|
|
|
|
|
|
+ if err := m.save("ipsec_server.json", m.ipsecServer); err != nil {
|
|
|
|
|
+ m.ipsecServer = previous
|
|
|
|
|
+ return fmt.Errorf("saving IPSec configuration: %w", err)
|
|
|
|
|
+ }
|
|
|
|
|
+ if err := m.applyIPSecServer(); err != nil {
|
|
|
|
|
+ return fmt.Errorf("IPSec configuration saved but could not be applied: %w", err)
|
|
|
|
|
+ }
|
|
|
|
|
+ return nil
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
func (m *PPPServerManager) applyIPSecServer() error {
|
|
func (m *PPPServerManager) applyIPSecServer() error {
|
|
|
confFile := "/etc/swanctl/conf.d/rw-server.conf"
|
|
confFile := "/etc/swanctl/conf.d/rw-server.conf"
|
|
|
if !m.ipsecServer.Enabled {
|
|
if !m.ipsecServer.Enabled {
|
|
|
- _ = os.Remove(confFile)
|
|
|
|
|
- _ = exec.Command("swanctl", "--load-all").Run()
|
|
|
|
|
|
|
+ if err := os.Remove(confFile); err != nil && !os.IsNotExist(err) {
|
|
|
|
|
+ return fmt.Errorf("removing IPsec server configuration: %w", err)
|
|
|
|
|
+ }
|
|
|
|
|
+ if out, err := exec.Command("swanctl", "--load-all").CombinedOutput(); err != nil {
|
|
|
|
|
+ return fmt.Errorf("reloading IPsec configuration: %w: %s", err, strings.TrimSpace(string(out)))
|
|
|
|
|
+ }
|
|
|
|
|
+ out, err := exec.Command("swanctl", "--list-conns").CombinedOutput()
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ return fmt.Errorf("checking IPsec connections: %w: %s", err, strings.TrimSpace(string(out)))
|
|
|
|
|
+ }
|
|
|
|
|
+ for _, line := range strings.Split(string(out), "\n") {
|
|
|
|
|
+ if strings.HasPrefix(strings.TrimSpace(line), "rw-server:") {
|
|
|
|
|
+ return fmt.Errorf("IPsec server connection remains loaded after reload")
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
m.ipsecServer.Running = false
|
|
m.ipsecServer.Running = false
|
|
|
m.ipsecServer.LastError = ""
|
|
m.ipsecServer.LastError = ""
|
|
|
return nil
|
|
return nil
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- _ = os.MkdirAll("/etc/swanctl/conf.d", 0755)
|
|
|
|
|
|
|
+ if err := os.MkdirAll("/etc/swanctl/conf.d", 0755); err != nil {
|
|
|
|
|
+ return fmt.Errorf("creating IPsec configuration directory: %w", err)
|
|
|
|
|
+ }
|
|
|
swanConf := fmt.Sprintf(`connections {
|
|
swanConf := fmt.Sprintf(`connections {
|
|
|
rw-server {
|
|
rw-server {
|
|
|
local_addrs = %%any
|
|
local_addrs = %%any
|