In this lab, you'll set up Gopass to manage your secrets securely. You'll use AGE encryption to protect your secrets and store them in a Git repository. We'll create an SSH keypair and store it in Gopass, which we'll later use with Chezmoi in Lab 5.
45-60 minutes
- Understand AGE encryption and why we use it
- Install Gopass and AGE tools (we actually did this in lab 2 to save time - but we'll recap here)
- Initialize a Gopass store with AGE encryption
- Generate and store an SSH keypair
- Store additional secrets (API tokens, etc.)
- Back up your encrypted store to GitHub
- Retrieve and use secrets from Gopass
- Completed Lab 3 (running your custom Bluefin VM)
- GitHub Personal Access Token (from Lab 0)
- GitHub account
Gopass is a password manager for teams and individuals:
- Command-line based
- Stores encrypted secrets in Git repositories
- Supports multiple encryption backends
- Team-friendly (can share secrets)
- Cross-platform
AGE (Actually Good Encryption) is a modern encryption tool:
- Simple and secure
- Modern cryptography (Curve25519)
- Better than GPG for most use cases
- Easy to use
- Small attack surface
GPG (traditional):
- Complex key management
- Large attack surface
- Complicated trust model
- Hard to use correctly
AGE (modern):
- Simple key pair (public/private)
- Easy and straightforward to use
- Designed for the most common use cases
- Much harder to use incorrectly
As I have made these tools part of my workflow, I like to "bake them in" to the image that I've already created. If you look carefully at the recipe.yml from Lab 2, you'll notice that we actually already installed them then:
- type: dnf
...
install:
packages:
...
- gopass # We'll need this for lab 4
- age # We'll need this for lab 4
- chezmoi # We'll need this for lab 5Whilst this is my preferred option, if you don't want them to be part of the image you use (or if you want to use the "vanilla" Bluefin Linux image, you can install them using toolbox or Linuxbrew with the brew command - in both these cases, the tools will live in the user space (normally under your home directory) and you'll need to install them on every new machine you build).
Option B - Use Linuxbrew:
Linuxbrew is built into Bluefin based images (but be aware it's not there by default on some of the other immutable images such as Silverblue). Installing the required tools using Linuxbrew is as easy as:
# Install gopass and age
$ brew install gopass age
# Verify
$ gopass --version**Option B - Use Toolbox **:
If you want to install the tools another way, as discussed we can use a Fedora toolbox container instead:
# Create a Fedora toolbox
$ toolbox create workshop
# Enter the toolbox
$ toolbox enter workshop
# Inside toolbox, install gopass and age
$ sudo dnf install -y gopass age
# Verify
$ gopass --versionFor option C, all Gopass commands below should be run inside the toolbox.
Before we proceed with any further local setup, let's set up a repository on GitHub where we'll sync our secrets to.
On GitHub:
- Go to https://github.com/new
- Repository name:
gopass-store - Description: "Encrypted password store"
- Visibility: Private (IMPORTANT!)
- Do NOT initialize with README
- Click "Create repository"
As this is a private repository, you will need to configure access to it, either using a Personal Access Token, or a Deploy Key if using SSH access. I'll assume we're using SSH access with a deploy key here, and that you're familiar with setting this up on GitHub, but if you need more assistance please refer to: https://docs.github.com/en/authentication/connecting-to-github-with-ssh/managing-deploy-keys#deploy-keys
For the rest of this guide, I'll assume we're using SSH to access our GitHub repo, and that our deploy key is called gopass-deploy-key.
This is the central piece of this lab - getting gopass configured and running. We'll assume you're doing this for the first time, but if you are trying to recover from a failed attempt, please clear out existing gopass data first using the command:
$ rm -rf .config/gopass .local/share/gopass/When that is done, you can run the following command to set up your AGE keys and gopass. Note that you will need to use some mechanism to ensure you can access your private GitHub repository over SSH - here I'm setting GIT_SSH_COMMAND at the time I run the command to demonstrate this approach, but you could also use ssh-agent, or a configuration in ~/.ssh - these options are left as an exercise for the participant.
$ GIT_SSH_COMMAND="ssh -i ~/gopass-deploy-key -o IdentitiesOnly=yes" gopass setup --crypto ageYou will be guided through a wizard to set up the various parameters - see below an example of the wizard and responses suitable for the lab:
__ _ _ _ _ _ ___ ___
/'_ '\ /'_'\ ( '_'\ /'_' )/',__)/',__)
( (_) |( (_) )| (_) )( (_| |\__, \\__, \
'\__ |'\___/'| ,__/''\__,_)(____/(____/
( )_) | | |
\___/' (_)
🌟 Welcome to gopass!
🌟 Initializing a new password store ...
🔐 Using crypto backend: age
💾 Using storage backend: gitfs
🔐 No useable cryptographic keys. Generating new key pair
🧪 Creating cryptographic key pair (age) ...
⚠ Do you want to enter a passphrase? (otherwise we generate one for you) [y/N/q]:
Passphrase: smokiness upheld gents impotent
⚠ You need to remember this very well!
Did you save your passphrase? [Y/n/q]:
✅ Key pair for age generated
⚠ 🔐 We need to unlock your newly created private key now! Please enter the passphrase you just generated.
✅ Key pair age1m9nkvjx2hns6rha2pr62cmm323yacd8kmyued3uq9q5n9fy82guq38lym4 validated
🔐 Cryptographic keys generated
🌟 Configuring your password store ...
Please enter an email address for password store git config []: sysadmin@example.com
❓ Do you want to add a git remote? [y/N/q]: y
Configuring the git remote ...
Please enter the git remote for your shared store []: git@github.com:<YOUR_USERNAME>/gopass-store.git
✅ Configuration writtenBe sure you memorize or store securely the passphrase that you either entered or generated above - if you don't have this, you won't be able to recover your secrets!
Your AGE key should be backed up securely in the same manner as you would handle any other key-pair (e.g. an SSH key) - in the event of a loss of your system, the only copy of the private key is stored in your home directory tree and so you would be unable to decrypt your secrets on a new or replacement system without this.
To back up the key, run the following command:
$ cat ~/.config/gopass/age/identities | age -d
AGE-SECRET-KEY-1XXXXXX-XXXX-XXXXXXXXX-XXXXXXStore the resulting string in a secure manner, consistent with how you back up other private keys. You can also back up the ~/.config/gopass/age/identities file directly, but you will need the decrypted string beginning AGE-SECRET-KEY in a DR scenario.
- Public key: Starts with
age1...- use this to encrypt - you saw this in the wizard output in Step 3 - Secret key: Starts with
AGE-SECRET-KEY-1...- use this to decrypt (retrieved above)
For convenience, you can see the public key in future by running:
$ gopass recipients
Hint: run 'gopass sync' to import any missing public keys
gopass
└── age1m9nkvjx2hns6rha2pr62cmm323yacd8kmyued3uq9q5n9fy82guq38lym4Now let's create an SSH keypair that we'll store in Gopass.
# Generate ED25519 SSH keypair (modern and secure)
$ ssh-keygen -t ed25519 -C "workshop@cfgmgmtcamp" -f /tmp/workshop_ssh_key -N ""This creates:
/tmp/workshop_ssh_key- private key/tmp/workshop_ssh_key.pub- public key
# Private key (keep secret!)
$ cat /tmp/workshop_ssh_key
# Public key (safe to share)
$ cat /tmp/workshop_ssh_key.pub# Export GIT_SSH_COMMAND to save having to specify it on each gopass command invocation
$ export GIT_SSH_COMMAND="ssh -i ~/gopass-deploy-key -o IdentitiesOnly=yes"
# Store SSH private key
$ gopass insert -m ssh/workshop_private_key < /tmp/workshop_ssh_key# Store SSH public key
$ gopass insert -m ssh/workshop_public_key < /tmp/workshop_ssh_key.pub# Store your GitHub token from Lab 0
$ gopass insert github/personal_access_token
# When prompted, paste your token
# Press Enter
# Enter it a second time to verify as requestedLet's add a few more examples:
# Generate a random password
$ gopass generate web/example.com 32
# Add a note
$ gopass insert -m notes/ssh-usage
# Type your note, then Ctrl+DLearn how to retrieve secrets from Gopass.
# List all stored secrets
$ gopass lsShould show:
gopass
├── github/
│ └── personal_access_token
├── notes/
│ └── ssh-usage
├── ssh/
│ ├── workshop_private_key
│ └── workshop_public_key
└── web/
└── example.com
# Show SSH private key
$ gopass show ssh/workshop_private_key
# Show just the password (first line)
$ gopass show -o ssh/workshop_public_key# Copy to clipboard (if clipboard is available)
$ gopass show -c web/example.com
# Secret is copied for 45 seconds, then clipboard is clearedGopass integrates well with scripts:
# Get secret in a script
TOKEN=$(gopass show -o github/personal_access_token)
echo "Token: $TOKEN"
# Use in a command
curl -H "Authorization: token $(gopass show -o github/personal_access_token)" \
https://api.github.com/userYour secrets are encrypted locally. Let's back them up to GitHub.
$ gopass sync
🚥 Syncing with all remotes ...
[<root>]
gitfs pull and push ... OK (no changes)
done
✅ All doneNote: The files in Git are encrypted! Even though the repository is on GitHub, only you can decrypt the secrets with your AGE key.
- Go to
https://github.com/YOUR_USERNAME/gopass-store - You should see encrypted files (
.gpg-id,.gopass.yml, and secret files) - Open one of the secret files
- It should contain encrypted gibberish - this is correct!
# View files
$ ls -la ~/.local/share/gopass/stores/rootYou'll see:
.age-recipiets- Contains your AGE public keyssh/- Directory with encrypted secret filesgithub/- Directory with encrypted files- etc.
# Look at raw encrypted file
$ cat ~/.local/share/gopass/stores/root/ssh/workshop_private_key.ageIt's encrypted! This is what's stored in Git.
What's secret:
- Your AGE private key (
~/.config/gopass/age/identities) - Decrypted secrets (only in memory when accessed)
What's not secret:
- Encrypted files (safe to store in Git)
- AGE public key (used only for encryption)
- Directory structure (you can see secret names)
Important: Never commit your AGE private key to Git!
Let's simulate losing your machine and recovering your secrets.
First, ensure your AGE key is backed up somewhere safe:
# Display your AGE key (copy this somewhere safe outside the VM)
$ cat ~/.config/gopass/age/identities | age -d
# In real life, you might:
# - Print it and store physically
# - Save to an encrypted USB drive
# - Store in a secure vaultCRITICAL: Without this key, you cannot decrypt your secrets!
If you want to test recovery:
# Create a second toolbox to simulate a new machine
$ toolbox create workshop-recovery
$ toolbox enter workshop-recovery
# Install dependencies
$ sudo dnf install -y gopass age
# Toolbox has shared access to your existing home directory (where your gopass secrets live!), so we'll create a separate user under which to perform the recovery.
$ sudo useradd recovery
$ sudo cp gopass-deploy-key /home/recovery/
$ sudo chown recovery:recovery /home/recovery/gopass-deploy-key
$ sudo su - recoveryIn the new user account:
$ gopass age identities add
Enter the age identity starting in AGE-: <PASTE YOUR AGE SECRET KEY HERE>
Enter your PIN: <PASTE YOUR PASSPHRASE HERE>
Retype your PIN: <PASTE YOUR PASSPHRASE HERE>
⚠ New age identities are not automatically added to your recipient list, consider adding it using 'gopass recipients add age1m9nkvjx2hns6rha2pr62cmm323yacd8kmyued3uq9q5n9fy82guq38lym4'
⚠ If you do not add this recipient to the recipient list, make sure to re-encrypt using 'gopass fsck --decrypt' to properly support this identity$ GIT_SSH_COMMAND="ssh -i ~/gopass-deploy-key -o IdentitiesOnly=yes" \
gopass --yes setup --remote git@github.com:YOUR_USERNAME/gopass-store.git \
--name "James Freeman" --email "sysadmin@example.com" --crypto age
__ _ _ _ _ _ ___ ___
/'_ '\ /'_'\ ( '_'\ /'_' )/',__)/',__)
( (_) |( (_) )| (_) )( (_| |\__, \\__, \
'\__ |'\___/'| ,__/''\__,_)(____/(____/
( )_) | | |
\___/' (_)
🌟 Welcome to gopass!
🌟 Initializing a new password store ...
🔐 Using crypto backend: age
💾 Using storage backend: gitfs
Enter your PIN:
Joining existing team ...
🌟 Configuring your password store ...
Enter your PIN:
✅ Configuration written
[gopass]Configuring git remote ...
[gopass]Cloning from the git remote ...
The authenticity of host 'github.com (20.26.156.215)' can't be established.
ED25519 key fingerprint is SHA256:+DiY3wvvV6TuJJhbpZisF/zLDA0zPMSvHdkr4UvCOqU.
This key is not known by any other names.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
[gopass]✅ Done. Joined Team "gopass"
[gopass]⚠ You still need to request access to decrypt secrets!
# List secrets
$ export GIT_SSH_COMMAND="ssh -i ~/gopass-deploy-key -o IdentitiesOnly=yes"
$ gopass ls
# Retrieve a secret
$ gopass show ssh/workshop_public_keyIf you can decrypt and see your secrets, recovery was successful!
# Add a new secret
$ gopass insert service/api-key
# Gopass automatically commits to Git
$ gopass show service/api-key # Verify it's there$ gopass sync# Search for secrets
$ gopass search ssh
# Find secrets with fuzzy matching
$ gopass find github# Edit an existing secret
$ gopass edit ssh/workshop_private_key
# This opens your default editor# Remove a secret
$ gopass rm web/example.com
# Confirm deletion# List all secrets
$ gopass ls
# Show a secret
$ gopass show path/to/secret
# Show only password (first line)
$ gopass show -o path/to/secret
# Copy to clipboard
$ gopass show -c path/to/secret
# Insert new secret
$ gopass insert path/to/secret
# Insert multiline secret
$ gopass insert -m path/to/secret
# Generate random password
$ gopass generate path/to/secret 32
# Edit secret
$ gopass edit path/to/secret
# Delete secret
$ gopass rm path/to/secret
# Search secrets
$ gopass search keyword
# Sync with Git
$ gopass syncError: no valid decryption key
Solution:
# Verify AGE key location
$ ls -la ~/.config/gopass/age/identitiesError: authentication failed
Solution:
- Verify you're using Personal Access Token, not your GitHub password
- Check token has
repopermissions - Generate a new token if needed
- If you are using SSH access, make sure you have exported
GIT_SSH_COMMANDor otherwise configured your private key
This is critical - without your AGE private key, secrets are unrecoverable.
Prevention:
- Back up key to secure location
- Print and store physically
- Use a password manager for the key itself
- Consider storing encrypted in multiple locations
-
Protect Your AGE Private Key:
- Never commit to Git
- Never share with others
- Back up securely
- Use strong filesystem permissions (600)
-
Use Private Git Repositories:
- Even though encrypted, use private repos
- Reduces metadata exposure
- Extra layer of security
-
Unique Keys Per Person:
- Don't share AGE keys between people
- Use Gopass team features for sharing
-
Regular Backups:
- Push to Git regularly
- Test recovery periodically
- Keep AGE key backup up-to-date
-
Audit Secret Access:
- Review Git history
- Know what secrets exist
- Remove old/unused secrets
- AGE provides simple, modern encryption
- Gopass stores secrets encrypted in Git
- Your AGE private key is the only thing you must protect
- Encrypted secrets are safe to store in version control
- Recovery requires your AGE key and Git repository
- Gopass integrates well with scripts and automation
- The command-line interface is simple and consistent
Before moving to the next lab:
- AGE installed and key pair generated
- Gopass installed and initialized with AGE
- SSH keypair created and stored in Gopass
- GitHub Personal Access Token stored in Gopass
- Gopass store backed up to GitHub repository
- Can retrieve secrets with
gopass show - Understand the security model
- AGE private key backed up securely
Excellent work! You now have a secure, version-controlled password store. In the next lab, we'll use Chezmoi to manage dotfiles and integrate it with Gopass to automatically deploy your SSH keys and configuration.
Previous: Lab 3: Installing and Running Your Custom Image Next: Lab 5: Managing Dotfiles with Chezmoi