-
Notifications
You must be signed in to change notification settings - Fork 1.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
1 changed file
with
173 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,173 @@ | ||
--- | ||
authors: Brian Joerger (bjoerger@goteleport.com) | ||
state: draft | ||
--- | ||
|
||
# RFD 198 - Hardware Key Pin Caching | ||
|
||
## Required Approvers | ||
|
||
* Engineering: @rosstimothy | ||
* Product: @xinding33 || @klizhentas | ||
|
||
## What | ||
|
||
Teleport offers the option to enforce the use of a hardware-backed private key | ||
to connect to Teleport servers and services, with an additional option to | ||
require the user's hardware key (PIV) pin for every operation | ||
(`hardware_key_pin`). | ||
|
||
This RFD proposes the implementation of a pin caching mechanism to improve UX | ||
when using the `hardware_key_pin` option. | ||
|
||
See [RFD 80](./0080-hardware-key-support.md) for more details on Hardware Key | ||
Support. | ||
|
||
## Why | ||
|
||
Although `hardware_key_pin` provides a great security option, it currently | ||
requires users to enter their pin once for every single action | ||
(e.g. `tsh ssh`, `tsh ls`). This is very disruptive when running several | ||
commands in short succession, especially when: | ||
|
||
* running several kubernetes or database commands through a Teleport local proxy. | ||
* using automated scripts which runs commands in bulk. | ||
|
||
## Details | ||
|
||
### Single Process Solution | ||
|
||
Caching the pin for a single process is not complicated. For example, we can | ||
easily enabled pin caching to make commands like `tsh proxy db` cache the pin | ||
for any incoming db connections coming through the local proxy: | ||
|
||
```console | ||
> tsh proxy db --tunnel --port=60000 | ||
Enter your YubiKey PIV PIN: | ||
Started authenticated tunnel for the PostgreSQL database "postgres" in cluster "root.example.com" on 127.0.0.1:60649. | ||
|
||
Use the following command to connect to the database or to the address above using other database GUI/CLI clients: | ||
$ psql postgres://postgres@localhost:60000/postgres | ||
|
||
# User can open several postgres connections over the local proxy without | ||
# being re-prompted for pin, until the pin cache times out. | ||
# | ||
# Note that the user is not re-prompted for pin immediately after the cache times | ||
# out. Instead they are prompted the next time they try to create a psql connection | ||
# after the timeout. | ||
Enter your YubiKey PIV PIN: | ||
``` | ||
|
||
### Multi Process Solution | ||
|
||
In order to cache the pin across multiple Teleport client processes (`tsh`, | ||
`tctl`, Teleport Connect), we can use the new [Teleport Key Agent](./0199-teleport-key-agent). | ||
When used with the `hardware_key_pin` requirement, the Teleport Key Agent will | ||
act as the pin handler for all other Teleport clients performing private key | ||
operations through the agent. This means that the Teleport Key Agent will be | ||
solely responsible for both prompting the pin and caching it. | ||
|
||
This solution will have by far the best UX when paired with Teleport Connect | ||
because Teleport Connect has the ability to pop into the foreground when the | ||
user needs to enter their PIV pin. | ||
|
||
### UX | ||
|
||
Pin caching should not on its own introduce any significant UX impacts, other | ||
than prompting for pin less often, as needed. UX implications of the Teleport | ||
Key Agent will be covered in the Teleport Key Agent RFD. | ||
|
||
### Security | ||
|
||
Caching the pin in process memory introduces a risk for the pin to be | ||
compromised. Fortunately, this risk can be almost entirely mitigated with | ||
secure memory practices implemented in [memguard](github.com/awnumar/memguard). | ||
|
||
Using [memguard](github.com/awnumar/memguard), Teleport clients will store the | ||
cached pin in a secure enclave. | ||
|
||
### Configuration | ||
|
||
#### Cluster Auth Preference | ||
|
||
To enable pin caching for Teleport clients, set `cap.hardware_key.pin_cache_timeout` | ||
to the desired timeout duration: | ||
|
||
```yaml | ||
kind: cluster_auth_preference | ||
version: v2 | ||
metadata: | ||
name: cluster-auth-preference | ||
spec: | ||
... | ||
hardware_key: | ||
# pin_cache_timeout is the amount of time that Teleport clients will cache | ||
# the user's PIV pin. The timeout countdown is started when the pin is | ||
# stored and is not extended by subsequent accesses. | ||
pin_cache_timeout: 15m | ||
``` | ||
Teleport Clients will retrieve this setting through `/webapi/ping`. | ||
|
||
### Proto | ||
|
||
```diff | ||
### types.proto | ||
message HardwareKey { | ||
... | ||
+ // PinCacheTimeout is the amount of time in nanoseconds that Teleport clients | ||
+ // will cache the user's PIV PIN when hardware key PIN policy is enabled. | ||
+ int64 PinCacheTimeout = 3 [ | ||
+ (gogoproto.jsontag) = "pin_cache_timeout,omitempty", | ||
+ (gogoproto.casttype) = "Duration" | ||
+ ]; | ||
} | ||
``` | ||
|
||
### Backward Compatibility | ||
|
||
Pin caching is purely a client-side feature with no backwards compatibility | ||
concerns. | ||
|
||
### Audit Events | ||
|
||
N/A | ||
|
||
### Additional considerations | ||
|
||
#### Utilize internal PIV pin caching | ||
|
||
Since we are using the PIV pin policy `once`, the pin only needs to be provided | ||
once per PC/SC transaction. This means that we could avoid caching the pin | ||
explicitly by just holding open the PC/SC transaction for however long we want | ||
the pin to be cached. Unfortunately, this built-in pin caching only works when | ||
claiming an exclusive transaction on the PIV key, locking out any other PIV | ||
applications from connecting to the key until the transaction is released. | ||
|
||
Reportedly, it may be possible to detect when another process is trying to claim | ||
a PC/SC transaction with the `SCardGetStatusChange` function. Theoretically, this | ||
means that a Teleport client, or Teleport Key Agent, could hold the PC/SC | ||
transaction for an extended period of time, dropping the transaction whenever | ||
another process needs to access it. The other process can take advantage of | ||
the cached pin, and then the original Teleport Client can reclaim the transaction | ||
to maintain the cached pin further. | ||
|
||
While this solution may work, it has the following drawbacks compared to the | ||
explicit pin caching solution above. | ||
|
||
* The implementation is more complex and would require upstream changes to piv-go | ||
* Competing over PC/SC transactions is not as efficient as sharing one through the | ||
Teleport Key Agent | ||
* The duration of the PIN cache would not be as consistent, since the PC/SC transaction | ||
could be reclaimed at any time to prolong the internal pin cache. | ||
|
||
#### Corollary - `memguard` | ||
|
||
Since we are introducing [memguard](github.com/awnumar/memguard) as a dependency | ||
with this change, we should consider utilizing it to secure private keys and | ||
restricted certificates (e.g. MFA verified certs) stored in memory. | ||
|
||
This is particularly important for [Headless Authentication](./0105-headless-authentication.md), | ||
which takes place on a remote host. Currently we only protect against the | ||
possibility of memory swaps using [`unix.Mlockall`](https://pkg.go.dev/golang.org/x/sys/unix#Mlockall) | ||
and print a warning for non linux environments. |