blob: 43e63b7ea94711ebc5429af0ef738fc6c02a9797 [file] [log] [blame]
Giorgi Lekveishvili52814d92023-06-15 19:30:32 +04001package main
2
3import (
4 "encoding/json"
5 "flag"
6 "fmt"
7 "log"
Giorgi Lekveishvili9d5e3f52024-03-13 15:02:50 +04008 "net"
Giorgi Lekveishvili52814d92023-06-15 19:30:32 +04009 "net/http"
Giorgi Lekveishvili6ae65d12023-12-04 15:37:53 +040010 "os"
gioc23530e2024-05-01 11:06:09 +040011 "strings"
Giorgi Lekveishvili6ae65d12023-12-04 15:37:53 +040012 "text/template"
Giorgi Lekveishvili52814d92023-06-15 19:30:32 +040013
14 "github.com/labstack/echo/v4"
15)
16
17var port = flag.Int("port", 3000, "Port to listen on")
18var config = flag.String("config", "", "Path to headscale config")
Giorgi Lekveishvili6ae65d12023-12-04 15:37:53 +040019var acls = flag.String("acls", "", "Path to the headscale acls file")
Giorgi Lekveishvili9d5e3f52024-03-13 15:02:50 +040020var ipSubnet = flag.String("ip-subnet", "10.1.0.0/24", "IP subnet of the private network")
Giorgi Lekveishvili6ae65d12023-12-04 15:37:53 +040021
Giorgi Lekveishvili2dbce6c2023-12-05 15:16:27 +040022// TODO(gio): make internal network cidr and proxy user configurable
Giorgi Lekveishvili6ae65d12023-12-04 15:37:53 +040023const defaultACLs = `
24{
Giorgi Lekveishvili6ae65d12023-12-04 15:37:53 +040025 "autoApprovers": {
26 "routes": {
gioc23530e2024-05-01 11:06:09 +040027 {{- range .cidrs }}
28 "{{ . }}": ["*"],
29 {{- end }}
Giorgi Lekveishvili6ae65d12023-12-04 15:37:53 +040030 },
31 },
32 "acls": [
gioc23530e2024-05-01 11:06:09 +040033 {{- range .cidrs }}
Giorgi Lekveishvili2dbce6c2023-12-05 15:16:27 +040034 { // Everyone has passthough access to private-network-proxy node
Giorgi Lekveishvili6ae65d12023-12-04 15:37:53 +040035 "action": "accept",
36 "src": ["*"],
gioc23530e2024-05-01 11:06:09 +040037 "dst": ["{{ . }}:*", "private-network-proxy:0"],
Giorgi Lekveishvili6ae65d12023-12-04 15:37:53 +040038 },
gioc23530e2024-05-01 11:06:09 +040039 {{- end }}
Giorgi Lekveishvili6ae65d12023-12-04 15:37:53 +040040 ],
41}
42`
Giorgi Lekveishvili52814d92023-06-15 19:30:32 +040043
44type server struct {
45 port int
46 client *client
47}
48
49func newServer(port int, client *client) *server {
50 return &server{
51 port,
52 client,
53 }
54}
55
56func (s *server) start() {
57 e := echo.New()
58 e.POST("/user/:user/preauthkey", s.createReusablePreAuthKey)
59 e.POST("/user", s.createUser)
60 e.POST("/routes/:id/enable", s.enableRoute)
61 log.Fatal(e.Start(fmt.Sprintf(":%d", s.port)))
62}
63
64type createUserReq struct {
65 Name string `json:"name"`
66}
67
68func (s *server) createUser(c echo.Context) error {
69 var req createUserReq
70 if err := json.NewDecoder(c.Request().Body).Decode(&req); err != nil {
71 return err
72 }
73 if err := s.client.createUser(req.Name); err != nil {
74 return err
75 } else {
76 return c.String(http.StatusOK, "")
77 }
78}
79
80func (s *server) createReusablePreAuthKey(c echo.Context) error {
81 if key, err := s.client.createPreAuthKey(c.Param("user")); err != nil {
82 return err
83 } else {
84 return c.String(http.StatusOK, key)
85 }
86}
87
88func (s *server) enableRoute(c echo.Context) error {
89 if err := s.client.enableRoute(c.Param("id")); err != nil {
90 return err
91 } else {
92 return c.String(http.StatusOK, "")
93 }
94}
95
gioc23530e2024-05-01 11:06:09 +040096func updateACLs(cidrs []string, aclsPath string) error {
Giorgi Lekveishvili6ae65d12023-12-04 15:37:53 +040097 tmpl, err := template.New("acls").Parse(defaultACLs)
98 if err != nil {
99 return err
100 }
Giorgi Lekveishvili9d5e3f52024-03-13 15:02:50 +0400101 out, err := os.Create(aclsPath)
Giorgi Lekveishvili6ae65d12023-12-04 15:37:53 +0400102 if err != nil {
103 return err
104 }
105 defer out.Close()
gioc23530e2024-05-01 11:06:09 +0400106 tmpl.Execute(os.Stdout, map[string]any{
107 "cidrs": cidrs,
108 })
Giorgi Lekveishvili6ae65d12023-12-04 15:37:53 +0400109 return tmpl.Execute(out, map[string]any{
gioc23530e2024-05-01 11:06:09 +0400110 "cidrs": cidrs,
Giorgi Lekveishvili6ae65d12023-12-04 15:37:53 +0400111 })
112}
113
Giorgi Lekveishvili52814d92023-06-15 19:30:32 +0400114func main() {
115 flag.Parse()
gioc23530e2024-05-01 11:06:09 +0400116 var cidrs []string
117 for _, ips := range strings.Split(*ipSubnet, ",") {
118 _, cidr, err := net.ParseCIDR(ips)
119 if err != nil {
120 panic(err)
121 }
122 cidrs = append(cidrs, cidr.String())
Giorgi Lekveishvili9d5e3f52024-03-13 15:02:50 +0400123 }
gioc23530e2024-05-01 11:06:09 +0400124 updateACLs(cidrs, *acls)
Giorgi Lekveishvili52814d92023-06-15 19:30:32 +0400125 c := newClient(*config)
126 s := newServer(*port, c)
127 s.start()
128}