Script to store passwords in a file with GPG or using OSX's secure keychain

Submitted-by: https://github.com/lorenzog
Signed-off-by: Nicolas Sebrecht <nicolas.s-dev@laposte.net>
This commit is contained in:
Lorenzo 2018-06-10 12:18:27 +01:00 committed by Nicolas Sebrecht
parent b5ffa1c163
commit c865dcc03c
4 changed files with 201 additions and 0 deletions

View File

@ -0,0 +1,37 @@
# gpg-offlineimap
Python bindings for offlineimap to use gpg instead of storing cleartext passwords
Author: Lorenzo G.
[GitHub](https://github.com/lorenzog/gpg-offlineimap)
## Quickstart
Requirements: a working GPG set-up. Ideally with gpg-agent. Should work
out of the box on most modern Linux desktop environments.
1. Enable IMAP in gmail (if you have two factor authentication, you
need to create an app-specific password)
2. Create a directory `~/Mail`
3. In `~/Mail`, create a password file `passwords-gmail.txt`. Format:
`account@gmail.com password`. Look at the example file in this
directory.
4. **ENCRYPT** the file: `gpg -e passwords-gmail.txt`. It should create
a file `passwords-gmail.txt.gpg`. Check you can decrypt it: `gpg -d
passwords-gmail.txt.gpg`: it will ask you for your GPG password and
show it to you.
5. Use the file `offlineimaprc.sample` as a sample for your own
`.offlineimaprc`; edit it by following the comments. Minimal items
to configure: the `remoteuser` field and the `pythonfile` parameter
pointing at the `offlineimap.py` file in this directory.
6. Run it: `offlineimap`. It should ask you for your GPG passphrase to
decrypt the password file.
7. If all works well, delete the cleartext password file.

View File

@ -0,0 +1,99 @@
#!/usr/bin/python
# Originally taken from: http://stevelosh.com/blog/2012/10/the-homely-mutt/
# by Steve Losh
# Modified by Lorenzo Grespan on Jan, 2014
import re
import subprocess
from sys import argv
import logging
from os.path import expanduser
import unittest
import os
import sys
logging.basicConfig(level=logging.INFO)
DEFAULT_PASSWORDS_FILE = os.path.join(
os.path.expanduser('~/Mail'),
'passwords.gpg')
def get_keychain_pass(account=None, server=None):
'''Mac OSX keychain password extraction'''
params = {
'security': '/usr/bin/security',
'command': 'find-internet-password',
'account': account,
'server': server,
'keychain': expanduser('~') + '/Library/Keychains/login.keychain',
}
command = ("%(security)s -v %(command)s"
" -g -a %(account)s -s %(server)s %(keychain)s" % params)
output = subprocess.check_output(
command, shell=True, stderr=subprocess.STDOUT)
outtext = [l for l in output.splitlines()
if l.startswith('password: ')][0]
return find_password(outtext)
def find_password(text):
'''Helper method for osx password extraction'''
# a non-capturing group
r = re.match(r'password: (?:0x[A-F0-9]+ )?"(.*)"', text)
if r:
return r.group(1)
else:
logging.warn("Not found")
return None
def get_gpg_pass(account, storage):
'''GPG method'''
command = ("gpg", "-d", storage)
# get attention
print '\a' # BEL
output = subprocess.check_output(command)
# p = subprocess.Popen(command, stdout=subprocess.PIPE)
# output, err = p.communicate()
for line in output.split('\n'):
r = re.match(r'{} ([a-zA-Z0-9]+)'.format(account), line)
if r:
return r.group(1)
return None
def get_pass(account=None, server=None, passwd_file=None):
'''Main method'''
if not passwd_file:
storage = DEFAULT_PASSWORDS_FILE
else:
storage = os.path.join(
os.path.expanduser('~/Mail'),
passwd_file)
if os.path.exists('/usr/bin/security'):
return get_keychain_pass(account, server)
if os.path.exists(storage):
logging.info("Using {}".format(storage))
return get_gpg_pass(account, storage)
else:
logging.warn("No password file found")
sys.exit(1)
return None
# test with: python -m unittest <this module name>
# really basic tests.. nothing to see. move along
class Tester(unittest.TestCase):
def testMatchSimple(self):
text = 'password: "exampleonetimepass "'
self.assertTrue(find_password(text))
def testMatchComplex(self):
text = r'password: 0x74676D62646D736B646970766C66696B0A "anotherexamplepass\012"'
self.assertTrue(find_password(text))
if __name__ == "__main__":
print get_pass(argv[1], argv[2], argv[3])

View File

@ -0,0 +1,63 @@
[general]
# GPG quirks, leave unconfigured
ui = ttyui
# you can use any name as long as it matches the 'account1, 'account2' in the rest
# of the file
accounts = account1, account2
# this is where the `gpg-pw.py` file is on disk
pythonfile=~/where/is/the/file/gpg-pw.py
fsync = False
# you can call this any way you like
[Account account1]
localrepository = account1-local
remoterepository = account1-remote
# no need to touch this
status_backend = sqlite
[Account account2]
localrepository = account2-local
remoterepository = account2-remote
status_backend = sqlite
# thi sis a gmail account
[Repository account1-local]
type = Maildir
# create with maildirmake or by hand by creating cur, new, tmp
localfolders = ~/Mail/Mailboxes/account1
# standard Gmail stuff
nametrans = lambda folder: { 'drafts': '[Gmail]/Drafts',
'sent': '[Gmail]/Sent mail',
'flagged': '[Gmail]/Starred',
'trash': '[Gmail]/Trash',
'archive': '[Gmail]/All Mail'
}.get(folder, folder)
[Repository account1-remote]
maxconnections = 1
type = Gmail
ssl=yes
# for osx, you might need to download the certs by hand
#sslcacertfile=~/Mail/certs.pem
#sslcacertfile=~/Mail/imap.gmail.com.pem
# sslcacertfile=/etc/ssl/cert.pem
# or use Linux's standard certs
sslcacertfile=/etc/ssl/certs/ca-certificates.crt
# your account
remoteuser = account1@gmail.com
remotepasseval = get_pass(account="account1@gmail.com", server="imap.gmail.com", passwd_file="passwords-gmail.txt.gpg")
realdelete = no
createfolders = no
nametrans = lambda folder: {'[Gmail]/Drafts': 'drafts',
'[Gmail]/Sent Mail': 'sent',
'[Gmail]/Starred': 'star',
'[Gmail]/Trash': 'trash',
'[Gmail]/All Mail': 'archive',
}.get(folder, folder)
folderfilter = lambda folder: folder not in ['[Gmail]/Trash',
'[Gmail]/Spam',
]
[Repository account2-remote]
# copy the stanza above, change the 'account' parameter of get_pass, etc.

View File

@ -0,0 +1,2 @@
account1@gmail.com password1
account2@gmail.com password2