blob: 51302ad22a1c01d831be992b7826cc844a4ae864 [file] [log] [blame]
giolekva4b2934b2021-10-08 19:37:12 +04001package main
2
3import (
giolekva4b2934b2021-10-08 19:37:12 +04004 "embed"
giolekvab64297c2021-12-13 14:36:32 +04005 "encoding/json"
giolekva4b2934b2021-10-08 19:37:12 +04006 "flag"
7 "fmt"
8 "html/template"
9 "log"
10 "net/http"
11
12 "github.com/gorilla/mux"
13
giolekva4b2934b2021-10-08 19:37:12 +040014 "k8s.io/client-go/kubernetes"
15 "k8s.io/client-go/tools/clientcmd"
16
giolekvac6859b02021-12-09 18:40:51 +040017 clientset "github.com/giolekva/pcloud/core/nebula/controller/generated/clientset/versioned"
giolekva4b2934b2021-10-08 19:37:12 +040018)
19
20var port = flag.Int("port", 8080, "Port to listen on.")
21var kubeConfig = flag.String("kubeconfig", "", "Path to a kubeconfig. Only required if out-of-cluster.")
22var masterURL = flag.String("master", "", "The address of the Kubernetes API server. Overrides any value in kubeconfig. Only required if out-of-cluster.")
giolekvab64297c2021-12-13 14:36:32 +040023var namespace = flag.String("namespace", "", "Namespace where Nebula CA and Node secrets are stored.")
giolekvaf58a7692021-12-15 18:05:39 +040024var caName = flag.String("ca-name", "", "Name of the Nebula CA.")
giolekva4b2934b2021-10-08 19:37:12 +040025
26//go:embed templates/*
27var tmpls embed.FS
28
29type Templates struct {
30 Index *template.Template
31}
32
33func ParseTemplates(fs embed.FS) (*Templates, error) {
34 index, err := template.ParseFS(fs, "templates/index.html")
35 if err != nil {
36 return nil, err
37 }
38 return &Templates{index}, nil
39}
40
giolekva4b2934b2021-10-08 19:37:12 +040041type Handler struct {
42 mgr Manager
43 tmpls *Templates
44}
45
46func (h *Handler) handleIndex(w http.ResponseWriter, r *http.Request) {
47 cas, err := h.mgr.ListAll()
48 if err != nil {
49 http.Error(w, err.Error(), http.StatusInternalServerError)
50 return
51 }
52 if err := h.tmpls.Index.Execute(w, cas); err != nil {
53 http.Error(w, err.Error(), http.StatusInternalServerError)
54 }
55}
56
57func (h *Handler) handleNode(w http.ResponseWriter, r *http.Request) {
58 vars := mux.Vars(r)
59 namespace := vars["namespace"]
60 name := vars["name"]
giolekvab64297c2021-12-13 14:36:32 +040061 qr, err := h.mgr.GetNodeCertQR(namespace, name)
giolekva4b2934b2021-10-08 19:37:12 +040062 if err != nil {
63 http.Error(w, err.Error(), http.StatusInternalServerError)
64 }
65 w.Header().Set("Content-Type", "img/png")
66 w.Write(qr)
67}
68
69func (h *Handler) handleCA(w http.ResponseWriter, r *http.Request) {
70 vars := mux.Vars(r)
71 namespace := vars["namespace"]
72 name := vars["name"]
giolekvab64297c2021-12-13 14:36:32 +040073 qr, err := h.mgr.GetCACertQR(namespace, name)
giolekva4b2934b2021-10-08 19:37:12 +040074 if err != nil {
75 http.Error(w, err.Error(), http.StatusInternalServerError)
76 }
77 w.Header().Set("Content-Type", "img/png")
78 w.Write(qr)
79}
80
81func (h *Handler) handleSignNode(w http.ResponseWriter, r *http.Request) {
82 if err := r.ParseForm(); err != nil {
83 http.Error(w, err.Error(), http.StatusBadRequest)
84 return
85 }
giolekvab64297c2021-12-13 14:36:32 +040086 _, _, err := h.mgr.CreateNode(
giolekva4b2934b2021-10-08 19:37:12 +040087 r.FormValue("node-namespace"),
88 r.FormValue("node-name"),
89 r.FormValue("ca-namespace"),
90 r.FormValue("ca-name"),
91 r.FormValue("ip-cidr"),
92 r.FormValue("pub-key"),
93 )
94 if err != nil {
95 http.Error(w, err.Error(), http.StatusInternalServerError)
96 return
97 }
98 http.Redirect(w, r, "/", http.StatusSeeOther)
99}
100
giolekvab64297c2021-12-13 14:36:32 +0400101func (h *Handler) getNextIP(w http.ResponseWriter, r *http.Request) {
102 ip, err := h.mgr.getNextIP()
103 if err != nil {
104 http.Error(w, err.Error(), http.StatusInternalServerError)
105 return
106 }
107 fmt.Fprint(w, ip)
108}
109
110type signReq struct {
111 Message []byte `json:"message"`
112}
113
114type signResp struct {
115 Signature []byte `json:"signature"`
116}
117
118func (h *Handler) sign(w http.ResponseWriter, r *http.Request) {
119 var req signReq
120 if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
121 http.Error(w, err.Error(), http.StatusBadRequest)
122 return
123 }
124 signature, err := h.mgr.Sign(req.Message)
125 if err != nil {
126 http.Error(w, err.Error(), http.StatusInternalServerError)
127 return
128 }
129 w.Header().Set("Content-Type", "application/json")
130 resp := signResp{
131 signature,
132 }
133 if err := json.NewEncoder(w).Encode(resp); err != nil {
134 http.Error(w, err.Error(), http.StatusInternalServerError)
135 return
136 }
137}
138
giolekvaf58a7692021-12-15 18:05:39 +0400139type joinReq struct {
giolekvab64297c2021-12-13 14:36:32 +0400140 Message []byte `json:"message"`
141 Signature []byte `json:"signature"`
giolekvaf58a7692021-12-15 18:05:39 +0400142 Name string `json:"name"`
143 PublicKey []byte `json:"public_key"`
144 IPCidr string `json:"ip_cidr"`
giolekvab64297c2021-12-13 14:36:32 +0400145}
146
giolekvaf58a7692021-12-15 18:05:39 +0400147type joinResp struct {
148}
149
150func (h *Handler) join(w http.ResponseWriter, r *http.Request) {
151 var req joinReq
giolekvab64297c2021-12-13 14:36:32 +0400152 if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
153 http.Error(w, err.Error(), http.StatusBadRequest)
154 return
155 }
156 valid, err := h.mgr.VerifySignature(req.Message, req.Signature)
157 if err != nil {
158 http.Error(w, err.Error(), http.StatusInternalServerError)
159 return
160 }
161 if !valid {
162 http.Error(w, "Signature could not be verified", http.StatusBadRequest)
163 return
164 }
giolekvaf58a7692021-12-15 18:05:39 +0400165 _, _, err = h.mgr.CreateNode(
166 *namespace,
167 req.Name,
168 *namespace,
169 *caName,
170 req.IPCidr,
171 string(req.PublicKey),
172 )
173 if err != nil {
174 http.Error(w, err.Error(), http.StatusInternalServerError)
175 return
176 }
giolekvab64297c2021-12-13 14:36:32 +0400177}
178
giolekva4b2934b2021-10-08 19:37:12 +0400179func main() {
180 flag.Parse()
181 cfg, err := clientcmd.BuildConfigFromFlags(*masterURL, *kubeConfig)
182 if err != nil {
183 panic(err)
184 }
185 kubeClient, err := kubernetes.NewForConfig(cfg)
186 if err != nil {
187 panic(err)
188 }
189 nebulaClient := clientset.NewForConfigOrDie(cfg)
190 t, err := ParseTemplates(tmpls)
191 if err != nil {
192 log.Fatal(err)
193 }
194 mgr := Manager{
195 kubeClient: kubeClient,
196 nebulaClient: nebulaClient,
giolekvab64297c2021-12-13 14:36:32 +0400197 namespace: *namespace,
giolekvaf58a7692021-12-15 18:05:39 +0400198 caName: *caName,
giolekva4b2934b2021-10-08 19:37:12 +0400199 }
200 handler := Handler{
201 mgr: mgr,
202 tmpls: t,
203 }
204 r := mux.NewRouter()
giolekvab64297c2021-12-13 14:36:32 +0400205 r.HandleFunc("/api/ip", handler.getNextIP)
206 r.HandleFunc("/api/sign", handler.sign)
giolekvaf58a7692021-12-15 18:05:39 +0400207 r.HandleFunc("/api/join", handler.join)
giolekva4b2934b2021-10-08 19:37:12 +0400208 r.HandleFunc("/node/{namespace:[a-zA-z0-9-]+}/{name:[a-zA-z0-9-]+}", handler.handleNode)
209 r.HandleFunc("/ca/{namespace:[a-zA-z0-9-]+}/{name:[a-zA-z0-9-]+}", handler.handleCA)
210 r.HandleFunc("/sign-node", handler.handleSignNode)
211 r.HandleFunc("/", handler.handleIndex)
212 http.Handle("/", r)
213 fmt.Printf("Starting HTTP server on port: %d\n", *port)
214 log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", *port), nil))
215}