# Using GAM7 with a YubiKey
- [Thanks](#thanks)
- [Yubikey ykman PIV Commands](https://docs.yubico.com/software/yubikey/tools/ykman/PIV_Commands.html)
- [Introduction](#introduction)
- [FAQs](#faqs)
- [Setup Steps](#setup-steps)

## Thanks

Thanks to Jay Lee for the original version of this document.

## Introduction
GAM7 supports using a [YubiKey](https://www.yubico.com/products/yubikey-5-overview/) to generate and store the service account's private RSA key. Private keys generated by the YubiKey cannot be exported even to the computer running GAM7. When compared to the plain text oauth2service.json file with the private key stored in text, the YubiKey offers a more secure option that prevents digital theft and copying of the private key. Instead of reading the private key from the oauth2service.json file and signing requests itself, GAM7 will simply send signing requests to the YubiKey and get back the signature.

GAM7 version 6.50.01 or higher is required. Best practice is to always use the [latest version of GAM7](https://github.com/GAM-team/GAM/wiki/How-to-Update-Advanced-GAM).

## FAQs
### Can I use a Google Titan or other brand security key?
No, while Titan keys are great as security keys / U2F / 2SV, that is not the protocol being used by GAM7 here. GAM7 uses the PIV app of YubiKeys to work with service accounts. You need to use [a genuine Yubikey.](https://yubico.com/genuine/).

### Does this protect the admin credentials GAM7 stores in oauth2.txt?
No, the admin credentials GAM7 stores in oauth2.txt are not protected by the YubiKey as they are not using RSA private keys. Only the service account credentials normally stored in oauth2service.json are protected. The service account credentials are used for domain-wide delegation operations like managing Workspace user data in Drive, Gmail and Calendar. Note that GAM7 also has the ability to perform admin actions as a delegated admin service account (DASA). See [instructions for setting up DASA](https://github.com/GAM-team/GAM/wiki/Using-GAM7-with-a-delegated-admin-service-account.md). When DASA is setup, GAM7 will use the service account to authenticate which can be protected by the YubiKey.

### What if someone physically steals the YubiKey?
The YubiKey can be configured with a PIN that must be entered in order for it to sign data with the private key. GAM7 stores this PIN string in the oauth2service.json file so it can use it as needed. What this means is that an attacker would need to steal *both* the physical YubiKey and the PIN stored in oauth2service.json. The recommendation is to store oauth2service.json and the rest of the GAM directory on an encrypted partition. The YubiKey itself should also be kept in a secure location.

### Can I require a physical touch of the YubiKey before the private key can be used?
Yes but in practice this does not work very well with GAM7. The YubiKey will need to be touched every time there is a GAM7 command running which for batch or cron jobs may be constant. GAM7 can use a PIN configured on the YubiKey in order to offer an additional layer of protection.

### If I use a YubiKey, do I need to rotate the private key regularly?
No, because the YubiKey generated the private key it cannot be digitally exported from the YubiKey so there is no chance for it to be copied and stolen. Instead you should physically secure the YubiKey from theft.

### What data does the service account private key have access to?
When using domain-wide delegation with GAM7, the service account and anyone possessing the service account private key oauth2service.json file has access to the Gmail, Drive and Calendar data of ALL Workspace users in your domain. For this reason, whether using a YubiKey or not, you should take strong measures to protect the service account private key.

## Setup Steps
1. Upgrade to at least GAM7 6.50.01.
2. **If you are using a new YubiKey or don't care about the PIV app data on the YubiKey**
    1. Tell GAM7 to reset and configure the PIV app data on the YubiKey. This wipes all existing keys and configuration and then configures a private key and PIN for GAM7.
      * Single YubiKey - `gam yubikey reset_piv`
      * Multiple YubiKeys - `gam yubikey reset_piv yubikeyserialnumber <Number>`
    2. During the PIV reset, GAM7 will print out a PIN for the private key, record this key.
4. **If you are already using the YubiKey and wish to preserve the PIV app data and keys**
    1. You need to configure one of the PIV slots for a private key GAM7 can use.
      * [ykman piv keys generate](https://docs.yubico.com/software/yubikey/tools/ykman/PIV_Commands.html#ykman-piv-keys-options-command-args)
        `ykman piv keys generate -P <Text> --pin-policy ALWAYS --touch-policy NEVER --algorithm RSA2048 9a new_pubkey.txt`
      * Use `9a` for the `AUTHENTICATION` slot, `9c` for the `SIGNATURE` slot
    2. You need to generate a certificate for that slot.
      * [ykman piv certificates generate](https://docs.yubico.com/software/yubikey/tools/ykman/PIV_Commands.html#ykman-piv-certificates-generate-options-slot-public-key)
        `ykman piv certificates generate -P <Text> --subject "GAM Service Account" -d 36500 9a new_pubkey.txt`
      * Use `9a` for the `AUTHENTICATION` slot, `9c` for the `SIGNATURE` slot

5. Now that you have a private key on your YubiKey, tell GAM7 to use that instead of the private_key stored in oauth2service.json. We can do that by rotating the key:
```
copy oauth2service.json to oauth2service.save
gam create sakey yubikey yubikey_pin yubikey_slot AUTHENTICATION|SIGNATURE
```
The yubikey argument tells GAM7 to use a private key on a plugged in YubiKey. The yubikey_pin argument tells GAM7 to prompt you to input the PIN that was set in the previous step. The yubikey_slot argument tells GAM7 which PIV slot to use on the YubiKey.

If there are problems, you can go back to the original oauth2service.json.
```
copy oauth2service.json to oauth2service.yk
copy oauth2service.save to oauth2service.json
```

6. Now you should be able to run GAM7 commands like:
```
gam user admin@example.com check serviceaccount
```
and see the YubiKey lights flash as the YubiKey interacts with GAM7 to sign the GAM7 authentication requests. If you look at the oauth2service.json file, you'll see it contains some new fields like yubikey_serial and yubikey_pin but no longer contains the private_key field where GAM7 would normally store the private key data.

7. As a last step, since YubiKey-stored private keys do not need to be and should not be rotated, you can remove the service account's permissions to change it's own key. Navigate to the [Cloud Console](https://console.cloud.google.com/iam-admin/serviceaccounts) select the correct project and service account and on the Permissions tab, edit and remove the "Service Account Key Admin" permission that the service account has to itself.