mirror of
https://github.com/netbirdio/netbird.git
synced 2024-11-08 01:04:47 +01:00
86a66c6202
Right now Signal Service runs the Let'sEncrypt manager on port 80 and a gRPC server on port 10000. There are two separate listeners. This PR combines these listeners into one with a cmux lib. The gRPC server runs on either 443 with TLS or 80 without TLS. Let's Encrypt manager always runs on port 80.
331 lines
9.0 KiB
Go
331 lines
9.0 KiB
Go
package cmd
|
|
|
|
import (
|
|
"context"
|
|
"crypto/tls"
|
|
"errors"
|
|
"flag"
|
|
"fmt"
|
|
"io"
|
|
"io/fs"
|
|
"io/ioutil"
|
|
"net"
|
|
"os"
|
|
"path"
|
|
"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"
|
|
|
|
"github.com/netbirdio/netbird/encryption"
|
|
mgmtProto "github.com/netbirdio/netbird/management/proto"
|
|
log "github.com/sirupsen/logrus"
|
|
"github.com/spf13/cobra"
|
|
"google.golang.org/grpc"
|
|
"google.golang.org/grpc/credentials"
|
|
"google.golang.org/grpc/keepalive"
|
|
)
|
|
|
|
var (
|
|
mgmtPort int
|
|
mgmtLetsencryptDomain string
|
|
certFile string
|
|
certKey string
|
|
|
|
kaep = keepalive.EnforcementPolicy{
|
|
MinTime: 15 * time.Second,
|
|
PermitWithoutStream: true,
|
|
}
|
|
|
|
kasp = keepalive.ServerParameters{
|
|
MaxConnectionIdle: 15 * time.Second,
|
|
MaxConnectionAgeGrace: 5 * time.Second,
|
|
Time: 5 * time.Second,
|
|
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) {
|
|
flag.Parse()
|
|
err := util.InitLog(logLevel, logFile)
|
|
if err != nil {
|
|
log.Fatalf("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)
|
|
}
|
|
|
|
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)
|
|
}
|
|
}
|
|
|
|
store, err := server.NewStore(config.Datadir)
|
|
if err != nil {
|
|
log.Fatalf("failed creating a store: %s: %v", config.Datadir, err)
|
|
}
|
|
peersUpdateManager := server.NewPeersUpdateManager()
|
|
|
|
var idpManager idp.Manager
|
|
if config.IdpManagerConfig != nil {
|
|
idpManager, err = idp.NewManager(*config.IdpManagerConfig)
|
|
if err != nil {
|
|
log.Fatalln("failed retrieving a new idp manager with err: ", err)
|
|
}
|
|
}
|
|
|
|
accountManager, err := server.BuildManager(store, peersUpdateManager, idpManager)
|
|
if err != nil {
|
|
log.Fatalln("failed build default manager: ", err)
|
|
}
|
|
|
|
var opts []grpc.ServerOption
|
|
|
|
var httpServer *http.Server
|
|
if config.HttpConfig.LetsEncryptDomain != "" {
|
|
// automatically generate a new certificate with Let's Encrypt
|
|
certManager, err := encryption.CreateCertManager(config.Datadir, config.HttpConfig.LetsEncryptDomain)
|
|
if err != nil {
|
|
log.Fatalf("failed creating Let's Encrypt cert manager: %v", err)
|
|
}
|
|
transportCredentials := credentials.NewTLS(certManager.TLSConfig())
|
|
opts = append(opts, grpc.Creds(transportCredentials))
|
|
|
|
httpServer = http.NewHttpsServer(config.HttpConfig, certManager, accountManager)
|
|
} else if config.HttpConfig.CertFile != "" && config.HttpConfig.CertKey != "" {
|
|
// use provided certificate
|
|
tlsConfig, err := loadTLSConfig(config.HttpConfig.CertFile, config.HttpConfig.CertKey)
|
|
if err != nil {
|
|
log.Fatal("cannot load TLS credentials: ", 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)
|
|
}
|
|
|
|
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)
|
|
if err != nil {
|
|
log.Fatalf("failed creating new server: %v", err)
|
|
}
|
|
mgmtProto.RegisterManagementServiceServer(grpcServer, server)
|
|
log.Printf("started server: localhost:%v", mgmtPort)
|
|
|
|
lis, err := net.Listen("tcp", fmt.Sprintf(":%d", mgmtPort))
|
|
if err != nil {
|
|
log.Fatalf("failed to listen: %v", err)
|
|
}
|
|
|
|
go func() {
|
|
if err = grpcServer.Serve(lis); err != nil {
|
|
log.Fatalf("failed to serve gRpc server: %v", err)
|
|
}
|
|
}()
|
|
|
|
go func() {
|
|
err = httpServer.Start()
|
|
if err != nil {
|
|
log.Fatalf("failed to serve http server: %v", err)
|
|
}
|
|
}()
|
|
|
|
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()
|
|
},
|
|
}
|
|
)
|
|
|
|
func loadMgmtConfig(mgmtConfigPath string) (*server.Config, error) {
|
|
config := &server.Config{}
|
|
_, err := util.ReadJson(mgmtConfigPath, config)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if mgmtLetsencryptDomain != "" {
|
|
config.HttpConfig.LetsEncryptDomain = mgmtLetsencryptDomain
|
|
}
|
|
if mgmtDataDir != "" {
|
|
config.Datadir = mgmtDataDir
|
|
}
|
|
|
|
if certKey != "" && certFile != "" {
|
|
config.HttpConfig.CertFile = certFile
|
|
config.HttpConfig.CertKey = certKey
|
|
}
|
|
|
|
return config, err
|
|
}
|
|
|
|
func loadTLSConfig(certFile string, certKey string) (*tls.Config, error) {
|
|
// Load server's certificate and private key
|
|
serverCert, err := tls.LoadX509KeyPair(certFile, certKey)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Create the credentials and return it
|
|
config := &tls.Config{
|
|
Certificates: []tls.Certificate{serverCert},
|
|
ClientAuth: tls.NoClientCert,
|
|
}
|
|
|
|
return config, nil
|
|
}
|
|
|
|
func handleRebrand(cmd *cobra.Command) error {
|
|
var err error
|
|
if logFile == defaultLogFile {
|
|
if migrateToNetbird(oldDefaultLogFile, defaultLogFile) {
|
|
cmd.Printf("will copy Log dir %s and its content to %s\n", oldDefaultLogDir, defaultLogDir)
|
|
err = cpDir(oldDefaultLogDir, defaultLogDir)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
if mgmtConfig == defaultMgmtConfig {
|
|
if migrateToNetbird(oldDefaultMgmtConfig, defaultMgmtConfig) {
|
|
cmd.Printf("will copy Config dir %s and its content to %s\n", oldDefaultMgmtConfigDir, defaultMgmtConfigDir)
|
|
err = cpDir(oldDefaultMgmtConfigDir, defaultMgmtConfigDir)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
if mgmtDataDir == defaultMgmtDataDir {
|
|
if migrateToNetbird(oldDefaultMgmtDataDir, defaultMgmtDataDir) {
|
|
cmd.Printf("will copy Config dir %s and its content to %s\n", oldDefaultMgmtDataDir, defaultMgmtDataDir)
|
|
err = cpDir(oldDefaultMgmtDataDir, defaultMgmtDataDir)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func cpFile(src, dst string) error {
|
|
var err error
|
|
var srcfd *os.File
|
|
var dstfd *os.File
|
|
var srcinfo os.FileInfo
|
|
|
|
if srcfd, err = os.Open(src); err != nil {
|
|
return err
|
|
}
|
|
defer srcfd.Close()
|
|
|
|
if dstfd, err = os.Create(dst); err != nil {
|
|
return err
|
|
}
|
|
defer dstfd.Close()
|
|
|
|
if _, err = io.Copy(dstfd, srcfd); err != nil {
|
|
return err
|
|
}
|
|
if srcinfo, err = os.Stat(src); err != nil {
|
|
return err
|
|
}
|
|
return os.Chmod(dst, srcinfo.Mode())
|
|
}
|
|
|
|
func copySymLink(source, dest string) error {
|
|
link, err := os.Readlink(source)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return os.Symlink(link, dest)
|
|
}
|
|
|
|
func cpDir(src string, dst string) error {
|
|
var err error
|
|
var fds []os.FileInfo
|
|
var srcinfo os.FileInfo
|
|
|
|
if srcinfo, err = os.Stat(src); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err = os.MkdirAll(dst, srcinfo.Mode()); err != nil {
|
|
return err
|
|
}
|
|
|
|
if fds, err = ioutil.ReadDir(src); err != nil {
|
|
return err
|
|
}
|
|
for _, fd := range fds {
|
|
srcfp := path.Join(src, fd.Name())
|
|
dstfp := path.Join(dst, fd.Name())
|
|
|
|
fileInfo, err := os.Stat(srcfp)
|
|
if err != nil {
|
|
log.Fatalf("Couldn't get fileInfo; %v", err)
|
|
}
|
|
|
|
switch fileInfo.Mode() & os.ModeType {
|
|
case os.ModeSymlink:
|
|
if err = copySymLink(srcfp, dstfp); err != nil {
|
|
log.Fatalf("Failed to copy from %s to %s; %v", srcfp, dstfp, err)
|
|
}
|
|
case os.ModeDir:
|
|
if err = cpDir(srcfp, dstfp); err != nil {
|
|
log.Fatalf("Failed to copy from %s to %s; %v", srcfp, dstfp, err)
|
|
}
|
|
default:
|
|
if err = cpFile(srcfp, dstfp); err != nil {
|
|
log.Fatalf("Failed to copy from %s to %s; %v", srcfp, dstfp, err)
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func migrateToNetbird(oldPath, newPath string) bool {
|
|
_, errOld := os.Stat(oldPath)
|
|
_, errNew := os.Stat(newPath)
|
|
|
|
if errors.Is(errOld, fs.ErrNotExist) || errNew == nil {
|
|
return false
|
|
}
|
|
|
|
return true
|
|
}
|