teleport/rfd/0015-2fa-management.md
Zac Bergquist e174004612
Update RFD statuses (#24454)
We haven't been good about going back and marking RFDs as implemented,
and it's helpful when looking at old designs to know if they ever made
their way into the product.
2023-04-13 17:22:46 +00:00

10 KiB

authors state
Andrew Lytvynov (andrew@goteleport.com) implemented

RFD 15 - 2FA device management

What

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

Why

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.

RFD 14 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.

Details

protocols

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.

UX

For the below examples, we assume that the user already has at least one 2FA device enrolled. The bootstrap section covers registration of the first device.

CLI

Login:

$ 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
# SSO page opens
# no 2FA prompt from Teleport

2FA management:

$ 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

# Add U2F token.
$ 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.

# Add OTP app.
$ 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" added.

$ 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

# remove by name
$ tsh mfa rm yubikey
Tap any *registered* security key or enter an OTP code: <tap>
MFA device "yubikey" removed.

# remove by ID, which can be found via `tsh mfa ls -v`
$ 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

# If 2FA is optional:
$ 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

# If 2FA is required:
$ 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

``

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.

Bootstrap

Initially, a user account doesn't have a 2FA device. Depending on cluster 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.

Configuration

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 auth_service.authentication.second_factor 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

Restricted device vendors

Another new option is restrictions on U2F device manufacturers. This is done using attestation certificates presented by the device during enrollment. See FIDO docs about attestation.

On the Teleport side, users can pass the trusted attestation CAs as so:

# teleport.yaml
auth_service:
  authentication:
    second_factor: "on" # or "u2f" or "optional"
    u2f_device_attestation_cas:
      # CAs can be passed as local file paths
      - "/var/lib/teleport/u2f_ca1.pem"
      # or as raw PEM blocks
      - |
        -----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, you can download their attestation CA, write it into a file and set u2f_device_attestation_cas: ["/path/to/yubikey_ca.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.

Backend storage

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;
}

Migration

The above LocalAuthSecrets will be migrated by Teleport at startup in v6. In v7, we will remove the deprecated MFA fields from LocalAuthSecrets.

Audit log

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.