blob: beeb8e4968c55666d1e39f7b31b3ea8bed3fb26d [file] [log] [blame]
gio36b23b32024-08-25 12:20:54 +04001package installer
2
3import (
4 "bytes"
gio864b4332024-09-05 13:56:47 +04005 "encoding/json"
gio36b23b32024-08-25 12:20:54 +04006 "errors"
7 "fmt"
8 "io"
giof6ad2982024-08-23 17:42:49 +04009 "net"
gio36b23b32024-08-25 12:20:54 +040010 "net/http"
gio864b4332024-09-05 13:56:47 +040011 "net/url"
gio36b23b32024-08-25 12:20:54 +040012)
13
gio864b4332024-09-05 13:56:47 +040014type VPNAPIClient interface {
15 GenerateAuthKey(username string) (string, error)
16 ExpireKey(username, key string) error
17 ExpireNode(username, node string) error
18 RemoveNode(username, node string) error
giof6ad2982024-08-23 17:42:49 +040019 GetNodeIP(username, node string) (net.IP, error)
gio36b23b32024-08-25 12:20:54 +040020}
21
22type headscaleAPIClient struct {
gio864b4332024-09-05 13:56:47 +040023 c *http.Client
gio36b23b32024-08-25 12:20:54 +040024 apiAddr string
25}
26
gio864b4332024-09-05 13:56:47 +040027func NewHeadscaleAPIClient(apiAddr string) VPNAPIClient {
28 return &headscaleAPIClient{
29 &http.Client{},
30 apiAddr,
31 }
gio36b23b32024-08-25 12:20:54 +040032}
33
gio864b4332024-09-05 13:56:47 +040034func (g *headscaleAPIClient) GenerateAuthKey(username string) (string, error) {
gio36b23b32024-08-25 12:20:54 +040035 resp, err := http.Post(fmt.Sprintf("%s/user/%s/preauthkey", g.apiAddr, username), "application/json", nil)
36 if err != nil {
37 return "", err
38 }
39 var buf bytes.Buffer
40 io.Copy(&buf, resp.Body)
41 if resp.StatusCode != http.StatusOK {
42 return "", errors.New(buf.String())
43 }
44 return buf.String(), nil
45}
gio864b4332024-09-05 13:56:47 +040046
47type expirePreAuthKeyReq struct {
48 AuthKey string `json:"authKey"`
49}
50
51func (g *headscaleAPIClient) ExpireKey(username, key string) error {
52 addr, err := url.Parse(fmt.Sprintf("%s/user/%s/preauthkey", g.apiAddr, username))
53 if err != nil {
54 return err
55 }
56 var buf bytes.Buffer
57 if err := json.NewEncoder(&buf).Encode(expirePreAuthKeyReq{key}); err != nil {
58 return err
59 }
60 resp, err := g.c.Do(&http.Request{
61 URL: addr,
62 Method: http.MethodDelete,
63 Body: io.NopCloser(&buf),
64 })
65 if err != nil {
66 return err
67 }
68 if resp.StatusCode != http.StatusOK {
69 var buf bytes.Buffer
70 io.Copy(&buf, resp.Body)
71 return errors.New(buf.String())
72 }
73 return nil
74}
75
76func (g *headscaleAPIClient) ExpireNode(username, node string) error {
77 resp, err := g.c.Post(
78 fmt.Sprintf("%s/user/%s/node/%s/expire", g.apiAddr, username, node),
79 "text/plain",
80 nil,
81 )
82 if err != nil {
83 return err
84 }
85 if resp.StatusCode != http.StatusOK {
86 var buf bytes.Buffer
87 io.Copy(&buf, resp.Body)
88 return errors.New(buf.String())
89 }
90 return nil
91}
92
93func (g *headscaleAPIClient) RemoveNode(username, node string) error {
94 addr, err := url.Parse(fmt.Sprintf("%s/user/%s/node/%s", g.apiAddr, username, node))
95 if err != nil {
96 return err
97 }
98 resp, err := g.c.Do(&http.Request{
99 URL: addr,
100 Method: http.MethodDelete,
101 Body: nil,
102 })
103 if err != nil {
104 return err
105 }
106 if resp.StatusCode != http.StatusOK {
107 var buf bytes.Buffer
108 io.Copy(&buf, resp.Body)
109 return errors.New(buf.String())
110 }
111 return nil
112}
giof6ad2982024-08-23 17:42:49 +0400113
114func (g *headscaleAPIClient) GetNodeIP(username, node string) (net.IP, error) {
115 addr, err := url.Parse(fmt.Sprintf("%s/user/%s/node/%s/ip", g.apiAddr, username, node))
116 if err != nil {
117 return nil, err
118 }
119 resp, err := g.c.Do(&http.Request{
120 URL: addr,
121 Method: http.MethodGet,
122 Body: nil,
123 })
124 if err != nil {
125 return nil, err
126 }
127 var buf bytes.Buffer
128 if _, err := io.Copy(&buf, resp.Body); err != nil {
129 return nil, err
130 }
131 bufS := buf.String()
132 if resp.StatusCode == http.StatusNotFound {
133 return nil, ErrorNotFound
134 }
135 if resp.StatusCode != http.StatusOK {
136 return nil, errors.New(bufS)
137 }
138 ip := net.ParseIP(bufS)
139 if ip == nil {
140 return nil, fmt.Errorf("invalid ip")
141 }
142 return ip, nil
143}