diff --git a/go.mod b/go.mod index 1323955..10d1fbc 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,9 @@ go 1.12 require ( github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc // indirect github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf // indirect + github.com/dgrijalva/jwt-go v3.2.0+incompatible github.com/google/nftables v0.0.0-20190430150743-07c974e3643d + github.com/julienschmidt/httprouter v1.2.0 github.com/koneu/natend v0.0.0-20150829182554-ec0926ea948d // indirect github.com/mdlayher/wireguardctrl v0.0.0-20190419142446-a4a944b88a6b github.com/sirupsen/logrus v1.4.1 diff --git a/go.sum b/go.sum index c9488ae..ba125e1 100644 --- a/go.sum +++ b/go.sum @@ -3,10 +3,14 @@ github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuy github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf h1:qet1QNfXsQxTZqLG4oE62mJzwPIB8+Tee4RNCL9ulrY= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/nftables v0.0.0-20190430150743-07c974e3643d h1:TC4HtISCtWOTUVPQmq4CAqe0+8R2fvnkJlQn47ep2hI= github.com/google/nftables v0.0.0-20190430150743-07c974e3643d/go.mod h1:H/FvZdk1xWxiE2Ad0wX6mq/oqHvEE1LDDujRgYwi3ns= +github.com/julienschmidt/httprouter v1.2.0 h1:TDTW5Yz1mjftljbcKqRcrYhd4XeOoI98t+9HbQbYf7g= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/koneu/natend v0.0.0-20150829182554-ec0926ea948d h1:MFX8DxRnKMY/2M3H61iSsVbo/n3h0MWGmWNN1UViOU0= github.com/koneu/natend v0.0.0-20150829182554-ec0926ea948d/go.mod h1:QHb4k4cr1fQikUahfcRVPcEXiUgFsdIstGqlurL0XL4= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= diff --git a/server.go b/server.go index e795bd1..45ea4b5 100644 --- a/server.go +++ b/server.go @@ -1,11 +1,16 @@ package main import ( + "context" + "encoding/json" "net/http" "os" + "strings" + "github.com/dgrijalva/jwt-go" "github.com/google/nftables" "github.com/google/nftables/expr" + "github.com/julienschmidt/httprouter" "github.com/mdlayher/wireguardctrl" "github.com/mdlayher/wireguardctrl/wgtypes" log "github.com/sirupsen/logrus" @@ -15,15 +20,16 @@ import ( ) var ( - listenAddr = kingpin.Flag("listen-address", "Address to listen to").Default(":8080").String() + listenAddr = kingpin.Flag("listen-address", "Address to listen to").Default(":8080").String() + natLink = kingpin.Flag("nat-device", "Network interface to masquerade").Default("wlp2s0").String() + wgLinkName = kingpin.Flag("wg-device-name", "Wireguard network device name").Default("wg0").String() wgLinkAddr = kingpin.Flag("wg-link-addr", "Wireguard interface address").Default("172.72.72.1/32").String() - natLink = kingpin.Flag("nat-device", "Network interface to masquerade").Default("wlp2s0").String() wgListenPort = kingpin.Flag("wg-listen-port", "Wireguard UDP port to listen to").Default("51820").Int() + wgEndpoint = kingpin.Flag("wg-endpoint", "Wireguard endpoint address").Default("127.0.0.1").String() ) type Server struct { - mux *http.ServeMux storage *Storage config *ServerConfig } @@ -32,6 +38,9 @@ type WgLink struct { attrs *netlink.LinkAttrs } +type jwtClaims struct { +} + func (w *WgLink) Attrs() *netlink.LinkAttrs { return w.attrs } @@ -50,13 +59,10 @@ func NewServer() *Server { storage := NewStorage() server := &Server{ - mux: http.NewServeMux(), storage: storage, config: storage.GetServerConfig(), } - server.mux.HandleFunc("/", server.Hello) - return server } @@ -95,7 +101,7 @@ func (s *Server) initInterface() error { conn := nftables.Conn{NetNS: int(ns)} log.Debug("Flushing nftable rulesets") - conn.FlushRuleset() + // conn.FlushRuleset() log.Debug("Setting up nftable rules for ip masquerading") @@ -162,10 +168,78 @@ func (s *Server) Start() error { return err } + router := httprouter.New() + router.GET("/", s.Index) + router.GET("/api/v1/users/:user/devices", s.withAuth(s.GetDevices)) + log.WithField("listenAddr", *listenAddr).Info("Starting server") - return http.ListenAndServe(*listenAddr, s.mux) + return http.ListenAndServe(*listenAddr, router) } -func (s *Server) Hello(w http.ResponseWriter, r *http.Request) { +func userFromJwtToken(r *http.Request) string { + authHeader := r.Header.Get("authorization") + if authHeader == "" { + log.Debug("No Authorization header") + return "" + } + + if !strings.HasPrefix(authHeader, "Bearer ") { + log.Debug("Incorrect Authorization header: ", authHeader) + return "" + } + + claims := jwt.MapClaims{} + token, err := jwt.ParseWithClaims(authHeader[7:], &claims, func(token *jwt.Token) (interface{}, error) { + return []byte(""), nil + }) + + if token == nil { + log.Debug("Error parsing JWT token: ", err) + return "" + } + + user, ok := claims["email"] + if ok { + return user.(string) + } + + user, ok = claims["sub"] + if ok { + return user.(string) + } + + return "" +} + +func (s *Server) withAuth(handler httprouter.Handle) httprouter.Handle { + return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { + log.Debug("Auth required") + + user := userFromJwtToken(r) + if user == "" { + user = "anonymous" + } + + ctx := context.WithValue(r.Context(), "user", user) + handler(w, r.WithContext(ctx), ps) + } +} + +func (s *Server) Index(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { + log.Debug("Index") w.Write([]byte("Hello World")) } + +func (s *Server) GetDevices(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { + user := r.Context().Value("user") + if user != ps.ByName("user") { + log.WithField("user", user).WithField("path", r.URL.Path).Warn("Unauthorized access") + w.WriteHeader(http.StatusUnauthorized) + return + } + err := json.NewEncoder(w).Encode(s.config) + if err != nil { + log.Error(err) + w.WriteHeader(http.StatusInternalServerError) + } +} diff --git a/storage.go b/storage.go index b762342..ea188d4 100644 --- a/storage.go +++ b/storage.go @@ -18,15 +18,18 @@ var ( type ServerConfig struct { PrivateKey string PublicKey string + Users []*UserConfig } type UserConfig struct { Name string - Devices []DeviceConfig + Devices []*DeviceConfig } type DeviceConfig struct { - PublicKey string + Name string + PrivateKey string + PublicKey string } type Storage struct {