Project: Teleport (Provisioning / Security & Compliance)
File: 0015-2fa-management.md

authors: Andrew Lytvynov (andrew@goteleport.com) state: implemented Improvements to 2FA device management (OTP and U2F): support for multiple 2FA devices per account ability to mix OTP and U2F devices (and possibly more in the future) ability to list/add/delete devices by the user from Web UI and CLI Currently, Teleport only supports a single OTP or U2F device per local user. This setting is global to the cluster - either everyone uses OTP or U2F or nothing. Users don't have a choice, and losing a 2FA device requires an admin to reset the account to recover. adds support for 2FA challenges per-connection. 2FA devices will become more widely used by Enterprise users, who would previously delegate this to their SSO provider. Teleport needs to make 2FA device support more flexible, as both a long-standing OSS users' request, and the new Enterprise use-case. Teleport supports two 2FA protocols: OTP and U2F. There's no change to the list of supported protocols, but the implementation should assume that more protocols will be added (e.g. WebAuthn). For U2F, we will migrate from https://github.com/tstranex/u2f to https://github.com/flynn/u2f, to allow client-side CLI authentication without the `u2f-host` dependency. For the below examples, we assume that the user already has at least one 2FA device enrolled. The section covers registration of the first device. Login: ```sh $ tsh login --login=non-sso-user Enter password for Teleport user non-sso-user: ... Tap your security key... <tap> $ tsh login --login=non-sso-user --mfa=otp Enter password for Teleport user non-sso-user: ... Enter OTP code: ... $ tsh login --login=sso-user --auth=oidc ``` 2FA management: ```sh $ tsh mfa ls MFA device name Type Added at Last used - - - android OTP OTP Tue 08 Dec 2020 01:29:42 PM PST Tue 15 Dec 2020 01:29:42 PM PST yubikey U2F Wed 09 Dec 2020 02:00:13 PM PST Wed 16 Dec 2020 02:00:13 PM PST $ tsh mfa add Adding a new MFA device. Choose device type (1 - OTP, 2 - U2F): 2 Enter device name: solokey Tap any registered security key or enter an OTP code: <tap> Tap your new security key... <tap> MFA device "solokey" added. $ tsh mfa add Adding a new MFA device. Choose device type (1 - OTP, 2 - U2F): 1 Enter device name: my-otp-app Tap any registered security key or enter an OTP code: <tap> Enter this secret into your OTP app: 1234567890ABCDEF Enter the OTP code generated by your OTP app: 123456 MFA device "my-otp-app"

$ tsh mfa ls MFA device name Type Added at Last used - - - android OTP OTP Tue 08 Dec 2020 01:29:42 PM PST Tue 15 Dec 2020 01:29:42 PM PST yubikey U2F Wed 09 Dec 2020 02:00:13 PM PST Wed 16 Dec 2020 02:00:13 PM PST solokey U2F Wed 16 Dec 2020 02:05:46 PM PST Wed 16 Dec 2020 02:05:46 PM PST my-otp-app OTP Wed 16 Dec 2020 02:06:46 PM PST Wed 16 Dec 2020 02:06:46 PM PST $ tsh mfa rm yubikey Tap any registered security key or enter an OTP code: <tap> MFA device "yubikey" removed. $ tsh mfa rm fa004bf4-acc7-435d-8965-5f5a0a4552e8 Tap any registered security key or enter an OTP code: <tap> MFA device "android OTP" removed. $ tsh mfa ls MFA device name Type Added at Last used - - - solokey U2F Wed 16 Dec 2020 02:05:46 PM PST Wed 16 Dec 2020 02:05:46 PM PST my-otp-app OTP Wed 16 Dec 2020 02:06:46 PM PST Wed 16 Dec 2020 02:06:46 PM PST $ tsh mfa rm solokey You are about to remove the only remaining MFA device. This will disable MFA during login. Are you sure? (y/N): N $ tsh mfa rm solokey Can't remove the only remaining MFA device. Please add a replacement MFA device first using "tsh mfa add". ``` `` Web UI management of 2FA devices should be logically similar to the CLI: a page to see all enrolled devices a button to enroll a new device buttons to remove any enrolled device (except for the last one) Web UI details, wireframes and implementation will be added later, when we have the capacity to do it. Initially, 2FA management is CLI-only. Initially, a user account doesn't have a 2FA device. Depending on [cluster configuration](#configuration), use of 2FA devices might be required. If 2FA is required, a user is required to enroll a device during account creation (for local users) or first login (for SSO users). If 2FA is optional, a user can create an account and login without 2FA. They can then add 2FA devices as described above. If an existing user has at least one 2FA device registered, it's required during login. The current 2FA configuration in Teleport only applies to local users and is always required. We need to allow this to be optional (for users to migrate) and allow to mix different 2FA methods. Existing configuration options must be backwards-compatible - no change in behavior unless config values are changed. New values for

for this: `off` (existing) - no 2FA can be enrolled `otp` (existing) - only OTP can be enrolled and is required for all local users `u2f` (existing) - only U2F can be enrolled and is required for all local users `on` (new) - users can enroll both OTP and U2F devices, and 2FA is required for all local users `optional` (new) - users can enroll both OTP and U2F devices, and 2FA is required only for users with 2FA enrolled Another new option is restrictions on U2F device manufacturers. This is done using attestation certificates presented by the device during enrollment. See [FIDO docs](https://fidoalliance.org/specs/fido-u2f-v1.2-ps-20170411/fido-u2f-overview-v1.2-ps-20170411.html#verifying-that-a-u2f-device-is-genuine) about attestation. On the Teleport side, users can pass the trusted attestation CAs as so: ```yaml auth_service: authentication: second_factor: "on" # or "u2f" or "optional" u2fdeviceattestation_cas: "/var/lib/teleport/u2f_ca1.pem" | --BEGIN CERTIFICATE-- MIIDFzCCAf+gAwIBAgIDBAZHMA0GCSqGSIb3DQEBCwUAMCsxKTAnBgNVBAMMIFl1 YmljbyBQSVYgUm9vdCBDQSBTZXJpYWwgMjYzNzUxMCAXDTE2MDMxNDAwMDAwMFoY DzIwNTIwNDE3MDAwMDAwWjArMSkwJwYDVQQDDCBZdWJpY28gUElWIFJvb3QgQ0Eg U2VyaWFsIDI2Mzc1MTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMN2 cMTNR6YCdcTFRxuPy31PabRn5m6pJ+nSE0HRWpoaM8fc8wHC+Tmb98jmNvhWNE2E ilU85uYKfEFP9d6Q2GmytqBnxZsAa3KqZiCCx2LwQ4iYEOb1llgotVr/whEpdVOq joU0P5e1j1y7OfwOvky/+AXIN/9Xp0VFlYRk2tQ9GcdYKDmqU+db9iKwpAzid4oH BVLIhmD3pvkWaRA2H3DA9t7H/HNq5v3OiO1jyLZeKqZoMbPObrxqDg+9fOdShzgf wCqgT3XVmTeiwvBSTctyi9mHQfYd2DwkaqxRnLbNVyK9zl+DzjSGp9IhVPiVtGet X02dxhQnGS7K6BO0Qe8CAwEAAaNCMEAwHQYDVR0OBBYEFMpfyvLEojGc6SJf8ez0 1d8Cv4O/MA8GA1UdEwQIMAYBAf8CAQEwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3 DQEBCwUAA4IBAQBc7Ih8Bc1fkC+FyN1fhjWioBCMr3vjneh7MLbA6kSoyWF70N3s XhbXvT4eRh0hvxqvMZNjPU/VlRn6gLVtoEikDLrYFXN6Hh6Wmyy1GTnspnOvMvz2 lLKuym9KYdYLDgnj3BeAvzIhVzzYSeU77/Cupofj093OuAswW0jYvXsGTyix6B3d bW5yWvyS9zNXaqGaUmP3U9/b6DlHdDogMLu3VLpBB9bm5bjaKWWJYgWltCVgUbFq Fqyi4+JE014cSgR57Jcu3dZiehB6UtAPgad9L5cNvua/IWRmm+ANy3O2LH++Pyl8 SREzU8onbBsjMg9QDiSf5oJLKvd/Ren+zGY7 --END CERTIFICATE-- ``` For example, to restrict users to [only use Yubikeys](https://developers.yubico.com/U2F/AttestationandMetadata/), you can download their [attestation CA](https://developers.yubico.com/U2F/yubico-u2f-ca-certs.txt), write it into a file and set `u2fdeviceattestationcas: ["/path/to/yubikeyca.pem"]` and restart the auth server. After this, users will only be able to enroll Yubikey devices. Note that existing enrolled keys will remain, even if they aren't Yubikeys. If we wanted to re-check the existing enrolled keys against updated attestation CAs, we would have to store the attestation cert of every enrolled key. This would impose extra storage costs, add complexity and create the risk of user lockout. Each Teleport `User` object has a `LocalAuth` proto field: ``` message LocalAuthSecrets { bytes PasswordHash = 1 [ (gogoproto.jsontag) = "password_hash,omitempty" ]; string TOTPKey = 2 [ (gogoproto.jsontag) = "totp_key,omitempty" ]; U2FRegistrationData U2FRegistration = 3 [ (gogoproto.jsontag) = "u2f_registration,omitempty" ]; uint32 U2FCounter = 4 [ (gogoproto.jsontag) = "u2f_counter,omitempty" ]; } ``` To support multiple 2FA devices, we'll modify it: ``` message LocalAuthSecrets { bytes PasswordHash = 1 [ (gogoproto.jsontag) = "password_hash,omitempty" ]; // Deprecated MFA fields. string TOTPKey = 2 [ deprecated = true, (gogoproto.jsontag) = "totp_key,omitempty" ]; U2FRegistrationData U2FRegistration = 3 [ deprecated = true, (gogoproto.jsontag) = "u2f_registration,omitempty" ]; uint32 U2FCounter = 4 [ deprecated = true, (gogoproto.jsontag) = "u2f_counter,omitempty" ]; repeated MFADevice MFA = 5; } message MFADevice { string ID = 1; string Name = 2; google.protobuf.Timestamp AddedAt = 3; google.protobuf.Timestamp LastUsed = 4; oneof Device { TOTPDevice TOTP = 5; U2FDevice U2F = 6; } } message TOTPDevice { string Key = 1; } message U2FDevice { // Copied from U2FRegistrationData bytes Raw = 1; bytes KeyHandle = 2; bytes PubKey = 3; uint32 U2FCounter = 4; } ``` The above `LocalAuthSecrets` will be migrated by Teleport at startup in v6. In v7, we will remove the deprecated MFA fields from `LocalAuthSecrets.` All 2FA device operations (create/delete) will emit an audit log entry. The entry should contain the user, device UUID and device name at a minimum. Logging in with a 2FA check will add a `With2FA` field containing the device UUID to `UserLogin` event.