mirror of
https://github.com/Telecominfraproject/OpenNetworkLinux.git
synced 2025-12-27 18:25:00 +00:00
ONL-CONFIG datastore and PKI infrastructure.
This commit is contained in:
6
packages/base/all/boot.d/src/51.pki
Executable file
6
packages/base/all/boot.d/src/51.pki
Executable file
@@ -0,0 +1,6 @@
|
||||
#!/bin/sh
|
||||
if [ -f /usr/bin/pki ]; then
|
||||
/usr/bin/pki --init
|
||||
fi
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
3
packages/base/all/vendor-config-onl/src/bin/pki
Executable file
3
packages/base/all/vendor-config-onl/src/bin/pki
Executable file
@@ -0,0 +1,3 @@
|
||||
#!/usr/bin/python
|
||||
from onl.pki import OnlPKI
|
||||
OnlPKI.main()
|
||||
@@ -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))
|
||||
|
||||
|
||||
|
||||
129
packages/base/all/vendor-config-onl/src/python/onl/pki/__init__.py
Executable file
129
packages/base/all/vendor-config-onl/src/python/onl/pki/__init__.py
Executable 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()
|
||||
Reference in New Issue
Block a user