Skip to content

Commit a585619

Browse files
gzdunekJoerger
andauthored
[v16] Cache PIV connections to share across the program execution (#47952)
* Cache PIV connections to share across the program execution (#47091) * Cache yubikey objects. * Cache PIV connections to share across the program execution. * Do not release the connection until `sign` returns * Do not ignore errors * Perform a "warm up" call to YubiKey * Fix tests * Use a specific interface to check if the key can be "warmed up" * Allow abandoning `signer.Sign` call when context is canceled * Make sure that the cached key is valid for the given private key policy The reason for adding this check was failing `invalid key policies` test. * Make `hardwareKeyWarmer` private * Force callers to release connection * Improve comments * Fix lint * Improve `connect` comment * Fix race condition * Simplify `release` logic * Trigger license/cla --------- Co-authored-by: joerger <bjoerger@goteleport.com> (cherry picked from commit bd6fdbf) * Sign a hashed message in hardware key warmup call (#48206) Otherwise, signing may fail with "input must be a hashed message" error. (cherry picked from commit 47494db) * Remove delayed closing of yubikey connection to prevent the connection from leaking after program execution. (#48414) (cherry picked from commit b7c0e79) --------- Co-authored-by: Brian Joerger <bjoerger@goteleport.com>
1 parent 2c99ccc commit a585619

File tree

3 files changed

+412
-203
lines changed

3 files changed

+412
-203
lines changed

api/client/proxy/client.go

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ type ClientConfig struct {
8282

8383
// CheckAndSetDefaults ensures required options are present and
8484
// sets the default value of any that are omitted.
85-
func (c *ClientConfig) CheckAndSetDefaults() error {
85+
func (c *ClientConfig) CheckAndSetDefaults(ctx context.Context) error {
8686
if c.ProxyAddress == "" {
8787
return trace.BadParameter("missing required parameter ProxyAddress")
8888
}
@@ -115,6 +115,21 @@ func (c *ClientConfig) CheckAndSetDefaults() error {
115115
// not to send the client certificate by looking at certificate request.
116116
if len(tlsCfg.Certificates) > 0 {
117117
cert := tlsCfg.Certificates[0]
118+
119+
// When a hardware key is used to store the private key, the user may fail to provide
120+
// a PIN or touch before a gRPC dial timeout occurs.
121+
// The resulting "dial timeout" error is generic and doesn't indicate an issue with the
122+
// hardware key itself (since YubiKey is treated like any other key).
123+
// To avoid this, we perform a "warm-up" call to the key, ensuring it is ready
124+
// before initiating the gRPC dial.
125+
// This approach works because the connection is cached for a few seconds,
126+
// allowing subsequent calls without requiring additional user action.
127+
if priv, ok := cert.PrivateKey.(hardwareKeyWarmer); ok {
128+
err := priv.WarmupHardwareKey(ctx)
129+
if err != nil {
130+
return nil, trace.Wrap(err)
131+
}
132+
}
118133
tlsCfg.Certificates = nil
119134
tlsCfg.GetClientCertificate = func(_ *tls.CertificateRequestInfo) (*tls.Certificate, error) {
120135
return &cert, nil
@@ -183,7 +198,7 @@ const protocolProxySSHGRPC string = "teleport-proxy-ssh-grpc"
183198
// of the caller, then prefer to use NewSSHClient instead which omits
184199
// the gRPC dialing altogether.
185200
func NewClient(ctx context.Context, cfg ClientConfig) (*Client, error) {
186-
if err := cfg.CheckAndSetDefaults(); err != nil {
201+
if err := cfg.CheckAndSetDefaults(ctx); err != nil {
187202
return nil, trace.Wrap(err)
188203
}
189204

@@ -439,3 +454,9 @@ func (c *Client) Ping(ctx context.Context) error {
439454
_, _ = c.transport.ClusterDetails(ctx)
440455
return nil
441456
}
457+
458+
// hardwareKeyWarmer performs a bogus call to the hardware key,
459+
// to proactively prompt the user for a PIN/touch (if needed).
460+
type hardwareKeyWarmer interface {
461+
WarmupHardwareKey(ctx context.Context) error
462+
}

0 commit comments

Comments
 (0)