2025-12-31
There are times when you need to access credentials from the command line or from within a shell script. For personal use, this often comes up when running one-off scripts, automations, or provisioning tasks that require a password as input. In those cases, solutions like AWS Secrets Manager add unnecessary complexity and overhead.
People often copy/paste the password from a password manager, or store it in plaintext. I'm assuming I don't have to sell you on the idea of why storing passwords in plaintext is a bad idea, so the latter solution is out the window. The first solution is also not great, as it's tedious at best, or insecure at worst (if your password gets stored in your shell's history).
What’s missing is a middle ground: something lightweight, scriptable, and secure, without introducing cloud dependencies or complex key-management systems. Luckily, there's a command line utility called Password Store (i.e. pass), which provides an easy way to locally store encrypted passwords using GPG.
To set up a password store, you need to generate a PGP key pair first. I'll skip over this step, as there are plenty of great tutorials online about it. Then, run the following command:
pass init <gpg-id>
By default, passwords are stored in ~/.password-store, but you can specify the location using the --path=sub-folder argument. You can also specify multiple gpg-ids.
Adding a password is just as simple:
pass insert <PASSWORD_NAME>
This will prompt you to enter the new password twice. The <PASSWORD_NAME> can be a simple name like bank_password, but you can also format it as a path, if you want to store multiple passwords in a directory structure, e.g. bank/savings_account.
List all the passwords that you have with pass ls (or just pass).
Password Store
├── bank
│ ├── savings_account
│ ├── joint_account
│ └── checking_account
└── email
├── me@workemail.com
└── me@personalemail.com
Passwords can also be generated, edited, copied, moved, deleted, etc. I'll leave you to read the documentation on those commands.
To retrieve a password, you only have to run pass <PASSWORD_NAME>. By default, it prints it to stdout. Naturally, you can use it in a bash script to retrieve a password at any point:
foo --password=$(pass bank/savings_account)
The same idea extends cleanly into infrastructure management. For example, my Ansible playbooks for provisioning remote servers include entries like this:
- name: fail2ban
hosts: my_remote_host
become: true
vars:
ansible_become_password: "{{ lookup('community.general.passwordstore', 'remote_machines/{{inventory_hostname}}', missing='warn')}}"
tasks:
- name: Configure fail2ban
ansible.builtin.import_tasks: tasks/config_fail2ban.yml
In this setup, the sudo password for each machine is stored by pass under remote_machines/<hostname>. When Ansible runs, it prompts me for my GPG passphrase, decrypts the password on demand, and uses it to gain elevated privileges.
To enable the use of pass in Ansible, you need to add the following to the ansible.cfg file:
[passwordstore_lookup]
backend = pass
pass is effective precisely because it follows the Unix philosophy: do one thing, and do it well. It doesn't try to be a full secret-management platform, a policy engine, or a cloud service. It stores encrypted secrets on disk and exposes them through a simple command-line interface. For local scripts, personal automation, and small-scale infrastructure management, it provides a secure, composable solution with almost no overhead.