blob: c0c4817a1a8a61ba2a6833fc9e9a5d84117293ef [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.")
24var caSecretName = flag.String("ca-secret-name", "", "Name of the Nebula CA secret storing certificate information.")
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
139type verifyReq struct {
140 Message []byte `json:"message"`
141 Signature []byte `json:"signature"`
142}
143
144func (h *Handler) verify(w http.ResponseWriter, r *http.Request) {
145 var req verifyReq
146 if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
147 http.Error(w, err.Error(), http.StatusBadRequest)
148 return
149 }
150 valid, err := h.mgr.VerifySignature(req.Message, req.Signature)
151 if err != nil {
152 http.Error(w, err.Error(), http.StatusInternalServerError)
153 return
154 }
155 if !valid {
156 http.Error(w, "Signature could not be verified", http.StatusBadRequest)
157 return
158 }
159}
160
giolekva4b2934b2021-10-08 19:37:12 +0400161func main() {
162 flag.Parse()
163 cfg, err := clientcmd.BuildConfigFromFlags(*masterURL, *kubeConfig)
164 if err != nil {
165 panic(err)
166 }
167 kubeClient, err := kubernetes.NewForConfig(cfg)
168 if err != nil {
169 panic(err)
170 }
171 nebulaClient := clientset.NewForConfigOrDie(cfg)
172 t, err := ParseTemplates(tmpls)
173 if err != nil {
174 log.Fatal(err)
175 }
176 mgr := Manager{
177 kubeClient: kubeClient,
178 nebulaClient: nebulaClient,
giolekvab64297c2021-12-13 14:36:32 +0400179 namespace: *namespace,
180 caSecretName: *caSecretName,
giolekva4b2934b2021-10-08 19:37:12 +0400181 }
182 handler := Handler{
183 mgr: mgr,
184 tmpls: t,
185 }
186 r := mux.NewRouter()
giolekvab64297c2021-12-13 14:36:32 +0400187 r.HandleFunc("/api/ip", handler.getNextIP)
188 r.HandleFunc("/api/sign", handler.sign)
189 r.HandleFunc("/api/verify", handler.verify)
giolekva4b2934b2021-10-08 19:37:12 +0400190 r.HandleFunc("/node/{namespace:[a-zA-z0-9-]+}/{name:[a-zA-z0-9-]+}", handler.handleNode)
191 r.HandleFunc("/ca/{namespace:[a-zA-z0-9-]+}/{name:[a-zA-z0-9-]+}", handler.handleCA)
192 r.HandleFunc("/sign-node", handler.handleSignNode)
193 r.HandleFunc("/", handler.handleIndex)
194 http.Handle("/", r)
195 fmt.Printf("Starting HTTP server on port: %d\n", *port)
196 log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", *port), nil))
197}