AppManager: Clean up VPN node and auth keys upon app removal
Change-Id: Ie76278556247d16806ba81286621adca973e3f6e
diff --git a/core/installer/vpn.go b/core/installer/vpn.go
index 4739bf2..01161df 100644
--- a/core/installer/vpn.go
+++ b/core/installer/vpn.go
@@ -2,25 +2,34 @@
import (
"bytes"
+ "encoding/json"
"errors"
"fmt"
"io"
"net/http"
+ "net/url"
)
-type VPNAuthKeyGenerator interface {
- Generate(username string) (string, error)
+type VPNAPIClient interface {
+ GenerateAuthKey(username string) (string, error)
+ ExpireKey(username, key string) error
+ ExpireNode(username, node string) error
+ RemoveNode(username, node string) error
}
type headscaleAPIClient struct {
+ c *http.Client
apiAddr string
}
-func NewHeadscaleAPIClient(apiAddr string) VPNAuthKeyGenerator {
- return &headscaleAPIClient{apiAddr}
+func NewHeadscaleAPIClient(apiAddr string) VPNAPIClient {
+ return &headscaleAPIClient{
+ &http.Client{},
+ apiAddr,
+ }
}
-func (g *headscaleAPIClient) Generate(username string) (string, error) {
+func (g *headscaleAPIClient) GenerateAuthKey(username string) (string, error) {
resp, err := http.Post(fmt.Sprintf("%s/user/%s/preauthkey", g.apiAddr, username), "application/json", nil)
if err != nil {
return "", err
@@ -32,3 +41,70 @@
}
return buf.String(), nil
}
+
+type expirePreAuthKeyReq struct {
+ AuthKey string `json:"authKey"`
+}
+
+func (g *headscaleAPIClient) ExpireKey(username, key string) error {
+ addr, err := url.Parse(fmt.Sprintf("%s/user/%s/preauthkey", g.apiAddr, username))
+ if err != nil {
+ return err
+ }
+ var buf bytes.Buffer
+ if err := json.NewEncoder(&buf).Encode(expirePreAuthKeyReq{key}); err != nil {
+ return err
+ }
+ resp, err := g.c.Do(&http.Request{
+ URL: addr,
+ Method: http.MethodDelete,
+ Body: io.NopCloser(&buf),
+ })
+ if err != nil {
+ return err
+ }
+ if resp.StatusCode != http.StatusOK {
+ var buf bytes.Buffer
+ io.Copy(&buf, resp.Body)
+ return errors.New(buf.String())
+ }
+ return nil
+}
+
+func (g *headscaleAPIClient) ExpireNode(username, node string) error {
+ resp, err := g.c.Post(
+ fmt.Sprintf("%s/user/%s/node/%s/expire", g.apiAddr, username, node),
+ "text/plain",
+ nil,
+ )
+ if err != nil {
+ return err
+ }
+ if resp.StatusCode != http.StatusOK {
+ var buf bytes.Buffer
+ io.Copy(&buf, resp.Body)
+ return errors.New(buf.String())
+ }
+ return nil
+}
+
+func (g *headscaleAPIClient) RemoveNode(username, node string) error {
+ addr, err := url.Parse(fmt.Sprintf("%s/user/%s/node/%s", g.apiAddr, username, node))
+ if err != nil {
+ return err
+ }
+ resp, err := g.c.Do(&http.Request{
+ URL: addr,
+ Method: http.MethodDelete,
+ Body: nil,
+ })
+ if err != nil {
+ return err
+ }
+ if resp.StatusCode != http.StatusOK {
+ var buf bytes.Buffer
+ io.Copy(&buf, resp.Body)
+ return errors.New(buf.String())
+ }
+ return nil
+}