2021-07-17 14:38:59 +02:00
package cmd
import (
2021-08-07 12:26:07 +02:00
"context"
2021-09-25 19:22:49 +02:00
"crypto/tls"
2022-05-22 18:53:47 +02:00
"errors"
2021-07-17 14:38:59 +02:00
"flag"
"fmt"
2022-05-13 14:11:21 +02:00
"io"
2022-05-22 18:53:47 +02:00
"io/fs"
2022-05-13 14:11:21 +02:00
"io/ioutil"
"net"
"os"
"path"
"time"
2022-03-26 12:08:54 +01:00
"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"
2021-07-24 16:14:29 +02:00
2022-03-26 12:08:54 +01:00
"github.com/netbirdio/netbird/encryption"
mgmtProto "github.com/netbirdio/netbird/management/proto"
2021-07-17 14:38:59 +02:00
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"google.golang.org/grpc"
2021-07-17 14:51:16 +02:00
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/keepalive"
2021-07-17 14:38:59 +02:00
)
var (
2021-07-17 14:51:16 +02:00
mgmtPort int
2022-05-13 14:11:21 +02:00
defaultMgmtDataDir string
defaultMgmtConfig string
2021-07-17 14:51:16 +02:00
mgmtDataDir string
2021-07-30 17:46:38 +02:00
mgmtConfig string
2021-07-17 14:51:16 +02:00
mgmtLetsencryptDomain string
2021-09-25 19:22:49 +02:00
certFile string
certKey string
2021-07-17 14:51:16 +02:00
kaep = keepalive . EnforcementPolicy {
2021-07-22 10:28:00 +02:00
MinTime : 15 * time . Second ,
2021-07-17 14:51:16 +02:00
PermitWithoutStream : true ,
}
kasp = keepalive . ServerParameters {
MaxConnectionIdle : 15 * time . Second ,
MaxConnectionAgeGrace : 5 * time . Second ,
Time : 5 * time . Second ,
Timeout : 2 * time . Second ,
}
2021-07-17 14:38:59 +02:00
mgmtCmd = & cobra . Command {
Use : "management" ,
2022-05-13 14:11:21 +02:00
Short : "start Netbird Management Server" ,
2021-07-17 14:38:59 +02:00
Run : func ( cmd * cobra . Command , args [ ] string ) {
flag . Parse ( )
2021-09-07 09:53:18 +02:00
err := util . InitLog ( logLevel , logFile )
if err != nil {
log . Fatalf ( "failed initializing log %v" , err )
}
2021-07-17 14:38:59 +02:00
2022-05-13 14:11:21 +02:00
if mgmtDataDir == "" {
oldPath := "/var/lib/wiretrustee"
2022-05-13 21:51:41 +02:00
if migrateToNetbird ( oldPath , defaultMgmtDataDir ) {
if err := cpDir ( oldPath , defaultMgmtDataDir ) ; err != nil {
2022-05-13 14:11:21 +02:00
log . Fatal ( err )
}
}
}
actualMgmtConfigPath := mgmtConfig
if mgmtConfig == "" {
oldPath := "/etc/wiretrustee/management.json"
if migrateToNetbird ( oldPath , defaultMgmtConfig ) {
2022-05-13 21:51:41 +02:00
if err := cpDir ( "/etc/wiretrustee/" , defaultConfigPath ) ; err != nil {
2022-05-13 14:11:21 +02:00
log . Fatal ( err )
}
if err := cpFile ( oldPath , defaultMgmtConfig ) ; err != nil {
log . Fatal ( err )
}
}
actualMgmtConfigPath = defaultMgmtConfig
}
config , err := loadMgmtConfig ( actualMgmtConfigPath )
2021-07-30 17:46:38 +02:00
if err != nil {
2022-05-13 14:11:21 +02:00
log . Fatalf ( "failed reading provided config file: %s: %v" , actualMgmtConfigPath , err )
2021-07-30 17:46:38 +02:00
}
if _ , err = os . Stat ( config . Datadir ) ; os . IsNotExist ( err ) {
err = os . MkdirAll ( config . Datadir , os . ModeDir )
2021-07-17 17:26:51 +02:00
if err != nil {
2021-07-30 17:46:38 +02:00
log . Fatalf ( "failed creating datadir: %s: %v" , config . Datadir , err )
2021-07-17 17:26:51 +02:00
}
2021-07-17 14:38:59 +02:00
}
2021-08-12 12:49:10 +02:00
store , err := server . NewStore ( config . Datadir )
if err != nil {
log . Fatalf ( "failed creating a store: %s: %v" , config . Datadir , err )
}
2021-09-07 18:36:46 +02:00
peersUpdateManager := server . NewPeersUpdateManager ( )
2022-02-17 19:18:46 +01:00
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 )
}
2022-01-24 11:21:30 +01:00
}
2022-02-17 19:18:46 +01:00
2022-05-21 15:21:39 +02:00
accountManager , err := server . BuildManager ( store , peersUpdateManager , idpManager )
if err != nil {
log . Fatalln ( "failed build default manager: " , err )
}
2021-08-12 12:49:10 +02:00
2021-07-17 14:51:16 +02:00
var opts [ ] grpc . ServerOption
2021-08-07 13:51:17 +02:00
var httpServer * http . Server
2021-08-07 13:35:52 +02:00
if config . HttpConfig . LetsEncryptDomain != "" {
2022-05-21 15:21:39 +02:00
// automatically generate a new certificate with Let's Encrypt
2021-08-07 13:35:52 +02:00
certManager := encryption . CreateCertManager ( config . Datadir , config . HttpConfig . LetsEncryptDomain )
2021-08-07 12:26:07 +02:00
transportCredentials := credentials . NewTLS ( certManager . TLSConfig ( ) )
2021-07-22 15:23:24 +02:00
opts = append ( opts , grpc . Creds ( transportCredentials ) )
2021-08-07 12:26:07 +02:00
2021-08-12 12:49:10 +02:00
httpServer = http . NewHttpsServer ( config . HttpConfig , certManager , accountManager )
2021-09-25 19:22:49 +02:00
} else if config . HttpConfig . CertFile != "" && config . HttpConfig . CertKey != "" {
2022-05-21 15:21:39 +02:00
// use provided certificate
2021-09-25 19:22:49 +02:00
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 )
2021-08-07 12:26:07 +02:00
} else {
2022-05-21 15:21:39 +02:00
// start server without SSL
2021-08-12 12:49:10 +02:00
httpServer = http . NewHttpServer ( config . HttpConfig , accountManager )
2021-07-17 14:38:59 +02:00
}
2021-07-17 14:51:16 +02:00
opts = append ( opts , grpc . KeepaliveEnforcementPolicy ( kaep ) , grpc . KeepaliveParams ( kasp ) )
2021-07-17 14:38:59 +02:00
grpcServer := grpc . NewServer ( opts ... )
2021-09-02 14:41:54 +02:00
turnManager := server . NewTimeBasedAuthSecretsManager ( peersUpdateManager , config . TURNConfig )
server , err := server . NewServer ( config , accountManager , peersUpdateManager , turnManager )
2021-07-17 14:38:59 +02:00
if err != nil {
log . Fatalf ( "failed creating new server: %v" , err )
}
mgmtProto . RegisterManagementServiceServer ( grpcServer , server )
log . Printf ( "started server: localhost:%v" , mgmtPort )
2021-07-17 14:51:16 +02:00
lis , err := net . Listen ( "tcp" , fmt . Sprintf ( ":%d" , mgmtPort ) )
if err != nil {
log . Fatalf ( "failed to listen: %v" , err )
}
2021-07-17 14:38:59 +02:00
go func ( ) {
if err = grpcServer . Serve ( lis ) ; err != nil {
2021-07-17 14:51:16 +02:00
log . Fatalf ( "failed to serve gRpc server: %v" , err )
2021-07-17 14:38:59 +02:00
}
} ( )
2021-08-07 12:26:07 +02:00
go func ( ) {
err = httpServer . Start ( )
if err != nil {
log . Fatalf ( "failed to serve http server: %v" , err )
}
} ( )
2021-07-17 14:38:59 +02:00
SetupCloseHandler ( )
<- stopCh
log . Println ( "Receive signal to stop running Management server" )
2021-08-07 12:26:07 +02:00
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 ( )
2021-07-17 14:38:59 +02:00
} ,
}
)
2022-05-13 14:11:21 +02:00
func loadMgmtConfig ( mgmtConfigPath string ) ( * server . Config , error ) {
2021-07-30 17:46:38 +02:00
config := & server . Config { }
2022-05-13 14:11:21 +02:00
_ , err := util . ReadJson ( mgmtConfigPath , config )
2021-07-30 17:46:38 +02:00
if err != nil {
return nil , err
}
if mgmtLetsencryptDomain != "" {
2021-08-07 13:35:52 +02:00
config . HttpConfig . LetsEncryptDomain = mgmtLetsencryptDomain
2021-07-30 17:46:38 +02:00
}
if mgmtDataDir != "" {
config . Datadir = mgmtDataDir
}
2021-09-25 19:22:49 +02:00
if certKey != "" && certFile != "" {
config . HttpConfig . CertFile = certFile
config . HttpConfig . CertKey = certKey
}
2021-07-30 17:46:38 +02:00
return config , err
}
2021-09-25 19:22:49 +02:00
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
}
2022-05-13 14:11:21 +02:00
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 {
2022-05-22 18:53:47 +02:00
_ , errOld := os . Stat ( oldPath )
_ , errNew := os . Stat ( newPath )
2022-05-13 14:11:21 +02:00
2022-05-22 18:53:47 +02:00
if errors . Is ( errOld , fs . ErrNotExist ) || errNew == nil {
2022-05-13 14:11:21 +02:00
return false
}
return true
}
2021-07-17 14:38:59 +02:00
func init ( ) {
2021-07-17 14:51:16 +02:00
mgmtCmd . Flags ( ) . IntVar ( & mgmtPort , "port" , 33073 , "server port to listen on" )
2022-05-13 14:11:21 +02:00
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" )
2021-07-17 14:51:16 +02:00
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" )
2021-09-25 19:22:49 +02:00
mgmtCmd . Flags ( ) . StringVar ( & certFile , "cert-file" , "" , "Location of your SSL certificate. Can be used when you have an existing certificate and don't want a new certificate be generated automatically. If letsencrypt-domain is specified this property has no effect" )
mgmtCmd . Flags ( ) . StringVar ( & certKey , "cert-key" , "" , "Location of your SSL certificate private key. Can be used when you have an existing certificate and don't want a new certificate be generated automatically. If letsencrypt-domain is specified this property has no effect" )
2021-07-30 17:46:38 +02:00
rootCmd . MarkFlagRequired ( "config" ) //nolint
2021-07-17 14:38:59 +02:00
}