prod{access,vider}: implement

Prodaccess/Prodvider allow issuing short-lived certificates for all SSO
users to access the kubernetes cluster.

Currently, all users get a personal-$username namespace in which they
have adminitrative rights. Otherwise, they get no access.

In addition, we define a static CRB to allow some admins access to
everything. In the future, this will be more granular.

We also update relevant documentation.

Change-Id: Ia18594eea8a9e5efbb3e9a25a04a28bbd6a42153
diff --git a/cluster/clustercfg/ca.py b/cluster/clustercfg/ca.py
index 4d359c7..9ed2053 100644
--- a/cluster/clustercfg/ca.py
+++ b/cluster/clustercfg/ca.py
@@ -4,6 +4,7 @@
 import os
 from six import StringIO
 import subprocess
+import tempfile
 
 
 logger = logging.getLogger(__name__)
@@ -32,6 +33,20 @@
       "expiry": "168h"
     },
     "profiles": {
+      "intermediate": {
+        "expiry": "8760h",
+        "usages": [
+          "signing",
+          "key encipherment",
+          "cert sign",
+          "crl sign",
+          "server auth",
+          "client auth",
+        ],
+        "ca_constraint": {
+          "is_ca": True,
+        },
+      },
       "server": {
         "expiry": "8760h",
         "usages": [
@@ -156,12 +171,17 @@
 
         return key, csr
 
-    def sign(self, csr, save=None):
+    def sign(self, csr, save=None, profile='client-server'):
         logging.info("{}: Signing CSR".format(self))
         ca = self._cert
         cakey = self.ss.plaintext(self._secret_key)
+
+        config = tempfile.NamedTemporaryFile(mode='w')
+        json.dump(_ca_config, config)
+        config.flush()
+
         out = self._cfssl_call(['sign', '-ca=' + ca, '-ca-key=' + cakey,
-                                '-profile=client-server', '-'], stdin=csr)
+                                '-profile='+profile, '-config='+config.name, '-'], stdin=csr)
         cert = out['cert']
         if save is not None:
             name = os.path.join(self.cdir, save)
@@ -170,6 +190,7 @@
             f.write(cert)
             f.close()
 
+        config.close()
         return cert
 
     def upload(self, c, remote_cert):
@@ -181,7 +202,7 @@
 
 
 class ManagedCertificate(object):
-    def __init__(self, ca, name, hosts, o=None, ou=None):
+    def __init__(self, ca, name, hosts, o=None, ou=None, profile='client-server'):
         self.ca = ca
 
         self.hosts = hosts
@@ -190,6 +211,7 @@
         self.cert = '{}.cert'.format(name)
         self.o = o
         self.ou = ou
+        self.profile = profile
 
         self.ensure()
 
@@ -230,7 +252,7 @@
 
         logger.info("{}: Generating...".format(self))
         key, csr = self.ca.gen_key(self.hosts, o=self.o, ou=self.ou, save=self.key)
-        self.ca.sign(csr, save=self.cert)
+        self.ca.sign(csr, save=self.cert, profile=self.profile)
 
     def upload(self, c, remote_cert, remote_key, concat_ca=False):
         logger.info("Uploading Cert {} to {} & {}".format(self, remote_cert, remote_key))