Documentation
¶
Index ¶
Constants ¶
This section is empty.
Variables ¶
View Source
var ConnectCmd = &cli.Command{ Name: "connect", Usage: "Connect to server", Description: "Authenticate the client with a remote server and save the server address and access key.", Arguments: []cli.Argument{ &cli.StringArg{ Name: "server", Usage: "The server to connect to", Required: true, }, }, MaxArgs: cli.NoArgs, Flags: []cli.Flag{ &cli.BoolFlag{ Name: "use-web-auth", Usage: "If given then authorization will be done via the web interface.", }, &cli.BoolFlag{ Name: "tls-skip-verify", Usage: "Skip TLS verification when talking to server.", ConfigPath: []string{"tls.skip_verify"}, EnvVars: []string{config.CONFIG_ENV_PREFIX + "_TLS_SKIP_VERIFY"}, DefaultValue: true, Global: true, }, &cli.StringFlag{ Name: "username", Aliases: []string{"u"}, Usage: "Username to use for authentication.", }, &cli.StringFlag{ Name: "alias", Aliases: []string{"a"}, Usage: "The server alias to use to identify the connection.", DefaultValue: "default", }, }, Commands: []*cli.Command{ connectcmd.ConnectListCmd, connectcmd.ConnectDeleteCmd, }, Run: func(ctx context.Context, cmd *cli.Command) error { var token string server := cmd.GetStringArg("server") if !strings.HasPrefix(server, "http://") && !strings.HasPrefix(server, "https://") { server = "https://" + server } fmt.Println("Connecting to server: ", server) u, err := url.Parse(server) if err != nil { fmt.Println("Failed to parse server URL") os.Exit(1) } hostname, err := os.Hostname() if err != nil { fmt.Println("Failed to get hostname") os.Exit(1) } hostname = "knot client " + hostname client, err := apiclient.NewClient( server, "", cmd.GetBool("tls-skip-verify"), ) if err != nil { fmt.Println("Failed to create API client:", err) os.Exit(1) } totp, _, err := client.UsingTOTP(context.Background()) if err != nil { fmt.Println("Failed to query server for TOTP") os.Exit(1) } if totp || cmd.GetBool("use-web-auth") { u.Path = "/api-tokens/create/" + url.PathEscape(hostname) err = open(u.String()) if err != nil { fmt.Println("Failed to open server URL, you will need to generate the API token manually") os.Exit(1) } fmt.Print("Enter token: ") _, err = fmt.Scanln(&token) if err != nil { fmt.Println("Failed to read token, you will need to generate the API token manually") os.Exit(1) } } else { username := cmd.GetString("username") var password []byte if username == "" { fmt.Print("Enter email: ") _, err = fmt.Scanln(&username) if err != nil { fmt.Println("Failed to read email address") os.Exit(1) } } fmt.Print("Enter password: ") password, err = term.ReadPassword(int(syscall.Stdin)) if err != nil { fmt.Println("Failed to read password") os.Exit(1) } fmt.Println() if username == "" || string(password) == "" { fmt.Println("Username and password must be given") os.Exit(1) } response, _, _ := client.Login(context.Background(), username, string(password), "") if response == nil || response.Token == "" { fmt.Println("Failed to login") os.Exit(1) } client.UseSessionCookie(true).SetAuthToken(response.Token) token, _, err = client.CreateToken(context.Background(), hostname) if err != nil || token == "" { fmt.Println("Failed to create token") os.Exit(1) } } alias := cmd.GetString("alias") if err := config.SaveConnection(alias, server, token, cmd); err != nil { fmt.Println("Failed to save connection:", err) os.Exit(1) } fmt.Println("Successfully connected to server:", server) return nil }, }
View Source
var GenkeyCmd = &cli.Command{ Name: "genkey", Usage: "Generate Encryption Key", Description: "Generate an encryption key for encrypting stored variables and cluster communications.", MaxArgs: cli.NoArgs, Run: func(ctx context.Context, cmd *cli.Command) error { key := crypt.CreateKey() fmt.Println("Encryption Key:", key) fmt.Println("") return nil }, }
View Source
var LegalCmd = &cli.Command{ Name: "legal", Usage: "Show legal information", Description: "Output all the legal notices.", MaxArgs: cli.NoArgs, Run: func(ctx context.Context, cmd *cli.Command) error { legal.ShowLicenses() return nil }, }
View Source
var PingCmd = &cli.Command{ Name: "ping", Usage: "Ping the server", Description: "Ping the server and display the health and version number.", MaxArgs: cli.NoArgs, Flags: []cli.Flag{ &cli.StringFlag{ Name: "server", Aliases: []string{"s"}, Usage: "The address of the remote server to ping.", EnvVars: []string{config.CONFIG_ENV_PREFIX + "_SERVER"}, }, &cli.StringFlag{ Name: "token", Aliases: []string{"t"}, Usage: "The token to use for authentication.", EnvVars: []string{config.CONFIG_ENV_PREFIX + "_TOKEN"}, }, &cli.StringFlag{ Name: "alias", Aliases: []string{"a"}, Usage: "The server alias to use.", DefaultValue: "default", }, &cli.BoolFlag{ Name: "tls-skip-verify", Usage: "Skip TLS verification when talking to server.", ConfigPath: []string{"tls.skip_verify"}, EnvVars: []string{config.CONFIG_ENV_PREFIX + "_TLS_SKIP_VERIFY"}, DefaultValue: true, }, }, Run: func(ctx context.Context, cmd *cli.Command) error { alias := cmd.GetString("alias") cfg := config.GetServerAddr(alias, cmd) fmt.Println("Pinging server: ", cfg.HttpServer) client, err := apiclient.NewClient( cfg.HttpServer, cfg.ApiToken, cmd.GetBool("tls-skip-verify"), ) if err != nil { return fmt.Errorf("Failed to create API client: %w", err) } version, err := client.Ping(ctx) if err != nil { return fmt.Errorf("Failed to ping server: %w", err) } fmt.Println("\nServer is healthy") fmt.Println("Version: ", version) return nil }, }
View Source
var ScaffoldCmd = &cli.Command{ Name: "scaffold", Usage: "Generate configuration files", Description: "Generates example configuration files for use with knot.", MaxArgs: cli.NoArgs, Flags: []cli.Flag{ &cli.BoolFlag{ Name: "server", Usage: "Generate a server configuration file", ConfigPath: []string{"scaffold.server"}, }, &cli.BoolFlag{ Name: "client", Usage: "Generate a client configuration file", ConfigPath: []string{"scaffold.client"}, }, &cli.BoolFlag{ Name: "agent", Usage: "Generate an agent configuration file", ConfigPath: []string{"scaffold.agent"}, }, &cli.BoolFlag{ Name: "nomad", Usage: "Generate a nomad job file", ConfigPath: []string{"scaffold.nomad"}, }, }, Run: func(ctx context.Context, cmd *cli.Command) error { any := false if cmd.GetBool("server") { fmt.Println(scaffold.ServerScaffold) any = true } if cmd.GetBool("client") { fmt.Println(scaffold.ClientScaffold) any = true } if cmd.GetBool("agent") { fmt.Println(scaffold.AgentScaffold) any = true } if cmd.GetBool("nomad") { fmt.Println(scaffold.NomadScaffold) any = true } if !any { cmd.ShowHelp() } return nil }, }
View Source
var ServerCmd = &cli.Command{ Name: "server", Usage: "Start the knot server", Description: "Start the knot server and listen for incoming connections.", MaxArgs: cli.NoArgs, Flags: []cli.Flag{ &cli.StringFlag{ Name: "listen", Aliases: []string{"l"}, Usage: "The address to listen on.", ConfigPath: []string{"server.listen"}, EnvVars: []string{config.CONFIG_ENV_PREFIX + "_LISTEN"}, DefaultValue: ":3000", }, &cli.StringFlag{ Name: "listen-agent", Usage: "The address to listen on for agent connections.", ConfigPath: []string{"server.listen_agent"}, EnvVars: []string{config.CONFIG_ENV_PREFIX + "_LISTEN_AGENT"}, DefaultValue: "127.0.0.1:3010", }, &cli.StringFlag{ Name: "listen-tunnel", Usage: "The address to listen on for tunnel connections.", ConfigPath: []string{"server.listen_tunnel"}, EnvVars: []string{config.CONFIG_ENV_PREFIX + "_LISTEN_TUNNEL"}, DefaultValue: "", }, &cli.StringFlag{ Name: "url", Aliases: []string{"u"}, Usage: "The URL to use for the server.", ConfigPath: []string{"server.url"}, EnvVars: []string{config.CONFIG_ENV_PREFIX + "_URL"}, DefaultValue: "http://127.0.0.1:3000", }, &cli.StringFlag{ Name: "tunnel-server", Usage: "The URL for the tunnel client to connect to the individual server.", ConfigPath: []string{"server.tunnel_server"}, EnvVars: []string{config.CONFIG_ENV_PREFIX + "_TUNNEL_SERVER"}, DefaultValue: "", }, &cli.BoolFlag{ Name: "terminal-webgl", Usage: "Enable WebGL terminal renderer.", ConfigPath: []string{"server.terminal.webgl"}, EnvVars: []string{config.CONFIG_ENV_PREFIX + "_WEBGL"}, DefaultValue: true, }, &cli.StringFlag{ Name: "download-path", Usage: "The path to serve download files from if set.", ConfigPath: []string{"server.download_path"}, EnvVars: []string{config.CONFIG_ENV_PREFIX + "_DOWNLOAD_PATH"}, DefaultValue: "", }, &cli.StringFlag{ Name: "wildcard-domain", Usage: "The wildcard domain to use for proxying to spaces.", ConfigPath: []string{"server.wildcard_domain"}, EnvVars: []string{config.CONFIG_ENV_PREFIX + "_WILDCARD_DOMAIN"}, DefaultValue: "", }, &cli.StringFlag{ Name: "encrypt", Usage: "The encryption key to use for encrypting stored variables.", ConfigPath: []string{"server.encrypt"}, EnvVars: []string{config.CONFIG_ENV_PREFIX + "_ENCRYPT"}, DefaultValue: "", }, &cli.StringFlag{ Name: "agent-endpoint", Usage: "The address agents should use to talk to the server.", ConfigPath: []string{"server.agent_endpoint"}, EnvVars: []string{config.CONFIG_ENV_PREFIX + "_AGENT_ENDPOINT"}, DefaultValue: "", }, &cli.StringFlag{ Name: "zone", Usage: "The zone of the server.", ConfigPath: []string{"server.zone"}, EnvVars: []string{config.CONFIG_ENV_PREFIX + "_ZONE"}, }, &cli.StringFlag{ Name: "html-path", Usage: "The optional path to the html files to serve.", ConfigPath: []string{"server.html_path"}, EnvVars: []string{config.CONFIG_ENV_PREFIX + "_HTML_PATH"}, DefaultValue: "", }, &cli.StringFlag{ Name: "template-path", Usage: "The optional path to the template files to serve.", ConfigPath: []string{"server.template_path"}, EnvVars: []string{config.CONFIG_ENV_PREFIX + "_TEMPLATE_PATH"}, DefaultValue: "", }, &cli.StringFlag{ Name: "agent-path", Usage: "The optional path to the agent files to serve.", ConfigPath: []string{"server.agent_path"}, EnvVars: []string{config.CONFIG_ENV_PREFIX + "_AGENT_PATH"}, DefaultValue: "", }, &cli.StringFlag{ Name: "timezone", Usage: "The timezone to use for the server.", ConfigPath: []string{"server.timezone"}, EnvVars: []string{config.CONFIG_ENV_PREFIX + "_TIMEZONE"}, DefaultValue: "", }, &cli.StringFlag{ Name: "tunnel-domain", Usage: "The domain to use for tunnel connections.", ConfigPath: []string{"server.tunnel_domain"}, EnvVars: []string{config.CONFIG_ENV_PREFIX + "_TUNNEL_DOMAIN"}, DefaultValue: "", }, &cli.IntFlag{ Name: "audit-retention", Usage: "The number of days to keep audit logs.", ConfigPath: []string{"server.audit_retention"}, EnvVars: []string{config.CONFIG_ENV_PREFIX + "_AUDIT_RETENTION"}, DefaultValue: 90, }, &cli.BoolFlag{ Name: "disable-space-create", Usage: "Disable the ability to create spaces.", ConfigPath: []string{"server.disable_space_create"}, EnvVars: []string{config.CONFIG_ENV_PREFIX + "_DISABLE_SPACE_CREATE"}, DefaultValue: false, }, &cli.BoolFlag{ Name: "auth-ip-rate-limiting", Usage: "Enable IP rate limiting of authentication.", ConfigPath: []string{"server.auth_ip_rate_limiting"}, EnvVars: []string{config.CONFIG_ENV_PREFIX + "_AUTH_IP_RATE_LIMITING"}, DefaultValue: true, }, &cli.StringFlag{ Name: "public-files-path", Usage: "The path to the a directory to serve as /public-files.", ConfigPath: []string{"server.public_files_path"}, EnvVars: []string{config.CONFIG_ENV_PREFIX + "_PUBLIC_FILES_PATH"}, DefaultValue: "", }, &cli.StringFlag{ Name: "private-files-path", Usage: "The path to the a directory to serve as /private-files.", ConfigPath: []string{"server.private_files_path"}, EnvVars: []string{config.CONFIG_ENV_PREFIX + "_PRIVATE_FILES_PATH"}, DefaultValue: "", }, &cli.BoolFlag{ Name: "hide-support-links", Usage: "Hide the support links in the UI.", ConfigPath: []string{"server.ui.hide_support_links"}, EnvVars: []string{config.CONFIG_ENV_PREFIX + "_HIDE_SUPPORT_LINKS"}, DefaultValue: false, }, &cli.BoolFlag{ Name: "hide-api-tokens", Usage: "Hide the API tokens menu item in the UI.", ConfigPath: []string{"server.ui.hide_api_tokens"}, EnvVars: []string{config.CONFIG_ENV_PREFIX + "_HIDE_API_TOKENS"}, DefaultValue: false, }, &cli.BoolFlag{ Name: "enable-gravatar", Usage: "Enable Gravatar support in the UI.", ConfigPath: []string{"server.ui.enable_gravatar"}, EnvVars: []string{config.CONFIG_ENV_PREFIX + "_ENABLE_GRAVATAR"}, DefaultValue: true, }, &cli.StringSliceFlag{ Name: "icons", Usage: "File defining icons for use with templates and spaces, can be given multiple times.", ConfigPath: []string{"server.ui.icons"}, EnvVars: []string{config.CONFIG_ENV_PREFIX + "_ICONS"}, }, &cli.BoolFlag{ Name: "enable-builtin-icons", Usage: "Enable the use of the built-in icons.", ConfigPath: []string{"server.ui.enable_builtin_icons"}, EnvVars: []string{config.CONFIG_ENV_PREFIX + "_ENABLE_BUILTIN_ICONS"}, DefaultValue: true, }, &cli.StringFlag{ Name: "logo-url", Usage: "The URL to the logo to use in the UI.", ConfigPath: []string{"server.ui.logo_url"}, EnvVars: []string{config.CONFIG_ENV_PREFIX + "_LOGO_URL"}, DefaultValue: "", }, &cli.BoolFlag{ Name: "logo-invert", Usage: "Invert the logo colors in the UI for dark mode.", ConfigPath: []string{"server.ui.logo_invert"}, EnvVars: []string{config.CONFIG_ENV_PREFIX + "_LOGO_INVERT"}, DefaultValue: false, }, &cli.StringFlag{ Name: "cluster-key", Usage: "The shared cluster key.", ConfigPath: []string{"server.cluster.key"}, EnvVars: []string{config.CONFIG_ENV_PREFIX + "_CLUSTER_KEY"}, DefaultValue: "", }, &cli.StringFlag{ Name: "cluster-advertise-addr", Usage: "The address to advertise to other servers.", ConfigPath: []string{"server.cluster.advertise_addr"}, EnvVars: []string{config.CONFIG_ENV_PREFIX + "_CLUSTER_ADVERTISE_ADDR"}, DefaultValue: "", }, &cli.StringFlag{ Name: "cluster-bind-addr", Usage: "The address to bind to for cluster communication.", ConfigPath: []string{"server.cluster.bind_addr"}, EnvVars: []string{config.CONFIG_ENV_PREFIX + "_CLUSTER_BIND_ADDR"}, DefaultValue: "", }, &cli.StringSliceFlag{ Name: "cluster-peer", Usage: "The addresses of the other servers in the cluster, can be given multiple times.", ConfigPath: []string{"server.cluster.peers"}, EnvVars: []string{config.CONFIG_ENV_PREFIX + "_CLUSTER_PEERS"}, }, &cli.BoolFlag{ Name: "allow-leaf-nodes", Usage: "Allow leaf nodes to connect to the cluster.", ConfigPath: []string{"server.cluster.allow_leaf_nodes"}, EnvVars: []string{config.CONFIG_ENV_PREFIX + "_ALLOW_LEAF_NODES"}, DefaultValue: true, }, &cli.BoolFlag{ Name: "cluster-compression", Usage: "Enable compression for cluster communication.", ConfigPath: []string{"server.cluster.compression"}, EnvVars: []string{config.CONFIG_ENV_PREFIX + "_CLUSTER_COMPRESSION"}, DefaultValue: true, }, &cli.StringFlag{ Name: "origin-server", Usage: "The address of the origin server.", ConfigPath: []string{"server.origin.server"}, EnvVars: []string{config.CONFIG_ENV_PREFIX + "_ORIGIN_SERVER"}, DefaultValue: "", }, &cli.StringFlag{ Name: "origin-token", Usage: "The token to use for the origin server.", ConfigPath: []string{"server.origin.token"}, EnvVars: []string{config.CONFIG_ENV_PREFIX + "_ORIGIN_TOKEN"}, DefaultValue: "", }, &cli.BoolFlag{ Name: "enable-totp", Usage: "Enable TOTP for users.", ConfigPath: []string{"server.totp.enabled"}, EnvVars: []string{config.CONFIG_ENV_PREFIX + "_ENABLE_TOTP"}, DefaultValue: false, }, &cli.IntFlag{ Name: "totp-window", Usage: "The number of time steps (30 seconds) to check for TOTP codes.", ConfigPath: []string{"server.totp.window"}, EnvVars: []string{config.CONFIG_ENV_PREFIX + "_TOTP_WINDOW"}, DefaultValue: 1, }, &cli.StringFlag{ Name: "totp-issuer", Usage: "The issuer to use for TOTP codes.", ConfigPath: []string{"server.totp.issuer"}, EnvVars: []string{config.CONFIG_ENV_PREFIX + "_TOTP_ISSUER"}, DefaultValue: "Knot", }, &cli.StringFlag{ Name: "cert-file", Usage: "The file with the PEM encoded certificate to use for the server.", ConfigPath: []string{"server.tls.cert_file"}, EnvVars: []string{config.CONFIG_ENV_PREFIX + "_CERT_FILE"}, DefaultValue: "", }, &cli.StringFlag{ Name: "key-file", Usage: "The file with the PEM encoded key to use for the server.", ConfigPath: []string{"server.tls.key_file"}, EnvVars: []string{config.CONFIG_ENV_PREFIX + "_KEY_FILE"}, DefaultValue: "", }, &cli.BoolFlag{ Name: "use-tls", Usage: "Enable TLS.", ConfigPath: []string{"server.tls.use_tls"}, EnvVars: []string{config.CONFIG_ENV_PREFIX + "_USE_TLS"}, DefaultValue: true, }, &cli.BoolFlag{ Name: "agent-use-tls", Usage: "Enable TLS when talking to agents.", ConfigPath: []string{"server.tls.agent_use_tls"}, EnvVars: []string{config.CONFIG_ENV_PREFIX + "_AGENT_USE_TLS"}, DefaultValue: true, }, &cli.BoolFlag{ Name: "tls-skip-verify", Usage: "Skip TLS verification when talking to agents.", ConfigPath: []string{"tls.skip_verify"}, EnvVars: []string{config.CONFIG_ENV_PREFIX + "_TLS_SKIP_VERIFY"}, DefaultValue: true, }, &cli.StringFlag{ Name: "nomad-addr", Usage: "The address of the Nomad server.", ConfigPath: []string{"server.nomad.addr"}, EnvVars: []string{config.CONFIG_ENV_PREFIX + "_NOMAD_ADDR"}, DefaultValue: "http://127.0.0.1:4646", }, &cli.StringFlag{ Name: "nomad-token", Usage: "The token to use for Nomad API requests.", ConfigPath: []string{"server.nomad.token"}, EnvVars: []string{config.CONFIG_ENV_PREFIX + "_NOMAD_TOKEN"}, DefaultValue: "", }, &cli.BoolFlag{ Name: "mysql-enabled", Usage: "Enable MySQL database backend.", ConfigPath: []string{"server.mysql.enabled"}, EnvVars: []string{config.CONFIG_ENV_PREFIX + "_MYSQL_ENABLED"}, DefaultValue: false, }, &cli.StringFlag{ Name: "mysql-host", Usage: "The MySQL host to connect to.", ConfigPath: []string{"server.mysql.host"}, EnvVars: []string{config.CONFIG_ENV_PREFIX + "_MYSQL_HOST"}, DefaultValue: "localhost", }, &cli.IntFlag{ Name: "mysql-port", Usage: "The MySQL port to connect to.", ConfigPath: []string{"server.mysql.port"}, EnvVars: []string{config.CONFIG_ENV_PREFIX + "_MYSQL_PORT"}, DefaultValue: 3306, }, &cli.StringFlag{ Name: "mysql-user", Usage: "The MySQL user to connect as.", ConfigPath: []string{"server.mysql.user"}, EnvVars: []string{config.CONFIG_ENV_PREFIX + "_MYSQL_USER"}, DefaultValue: "root", }, &cli.StringFlag{ Name: "mysql-password", Usage: "The MySQL password to use.", ConfigPath: []string{"server.mysql.password"}, EnvVars: []string{config.CONFIG_ENV_PREFIX + "_MYSQL_PASSWORD"}, DefaultValue: "", }, &cli.StringFlag{ Name: "mysql-database", Usage: "The MySQL database to use.", ConfigPath: []string{"server.mysql.database"}, EnvVars: []string{config.CONFIG_ENV_PREFIX + "_MYSQL_DATABASE"}, DefaultValue: "knot", }, &cli.IntFlag{ Name: "mysql-connection-max-idle", Usage: "The maximum number of idle connections in the connection pool.", ConfigPath: []string{"server.mysql.connection_max_idle"}, EnvVars: []string{config.CONFIG_ENV_PREFIX + "_MYSQL_CONNECTION_MAX_IDLE"}, DefaultValue: 10, }, &cli.IntFlag{ Name: "mysql-connection-max-open", Usage: "The maximum number of open connections to the database.", ConfigPath: []string{"server.mysql.connection_max_open"}, EnvVars: []string{config.CONFIG_ENV_PREFIX + "_MYSQL_CONNECTION_MAX_OPEN"}, DefaultValue: 100, }, &cli.IntFlag{ Name: "mysql-connection-max-lifetime", Usage: "The maximum amount of time in minutes a connection may be reused.", ConfigPath: []string{"server.mysql.connection_max_lifetime"}, EnvVars: []string{config.CONFIG_ENV_PREFIX + "_MYSQL_CONNECTION_MAX_LIFETIME"}, DefaultValue: 5, }, &cli.BoolFlag{ Name: "badgerdb-enabled", Usage: "Enable BadgerDB database backend.", ConfigPath: []string{"server.badgerdb.enabled"}, EnvVars: []string{config.CONFIG_ENV_PREFIX + "_BADGERDB_ENABLED"}, DefaultValue: false, }, &cli.StringFlag{ Name: "badgerdb-path", Usage: "The path to the BadgerDB database.", ConfigPath: []string{"server.badgerdb.path"}, EnvVars: []string{config.CONFIG_ENV_PREFIX + "_BADGERDB_PATH"}, DefaultValue: "./badger", }, &cli.BoolFlag{ Name: "redis-enabled", Usage: "Enable Redis database backend.", ConfigPath: []string{"server.redis.enabled"}, EnvVars: []string{config.CONFIG_ENV_PREFIX + "_REDIS_ENABLED"}, DefaultValue: false, }, &cli.StringSliceFlag{ Name: "redis-hosts", Usage: "The redis server(s), can be specified multiple times.", ConfigPath: []string{"server.redis.hosts"}, EnvVars: []string{config.CONFIG_ENV_PREFIX + "_REDIS_HOSTS"}, DefaultValue: []string{"localhost:6379"}, }, &cli.StringFlag{ Name: "redis-password", Usage: "The password to use for the redis server.", ConfigPath: []string{"server.redis.password"}, EnvVars: []string{config.CONFIG_ENV_PREFIX + "_REDIS_PASSWORD"}, DefaultValue: "", }, &cli.IntFlag{ Name: "redis-db", Usage: "The redis database to use.", ConfigPath: []string{"server.redis.db"}, EnvVars: []string{config.CONFIG_ENV_PREFIX + "_REDIS_DB"}, DefaultValue: 0, }, &cli.StringFlag{ Name: "redis-master-name", Usage: "The name of the master to use for failover clients.", ConfigPath: []string{"server.redis.master_name"}, EnvVars: []string{config.CONFIG_ENV_PREFIX + "_REDIS_MASTER_NAME"}, DefaultValue: "", }, &cli.StringFlag{ Name: "redis-key-prefix", Usage: "The prefix to use for all keys in the redis database.", ConfigPath: []string{"server.redis.key_prefix"}, EnvVars: []string{config.CONFIG_ENV_PREFIX + "_REDIS_KEY_PREFIX"}, DefaultValue: "", }, &cli.StringFlag{ Name: "docker-host", Usage: "The Docker host to connect to.", ConfigPath: []string{"server.docker.host"}, EnvVars: []string{config.CONFIG_ENV_PREFIX + "_DOCKER_HOST"}, DefaultValue: "unix:///var/run/docker.sock", }, &cli.StringFlag{ Name: "podman-host", Usage: "The Podman host to connect to.", ConfigPath: []string{"server.podman.host"}, EnvVars: []string{config.CONFIG_ENV_PREFIX + "_PODMAN_HOST"}, DefaultValue: "unix:///var/run/podman.sock", }, &cli.BoolFlag{ Name: "dns-enabled", Usage: "Enable DNS server.", ConfigPath: []string{"server.dns.enabled"}, EnvVars: []string{config.CONFIG_ENV_PREFIX + "_DNS_ENABLED"}, DefaultValue: false, }, &cli.StringFlag{ Name: "dns-listen", Usage: "The address and port to listen on for DNS queries.", ConfigPath: []string{"server.dns.listen"}, EnvVars: []string{config.CONFIG_ENV_PREFIX + "_DNS_LISTEN"}, DefaultValue: ":3053", }, &cli.StringSliceFlag{ Name: "dns-records", Usage: "The DNS records to add, can be specified multiple times.", ConfigPath: []string{"server.dns.records"}, EnvVars: []string{config.CONFIG_ENV_PREFIX + "_DNS_RECORDS"}, }, &cli.IntFlag{ Name: "dns-default-ttl", Usage: "Default TTL for records if a TTL isn't explicitly set.", ConfigPath: []string{"server.dns.default_ttl"}, EnvVars: []string{config.CONFIG_ENV_PREFIX + "_DNS_DEFAULT_TTL"}, DefaultValue: 300, }, &cli.BoolFlag{ Name: "dns-enable-upstream", Usage: "Enable resolution of unknown domains by passing to upstream DNS servers.", ConfigPath: []string{"server.dns.enable_upstream"}, EnvVars: []string{config.CONFIG_ENV_PREFIX + "_DNS_ENABLE_UPSTREAM"}, DefaultValue: false, }, }, Run: func(ctx context.Context, cmd *cli.Command) error { cfg := buildServerConfig(cmd) listen := util.FixListenAddress(cfg.Listen) if cfg.AgentEndpoint == "" { log.Fatal().Msg("server: agent endpoint not given") } log.Info().Msgf("server: starting knot version: %s", build.Version) log.Info().Msgf("server: starting on: %s", listen) service.SetUserService(api_utils.NewApiUtilsUsers()) service.SetContainerService(containerHelper.NewContainerHelper()) middleware.Initialize() roles, err := database.GetInstance().GetRoles() if err != nil { log.Fatal().Msgf("server: failed to get roles: %s", err.Error()) } model.SetRoleCache(roles) if cmd.GetBool("dns-enabled") { dnsServerCfg := dns.DNSServerConfig{ ListenAddr: cmd.GetString("dns-listen"), Records: cmd.GetStringSlice("dns-records"), DefaultTTL: cmd.GetInt("dns-default-ttl"), } if cmd.GetBool("dns-enable-upstream") { dnsServerCfg.Resolver = dns.GetDefaultResolver() dnsServerCfg.Resolver.SetConfig(dns.ResolverConfig{ QueryTimeout: 2 * time.Second, EnableCache: true, MaxCacheTTL: 30, }) } dnsServer, err := dns.NewDNSServer(dnsServerCfg) if err != nil { log.Fatal().Err(err).Msg("Failed to create DNS server") } if err = dnsServer.Start(); err != nil { log.Fatal().Err(err).Msg("Failed to start DNS server") } defer dnsServer.Stop() } var router http.Handler wildcardDomain := cfg.WildcardDomain serverURL := cfg.URL u, err := url.Parse(serverURL) if err != nil { log.Fatal().Msg(err.Error()) } log.Debug().Msgf("Host: %s", u.Host) var tunnelServerUrl *url.URL = nil if cfg.TunnelServer != "" && cfg.ListenTunnel != "" { tunnelServerUrl, err = url.Parse(cfg.TunnelServer) if err != nil { log.Fatal().Msgf("Error parsing tunnel server URL: %v", err) } log.Debug().Msgf("Tunnel Server URL: %s", tunnelServerUrl.Host) } routes := http.NewServeMux() api.ApiRoutes(routes) proxy.Routes(routes, cfg) web.Routes(routes, cfg) appRoutes := web.HandlePageNotFound(routes) if cfg.ListenTunnel != "" { tunnel_server.Routes(routes) } if wildcardDomain != "" { log.Debug().Msgf("Wildcard Domain: %s", wildcardDomain) if host, _, err := net.SplitHostPort(wildcardDomain); err == nil { wildcardDomain = host } match := regexp.MustCompile("^[a-zA-Z0-9-]+" + strings.TrimLeft(strings.Replace(wildcardDomain, ".", "\\.", -1), "*") + "$") hostname := u.Host if host, _, err := net.SplitHostPort(hostname); err == nil { hostname = host } wildcardRoutes := proxy.PortRoutes() domainMux := http.NewServeMux() domainMux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { requestHost := r.Host if host, _, err := net.SplitHostPort(requestHost); err == nil { requestHost = host } if requestHost == hostname || (tunnelServerUrl != nil && requestHost == tunnelServerUrl.Host) { appRoutes.ServeHTTP(w, r) } else if match.MatchString(requestHost) { wildcardRoutes.ServeHTTP(w, r) } else { if r.URL.Path == "/health" { web.HandleHealthPage(w, r) } else { http.NotFound(w, r) } } }) router = domainMux } else { router = appRoutes } var tlsConfig *tls.Config = nil if cfg.TLS.UseTLS { log.Debug().Msg("server: using TLS") certFile := cfg.TLS.CertFile keyFile := cfg.TLS.KeyFile if certFile != "" && keyFile != "" { log.Info().Msgf("server: using cert file: %s", certFile) log.Info().Msgf("server: using key file: %s", keyFile) serverTLSCert, err := tls.LoadX509KeyPair(certFile, keyFile) if err != nil { log.Fatal().Msgf("Error loading certificate and key file: %v", err) } tlsConfig = &tls.Config{ Certificates: []tls.Certificate{serverTLSCert}, } } else { log.Info().Msg("server: generating self-signed certificate") // Build the list of domains to include in the cert var sslDomains []string serverURL := cfg.URL u, err := url.Parse(serverURL) if err != nil { log.Fatal().Msg(err.Error()) } hostname := u.Host if host, _, err := net.SplitHostPort(hostname); err == nil { hostname = host } sslDomains = append(sslDomains, hostname) if hostname != "localhost" { sslDomains = append(sslDomains, "localhost") } if tunnelServerUrl != nil { sslDomains = append(sslDomains, tunnelServerUrl.Host) } wildcardDomain := cfg.WildcardDomain if wildcardDomain != "" { if host, _, err := net.SplitHostPort(wildcardDomain); err == nil { wildcardDomain = host } sslDomains = append(sslDomains, wildcardDomain) } cert, key, err := util.GenerateCertificate(sslDomains, []net.IP{net.ParseIP("127.0.0.1")}) if err != nil { log.Fatal().Msgf("Error generating certificate and key: %v", err) } serverTLSCert, err := tls.X509KeyPair([]byte(cert), []byte(key)) if err != nil { log.Fatal().Msgf("Error generating server TLS cert: %v", err) } tlsConfig = &tls.Config{ Certificates: []tls.Certificate{serverTLSCert}, } } } cluster := cluster.NewCluster( cfg.Cluster.Key, cfg.Cluster.AdvertiseAddr, cfg.Cluster.BindAddr, routes, cfg.Cluster.Compression, cfg.Cluster.AllowLeafNodes, ) service.SetTransport(cluster) server := &http.Server{ Addr: listen, Handler: router, ReadTimeout: 10 * time.Second, WriteTimeout: 10 * time.Second, TLSConfig: tlsConfig, } go func() { for { if cfg.TLS.UseTLS { if err := server.ListenAndServeTLS("", ""); err != http.ErrServerClosed { log.Error().Err(err).Msgf("server: web server") } } else { if err := server.ListenAndServe(); err != http.ErrServerClosed { log.Error().Err(err).Msgf("server: web server") } } } }() c := make(chan os.Signal, 1) signal.Notify(c, os.Interrupt, syscall.SIGTERM) cluster.Start( cfg.Cluster.Peers, cfg.Origin.Server, cfg.Origin.Token, ) service.GetContainerService().CleanupOnBoot() agent_server.ListenAndServe(util.FixListenAddress(cfg.ListenAgent), tlsConfig) if cfg.ListenTunnel != "" { tunnel_server.ListenAndServe(util.FixListenAddress(cfg.ListenTunnel), tlsConfig) } audit.Log( model.AuditActorSystem, model.AuditActorTypeSystem, model.AuditEventSystemStart, "", &map[string]interface{}{ "build": build.Version, }, ) <-c cluster.Stop() ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel() server.Shutdown(ctx) fmt.Print("\r") log.Info().Msg("server: shutdown") return nil }, }
Functions ¶
This section is empty.
Types ¶
This section is empty.
Click to show internal directories.
Click to hide internal directories.