ONL-CONFIG datastore and PKI infrastructure.

This commit is contained in:
Jeffrey Townsend
2016-04-21 01:00:04 +00:00
parent 16284d277f
commit 2640aec6d8
6 changed files with 301 additions and 1 deletions

View File

@@ -0,0 +1,6 @@
#!/bin/sh
if [ -f /usr/bin/pki ]; then
/usr/bin/pki --init
fi

View File

@@ -0,0 +1,7 @@
C: US
ST: CA
O: Open Compute Project
localityName: Santa Clara
commonName: Networking
organizationalUnitName: Open Network Linux
emailAddress: support@bigswitch.com

View File

@@ -25,5 +25,6 @@ packages:
files:
src/python/onl : /usr/lib/python2.7/onl
src/bin/initmounts : /bin/initmounts
src/bin/pki : /sbin/pki
changelog: Changes

View File

@@ -0,0 +1,3 @@
#!/usr/bin/python
from onl.pki import OnlPKI
OnlPKI.main()

View File

@@ -7,6 +7,8 @@ import logging
import time
import json
import yaml
import tempfile
import shutil
class OnlMountManager(object):
def __init__(self, mdata, logger):
@@ -88,7 +90,7 @@ class OnlMountManager(object):
out = subprocess.check_output("umount %s" % v['device'], shell=True)
self.logger.info("%s now unmounted." % (k))
except subprocess.CalledProcessError,e:
self.logger.error("Could not unmount %s @ %s: " % (k, v['device'], e.output))
self.logger.error("Could not unmount %s @ %s: %s" % (k, v['device'], e.output))
continue
#
# FS Checks
@@ -151,3 +153,155 @@ class OnlMountManager(object):
mm.mountall(all_=True, fsck=False)
else:
mm.mountall()
############################################################
#
# Fix this stuff
#
############################################################
class ServiceMixin(object):
def _execute(self, cmd, root=False, ex=True):
self.logger.debug("Executing: %s" % cmd)
if root is True and os.getuid() != 0:
cmd = "sudo " + cmd
try:
subprocess.check_call(cmd, shell=True)
except Exception, e:
if ex:
self.logger.error("Command failed: %s" % e)
raise
else:
return e.returncode
def _raise(self, msg, klass):
self.logger.critical(msg)
raise klass(msg)
class DataMount(ServiceMixin):
def __init__(self, partition, logger=None):
self.partition = partition
self.logger = logger
self.mountpoint = None
self.mounted = False
if os.path.isabs(partition) and not os.path.exists(partition):
# Implicitly a bind mount. It may not exist yet, so create it
os.makedirs(partition)
if os.path.exists(partition):
# Bind mount
self.device = None
else:
self.device = subprocess.check_output("blkid | grep %s | awk '{print $1}' | tr -d ':'" % self.partition, shell=True).strip()
if self.device is None or len(self.device) is 0:
self._raise("Data partition %s does not exist." % self.partition,
RuntimeError)
self.logger.debug("device is %s" % self.device)
def _mount(self):
if self.device:
self._execute("mount %s %s" % (self.device, self.mountdir()), root=True)
else:
self._execute("mount --bind %s %s" % (self.partition,
self.mountdir()), root=True)
self.mounted = True
def _umount(self):
mounted, self.mounted = self.mounted, False
if mounted:
self._execute("umount %s" % self.mountpoint, root=True)
mountpoint, self.mountpoint = self.mountpoint, None
if mountpoint and os.path.exists(mountpoint):
self.logger.debug("+ /bin/rmdir %s", mountpoint)
os.rmdir(mountpoint)
def __enter__(self):
self._mount()
return self
def __exit__(self, type_, value, traceback):
self._umount()
def mountdir(self):
if self.mountpoint is None:
self.mountpoint = tempfile.mkdtemp(prefix="pki-", suffix=".d")
self.logger.debug("mountpoint is %s" % self.mountpoint)
return self.mountpoint
class OnlDataStore(ServiceMixin):
# Data partition containing the persistant store
DATA_PARTITION='ONL-CONFIG'
# Persistant directory on DATA_PARTITION
P_DIR=None
# Runtime directory in the root filesystem
R_DIR=None
def __init__(self, logger=None):
if logger is None:
logging.basicConfig()
logger = logging.getLogger(str(self.__class__))
logger.setLevel(logging.WARN)
self.logger = logger
self.mount = DataMount(self.DATA_PARTITION, logger=self.logger)
if self.P_DIR is None:
raise AttributeError("P_DIR must be set in the derived class.")
if self.R_DIR is None:
raise ValueError("R_DIR must be set in the derived class.")
# The R_DIR is accessed here
self.r_dir = self.R_DIR
self.logger.debug("persistant dir: %s" % self.p_dir)
self.logger.debug(" runtime dir: %s" % self.r_dir)
@property
def p_dir(self):
return os.path.join(self.mount.mountdir(), self.P_DIR)
def _sync_dir(self, src, dst):
self.logger.debug("Syncing store from %s -> %s" % (src, dst))
if os.path.exists(dst):
shutil.rmtree(dst)
if not os.path.exists(src):
os.makedirs(src)
shutil.copytree(src, dst)
def init_runtime(self):
with self.mount:
self._sync_dir(self.p_dir, self.r_dir)
def commit_runtime(self):
with self.mount:
self._sync_dir(self.r_dir, self.p_dir)
def diff(self):
with self.mount:
rv = self._execute("diff -rNq %s %s" % (self.p_dir, self.r_dir), ex=False)
return rv == 0
def ls(self):
with self.mount:
self._execute("cd %s && find ." % (self.p_dir))
def rm(self, filename):
with self.mount:
os.unlink(os.path.join(self.p_dir, filename))
os.unlink(os.path.join(r_dir, filename))

View File

@@ -0,0 +1,129 @@
#!/usr/bin/python
############################################################
#
# ONL PKI Management
#
############################################################
import sys
import os
import argparse
import logging
import tempfile
import shutil
import subprocess
import tempfile
import yaml
from onl.mounts import OnlDataStore
class OnlPKI(OnlDataStore):
P_DIR='private'
R_DIR='/private'
PRIVATE_KEY='key.pem'
CERTIFICATE='certificate'
KLEN=2048
CDAYS=3650
CSR_FILE='/etc/onl/config/csr.yml'
def __init__(self, logger=None):
OnlDataStore.__init__(self, logger)
self.kpath = os.path.join(self.R_DIR, self.PRIVATE_KEY)
self.cpath = os.path.join(self.R_DIR, self.CERTIFICATE)
def init_cert(self, force=False):
if not os.path.exists(self.cpath) or force:
self.logger.info("Generating self-signed certificate...")
#
# The csr.yml file allows the system integrator to customize
# the fields for the certificate.
#
fdict = {}
try:
fdict = yaml.load(open(self.CSR_FILE))
except Exception, e:
self.logger.error(e);
csr = tempfile.NamedTemporaryFile(prefix="pki-", suffix=".csr", delete=False)
csr.close()
fields = [ "%s=%s" % (k, v) for k,v in fdict.iteritems() ]
subject = "/" + "/".join(fields)
self.logger.debug("Subject: '%s'", subject)
self.logger.debug("CSR: %s", csr.name)
self._execute("""openssl req -new -batch -subj "%s" -key %s -out %s""" % (
subject, self.kpath, csr.name))
self._execute("""openssl x509 -req -days %s -in %s -signkey %s -out %s""" % (
self.CDAYS,
csr.name, self.kpath, self.cpath))
os.unlink(csr.name)
else:
self.logger.info("Using existing certificate.")
def init_key(self, force=False):
if not os.path.exists(self.kpath) or force:
self.logger.info("Generating private key...")
cmd = "openssl genrsa -out %s %s" % (self.kpath, self.KLEN)
self._execute(cmd)
self.init_cert(force=True)
else:
self.logger.info("Using existing private key.")
def init(self, force=False):
self.init_key(force=force)
self.init_cert(force=force)
self.commit_runtime()
@staticmethod
def main():
ap = argparse.ArgumentParser(description="ONL PKI Management")
ap.add_argument("--init", action='store_true', help="Initialize /private and PKI files if necessary.")
ap.add_argument("--regen-cert", action='store_true', help="Regenerate certificate.")
ap.add_argument("--force", "-f", action='store_true', help="Force regeneration of the key and certificate during initialization (--init)")
ap.add_argument("--commit", action='store_true', help="Commit the runtime /private directory to the persistant storage.")
ap.add_argument("--ls", action='store_true', help="List contents of the peristant directory.")
ap.add_argument("--quiet", "-q", action='store_true', help="Quiet output.")
ap.add_argument("--verbose", "-v", action='store_true', help="Verbose output.")
ap.add_argument("--part", help='Override Data Partition (testing only).')
ap.add_argument("--rd", help='Override /private runtime directory (testing only)')
ops = ap.parse_args()
logging.basicConfig()
logger = logging.getLogger("PKI")
if ops.verbose:
logger.setLevel(logging.DEBUG)
elif ops.quiet:
logger.setLevel(logging.WARNING)
else:
logger.setLevel(logging.INFO)
if ops.part:
OnlPKI.DATA_PARTITION=ops.part
if ops.rd:
OnlPKI.R_DIR=ops.rd
pki = OnlPKI(logger)
if ops.init:
pki.init_runtime()
pki.init_key(force=ops.force)
pki.init_cert(force=ops.force)
pki.commit_runtime()
elif ops.regen_cert:
pki.init_cert(force=True)
pki.commit_runtime()
elif ops.commit:
pki.commit_runtime()
elif ops.ls:
pki.ls()
else:
pki.diff()