wireguard.go 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737
  1. package vpn
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "net"
  6. "os"
  7. "os/exec"
  8. "path/filepath"
  9. "strconv"
  10. "strings"
  11. "sync"
  12. "time"
  13. "github.com/vishvananda/netlink"
  14. )
  15. const (
  16. WGInterfacesStorePath = "/etc/debtik/vpn/wireguard_interfaces.json"
  17. WGPeersStorePath = "/etc/debtik/vpn/wireguard_peers.json"
  18. )
  19. type WireGuardManager struct {
  20. mu sync.Mutex
  21. interfaces []WireGuardInterface
  22. peers []WireGuardPeer
  23. }
  24. func NewWireGuardManager() *WireGuardManager {
  25. m := &WireGuardManager{
  26. interfaces: make([]WireGuardInterface, 0),
  27. peers: make([]WireGuardPeer, 0),
  28. }
  29. m.load()
  30. // Autostart all enabled WireGuard interfaces
  31. for _, iface := range m.interfaces {
  32. if !iface.Disabled {
  33. if link, err := netlink.LinkByName(iface.Name); err == nil && link.Type() == "wireguard" {
  34. continue
  35. }
  36. name := iface.Name
  37. go func(n string) {
  38. _ = m.startWGInterface(n)
  39. }(name)
  40. }
  41. }
  42. return m
  43. }
  44. // CleanEndpoint normalizes an endpoint address and port.
  45. // If addr contains a port (e.g. "1.2.3.4:51820", "[2001:db8::1]:51820"), it strips the port
  46. // from addr and returns the clean host and the parsed port.
  47. func CleanEndpoint(addr string, port int) (string, int) {
  48. addr = strings.TrimSpace(addr)
  49. if addr == "" || addr == "(none)" {
  50. return "", 0
  51. }
  52. // Remove multiple colons/ports if corrupted (e.g. "10.2.145.253:37616:37616")
  53. for {
  54. if h, p, err := net.SplitHostPort(addr); err == nil {
  55. if parsedPort, err := strconv.Atoi(p); err == nil && parsedPort > 0 {
  56. port = parsedPort
  57. }
  58. addr = h
  59. } else if strings.Contains(addr, ":") && !strings.HasPrefix(addr, "[") {
  60. lastColon := strings.LastIndex(addr, ":")
  61. maybePort := addr[lastColon+1:]
  62. if parsedPort, err := strconv.Atoi(maybePort); err == nil && parsedPort > 0 {
  63. port = parsedPort
  64. addr = addr[:lastColon]
  65. } else {
  66. break
  67. }
  68. } else {
  69. break
  70. }
  71. }
  72. return addr, port
  73. }
  74. func (m *WireGuardManager) load() {
  75. if data, err := os.ReadFile(WGInterfacesStorePath); err == nil {
  76. _ = json.Unmarshal(data, &m.interfaces)
  77. }
  78. if data, err := os.ReadFile(WGPeersStorePath); err == nil {
  79. _ = json.Unmarshal(data, &m.peers)
  80. }
  81. if m.interfaces == nil {
  82. m.interfaces = make([]WireGuardInterface, 0)
  83. }
  84. if m.peers == nil {
  85. m.peers = make([]WireGuardPeer, 0)
  86. }
  87. // Sanitize endpoints for all stored peers
  88. for i := range m.peers {
  89. m.peers[i].EndpointAddress, m.peers[i].EndpointPort = CleanEndpoint(m.peers[i].EndpointAddress, m.peers[i].EndpointPort)
  90. }
  91. }
  92. func (m *WireGuardManager) save() error {
  93. _ = os.MkdirAll(filepath.Dir(WGInterfacesStorePath), 0755)
  94. if data, err := json.MarshalIndent(m.interfaces, "", " "); err != nil {
  95. return err
  96. } else if err = os.WriteFile(WGInterfacesStorePath, data, 0600); err != nil {
  97. return err
  98. }
  99. if data, err := json.MarshalIndent(m.peers, "", " "); err != nil {
  100. return err
  101. } else if err = os.WriteFile(WGPeersStorePath, data, 0600); err != nil {
  102. return err
  103. }
  104. return nil
  105. }
  106. func (m *WireGuardManager) GenerateKeyPair() (string, string, error) {
  107. privCmd := exec.Command("wg", "genkey")
  108. privOut, err := privCmd.Output()
  109. if err != nil {
  110. return "", "", fmt.Errorf("failed to generate private key: %w", err)
  111. }
  112. privKey := strings.TrimSpace(string(privOut))
  113. pubKey, err := m.CalcPublicKey(privKey)
  114. if err != nil {
  115. return "", "", err
  116. }
  117. return privKey, pubKey, nil
  118. }
  119. func (m *WireGuardManager) CalcPublicKey(privateKey string) (string, error) {
  120. pubCmd := exec.Command("wg", "pubkey")
  121. pubCmd.Stdin = strings.NewReader(strings.TrimSpace(privateKey))
  122. pubOut, err := pubCmd.Output()
  123. if err != nil {
  124. return "", fmt.Errorf("failed to derive public key: %w", err)
  125. }
  126. return strings.TrimSpace(string(pubOut)), nil
  127. }
  128. // ListInterfaces returns all WireGuard interfaces with MikroTik running flags
  129. func (m *WireGuardManager) ListInterfaces() []WireGuardInterface {
  130. m.mu.Lock()
  131. defer m.mu.Unlock()
  132. res := make([]WireGuardInterface, len(m.interfaces))
  133. for i, iface := range m.interfaces {
  134. res[i] = iface
  135. // Check running state via netlink
  136. if _, err := netlink.LinkByName(iface.Name); err == nil {
  137. res[i].Running = true
  138. } else {
  139. res[i].Running = false
  140. }
  141. // Count attached peers
  142. pCount := 0
  143. for _, p := range m.peers {
  144. if p.Interface == iface.Name {
  145. pCount++
  146. }
  147. }
  148. res[i].PeersCount = pCount
  149. }
  150. return res
  151. }
  152. func (m *WireGuardManager) SaveInterface(req SaveWireGuardInterfaceRequest) error {
  153. m.mu.Lock()
  154. defer m.mu.Unlock()
  155. if req.Name == "" {
  156. return fmt.Errorf("interface name is required (e.g. wg0)")
  157. }
  158. if req.ListenPort <= 0 {
  159. req.ListenPort = 51820
  160. }
  161. if req.MTU <= 0 {
  162. req.MTU = 1420
  163. }
  164. var old *WireGuardInterface
  165. for i := range m.interfaces {
  166. if m.interfaces[i].Name == req.Name || (req.OrigName != "" && m.interfaces[i].Name == req.OrigName) {
  167. copy := m.interfaces[i]
  168. old = &copy
  169. break
  170. }
  171. }
  172. if req.PrivateKey == "" && old != nil {
  173. req.PrivateKey = old.PrivateKey
  174. req.PublicKey = old.PublicKey
  175. }
  176. // Auto generate or derive public key
  177. if req.PrivateKey != "" {
  178. pub, err := m.CalcPublicKey(req.PrivateKey)
  179. if err != nil {
  180. return err
  181. }
  182. if pub == "" {
  183. return fmt.Errorf("invalid private key")
  184. }
  185. req.PublicKey = pub
  186. } else {
  187. priv, pub, err := m.GenerateKeyPair()
  188. if err != nil {
  189. return err
  190. }
  191. req.PrivateKey = priv
  192. req.PublicKey = pub
  193. }
  194. // Support interface renaming: clean up old interface if renamed
  195. if req.OrigName != "" && req.OrigName != req.Name {
  196. _ = m.stopWGInterface(req.OrigName)
  197. _ = os.Remove(fmt.Sprintf("/etc/wireguard/%s.conf", req.OrigName))
  198. // Update all peers associated with OrigName
  199. for i := range m.peers {
  200. if m.peers[i].Interface == req.OrigName {
  201. m.peers[i].Interface = req.Name
  202. }
  203. }
  204. // Remove old interface from slice
  205. updated := make([]WireGuardInterface, 0)
  206. for _, iface := range m.interfaces {
  207. if iface.Name != req.OrigName {
  208. updated = append(updated, iface)
  209. }
  210. }
  211. m.interfaces = updated
  212. }
  213. found := false
  214. for i, existing := range m.interfaces {
  215. if existing.Name == req.Name {
  216. m.interfaces[i] = req.WireGuardInterface
  217. found = true
  218. break
  219. }
  220. }
  221. if !found {
  222. m.interfaces = append(m.interfaces, req.WireGuardInterface)
  223. }
  224. if err := m.save(); err != nil {
  225. return err
  226. }
  227. if err := m.generateWGConfigFile(req.Name); err != nil {
  228. return err
  229. }
  230. if old != nil && old.Name == req.Name && old.Disabled == req.Disabled && old.PrivateKey == req.PrivateKey && old.ListenPort == req.ListenPort && old.Address == req.Address && old.MTU == req.MTU {
  231. return nil
  232. }
  233. if !req.Disabled {
  234. return m.startWGInterface(req.Name)
  235. }
  236. return m.stopWGInterface(req.Name)
  237. }
  238. func (m *WireGuardManager) DeleteInterface(name string) error {
  239. m.mu.Lock()
  240. defer m.mu.Unlock()
  241. _ = m.stopWGInterface(name)
  242. _ = os.Remove(fmt.Sprintf("/etc/wireguard/%s.conf", name))
  243. updatedIfaces := make([]WireGuardInterface, 0)
  244. for _, iface := range m.interfaces {
  245. if iface.Name != name {
  246. updatedIfaces = append(updatedIfaces, iface)
  247. }
  248. }
  249. m.interfaces = updatedIfaces
  250. // Remove associated peers
  251. updatedPeers := make([]WireGuardPeer, 0)
  252. for _, p := range m.peers {
  253. if p.Interface != name {
  254. updatedPeers = append(updatedPeers, p)
  255. }
  256. }
  257. m.peers = updatedPeers
  258. return m.save()
  259. }
  260. func (m *WireGuardManager) SetInterfaceEnabled(name string, enabled bool) error {
  261. m.mu.Lock()
  262. defer m.mu.Unlock()
  263. for i, iface := range m.interfaces {
  264. if iface.Name == name {
  265. m.interfaces[i].Disabled = !enabled
  266. if err := m.save(); err != nil {
  267. return err
  268. }
  269. if enabled {
  270. return m.startWGInterface(name)
  271. } else {
  272. return m.stopWGInterface(name)
  273. }
  274. }
  275. }
  276. return fmt.Errorf("interface %q not found", name)
  277. }
  278. func (m *WireGuardManager) StartInterface(name string) error {
  279. m.mu.Lock()
  280. defer m.mu.Unlock()
  281. return m.startWGInterface(name)
  282. }
  283. func (m *WireGuardManager) StopInterface(name string) error {
  284. m.mu.Lock()
  285. defer m.mu.Unlock()
  286. return m.stopWGInterface(name)
  287. }
  288. func (m *WireGuardManager) startWGInterface(name string) error {
  289. if err := m.generateWGConfigFile(name); err != nil {
  290. return err
  291. }
  292. _ = exec.Command("wg-quick", "down", name).Run()
  293. cmd := exec.Command("wg-quick", "up", name)
  294. if out, err := cmd.CombinedOutput(); err != nil {
  295. return fmt.Errorf("failed to start %s: %s: %w", name, string(out), err)
  296. }
  297. _ = exec.Command("systemctl", "enable", fmt.Sprintf("wg-quick@%s", name)).Run()
  298. return nil
  299. }
  300. func (m *WireGuardManager) stopWGInterface(name string) error {
  301. _ = exec.Command("systemctl", "disable", fmt.Sprintf("wg-quick@%s", name)).Run()
  302. if _, err := netlink.LinkByName(name); err != nil {
  303. if _, absent := err.(netlink.LinkNotFoundError); absent {
  304. return nil
  305. }
  306. return err
  307. }
  308. cmd := exec.Command("wg-quick", "down", name)
  309. if out, err := cmd.CombinedOutput(); err != nil {
  310. return fmt.Errorf("failed to stop %s: %s: %w", name, out, err)
  311. }
  312. return nil
  313. }
  314. // ----------------- Peers Management (MikroTik style) -----------------
  315. func (m *WireGuardManager) ListPeers(ifaceFilter string) []WireGuardPeer {
  316. m.mu.Lock()
  317. defer m.mu.Unlock()
  318. // Parse runtime dump from `wg show <iface> dump`
  319. dumpMap := make(map[string]map[string]WireGuardPeer) // iface -> pubkey -> peer stats
  320. for _, iface := range m.interfaces {
  321. if _, err := netlink.LinkByName(iface.Name); err == nil {
  322. cmd := exec.Command("wg", "show", iface.Name, "dump")
  323. out, err := cmd.Output()
  324. if err == nil {
  325. dumpMap[iface.Name] = make(map[string]WireGuardPeer)
  326. lines := strings.Split(strings.TrimSpace(string(out)), "\n")
  327. for idx, line := range lines {
  328. if idx == 0 {
  329. continue // skip interface line
  330. }
  331. parts := strings.Fields(line)
  332. if len(parts) >= 8 {
  333. pubKey := parts[0]
  334. endpoint := parts[2]
  335. if endpoint == "(none)" {
  336. endpoint = ""
  337. }
  338. handshakeUnix, _ := strconv.ParseInt(parts[4], 10, 64)
  339. rx, _ := strconv.ParseUint(parts[5], 10, 64)
  340. tx, _ := strconv.ParseUint(parts[6], 10, 64)
  341. handshakeStr := "Never"
  342. if handshakeUnix > 0 {
  343. handshakeStr = time.Since(time.Unix(handshakeUnix, 0)).Round(time.Second).String() + " ago"
  344. }
  345. epHost, epPort := CleanEndpoint(endpoint, 0)
  346. dumpMap[iface.Name][pubKey] = WireGuardPeer{
  347. EndpointAddress: epHost,
  348. EndpointPort: epPort,
  349. LatestHandshake: handshakeStr,
  350. TransferRxBytes: rx,
  351. TransferTxBytes: tx,
  352. TransferRxHuman: formatTrafficBytes(rx),
  353. TransferTxHuman: formatTrafficBytes(tx),
  354. }
  355. }
  356. }
  357. }
  358. }
  359. }
  360. result := make([]WireGuardPeer, 0)
  361. for _, p := range m.peers {
  362. if ifaceFilter != "" && p.Interface != ifaceFilter {
  363. continue
  364. }
  365. item := p
  366. // Overlay runtime stats if available
  367. if ifaceDumps, ok := dumpMap[p.Interface]; ok {
  368. if stats, has := ifaceDumps[p.PublicKey]; has {
  369. if stats.EndpointAddress != "" {
  370. item.EndpointAddress = stats.EndpointAddress
  371. if stats.EndpointPort > 0 {
  372. item.EndpointPort = stats.EndpointPort
  373. }
  374. }
  375. item.LatestHandshake = stats.LatestHandshake
  376. item.TransferRxBytes = stats.TransferRxBytes
  377. item.TransferTxBytes = stats.TransferTxBytes
  378. item.TransferRxHuman = stats.TransferRxHuman
  379. item.TransferTxHuman = stats.TransferTxHuman
  380. }
  381. }
  382. item.EndpointAddress, item.EndpointPort = CleanEndpoint(item.EndpointAddress, item.EndpointPort)
  383. result = append(result, item)
  384. }
  385. return result
  386. }
  387. func (m *WireGuardManager) SavePeer(req SaveWireGuardPeerRequest) error {
  388. m.mu.Lock()
  389. defer m.mu.Unlock()
  390. if req.Interface == "" {
  391. return fmt.Errorf("target WireGuard interface is required")
  392. }
  393. if strings.TrimSpace(req.PublicKey) == "" {
  394. return fmt.Errorf("peer public key is required")
  395. }
  396. req.PublicKey = strings.TrimSpace(req.PublicKey)
  397. // Validate that peer public key is not the same as the interface's own public key
  398. for _, iface := range m.interfaces {
  399. if iface.Name == req.Interface && iface.PublicKey == req.PublicKey {
  400. return fmt.Errorf("peer public key cannot be identical to interface %s public key", iface.Name)
  401. }
  402. }
  403. // Handle modifying an existing peer's public key or moving to a different interface
  404. if req.OrigPublicKey != "" && (req.OrigPublicKey != req.PublicKey || (req.OrigInterface != "" && req.OrigInterface != req.Interface)) {
  405. oldIface := req.OrigInterface
  406. if oldIface == "" {
  407. oldIface = req.Interface
  408. }
  409. // Remove old peer from runtime
  410. if _, err := netlink.LinkByName(oldIface); err == nil {
  411. if out, err := exec.Command("wg", "set", oldIface, "peer", req.OrigPublicKey, "remove").CombinedOutput(); err != nil {
  412. return fmt.Errorf("wg command failed: %s: %w", out, err)
  413. }
  414. }
  415. // Remove old peer entry
  416. updated := make([]WireGuardPeer, 0)
  417. for _, p := range m.peers {
  418. if !(p.Interface == oldIface && p.PublicKey == req.OrigPublicKey) {
  419. updated = append(updated, p)
  420. }
  421. }
  422. m.peers = updated
  423. if oldIface != req.Interface {
  424. _ = m.generateWGConfigFile(oldIface)
  425. }
  426. }
  427. req.EndpointAddress, req.EndpointPort = CleanEndpoint(req.EndpointAddress, req.EndpointPort)
  428. req.WireGuardPeer.EndpointAddress = req.EndpointAddress
  429. req.WireGuardPeer.EndpointPort = req.EndpointPort
  430. clearEndpoint := false
  431. for _, old := range m.peers {
  432. if old.Interface == req.Interface && old.PublicKey == req.PublicKey && old.EndpointAddress != "" && req.EndpointAddress == "" {
  433. clearEndpoint = true
  434. }
  435. }
  436. found := false
  437. for i, existing := range m.peers {
  438. if existing.PublicKey == req.PublicKey && existing.Interface == req.Interface {
  439. m.peers[i] = req.WireGuardPeer
  440. found = true
  441. break
  442. }
  443. }
  444. if !found {
  445. m.peers = append(m.peers, req.WireGuardPeer)
  446. }
  447. if err := m.save(); err != nil {
  448. return err
  449. }
  450. if err := m.generateWGConfigFile(req.Interface); err != nil {
  451. return err
  452. }
  453. // Apply dynamically to kernel if running
  454. if _, err := netlink.LinkByName(req.Interface); err == nil {
  455. if req.Disabled {
  456. if out, err := exec.Command("wg", "set", req.Interface, "peer", req.PublicKey, "remove").CombinedOutput(); err != nil {
  457. return fmt.Errorf("wg command failed: %s: %w", out, err)
  458. }
  459. } else {
  460. if clearEndpoint {
  461. if out, err := exec.Command("wg", "set", req.Interface, "peer", req.PublicKey, "remove").CombinedOutput(); err != nil {
  462. return fmt.Errorf("clear WireGuard endpoint: %s: %w", out, err)
  463. }
  464. }
  465. args := []string{"set", req.Interface, "peer", req.PublicKey}
  466. args = append(args, "allowed-ips", strings.Join(req.AllowedIPs, ","))
  467. if req.EndpointAddress != "" {
  468. ep := req.EndpointAddress
  469. if req.EndpointPort > 0 {
  470. ep = net.JoinHostPort(req.EndpointAddress, strconv.Itoa(req.EndpointPort))
  471. }
  472. args = append(args, "endpoint", ep)
  473. }
  474. args = append(args, "persistent-keepalive", strconv.Itoa(req.PersistentKeepalive))
  475. if true {
  476. pskFile, err := os.CreateTemp("", "wg-psk-*")
  477. if err == nil {
  478. _, _ = pskFile.WriteString(req.PresharedKey)
  479. _ = pskFile.Close()
  480. args = append(args, "preshared-key", pskFile.Name())
  481. defer os.Remove(pskFile.Name())
  482. }
  483. }
  484. if out, err := exec.Command("wg", args...).CombinedOutput(); err != nil {
  485. return fmt.Errorf("wg set failed: %s: %w", out, err)
  486. }
  487. }
  488. }
  489. return nil
  490. }
  491. func (m *WireGuardManager) SetPeerEnabled(ifaceName, pubKey string, enabled bool) error {
  492. m.mu.Lock()
  493. defer m.mu.Unlock()
  494. found := false
  495. for i, p := range m.peers {
  496. if p.Interface == ifaceName && p.PublicKey == pubKey {
  497. m.peers[i].Disabled = !enabled
  498. found = true
  499. break
  500. }
  501. }
  502. if !found {
  503. return fmt.Errorf("peer with public key %s not found on interface %s", pubKey, ifaceName)
  504. }
  505. if err := m.save(); err != nil {
  506. return err
  507. }
  508. _ = m.generateWGConfigFile(ifaceName)
  509. if _, err := netlink.LinkByName(ifaceName); err == nil {
  510. if !enabled {
  511. if out, err := exec.Command("wg", "set", ifaceName, "peer", pubKey, "remove").CombinedOutput(); err != nil {
  512. return fmt.Errorf("wg command failed: %s: %w", out, err)
  513. }
  514. } else {
  515. for _, p := range m.peers {
  516. if p.Interface == ifaceName && p.PublicKey == pubKey {
  517. args := []string{"set", ifaceName, "peer", pubKey}
  518. if len(p.AllowedIPs) > 0 {
  519. args = append(args, "allowed-ips", strings.Join(p.AllowedIPs, ","))
  520. }
  521. epHost, epPort := CleanEndpoint(p.EndpointAddress, p.EndpointPort)
  522. if epHost != "" {
  523. ep := epHost
  524. if epPort > 0 {
  525. ep = net.JoinHostPort(epHost, strconv.Itoa(epPort))
  526. }
  527. args = append(args, "endpoint", ep)
  528. }
  529. if p.PersistentKeepalive > 0 {
  530. args = append(args, "persistent-keepalive", strconv.Itoa(p.PersistentKeepalive))
  531. }
  532. if p.PresharedKey != "" {
  533. pskFile, err := os.CreateTemp("", "wg-psk-*")
  534. if err == nil {
  535. _, _ = pskFile.WriteString(p.PresharedKey)
  536. _ = pskFile.Close()
  537. args = append(args, "preshared-key", pskFile.Name())
  538. defer os.Remove(pskFile.Name())
  539. }
  540. }
  541. if out, err := exec.Command("wg", args...).CombinedOutput(); err != nil {
  542. return fmt.Errorf("wg command failed: %s: %w", out, err)
  543. }
  544. break
  545. }
  546. }
  547. }
  548. }
  549. return nil
  550. }
  551. func (m *WireGuardManager) DeletePeer(ifaceName, pubKey string) error {
  552. m.mu.Lock()
  553. defer m.mu.Unlock()
  554. updated := make([]WireGuardPeer, 0)
  555. for _, p := range m.peers {
  556. if p.Interface == ifaceName && p.PublicKey == pubKey {
  557. continue
  558. }
  559. updated = append(updated, p)
  560. }
  561. m.peers = updated
  562. if err := m.save(); err != nil {
  563. return err
  564. }
  565. _ = m.generateWGConfigFile(ifaceName)
  566. // Remove from runtime
  567. if _, err := netlink.LinkByName(ifaceName); err == nil {
  568. if out, err := exec.Command("wg", "set", ifaceName, "peer", pubKey, "remove").CombinedOutput(); err != nil {
  569. return fmt.Errorf("wg command failed: %s: %w", out, err)
  570. }
  571. }
  572. return nil
  573. }
  574. func (m *WireGuardManager) generateWGConfigFile(ifaceName string) error {
  575. var targetIface *WireGuardInterface
  576. for _, i := range m.interfaces {
  577. if i.Name == ifaceName {
  578. targetIface = &i
  579. break
  580. }
  581. }
  582. if targetIface == nil {
  583. return nil
  584. }
  585. _ = os.MkdirAll("/etc/wireguard", 0700)
  586. var sb strings.Builder
  587. sb.WriteString(fmt.Sprintf("# MikroTik-Style WireGuard Interface: %s\n", targetIface.Name))
  588. if targetIface.Comment != "" {
  589. sb.WriteString(fmt.Sprintf("# Comment: %s\n", targetIface.Comment))
  590. }
  591. sb.WriteString("[Interface]\n")
  592. sb.WriteString(fmt.Sprintf("PrivateKey = %s\n", targetIface.PrivateKey))
  593. if targetIface.Address != "" && targetIface.Address != "0.0.0.0/0" {
  594. sb.WriteString(fmt.Sprintf("Address = %s\n", targetIface.Address))
  595. }
  596. sb.WriteString(fmt.Sprintf("ListenPort = %d\n", targetIface.ListenPort))
  597. if targetIface.MTU > 0 {
  598. sb.WriteString(fmt.Sprintf("MTU = %d\n", targetIface.MTU))
  599. }
  600. sb.WriteString("Table = off\n")
  601. sb.WriteString("\n")
  602. for _, p := range m.peers {
  603. if p.Interface == ifaceName && !p.Disabled {
  604. sb.WriteString(fmt.Sprintf("# Peer: %s\n", p.Name))
  605. sb.WriteString("[Peer]\n")
  606. sb.WriteString(fmt.Sprintf("PublicKey = %s\n", p.PublicKey))
  607. if len(p.AllowedIPs) > 0 {
  608. sb.WriteString(fmt.Sprintf("AllowedIPs = %s\n", strings.Join(p.AllowedIPs, ", ")))
  609. }
  610. epHost, epPort := CleanEndpoint(p.EndpointAddress, p.EndpointPort)
  611. if epHost != "" {
  612. ep := epHost
  613. if epPort > 0 {
  614. ep = net.JoinHostPort(epHost, strconv.Itoa(epPort))
  615. }
  616. sb.WriteString(fmt.Sprintf("Endpoint = %s\n", ep))
  617. }
  618. if p.PersistentKeepalive > 0 {
  619. sb.WriteString(fmt.Sprintf("PersistentKeepalive = %d\n", p.PersistentKeepalive))
  620. }
  621. if p.PresharedKey != "" {
  622. sb.WriteString(fmt.Sprintf("PresharedKey = %s\n", p.PresharedKey))
  623. }
  624. sb.WriteString("\n")
  625. }
  626. }
  627. path := fmt.Sprintf("/etc/wireguard/%s.conf", ifaceName)
  628. return os.WriteFile(path, []byte(sb.String()), 0600)
  629. }
  630. // GenerateClientConfig creates a ready-to-use client .conf for mobile/desktop
  631. func (m *WireGuardManager) GenerateClientConfig(ifaceName, clientPrivKey, clientAddress, serverEndpoint, dns string) (string, error) {
  632. m.mu.Lock()
  633. defer m.mu.Unlock()
  634. var targetIface *WireGuardInterface
  635. for _, i := range m.interfaces {
  636. if i.Name == ifaceName {
  637. targetIface = &i
  638. break
  639. }
  640. }
  641. if targetIface == nil {
  642. return "", fmt.Errorf("interface %s not found", ifaceName)
  643. }
  644. if dns == "" {
  645. dns = "1.1.1.1, 8.8.8.8"
  646. }
  647. if serverEndpoint == "" {
  648. serverEndpoint = fmt.Sprintf("YOUR_ROUTER_IP:%d", targetIface.ListenPort)
  649. }
  650. var sb strings.Builder
  651. sb.WriteString("[Interface]\n")
  652. sb.WriteString(fmt.Sprintf("PrivateKey = %s\n", clientPrivKey))
  653. sb.WriteString(fmt.Sprintf("Address = %s\n", clientAddress))
  654. sb.WriteString(fmt.Sprintf("DNS = %s\n", dns))
  655. sb.WriteString("\n[Peer]\n")
  656. sb.WriteString(fmt.Sprintf("PublicKey = %s\n", targetIface.PublicKey))
  657. sb.WriteString(fmt.Sprintf("Endpoint = %s\n", serverEndpoint))
  658. sb.WriteString("AllowedIPs = 0.0.0.0/0, ::/0\n")
  659. sb.WriteString("PersistentKeepalive = 25\n")
  660. return sb.String(), nil
  661. }