// InitializeCertAuthConfig sets up the command line options for creating a CA func InitializeCertAuthConfig(logger log.Logger) error { viper.SetDefault("Bits", "4096") viper.SetDefault("Years", "10") viper.SetDefault("Organization", "kappa-ca") viper.SetDefault("Country", "USA") if initCmd.PersistentFlags().Lookup("bits").Changed { logger.Info("", "Bits", KeyBits) viper.Set("Bits", KeyBits) } if initCmd.PersistentFlags().Lookup("years").Changed { logger.Info("", "Years", Years) viper.Set("Years", Years) } if initCmd.PersistentFlags().Lookup("organization").Changed { logger.Info("", "Organization", Organization) viper.Set("Organization", Organization) } if initCmd.PersistentFlags().Lookup("country").Changed { logger.Info("", "Country", Country) viper.Set("Country", Country) } if initCmd.PersistentFlags().Lookup("hosts").Changed { logger.Info("", "Hosts", Hosts) viper.Set("Hosts", Hosts) } return nil }
// CreateCertificateAuthority generates a new CA func CreateCertificateAuthority(logger log.Logger, key *rsa.PrivateKey, years int, org, country, hostList string) ([]byte, error) { // Generate subject key id logger.Info("Generating SubjectKeyID") subjectKeyID, err := GenerateSubjectKeyID(key) if err != nil { return nil, err } // Create serial number logger.Info("Generating Serial Number") serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128) serialNumber, err := rand.Int(rand.Reader, serialNumberLimit) if err != nil { return nil, fmt.Errorf("failed to generate serial number: %s", err.Error()) } // Create template logger.Info("Creating Certificate template") template := &x509.Certificate{ IsCA: true, BasicConstraintsValid: true, SubjectKeyId: subjectKeyID, SerialNumber: serialNumber, Subject: pkix.Name{ Country: []string{country}, Organization: []string{org}, }, PublicKeyAlgorithm: x509.RSA, SignatureAlgorithm: x509.SHA512WithRSA, NotBefore: time.Now().Add(-600).UTC(), NotAfter: time.Now().AddDate(years, 0, 0).UTC(), // see http://golang.org/pkg/crypto/x509/#KeyUsage ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth}, KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign, } // Associate hosts logger.Info("Adding Hosts to Certificate") hosts := strings.Split(hostList, ",") for _, h := range hosts { if ip := net.ParseIP(h); ip != nil { template.IPAddresses = append(template.IPAddresses, ip) } else { template.DNSNames = append(template.DNSNames, h) } } // Create cert logger.Info("Generating Certificate") cert, err := x509.CreateCertificate(rand.Reader, template, template, &key.PublicKey, key) if err != nil { return nil, err } return cert, nil }
// SaveCertificateRequest saves a certificate in the PEM format. func SaveCertificateRequest(logger log.Logger, cert []byte, filename string) { logger.Info("Saving Certificate Request") pemfile, _ := os.Create(filename) pemkey := &pem.Block{ Type: "CERTIFICATE REQUEST", Bytes: cert} pem.Encode(pemfile, pemkey) pemfile.Close() }
// SavePrivateKey saves a PrivateKey in the PEM format. func SavePrivateKey(logger log.Logger, key *rsa.PrivateKey, filename string) { logger.Info("Saving Private Key") pemfile, _ := os.Create(filename) pemkey := &pem.Block{ Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(key)} pem.Encode(pemfile, pemkey) pemfile.Close() }
// SavePublicKey saves a public key in the PEM format. func SavePublicKey(logger log.Logger, key *rsa.PrivateKey, filename string) { logger.Info("Saving Public Key") pemfile, _ := os.Create(filename) bytes, _ := x509.MarshalPKIXPublicKey(key.PublicKey) pemkey := &pem.Block{ Type: "RSA PUBLIC KEY", Bytes: bytes} pem.Encode(pemfile, pemkey) pemfile.Close() }
func handleChannelRequests(logger log.Logger, channel ssh.Channel, requests <-chan *ssh.Request, system datamodel.System, user datamodel.User) { defer channel.Close() for req := range requests { if req.Type == "skl" { logger.Info("SKL request", "request", string(req.Payload)) req.Reply(true, nil) } else { if req.WantReply { req.Reply(false, nil) } } } }
// InitializeNewCertConfig sets up the command line options for creating a new certificate func InitializeNewCertConfig(logger log.Logger) error { viper.SetDefault("Name", "localhost") viper.SetDefault("ForceOverwrite", "false") if newCertCmd.PersistentFlags().Lookup("name").Changed { logger.Info("", "Name", Name) viper.Set("Name", Name) } if newCertCmd.PersistentFlags().Lookup("overwrite").Changed { logger.Info("", "ForceOverwrite", ForceOverwrite) viper.Set("ForceOverwrite", ForceOverwrite) } return nil }
// InitializeMainConfig sets up the config options for the kappa command func InitializeMainConfig(logger log.Logger) error { viper.SetConfigFile("config") viper.AddConfigPath(ConfigPath) // Read configuration file logger.Info("Reading configuration file") err := viper.ReadInConfig() if err != nil { logger.Warn("Unable to locate configuration file.") } if kappaCmd.PersistentFlags().Lookup("config").Changed { logger.Info("", "ConfigPath", ConfigPath) viper.Set("ConfigPath", ConfigPath) } return nil }
func handleTCPConnection(logger log.Logger, conn net.Conn, sshConfig *ssh.ServerConfig, system datamodel.System) { // Open SSH connection sshConn, channels, requests, err := ssh.NewServerConn(conn, sshConfig) if err != nil { logger.Warn("SSH handshake failed") return } // Get user if exists, otherwise return error users, _ := system.Users() user, _ := users.Get(sshConn.Permissions.Extensions["username"]) logger.Debug("Handshake successful") defer sshConn.Conn.Close() // Discard requests go ssh.DiscardRequests(requests) for ch := range channels { t := ch.ChannelType() if t != "session" && t != "kappa-client" { logger.Info("UnknownChannelType", "type", t) ch.Reject(ssh.UnknownChannelType, t) break } // Accept channel channel, requests, err := ch.Accept() if err != nil { logger.Warn("Error creating channel") continue } if t == "session" { go handleSessionRequests(logger, channel, requests, system, user) } else if t == "kappa-client" { go handleChannelRequests(logger, channel, requests, system, user) } } }
// CreateCertificateRequest generates a new certificate request func CreateCertificateRequest(logger log.Logger, key *rsa.PrivateKey, name, org, country, hostList string) (*x509.CertificateRequest, []byte, error) { // Create template logger.Info("Creating Certificate template") template := &x509.CertificateRequest{ Subject: pkix.Name{ Country: []string{country}, Organization: []string{org}, OrganizationalUnit: []string{name}, }, } // Associate hosts logger.Info("Adding Hosts to Certificate") hosts := strings.Split(hostList, ",") for _, h := range hosts { if ip := net.ParseIP(h); ip != nil { template.IPAddresses = append(template.IPAddresses, ip) } else { template.DNSNames = append(template.DNSNames, h) } } // Create cert logger.Info("Generating Certificate") cert, err := x509.CreateCertificateRequest(rand.Reader, template, key) if err != nil { return nil, nil, err } return template, cert, nil }
func (s *SSHServer) Run(logger log.Logger, closer chan<- bool) { logger.Info("Starting SSH server", "addr", viper.GetString("SSHListen")) s.done = make(chan bool) // Start server go func(l log.Logger, sock *net.TCPListener, config *ssh.ServerConfig, c <-chan bool, complete chan<- bool) { defer sock.Close() for { // Accepts will only block for 1s sock.SetDeadline(time.Now().Add(time.Second)) select { // Stop server on channel recieve case <-c: l.Info("Stopping SSH server") complete <- true return default: // Accept new connection tcpConn, err := sock.Accept() if err != nil { if neterr, ok := err.(net.Error); ok && neterr.Timeout() { // l.Debug("Connection timeout...") } else { l.Warn("Connection failed", "error", err) } continue } // Handle connection l.Debug("Successful SSH connection") go handleTCPConnection(l, tcpConn, config, s.system) } } }(logger, s.listener, s.sshConfig, s.done, closer) }
// CreateCertificate generates a new cert func CreateCertificate(logger log.Logger, req *x509.CertificateRequest, key *rsa.PrivateKey, years int, hostList string) ([]byte, error) { // Read CA logger.Info("Reading Certificate Authority") pemBlock, err := ReadCertificate(path.Join(".", "pki", "ca.crt"), "CERTIFICATE") if err != nil { return nil, err } // Decrypt PEM logger.Info("Decoding Certificate Authority Public Key") authority, err := x509.ParseCertificate(pemBlock.Bytes) if err != nil { return nil, err } logger.Info("Reading Certificate Authority Private Key") pemBlock, err = ReadCertificate(path.Join(".", "pki", "private", "ca.key"), "RSA PRIVATE KEY") if err != nil { return nil, err } logger.Info("Parsing Certificate Authority Private Key") priv, err := x509.ParsePKCS1PrivateKey(pemBlock.Bytes) if err != nil { return nil, err } // Generate subject key id logger.Info("Generating SubjectKeyID") subjectKeyID, err := GenerateSubjectKeyID(key) if err != nil { return nil, err } // Create serial number logger.Info("Generating Serial Number") serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128) serialNumber, err := rand.Int(rand.Reader, serialNumberLimit) if err != nil { return nil, fmt.Errorf("failed to generate serial number: %s", err.Error()) } // Create template logger.Info("Creating Certificate template") template := &x509.Certificate{ IsCA: false, BasicConstraintsValid: false, SubjectKeyId: subjectKeyID, SerialNumber: serialNumber, Subject: req.Subject, PublicKeyAlgorithm: x509.RSA, SignatureAlgorithm: x509.SHA512WithRSA, NotBefore: time.Now().Add(-600).UTC(), NotAfter: time.Now().AddDate(years, 0, 0).UTC(), // see http://golang.org/pkg/crypto/x509/#KeyUsage ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth}, KeyUsage: x509.KeyUsageDigitalSignature, UnknownExtKeyUsage: nil, // Subject Alternative Name DNSNames: nil, PermittedDNSDomainsCritical: false, PermittedDNSDomains: nil, } // Associate hosts logger.Info("Adding Hosts to Certificate") hosts := strings.Split(hostList, ",") for _, h := range hosts { if ip := net.ParseIP(h); ip != nil { template.IPAddresses = append(template.IPAddresses, ip) } else { template.DNSNames = append(template.DNSNames, h) } } // Create cert logger.Info("Generating Certificate") cert, err := x509.CreateCertificate(rand.Reader, template, authority, &key.PublicKey, priv) if err != nil { return nil, err } return cert, nil }
// InitializeServerConfig sets up the config options for the database servers. func InitializeServerConfig(logger log.Logger) error { // Load default settings logger.Info("Loading default server settings") viper.SetDefault("CACert", "ca.crt") viper.SetDefault("AdminCert", "admin.crt") viper.SetDefault("SSHKey", "ssh-identity.key") viper.SetDefault("TLSCert", "tls-identity.crt") viper.SetDefault("TLSKey", "tls-identity.key") viper.SetDefault("DataPath", "./data") viper.SetDefault("SSHListen", ":9022") viper.SetDefault("HTTPListen", ":19022") if serverCmd.PersistentFlags().Lookup("ca-cert").Changed { logger.Info("", "CACert", CACert) viper.Set("CACert", CACert) } if serverCmd.PersistentFlags().Lookup("admin-cert").Changed { logger.Info("", "AdminCert", AdminCert) viper.Set("AdminCert", AdminCert) } if serverCmd.PersistentFlags().Lookup("ssh-key").Changed { logger.Info("", "SSHKey", SSHKey) viper.Set("SSHKey", SSHKey) } if serverCmd.PersistentFlags().Lookup("tls-cert").Changed { logger.Info("", "TLSCert", TLSCert) viper.Set("TLSCert", TLSCert) } if serverCmd.PersistentFlags().Lookup("tls-key").Changed { logger.Info("", "TLSKey", TLSKey) viper.Set("TLSKey", TLSKey) } if serverCmd.PersistentFlags().Lookup("ssh-listen").Changed { logger.Info("", "SSHListen", SSHListen) viper.Set("SSHListen", SSHListen) } if serverCmd.PersistentFlags().Lookup("http-listen").Changed { logger.Info("", "HTTPListen", HTTPListen) viper.Set("HTTPListen", HTTPListen) } if serverCmd.PersistentFlags().Lookup("data").Changed { logger.Info("", "DataPath", DataPath) viper.Set("DataPath", DataPath) } return nil }
func NewSSHServer(logger log.Logger, sys datamodel.System, privateKey ssh.Signer, roots *x509.CertPool) (server SSHServer, err error) { // Get user store users, err := sys.Users() // Create server config sshConfig := &ssh.ServerConfig{ NoClientAuth: false, PublicKeyCallback: func(conn ssh.ConnMetadata, key ssh.PublicKey) (perm *ssh.Permissions, err error) { // Get user if exists, otherwise return error user, err := users.Get(conn.User()) if err != nil { return } // Check keyring for public key if keyring := user.KeyRing(); !keyring.Contains(key.Marshal()) { err = fmt.Errorf("invalid public key") return } // Add pubkey and username to permissions perm = &ssh.Permissions{ Extensions: map[string]string{ "pubkey": string(key.Marshal()), "username": conn.User(), }, } return }, AuthLogCallback: func(conn ssh.ConnMetadata, method string, err error) { if err != nil { logger.Info("Login attempt", "user", conn.User(), "method", method, "error", err.Error()) } else { logger.Info("Successful login", "user", conn.User(), "method", method) } }, } sshConfig.AddHostKey(privateKey) // Get ssh bind addr bind := viper.GetString("SSHListen") if bind == "" { err = fmt.Errorf("Empty SSH bind address") return } // Open SSH socket logger.Info("Starting SSH server", "addr", bind) sshAddr, err := net.ResolveTCPAddr("tcp", bind) if err != nil { err = fmt.Errorf("Invalid tcp address") return } // Create listener listener, err := net.ListenTCP("tcp", sshAddr) if err != nil { return } server.logger = logger server.sshConfig = sshConfig server.listener = listener server.system = sys return }
// InitializeServerConfig sets up the config options for the database servers. func InitializeServerConfig(logger log.Logger) error { // Load default settings logger.Info("Loading default server settings") // CACert sets the certificate authority viper.SetDefault("CACert", "ca.crt") viper.BindEnv("CACert", "KAPPA_CA_CERT") // AdminCert sets the admin certificate viper.SetDefault("AdminCert", "admin.crt") viper.BindEnv("AdminCert", "KAPPA_ADMIN_CERT") // SSHKey sets the private key for the SSH server viper.SetDefault("SSHKey", "ssh-identity.key") viper.BindEnv("SSHKey", "KAPPA_SSH_KEY") // TLSCert sets the certificate for HTTPS viper.SetDefault("TLSCert", "tls-identity.crt") viper.BindEnv("TLSCert", "KAPPA_TLS_CERT") // TLSKey sets the private key for HTTPS viper.SetDefault("TLSKey", "tls-identity.key") viper.BindEnv("TLSKey", "KAPPA_TLS_KEY") // DataPath sets the directory for data storage viper.SetDefault("DataPath", "./data") viper.BindEnv("DataPath", "KAPPA_DATA_PATH") // SSHListen sets the address to listen for SSH traffic viper.SetDefault("SSHListen", ":9022") viper.BindEnv("SSHListen", "KAPPA_SSH_LISTEN") // HTTPListen sets the address to listen for HTTP traffic viper.SetDefault("HTTPListen", ":19022") viper.BindEnv("HTTPListen", "KAPPA_HTTP_LISTEN") // Serf config // ClusterNodes is a list of existing cluster nodes viper.SetDefault("ClusterNodes", "") viper.BindEnv("ClusterNodes", "KAPPA_CLUSTER_NODES") // NodeName sets the server's name viper.SetDefault("NodeName", "kappa-server") viper.BindEnv("NodeName", "KAPPA_NODE_NAME") // ClusterName sets the cluster name of this node. viper.SetDefault("ClusterName", "kappa") viper.BindEnv("ClusterName", "KAPPA_CLUSTER_NAME") // Bootstrap sets whether to bootstrap this node. viper.SetDefault("Bootstrap", false) viper.BindEnv("Bootstrap", "KAPPA_BOOTSTRAP") // BootstrapExpect is an argument used by Serf. viper.SetDefault("BootstrapExpect", 0) // Memberlist config // GossipBindAddr sets the Addr for cluster gossip. viper.SetDefault("GossipBindAddr", "0.0.0.0") viper.BindEnv("GossipBindAddr", "KAPPA_GOSSIP_BIND_ADDR") // GossipBindPort sets the port for cluster gossip. The port is used for both UDP and TCP gossip. viper.SetDefault("GossipBindPort", 7946) viper.BindEnv("GossipBindPort", "KAPPA_GOSSIP_BIND_PORT") // GossipAdvertiseAddr sets what address to advertise to other // cluster members. Used for nat traversal. viper.SetDefault("GossipAdvertiseAddr", "") viper.BindEnv("GossipAdvertiseAddr", "KAPPA_GOSSIP_ADVERTISE_ADDR") // GossipAdvertisePort sets the port for cluster gossip and can // be useful for NAT traversal. viper.SetDefault("GossipAdvertisePort", 7946) viper.BindEnv("GossipAdvertisePort", "KAPPA_GOSSIP_ADVERTISE_PORT") // Set viper flags if serverCmd.PersistentFlags().Lookup("ca-cert").Changed { logger.Info("", "CACert", CACert) viper.Set("CACert", CACert) } if serverCmd.PersistentFlags().Lookup("admin-cert").Changed { logger.Info("", "AdminCert", AdminCert) viper.Set("AdminCert", AdminCert) } if serverCmd.PersistentFlags().Lookup("ssh-key").Changed { logger.Info("", "SSHKey", SSHKey) viper.Set("SSHKey", SSHKey) } if serverCmd.PersistentFlags().Lookup("tls-cert").Changed { logger.Info("", "TLSCert", TLSCert) viper.Set("TLSCert", TLSCert) } if serverCmd.PersistentFlags().Lookup("tls-key").Changed { logger.Info("", "TLSKey", TLSKey) viper.Set("TLSKey", TLSKey) } if serverCmd.PersistentFlags().Lookup("ssh-listen").Changed { logger.Info("", "SSHListen", SSHListen) viper.Set("SSHListen", SSHListen) } if serverCmd.PersistentFlags().Lookup("http-listen").Changed { logger.Info("", "HTTPListen", HTTPListen) viper.Set("HTTPListen", HTTPListen) } if serverCmd.PersistentFlags().Lookup("data").Changed { logger.Info("", "DataPath", DataPath) viper.Set("DataPath", DataPath) } // Serf config if serverCmd.PersistentFlags().Lookup("nodes").Changed { logger.Info("", "ClusterNodes", ClusterNodes) viper.Set("ClusterNodes", ClusterNodes) } if serverCmd.PersistentFlags().Lookup("node-name").Changed { logger.Info("", "NodeName", NodeName) viper.Set("NodeName", NodeName) } if serverCmd.PersistentFlags().Lookup("cluster").Changed { logger.Info("", "ClusterName", ClusterName) viper.Set("ClusterName", ClusterName) } if serverCmd.PersistentFlags().Lookup("bootstrap").Changed { logger.Info("", "Bootstrap", Bootstrap) viper.Set("Bootstrap", Bootstrap) } if serverCmd.PersistentFlags().Lookup("bootstrap-expect").Changed { logger.Info("", "BootstrapExpect", BootstrapExpect) viper.Set("BootstrapExpect", BootstrapExpect) } // Memberlist Config if serverCmd.PersistentFlags().Lookup("gossip-bind-addr").Changed { logger.Info("", "GossipBindAddr", GossipBindAddr) viper.Set("GossipBindAddr", GossipBindAddr) } if serverCmd.PersistentFlags().Lookup("gossip-bind-port").Changed { logger.Info("", "GossipBindPort", GossipBindPort) viper.Set("GossipBindPort", GossipBindPort) } if serverCmd.PersistentFlags().Lookup("gossip-advert-addr").Changed { logger.Info("", "GossipAdvertiseAddr", GossipAdvertiseAddr) viper.Set("GossipAdvertiseAddr", GossipAdvertiseAddr) } if serverCmd.PersistentFlags().Lookup("gossip-advert-port").Changed { logger.Info("", "GossipAdvertisePort", GossipAdvertisePort) viper.Set("GossipAdvertisePort", GossipAdvertisePort) } return nil }
func startTerminal(logger log.Logger, channel ssh.Channel, system datamodel.System, user datamodel.User) { defer channel.Close() prompt := "kappa> " term := terminal.NewTerminal(channel, prompt) // // Try to make the terminal raw // oldState, err := terminal.MakeRaw(0) // if err != nil { // logger.Warn("Error making terminal raw: ", err.Error()) // } // defer terminal.Restore(0, oldState) // Write ascii text term.Write([]byte("\r\n")) for _, line := range ASCII { term.Write([]byte(line)) term.Write([]byte("\r\n")) } // Write login message term.Write([]byte("\r\n\n")) GetMessage(channel, DefaultColorCodes) term.Write([]byte("\n")) // Create query executor executor := Executor{ session: Session{ namespace: "", user: user, }, terminal: NewTerminal(term, prompt), system: system, } // Start REPL for { input, err := term.ReadLine() if err != nil { fmt.Errorf("Readline() error") break } // Process line line := strings.TrimSpace(input) if len(line) > 0 { // Log input and handle exit requests if line == "exit" || line == "quit" { logger.Info("Closing connection") break } else if line == "quote me" { term.Write([]byte("\r\n")) GetMessage(channel, DefaultColorCodes) term.Write([]byte("\r\n")) continue } else if strings.HasPrefix(line, "//") || strings.HasPrefix(line, "--") { channel.Write(DefaultColorCodes.LightGrey) channel.Write([]byte(line + "\r\n")) channel.Write(DefaultColorCodes.Reset) continue } // Parse statement stmt, err := skl.ParseStatement(line) // Return parse error in red if err != nil { logger.Warn("Bad Statement", "statement", line, "error", err) channel.Write(DefaultColorCodes.LightRed) channel.Write([]byte(err.Error())) channel.Write([]byte("\r\n")) channel.Write(DefaultColorCodes.Reset) continue } // Execute statements w := ResponseWriter{DefaultColorCodes, channel} executor.Execute(&w, stmt) } } }