aerotun.go 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. package cli
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "strconv"
  6. "strings"
  7. "debtik/internal/aerotun"
  8. "debtik/internal/auth"
  9. )
  10. func (e *Executor) handleInterfaceAeroTUN(parts []string, user *auth.PublicUser) string {
  11. if e.vpnMgr == nil || e.vpnMgr.AeroTUN == nil {
  12. return ColorRed + "AeroTUN subsystem not available.\n" + ColorReset
  13. }
  14. mgr := e.vpnMgr.AeroTUN
  15. action := "print"
  16. if len(parts) > 0 {
  17. action = strings.ToLower(parts[0])
  18. parts = parts[1:]
  19. }
  20. pos, params := parseArgs(parts)
  21. if action == "print" || action == "show" || action == "pr" {
  22. list := mgr.List()
  23. for i := range list {
  24. list[i] = list[i].PublicView()
  25. }
  26. if params["detail"] == "yes" {
  27. data, _ := json.MarshalIndent(list, "", " ")
  28. return string(data) + "\n"
  29. }
  30. tbl := NewTable("NAME", "VERSION", "STATE", "BACKEND", "LOCAL", "PEER", "ERROR")
  31. for _, inst := range list {
  32. tbl.AddRow(inst.Name, strconv.Itoa(inst.Protocol), aeroTUNState(inst), inst.Backend, inst.LocalAddress, inst.PeerAddress, inst.LastError)
  33. }
  34. return tbl.String()
  35. }
  36. if user == nil || user.Role != auth.RoleFull {
  37. return ColorRed + "Permission denied: admin role required to manage AeroTUN\n" + ColorReset
  38. }
  39. if action == "genkey" {
  40. key, err := aerotun.GenerateSecureKey()
  41. if err != nil {
  42. return cliError(err)
  43. }
  44. return key + "\n"
  45. }
  46. if action != "add" && action != "set" && action != "remove" && action != "enable" && action != "disable" && action != "start" && action != "stop" {
  47. return ColorRed + "Usage: /interface aerotun [print [detail=yes]|add|set <name>|remove <name>|enable <name>|disable <name>|start <name>|stop <name>|genkey]\n" + ColorReset
  48. }
  49. inst := aerotun.TunnelInstance{Protocol: 3, Mode: "server", Disabled: true, MTU: aerotun.DefaultMTU}
  50. if action != "add" {
  51. if len(pos) != 1 {
  52. return cliError(fmt.Errorf("%s requires one tunnel name or ID", action))
  53. }
  54. found := false
  55. for _, candidate := range mgr.List() {
  56. if candidate.Name == pos[0] || candidate.ID == pos[0] {
  57. inst, found = candidate, true
  58. break
  59. }
  60. }
  61. if !found {
  62. return cliError(fmt.Errorf("AeroTUN tunnel not found: %s", pos[0]))
  63. }
  64. }
  65. var err error
  66. switch action {
  67. case "remove":
  68. err = mgr.Delete(inst.ID)
  69. case "enable":
  70. err = mgr.ToggleState(inst.ID, false)
  71. case "disable":
  72. err = mgr.ToggleState(inst.ID, true)
  73. case "start":
  74. err = mgr.StartTunnel(inst.ID)
  75. case "stop":
  76. err = mgr.StopTunnel(inst.ID)
  77. case "add", "set":
  78. for key, value := range params {
  79. switch key {
  80. case "name":
  81. inst.Name = value
  82. case "mode":
  83. inst.Mode = value
  84. case "endpoint":
  85. inst.RemoteEndpoint = value
  86. case "local-address":
  87. inst.LocalAddress = value
  88. case "peer-address":
  89. inst.PeerAddress = value
  90. case "peer-public-key":
  91. inst.PeerPublicKey = value
  92. case "psk":
  93. inst.PSK = value
  94. case "comment":
  95. inst.Comment = value
  96. case "allowed-ips":
  97. inst.AllowedIPs = nil
  98. for _, prefix := range strings.Split(value, ",") {
  99. if s := strings.TrimSpace(prefix); s != "" {
  100. inst.AllowedIPs = append(inst.AllowedIPs, s)
  101. }
  102. }
  103. case "protocol", "listen-port", "mtu":
  104. var number int
  105. number, err = strconv.Atoi(value)
  106. if err == nil {
  107. switch key {
  108. case "protocol":
  109. inst.Protocol = number
  110. case "listen-port":
  111. inst.ListenPort = number
  112. case "mtu":
  113. inst.MTU = number
  114. }
  115. }
  116. case "disabled", "compression", "default-route":
  117. var enabled bool
  118. switch value {
  119. case "yes", "true":
  120. enabled = true
  121. case "no", "false":
  122. enabled = false
  123. default:
  124. err = fmt.Errorf("%s must be yes or no", key)
  125. }
  126. if err == nil {
  127. switch key {
  128. case "disabled":
  129. inst.Disabled = enabled
  130. case "compression":
  131. inst.Compression = enabled
  132. case "default-route":
  133. inst.DefaultRoute = enabled
  134. }
  135. }
  136. default:
  137. err = fmt.Errorf("unknown AeroTUN parameter: %s", key)
  138. }
  139. if err != nil {
  140. return cliError(err)
  141. }
  142. }
  143. if action == "add" {
  144. if inst.Name == "" {
  145. return cliError(fmt.Errorf("name is required"))
  146. }
  147. if inst.PSK == "" {
  148. return cliError(fmt.Errorf("psk is required; use /interface aerotun genkey and share it with the peer"))
  149. }
  150. }
  151. inst, err = mgr.Save(inst)
  152. }
  153. if err != nil {
  154. return cliError(err)
  155. }
  156. if current, ok := mgr.Get(inst.ID); ok {
  157. return ColorGreen + fmt.Sprintf("AeroTUN %s: %s\nPublic key: %s\n", current.Name, aeroTUNState(current), current.PublicKey) + ColorReset
  158. }
  159. return ColorGreen + "AeroTUN tunnel removed.\n" + ColorReset
  160. }
  161. func cliError(err error) string { return ColorRed + "Error: " + err.Error() + "\n" + ColorReset }
  162. func aeroTUNState(inst aerotun.TunnelInstance) string {
  163. switch {
  164. case inst.LastError != "":
  165. return "failed"
  166. case inst.Connected:
  167. return "connected"
  168. case inst.Running:
  169. return "connecting"
  170. case inst.Disabled:
  171. return "saved/disabled"
  172. default:
  173. return "stopped"
  174. }
  175. }