Storing Script Passwords In Macs Keychain
TODO: I'm doing things a little diferently now. This note is to remind me to link that.
_Forward_
- I built my own functions to use Mac's Keychain Access for password storage, but you should probably checkout keyring to do the same thing and more.
I've been using GPG to encrypt my various credentials so I don't accidentally flash them while streaming or taking zoom calls. The process also ensures I never store passwords directly inside a script. A sin that's all to easy to commit.
I drop passwords into individual plain-text files and encrypt them with:
Code
-- -- -- - @
When I need one, I use this Python function to grab it:
Code
import subprocess
def get_credentials(file_path):
return subprocess.check_output([
'/usr/local/bin/gpg', '--decrypt',
'-q', file_path
]).decode('utf-8').split("\n")[0]
credential = get_credentials('PASSWORD_FILE.asc')
A bit tedious, but it works great.
I recently started working with AWS Secrets Manager](https://aws.amazon.com/secrets-manager/). It got me thinking about the [Keychain Access app](https://support.apple.com/guide/keychain-access/what-is-keychain-access-kyca1083/mac) on Macs. I wondered if it was possible to store credentials for my scripts there. I found [this great post](https://blog.koehntopp.info/2017/01/26/command-line-access-to-the-mac-keychain.html) from [Kristian Köhntopp that shows how to use it from the command line. Working from the article, I built these simple Python functions.
#### # set_security_credential.py
Code
#!/usr/bin/env python3
import subprocess
import getpass
def set_security_credential(*, credential_for, credential_value):
subprocess.run([
'security', 'add-generic-password', '-U',
'-a', getpass.getuser(), '-s',
credential_for, '-w', credential_value
])
set_security_credential(
credential_for = 'alans-example-api',
credential_value = 'lorem ipsum 9000'
)
#### # get_security_credentail.py
Code
#!/usr/bin/env python3
import getpass
import subprocess
def get_security_credential(*, credential_for):
return subprocess.run([
'security', 'find-generic-password',
'-w', '-a', getpass.getuser(),
'-s', credential_for
], stdout=subprocess.PIPE).stdout.decode('utf-8')
password_value = get_security_credential(
credential_for = 'alans-example-api'
)
print(password_value)
Because the values are both set and retrieved by the `security` cli tool under your user ID, they don't require an additional password to retrieve them. And, since it's always the `security` app working under the covers, the functions work across different scripts.
My next step will be to drop these into pages on a localhost website. That'll let me add and update credentials from my own little UI. I'll still keep everything in my password manager, but this is how I'll get to them with scripts.
I really like this approach. No more having to mess with credentials to obfuscate them. Just let the system's built tool take care of it.
_Afterword_
- It's important to point out that neither the GPG method nor the `security` cli method provide extra security since they aren't setup to require their own passwords. The only thing they provide is a straightforward way to keep passwords out of scripts and a way obfuscate/hide them so you don't accidentally expose them. - I did this in Python. The same approach can be used by any language/tool that can execute the `security` process and gather its output. - The Keychain Access app is Mac specific. Windows provides Credential locker](https://docs.microsoft.com/en-us/windows/uwp/security/credential-locker) which should be able to do the same thing. Doing that exploration is left as an exercise for the reader (Probably see [keyring.