func New(c *Config) (cer Cerebrum, err error) { // Create logger if c.LogOutput == nil { c.LogOutput = log.NewConcurrentWriter(os.Stderr) } logger := log.NewLogger(c.LogOutput, "kappa") // Create data directory if err = os.MkdirAll(c.DataPath, 0755); err != nil { logger.Warn("Could not create data directory", "err", err) return } // Setup reconciler serfEventCh := make(chan serf.Event, 256) reconcilerCh := make(chan serf.Member, 32) ctx, cancel := context.WithCancel(context.Background()) cereb := &cerebrum{ config: c, logger: logger, dialer: NewDialer(NewPool(c.LogOutput, 5*time.Minute, c.TLSConfig)), serfEventCh: serfEventCh, reconcileCh: reconcilerCh, grim: grim.ReaperWithContext(ctx), context: ctx, cancel: cancel, } // Create serf server err = cereb.setupRaft() if err != nil { err = logger.Error("Failed to start serf: %v", err) return nil, err } isLeader := func() bool { return cereb.raft.State() == raft.Leader } reconciler := &Reconciler{reconcilerCh, isLeader} cereb.serfer = serfer.NewSerfer(serfEventCh, serfer.SerfEventHandler{ Logger: log.NewLogger(c.LogOutput, CerebrumEventPrefix), ServicePrefix: CerebrumEventPrefix, ReconcileOnJoin: true, ReconcileOnLeave: true, ReconcileOnFail: true, ReconcileOnUpdate: true, ReconcileOnReap: true, NodeJoined: c.NodeJoined, NodeUpdated: c.NodeUpdated, NodeLeft: c.NodeLeft, NodeFailed: c.NodeFailed, NodeReaped: c.NodeReaped, UserEvent: c.UserEvent, UnknownEventHandler: c.UnknownEventHandler, Reconciler: reconciler, IsLeader: isLeader, IsLeaderEvent: func(name string) bool { return name == CerebrumLeaderEvent }, LeaderElectionHandler: cereb, }) // Create serf server cereb.serf, err = cereb.setupSerf() if err != nil { err = logger.Error("Failed to start serf: %v", err) return nil, err } cer = cereb return cer, nil }
func NewServer(c *DatabaseConfig) (server *Server, err error) { // Create logger if c.LogOutput == nil { c.LogOutput = log.NewConcurrentWriter(os.Stdout) } logger := log.NewLogger(c.LogOutput, "kappa") // Create data directory if err = os.MkdirAll(c.DataPath, 0755); err != nil { logger.Warn("Could not create data directory", "err", err) // logger.Warn("Could not create data directory", "err", err.Error()) return } // Connect to database cwd, err := os.Getwd() if err != nil { logger.Error("Could not get working directory", "error", err.Error()) return } file := path.Join(cwd, c.DataPath, "meta.db") logger.Info("Connecting to database", "file", file) system, err := datamodel.NewSystem(file) if err != nil { logger.Error("Could not connect to database", "error", err.Error()) return } // Get SSH Key file sshKeyFile := c.SSHPrivateKeyFile logger.Info("Reading private key", "file", sshKeyFile) privateKey, err := auth.ReadPrivateKey(logger, sshKeyFile) if err != nil { return } // Get admin certificate adminCertFile := c.AdminCertificateFile logger.Info("Reading admin public key", "file", adminCertFile) // Read admin certificate cert, err := ioutil.ReadFile(adminCertFile) if err != nil { logger.Error("admin certificate could not be read", "filename", c.AdminCertificateFile) return } // Add admin cert to key ring userStore, err := system.Users() if err != nil { logger.Error("could not get user store", "error", err.Error()) return } // Create admin account admin, err := userStore.Create("admin") if err != nil { logger.Error("error creating admin account", "error", err.Error()) return } // Add admin certificate keyRing := admin.KeyRing() fingerprint, err := keyRing.AddPublicKey(cert) if err != nil { logger.Error("admin certificate could not be added", "error", err.Error()) return } logger.Info("Added admin certificate", "fingerprint", fingerprint) // Read root cert rootPem, err := ioutil.ReadFile(c.CACertificateFile) if err != nil { logger.Error("root certificate could not be read", "filename", c.CACertificateFile) return } // Create certificate pool roots := x509.NewCertPool() if ok := roots.AppendCertsFromPEM(rootPem); !ok { logger.Error("failed to parse root certificate") return } // Setup SSH Server sshLogger := log.NewLogger(c.LogOutput, "ssh") pubKeyCallback, err := PublicKeyCallback(system) if err != nil { logger.Error("failed to create PublicKeyCallback", err) return } // Setup server config config := sshh.Config{ Deadline: c.SSHConnectionDeadline, Logger: sshLogger, Bind: c.SSHBindAddress, PrivateKey: privateKey, PublicKeyCallback: pubKeyCallback, AuthLogCallback: func(meta ssh.ConnMetadata, method string, err error) { if err == nil { sshLogger.Info("login success", "user", meta.User()) } else if err != nil && method == "publickey" { sshLogger.Info("login failure", "user", meta.User(), "err", err.Error()) } }, Handlers: map[string]sshh.SSHHandler{ "kappa-client": &EchoHandler{}, }, } // Create SSH server sshServer, err := sshh.NewSSHServer(&config) if err != nil { logger.Error("SSH Server could not be configured", "error", err.Error()) return } // Setup Serf handlers mgr := NewNodeList() reconcilerCh := make(chan serf.Member, 32) serfEventCh := make(chan serf.Event, 256) userEventCh := make(chan serf.UserEvent, 256) serfer := serfer.NewSerfer(serfEventCh, serfer.SerfEventHandler{ Logger: log.NewLogger(c.LogOutput, "serf"), ServicePrefix: "kappa", ReconcileOnJoin: true, ReconcileOnLeave: true, ReconcileOnFail: true, ReconcileOnUpdate: true, ReconcileOnReap: true, NodeJoined: &SerfNodeJoinHandler{ mgr, log.NewLogger(c.LogOutput, "serf:node-join")}, NodeUpdated: &SerfNodeUpdateHandler{ mgr, log.NewLogger(c.LogOutput, "serf:node-update")}, NodeLeft: &SerfNodeLeaveHandler{ mgr, log.NewLogger(c.LogOutput, "serf:node-left")}, NodeFailed: &SerfNodeLeaveHandler{ mgr, log.NewLogger(c.LogOutput, "serf:node-fail")}, NodeReaped: &SerfNodeLeaveHandler{ mgr, log.NewLogger(c.LogOutput, "serf:node-reap")}, UserEvent: &SerfUserEventHandler{ log.NewLogger(c.LogOutput, "serf:user-events"), userEventCh}, UnknownEventHandler: &SerfUserEventHandler{ log.NewLogger(c.LogOutput, "serf:unknown-event"), userEventCh}, Reconciler: &SerfReconciler{reconcilerCh}, IsLeader: func() bool { // TODO: Replace with Raft IsLeader check return true }, }) // Create database server s := &Server{ config: c, logger: logger, sshServer: &sshServer, serfer: serfer, localKappas: make(map[string]*NodeDetails), serfEventCh: serfEventCh, kappaEventCh: userEventCh, reconcileCh: reconcilerCh, } // Create serf server s.serf, err = s.setupSerf() if err != nil { err = logger.Error("Failed to start serf: %v", err) return } return s, nil }