blob: f4d0f510b3250638f7c28a924a0d0318510eaed7 [file] [log] [blame]
Giorgi Lekveishvili52814d92023-06-15 19:30:32 +04001package main
2
3import (
gio864b4332024-09-05 13:56:47 +04004 "bytes"
5 "encoding/json"
giob36178f2024-08-23 18:59:15 +04006 "errors"
Giorgi Lekveishvili52814d92023-06-15 19:30:32 +04007 "fmt"
giof6ad2982024-08-23 17:42:49 +04008 "net"
Giorgi Lekveishvili52814d92023-06-15 19:30:32 +04009 "os/exec"
gio864b4332024-09-05 13:56:47 +040010 "strconv"
Giorgi Lekveishvili027ef432023-06-16 12:31:25 +040011 "strings"
Giorgi Lekveishvili52814d92023-06-15 19:30:32 +040012)
13
giob36178f2024-08-23 18:59:15 +040014var ErrorAlreadyExists = errors.New("already exists")
giof6ad2982024-08-23 17:42:49 +040015var ErrorNotFound = errors.New("not found")
giob36178f2024-08-23 18:59:15 +040016
Giorgi Lekveishvili52814d92023-06-15 19:30:32 +040017type client struct {
18 config string
19}
20
21func newClient(config string) *client {
22 return &client{
23 config: fmt.Sprintf("--config=%s", config),
24 }
25}
26
27func (c *client) createUser(name string) error {
28 cmd := exec.Command("headscale", c.config, "users", "create", name)
29 out, err := cmd.Output()
giob36178f2024-08-23 18:59:15 +040030 outStr := string(out)
31 if err != nil && strings.Contains(outStr, "User already exists") {
32 return ErrorAlreadyExists
33 }
Giorgi Lekveishvili52814d92023-06-15 19:30:32 +040034 return err
35}
36
37func (c *client) createPreAuthKey(user string) (string, error) {
38 // TODO(giolekva): make expiration configurable, and auto-refresh
39 cmd := exec.Command("headscale", c.config, "--user", user, "preauthkeys", "create", "--reusable", "--expiration", "365d")
40 out, err := cmd.Output()
gio864b4332024-09-05 13:56:47 +040041 fmt.Println(string(out))
Giorgi Lekveishvili027ef432023-06-16 12:31:25 +040042 if err != nil {
43 return "", err
44 }
Giorgi Lekveishvili027ef432023-06-16 12:31:25 +040045 return extractLastLine(string(out))
Giorgi Lekveishvili52814d92023-06-15 19:30:32 +040046}
47
gio864b4332024-09-05 13:56:47 +040048func (c *client) expirePreAuthKey(user, authKey string) error {
49 cmd := exec.Command("headscale", c.config, "--user", user, "preauthkeys", "expire", authKey)
50 out, err := cmd.Output()
51 fmt.Println(string(out))
52 if err != nil {
53 return err
54 }
55 return nil
56}
57
58func (c *client) expireUserNode(user, node string) error {
59 id, err := c.getNodeId(user, node)
60 if err != nil {
61 return err
62 }
63 cmd := exec.Command("headscale", c.config, "node", "expire", "--identifier", id)
64 out, err := cmd.Output()
65 fmt.Println(string(out))
66 if err != nil {
67 return err
68 }
69 return nil
70}
71
72func (c *client) removeUserNode(user, node string) error {
73 id, err := c.getNodeId(user, node)
74 if err != nil {
75 return err
76 }
77 cmd := exec.Command("headscale", c.config, "node", "delete", "--identifier", id, "--force")
78 out, err := cmd.Output()
79 fmt.Println(string(out))
80 if err != nil {
81 return err
82 }
83 return nil
84}
85
Giorgi Lekveishvili52814d92023-06-15 19:30:32 +040086func (c *client) enableRoute(id string) error {
Giorgi Lekveishvili52814d92023-06-15 19:30:32 +040087 cmd := exec.Command("headscale", c.config, "routes", "enable", "-r", id)
88 out, err := cmd.Output()
89 fmt.Println(string(out))
90 return err
91}
Giorgi Lekveishvili027ef432023-06-16 12:31:25 +040092
gio864b4332024-09-05 13:56:47 +040093type nodeInfo struct {
giof6ad2982024-08-23 17:42:49 +040094 Id int `json:"id"`
95 Name string `json:"name"`
96 IPAddresses []net.IP `json:"ip_addresses"`
gio864b4332024-09-05 13:56:47 +040097}
98
99func (c *client) getNodeId(user, node string) (string, error) {
100 cmd := exec.Command("headscale", c.config, "--user", user, "node", "list", "-o", "json")
101 out, err := cmd.Output()
102 if err != nil {
103 return "", err
104 }
105 var nodes []nodeInfo
106 if err := json.NewDecoder(bytes.NewReader(out)).Decode(&nodes); err != nil {
107 return "", err
108 }
109 for _, n := range nodes {
110 if n.Name == node {
111 return strconv.Itoa(n.Id), nil
112 }
113 }
gio92116ca2024-10-06 13:55:46 +0400114 return "", ErrorNotFound
gio864b4332024-09-05 13:56:47 +0400115}
116
giof6ad2982024-08-23 17:42:49 +0400117func (c *client) getNodeAddresses(user, node string) ([]net.IP, error) {
118 cmd := exec.Command("headscale", c.config, "--user", user, "node", "list", "-o", "json")
119 out, err := cmd.Output()
120 if err != nil {
121 return nil, err
122 }
123 var nodes []nodeInfo
124 if err := json.NewDecoder(bytes.NewReader(out)).Decode(&nodes); err != nil {
125 return nil, err
126 }
127 for _, n := range nodes {
128 if n.Name == node {
129 return n.IPAddresses, nil
130 }
131 }
132 return nil, ErrorNotFound
133}
134
Giorgi Lekveishvili027ef432023-06-16 12:31:25 +0400135func extractLastLine(s string) (string, error) {
136 items := strings.Split(s, "\n")
137 for i := len(items) - 1; i >= 0; i-- {
138 t := strings.TrimSpace(items[i])
139 if t != "" {
140 return t, nil
141 }
142 }
143 return "", fmt.Errorf("All lines are empty")
144}