mirror of
https://github.com/gregtwallace/apc-p15-tool.git
synced 2025-01-22 08:14:08 +00:00
install: add web ui cert verification
* 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
This commit is contained in:
parent
c22447b0c2
commit
1cd9916a17
3 changed files with 63 additions and 7 deletions
|
@ -145,7 +145,7 @@ disk. It instead installs the files directly on the NMC. Logic
|
||||||
automatically deduces if the device is an NMC2 or NMC3 and performs
|
automatically deduces if the device is an NMC2 or NMC3 and performs
|
||||||
the appropriate installation steps.
|
the appropriate installation steps.
|
||||||
|
|
||||||
e.g. `./apc-p15-tool install --keyfile ./apckey.pem --certfile ./apccert.pem --apchost myapc.example.com:22 --username apc --password someSecret --fingerprint 123abc`
|
e.g. `./apc-p15-tool install --keyfile ./apckey.pem --certfile ./apccert.pem --hostname myapc.example.com --username apc --password someSecret --fingerprint 123abc`
|
||||||
|
|
||||||
## Note About Install Automation
|
## Note About Install Automation
|
||||||
|
|
||||||
|
|
|
@ -2,9 +2,14 @@ package app
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"apc-p15-tool/pkg/apcssh"
|
"apc-p15-tool/pkg/apcssh"
|
||||||
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
|
"crypto/tls"
|
||||||
|
"encoding/pem"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// cmdInstall is the app's command to create apc p15 file content from key and cert
|
// cmdInstall is the app's command to create apc p15 file content from key and cert
|
||||||
|
@ -36,7 +41,9 @@ func (app *app) cmdInstall(cmdCtx context.Context, args []string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// host to install on must be specified
|
// host to install on must be specified
|
||||||
if app.config.install.hostAndPort == nil || *app.config.install.hostAndPort == "" {
|
if app.config.install.hostname == nil || *app.config.install.hostname == "" ||
|
||||||
|
app.config.install.sshport == nil || *app.config.install.sshport == 0 {
|
||||||
|
|
||||||
return errors.New("install: failed, apc host not specified")
|
return errors.New("install: failed, apc host not specified")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,7 +62,7 @@ func (app *app) cmdInstall(cmdCtx context.Context, args []string) error {
|
||||||
|
|
||||||
// make APC SSH client
|
// make APC SSH client
|
||||||
cfg := &apcssh.Config{
|
cfg := &apcssh.Config{
|
||||||
Hostname: *app.config.install.hostAndPort,
|
Hostname: *app.config.install.hostname + ":" + strconv.Itoa(*app.config.install.sshport),
|
||||||
Username: *app.config.install.username,
|
Username: *app.config.install.username,
|
||||||
Password: *app.config.install.password,
|
Password: *app.config.install.password,
|
||||||
ServerFingerprint: *app.config.install.fingerprint,
|
ServerFingerprint: *app.config.install.fingerprint,
|
||||||
|
@ -75,7 +82,7 @@ func (app *app) cmdInstall(cmdCtx context.Context, args []string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// installed
|
// installed
|
||||||
app.stdLogger.Printf("install: apc p15 file installed on %s", *app.config.install.hostAndPort)
|
app.stdLogger.Printf("install: apc p15 file installed on %s", *app.config.install.hostname)
|
||||||
|
|
||||||
// restart UPS webUI
|
// restart UPS webUI
|
||||||
if app.config.install.restartWebUI != nil && *app.config.install.restartWebUI {
|
if app.config.install.restartWebUI != nil && *app.config.install.restartWebUI {
|
||||||
|
@ -89,5 +96,48 @@ func (app *app) cmdInstall(cmdCtx context.Context, args []string) error {
|
||||||
app.stdLogger.Println("install: sent webui restart command")
|
app.stdLogger.Println("install: sent webui restart command")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// check the new certificate is installed
|
||||||
|
if app.config.install.skipVerify != nil && !*app.config.install.skipVerify &&
|
||||||
|
app.config.install.webUISSLPort != nil && *app.config.install.webUISSLPort != 0 {
|
||||||
|
|
||||||
|
app.stdLogger.Println("install: attempting to verify certificate install...")
|
||||||
|
|
||||||
|
// sleep for UPS to finish anything it might be doing
|
||||||
|
time.Sleep(5 * time.Second)
|
||||||
|
|
||||||
|
// if UPS web UI was restarted, sleep longer
|
||||||
|
if app.config.install.restartWebUI != nil && *app.config.install.restartWebUI {
|
||||||
|
app.stdLogger.Println("install: waiting for ups webui restart...")
|
||||||
|
time.Sleep(25 * time.Second)
|
||||||
|
}
|
||||||
|
|
||||||
|
// connect to the web UI to get the current certificate
|
||||||
|
conf := &tls.Config{
|
||||||
|
InsecureSkipVerify: true,
|
||||||
|
}
|
||||||
|
conn, err := tls.Dial("tcp", *app.config.install.hostname+":"+strconv.Itoa(*app.config.install.webUISSLPort), conf)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("install: failed to dial webui for verification (%s)", err)
|
||||||
|
}
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
|
// get top cert
|
||||||
|
leafCert := conn.ConnectionState().PeerCertificates[0]
|
||||||
|
if leafCert == nil {
|
||||||
|
return fmt.Errorf("install: failed to get web ui leaf cert for verification (%s)", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// convert pem to DER for comparison
|
||||||
|
pemBlock, _ := pem.Decode(certPem)
|
||||||
|
|
||||||
|
// verify cert is the correct one
|
||||||
|
certVerified := bytes.Equal(leafCert.Raw, pemBlock.Bytes)
|
||||||
|
if !certVerified {
|
||||||
|
return errors.New("install: web ui leaf cert does not match new cert")
|
||||||
|
}
|
||||||
|
|
||||||
|
app.stdLogger.Println("install: ups web ui cert verified")
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,11 +33,14 @@ type config struct {
|
||||||
}
|
}
|
||||||
install struct {
|
install struct {
|
||||||
keyCertPemCfg
|
keyCertPemCfg
|
||||||
hostAndPort *string
|
hostname *string
|
||||||
|
sshport *int
|
||||||
fingerprint *string
|
fingerprint *string
|
||||||
username *string
|
username *string
|
||||||
password *string
|
password *string
|
||||||
restartWebUI *bool
|
restartWebUI *bool
|
||||||
|
webUISSLPort *int
|
||||||
|
skipVerify *bool
|
||||||
insecureCipher *bool
|
insecureCipher *bool
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -92,16 +95,19 @@ func (app *app) getConfig(args []string) error {
|
||||||
cfg.install.certPemFilePath = installFlags.StringLong("certfile", "", "path and filename of the certificate 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.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.certPem = installFlags.StringLong("certpem", "", "string of the certificate in pem format")
|
||||||
cfg.install.hostAndPort = installFlags.StringLong("apchost", "", "hostname:port of the apc ups to install the certificate on")
|
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.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.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.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.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)")
|
cfg.install.insecureCipher = installFlags.BoolLong("insecurecipher", "allows the use of insecure ssh ciphers (NOT recommended)")
|
||||||
|
|
||||||
installCmd := &ff.Command{
|
installCmd := &ff.Command{
|
||||||
Name: "install",
|
Name: "install",
|
||||||
Usage: "apc-p15-tool install --keyfile key.pem --certfile cert.pem --apchost example.com:22 --fingerprint 123abc --username apc --password test",
|
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)",
|
ShortHelp: "install the specified key and cert pem files on an apc ups (they will be converted to a comaptible p15 file)",
|
||||||
Flags: installFlags,
|
Flags: installFlags,
|
||||||
Exec: app.cmdInstall,
|
Exec: app.cmdInstall,
|
||||||
|
|
Loading…
Reference in a new issue