config.go 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900
  1. package config
  2. import (
  3. "bufio"
  4. "bytes"
  5. "encoding/json"
  6. "fmt"
  7. "io"
  8. "log"
  9. "net"
  10. "net/http"
  11. "net/netip"
  12. "os"
  13. "strconv"
  14. "strings"
  15. "time"
  16. daemonproto "git.aetherial.dev/aeth/yosai/pkg/daemon-proto"
  17. "github.com/joho/godotenv"
  18. )
  19. const LogMsgTmpl = "YOSAI Daemon ||| time: %s ||| %s\n"
  20. var EnvironmentVariables = []string{
  21. "HASHICORP_VAULT_KEY",
  22. }
  23. const DefaultConfigLoc = "./.config.json"
  24. type DaemonConfigIO interface {
  25. Propogate(*Configuration)
  26. Save(Configuration) error
  27. }
  28. /*
  29. TODO:
  30. - Make an implementation for using a configuration server, like a database or maybe a custom API
  31. - Have only 1 SSH key secret type in the keyring, and propogate it into the other systems more intelligently
  32. - intelligent keyring bootstrapping
  33. [KEYS]
  34. - read a SSH key for the VPS_SSH_KEY key
  35. - read in another SSH key for the GIT_SSH_KEY incase theyre different
  36. - Cloud API key
  37. - Secrets API key
  38. - Ansible API key
  39. - VPS Root
  40. - credentials
  41. - ssh key
  42. - VPS service account
  43. - credentials
  44. - ssh key
  45. [SERVICES]
  46. In order of priority:
  47. - Environment variables
  48. - configuration file
  49. - configuration server
  50. - user supplied input
  51. - Create a configuration server with REST API, grabs config based on
  52. - LDAP
  53. - Local
  54. Configuration server should have a web UI that allows to create a config via a form, and then
  55. save it to that users account.
  56. I would also like to get Hashicorp vault working with LDAP, so that I can build a large portion of this
  57. around LDAP. Instead of needing to supply the root token to HCV, I can have the client use their LDAP credentials
  58. all around, in HCV, the config server, and semaphore.
  59. What else needs to be done to allow for seemless experience for a 'user' across clients?
  60. - No on-system stored data
  61. - Easy bootstrapping
  62. */
  63. /*
  64. Loads in the environment variable file at path, and then validates that all values in vars is present
  65. :param path: the path to the .env file
  66. :param vars: the list of variables to check were loaded by godotenv.Load()
  67. */
  68. func LoadAndVerifyEnv(path string, vars []string) error {
  69. err := godotenv.Load(".env")
  70. if err != nil {
  71. return err
  72. }
  73. var missing []string
  74. for i := range vars {
  75. val := os.Getenv(vars[i])
  76. if val == "" {
  77. missing = append(missing, vars[i])
  78. }
  79. }
  80. if len(missing) != 0 {
  81. return &EnvironmentVariableNotSet{Vars: missing}
  82. }
  83. return nil
  84. }
  85. type EnvironmentVariableNotSet struct {
  86. Vars []string
  87. }
  88. func (e *EnvironmentVariableNotSet) Error() string {
  89. return fmt.Sprintf("Environment variables: %v not set!", e.Vars)
  90. }
  91. func BlankEnv(path string) error {
  92. var data string
  93. for i := range EnvironmentVariables {
  94. data = data + fmt.Sprintf("%s=\n", EnvironmentVariables[i])
  95. }
  96. return os.WriteFile(path, []byte(data), 0666)
  97. }
  98. /*
  99. Wrapping the add peer functionality in a router friendly interface
  100. :param msg: a message to be parsed from the daemonproto socket
  101. */
  102. func (c *Configuration) AddPeerHandler(msg daemonproto.SockMessage) daemonproto.SockMessage {
  103. var peer VpnClient
  104. err := json.Unmarshal(msg.Body, &peer)
  105. if err != nil {
  106. return *daemonproto.NewSockMessage(daemonproto.MsgResponse, daemonproto.REQUEST_FAILED, []byte(err.Error()))
  107. }
  108. addr, err := c.GetAvailableVpnIpv4()
  109. if err != nil {
  110. return *daemonproto.NewSockMessage(daemonproto.MsgResponse, daemonproto.REQUEST_FAILED, []byte(err.Error()))
  111. }
  112. return *daemonproto.NewSockMessage(daemonproto.MsgResponse, daemonproto.REQUEST_OK, []byte("Client: "+c.AddClient(addr, peer.Pubkey, peer.Name)+" Successfully added."))
  113. }
  114. /*
  115. Wrapping the delete peer functionality in a router friendly interface
  116. :param msg: a message to be parsed from the daemonproto socket
  117. */
  118. func (c *Configuration) DeletePeerHandler(msg daemonproto.SockMessage) daemonproto.SockMessage {
  119. var req VpnClient
  120. err := json.Unmarshal(msg.Body, &req)
  121. if err != nil {
  122. return *daemonproto.NewSockMessage(daemonproto.MsgResponse, daemonproto.REQUEST_FAILED, []byte(err.Error()))
  123. }
  124. peer, err := c.GetClient(req.Name)
  125. if err != nil {
  126. return *daemonproto.NewSockMessage(daemonproto.MsgResponse, daemonproto.REQUEST_FAILED, []byte(err.Error()))
  127. }
  128. delete(c.Service.Clients, peer.Name)
  129. err = c.FreeAddress(peer.VpnIpv4.String())
  130. if err != nil {
  131. return *daemonproto.NewSockMessage(daemonproto.MsgResponse, daemonproto.REQUEST_FAILED, []byte(err.Error()))
  132. }
  133. return *daemonproto.NewSockMessage(daemonproto.MsgResponse, daemonproto.REQUEST_OK, []byte("Client: "+peer.Name+" Successfully deleted from the config."))
  134. }
  135. /*
  136. Wrapping the add server functionality in a router friendly interface
  137. :param msg: a message to be parsed from the daemonproto socket
  138. */
  139. func (c *Configuration) AddServerHandler(msg daemonproto.SockMessage) daemonproto.SockMessage {
  140. var req VpnServer
  141. err := json.Unmarshal(msg.Body, &req)
  142. if err != nil {
  143. return *daemonproto.NewSockMessage(daemonproto.MsgResponse, daemonproto.REQUEST_FAILED, []byte(err.Error()))
  144. }
  145. addr, err := c.GetAvailableVpnIpv4()
  146. if err != nil {
  147. return *daemonproto.NewSockMessage(daemonproto.MsgResponse, daemonproto.REQUEST_FAILED, []byte(err.Error()))
  148. }
  149. name := c.AddServer(addr, req.Name, req.WanIpv4)
  150. c.Log("address: ", addr.String(), "name:", name)
  151. return *daemonproto.NewSockMessage(daemonproto.MsgResponse, daemonproto.REQUEST_OK, []byte("Server: "+name+" Successfully added."))
  152. }
  153. /*
  154. Wrapping the delete server functionality in a router friendly interface
  155. :param msg: a message to be parsed from the daemonproto socket
  156. */
  157. func (c *Configuration) DeleteServerHandler(msg daemonproto.SockMessage) daemonproto.SockMessage {
  158. var req VpnServer
  159. err := json.Unmarshal(msg.Body, &req)
  160. if err != nil {
  161. return *daemonproto.NewSockMessage(daemonproto.MsgResponse, daemonproto.REQUEST_FAILED, []byte(err.Error()))
  162. }
  163. server, err := c.GetServer(req.Name)
  164. if err != nil {
  165. return *daemonproto.NewSockMessage(daemonproto.MsgResponse, daemonproto.REQUEST_FAILED, []byte(err.Error()))
  166. }
  167. delete(c.Service.Servers, server.Name)
  168. err = c.FreeAddress(server.VpnIpv4.String())
  169. if err != nil {
  170. return *daemonproto.NewSockMessage(daemonproto.MsgResponse, daemonproto.REQUEST_FAILED, []byte(err.Error()))
  171. }
  172. return *daemonproto.NewSockMessage(daemonproto.MsgResponse, daemonproto.REQUEST_OK, []byte("Server: "+server.Name+" Successfully deleted from the config."))
  173. }
  174. /*
  175. Wrapping the show config functionality in a router friendly interface
  176. :param msg: a message to be parsed from the daemonproto socket
  177. */
  178. func (c *Configuration) ShowConfigHandler(msg daemonproto.SockMessage) daemonproto.SockMessage {
  179. b, err := json.MarshalIndent(&c, "", " ")
  180. if err != nil {
  181. return *daemonproto.NewSockMessage(daemonproto.MsgResponse, daemonproto.REQUEST_FAILED, []byte(err.Error()))
  182. }
  183. return *daemonproto.NewSockMessage(daemonproto.MsgResponse, daemonproto.REQUEST_OK, b)
  184. }
  185. /*
  186. Wrapping the save config functionality in a router friendly interface
  187. :param msg: a message to be parsed from the daemonproto socket
  188. */
  189. func (c *Configuration) SaveConfigHandler(msg daemonproto.SockMessage) daemonproto.SockMessage {
  190. err := c.cfgIO.Save(*c)
  191. if err != nil {
  192. return *daemonproto.NewSockMessage(daemonproto.MsgResponse, daemonproto.REQUEST_FAILED, []byte(err.Error()))
  193. }
  194. return *daemonproto.NewSockMessage(daemonproto.MsgResponse, daemonproto.REQUEST_OK, []byte("Configuration saved successfully."))
  195. }
  196. /*
  197. Wrapping the reload config functionality in a router friendly interface
  198. :param msg: a message to be parsed from the daemonproto socket
  199. */
  200. func (c *Configuration) ReloadConfigHandler(msg daemonproto.SockMessage) daemonproto.SockMessage {
  201. c.cfgIO.Propogate(c)
  202. return *daemonproto.NewSockMessage(daemonproto.MsgResponse, daemonproto.REQUEST_OK, []byte("Configuration reloaded successfully."))
  203. }
  204. type ConfigRouter struct {
  205. routes map[daemonproto.Method]func(daemonproto.SockMessage) daemonproto.SockMessage
  206. }
  207. func (c *ConfigRouter) Register(method daemonproto.Method, callable func(daemonproto.SockMessage) daemonproto.SockMessage) {
  208. c.routes[method] = callable
  209. }
  210. func (c *ConfigRouter) Routes() map[daemonproto.Method]func(daemonproto.SockMessage) daemonproto.SockMessage {
  211. return c.routes
  212. }
  213. func NewConfigRouter() *ConfigRouter {
  214. return &ConfigRouter{routes: map[daemonproto.Method]func(daemonproto.SockMessage) daemonproto.SockMessage{}}
  215. }
  216. type Username string
  217. func ValidateUsername(name string) Username {
  218. return Username(name)
  219. }
  220. type User struct {
  221. Name Username
  222. Id int
  223. }
  224. type Configuration struct {
  225. stream io.Writer
  226. cfgIO DaemonConfigIO
  227. Username Username `json:"username"`
  228. Cloud cloudConfig `json:"cloud"`
  229. Ansible ansibleConfig `json:"ansible"`
  230. Service serviceConfig `json:"service"`
  231. HostInfo hostInfo `json:"host_info"`
  232. }
  233. type hostInfo struct {
  234. WireguardSavePath string `json:"wireguard_save_path"`
  235. }
  236. type ansibleConfig struct {
  237. Repo string `json:"repo_url"`
  238. Branch string `json:"branch"`
  239. PlaybookName string `json:"playbook_name"`
  240. }
  241. type serviceConfig struct {
  242. Servers map[string]VpnServer `json:"servers"`
  243. Clients map[string]VpnClient `json:"clients"`
  244. VpnAddressSpace net.IPNet `json:"vpn_address_space"`
  245. VpnAddresses map[string]bool `json:"vpn_addresses"` // Each key is a IPv4 in the VPN, and its corresponding value is what denotes if its in use or not. False == 'In use', True == 'available'
  246. VpnMask int `json:"vpn_mask"` // The mask of the VPN
  247. VpnServerPort int `json:"vpn_server_port"`
  248. SecretsBackend string `json:"secrets_backend"`
  249. SecretsBackendUrl string `json:"secrets_backend_url"`
  250. AnsibleBackend string `json:"ansible_backend"`
  251. AnsibleBackendUrl string `json:"ansible_backend_url"`
  252. }
  253. func (c *Configuration) GetServer(name string) (VpnServer, error) {
  254. server, ok := c.Service.Servers[name]
  255. if ok {
  256. return server, nil
  257. }
  258. for _, server := range c.Service.Servers {
  259. if server.Name == name {
  260. return server, nil
  261. }
  262. }
  263. return VpnServer{}, &ServerNotFound{}
  264. }
  265. func (c *Configuration) GetClient(name string) (VpnClient, error) {
  266. client, ok := c.Service.Clients[name]
  267. if ok {
  268. return client, nil
  269. }
  270. for _, client := range c.Service.Clients {
  271. if client.Name == name {
  272. return client, nil
  273. }
  274. }
  275. return VpnClient{}, &ServerNotFound{}
  276. }
  277. /*
  278. Add a VPN server to the Service configuration
  279. :param server: a VpnServer struct modeling the data that comprises of a VPN server
  280. */
  281. func (c *Configuration) AddServer(addr net.IP, name string, wan string) string {
  282. server, ok := c.Service.Servers[name]
  283. var serverLabel string
  284. if ok {
  285. serverLabel = c.resolveName(server.Name, name)
  286. } else {
  287. serverLabel = name
  288. }
  289. c.Service.Servers[serverLabel] = VpnServer{Name: serverLabel, WanIpv4: wan, VpnIpv4: addr}
  290. return serverLabel
  291. }
  292. type VpnClient struct {
  293. Name string `json:"name"`
  294. VpnIpv4 net.IP `json:"vpn_ipv4"`
  295. Pubkey string `json:"pubkey"`
  296. Default bool `json:"default"`
  297. }
  298. type VpnServer struct {
  299. Name string `json:"name"` // this Label is what is used to index that server and its data within the Daemons model of the VPN environment
  300. WanIpv4 string `json:"wan_ipv4"` // Public IPv4
  301. VpnIpv4 net.IP `json:"vpn_ipv4"` // the IP address that the server will occupy on the network
  302. }
  303. /*
  304. Retrieve an available IPv4 from the VPN's set address space. Returns an error if an internal address cant be
  305. parsed to a valid IPv4, or if there are no available addresses left.
  306. */
  307. func (c *Configuration) GetAvailableVpnIpv4() (net.IP, error) {
  308. for addr, used := range c.Service.VpnAddresses {
  309. if !used {
  310. parsedAddr := net.ParseIP(addr)
  311. if parsedAddr == nil {
  312. return nil, &VpnAddressSpaceError{Msg: "Address: " + addr + " couldnt be parsed into a valid IPv4"}
  313. }
  314. c.Service.VpnAddresses[parsedAddr.String()] = true
  315. return parsedAddr, nil
  316. }
  317. }
  318. return nil, &VpnAddressSpaceError{Msg: "No open addresses available in the current VPN address space!"}
  319. }
  320. /*
  321. Return all of the clients from the client list
  322. */
  323. func (c *Configuration) VpnClients() []VpnClient {
  324. clients := []VpnClient{}
  325. for _, val := range c.Service.Clients {
  326. clients = append(clients, val)
  327. }
  328. return clients
  329. }
  330. /*
  331. Get the default VPN client
  332. */
  333. func (c *Configuration) DefaultClient() (VpnClient, error) {
  334. for name := range c.Service.Clients {
  335. if c.Service.Clients[name].Default {
  336. return c.Service.Clients[name], nil
  337. }
  338. }
  339. return VpnClient{}, &ConfigError{Msg: "No default client was specified!"}
  340. }
  341. /*
  342. resolve naming collision in the client list
  343. :param existingName: the name of the existing client in the client list
  344. :param dupeName: the desired name of the client to be added
  345. */
  346. func (c *Configuration) resolveName(existingName string, dupeName string) string {
  347. incr, err := strconv.Atoi(strings.Trim(existingName, dupeName))
  348. if err != nil {
  349. c.Log("Name: ", existingName, "in the client list broke naming convention.")
  350. return dupeName + "0"
  351. }
  352. return fmt.Sprintf("%s%v", dupeName, incr+1)
  353. }
  354. /*
  355. Register a client as a VPN client. This configuration will be propogated into server configs, so that they may connect
  356. :param addr: a net.IP gotten from GetAvailableVpnIpv4()
  357. :param pubkey: the Wireguard public key
  358. :param name: the name/label of this client
  359. */
  360. func (c *Configuration) AddClient(addr net.IP, pubkey string, name string) string {
  361. client, ok := c.Service.Clients[name]
  362. var clientLabel string
  363. if ok {
  364. clientLabel = c.resolveName(client.Name, name)
  365. } else {
  366. clientLabel = name
  367. }
  368. c.Service.Clients[name] = VpnClient{Name: clientLabel, Pubkey: pubkey, VpnIpv4: addr}
  369. return clientLabel
  370. }
  371. /*
  372. Frees up an address to be used
  373. */
  374. func (c *Configuration) FreeAddress(addr string) error {
  375. _, ok := c.Service.VpnAddresses[addr]
  376. if !ok {
  377. return &VpnAddressSpaceError{Msg: "Address: " + addr + " is not in the designated VPN Address space."}
  378. }
  379. c.Service.VpnAddresses[addr] = false
  380. return nil
  381. }
  382. /*
  383. Get all of the in use addresses for the VPN
  384. */
  385. func (c *Configuration) AllVpnAddresses() []net.IP {
  386. addrs := []net.IP{}
  387. for i := range c.Service.Servers {
  388. addrs = append(addrs, c.Service.Servers[i].VpnIpv4)
  389. }
  390. for i := range c.Service.Clients {
  391. addrs = append(addrs, c.Service.Clients[i].VpnIpv4)
  392. }
  393. return addrs
  394. }
  395. type VpnAddressSpaceError struct {
  396. Msg string
  397. }
  398. func (v *VpnAddressSpaceError) Error() string {
  399. return v.Msg
  400. }
  401. type ServerNotFound struct{}
  402. func (s *ServerNotFound) Error() string { return "Server with the priority passed was not found." }
  403. type cloudConfig struct {
  404. Image string `json:"image"`
  405. Region string `json:"region"`
  406. LinodeType string `json:"linode_type"`
  407. }
  408. /*
  409. Log a message to the Contexts 'stream' io.Writer interface
  410. */
  411. func (c *Configuration) Log(data ...string) {
  412. c.stream.Write([]byte(fmt.Sprintf(LogMsgTmpl, time.Now().String(), data)))
  413. }
  414. func (c *Configuration) SetConfigIO(impl DaemonConfigIO) {
  415. c.cfgIO = impl
  416. }
  417. func (c *Configuration) SetStreamIO(impl io.Writer) {
  418. c.stream = impl
  419. }
  420. /*
  421. Calculate the VPN space details
  422. */
  423. func (c *Configuration) CalculateVpnSpace() error {
  424. mask, _ := c.Service.VpnAddressSpace.Mask.Size()
  425. vpnNetwork := fmt.Sprintf("%s/%v", c.Service.VpnAddressSpace.IP.String(), mask)
  426. addresses, err := GetNetworkAddresses(vpnNetwork)
  427. if err != nil {
  428. return err
  429. }
  430. _, ntwrk, _ := net.ParseCIDR(vpnNetwork)
  431. addrSpace := map[string]bool{}
  432. for i := range addresses.Ipv4s {
  433. addrSpace[addresses.Ipv4s[i].String()] = false
  434. }
  435. usedAddresses := c.AllVpnAddresses()
  436. for i := range usedAddresses {
  437. c.Log("Checking: ", usedAddresses[i].String())
  438. c.Service.VpnAddresses[usedAddresses[i].String()] = true
  439. }
  440. c.Service.VpnAddresses = addrSpace
  441. c.Service.VpnAddressSpace = *ntwrk
  442. c.Service.VpnMask = addresses.Mask
  443. return nil
  444. }
  445. type ConfigurationBuilder struct {
  446. fileLocations []string
  447. }
  448. /*
  449. Walk through all of the possible configuration avenues, and build out the configuration.
  450. */
  451. func (c ConfigurationBuilder) Build() *Configuration { return nil }
  452. func (c ConfigurationBuilder) readEnv() {}
  453. func (c ConfigurationBuilder) readFiles() {
  454. }
  455. func (c ConfigurationBuilder) readServer() {}
  456. type ConfigHostImpl struct {
  457. path string
  458. }
  459. func NewConfigHostImpl(path string) ConfigHostImpl {
  460. return ConfigHostImpl{path: path}
  461. }
  462. func (c ConfigHostImpl) Propogate(config *Configuration) {
  463. b, err := os.ReadFile(c.path)
  464. if err != nil {
  465. log.Fatal(err)
  466. }
  467. err = json.Unmarshal(b, config)
  468. if err != nil {
  469. log.Fatal(err)
  470. }
  471. err = config.CalculateVpnSpace()
  472. if err != nil {
  473. log.Fatal(err)
  474. }
  475. }
  476. func (c ConfigHostImpl) Save(config Configuration) error {
  477. b, err := json.MarshalIndent(c, " ", " ")
  478. if err != nil {
  479. return err
  480. }
  481. return os.WriteFile(c.path, b, 0666)
  482. }
  483. func NewConfigServerImpl(host string, port int, serverProto string) ConfigServerImpl {
  484. return ConfigServerImpl{
  485. http: http.Client{},
  486. addr: net.NS{Host: host},
  487. port: port,
  488. proto: serverProto}
  489. }
  490. type ConfigServerImpl struct {
  491. http http.Client
  492. addr net.NS
  493. port int
  494. proto string
  495. }
  496. func (s ConfigServerImpl) Propogate(config *Configuration) {
  497. resp, err := s.get("/get-config/" + string(config.Username))
  498. if err != nil {
  499. log.Fatal(err)
  500. }
  501. if err = json.Unmarshal(resp, &config); err != nil {
  502. log.Fatal(err)
  503. }
  504. err = config.CalculateVpnSpace()
  505. if err != nil {
  506. log.Fatal(err)
  507. }
  508. }
  509. func (s ConfigServerImpl) Save(config Configuration) error {
  510. _, err := s.post(config, "/update-config/"+string(config.Username))
  511. if err != nil {
  512. return err
  513. }
  514. return nil
  515. }
  516. /*
  517. Agnostic GET call
  518. :param path: the path to attach to the HTTP server
  519. */
  520. func (s ConfigServerImpl) get(path string) ([]byte, error) {
  521. url := fmt.Sprintf("%s://%s:%d%s", s.proto, s.addr.Host, s.port, path)
  522. req, err := http.NewRequest(http.MethodGet, url, nil)
  523. if err != nil {
  524. return nil, err
  525. }
  526. resp, err := s.http.Do(req)
  527. if err != nil {
  528. return nil, err
  529. }
  530. b, err := io.ReadAll(resp.Body)
  531. if err != nil {
  532. return nil, err
  533. }
  534. defer resp.Body.Close()
  535. return b, nil
  536. }
  537. /*
  538. Agnostic POST call
  539. :param body: a struct that is JSON encodable
  540. :param path: the path to attach to the HTTP server
  541. */
  542. func (s ConfigServerImpl) post(body interface{}, path string) ([]byte, error) {
  543. b, err := json.Marshal(body)
  544. if err != nil {
  545. return nil, err
  546. }
  547. url := fmt.Sprintf("%s://%s:%d%s", s.proto, s.addr.Host, s.port, path)
  548. req, err := http.NewRequest(http.MethodPost, url, bytes.NewBuffer(b))
  549. if err != nil {
  550. return nil, err
  551. }
  552. resp, err := s.http.Do(req)
  553. if err != nil {
  554. return nil, err
  555. }
  556. if resp.StatusCode > 299 {
  557. return nil, &ConfigError{}
  558. }
  559. rb, err := io.ReadAll(resp.Body)
  560. if err != nil {
  561. return nil, err
  562. }
  563. defer resp.Body.Close()
  564. return rb, nil
  565. }
  566. /*
  567. Create a new Configuration struct with initialized maps
  568. */
  569. func NewConfiguration(stream io.Writer, username Username) *Configuration {
  570. return &Configuration{Username: username, stream: stream, Service: serviceConfig{Servers: map[string]VpnServer{}, Clients: map[string]VpnClient{}, VpnAddresses: map[string]bool{}}}
  571. }
  572. func BlankConfig(path string) error {
  573. config := NewConfiguration(bytes.NewBuffer([]byte{}), "replace_me")
  574. b, err := json.Marshal(config)
  575. if err != nil {
  576. return err
  577. }
  578. os.WriteFile(path, b, 0666)
  579. return nil
  580. }
  581. type ConfigError struct {
  582. Msg string
  583. }
  584. func (c *ConfigError) Error() string {
  585. return "There was an error with the configuration: " + c.Msg
  586. }
  587. /*
  588. ###############################################################
  589. ########### section for the address space functions ###########
  590. ###############################################################
  591. */
  592. type NetworkInterfaceNotFound struct{ Passed string }
  593. // Implementing error interface
  594. func (n *NetworkInterfaceNotFound) Error() string {
  595. return fmt.Sprintf("Interface: '%s' not found.", n.Passed)
  596. }
  597. type IpSubnetMapper struct {
  598. Ipv4s []net.IP `json:"addresses"`
  599. NetworkAddr net.IP
  600. Current net.IP
  601. Mask int
  602. }
  603. /*
  604. Get the next IPv4 address of the address specified in the 'addr' argument,
  605. :param addr: the address to get the next address of
  606. */
  607. func getNextAddr(addr string) string {
  608. parsed, err := netip.ParseAddr(addr)
  609. if err != nil {
  610. log.Fatal("failed while parsing address in getNextAddr() ", err, "\n")
  611. }
  612. return parsed.Next().String()
  613. }
  614. /*
  615. get the network address of the ip address in 'addr' with the subnet mask from 'cidr'
  616. :param addr: the ipv4 address to get the network address of
  617. :param cidr: the CIDR notation of the subbet
  618. */
  619. func getNetwork(addr string, cidr int) string {
  620. addr = fmt.Sprintf("%s/%v", addr, cidr)
  621. ip, net, err := net.ParseCIDR(addr)
  622. if err != nil {
  623. log.Fatal("failed whilst attempting to parse cidr in getNetwork() ", err, "\n")
  624. }
  625. return ip.Mask(net.Mask).String()
  626. }
  627. /*
  628. Recursive function to get all of the IPv4 addresses for each IPv4 network that the host is on
  629. :param ipmap: a pointer to an IpSubnetMapper struct which contains domain details such as
  630. the subnet mask, the original network mask, and the current IP address used in the
  631. recursive function
  632. :param max: This is safety feature to prevent stack overflows, so you can manually set the depth to
  633. call the function
  634. */
  635. func addressRecurse(ipmap *IpSubnetMapper) {
  636. next := getNextAddr(ipmap.Current.String())
  637. nextNet := getNetwork(next, ipmap.Mask)
  638. currentNet := ipmap.NetworkAddr.String()
  639. if nextNet != currentNet {
  640. return
  641. }
  642. ipmap.Current = net.ParseIP(next)
  643. ipmap.Ipv4s = append(ipmap.Ipv4s, net.ParseIP(next))
  644. addressRecurse(ipmap)
  645. }
  646. /*
  647. Get all of the IPv4 addresses in the network that 'addr' belongs to. YOU MUST PASS THE ADDRESS WITH CIDR NOTATION
  648. i.e. '192.168.50.1/24'
  649. :param addr: the ipv4 address to use for subnet discovery
  650. */
  651. func GetNetworkAddresses(addr string) (*IpSubnetMapper, error) {
  652. ipmap := &IpSubnetMapper{Ipv4s: []net.IP{}}
  653. ip, net, err := net.ParseCIDR(addr)
  654. if err != nil {
  655. return nil, err
  656. }
  657. mask, err := strconv.Atoi(strings.Split(addr, "/")[1])
  658. if err != nil {
  659. return nil, err
  660. }
  661. ipmap.NetworkAddr = ip.Mask(net.Mask)
  662. ipmap.Mask = mask
  663. ipmap.Current = ip.Mask(net.Mask)
  664. addressRecurse(ipmap)
  665. return ipmap, nil
  666. }
  667. /*
  668. ##########################################
  669. ##### Initial daemon startup helpers #####
  670. ##########################################
  671. */
  672. type StartupArgKeyname string
  673. const (
  674. ConfigModeArg StartupArgKeyname = "CONFIG_MODE"
  675. UsernameArg = "USERNAME"
  676. SecretsBackendKeyArg = "SECRET_BACKEND_KEY"
  677. ConfigServerAddr = "YOSAI_CONFIG_SERVER_ADDR"
  678. ConfigServerPort = "YOSAI_CONFIG_SERVER_PORT"
  679. ConfigServerProto = "YOSAI_CONFIG_SERVER_PROTO"
  680. ConfigFileLoc = "YOSAI_CONFIG_FILE_LOC"
  681. )
  682. type StartupRequirements struct {
  683. ConfigurationMode string
  684. Username string
  685. SecretsBackendKey string
  686. ConfigServerPort int
  687. ConfigServerAddr string
  688. ConfigServerProto string
  689. ConfigFileLoc string
  690. }
  691. /*
  692. Grab user input for values passed via 'missing', and set the startup data configuration map with the value supplied
  693. :param cfg: a map that holds the data required to start the daemon
  694. :param missing: a map of missing values that the function can use to index the cfg struct
  695. */
  696. func getUserInput(cfg map[StartupArgKeyname]string, missing []StartupArgKeyname) {
  697. for i := range missing {
  698. fmt.Print(missing[i], ":")
  699. reader := bufio.NewReader(os.Stdin)
  700. line, err := reader.ReadString('\n')
  701. if err != nil {
  702. log.Fatal(err)
  703. }
  704. cfg[missing[i]] = strings.Trim(line, "\n")
  705. }
  706. }
  707. /*
  708. Pass in a map of arguments supplied from program flags, and this function will attempt to try a
  709. variety of different sources to fill in the gaps, i.e. environment vars, user input
  710. :param args: the argument map, likely called from the main function.
  711. */
  712. func Turnkey(args map[StartupArgKeyname]string) StartupRequirements {
  713. cfg := map[StartupArgKeyname]string{
  714. ConfigModeArg: "",
  715. UsernameArg: "",
  716. SecretsBackendKeyArg: "",
  717. ConfigServerAddr: "",
  718. ConfigServerProto: "",
  719. ConfigFileLoc: "",
  720. }
  721. fromEnv := map[StartupArgKeyname]string{
  722. ConfigModeArg: os.Getenv(string(ConfigModeArg)),
  723. UsernameArg: os.Getenv(string(UsernameArg)),
  724. SecretsBackendKeyArg: os.Getenv(string(SecretsBackendKeyArg)),
  725. ConfigServerAddr: os.Getenv(string(ConfigServerAddr)),
  726. ConfigServerProto: os.Getenv(string(ConfigServerProto)),
  727. ConfigFileLoc: os.Getenv(string(ConfigFileLoc))}
  728. for i := range args {
  729. if args[i] == "" {
  730. continue
  731. }
  732. cfg[i] = args[i]
  733. }
  734. for i := range fromEnv {
  735. if fromEnv[i] == "" {
  736. continue
  737. }
  738. if cfg[i] != "" {
  739. fmt.Println("Overwriting value: ", i, " from set environment variable.")
  740. }
  741. cfg[i] = fromEnv[i]
  742. }
  743. var missingValues []StartupArgKeyname
  744. for i := range cfg {
  745. if cfg[i] == "" {
  746. fmt.Println("missing value for: ", i)
  747. missingValues = append(missingValues, i)
  748. }
  749. }
  750. if len(missingValues) != 0 {
  751. getUserInput(cfg, missingValues)
  752. }
  753. return StartupRequirements{
  754. ConfigurationMode: cfg[ConfigModeArg],
  755. Username: cfg[UsernameArg],
  756. SecretsBackendKey: cfg[SecretsBackendKeyArg],
  757. ConfigServerAddr: cfg[ConfigServerAddr],
  758. ConfigServerProto: cfg[ConfigServerProto],
  759. ConfigFileLoc: cfg[ConfigFileLoc],
  760. }
  761. }