================
PIV keys support
================

.. contents::

Introduction
============

The Bastion supports enabling a policy forcing accounts SSH ingress keys to originate from a known hardware token,
ensuring that the private SSH key is only stored on this hardware token, and not on the filesystem.

Currently, only Yubico keys implementing PIV can be verified this way. In that case, each individual hardware token
has a builtin Certificate Authority, signed by a well-known Yubico certificate, hence proving that the hardware token
is known and legit.

This builtin CA, in turn, emits an attestation certificate each time a new PIV key is generated on the hardware token,
hence proving that the bikey (private and public) has been generated by this individual hardware token.
Other metadata is included in the attestation, such as the firmware version, the serial number of the token,
the *TouchPolicy* and *PinPolicy*. Note that you may decide to overwrite the builtin CA by a one of your own,
possibly signed by a CA of your company. This would ensure not only that the SSH key is provided by the device,
but also that the device has been provided by your company.

Please refer to
the `Yubico PIV attestation page <https://developers.yubico.com/PIV/Introduction/PIV_attestation.html>`_ and
the `Yubico PIV tool page <https://developers.yubico.com/yubico-piv-tool/YubiKey_PIV_introduction.html>`_
for more information.

Without a policy
================

If you want to support PIV keys without making those mandatory, you don't have anything to do:
those keys are just regular RSA/ECDSA keys and they *just work* with The Bastion.
In that case, after having properly configured your hardware token with a key in slot 9a,
you can just use :doc:`/plugins/open/selfAddIngressKey` to add the key to your bastion account, and call it a day.
As a quick guidance, on a Yubikey you can usually generate a key in the proper slot this way,
after you've setup a management key:

.. code-block:: shell
   :emphasize-lines: 1

   yubico-piv-tool --key=YOUR_MGMT_KEY --action generate --pin-policy always --touch-policy never --slot 9a -o -

Now, if you want the bastion to be aware that this key is from a hardware token, you shall use the ``--piv`` option
to :doc:`/plugins/open/selfAddIngressKey`. This won't do anything special per-se, except storing
the certificates information, and showing the details of the PIV key in command outputs
such as :doc:`/plugins/open/selfListIngressKeys`.
Note however that if in the future you enable the PIV enforcing policy either on your account or globally,
this key will be considered valid, contrary to all the keys added without the ``--piv`` option,
even if these keys happen to be PIV ones. To add a key with the ``--piv`` option, you'll need the SSH public key
as usual, but also the attestation certificate and the key certificate.
Step by step details on how to get those are out of the scope of this document,
but again as a quick guidance, on a Yubikey you can usually get those this way:

.. code-block:: shell
   :emphasize-lines: 1,2,3

   yubico-piv-tool --action=read-certificate --slot=9a --key-format=SSH
   yubico-piv-tool --action=attest --slot=9a
   yubico-piv-tool --action=read-certificate --slot=f9

When you'll have added your key, you'll see a few more details than usual:

.. code-block:: console
   :emphasize-lines: 1

   bssh --osh selfAddIngressKey --piv
   Enter PIN for 'PIV Card Holder pin (PIV_II)':
   ---the-bastion.example.org--------------------------------the-bastion-3.01.03---
   => add a new public key to your account
   --------------------------------------------------------------------------------
   ~ Please paste the SSH key you want to add. This bastion supports the following algorithms:
   ~ ED25519: strongness[#####] speed[#####], use `ssh-keygen -t ed25519' to generate one
   ~ ECDSA  : strongness[####.] speed[#####], use `ssh-keygen -t ecdsa -b 521' to generate one
   ~ RSA    : strongness[###..] speed[#....], use `ssh-keygen -t rsa -b 4096' to generate one
   ~ 
   ~ In any case, don't save it without a passphrase.
   ~ You can prepend your key with a from="IP1,IP2,..." as this bastion policy allows ingress keys "from" override by users
   ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCyAMtxGT/RvzBZXiYlrCswZMruRtoBtONrVJTZ3Cj5ZpjaZyCRjQ/ETzZXXbvu9KiBsZyhVb/5H9F7CSGi+D5BlcRAKrT9P8MsT7BHWU14GhJddhHDy4rMnXapE93oxbnQIjQT34ozvTKlb0qOoR/SlT14LllvQS6ajaXB7Fm4bAJG/gYGXHEs2nmZn37Rll6vvpZ4ExM29UrqU3hAjYO0Ha+kL5G8Tr+fOhV/5ZmzNsYigdW7Ft7Co4Tpld9D0PqVhDPK7F1zHIFUXunFsewGtB3IQxLdLGDaCMzrRi11V6q/pBzN/75YsW6npRdOzJKjnwxG19lTtVCmCY3EPRFz
   ~ 
   ~ You have requested to add a PIV-enabled SSH key.
   ~ Please paste the PIV attestation certificate of your hardware key in PEM format.
   ~ This snippet should start with '-----BEGIN CERTIFICATE-----' and end with '-----END CERTIFICATE-----':
   ~ 
   -----BEGIN CERTIFICATE-----
   MIIDIDCCAgigAwIBAgIQAajpKeFbM+X1Yfk8GaH9dzANBgkqhkiG9w0BAQsFADAh
   MR8wHQYDVQQDDBZZdWJpY28gUElWIEF0dGVzdGF0aW9uMCAXDTE2MDMxNDAwMDAw
   MFoYDzIwNTIwNDE3MDAwMDAwWjAlMSMwIQYDVQQDDBpZdWJpS2V5IFBJViBBdHRl
   c3RhdGlvbiA5YTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALIAy3EZ
   P9G/MFleJiWsKzBkyu5G2gG042tUlNncKPlmmNpnIJGND8RPNlddu+70qIGxnKFV
   v/kf0XsJIaL4PkGVxEAqtP0/wyxPsEdZTXgaEl12EcPLisyddqkT3ejFudAiNBPf
   ijO9MqVvSo6hH9KVPXguWW9BLpqNpcHsWbhsAkb+BgZccSzaeZmfftGWXq++lngT
   Ezb1SupTeECNg7Qdr6QvkbxOv586FX/lmbM2xiKB1bsW3sKjhOmV30PQ+pWEM8rs
   XXMcgVRe6cWx7Aa0HchDEt0sYNoIzOtGLXVXqr+kHM3/vlixbqelF07MkqOfDEbX
   2VO1UKYJjcQ9EXMCAwEAAaNOMEwwEQYKKwYBBAGCxAoDAwQDBQIEMBQGCisGAQQB
   gsQKAwcEBgIEALeG1jAQBgorBgEEAYLECgMIBAIDATAPBgorBgEEAYLECgMJBAEB
   MA0GCSqGSIb3DQEBCwUAA4IBAQAq9O6H02KRvSmBYsz23r6cNTNS/fr5lSPYMHz/
   fX+D5B1thKKGstsfZVzoopwIjj86cIWpCYuNfEje+a5HrELL8ClV88JutJR2Nihs
   NxU3BbsSUqnwi2rQHcmtHJcC8rjfDzpYDlW1yR+SxVenbVxuRy0v8sbleHSPYaXG
   EhjupEAuhq7n0TjZMF1X7KElx9FZZM9HeuxUJvzV7XWiUgA4Zm05+4/zKW01n2kt
   +aMaQk7T1oiE0oOK51wJX6J80GzF51pM00oPlh4iDvnnNXYN2KvkNuNwPoceDDE/
   8K23ZfJyTN5nibk13UbxEWSHMUue1zcnFp0KdhqxbJYSS/9q
   -----END CERTIFICATE-----
   ~ 
   ~ Thanks, now please paste the PIV key certificate of your generated key in PEM format.
   ~ This snippet should also start with '-----BEGIN CERTIFICATE-----' and end with '-----END CERTIFICATE-----':
   ~ 
   -----BEGIN CERTIFICATE-----
   MIIC5jCCAc6gAwIBAgIJAKT/dqaxohbiMA0GCSqGSIb3DQEBCwUAMCsxKTAnBgNV
   BAMMIFl1YmljbyBQSVYgUm9vdCBDQSBTZXJpYWwgMjYzNzUxMCAXDTE2MDMxNDAw
   MDAwMFoYDzIwNTIwNDE3MDAwMDAwWjAhMR8wHQYDVQQDDBZZdWJpY28gUElWIEF0
   dGVzdGF0aW9uMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwDhP3YUI
   yLWSjseIKNzMscqCdicslrdkxPgMoK8Ocxu0err4yvFXiSZZL32BTZYLD8N7Y+d1
   cww6VVsFYdwn01Kc6YLrwM5FIN/msXkGTPdPVhVeqNMHh4QyYrYixwWaTbDCGoQD
   axVlifVmPS02Mvm8NDjC17X3LhsV1OiS/wOScsI8HHGgQXQIQEDMnt6cwZ83QK73
   7Wuu5uhSzT3jVOz28Rnij1p/8PcVWcGKWCPVYNbCmCdcm/sQeJB8y5aERDaePIIZ
   v9axnDT0DnUO7aDpzXA7i7XPbrkiSBEp7RCqXGs5cBqGCbq//xGh+/AGtCCV/sQM
   nTjl0d2k2Q8XTwIDAQABoxUwEzARBgorBgEEAYLECgMDBAMFAgQwDQYJKoZIhvcN
   AQELBQADggEBAHCnp3k5kQaBwYmR9nUHKGY1dgCvhJUlX2SAyY2fUeaMuURcRRlW
   BFw6CvLAjvSs5Dy3O6JWDmk+1WFZo0UMr15WZFiS5Fpy0M+GWvBCRP3YmbSw+J2t
   kyWypCIIu7cMtLpRYkL5SAlWmUCAz8dZPk5FLPpeqmxgQnRoSSe67IXiv3bNyPA1
   3NoXI2xw0hWQU1+85tfTxoTxOiAzY8UpAT2GggtSmCwO3sHsHJUYXRyCf8e6jtJL
   OFBx/uz+VJoRH7hUVOY+sbP5JJ83dRrWZkS57Hf3q0LOtbn27vM+fmL0y7z4vgDo
   DedmrmsbPtsRc3t7RWoqCa80Iq1jPvdm5gw=
   -----END CERTIFICATE-----
   ~  
   ~ Public key successfully added:
   ~ info: ADDED_BY=jdoe USING=selfAddIngressKey UNIQID=2993de2bb014 TIMESTAMP=1609427402 DATETIME=2020-12-31T15:10:02 VERSION=3.01.03
   ~ PIV: TouchPolicy=Never, PinPolicy=Always, SerialNo=12345678, Firmware=5.2.4
   ~ fingerprint: SHA256:8B0T6174KUPL1iTSyC0UpnDOvuaCgyKpu8zo9rb2lco (RSA-2048) [2020/12/17]
   ---------------------------------------------------------</selfAddIngressKey>---

As you can see, we added the public key as usual but were also asked for the two certificates.
On the bastion answer, right before the fingerprint of the key, we have a line starting with *PIV:*,
with some metadata extracted from the certificate.

Per-account policy
==================

If you want to force several accounts to only use certified PIV keys, you can set the option per-account
using the :doc:`/plugins/restricted/accountPIV` command, see its documentation page for all the possible options.
The main takeaways are:

- If you want an account to only have PIV keys, set the ``enforce`` policy for this account
- If you want an account to never require PIV keys, even if the global policy would require it,
  set the ``never`` policy (useful for accounts used by automated workflows)

Global policy
=============

If you want to apply a policy bastion-wide, please refer to the :ref:`ingressRequirePIV` option.
This policy can still be overridden per-account if needed, see above.

Temporary grace period
======================

If you enable the PIV policy globally or on several accounts, you'll soon find out that sometimes people forget
or lose their PIV-enabled hardware tokens, effectively locking them out of the bastion.
There is a *temporary grace period* feature you can use to handle such cases nicely:

.. code-block:: console
   :emphasize-lines: 1

   bssh --osh accountPIV --account lechuck --policy grace --ttl 48h
   ---the-bastion.example.org--------------------------------the-bastion-3.01.03---
   => modify the PIV policy of an account
   --------------------------------------------------------------------------------
   ~ Changing account configuration...
   
   ~ PIV grace up to 2d+00:00:00 (Wed 2021-01-13 09:22:29 UTC) has been set for this account
   ~ Applying change to keys...
   
   ~ Non-PIV account's ingress keys, if any, have been restored
   ----------------------------------------------------------------</accountPIV>---

What happens here is that, for a duration of 48 hours, this account will behave as if no PIV policy was enforced:
non-PIV keys are allowed again. If this account had non-PIV keys before its policy was set to enforce,
those keys are even restored (can be viewed using :doc:`/plugins/open/selfListIngressKeys` as usual),
so that they can easily connect again. However, after the grace period expires, their policy will go back to
what it was previously, and all the non-PIV keys will be disabled again.
This event is logged, so you can easily link this event from your SIEM to a potential ticket to your Helpdesk
for a hardware key replacement, or such.

This mechanism allows some flexibility (avoiding sending people back home just because they forgot their hardware key),
while still enforcing a high-level security policy with the proper processes in place.
