blob: 942d71a54dff64704d33976e0581df162b80662c [file] [log] [blame]
Giorgi Lekveishvili52814d92023-06-15 19:30:32 +04001package main
2
3import (
4 "encoding/json"
5 "flag"
6 "fmt"
7 "log"
8 "net/http"
Giorgi Lekveishvili6ae65d12023-12-04 15:37:53 +04009 "os"
10 "text/template"
Giorgi Lekveishvili52814d92023-06-15 19:30:32 +040011
12 "github.com/labstack/echo/v4"
13)
14
15var port = flag.Int("port", 3000, "Port to listen on")
16var config = flag.String("config", "", "Path to headscale config")
Giorgi Lekveishvili6ae65d12023-12-04 15:37:53 +040017var acls = flag.String("acls", "", "Path to the headscale acls file")
18var domain = flag.String("domain", "", "Environment domain")
19
Giorgi Lekveishvili2dbce6c2023-12-05 15:16:27 +040020// TODO(gio): make internal network cidr and proxy user configurable
Giorgi Lekveishvili6ae65d12023-12-04 15:37:53 +040021const defaultACLs = `
22{
Giorgi Lekveishvili6ae65d12023-12-04 15:37:53 +040023 "autoApprovers": {
24 "routes": {
Giorgi Lekveishvili2dbce6c2023-12-05 15:16:27 +040025 // "10.1.0.0/24": ["private-network-proxy@{{ .Domain }}"],
26 "10.1.0.0/24": ["*"],
Giorgi Lekveishvili6ae65d12023-12-04 15:37:53 +040027 },
28 },
29 "acls": [
Giorgi Lekveishvili2dbce6c2023-12-05 15:16:27 +040030 { // Everyone has passthough access to private-network-proxy node
Giorgi Lekveishvili6ae65d12023-12-04 15:37:53 +040031 "action": "accept",
32 "src": ["*"],
Giorgi Lekveishvili2dbce6c2023-12-05 15:16:27 +040033 "dst": ["10.1.0.0/24:*", "private-network-proxy:0"],
34 },
35 ],
36 "tests": [
37 {
38 "src": "*",
39 "accept": ["10.1.0.1:80", "10.1.0.1:443"],
Giorgi Lekveishvili6ae65d12023-12-04 15:37:53 +040040 },
41 ],
42}
43`
Giorgi Lekveishvili52814d92023-06-15 19:30:32 +040044
45type server struct {
46 port int
47 client *client
48}
49
50func newServer(port int, client *client) *server {
51 return &server{
52 port,
53 client,
54 }
55}
56
57func (s *server) start() {
58 e := echo.New()
59 e.POST("/user/:user/preauthkey", s.createReusablePreAuthKey)
60 e.POST("/user", s.createUser)
61 e.POST("/routes/:id/enable", s.enableRoute)
62 log.Fatal(e.Start(fmt.Sprintf(":%d", s.port)))
63}
64
65type createUserReq struct {
66 Name string `json:"name"`
67}
68
69func (s *server) createUser(c echo.Context) error {
70 var req createUserReq
71 if err := json.NewDecoder(c.Request().Body).Decode(&req); err != nil {
72 return err
73 }
74 if err := s.client.createUser(req.Name); err != nil {
75 return err
76 } else {
77 return c.String(http.StatusOK, "")
78 }
79}
80
81func (s *server) createReusablePreAuthKey(c echo.Context) error {
82 if key, err := s.client.createPreAuthKey(c.Param("user")); err != nil {
83 return err
84 } else {
85 return c.String(http.StatusOK, key)
86 }
87}
88
89func (s *server) enableRoute(c echo.Context) error {
90 if err := s.client.enableRoute(c.Param("id")); err != nil {
91 return err
92 } else {
93 return c.String(http.StatusOK, "")
94 }
95}
96
Giorgi Lekveishvili6ae65d12023-12-04 15:37:53 +040097func updateACLs(domain, acls string) error {
98 tmpl, err := template.New("acls").Parse(defaultACLs)
99 if err != nil {
100 return err
101 }
102 out, err := os.Create(acls)
103 if err != nil {
104 return err
105 }
106 defer out.Close()
107 return tmpl.Execute(out, map[string]any{
108 "Domain": domain,
109 })
110}
111
Giorgi Lekveishvili52814d92023-06-15 19:30:32 +0400112func main() {
113 flag.Parse()
Giorgi Lekveishvili6ae65d12023-12-04 15:37:53 +0400114 updateACLs(*domain, *acls)
Giorgi Lekveishvili52814d92023-06-15 19:30:32 +0400115 c := newClient(*config)
116 s := newServer(*port, c)
117 s.start()
118}