Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion command/ssh/certificate.go
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@ $ step ssh certificate --kty OKP --curve Ed25519 mariano@work id_ed25519
sshPrivateKeyFlag,
sshProvisionerPasswordFlag,
sshSignFlag,
sshConfirmFlag,
flags.KTY,
flags.Curve,
flags.Size,
Expand Down Expand Up @@ -531,7 +532,11 @@ func certificateAction(ctx *cli.Context) error {
ui.Printf(`{{ "%s" | red }} {{ "SSH Agent:" | bold }} %v`+"\n", ui.IconBad, err)
} else {
defer agent.Close()
if err := agent.AddCertificate(comment, resp.Certificate.Certificate, priv); err != nil {
var opts []sshutil.AgentOption
if ctx.Bool("confirm") {
opts = append(opts, sshutil.WithConfirmBeforeUse())
}
if err := agent.AddCertificate(comment, resp.Certificate.Certificate, priv, opts...); err != nil {
ui.Printf(`{{ "%s" | red }} {{ "SSH Agent:" | bold }} %v`+"\n", ui.IconBad, err)
} else {
ui.PrintSelected("SSH Agent", "yes")
Expand Down
12 changes: 10 additions & 2 deletions command/ssh/login.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ $ step ssh certificate --kty OKP --curve Ed25519 mariano@work id_ed25519
flags.Curve,
flags.Size,
flags.Insecure,
sshConfirmFlag,
},
}
}
Expand Down Expand Up @@ -283,20 +284,27 @@ func loginAction(ctx *cli.Context) error {
}

// Attempt to add key to agent if private key defined.
if err := agent.AddCertificate(comment, resp.Certificate.Certificate, priv); err != nil {
var agentOpts []sshutil.AgentOption
if ctx.Bool("confirm") {
agentOpts = append(agentOpts, sshutil.WithConfirmBeforeUse())
}

if err := agent.AddCertificate(comment, resp.Certificate.Certificate, priv, agentOpts...); err != nil {
ui.Printf(`{{ "%s" | red }} {{ "SSH Agent:" | bold }} %v`+"\n", ui.IconBad, err)
} else {
ui.PrintSelected("SSH Agent", "yes")
}

if isAddUser {
if resp.AddUserCertificate == nil {
ui.Printf(`{{ "%s" | red }} {{ "Add User Certificate:" | bold }} failed to create a provisioner certificate`+"\n", ui.IconBad)
} else if err := agent.AddCertificate(comment, resp.AddUserCertificate.Certificate, auPriv); err != nil {
} else if err := agent.AddCertificate(comment, resp.AddUserCertificate.Certificate, auPriv, agentOpts...); err != nil {
ui.Printf(`{{ "%s" | red }} {{ "Add User Certificate:" | bold }} %v`+"\n", ui.IconBad, err)
} else {
ui.PrintSelected("Add User Certificate", "yes")
}
}


return nil
}
7 changes: 6 additions & 1 deletion command/ssh/proxycommand.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ This command will add the user to the ssh-agent if necessary.
flags.CaURL,
flags.Root,
flags.Context,
sshConfirmFlag,
},
}
}
Expand Down Expand Up @@ -212,7 +213,11 @@ func doLoginIfNeeded(ctx *cli.Context, subject string) error {
}

// Add certificate and private key to agent
return agent.AddCertificate(subject, resp.Certificate.Certificate, priv)
var opts []sshutil.AgentOption
if ctx.Bool("confirm") {
opts = append(opts, sshutil.WithConfirmBeforeUse())
}
return agent.AddCertificate(subject, resp.Certificate.Certificate, priv, opts...)
}

func getBastion(ctx *cli.Context, user, host string) (*api.SSHBastionResponse, error) {
Expand Down
11 changes: 10 additions & 1 deletion command/ssh/ssh.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,11 @@ var (
Usage: `When signing an existing public key, use this flag to specify the corresponding
private key so that the pair can be added to an SSH Agent.`,
}

sshConfirmFlag = cli.BoolFlag{
Name: "confirm",
Usage: `Require user confirmation for every use of the certificate.`,
}
)

func loginOnUnauthorized(ctx *cli.Context) (ca.RetryFunc, error) {
Expand Down Expand Up @@ -244,7 +249,11 @@ func loginOnUnauthorized(ctx *cli.Context) (ca.RetryFunc, error) {

// Add ssh certificate to the agent, ignore errors.
if agent, err := sshutil.DialAgent(); err == nil {
agent.AddCertificate(jwt.Payload.Email, resp.Certificate.Certificate, priv)
var opts []sshutil.AgentOption
if ctx.Bool("confirm") {
opts = append(opts, sshutil.WithConfirmBeforeUse())
}
agent.AddCertificate(jwt.Payload.Email, resp.Certificate.Certificate, priv, opts...)
}

return true
Expand Down
20 changes: 15 additions & 5 deletions internal/sshutil/agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
type options struct {
filterBySignatureKey func(*agent.Key) bool
removeExpiredKey func(*Agent, *agent.Key) bool
confirmBeforeUse bool
}

func newOptions(opts []AgentOption) *options {
Expand All @@ -28,6 +29,13 @@ func newOptions(opts []AgentOption) *options {
// AgentOption is the type used for variadic options in Agent methods.
type AgentOption func(o *options)

// WithConfirmBeforeUse requires user confirmation for every use of the key.
func WithConfirmBeforeUse() AgentOption {
return func(o *options) {
o.confirmBeforeUse = true
}
}

// WithSignatureKey filters certificate not signed by the given signing keys.
func WithSignatureKey(keys []ssh.PublicKey) AgentOption {
signingKeys := make([][]byte, len(keys))
Expand Down Expand Up @@ -242,7 +250,7 @@ func (a *Agent) RemoveAllKeys(opts ...AgentOption) (bool, error) {
}

// AddCertificate adds the given certificate to the agent.
func (a *Agent) AddCertificate(subject string, cert *ssh.Certificate, priv interface{}) error {
func (a *Agent) AddCertificate(subject string, cert *ssh.Certificate, priv interface{}, opts ...AgentOption) error {
var (
lifetime uint64
now = cast.Uint64(time.Now().Unix())
Expand All @@ -262,10 +270,12 @@ func (a *Agent) AddCertificate(subject string, cert *ssh.Certificate, priv inter
lifetime = 0
}

o := newOptions(opts)
return errors.Wrap(a.Add(agent.AddedKey{
PrivateKey: priv,
Certificate: cert,
Comment: subject,
LifetimeSecs: cast.Uint32(lifetime),
PrivateKey: priv,
Certificate: cert,
Comment: subject,
LifetimeSecs: cast.Uint32(lifetime),
ConfirmBeforeUse: o.confirmBeforeUse,
}), "error adding key to agent")
}
21 changes: 21 additions & 0 deletions internal/sshutil/agent_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package sshutil

import (
"testing"

"github.com/stretchr/testify/require"
)

func Test_newOptions(t *testing.T) {
t.Run("default", func(t *testing.T) {
o := newOptions(nil)
require.False(t, o.confirmBeforeUse)
require.Nil(t, o.filterBySignatureKey)
require.Nil(t, o.removeExpiredKey)
})

t.Run("confirmBeforeUse", func(t *testing.T) {
o := newOptions([]AgentOption{WithConfirmBeforeUse()})
require.True(t, o.confirmBeforeUse)
})
}