Newer
Older
"github.com/sethvargo/go-password/password"
"google.golang.org/grpc/resolver"
Andre Sterba
committed
apppb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/app"
cmpb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/configurationmanagement"
cpb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/csbi"
mnepb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/networkelement"
pipb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/plugin-internal"
rpb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/plugin-registry"
ppb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/pnd"
apb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/rbac"
subpb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/subscriptionmanagement"
tpb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/topology"
Andre Sterba
committed
"code.fbi.h-da.de/danet/gosdn/controller/app"
"code.fbi.h-da.de/danet/gosdn/controller/nucleus/database"
Andre Sterba
committed
"code.fbi.h-da.de/danet/gosdn/controller/config"
"code.fbi.h-da.de/danet/gosdn/controller/conflict"
Fabian Seidl
committed
eventservice "code.fbi.h-da.de/danet/gosdn/controller/eventService"
"code.fbi.h-da.de/danet/gosdn/controller/interfaces/networkdomain"
"code.fbi.h-da.de/danet/gosdn/controller/interfaces/networkelement"
"code.fbi.h-da.de/danet/gosdn/controller/interfaces/plugin"
"code.fbi.h-da.de/danet/gosdn/controller/interfaces/rbac"
"code.fbi.h-da.de/danet/gosdn/controller/northbound/server"
nbi "code.fbi.h-da.de/danet/gosdn/controller/northbound/server"
rbacImpl "code.fbi.h-da.de/danet/gosdn/controller/rbac"
"code.fbi.h-da.de/danet/gosdn/controller/store"
"code.fbi.h-da.de/danet/gosdn/controller/topology"
"code.fbi.h-da.de/danet/gosdn/controller/topology/nodes"
"code.fbi.h-da.de/danet/gosdn/controller/topology/ports"
Andre Sterba
committed
routingtables "code.fbi.h-da.de/danet/gosdn/controller/topology/routing-tables"
Fabian Seidl
committed
eventInterfaces "code.fbi.h-da.de/danet/gosdn/controller/interfaces/event"
"code.fbi.h-da.de/danet/gosdn/controller/nucleus"
var coreLock sync.RWMutex
var coreOnce sync.Once
// Core is the representation of the controller's core.
pndStore networkdomain.PndStore
pndService networkdomain.Service
mneService networkelement.Service
changeStore store.ChangeStore
userService rbac.UserService
roleService rbac.RoleService
topologyService topology.Service
nodeService nodes.Service
portService ports.Service
routeService routingtables.Service
pluginService plugin.Service
httpServer *http.Server
grpcServer *grpc.Server
nbi *nbi.NorthboundInterface
eventService eventInterfaces.Service
appService app.ManagementService
networkElementWatcher *nucleus.NetworkElementWatcher
stopChan chan os.Signal
csbiClient cpb.CsbiServiceClient
pluginRegistryClient rpb.PluginRegistryServiceClient
// initialize does start-up housekeeping like reading controller config files.
func initialize() error {
if err := config.InitializeConfig(); err != nil {
return err
}
if err := config.ReadGnmiSubscriptionPaths(); err != nil {
log.Error("Error reading in gNMI subscription paths, can not watch network elements automatically: ", err)
}
Fabian Seidl
committed
eventService, err := eventservice.NewEventService()
if err != nil {
return err
}
db, err := database.GetDatabaseConnection()
if err != nil {
return err
}
nodeService := nodes.NewNodeService(nodes.NewNodeStore(db), eventService)
portService := ports.NewPortService(ports.NewPortStore(db), eventService)
Andre Sterba
committed
routeService := routingtables.NewRoutingTableService(
Andre Sterba
committed
nodeService,
portService,
eventService,
)
pluginRegistryClient := setupPluginRegistryClient()
pluginService := nucleus.NewPluginService(
nucleus.NewPluginStore(db),
eventService,
nucleus.NewPluginThroughReattachConfig,
pluginRegistryClient,
)
pndStore := nucleus.NewPndStore(db, pluginService)
changeStore := store.NewChangeStore()
pndStore: pndStore,
pndService: nucleus.NewPndService(pndStore),
mneService: nucleus.NewNetworkElementService(
nucleus.NewNetworkElementStore(db, config.BasePndUUID),
pluginService,
eventService,
),
changeStore: *changeStore,
userService: rbacImpl.NewUserService(rbacImpl.NewUserStore(db), eventService),
roleService: rbacImpl.NewRoleService(rbacImpl.NewRoleStore(db), eventService),
topologyService: topology.NewTopologyService(
nodeService,
portService,
eventService,
),
nodeService: nodeService,
portService: portService,
routeService: routeService,
eventService: eventService,
pluginService: pluginService,
appService: app.NewAppService(app.NewAppStore(db)),
stopChan: make(chan os.Signal, 1),
pluginRegistryClient: pluginRegistryClient,
signal.Notify(c.stopChan, os.Interrupt, syscall.SIGTERM)
setupOrchestratorClient()
if err := createPrincipalNetworkDomain(); err != nil {
return err
}
c.networkElementWatcher = nucleus.NewNetworkElementWatcher(c.mneService, c.eventService)
Fabian Seidl
committed
c.networkElementWatcher.SubscribeToNetworkElements(nil)
if err := ensureDefaultRolesExist(); err != nil {
return err
}
if err := ensureDefaultUserExists(); err != nil {
return err
}
if err := deleteAllExpiredUserTokens(); err != nil {
return err
}
if err := startGrpc(); err != nil {
return err
}
coreLock.Lock()
defer coreLock.Unlock()
err = startHttpServer()
if err != nil {
return err
}
func setupOrchestratorClient() {
orchestrator := viper.GetString("csbi-orchestrator")
conn, err := grpc.NewClient(orchestrator, grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
log.Fatal(err)
}
c.csbiClient = cpb.NewCsbiServiceClient(conn)
func setupPluginRegistryClient() rpb.PluginRegistryServiceClient {
pluginRegistry := viper.GetString("plugin-registry")
conn, err := grpc.NewClient(pluginRegistry, grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
log.Fatal(err)
}
return rpb.NewPluginRegistryServiceClient(conn)
}
noPassthrough := viper.GetBool("noGRPCPassthrough")
if noPassthrough {
log.Info("gRPC default resolver scheme is not set to passthrough. This might cause issues with the gRPC connection when no real DNS server is available as each gRPC requests requires a DNS request.")
} else {
log.Info("Setting gRPC default resolver scheme to passthrough. No DNS queries are being made when doing a gRPC request.")
resolver.SetDefaultScheme("passthrough")
}
socket := viper.GetString("socket")
lislisten, err := net.Listen("tcp", socket)
log.Infof("listening to %v", lislisten.Addr())
jwtManager := rbacImpl.NewJWTManager(config.JWTSecret, config.JWTDuration)
setupGRPCServerWithCorrectSecurityLevel(jwtManager, c.userService, c.roleService)
c.nbi = nbi.NewNBI(
c.pndStore,
c.pndService,
c.mneService,
c.changeStore,
c.userService,
c.roleService,
*jwtManager,
c.topologyService,
c.nodeService,
c.portService,
Andre Sterba
committed
c.routeService,
c.appService,
c.pluginService,
c.pluginRegistryClient,
c.csbiClient,
callback,
c.networkElementWatcher,
ppb.RegisterPndServiceServer(c.grpcServer, c.nbi.Pnd)
cpb.RegisterCsbiServiceServer(c.grpcServer, c.nbi.Csbi)
apb.RegisterAuthServiceServer(c.grpcServer, c.nbi.Auth)
apb.RegisterUserServiceServer(c.grpcServer, c.nbi.User)
apb.RegisterRoleServiceServer(c.grpcServer, c.nbi.Role)
Andre Sterba
committed
apppb.RegisterAppServiceServer(c.grpcServer, c.nbi.App)
tpb.RegisterTopologyServiceServer(c.grpcServer, c.nbi.Topology)
mnepb.RegisterNetworkElementServiceServer(c.grpcServer, c.nbi.NetworkElement)
Andre Sterba
committed
tpb.RegisterRoutingTableServiceServer(c.grpcServer, c.nbi.Routes)
pipb.RegisterPluginInternalServiceServer(c.grpcServer, c.nbi.Plugin)
cmpb.RegisterConfigurationManagementServiceServer(c.grpcServer, c.nbi.ConfigurationManagement)
subpb.RegisterSubscriptionManagementServiceServer(c.grpcServer, c.nbi.SubManagement)
if err := c.grpcServer.Serve(lislisten); err != nil {
// createPrincipalNetworkDomain initializes the controller with an initial PND.
func createPrincipalNetworkDomain() error {
basePnd, err := c.pndService.Get(store.Query{ID: config.BasePndUUID})
log.Info(err)
if basePnd == nil {
pnd := nucleus.NewPND(
config.BasePndUUID,
"base",
"gosdn base pnd",
err = c.pndStore.Add(context.Background(), pnd)
if err != nil {
return err
}
return nil
func ensureDefaultRolesExist() error {
err := ensureAdminRoleExists()
if err != nil {
return err
}
err = ensureGenericAppRoleExists()
if err != nil {
return err
}
return nil
}
func ensureAdminRoleExists() error {
defaultAdminRoleName := "admin"
adminRole, err := c.roleService.Get(store.Query{ID: uuid.Nil, Name: defaultAdminRoleName})
if err != nil {
log.Info(err)
}
if adminRole == nil {
err := c.roleService.Add(rbacImpl.NewRole(uuid.New(), defaultAdminRoleName, "admin role", []string{
"/gosdn.pnd.PndService/GetPnd",
"/gosdn.pnd.PndService/GetPndList",
"/gosdn.pnd.PndService/CreatePndList",
"/gosdn.pnd.PndService/DeletePnd",
"/gosdn.rbac.UserService/CreateUsers",
"/gosdn.rbac.UserService/GetUser",
"/gosdn.rbac.UserService/GetUsers",
"/gosdn.rbac.UserService/UpdateUsers",
"/gosdn.rbac.UserService/DeleteUsers",
"/gosdn.rbac.RoleService/CreateRoles",
"/gosdn.rbac.RoleService/GetRole",
"/gosdn.rbac.RoleService/GetRoles",
"/gosdn.rbac.RoleService/UpdateRoles",
"/gosdn.rbac.RoleService/DeletePermissionsForRole",
"/gosdn.rbac.RoleService/DeleteRoles",
Fabian Seidl
committed
"/gosdn.networkelement.NetworkElementService/Get",
"/gosdn.networkelement.NetworkElementService/GetFlattened",
"/gosdn.networkelement.NetworkElementService/GetAll",
"/gosdn.networkelement.NetworkElementService/GetAllFlattened",
"/gosdn.networkelement.NetworkElementService/GetPath",
Fabian Seidl
committed
"/gosdn.networkelement.NetworkElementService/GetIntendedPath",
"/gosdn.networkelement.NetworkElementService/GetChange",
"/gosdn.networkelement.NetworkElementService/GetChangeList",
Fabian Seidl
committed
"/gosdn.networkelement.NetworkElementService/AddList",
"/gosdn.networkelement.NetworkElementService/SetChangeList",
"/gosdn.networkelement.NetworkElementService/SetPathList",
"/gosdn.networkelement.NetworkElementService/DeviceSchema",
Fabian Seidl
committed
"/gosdn.networkelement.NetworkElementService/Delete",
"/gosdn.networkelement.NetworkElementService/SubscribePath",
"/gosdn.plugin_internal.PluginInternalService/AvailablePlugins",
"/gosdn.plugin_internal.PluginInternalService/GetPluginSchema",
"/gosdn.app.AppService/Register",
"/gosdn.app.AppService/Deregister",
"/gosdn.configurationmanagement.ConfigurationManagementService/ExportSDNConfig",
"/gosdn.configurationmanagement.ConfigurationManagementService/ImportSDNConfig",
"/gosdn.topology.RoutingTableService/AddRoutingTable",
"/gosdn.topology.RoutingTableService/GetRoutes",
"/gosdn.topology.RoutingTableService/DeleteRoute",
"/gosdn.topology.TopologyService/AddLink",
"/gosdn.topology.TopologyService/GetTopology",
"/gosdn.topology.TopologyService/UpdateLink",
"/gosdn.topology.TopologyService/DeleteLink",
"/gosdn.subscriptionmanagement.SubscriptionManagementService/ResetAllSubscriptions",
"/gosdn.subscriptionmanagement.SubscriptionManagementService/GetAll",
"/gosdn.subscriptionmanagement.SubscriptionManagementService/Get",
"/gosdn.subscriptionmanagement.SubscriptionManagementService/Delete",
"/gosdn.subscriptionmanagement.SubscriptionManagementService/Add",
}))
if err != nil {
return err
}
}
return nil
}
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
func ensureGenericAppRoleExists() error {
defaultAppRoleName := "app"
appRole, err := c.roleService.Get(store.Query{ID: uuid.Nil, Name: defaultAppRoleName})
if err != nil {
log.Info(err)
}
if appRole == nil {
err := c.roleService.Add(rbacImpl.NewRole(uuid.New(), defaultAppRoleName, "generic app role", []string{
"/gosdn.pnd.PndService/GetPnd",
"/gosdn.pnd.PndService/GetPndList",
"/gosdn.networkelement.NetworkElementService/Get",
"/gosdn.networkelement.NetworkElementService/GetFlattened",
"/gosdn.networkelement.NetworkElementService/GetAll",
"/gosdn.networkelement.NetworkElementService/GetAllFlattened",
"/gosdn.networkelement.NetworkElementService/GetPath",
"/gosdn.networkelement.NetworkElementService/GetIntendedPath",
"/gosdn.networkelement.NetworkElementService/GetChange",
"/gosdn.networkelement.NetworkElementService/GetChangeList",
"/gosdn.networkelement.NetworkElementService/AddList",
"/gosdn.networkelement.NetworkElementService/SetChangeList",
"/gosdn.networkelement.NetworkElementService/SetPathList",
"/gosdn.networkelement.NetworkElementService/DeviceSchema",
"/gosdn.networkelement.NetworkElementService/Delete",
"/gosdn.networkelement.NetworkElementService/SubscribePath",
"/gosdn.plugin_internal.PluginInternalService/AvailablePlugins",
"/gosdn.plugin_internal.PluginInternalService/GetPluginSchema",
"/gosdn.app.AppService/Register",
"/gosdn.app.AppService/Deregister",
}))
if err != nil {
return err
}
}
return nil
}
func ensureDefaultUserExists() error {
defaultUserName := "admin"
adminUser, err := c.userService.Get(store.Query{ID: uuid.Nil, Name: defaultUserName})
if err != nil {
log.Info(err)
}
if adminUser == nil {

Katharina Renk
committed
// Getting the password from the environment variable which is set in gosdn.clab.yaml.
var preDefinedPassword = os.Getenv("GOSDN_ADMIN_PASSWORD")
var usedPassword string
// If environment variable is set and password is not 0, the password from the environment variable will be used.
if len(preDefinedPassword) == 0 {
// Generate a password that is 16 characters long with 3 digits, 0 symbols,
// allowing upper and lower case letters, disallowing repeat characters.
generatedPassword, err := password.Generate(16, 3, 0, true, false)
if err != nil {
log.Fatal(err)
}
usedPassword = generatedPassword
} else {
usedPassword = preDefinedPassword
salt, err := password.Generate(16, 3, 0, true, false)
if err != nil {
log.Fatal(err)
}

Katharina Renk
committed
hashedPassword := base64.RawStdEncoding.EncodeToString(argon2.IDKey([]byte(usedPassword), []byte(salt), 1, 64*1024, 4, 32))
err = c.userService.Add(rbacImpl.NewUser(uuid.New(), defaultUserName, map[string]string{config.BasePndUUID.String(): "admin"}, string(hashedPassword), []string{}, salt, conflict.Metadata{ResourceVersion: 0}))
if err != nil {
return err
}

Katharina Renk
committed
fmt.Printf("########\n Generated admin password: %s\n########\n", usedPassword)
}
return nil
}
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
func deleteAllExpiredUserTokens() error {
var usersToUpdate []rbac.User
// Temporary create JWT manager just to evaluate tokens here
jwtManager := rbacImpl.NewJWTManager(config.JWTSecret, config.JWTDuration)
users, err := c.userService.GetAll()
if err != nil {
return fmt.Errorf("error getting all users while deleting expired user tokens: %w", err)
}
for _, user := range users {
tokens := user.GetTokens()
for _, token := range tokens {
claims, err := jwtManager.GetClaimsFromToken(token)
if err != nil {
return fmt.Errorf("error getting claims from token while deleting expired user tokens: %w", err)
}
if claims.ExpiresAt < time.Now().Unix() {
err := user.RemoveToken(token)
if err != nil {
return fmt.Errorf("error removing token while deleting expired user tokens: %w", err)
}
usersToUpdate = append(usersToUpdate, user)
}
}
}
for _, user := range usersToUpdate {
err := c.userService.Update(user)
if err != nil {
return fmt.Errorf("error updating user while deleting expired user tokens: %w", err)
}
}
return nil
}
// Run calls initialize to start the controller.
initError = initialize()
})
if initError != nil {
log.WithFields(log.Fields{}).Error(initError)
return initError
log.WithFields(log.Fields{}).Info("initialisation finished")
select {
case <-c.stopChan:
return shutdown()
case <-ctx.Done():
return shutdown()
defer func() {
plugins, err := c.pluginService.GetAll()
if err != nil {
log.Error(err)
}
for _, plugin := range plugins {
log.Info("Defer: ", plugin.Manifest().Name)
Malte Bauch
committed
plugin.Close()
log.Info("Defer - exited: ", plugin.GetClient().Exited())
}
coreLock.Unlock()
}()
Fabian Seidl
committed
c.eventService.CloseConnection()
func callback(id uuid.UUID, ch chan networkelement.Details) {
c.pndStore.AddPendingChannel(id, ch)
log.Infof("pending channel %v added", id)
} else {
c.pndStore.RemovePendingChannel(id)
log.Infof("pending channel %v removed", id)
}
}
// setupGRPCServerWithCorrectSecurityLevel sets up a gRPC server with desired security level
//
// Only two options for now: insecure or secure, add 'else if' if required.
// Secure is the recommended mode and is set as default.
// Insecure starts the controller without the gRPC interceptor which is supposed to handle authz.
// This allows users to operate on the controller without any authentication/authorization,
// but they could still login if they want to.
// Use insecure only for testing purposes and with caution.
func setupGRPCServerWithCorrectSecurityLevel(jwt *rbacImpl.JWTManager, userService rbac.UserService, roleService rbac.RoleService) {
securityLevel := viper.GetString("security")
if securityLevel == "insecure" {
c.grpcServer = grpc.NewServer()
log.Info("set up grpc server in insecure mode")
} else {
interceptor := server.NewAuthInterceptor(jwt, userService, roleService)
c.grpcServer = grpc.NewServer(grpc.UnaryInterceptor(interceptor.Unary()), grpc.StreamInterceptor(interceptor.Stream()))
log.Info("set up grpc server in secure mode")
}
}