diff --git a/client/internal/config.go b/client/internal/config.go index 8c9f03c6b..e1dc62b3f 100644 --- a/client/internal/config.go +++ b/client/internal/config.go @@ -22,7 +22,7 @@ func ManagementURLDefault() *url.URL { } func init() { - managementURL, err := parseURL("Management URL", "https://api.wiretrustee.com:33073") + managementURL, err := parseURL("Management URL", "https://api.wiretrustee.com:443") if err != nil { panic(err) } diff --git a/go.mod b/go.mod index 8cfd0e43e..3e2cddabb 100644 --- a/go.mod +++ b/go.mod @@ -39,6 +39,7 @@ require ( github.com/rs/xid v1.3.0 github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 github.com/stretchr/testify v1.7.1 + golang.org/x/net v0.0.0-20220513224357-95641704303c golang.org/x/term v0.0.0-20220526004731-065cf7ba2467 ) @@ -96,7 +97,6 @@ require ( github.com/yuin/goldmark v1.4.1 // indirect golang.org/x/image v0.0.0-20200430140353-33d19683fad8 // indirect golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 // indirect - golang.org/x/net v0.0.0-20220513224357-95641704303c // indirect golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect golang.org/x/text v0.3.8-0.20211105212822-18b340fc7af2 // indirect golang.org/x/tools v0.1.10 // indirect diff --git a/management/cmd/management.go b/management/cmd/management.go index 45942db97..9ae607049 100644 --- a/management/cmd/management.go +++ b/management/cmd/management.go @@ -1,21 +1,25 @@ package cmd import ( - "context" "crypto/tls" "errors" "flag" "fmt" + httpapi "github.com/netbirdio/netbird/management/server/http" + "golang.org/x/crypto/acme/autocert" + "golang.org/x/net/http2" + "golang.org/x/net/http2/h2c" "io" "io/fs" "io/ioutil" "net" + "net/http" "os" "path" + "strings" "time" "github.com/netbirdio/netbird/management/server" - "github.com/netbirdio/netbird/management/server/http" "github.com/netbirdio/netbird/management/server/idp" "github.com/netbirdio/netbird/util" @@ -28,11 +32,16 @@ import ( "google.golang.org/grpc/keepalive" ) +// ManagementLegacyPort is the port that was used before by the Management gRPC server. +// It is used for backward compatibility now. +const ManagementLegacyPort = 33073 + var ( mgmtPort int mgmtLetsencryptDomain string certFile string certKey string + config *server.Config kaep = keepalive.EnforcementPolicy{ MinTime: 15 * time.Second, @@ -46,47 +55,57 @@ var ( Timeout: 2 * time.Second, } - // TLS enabled: - // - HTTP 80 for LetsEncrypt - // - if --port not specified gRPC and HTTP servers on 443 (with multiplexing) - // - if --port=X specified then run gRPC and HTTP servers on X (with multiplexing) - // - if --port=80 forbid this (throw error, otherwise we need to overcomplicate the logic with multiplexing) - // TLS disabled: - // - if --port not specified gRPC and HTTP servers on 443 on 80 (with multiplexing) - // - if --port=X specified then run gRPC and HTTP servers on 443 on X (with multiplexing) - // Always run gRPC on port 33073 regardless of TLS to be backward compatible - // Remove HTTP port 33071 from the configuration. - mgmtCmd = &cobra.Command{ Use: "management", - Short: "start Netbird Management Server", - Run: func(cmd *cobra.Command, args []string) { + Short: "start NetBird Management Server", + PreRunE: func(cmd *cobra.Command, args []string) error { + // detect whether user specified a port + userPort := cmd.Flag("port").Changed + + var err error + config, err = loadMgmtConfig(mgmtConfig) + if err != nil { + return fmt.Errorf("failed reading provided config file: %s: %v", mgmtConfig, err) + } + + tlsEnabled := false + if mgmtLetsencryptDomain != "" || (config.HttpConfig.CertFile != "" && config.HttpConfig.CertKey != "") { + tlsEnabled = true + } + + if !userPort { + // different defaults for port when tls enabled/disabled + if tlsEnabled { + mgmtPort = 443 + } else { + mgmtPort = 80 + } + } + + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { flag.Parse() err := util.InitLog(logLevel, logFile) if err != nil { - log.Fatalf("failed initializing log %v", err) + return fmt.Errorf("failed initializing log %v", err) } err = handleRebrand(cmd) if err != nil { - log.Fatalf("failed to migrate files %v", err) - } - - config, err := loadMgmtConfig(mgmtConfig) - if err != nil { - log.Fatalf("failed reading provided config file: %s: %v", mgmtConfig, err) + return fmt.Errorf("failed to migrate files %v", err) } if _, err = os.Stat(config.Datadir); os.IsNotExist(err) { err = os.MkdirAll(config.Datadir, os.ModeDir) if err != nil { - log.Fatalf("failed creating datadir: %s: %v", config.Datadir, err) + return fmt.Errorf("failed creating datadir: %s: %v", config.Datadir, err) } } store, err := server.NewStore(config.Datadir) if err != nil { - log.Fatalf("failed creating a store: %s: %v", config.Datadir, err) + return fmt.Errorf("failed creating Store: %s: %v", config.Datadir, err) } peersUpdateManager := server.NewPeersUpdateManager() @@ -94,85 +113,181 @@ var ( if config.IdpManagerConfig != nil { idpManager, err = idp.NewManager(*config.IdpManagerConfig) if err != nil { - log.Fatalln("failed retrieving a new idp manager with err: ", err) + return fmt.Errorf("failed retrieving a new idp manager with err: %v", err) } } accountManager, err := server.BuildManager(store, peersUpdateManager, idpManager) if err != nil { - log.Fatalln("failed build default manager: ", err) + return fmt.Errorf("failed to build default manager: %v", err) } - var opts []grpc.ServerOption + turnManager := server.NewTimeBasedAuthSecretsManager(peersUpdateManager, config.TURNConfig) - var httpServer *http.Server + gRPCOpts := []grpc.ServerOption{grpc.KeepaliveEnforcementPolicy(kaep), grpc.KeepaliveParams(kasp)} + var certManager *autocert.Manager + var tlsConfig *tls.Config + tlsEnabled := false if config.HttpConfig.LetsEncryptDomain != "" { - // automatically generate a new certificate with Let's Encrypt - certManager, err := encryption.CreateCertManager(config.Datadir, config.HttpConfig.LetsEncryptDomain) + certManager, err = encryption.CreateCertManager(config.Datadir, config.HttpConfig.LetsEncryptDomain) if err != nil { - log.Fatalf("failed creating Let's Encrypt cert manager: %v", err) + return fmt.Errorf("failed creating LetsEncrypt cert manager: %v", err) } transportCredentials := credentials.NewTLS(certManager.TLSConfig()) - opts = append(opts, grpc.Creds(transportCredentials)) - - httpServer = http.NewHttpsServer(config.HttpConfig, certManager, accountManager) + gRPCOpts = append(gRPCOpts, grpc.Creds(transportCredentials)) + tlsEnabled = true } else if config.HttpConfig.CertFile != "" && config.HttpConfig.CertKey != "" { - // use provided certificate - tlsConfig, err := loadTLSConfig(config.HttpConfig.CertFile, config.HttpConfig.CertKey) + tlsConfig, err = loadTLSConfig(config.HttpConfig.CertFile, config.HttpConfig.CertKey) if err != nil { - log.Fatal("cannot load TLS credentials: ", err) + log.Errorf("cannot load TLS credentials: %v", err) + return err } transportCredentials := credentials.NewTLS(tlsConfig) - opts = append(opts, grpc.Creds(transportCredentials)) - httpServer = http.NewHttpsServerWithTLSConfig(config.HttpConfig, tlsConfig, accountManager) - } else { - // start server without SSL - httpServer = http.NewHttpServer(config.HttpConfig, accountManager) + gRPCOpts = append(gRPCOpts, grpc.Creds(transportCredentials)) + tlsEnabled = true } - opts = append(opts, grpc.KeepaliveEnforcementPolicy(kaep), grpc.KeepaliveParams(kasp)) - grpcServer := grpc.NewServer(opts...) - turnManager := server.NewTimeBasedAuthSecretsManager(peersUpdateManager, config.TURNConfig) - server, err := server.NewServer(config, accountManager, peersUpdateManager, turnManager) + httpAPIHandler, err := httpapi.APIHandler(accountManager, + config.HttpConfig.AuthIssuer, config.HttpConfig.AuthAudience, config.HttpConfig.AuthKeysLocation) if err != nil { - log.Fatalf("failed creating new server: %v", err) + return fmt.Errorf("failed creating HTTP API handler: %v", err) } - mgmtProto.RegisterManagementServiceServer(grpcServer, server) - log.Printf("started server: localhost:%v", mgmtPort) - lis, err := net.Listen("tcp", fmt.Sprintf(":%d", mgmtPort)) + gRPCAPIHandler := grpc.NewServer(gRPCOpts...) + srv, err := server.NewServer(config, accountManager, peersUpdateManager, turnManager) if err != nil { - log.Fatalf("failed to listen: %v", err) + return fmt.Errorf("failed creating gRPC API handler: %v", err) } + mgmtProto.RegisterManagementServiceServer(gRPCAPIHandler, srv) - go func() { - if err = grpcServer.Serve(lis); err != nil { - log.Fatalf("failed to serve gRpc server: %v", err) - } - }() - - go func() { - err = httpServer.Start() + var compatListener net.Listener + if mgmtPort != ManagementLegacyPort { + // The Management gRPC server was running on port 33073 previously. Old agents that are already connected to it + // are using port 33073. For compatibility purposes we keep running a 2nd gRPC server on port 33073. + compatListener, err = serveGRPC(gRPCAPIHandler, ManagementLegacyPort) if err != nil { - log.Fatalf("failed to serve http server: %v", err) + return err } - }() + log.Infof("running gRPC backward compatibility server: %s", compatListener.Addr().String()) + } + + rootHandler := handlerFunc(gRPCAPIHandler, httpAPIHandler) + var listener net.Listener + if certManager != nil { + // a call to certManager.Listener() always creates a new listener so we do it once + cml := certManager.Listener() + if mgmtPort == 443 { + // CertManager, HTTP and gRPC API all on the same port + rootHandler = certManager.HTTPHandler(rootHandler) + listener = cml + } else { + listener, err = tls.Listen("tcp", fmt.Sprintf(":%d", mgmtPort), certManager.TLSConfig()) + if err != nil { + return fmt.Errorf("failed creating TLS listener on port %d: %v", mgmtPort, err) + } + log.Infof("running HTTP server (LetsEncrypt challenge handler): %s", cml.Addr().String()) + serveHTTP(cml, certManager.HTTPHandler(nil)) + } + } else if tlsConfig != nil { + listener, err = tls.Listen("tcp", fmt.Sprintf(":%d", mgmtPort), tlsConfig) + if err != nil { + return fmt.Errorf("failed creating TLS listener on port %d: %v", mgmtPort, err) + } + } else { + listener, err = net.Listen("tcp", fmt.Sprintf(":%d", mgmtPort)) + if err != nil { + return fmt.Errorf("failed creating TCP listener on port %d: %v", mgmtPort, err) + } + } + + log.Infof("running HTTP server and gRPC server on the same port: %s", listener.Addr().String()) + serveGRPCWithHTTP(listener, rootHandler, tlsEnabled) SetupCloseHandler() - <-stopCh - log.Println("Receive signal to stop running Management server") - ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) - defer cancel() - err = httpServer.Stop(ctx) - if err != nil { - log.Fatalf("failed stopping the http server %v", err) - } - grpcServer.Stop() + <-stopCh + _ = listener.Close() + if certManager != nil { + _ = certManager.Listener().Close() + } + gRPCAPIHandler.Stop() + log.Infof("stopped Management Service") + + return nil }, } ) +func notifyStop(msg string) { + select { + case stopCh <- 1: + log.Error(msg) + default: + // stop has been already called, nothing to report + } +} + +func serveGRPC(grpcServer *grpc.Server, port int) (net.Listener, error) { + listener, err := net.Listen("tcp", fmt.Sprintf(":%d", port)) + if err != nil { + return nil, err + } + go func() { + err := grpcServer.Serve(listener) + if err != nil { + notifyStop(fmt.Sprintf("failed running gRPC server on port %d: %v", port, err)) + } + }() + return listener, nil +} + +func serveHTTP(httpListener net.Listener, handler http.Handler) { + go func() { + err := http.Serve(httpListener, handler) + if err != nil { + notifyStop(fmt.Sprintf("failed running HTTP server: %v", err)) + } + }() +} + +func serveGRPCWithHTTP(listener net.Listener, handler http.Handler, tlsEnabled bool) { + go func() { + var err error + if tlsEnabled { + err = http.Serve(listener, handler) + } else { + // the following magic is needed to support HTTP2 without TLS + // and still share a single port between gRPC and HTTP APIs + h1s := &http.Server{ + Handler: h2c.NewHandler(handler, &http2.Server{}), + } + err = h1s.Serve(listener) + } + + if err != nil { + select { + case stopCh <- 1: + log.Errorf("failed to serve HTTP and gRPC server: %v", err) + default: + // stop has been already called, nothing to report + } + } + }() +} + +func handlerFunc(gRPCHandler *grpc.Server, httpHandler http.Handler) http.Handler { + return http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { + grpcHeader := strings.HasPrefix(request.Header.Get("Content-Type"), "application/grpc") || + strings.HasPrefix(request.Header.Get("Content-Type"), "application/grpc+proto") + fmt.Println(grpcHeader) + if request.ProtoMajor == 2 && grpcHeader { + gRPCHandler.ServeHTTP(writer, request) + } else { + httpHandler.ServeHTTP(writer, request) + } + }) +} + func loadMgmtConfig(mgmtConfigPath string) (*server.Config, error) { config := &server.Config{} _, err := util.ReadJson(mgmtConfigPath, config) diff --git a/management/cmd/root.go b/management/cmd/root.go index 37ea347c1..a6cb951af 100644 --- a/management/cmd/root.go +++ b/management/cmd/root.go @@ -60,7 +60,7 @@ func init() { oldDefaultMgmtConfig = oldDefaultMgmtConfigDir + "/management.json" oldDefaultLogFile = oldDefaultLogDir + "/management.log" - mgmtCmd.Flags().IntVar(&mgmtPort, "port", 33073, "server port to listen on") + mgmtCmd.Flags().IntVar(&mgmtPort, "port", 80, "server port to listen on (defaults to 443 if TLS is enabled, 80 otherwise") mgmtCmd.Flags().StringVar(&mgmtDataDir, "datadir", defaultMgmtDataDir, "server data directory location") mgmtCmd.Flags().StringVar(&mgmtConfig, "config", defaultMgmtConfig, "Netbird config file location. Config params specified via command line (e.g. datadir) have a precedence over configuration from this file") mgmtCmd.Flags().StringVar(&mgmtLetsencryptDomain, "letsencrypt-domain", "", "a domain to issue Let's Encrypt certificate for. Enables TLS using Let's Encrypt. Will fetch and renew certificate, and run the server with TLS") diff --git a/management/server/config.go b/management/server/config.go index fe86b925e..edc8c6eb6 100644 --- a/management/server/config.go +++ b/management/server/config.go @@ -49,7 +49,6 @@ type HttpServerConfig struct { CertFile string //CertKey is the location of the certificate private key CertKey string - Address string // AuthAudience identifies the recipients that the JWT is intended for (aud in JWT) AuthAudience string // AuthIssuer identifies principal that issued the JWT. diff --git a/management/server/grpcserver.go b/management/server/grpcserver.go index f39f738db..ed211f063 100644 --- a/management/server/grpcserver.go +++ b/management/server/grpcserver.go @@ -18,8 +18,8 @@ import ( "google.golang.org/grpc/status" ) -// Server an instance of a Management server -type Server struct { +// GRPCServer an instance of a Management gRPC API server +type GRPCServer struct { accountManager AccountManager wgKey wgtypes.Key proto.UnimplementedManagementServiceServer @@ -30,7 +30,7 @@ type Server struct { } // NewServer creates a new Management server -func NewServer(config *Config, accountManager AccountManager, peersUpdateManager *PeersUpdateManager, turnCredentialsManager TURNCredentialsManager) (*Server, error) { +func NewServer(config *Config, accountManager AccountManager, peersUpdateManager *PeersUpdateManager, turnCredentialsManager TURNCredentialsManager) (*GRPCServer, error) { key, err := wgtypes.GeneratePrivateKey() if err != nil { return nil, err @@ -50,7 +50,7 @@ func NewServer(config *Config, accountManager AccountManager, peersUpdateManager log.Debug("unable to use http config to create new jwt middleware") } - return &Server{ + return &GRPCServer{ wgKey: key, // peerKey -> event channel peersUpdateManager: peersUpdateManager, @@ -61,7 +61,7 @@ func NewServer(config *Config, accountManager AccountManager, peersUpdateManager }, nil } -func (s *Server) GetServerKey(ctx context.Context, req *proto.Empty) (*proto.ServerKeyResponse, error) { +func (s *GRPCServer) GetServerKey(ctx context.Context, req *proto.Empty) (*proto.ServerKeyResponse, error) { // todo introduce something more meaningful with the key expiration/rotation now := time.Now().Add(24 * time.Hour) secs := int64(now.Second()) @@ -76,7 +76,7 @@ func (s *Server) GetServerKey(ctx context.Context, req *proto.Empty) (*proto.Ser // Sync validates the existence of a connecting peer, sends an initial state (all available for the connecting peers) and // notifies the connected peer of any updates (e.g. new peers under the same account) -func (s *Server) Sync(req *proto.EncryptedMessage, srv proto.ManagementService_SyncServer) error { +func (s *GRPCServer) Sync(req *proto.EncryptedMessage, srv proto.ManagementService_SyncServer) error { log.Debugf("Sync request from peer %s", req.WgPubKey) peerKey, err := wgtypes.ParseKey(req.GetWgPubKey()) @@ -150,7 +150,7 @@ func (s *Server) Sync(req *proto.EncryptedMessage, srv proto.ManagementService_S } } -func (s *Server) registerPeer(peerKey wgtypes.Key, req *proto.LoginRequest) (*Peer, error) { +func (s *GRPCServer) registerPeer(peerKey wgtypes.Key, req *proto.LoginRequest) (*Peer, error) { var ( reqSetupKey string userId string @@ -245,7 +245,7 @@ func (s *Server) registerPeer(peerKey wgtypes.Key, req *proto.LoginRequest) (*Pe // In case it is, the login is successful // In case it isn't, the endpoint checks whether setup key is provided within the request and tries to register a peer. // In case of the successful registration login is also successful -func (s *Server) Login(ctx context.Context, req *proto.EncryptedMessage) (*proto.EncryptedMessage, error) { +func (s *GRPCServer) Login(ctx context.Context, req *proto.EncryptedMessage) (*proto.EncryptedMessage, error) { log.Debugf("Login request from peer %s", req.WgPubKey) peerKey, err := wgtypes.ParseKey(req.GetWgPubKey()) @@ -429,12 +429,12 @@ func toSyncResponse(config *Config, peer *Peer, peers []*Peer, turnCredentials * } // IsHealthy indicates whether the service is healthy -func (s *Server) IsHealthy(ctx context.Context, req *proto.Empty) (*proto.Empty, error) { +func (s *GRPCServer) IsHealthy(ctx context.Context, req *proto.Empty) (*proto.Empty, error) { return &proto.Empty{}, nil } // sendInitialSync sends initial proto.SyncResponse to the peer requesting synchronization -func (s *Server) sendInitialSync(peerKey wgtypes.Key, peer *Peer, srv proto.ManagementService_SyncServer) error { +func (s *GRPCServer) sendInitialSync(peerKey wgtypes.Key, peer *Peer, srv proto.ManagementService_SyncServer) error { networkMap, err := s.accountManager.GetNetworkMap(peer.Key) if err != nil { log.Warnf("error getting a list of peers for a peer %s", peer.Key) @@ -472,7 +472,7 @@ func (s *Server) sendInitialSync(peerKey wgtypes.Key, peer *Peer, srv proto.Mana // GetDeviceAuthorizationFlow returns a device authorization flow information // This is used for initiating an Oauth 2 device authorization grant flow // which will be used by our clients to Login -func (s *Server) GetDeviceAuthorizationFlow(ctx context.Context, req *proto.EncryptedMessage) (*proto.EncryptedMessage, error) { +func (s *GRPCServer) GetDeviceAuthorizationFlow(ctx context.Context, req *proto.EncryptedMessage) (*proto.EncryptedMessage, error) { peerKey, err := wgtypes.ParseKey(req.GetWgPubKey()) if err != nil { errMSG := fmt.Sprintf("error while parsing peer's Wireguard public key %s on GetDeviceAuthorizationFlow request.", req.WgPubKey) diff --git a/management/server/http/handler/groups.go b/management/server/http/groups.go similarity index 99% rename from management/server/http/handler/groups.go rename to management/server/http/groups.go index 66fa8e3e2..185e0ec0c 100644 --- a/management/server/http/handler/groups.go +++ b/management/server/http/groups.go @@ -1,4 +1,4 @@ -package handler +package http import ( "encoding/json" diff --git a/management/server/http/handler/groups_test.go b/management/server/http/groups_test.go similarity index 99% rename from management/server/http/handler/groups_test.go rename to management/server/http/groups_test.go index ba68d4d2b..115da9b66 100644 --- a/management/server/http/handler/groups_test.go +++ b/management/server/http/groups_test.go @@ -1,4 +1,4 @@ -package handler +package http import ( "bytes" diff --git a/management/server/http/handler.go b/management/server/http/handler.go new file mode 100644 index 000000000..79ad6b4b3 --- /dev/null +++ b/management/server/http/handler.go @@ -0,0 +1,64 @@ +package http + +import ( + "github.com/gorilla/mux" + s "github.com/netbirdio/netbird/management/server" + "github.com/netbirdio/netbird/management/server/http/middleware" + "github.com/rs/cors" + "net/http" +) + +// APIHandler creates the Management service HTTP API handler registering all the available endpoints. +func APIHandler(accountManager s.AccountManager, authIssuer string, authAudience string, authKeysLocation string) (http.Handler, error) { + jwtMiddleware, err := middleware.NewJwtMiddleware( + authIssuer, + authAudience, + authKeysLocation, + ) + if err != nil { + return nil, err + } + + corsMiddleware := cors.AllowAll() + + acMiddleware := middleware.NewAccessControll( + authAudience, + accountManager.IsUserAdmin) + + apiHandler := mux.NewRouter() + apiHandler.Use(jwtMiddleware.Handler, corsMiddleware.Handler, acMiddleware.Handler) + + groupsHandler := NewGroups(accountManager, authAudience) + rulesHandler := NewRules(accountManager, authAudience) + peersHandler := NewPeers(accountManager, authAudience) + keysHandler := NewSetupKeysHandler(accountManager, authAudience) + userHandler := NewUserHandler(accountManager, authAudience) + + apiHandler.HandleFunc("/api/peers", peersHandler.GetPeers).Methods("GET", "OPTIONS") + apiHandler.HandleFunc("/api/peers/{id}", peersHandler.HandlePeer). + Methods("GET", "PUT", "DELETE", "OPTIONS") + apiHandler.HandleFunc("/api/users", userHandler.GetUsers).Methods("GET", "OPTIONS") + apiHandler.HandleFunc("/api/setup-keys", keysHandler.GetKeys).Methods("GET", "POST", "OPTIONS") + apiHandler.HandleFunc("/api/setup-keys/{id}", keysHandler.HandleKey).Methods("GET", "PUT", "OPTIONS") + + apiHandler.HandleFunc("/api/setup-keys", keysHandler.GetKeys).Methods("POST", "OPTIONS") + apiHandler.HandleFunc("/api/setup-keys/{id}", keysHandler.HandleKey). + Methods("GET", "PUT", "DELETE", "OPTIONS") + + apiHandler.HandleFunc("/api/rules", rulesHandler.GetAllRulesHandler).Methods("GET", "OPTIONS") + apiHandler.HandleFunc("/api/rules", rulesHandler.CreateRuleHandler).Methods("POST", "OPTIONS") + apiHandler.HandleFunc("/api/rules/{id}", rulesHandler.UpdateRuleHandler).Methods("PUT", "OPTIONS") + apiHandler.HandleFunc("/api/rules/{id}", rulesHandler.PatchRuleHandler).Methods("PATCH", "OPTIONS") + apiHandler.HandleFunc("/api/rules/{id}", rulesHandler.GetRuleHandler).Methods("GET", "OPTIONS") + apiHandler.HandleFunc("/api/rules/{id}", rulesHandler.DeleteRuleHandler).Methods("DELETE", "OPTIONS") + + apiHandler.HandleFunc("/api/groups", groupsHandler.GetAllGroupsHandler).Methods("GET", "OPTIONS") + apiHandler.HandleFunc("/api/groups", groupsHandler.CreateGroupHandler).Methods("POST", "OPTIONS") + apiHandler.HandleFunc("/api/groups/{id}", groupsHandler.UpdateGroupHandler).Methods("PUT", "OPTIONS") + apiHandler.HandleFunc("/api/groups/{id}", groupsHandler.PatchGroupHandler).Methods("PATCH", "OPTIONS") + apiHandler.HandleFunc("/api/groups/{id}", groupsHandler.GetGroupHandler).Methods("GET", "OPTIONS") + apiHandler.HandleFunc("/api/groups/{id}", groupsHandler.DeleteGroupHandler).Methods("DELETE", "OPTIONS") + + return apiHandler, nil + +} diff --git a/management/server/http/handler/peers.go b/management/server/http/peers.go similarity index 99% rename from management/server/http/handler/peers.go rename to management/server/http/peers.go index dd7592d2f..d78921a6e 100644 --- a/management/server/http/handler/peers.go +++ b/management/server/http/peers.go @@ -1,4 +1,4 @@ -package handler +package http import ( "encoding/json" diff --git a/management/server/http/handler/peers_test.go b/management/server/http/peers_test.go similarity index 99% rename from management/server/http/handler/peers_test.go rename to management/server/http/peers_test.go index 132ee1d6d..7f29dde99 100644 --- a/management/server/http/handler/peers_test.go +++ b/management/server/http/peers_test.go @@ -1,4 +1,4 @@ -package handler +package http import ( "encoding/json" diff --git a/management/server/http/handler/rules.go b/management/server/http/rules.go similarity index 99% rename from management/server/http/handler/rules.go rename to management/server/http/rules.go index 16220f30b..b1f4db2b8 100644 --- a/management/server/http/handler/rules.go +++ b/management/server/http/rules.go @@ -1,4 +1,4 @@ -package handler +package http import ( "encoding/json" diff --git a/management/server/http/handler/rules_test.go b/management/server/http/rules_test.go similarity index 99% rename from management/server/http/handler/rules_test.go rename to management/server/http/rules_test.go index 3d307c3fe..55033812a 100644 --- a/management/server/http/handler/rules_test.go +++ b/management/server/http/rules_test.go @@ -1,4 +1,4 @@ -package handler +package http import ( "bytes" diff --git a/management/server/http/server.go b/management/server/http/server.go deleted file mode 100644 index 639e1d012..000000000 --- a/management/server/http/server.go +++ /dev/null @@ -1,167 +0,0 @@ -package http - -import ( - "context" - "crypto/tls" - "net/http" - "time" - - "github.com/gorilla/mux" - s "github.com/netbirdio/netbird/management/server" - "github.com/netbirdio/netbird/management/server/http/handler" - "github.com/netbirdio/netbird/management/server/http/middleware" - "github.com/rs/cors" - log "github.com/sirupsen/logrus" - "golang.org/x/crypto/acme/autocert" -) - -type Server struct { - server *http.Server - config *s.HttpServerConfig - certManager *autocert.Manager - tlsConfig *tls.Config - accountManager s.AccountManager -} - -// NewHttpsServer creates a new HTTPs server (with HTTPS support) and a certManager that is responsible for generating and renewing Let's Encrypt certificate -// The listening address will be :443 no matter what was specified in s.HttpServerConfig.Address -func NewHttpsServer( - config *s.HttpServerConfig, - certManager *autocert.Manager, - accountManager s.AccountManager, -) *Server { - server := &http.Server{ - Addr: config.Address, - WriteTimeout: time.Second * 15, - ReadTimeout: time.Second * 15, - IdleTimeout: time.Second * 60, - } - return &Server{ - server: server, - config: config, - certManager: certManager, - accountManager: accountManager, - } -} - -// NewHttpsServerWithTLSConfig creates a new HTTPs server with a provided tls.Config. -// Usually used when you already have a certificate -func NewHttpsServerWithTLSConfig( - config *s.HttpServerConfig, - tlsConfig *tls.Config, - accountManager s.AccountManager, -) *Server { - server := &http.Server{ - Addr: config.Address, - WriteTimeout: time.Second * 15, - ReadTimeout: time.Second * 15, - IdleTimeout: time.Second * 60, - } - return &Server{ - server: server, - config: config, - tlsConfig: tlsConfig, - accountManager: accountManager, - } -} - -// NewHttpServer creates a new HTTP server (without HTTPS) -func NewHttpServer(config *s.HttpServerConfig, accountManager s.AccountManager) *Server { - return NewHttpsServer(config, nil, accountManager) -} - -// Stop stops the http server -func (s *Server) Stop(ctx context.Context) error { - err := s.server.Shutdown(ctx) - if err != nil { - return err - } - return nil -} - -// Start defines http handlers and starts the http server. Blocks until server is shutdown. -func (s *Server) Start() error { - jwtMiddleware, err := middleware.NewJwtMiddleware( - s.config.AuthIssuer, - s.config.AuthAudience, - s.config.AuthKeysLocation, - ) - if err != nil { - return err - } - - corsMiddleware := cors.AllowAll() - - acMiddleware := middleware.NewAccessControll( - s.config.AuthAudience, - s.accountManager.IsUserAdmin) - - r := mux.NewRouter() - r.Use(jwtMiddleware.Handler, corsMiddleware.Handler, acMiddleware.Handler) - - groupsHandler := handler.NewGroups(s.accountManager, s.config.AuthAudience) - rulesHandler := handler.NewRules(s.accountManager, s.config.AuthAudience) - peersHandler := handler.NewPeers(s.accountManager, s.config.AuthAudience) - keysHandler := handler.NewSetupKeysHandler(s.accountManager, s.config.AuthAudience) - userHandler := handler.NewUserHandler(s.accountManager, s.config.AuthAudience) - - r.HandleFunc("/api/peers", peersHandler.GetPeers).Methods("GET", "OPTIONS") - r.HandleFunc("/api/peers/{id}", peersHandler.HandlePeer). - Methods("GET", "PUT", "DELETE", "OPTIONS") - r.HandleFunc("/api/users", userHandler.GetUsers).Methods("GET", "OPTIONS") - r.HandleFunc("/api/setup-keys", keysHandler.GetKeys).Methods("GET", "POST", "OPTIONS") - r.HandleFunc("/api/setup-keys/{id}", keysHandler.HandleKey).Methods("GET", "PUT", "OPTIONS") - - r.HandleFunc("/api/setup-keys", keysHandler.GetKeys).Methods("POST", "OPTIONS") - r.HandleFunc("/api/setup-keys/{id}", keysHandler.HandleKey). - Methods("GET", "PUT", "DELETE", "OPTIONS") - - r.HandleFunc("/api/rules", rulesHandler.GetAllRulesHandler).Methods("GET", "OPTIONS") - r.HandleFunc("/api/rules", rulesHandler.CreateRuleHandler).Methods("POST", "OPTIONS") - r.HandleFunc("/api/rules/{id}", rulesHandler.UpdateRuleHandler).Methods("PUT", "OPTIONS") - r.HandleFunc("/api/rules/{id}", rulesHandler.PatchRuleHandler).Methods("PATCH", "OPTIONS") - r.HandleFunc("/api/rules/{id}", rulesHandler.GetRuleHandler).Methods("GET", "OPTIONS") - r.HandleFunc("/api/rules/{id}", rulesHandler.DeleteRuleHandler).Methods("DELETE", "OPTIONS") - - r.HandleFunc("/api/groups", groupsHandler.GetAllGroupsHandler).Methods("GET", "OPTIONS") - r.HandleFunc("/api/groups", groupsHandler.CreateGroupHandler).Methods("POST", "OPTIONS") - r.HandleFunc("/api/groups/{id}", groupsHandler.UpdateGroupHandler).Methods("PUT", "OPTIONS") - r.HandleFunc("/api/groups/{id}", groupsHandler.PatchGroupHandler).Methods("PATCH", "OPTIONS") - r.HandleFunc("/api/groups/{id}", groupsHandler.GetGroupHandler).Methods("GET", "OPTIONS") - r.HandleFunc("/api/groups/{id}", groupsHandler.DeleteGroupHandler).Methods("DELETE", "OPTIONS") - http.Handle("/", r) - - if s.certManager != nil { - // if HTTPS is enabled we reuse the listener from the cert manager - listener := s.certManager.Listener() - log.Infof( - "HTTPs server listening on %s with Let's Encrypt autocert configured", - listener.Addr(), - ) - if err = http.Serve(listener, s.certManager.HTTPHandler(r)); err != nil { - log.Errorf("failed to serve https server: %v", err) - return err - } - } else if s.tlsConfig != nil { - listener, err := tls.Listen("tcp", s.config.Address, s.tlsConfig) - if err != nil { - log.Errorf("failed to serve https server: %v", err) - return err - } - log.Infof("HTTPs server listening on %s", listener.Addr()) - - if err = http.Serve(listener, r); err != nil { - log.Errorf("failed to serve https server: %v", err) - return err - } - - } else { - log.Infof("HTTP server listening on %s", s.server.Addr) - if err = s.server.ListenAndServe(); err != nil { - log.Errorf("failed to serve http server: %v", err) - return err - } - } - - return nil -} diff --git a/management/server/http/handler/setupkeys.go b/management/server/http/setupkeys.go similarity index 99% rename from management/server/http/handler/setupkeys.go rename to management/server/http/setupkeys.go index b64c9b03e..eb154b226 100644 --- a/management/server/http/handler/setupkeys.go +++ b/management/server/http/setupkeys.go @@ -1,4 +1,4 @@ -package handler +package http import ( "encoding/json" diff --git a/management/server/http/handler/users.go b/management/server/http/users.go similarity index 98% rename from management/server/http/handler/users.go rename to management/server/http/users.go index 113a0dfba..d5f5fef03 100644 --- a/management/server/http/handler/users.go +++ b/management/server/http/users.go @@ -1,4 +1,4 @@ -package handler +package http import ( "github.com/netbirdio/netbird/management/server/http/api" diff --git a/management/server/http/handler/users_test.go b/management/server/http/users_test.go similarity index 99% rename from management/server/http/handler/users_test.go rename to management/server/http/users_test.go index 7a42c0707..6c4412848 100644 --- a/management/server/http/handler/users_test.go +++ b/management/server/http/users_test.go @@ -1,4 +1,4 @@ -package handler +package http import ( "encoding/json" diff --git a/management/server/http/handler/util.go b/management/server/http/util.go similarity index 99% rename from management/server/http/handler/util.go rename to management/server/http/util.go index 5ae2a0d6f..9d14b2b52 100644 --- a/management/server/http/handler/util.go +++ b/management/server/http/util.go @@ -1,4 +1,4 @@ -package handler +package http import ( "encoding/json" diff --git a/management/server/management_proto_test.go b/management/server/management_proto_test.go index 877e7c50a..115e18627 100644 --- a/management/server/management_proto_test.go +++ b/management/server/management_proto_test.go @@ -359,7 +359,7 @@ func TestServer_GetDeviceAuthorizationFlow(t *testing.T) { for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { - mgmtServer := &Server{ + mgmtServer := &GRPCServer{ wgKey: testingServerKey, config: &Config{ DeviceAuthorizationFlow: testCase.inputFlow,