mirror of
				https://github.com/gregtwallace/apc-p15-tool.git
				synced 2025-11-03 23:57:33 +00:00 
			
		
		
		
	* connect to the ups web ui after install and verify the proper certificate is being served * rename `apchost` flag to `hostname` * separate ports to additional flags (`sshport` `sslport`) with sane defaults
		
			
				
	
	
		
			177 lines
		
	
	
	
		
			6.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			177 lines
		
	
	
	
		
			6.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package app
 | 
						|
 | 
						|
import (
 | 
						|
	"errors"
 | 
						|
	"fmt"
 | 
						|
	"os"
 | 
						|
 | 
						|
	"github.com/peterbourgon/ff/v4"
 | 
						|
)
 | 
						|
 | 
						|
var (
 | 
						|
	ErrExtraArgs = errors.New("extra args present")
 | 
						|
 | 
						|
	environmentVarPrefix = "APC_P15_TOOL"
 | 
						|
)
 | 
						|
 | 
						|
// keyCertPemCfg contains values common to subcommands that need to use key
 | 
						|
// and cert pem
 | 
						|
type keyCertPemCfg struct {
 | 
						|
	keyPemFilePath  *string
 | 
						|
	certPemFilePath *string
 | 
						|
	keyPem          *string
 | 
						|
	certPem         *string
 | 
						|
}
 | 
						|
 | 
						|
// app's config options from user
 | 
						|
type config struct {
 | 
						|
	debugLogging *bool
 | 
						|
	create       struct {
 | 
						|
		keyCertPemCfg
 | 
						|
		outFilePath    *string
 | 
						|
		outKeyFilePath *string
 | 
						|
	}
 | 
						|
	install struct {
 | 
						|
		keyCertPemCfg
 | 
						|
		hostname       *string
 | 
						|
		sshport        *int
 | 
						|
		fingerprint    *string
 | 
						|
		username       *string
 | 
						|
		password       *string
 | 
						|
		restartWebUI   *bool
 | 
						|
		webUISSLPort   *int
 | 
						|
		skipVerify     *bool
 | 
						|
		insecureCipher *bool
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// getConfig returns the app's configuration from either command line args,
 | 
						|
// or environment variables
 | 
						|
func (app *app) getConfig(args []string) error {
 | 
						|
	// make config
 | 
						|
	cfg := &config{}
 | 
						|
 | 
						|
	// commands:
 | 
						|
	// create
 | 
						|
	// install
 | 
						|
	// TODO:
 | 
						|
	// unpack (both key & key+cert)
 | 
						|
 | 
						|
	// apc-p15-tool -- root command
 | 
						|
	rootFlags := ff.NewFlagSet("apc-p15-tool")
 | 
						|
 | 
						|
	cfg.debugLogging = rootFlags.BoolLong("debug", "set this flag to enable additional debug logging messages and files")
 | 
						|
 | 
						|
	rootCmd := &ff.Command{
 | 
						|
		Name:  "apc-p15-tool",
 | 
						|
		Usage: "apc-p15-tool [FLAGS] SUBCOMMAND ...",
 | 
						|
		Flags: rootFlags,
 | 
						|
	}
 | 
						|
 | 
						|
	// create -- subcommand
 | 
						|
	createFlags := ff.NewFlagSet("create").SetParent(rootFlags)
 | 
						|
 | 
						|
	cfg.create.keyPemFilePath = createFlags.StringLong("keyfile", "", "path and filename of the key in pem format")
 | 
						|
	cfg.create.certPemFilePath = createFlags.StringLong("certfile", "", "path and filename of the certificate in pem format")
 | 
						|
	cfg.create.keyPem = createFlags.StringLong("keypem", "", "string of the key in pem format")
 | 
						|
	cfg.create.certPem = createFlags.StringLong("certpem", "", "string of the certificate in pem format")
 | 
						|
	cfg.create.outFilePath = createFlags.StringLong("outfile", createDefaultOutFilePath, "path and filename to write the key+cert p15 file to")
 | 
						|
	cfg.create.outKeyFilePath = createFlags.StringLong("outkeyfile", createDefaultOutKeyFilePath, "path and filename to write the key p15 file to")
 | 
						|
 | 
						|
	createCmd := &ff.Command{
 | 
						|
		Name:      "create",
 | 
						|
		Usage:     "apc-p15-tool create --keyfile key.pem --certfile cert.pem [--outfile apctool.p15]",
 | 
						|
		ShortHelp: "create an apc p15 file from the specified key and cert pem files",
 | 
						|
		Flags:     createFlags,
 | 
						|
		Exec:      app.cmdCreate,
 | 
						|
	}
 | 
						|
 | 
						|
	rootCmd.Subcommands = append(rootCmd.Subcommands, createCmd)
 | 
						|
 | 
						|
	// install -- subcommand
 | 
						|
	installFlags := ff.NewFlagSet("install").SetParent(rootFlags)
 | 
						|
 | 
						|
	cfg.install.keyPemFilePath = installFlags.StringLong("keyfile", "", "path and filename of the key in pem format")
 | 
						|
	cfg.install.certPemFilePath = installFlags.StringLong("certfile", "", "path and filename of the certificate in pem format")
 | 
						|
	cfg.install.keyPem = installFlags.StringLong("keypem", "", "string of the key in pem format")
 | 
						|
	cfg.install.certPem = installFlags.StringLong("certpem", "", "string of the certificate in pem format")
 | 
						|
	cfg.install.hostname = installFlags.StringLong("hostname", "", "hostname of the apc ups to install the certificate on")
 | 
						|
	cfg.install.sshport = installFlags.IntLong("sshport", 22, "apc ups ssh port number")
 | 
						|
	cfg.install.fingerprint = installFlags.StringLong("fingerprint", "", "the SHA256 fingerprint value of the ups' ssh server")
 | 
						|
	cfg.install.username = installFlags.StringLong("username", "", "username to login to the apc ups")
 | 
						|
	cfg.install.password = installFlags.StringLong("password", "", "password to login to the apc ups")
 | 
						|
	cfg.install.restartWebUI = installFlags.BoolLong("restartwebui", "some devices may need a webui restart to begin using the new cert, enabling this option sends the restart command after the p15 is installed")
 | 
						|
	cfg.install.webUISSLPort = installFlags.IntLong("sslport", 443, "apc ups ssl webui port number")
 | 
						|
	cfg.install.skipVerify = installFlags.BoolLong("skipverify", "the tool will try to connect to the UPS web UI to verify install success; this flag disables that check")
 | 
						|
	cfg.install.insecureCipher = installFlags.BoolLong("insecurecipher", "allows the use of insecure ssh ciphers (NOT recommended)")
 | 
						|
 | 
						|
	installCmd := &ff.Command{
 | 
						|
		Name:      "install",
 | 
						|
		Usage:     "apc-p15-tool install --keyfile key.pem --certfile cert.pem --hostname example.com --fingerprint 123abc --username apc --password test",
 | 
						|
		ShortHelp: "install the specified key and cert pem files on an apc ups (they will be converted to a comaptible p15 file)",
 | 
						|
		Flags:     installFlags,
 | 
						|
		Exec:      app.cmdInstall,
 | 
						|
	}
 | 
						|
 | 
						|
	rootCmd.Subcommands = append(rootCmd.Subcommands, installCmd)
 | 
						|
 | 
						|
	// set cfg & parse
 | 
						|
	app.config = cfg
 | 
						|
	app.cmd = rootCmd
 | 
						|
	err := app.cmd.Parse(args[1:], ff.WithEnvVarPrefix(environmentVarPrefix))
 | 
						|
	if err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
// GetPemBytes returns the key and cert pem bytes as specified in keyCertPemCfg
 | 
						|
// or an error if it cant get the bytes of both
 | 
						|
func (kcCfg *keyCertPemCfg) GetPemBytes(subcommand string) (keyPem, certPem []byte, err error) {
 | 
						|
	// key pem (from arg or file)
 | 
						|
	if kcCfg.keyPem != nil && *kcCfg.keyPem != "" {
 | 
						|
		// error if filename is also set
 | 
						|
		if kcCfg.keyPemFilePath != nil && *kcCfg.keyPemFilePath != "" {
 | 
						|
			return nil, nil, fmt.Errorf("%s: failed, both key pem and key file specified", subcommand)
 | 
						|
		}
 | 
						|
 | 
						|
		// use pem
 | 
						|
		keyPem = []byte(*kcCfg.keyPem)
 | 
						|
	} else {
 | 
						|
		// pem wasn't specified, try reading file
 | 
						|
		if kcCfg.keyPemFilePath == nil || *kcCfg.keyPemFilePath == "" {
 | 
						|
			return nil, nil, fmt.Errorf("%s: failed, neither key pem nor key file specified", subcommand)
 | 
						|
		}
 | 
						|
 | 
						|
		// read file to get pem
 | 
						|
		keyPem, err = os.ReadFile(*kcCfg.keyPemFilePath)
 | 
						|
		if err != nil {
 | 
						|
			return nil, nil, fmt.Errorf("%s: failed to read key file (%w)", subcommand, err)
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	// cert pem (repeat same process)
 | 
						|
	if kcCfg.certPem != nil && *kcCfg.certPem != "" {
 | 
						|
		// error if filename is also set
 | 
						|
		if kcCfg.certPemFilePath != nil && *kcCfg.certPemFilePath != "" {
 | 
						|
			return nil, nil, fmt.Errorf("%s: failed, both cert pem and cert file specified", subcommand)
 | 
						|
		}
 | 
						|
 | 
						|
		// use pem
 | 
						|
		certPem = []byte(*kcCfg.certPem)
 | 
						|
	} else {
 | 
						|
		// pem wasn't specified, try reading file
 | 
						|
		if kcCfg.certPemFilePath == nil || *kcCfg.certPemFilePath == "" {
 | 
						|
			return nil, nil, fmt.Errorf("%s: failed, neither cert pem nor cert file specified", subcommand)
 | 
						|
		}
 | 
						|
 | 
						|
		// read file to get pem
 | 
						|
		certPem, err = os.ReadFile(*kcCfg.certPemFilePath)
 | 
						|
		if err != nil {
 | 
						|
			return nil, nil, fmt.Errorf("%s: failed to read cert file (%w)", subcommand, err)
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return keyPem, certPem, nil
 | 
						|
}
 |