This repository contains usefull information and scripts to enable security on (Arch) Linux systems.
Assuming that you are setting up security from scratch
the following steps would be common to execute:
- Create GPG keys
- Create a backup of GPG keys on an external USB storage device
- Restore GPG keys from an external storage device
- Move the private GPG keys to an external YubiKey device, or
- Move the private GPG keys to an external OnlyKey device
Although the scripts are developed and tested on Arch Linux, they should work on other Linux distributions.
Script | Description |
---|---|
gpg-provision | Create a new set of GPG keys with [C] Certicate master and [S] Sign, [E] Encrypt and [A] Authenticate subkeys. |
gpg-backup-to-usb | Create a USB backup for the GnuPG key chain (LUKS) and public keys. |
gpg-restore-from-sub | Restore the USB backup created with ~gpg-backup-to-usb. |
onlykey-wipe | Clear all RSA and ECC registers on OnlyKey device. |
onlykey-provision | Transfer subkeys (created with gpg-provision) to OnlyKey device. |
For more information on OnlyKey
check these OnlyKey notes.
For more information on YubiKey
check these YubiKey notes.
For more information on Security
protocols check these Protocol notes.
Assuming that you have no GPG keys this description walks you through the process of:
- Creating a GPG key pair
- Back-up the GPG key pair to a USB storage device
- Restoring the GPG key pair from a USB storgae device
- Installing GPG keys on OnlyKey device, and / or
- Installing GPG kyes on YubiKey device
The scripts in this repository will assist you with the required tasks.
When creating and transfering GnuPG keys, you are handling secret
information. It is therefor best to execute the commands and scripts on a trusted system.
A trusted system would not be connected to the internet when handling GnuPG keys. It is assumed you installed Arch Linux in a virtual environment (see pure-arch for a secure Arch Linux installation script).
Once booted in an Arch Linux environment install the Linux packages for GnuPG key creation and USB backup:
pacman -Sy # update the pacman catalog
pacman -S git gnupg # install git and gnupg packages
git clone https://github.com/echjansen/pure-security
The following is required for OnlyKey hardware tokens:
pacman -S python-setuptools libusb python-pip libfido2
pacman -S python-pgpy python-ptyprocess
pip install onlykey # onlykey client tool
pip intsall onlykey-agent # onlykey agent
Linux system requires a special rule for non-root users to use USB devices.
curl -o 49-onlykey.rules https://raw.githubusercontent.com/echjansen/main/config/49-onlykey.rules
sudo cp 49-onlykey.rules /etc/udev/rules.d/
sudo udevadm control --reload-rules
sudo udevadm trigger
# unplug and plug in OnlyKey device
Usage:
./gpg-provision.py
This scripts provisions a new GPG Key-chain according to best practices with the [C] Certify
key on the Master key and [S][E][A] on sub-keys.
The script configures the keychain in a /tmp
directory, rather than in the default /.gnupg
.
The created GnuPG key-chain can then be:
- Taken offline for secure storage (USB) - see gpg-backup-to-usb
- Transfered to a smartcard such as OnlyKey (see onlykey-provision) or YubiKey
Output:
# ============================================================
# Create new GPG key pair.
# Inlcudes a Master [C] and subkeys [S][E][A]
# ============================================================
# Real Name: user
# Email: [email protected]
# Please provide a password to protect the secret key chain:
# Please repeat the password:
# The password provided is very short. Do you wish to continue (y/n)?y
# 1. Curve 25519 (default), 2. RSA: 1
# Expiration in years (2y): 2y
# ============================================================
# Selected values for GPG Key creation:
# ============================================================
# GNUPGHOME: /tmp/gpg_ajttd1g3
# IDENTITY: "user <[email protected]>"
# KEY TYPE: 25519
# EXPIRATION: 2y
# Continue (y/n)?y
# gpg: keybox '/tmp/gpg_ajttd1g3/pubring.kbx' created
# gpg: /tmp/gpg_ajttd1g3/trustdb.gpg: trustdb created
# gpg: directory '/tmp/gpg_ajttd1g3/openpgp-revocs.d' created
# gpg: revocation certificate stored as '/tmp/gpg_ajttd1g3/openpgp-revocs.d/95D2F7D300BF2DDA30CD217C586757876553EB4C.rev'
# gpg: checking the trustdb
# gpg: marginals needed: 3 completes needed: 1 trust model: pgp
# gpg: depth: 0 valid: 1 signed: 0 trust: 0-, 0q, 0n, 0m, 0f, 1u
# ============================================================
# Keys created and keys exported for backup
# Check GPG_GNUPGHOME for backup files.
# ============================================================
# /tmp/gpg_ajttd1g3/pubring.kbx
# -----------------------------
# sec ed25519/0x586757876553EB4C 2024-06-14 [C]
# Key fingerprint = 95D2 F7D3 00BF 2DDA 30CD 217C 5867 5787 6553 EB4C
# uid [ultimate] user <[email protected]>
# ssb ed25519/0x525446A57AA572A2 2024-06-14 [S] [expires: 2026-06-14]
# ssb cv25519/0x92B2CDB6AB2377E1 2024-06-14 [E] [expires: 2026-06-14]
# ssb ed25519/0xBA403E26A834F226 2024-06-14 [A] [expires: 2026-06-14]
Usage:
./gpg-backup-to-usb.py [-h] usb gnupghome pubkey
Backup GnuPG private and public keys to USB backup drive.
This script requires three argumenents.
- The connected USB device in
sdx
format. Use thelsblk
command to list available USB devices. - The full path to the GnuPG keychain is stored ($GNUPGHOME).
- The full path to the public key in armored format
public-key.asc
.
This script must be executed as root sudo gpg-backup
.
Only run this on a secure and trusted system.
positional arguments:
usb USB device in sdx
format
gnupghome path to the GnuPG path
pubkey Public key exported using ~ gpg -a –export public.asc
options: -h, –help show this help message and exit
Backup GnuPG keys to USB device example: sudo ./gpgbackup.py sda $GNUPGHOME public.asc
Output:
# =====================================================
# Backup GnuPG Key-chain to USB Drive:
# =====================================================
# All data on /dev/sdb will be deleted. Continue (y/n)?y
# Please provide a password to protect the secret key partition:
# Please repeat the password:
# The password provided is very short. Do you wish to continue (y/n)?y
# Archiving GNUPGHOME in LUKS partition on USB.
# =============================================
# [ * ] Creating new partition table for: /dev/sdb
# [ * ] Creating partition: /dev/sdb1
# [ * ] Creating LUKS partition: /dev/sdb1
# [ * ] Opening LUKS partition: SECRET
# [ * ] Formatting partition: /dev/mapper/SECRET
# [ * ] Removing folder: /mnt/secret
# [ * ] Creating folder /mnt/secret
# [ * ] Mounting partition: /dev/mapper/SECRET to /mnt/secret
# [ * ] Copying folder from: /tmp/gpg_mxxn633x/ to /mnt/secret
# [ * ] Unmounting partition: /mnt/secret
# [ * ] Removing folder: /mnt/secret
# [ * ] Closing LUKS partition: /dev/mapper/SECRET
# Copying GNUPG public key to partition on USB.
# =============================================
# [ * ] Creating partition: /dev/sdb2
# [ * ] Formatting partition: /dev/sdb2
# [ * ] Creating folder /mnt/public
# [ * ] Mounting partition: /dev/sdb2 to /mnt/public
# [ * ] Copying file from: /tmp/gpg_mxxn633x/1C68B003C941458B.public.key.asc to /mnt/public
# [ * ] Unmounting partition: /mnt/public
# [ * ] Removing folder: /mnt/public
# =====================================================
# GPG Key Backup to USB Drive completed successfully.
# =====================================================
# Remove the USB device, and store it in a save location.
# The USB contains two partitions:
# 1. /dev/sdb1 - The secret LUKS partition that contains the complete GNUPGHOME content and exported key files.
# 2. /dev/sdb2 - The public partition also contains the scripts in case a reverse engineering is required.
This script assists in restoring GnuPG private and public keys from USB backup drive, created with gpg-backup-to-usb.
Once the gpg-restore-from-sub script has been executed the secret and public partitions on the backup USB have been mounted,
and are accessible on /mnt/public
for the public keys, and /mnt/secret
for the private keys.
To restore the keychain, configuration, and trusted keys:
rm ~/.gnupg/ # Remove the old keychain. cp -r /mnt/secret/gpg_xxx ~/.gnupg # Copy the stored keychain and configuration gpg --list-secret-keys # Validate if the GnuPG keychain is correct. sudo reboot now # Reboot
To restore the keychain only - follow the following procedure:
rm ~/.gnupg/ # Remove the old keychain. gpg -k # Innitiate the gpg keychain. gpg --import xxxx.public.key.asc # Import the public keys from ~/mnt/secret~ gpg --import xxxx.private.master.asc # Import the private keys from ~/mnt/secret~ gpg --list-secret-keys # Validate if the GnuPG keychain is correct. gpg --edit-key <KEY-ID> # Edit the key and set the trust level to 5. > trust > 5 sudo reboot now # Reboot
usage:
gpg-restore-from-usb.py [-h] usb
usage: gpg-restore-from-usb.py [-h] usb
Restore the GnuPG private and public keys from USB backup drive.
This script requires one argumenent.
- The connected USB device in
sdx
format. Use thelsblk
command to list available USB devices.
This script must be executed as root sudo ./gpg-restore-from-usb.py
.
Only run this on a secure and trusted system, like a live Arch Linux ISO.
positional arguments: usb path to the USB device in sdx format
options: -h, –help show this help message and exit
Restore GnuPG keys from USB device example: sudo ./gpg-restore-from-usb.py sda
script output:
# ================================================================
# Restore GnuPG Key-chain from USB Drive:
# ================================================================
# Note: you likely want to execute this script on a Live Arch ISO!
# Please provide the password to unlock the secret partition:
# [ * ] Opening LUKS partition: SECRET
# [ * ] Creating folder /mnt/private
# [ * ] Mounting partition: /dev/mapper/SECRET to /mnt/private
# [ * ] Copying folder from: /mnt/private to /tmp/gpg_418qjzms
# [ * ] Unmounting partition: /mnt/private
# [ * ] Removing folder: /mnt/private
# [ * ] Closing LUKS partition: /dev/mapper/SECRET
# =====================================================
# Restore of GPG Key Backup from USB completed.
# =====================================================
# Remove the USB device, and store it in a save location.
# a. The GnuPG key has been restored to: /tmp/gpg_418qjzms
# b. It might be required to take ownership if the secret partition with: sudo chown -R user:user ~/tmp/gpx_xxxxx~
# You have now several options of using the restored gpg data:
# 1. Import the secret keys on the harddrive (not recommended) with: ~gpg --import /tmp/gpg_xxx/xxx.private.subkeys.asc~
# 2. Move the imported secret keys to a YubiKey, or
# 3. Move the imported secret keys to an OnlyKey
# 4. Reboot the machine to remove all data.
This script wipes all existing GPG - ECC (16) / RSA (4) keys from OnlyKey.
usage:
./onlykey-wipe.sh
script output:
# Successfully wiped ECC Key
# Successfully set Label
# Successfully wiped ECC Key
# Successfully set Label
# Successfully wiped ECC Key
# Successfully set Label
# Successfully wiped ECC Key
# Successfully set Label
# Successfully wiped ECC Key
# Successfully set Label
# Successfully wiped ECC Key
# Successfully set Label
# Successfully wiped ECC Key
# Successfully set Label
# Successfully wiped ECC Key
# Successfully set Label
# Successfully wiped ECC Key
# Successfully set Label
# Successfully wiped ECC Key
# Successfully set Label
# Successfully wiped ECC Key
# Successfully set Label
# Successfully wiped ECC Key
# Successfully set Label
# Successfully wiped ECC Key
# Successfully set Label
# Successfully wiped ECC Key
# Successfully set Label
# Successfully wiped ECC Key
# Successfully set Label
# Successfully wiped ECC Key
# Successfully set Label
# Successfully wiped RSA Private Key
# Successfully set Label
# Successfully wiped RSA Private Key
# Successfully set Label
# Successfully wiped RSA Private Key
# Successfully set Label
# Successfully wiped RSA Private Key
# Successfully set Label
This script transfers private subkeys to OnlyKey.
If OnlyKey has already keys loaded, the script will strore the new sub-keys in the next available slots (there are 16 slots in total available for GPG keys). Alternatively, any pre-programmed keys can be wirped with the onlykey-wipe
script.
usage:
./onlykey-provision.py -d private-subkey.asc # Dryrun
./onlykey-provision.py private-subkey.asc # Transfer private keys
usage: onlykey-provision.py [-h] [-d] [–no-expired] [–no-colors] [-p PASSPHRASE] keyfile
Extract secret subkeys from a OpenPGP key.
This script will display and set the raw private keys and subkeys on your OnlyKey. Only run this on a secure trusted system.
positional arguments: keyfile path to the secret PEM-encoded key file, or ‘-’ for stdin.
options: -h, –help show this help message and exit -d, –display display only, extracted keys shown for loading in the OnlyKey Desktop App –no-expired do not show expired subkeys –no-colors do not output with colors. Usefull for piping output and use in scripts. -p PASSPHRASE, –passphrase PASSPHRASE the passphrase of the key. Don’t forget bash’s history keeps everything !
Extract and load keys onto OnlyKey example: gpg –export-secret-keys -a keyid | ./onlykey-cli-gpg-add-keys - yubikey.org ~/mykey.asc –no-expired Extract and display for loading in the OnlyKey Desktop App example: ./onlykey-cli-gpg-add-keys ~/mykey.asc -d
script output:
# =====================================================
# | OnlyKey Provisioning script |
# =====================================================
# Enter GPG key password to open key:
# No secret primary key
# Extracting subkeys...
# subkey id: XXXXXXXXXXXXXXXX
# subkey type: EdDSA
# subkey usage: S
# subkey size: 256 bits
# subkey id: XXXXXXXXXXXXXXXX
# subkey type: ECDSA
# subkey usage: E
# subkey size: 256 bits
# subkey id: XXXXXXXXXXXXXXXX
# subkey type: EdDSA
# subkey usage: A
# subkey size: 256 bits
# Keys without a private key:
# keyid: b'XXXXXXXXXXXXXXXX', type: b'cESCA', algorithm: 22, keylength b'255'
# Keys not supported:
# keyid: b'XXXXXXXXXXXXXXXX', type: b'a', algorithm: 22, keylength b'255'
# Keys to create:
# Transfering keys ...
# b's'
# only_key.setkey(101, 'x', 's', 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx')
# Successfully set ECC Key
# only_key.setslot(29, MessageField.LABEL, XXXXXXXXXXXXXXXX)
# Successfully set Label
# b'e'
# only_key.setkey(102, 'x', 'd', 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx')
# Successfully set ECC Key
# only_key.setslot(30, MessageField.LABEL, XXXXXXXXXXXXXXXX)
# Successfully set Label
# Keyslots:
# <Slot 'RSA Key 1|b'''>
# <Slot 'RSA Key 2|b'''>
# <Slot 'RSA Key 3|b'''>
# <Slot 'RSA Key 4|b'''>
# <Slot 'ECC Key 1|b'XXXXXXXXXXXXXXXX''>
# <Slot 'ECC Key 2|b'XXXXXXXXXXXXXXXX''>
# <Slot 'ECC Key 3|b'''>
# <Slot 'ECC Key 4|b'''>
# <Slot 'ECC Key 5|b'''>
# <Slot 'ECC Key 6|b'''>
# <Slot 'ECC Key 7|b'''>
# <Slot 'ECC Key 8|b'''>
# <Slot 'ECC Key 9|b'''>
# <Slot 'ECC Key 10|b'''>
# <Slot 'ECC Key 11|b'''>
# <Slot 'ECC Key 12|b'''>
# <Slot 'ECC Key 13|b'''>
# <Slot 'ECC Key 14|b'''>
# <Slot 'ECC Key 15|b'''>
# <Slot 'ECC Key 16|b'''>
Although one can choose to use SSH generated keys for SSH authentication, it does mean another set of keys to manage. Since we went through all the trouble to create, backup and secure our GPG keys, we might as well use them for SSH authentication purposes.
First we need to get a hnadle on the Authentication key-grip:
gpg --list-keys --with-keygrip
# /home/user/.gnupg/pubring.kbx
# ----------------------------------
# pub ed25519 2024-06-30 [C]
# XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
# Keygrip = 0000000000000000000000000000000000000000
# uid [ultimate] user <[email protected]>
# sub ed25519 2024-06-30 [S]
# Keygrip = 1111111111111111111111111111111111111111
# sub cv25519 2024-06-30 [E]
# Keygrip = 2222222222222222222222222222222222222222
# sub ed25519 2024-06-30 [A]
# Keygrip = 3333333333333333333333333333333333333333
Copy the keygrip from the [A] authentication sub-key (although technically the sign key can be taken as well - for instance when using the keys using OnleyKey), and copy them into the sshcontrol
from GnuPG:
echo 3333333333333333333333333333333333333333 > ~/.gnupg/sshcontrol
Append the following to the .bashrc
or .zshrc
file:
# GPG for SSH Authentication
unset SSH_AGENT_PID
if [ "${gnupg_SSH_AUTH_SOCK_by:-0}" -ne $$ ]; then
export SSH_AUTH_SOCK="$(gpgconf --list-dirs agent-ssh-socket)"
fi
Append the following to the .ssh/config
file:
Match host * exec "gpg-connect-agent UPDATESTARTUPTTY /bye"
Restart the system or service.
Obtain the public key to register with the server or services:
ssh-add -L
# ssh-ed25519 00000000000000000000000000000000000000000000000000000000000000000000