mirror of
https://github.com/optim-enterprises-bv/vault.git
synced 2025-11-03 03:58:01 +00:00
Fixes for SSH command CA mode (#3922)
* Add `valid-principals` flag to SSH command CA mode options * Fix SSH command CA mode host certificate validation
This commit is contained in:
@@ -38,6 +38,7 @@ type SSHCommand struct {
|
|||||||
flagPrivateKeyPath string
|
flagPrivateKeyPath string
|
||||||
flagHostKeyMountPoint string
|
flagHostKeyMountPoint string
|
||||||
flagHostKeyHostnames string
|
flagHostKeyHostnames string
|
||||||
|
flagValidPrincipals string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *SSHCommand) Synopsis() string {
|
func (c *SSHCommand) Synopsis() string {
|
||||||
@@ -191,6 +192,16 @@ func (c *SSHCommand) Flags() *FlagSets {
|
|||||||
"list of values.",
|
"list of values.",
|
||||||
})
|
})
|
||||||
|
|
||||||
|
f.StringVar(&StringVar{
|
||||||
|
Name: "valid-principals",
|
||||||
|
Target: &c.flagValidPrincipals,
|
||||||
|
Default: "",
|
||||||
|
EnvVar: "",
|
||||||
|
Completion: complete.PredictAnything,
|
||||||
|
Usage: "List of valid principal names to include in the generated " +
|
||||||
|
"user certificate. This is specified as a comma-separated list of values.",
|
||||||
|
})
|
||||||
|
|
||||||
return set
|
return set
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -232,7 +243,7 @@ func (c *SSHCommand) Run(args []string) int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Extract the username and IP.
|
// Extract the username and IP.
|
||||||
username, ip, err := c.userAndIP(args[0])
|
username, hostname, ip, err := c.userHostAndIP(args[0])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.UI.Error(fmt.Sprintf("Error parsing user and IP: %s", err))
|
c.UI.Error(fmt.Sprintf("Error parsing user and IP: %s", err))
|
||||||
return 1
|
return 1
|
||||||
@@ -317,7 +328,7 @@ func (c *SSHCommand) Run(args []string) int {
|
|||||||
|
|
||||||
switch strings.ToLower(c.flagMode) {
|
switch strings.ToLower(c.flagMode) {
|
||||||
case ssh.KeyTypeCA:
|
case ssh.KeyTypeCA:
|
||||||
return c.handleTypeCA(username, ip, sshArgs)
|
return c.handleTypeCA(username, hostname, ip, sshArgs)
|
||||||
case ssh.KeyTypeOTP:
|
case ssh.KeyTypeOTP:
|
||||||
return c.handleTypeOTP(username, ip, sshArgs)
|
return c.handleTypeOTP(username, ip, sshArgs)
|
||||||
case ssh.KeyTypeDynamic:
|
case ssh.KeyTypeDynamic:
|
||||||
@@ -329,7 +340,7 @@ func (c *SSHCommand) Run(args []string) int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// handleTypeCA is used to handle SSH logins using the "CA" key type.
|
// handleTypeCA is used to handle SSH logins using the "CA" key type.
|
||||||
func (c *SSHCommand) handleTypeCA(username, ip string, sshArgs []string) int {
|
func (c *SSHCommand) handleTypeCA(username, hostname, ip string, sshArgs []string) int {
|
||||||
// Read the key from disk
|
// Read the key from disk
|
||||||
publicKey, err := ioutil.ReadFile(c.flagPublicKeyPath)
|
publicKey, err := ioutil.ReadFile(c.flagPublicKeyPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -340,12 +351,17 @@ func (c *SSHCommand) handleTypeCA(username, ip string, sshArgs []string) int {
|
|||||||
|
|
||||||
sshClient := c.client.SSHWithMountPoint(c.flagMountPoint)
|
sshClient := c.client.SSHWithMountPoint(c.flagMountPoint)
|
||||||
|
|
||||||
|
var principals = username
|
||||||
|
if c.flagValidPrincipals != "" {
|
||||||
|
principals = c.flagValidPrincipals
|
||||||
|
}
|
||||||
|
|
||||||
// Attempt to sign the public key
|
// Attempt to sign the public key
|
||||||
secret, err := sshClient.SignKey(c.flagRole, map[string]interface{}{
|
secret, err := sshClient.SignKey(c.flagRole, map[string]interface{}{
|
||||||
// WARNING: publicKey is []byte, which is b64 encoded on JSON upload. We
|
// WARNING: publicKey is []byte, which is b64 encoded on JSON upload. We
|
||||||
// have to convert it to a string. SV lost many hours to this...
|
// have to convert it to a string. SV lost many hours to this...
|
||||||
"public_key": string(publicKey),
|
"public_key": string(publicKey),
|
||||||
"valid_principals": username,
|
"valid_principals": principals,
|
||||||
"cert_type": "user",
|
"cert_type": "user",
|
||||||
|
|
||||||
// TODO: let the user configure these. In the interim, if users want to
|
// TODO: let the user configure these. In the interim, if users want to
|
||||||
@@ -436,7 +452,7 @@ func (c *SSHCommand) handleTypeCA(username, ip string, sshArgs []string) int {
|
|||||||
"-i", signedPublicKeyPath,
|
"-i", signedPublicKeyPath,
|
||||||
"-o UserKnownHostsFile=" + userKnownHostsFile,
|
"-o UserKnownHostsFile=" + userKnownHostsFile,
|
||||||
"-o StrictHostKeyChecking=" + strictHostKeyChecking,
|
"-o StrictHostKeyChecking=" + strictHostKeyChecking,
|
||||||
username + "@" + ip,
|
username + "@" + hostname,
|
||||||
}, sshArgs...)
|
}, sshArgs...)
|
||||||
|
|
||||||
cmd := exec.Command("ssh", args...)
|
cmd := exec.Command("ssh", args...)
|
||||||
@@ -709,7 +725,7 @@ func (c *SSHCommand) defaultRole(mountPoint, ip string) (string, error) {
|
|||||||
|
|
||||||
// userAndIP takes an argument in the format foo@1.2.3.4 and separates the IP
|
// userAndIP takes an argument in the format foo@1.2.3.4 and separates the IP
|
||||||
// and user parts, returning any errors.
|
// and user parts, returning any errors.
|
||||||
func (c *SSHCommand) userAndIP(s string) (string, string, error) {
|
func (c *SSHCommand) userHostAndIP(s string) (string, string, string, error) {
|
||||||
// split the parameter username@ip
|
// split the parameter username@ip
|
||||||
input := strings.Split(s, "@")
|
input := strings.Split(s, "@")
|
||||||
var username, address string
|
var username, address string
|
||||||
@@ -722,22 +738,22 @@ func (c *SSHCommand) userAndIP(s string) (string, string, error) {
|
|||||||
case 1:
|
case 1:
|
||||||
u, err := user.Current()
|
u, err := user.Current()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", errors.Wrap(err, "failed to fetch current user")
|
return "", "", "", errors.Wrap(err, "failed to fetch current user")
|
||||||
}
|
}
|
||||||
username, address = u.Username, input[0]
|
username, address = u.Username, input[0]
|
||||||
case 2:
|
case 2:
|
||||||
username, address = input[0], input[1]
|
username, address = input[0], input[1]
|
||||||
default:
|
default:
|
||||||
return "", "", fmt.Errorf("invalid arguments: %q", s)
|
return "", "", "", fmt.Errorf("invalid arguments: %q", s)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resolving domain names to IP address on the client side.
|
// Resolving domain names to IP address on the client side.
|
||||||
// Vault only deals with IP addresses.
|
// Vault only deals with IP addresses.
|
||||||
ipAddr, err := net.ResolveIPAddr("ip", address)
|
ipAddr, err := net.ResolveIPAddr("ip", address)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", errors.Wrap(err, "failed to resolve IP address")
|
return "", "", "", errors.Wrap(err, "failed to resolve IP address")
|
||||||
}
|
}
|
||||||
ip := ipAddr.String()
|
ip := ipAddr.String()
|
||||||
|
|
||||||
return username, ip, nil
|
return username, address, ip, nil
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user