Files
threshold/internal/cmd/commands/credentialscmd/ssh-private-key_funcs.go
shadowdaoandClaude Sonnet 5.5 962d708fdd Rename Go module paths to repo.anhonesthost.net/CyberCoveLLC/threshold
Rename root, api and sdk module paths (imports, go.mod, go_package,
ldflags, build scripts, buf configs) and regenerate protobuf/gateway
code with the project's pinned toolchain. Wire identifiers unchanged.
api gets a local replace for sdk; root/api/sdk require v0.1.0.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-12 10:33:41 -07:00

179 lines
6.0 KiB
Go

// Copyright IBM Corp. 2020, 2026
// SPDX-License-Identifier: BUSL-1.1
package credentialscmd
import (
"errors"
"fmt"
"os"
"strings"
"repo.anhonesthost.net/CyberCoveLLC/threshold/api/credentials"
"repo.anhonesthost.net/CyberCoveLLC/threshold/internal/cmd/base"
"github.com/hashicorp/go-secure-stdlib/parseutil"
"github.com/hashicorp/go-secure-stdlib/password"
"golang.org/x/crypto/ssh"
)
func init() {
extraSshPrivateKeyFlagsFunc = extraSshPrivateKeyFlagsFuncImpl
extraSshPrivateKeyActionsFlagsMapFunc = extraSshPrivateKeyActionsFlagsMapFuncImpl
extraSshPrivateKeyFlagsHandlingFunc = extraSshPrivateKeyFlagHandlingFuncImpl
}
type extraSshPrivateKeyCmdVars struct {
flagUsername string
flagPrivateKey string
flagPrivateKeyPassphrase string
}
func extraSshPrivateKeyActionsFlagsMapFuncImpl() map[string][]string {
flags := map[string][]string{
"create": {
usernameFlagName,
privateKeyFlagName,
privateKeyPassphraseFlagName,
},
}
flags["update"] = flags["create"]
return flags
}
func extraSshPrivateKeyFlagsFuncImpl(c *SshPrivateKeyCommand, set *base.FlagSets, _ *base.FlagSet) {
f := set.NewFlagSet("SSH Private Key Credential Options")
for _, name := range flagsSshPrivateKeyMap[c.Func] {
switch name {
case usernameFlagName:
f.StringVar(&base.StringVar{
Name: usernameFlagName,
Target: &c.flagUsername,
Usage: "The username associated with the credential.",
})
case privateKeyFlagName:
f.StringVar(&base.StringVar{
Name: privateKeyFlagName,
Target: &c.flagPrivateKey,
Usage: "The SSH private key associated with the credential. This can refer to a file on disk (file://) from which the value will be read or an env var (env://) from which the value will be read.",
})
case privateKeyPassphraseFlagName:
f.StringVar(&base.StringVar{
Name: privateKeyPassphraseFlagName,
Target: &c.flagPrivateKeyPassphrase,
Usage: "The passphrase associated with the SSH private key. This value is ignored if the private key does not require a passphrase or if no private key is supplied. This can refer to a file on disk (file://) from which the value will be read, or an env var (env://) from which the value will be read. Or, if left empty, if the key requires a passphrase it can be entered manually.",
})
}
}
}
func extraSshPrivateKeyFlagHandlingFuncImpl(c *SshPrivateKeyCommand, _ *base.FlagSets, opts *[]credentials.Option) bool {
switch c.flagUsername {
case "":
default:
*opts = append(*opts, credentials.WithSshPrivateKeyCredentialUsername(c.flagUsername))
}
// If private key not set (e.g. just a username update) then don't check
// either private key or passphrase
if c.flagPrivateKey == "" {
return true
}
// First parse private key
privateKey, err := parseutil.MustParsePath(c.flagPrivateKey)
switch {
case err == nil:
case errors.Is(err, parseutil.ErrNotAUrl), errors.Is(err, parseutil.ErrNotParsed):
c.UI.Error("Private key flag must be used with env:// or file:// syntax")
return false
default:
c.UI.Error(fmt.Sprintf("Error parsing private key flag: %v", err))
return false
}
// Now validate it
_, err = ssh.ParsePrivateKey([]byte(privateKey))
// If nil, parsed successfully without passphrase, ignore passphrase flag
if err == nil {
if c.flagPrivateKeyPassphrase != "" {
c.UI.Warn("Ignoring private key passphrase as private key is not encrypted.")
}
*opts = append(*opts, credentials.WithSshPrivateKeyCredentialPrivateKey(privateKey))
return true
}
// If an error but it's not a passphrase missing error, send the error back
if err.Error() != (&ssh.PassphraseMissingError{}).Error() {
c.UI.Error(fmt.Sprintf("Error parsing private key: %v", err))
return false
}
// Passphrase required
var privateKeyPassphrase string
switch c.flagPrivateKeyPassphrase {
case "":
fmt.Print("Please enter the passphrase for the private key (it will be hidden): ")
privateKeyPassphrase, err = password.Read(os.Stdin)
fmt.Print("\n")
if err != nil {
c.UI.Error(fmt.Sprintf("An error occurred attempting to read the passphrase. The raw error message is shown below but usually this is because you attempted to pipe a value into the command or you are executing outside of a terminal (TTY). The raw error was:\n\n%s", err.Error()))
return false
}
privateKeyPassphrase = strings.TrimSpace(privateKeyPassphrase)
default:
privateKeyPassphrase, err = parseutil.MustParsePath(c.flagPrivateKeyPassphrase)
switch {
case err == nil:
case errors.Is(err, parseutil.ErrNotParsed):
c.UI.Error("Private key passphrase flag must be used with env:// or file:// syntax")
return false
default:
c.UI.Error(fmt.Sprintf("Error parsing private key passphrase flag: %v", err))
return false
}
}
if _, err = ssh.ParsePrivateKeyWithPassphrase([]byte(privateKey), []byte(privateKeyPassphrase)); err != nil {
c.UI.Error(fmt.Sprintf("Error parsing private key passphrase: %v", err))
return false
}
*opts = append(*opts,
credentials.WithSshPrivateKeyCredentialPrivateKey(privateKey),
credentials.WithSshPrivateKeyCredentialPrivateKeyPassphrase(privateKeyPassphrase),
)
return true
}
func (c *SshPrivateKeyCommand) extraSshPrivateKeyHelpFunc(_ map[string]func() string) string {
var helpStr string
switch c.Func {
case "create":
helpStr = base.WrapForHelpText([]string{
"Usage: threshold credentials create ssh-private-key -credential-store-id [options] [args]",
"",
" Create an SSH private key credential. Example:",
"",
` $ threshold credentials create ssh-private-key -credential-store-id csvlt_1234567890 -username user -private-key file:///home/user/.ssh/id_ed25519`,
"",
"",
})
case "update":
helpStr = base.WrapForHelpText([]string{
"Usage: threshold credentials update ssh-private-key [options] [args]",
"",
" Update an SSH private key credential given its ID. Example:",
"",
` $ threshold credentials update ssh-private-key -id clvlt_1234567890 -name devops -description "For DevOps usage"`,
"",
"",
})
}
return helpStr + c.Flags().Help()
}