From 564c29b8e1cb26a8d4ec5bb7148daa2ade5b2567 Mon Sep 17 00:00:00 2001 From: Mark Peek Date: Sun, 9 Mar 2014 17:19:53 -0700 Subject: [PATCH] Add sqlite CA backing store type --- CHANGELOG.md | 3 ++ scripts/sign_key | 12 +++++-- ssh_ca/sqlite.py | 87 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 100 insertions(+), 2 deletions(-) create mode 100755 ssh_ca/sqlite.py diff --git a/CHANGELOG.md b/CHANGELOG.md index bfd64fc..8bab3a9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +## In progress +- Add sqlite option for the CA backing store type + ## 0.2.0 (2014-02-25) - Add environment-specific public keys - Run ssh-add as part of get cert so openssh will detect the new cert diff --git a/scripts/sign_key b/scripts/sign_key index ca057af..470fc05 100755 --- a/scripts/sign_key +++ b/scripts/sign_key @@ -27,6 +27,13 @@ from contextlib import closing import ssh_ca import ssh_ca.s3 +import ssh_ca.sqlite + + +ca_map = { + 's3': ssh_ca.s3.S3Authority, + 'sqlite': ssh_ca.sqlite.SqliteAuthority, +} if __name__ == '__main__': @@ -37,7 +44,8 @@ if __name__ == '__main__': parser = argparse.ArgumentParser(__doc__) parser.add_argument('-a', '--authority', dest='authority', default=default_authority, - help='Pick one: s3', + choices=['s3', 'sqlite'], + help='Specify CA backing store type', ) parser.add_argument('-c', '--config-file', default=default_config, @@ -99,7 +107,7 @@ if __name__ == '__main__': try: # Create our CA - ca = ssh_ca.s3.S3Authority(config, ssh_ca_section, ca_key) + ca = ca_map[args.authority](config, ssh_ca_section, ca_key) except ssh_ca.SSHCAInvalidConfiguration, e: print 'Issue with creating CA: %s' % e.message sys.exit(1) diff --git a/ssh_ca/sqlite.py b/ssh_ca/sqlite.py new file mode 100755 index 0000000..3a1f809 --- /dev/null +++ b/ssh_ca/sqlite.py @@ -0,0 +1,87 @@ +import datetime +import json +import sqlite3 + +import ssh_ca + + +class SqliteAuthority(ssh_ca.Authority): + def __init__(self, config, ssh_ca_section, ca_key): + super(SqliteAuthority, self).__init__(ca_key) + + self.dbfile = ssh_ca.get_config_value( + config, ssh_ca_section, 'dbfile', required=True) + self.conn = sqlite3.connect(self.dbfile) + self._check_schema() + + def _check_schema(self): + version = self.conn.execute('PRAGMA user_version').fetchone() + if version[0] == 0: + with self.conn: + self.conn.execute('PRAGMA user_version=1') + self.conn.execute( + 'create table keys (name, environment, public_key)') + self.conn.execute( + 'create table serial (row, serial integer)') + self.conn.execute( + 'create table audit_log (entry integer primary key, log)') + self.conn.execute( + 'insert into serial (row, serial) values (1, 0)') + + def increment_serial_number(self): + with self.conn: + self.conn.execute('update serial set serial=serial+1 where row=1') + cur = self.conn.execute('select serial from serial where row=1') + new_serial = cur.fetchone()[0] + return new_serial + + def get_public_key(self, username, environment): + select = 'select public_key from keys where name is ?' + args = (username, ) + cur = self.conn.execute(select, args) + result = cur.fetchone() + if result: + return result[0] + else: + return None + + def upload_public_key(self, username, key_file): + key = open(key_file).read() + arglist = (username, key) + insert_stmt = 'insert into keys (name, public_key) values (?, ?)' + with self.conn: + self.conn.execute(insert_stmt, arglist) + + def upload_public_key_cert(self, username, cert_contents): + return "%s: %s" % (username, cert_contents) + + def make_host_audit_log(self, serial, valid_for, ca_key_filename, + reason, hostnames): + audit_info = { + 'valid_for': valid_for, + 'ca_key_filename': ca_key_filename, + 'reason': reason, + 'hostnames': hostnames, + } + return self.drop_audit_blob(serial, audit_info) + + def make_audit_log(self, serial, valid_for, username, + ca_key_filename, reason, principals): + audit_info = { + 'username': username, + 'valid_for': valid_for, + 'ca_key_filename': ca_key_filename, + 'reason': reason, + 'principals': principals, + } + return self.drop_audit_blob(serial, audit_info) + + def drop_audit_blob(self, serial, blob): + timestamp = datetime.datetime.strftime( + datetime.datetime.utcnow(), '%Y-%m-%d-%H:%M:%S.%f') + blob['serial'] = serial + blob['timestamp'] = timestamp + + arglist = (None, json.dumps(blob)) + with self.conn: + self.conn.execute('insert into audit_log values (?, ?)', arglist)