| package main
|
|
|
| import (
|
| "fmt"
|
| "log"
|
| "os"
|
| "strings"
|
|
|
| "github.com/henrygd/beszel"
|
| "github.com/henrygd/beszel/agent"
|
| "github.com/henrygd/beszel/agent/health"
|
| "github.com/henrygd/beszel/agent/utils"
|
| "github.com/spf13/pflag"
|
| "golang.org/x/crypto/ssh"
|
| )
|
|
|
|
|
| type cmdOptions struct {
|
| key string
|
| listen string
|
| hubURL string
|
| token string
|
| }
|
|
|
|
|
|
|
| func (opts *cmdOptions) parse() bool {
|
| subcommand := ""
|
| if len(os.Args) > 1 {
|
| subcommand = os.Args[1]
|
| }
|
|
|
|
|
| switch subcommand {
|
| case "health":
|
| err := health.Check()
|
| if err != nil {
|
| log.Fatal(err)
|
| }
|
| fmt.Print("ok")
|
| return true
|
| case "fingerprint":
|
| handleFingerprint()
|
| return true
|
| }
|
|
|
|
|
| pflag.StringVarP(&opts.key, "key", "k", "", "Public key(s) for SSH authentication")
|
| pflag.StringVarP(&opts.listen, "listen", "l", "", "Address or port to listen on")
|
| pflag.StringVarP(&opts.hubURL, "url", "u", "", "URL of the Beszel hub")
|
| pflag.StringVarP(&opts.token, "token", "t", "", "Token to use for authentication")
|
| chinaMirrors := pflag.BoolP("china-mirrors", "c", false, "Use mirror for update (gh.beszel.dev) instead of GitHub")
|
| version := pflag.BoolP("version", "v", false, "Show version information")
|
| help := pflag.BoolP("help", "h", false, "Show this help message")
|
|
|
|
|
| flagsToConvert := []string{"key", "listen", "url", "token"}
|
| for i, arg := range os.Args {
|
| for _, flag := range flagsToConvert {
|
| singleDash := "-" + flag
|
| doubleDash := "--" + flag
|
| if arg == singleDash {
|
| os.Args[i] = doubleDash
|
| break
|
| } else if strings.HasPrefix(arg, singleDash+"=") {
|
| os.Args[i] = doubleDash + arg[len(singleDash):]
|
| break
|
| }
|
| }
|
| }
|
|
|
| pflag.Usage = func() {
|
| builder := strings.Builder{}
|
| builder.WriteString("Usage: ")
|
| builder.WriteString(os.Args[0])
|
| builder.WriteString(" [command] [flags]\n")
|
| builder.WriteString("\nCommands:\n")
|
| builder.WriteString(" fingerprint View or reset the agent fingerprint\n")
|
| builder.WriteString(" health Check if the agent is running\n")
|
| builder.WriteString(" update Update to the latest version\n")
|
| builder.WriteString("\nFlags:\n")
|
| fmt.Print(builder.String())
|
| pflag.PrintDefaults()
|
| }
|
|
|
|
|
| pflag.Parse()
|
|
|
|
|
| switch {
|
| case *version:
|
| fmt.Println(beszel.AppName+"-agent", beszel.Version)
|
| return true
|
| case *help || subcommand == "help":
|
| pflag.Usage()
|
| return true
|
| case subcommand == "update":
|
| agent.Update(*chinaMirrors)
|
| return true
|
| }
|
|
|
|
|
| if opts.hubURL != "" {
|
| os.Setenv("HUB_URL", opts.hubURL)
|
| }
|
| if opts.token != "" {
|
| os.Setenv("TOKEN", opts.token)
|
| }
|
| return false
|
| }
|
|
|
|
|
| func (opts *cmdOptions) loadPublicKeys() ([]ssh.PublicKey, error) {
|
|
|
| if opts.key != "" {
|
| return agent.ParseKeys(opts.key)
|
| }
|
|
|
|
|
| if key, ok := utils.GetEnv("KEY"); ok && key != "" {
|
| return agent.ParseKeys(key)
|
| }
|
|
|
|
|
| keyFile, ok := utils.GetEnv("KEY_FILE")
|
| if !ok {
|
| return nil, fmt.Errorf("no key provided: must set -key flag, KEY env var, or KEY_FILE env var. Use 'beszel-agent help' for usage")
|
| }
|
|
|
| pubKey, err := os.ReadFile(keyFile)
|
| if err != nil {
|
| return nil, fmt.Errorf("failed to read key file: %w", err)
|
| }
|
| return agent.ParseKeys(string(pubKey))
|
| }
|
|
|
| func (opts *cmdOptions) getAddress() string {
|
| return agent.GetAddress(opts.listen)
|
| }
|
|
|
|
|
| func handleFingerprint() {
|
| subCmd := ""
|
| if len(os.Args) > 2 {
|
| subCmd = os.Args[2]
|
| }
|
|
|
| switch subCmd {
|
| case "", "view":
|
| dataDir, _ := agent.GetDataDir()
|
| fp := agent.GetFingerprint(dataDir, "", "")
|
| fmt.Println(fp)
|
| case "help", "-h", "--help":
|
| fmt.Print(fingerprintUsage())
|
| case "reset":
|
| dataDir, err := agent.GetDataDir()
|
| if err != nil {
|
| log.Fatal(err)
|
| }
|
| if err := agent.DeleteFingerprint(dataDir); err != nil {
|
| log.Fatal(err)
|
| }
|
| fmt.Println("Fingerprint reset. A new one will be generated on next start.")
|
| default:
|
| log.Fatalf("Unknown command: %q\n\n%s", subCmd, fingerprintUsage())
|
| }
|
| }
|
|
|
| func fingerprintUsage() string {
|
| return fmt.Sprintf("Usage: %s fingerprint [view|reset]\n\nCommands:\n view Print fingerprint (default)\n reset Reset saved fingerprint\n", os.Args[0])
|
| }
|
|
|
| func main() {
|
| var opts cmdOptions
|
| subcommandHandled := opts.parse()
|
|
|
| if subcommandHandled {
|
| return
|
| }
|
|
|
| var serverConfig agent.ServerOptions
|
| var err error
|
| serverConfig.Keys, err = opts.loadPublicKeys()
|
| if err != nil {
|
| log.Fatal("Failed to load public keys:", err)
|
| }
|
|
|
| addr := opts.getAddress()
|
| serverConfig.Addr = addr
|
| serverConfig.Network = agent.GetNetwork(addr)
|
|
|
| a, err := agent.NewAgent()
|
| if err != nil {
|
| log.Fatal("Failed to create agent: ", err)
|
| }
|
|
|
| if err := a.Start(serverConfig); err != nil {
|
| log.Fatal("Failed to start: ", err)
|
| }
|
| }
|
|
|