2024-02-02 23:35:21 +00:00
package app
import (
2024-06-07 02:51:12 +00:00
"apc-p15-tool/pkg/apcssh"
2024-09-17 22:44:34 +00:00
"bytes"
2024-02-02 23:35:21 +00:00
"context"
2024-09-17 22:44:34 +00:00
"crypto/tls"
"encoding/pem"
2024-02-02 23:35:21 +00:00
"errors"
"fmt"
2024-09-17 22:44:34 +00:00
"strconv"
"time"
2024-02-02 23:35:21 +00:00
)
// cmdInstall is the app's command to create apc p15 file content from key and cert
// pem files and upload the p15 to the specified APC UPS
func ( app * app ) cmdInstall ( cmdCtx context . Context , args [ ] string ) error {
// extra args == error
if len ( args ) != 0 {
return fmt . Errorf ( "install: failed, %w (%d)" , ErrExtraArgs , len ( args ) )
}
// must have username
if app . config . install . username == nil || * app . config . install . username == "" {
return errors . New ( "install: failed, username not specified" )
}
// must have password
if app . config . install . password == nil || * app . config . install . password == "" {
return errors . New ( "install: failed, password not specified" )
}
// must have fingerprint
if app . config . install . fingerprint == nil || * app . config . install . fingerprint == "" {
return errors . New ( "install: failed, fingerprint not specified" )
}
2024-02-02 23:35:22 +00:00
keyPem , certPem , err := app . config . install . keyCertPemCfg . GetPemBytes ( "install" )
if err != nil {
return err
2024-02-02 23:35:21 +00:00
}
// host to install on must be specified
2024-09-17 22:44:34 +00:00
if app . config . install . hostname == nil || * app . config . install . hostname == "" ||
app . config . install . sshport == nil || * app . config . install . sshport == 0 {
2024-02-02 23:35:21 +00:00
return errors . New ( "install: failed, apc host not specified" )
}
// validation done
// make p15 file
2024-06-07 02:51:12 +00:00
keyP15 , keyCertP15 , err := app . pemToAPCP15 ( keyPem , certPem , "install" )
2024-02-02 23:35:21 +00:00
if err != nil {
return err
}
2024-06-07 02:51:13 +00:00
// log warning if insecure cipher
if app . config . install . insecureCipher != nil && * app . config . install . insecureCipher {
app . stdLogger . Println ( "WARNING: install: insecure ciphers are enabled (--insecurecipher). SSH with an insecure cipher is NOT secure and should NOT be used." )
}
2024-06-07 02:51:12 +00:00
// make APC SSH client
cfg := & apcssh . Config {
2024-09-17 22:44:34 +00:00
Hostname : * app . config . install . hostname + ":" + strconv . Itoa ( * app . config . install . sshport ) ,
2024-06-07 02:51:12 +00:00
Username : * app . config . install . username ,
Password : * app . config . install . password ,
ServerFingerprint : * app . config . install . fingerprint ,
InsecureCipher : * app . config . install . insecureCipher ,
2024-02-02 23:35:21 +00:00
}
2024-06-07 02:51:12 +00:00
client , err := apcssh . New ( cfg )
2024-02-02 23:35:21 +00:00
if err != nil {
return fmt . Errorf ( "install: failed to connect to host (%w)" , err )
}
2024-06-19 01:30:38 +00:00
app . stdLogger . Println ( "install: connected to ups ssh, installing ssl key and cert..." )
2024-02-02 23:35:21 +00:00
2024-06-07 02:51:12 +00:00
// install SSL Cert
2024-06-07 02:51:12 +00:00
err = client . InstallSSLCert ( keyP15 , certPem , keyCertP15 )
2024-02-02 23:35:21 +00:00
if err != nil {
2024-06-19 23:56:16 +00:00
return fmt . Errorf ( "install: %w" , err )
2024-02-02 23:35:21 +00:00
}
2024-02-05 23:25:55 +00:00
// installed
2024-09-17 22:44:34 +00:00
app . stdLogger . Printf ( "install: apc p15 file installed on %s" , * app . config . install . hostname )
2024-02-02 23:35:21 +00:00
2024-02-05 23:25:55 +00:00
// restart UPS webUI
if app . config . install . restartWebUI != nil && * app . config . install . restartWebUI {
app . stdLogger . Println ( "install: sending restart command" )
2024-06-07 02:51:12 +00:00
err = client . RestartWebUI ( )
2024-02-05 23:25:55 +00:00
if err != nil {
return fmt . Errorf ( "install: failed to send webui restart command (%w)" , err )
}
app . stdLogger . Println ( "install: sent webui restart command" )
}
2024-09-17 22:44:34 +00:00
// 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" )
}
2024-02-02 23:35:21 +00:00
return nil
}