blob: 18c37dfa9326337e0119753a3eeebb812164fe83 [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"
8 "os/exec"
gio864b4332024-09-05 13:56:47 +04009 "strconv"
Giorgi Lekveishvili027ef432023-06-16 12:31:25 +040010 "strings"
Giorgi Lekveishvili52814d92023-06-15 19:30:32 +040011)
12
giob36178f2024-08-23 18:59:15 +040013var ErrorAlreadyExists = errors.New("already exists")
14
Giorgi Lekveishvili52814d92023-06-15 19:30:32 +040015type client struct {
16 config string
17}
18
19func newClient(config string) *client {
20 return &client{
21 config: fmt.Sprintf("--config=%s", config),
22 }
23}
24
25func (c *client) createUser(name string) error {
26 cmd := exec.Command("headscale", c.config, "users", "create", name)
27 out, err := cmd.Output()
giob36178f2024-08-23 18:59:15 +040028 outStr := string(out)
29 if err != nil && strings.Contains(outStr, "User already exists") {
30 return ErrorAlreadyExists
31 }
Giorgi Lekveishvili52814d92023-06-15 19:30:32 +040032 return err
33}
34
35func (c *client) createPreAuthKey(user string) (string, error) {
36 // TODO(giolekva): make expiration configurable, and auto-refresh
37 cmd := exec.Command("headscale", c.config, "--user", user, "preauthkeys", "create", "--reusable", "--expiration", "365d")
38 out, err := cmd.Output()
gio864b4332024-09-05 13:56:47 +040039 fmt.Println(string(out))
Giorgi Lekveishvili027ef432023-06-16 12:31:25 +040040 if err != nil {
41 return "", err
42 }
Giorgi Lekveishvili027ef432023-06-16 12:31:25 +040043 return extractLastLine(string(out))
Giorgi Lekveishvili52814d92023-06-15 19:30:32 +040044}
45
gio864b4332024-09-05 13:56:47 +040046func (c *client) expirePreAuthKey(user, authKey string) error {
47 cmd := exec.Command("headscale", c.config, "--user", user, "preauthkeys", "expire", authKey)
48 out, err := cmd.Output()
49 fmt.Println(string(out))
50 if err != nil {
51 return err
52 }
53 return nil
54}
55
56func (c *client) expireUserNode(user, node string) error {
57 id, err := c.getNodeId(user, node)
58 if err != nil {
59 return err
60 }
61 cmd := exec.Command("headscale", c.config, "node", "expire", "--identifier", id)
62 out, err := cmd.Output()
63 fmt.Println(string(out))
64 if err != nil {
65 return err
66 }
67 return nil
68}
69
70func (c *client) removeUserNode(user, node string) error {
71 id, err := c.getNodeId(user, node)
72 if err != nil {
73 return err
74 }
75 cmd := exec.Command("headscale", c.config, "node", "delete", "--identifier", id, "--force")
76 out, err := cmd.Output()
77 fmt.Println(string(out))
78 if err != nil {
79 return err
80 }
81 return nil
82}
83
Giorgi Lekveishvili52814d92023-06-15 19:30:32 +040084func (c *client) enableRoute(id string) error {
Giorgi Lekveishvili52814d92023-06-15 19:30:32 +040085 cmd := exec.Command("headscale", c.config, "routes", "enable", "-r", id)
86 out, err := cmd.Output()
87 fmt.Println(string(out))
88 return err
89}
Giorgi Lekveishvili027ef432023-06-16 12:31:25 +040090
gio864b4332024-09-05 13:56:47 +040091type nodeInfo struct {
92 Id int `json:"id"`
93 Name string `json:"name"`
94}
95
96func (c *client) getNodeId(user, node string) (string, error) {
97 cmd := exec.Command("headscale", c.config, "--user", user, "node", "list", "-o", "json")
98 out, err := cmd.Output()
99 if err != nil {
100 return "", err
101 }
102 var nodes []nodeInfo
103 if err := json.NewDecoder(bytes.NewReader(out)).Decode(&nodes); err != nil {
104 return "", err
105 }
106 for _, n := range nodes {
107 if n.Name == node {
108 return strconv.Itoa(n.Id), nil
109 }
110 }
111 return "", fmt.Errorf("not found")
112}
113
Giorgi Lekveishvili027ef432023-06-16 12:31:25 +0400114func extractLastLine(s string) (string, error) {
115 items := strings.Split(s, "\n")
116 for i := len(items) - 1; i >= 0; i-- {
117 t := strings.TrimSpace(items[i])
118 if t != "" {
119 return t, nil
120 }
121 }
122 return "", fmt.Errorf("All lines are empty")
123}