PKI ACME OpenShift

From Dogtag
Jump to: navigation, search

Overview

This page describes the process to deploy PKI ACME Container on OpenShift.

Current Issues

  • This is still a work in progress.
  • The ACME instance cannot be deployed directly in OpenShift yet. The instance needs to be created locally then uploaded to OpenShift.

Certificates

Prepare an NSS database:

$ mkdir nssdb
$ certutil -N -d nssdb

Import the certificates into the NSS database.

Encode the files:

$ CERTDB=`cat nssdb/cert9.db | base64 -w0`
$ KEYDB=`cat nssdb/key4.db | base64 -w0`
$ PKCS11=`cat nssdb/pkcs11.txt | base64 -w0`

Prepare a file to store the NSS database files as a secret (e.g. acme-nssdb.yaml):

apiVersion: v1
kind: Secret
type: Opaque
metadata:
  name: acme-nssdb
data:
  cert9.db: $CERTDB
  key4.db: $KEYDB
  pkcs11.txt: $PKCS11

Then execute the following command:

$ oc create -f acme-nssdb.yaml

Configuration

Prepare ACME configuration files:

  • metadata.xml
  • database.xml
  • backend.xml

Encode the files:

$ METADATA=`cat metadata.xml | base64 -w0`
$ DATABASE=`cat database.xml | base64 -w0`
$ BACKEND=`cat backend.xml | base64 -w0`

Prepare a file to store the configuration files as a secret (e.g. acme-conf.yaml):

apiVersion: v1
kind: Secret
type: Opaque
metadata:
  name: acme-conf
data:
  metadata.xml: $METADATA
  database.xml: $DATABASE
  backend.xml: $BACKEND

Then execute the following command:

$ oc create -f acme-conf.yaml

Creating Local ACME Instance

Create Tomcat instance:

$ pki-server create tomcat@acme

Deploy ACME web application:

$ pki-server acme-create -i tomcat@acme
$ pki-server acme-deploy -i tomcat@acme

Then create a backup:

$ tar czvf acme.tar.gz -C / \
    var/lib/tomcats/acme

Creating Persistent Storage

Prepare a configuration file (e.g. acme-pvc.yaml):

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: acme
spec:
  accessModes:
  - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi

Then execute the following command:

$ oc create -f acme-pvc.yaml

Deploying ACME Image

To deploy ACME manually:

apiVersion: v1
kind: DeploymentConfig
spec:
  selector:
    app: acme
    deploymentconfig: acme
  template:
    metadata:
      labels:
        app: acme
        deploymentconfig: acme
    spec:
      containers:
        - image: edewata/acme
          name: acme
          ports:
            - containerPort: 8080
              protocol: TCP
          volumeMounts:
            - mountPath: /data
              name: data
            - mountPath: /var/lib/tomcats/acme/alias
              name: acme-nssdb
              readOnly: true
            - mountPath: /var/lib/tomcats/acme/conf/acme
              name: acme-conf
              readOnly: true
      volumes:
        - name: data
          persistentVolumeClaim:
            claimName: acme
        - name: acme-nssdb
          secret:
            secretName: acme-nssdb
        - name: acme-conf
          secret:
            secretName: acme-conf
  triggers:
    - type: ConfigChange
    - imageChangeParams:
        containerNames:
          - acme
        from:
          kind: ImageStreamTag
          name: 'acme:latest'
      type: ImageChange

To deploy ACME automatically:

$ oc new-app edewata/acme:latest --name acme
$ oc expose svc/acme --name=acme --hostname=acme.demo.dogtagpki.org

Add a CNAME DNS record to map to the canonical hostname.

Verify by opening the following URL in a browser:

Creating ACME Service

Prepare a configuration file (e.g. acme-svc.yaml):

apiVersion: v1
kind: Service
metadata:
  labels:
    app: acme
  name: acme
spec:
  ports:
    - name: 8080-tcp
      port: 8080
      protocol: TCP
      targetPort: 8080
    - name: 8443-tcp
      port: 8443
      protocol: TCP
      targetPort: 8443
  selector:
    app: acme
    deploymentconfig: acme
  sessionAffinity: None
  type: ClusterIP

Then execute the following command:

$ oc create -f acme-svc.yaml

Creating DNS Record

Create a CNAME record for the public hostname (e.g. acme.demo.dogtagpki.org) pointing to the router's canonical hostname provided by OpenShift.

Creating ACME Route

Prepare a configuration file (e.g. acme-route.yaml):

apiVersion: v1
kind: Route
metadata:
  labels:
    app: acme
  name: acme
spec:
  host: acme.demo.dogtagpki.org
  port:
    targetPort: 8080-tcp
  to:
    kind: Service
    name: acme
    weight: 100
  wildcardPolicy: None

Then execute the following command:

$ oc create -f acme-route.yaml

Generating Internal SSL Certificate

To generate HTTPS connector:

$ pki-server http-connector-add -i tomcat@acme \
  --port 8443 \
  --scheme https \
  --secure true \
  --sslEnabled true \
  --sslProtocol SSL \
  Secure

To generate internal SSL certificate:

$ openssl req \
  -newkey rsa:2048 \
  -x509 \
  -nodes \
  -days 365 \
  -subj "/CN=acme.pki-demo.svc" \
  -keyout /var/lib/tomcats/acme/conf/sslserver.key \
  -out /var/lib/tomcats/acme/conf/sslserver.crt

To install internal SSL certificate:

$ pki-server http-connector-cert-add -i tomcat@acme \
  --certFile /var/lib/tomcats/acme/conf/sslserver.crt \
  --keyFile /var/lib/tomcats/acme/conf/sslserver.key

Generating External SSL Certificate

Run the following command to request a certificate from Let's Encrypt:

$ certbot certonly --manual -d acme.demo.dogtagpki.org --register-unsafely-without-email

Create the ACME response:

$ oc rsh <pod> mkdir -p /var/lib/tomcats/acme/webapps/.well-known/acme-challenge
$ oc rsync acme-challenge/ <pod>:/var/lib/tomcats/acme/webapps/.well-known/acme-challenge

Create an unsecure HTTP route for port 8080 of the PKI CA service, then complete the ACME validation. The results will be stored in:

  • certificate: /etc/letsencrypt/live/acme.demo.dogtagpki.org/fullchain.pem
  • key: /etc/letsencrypt/live/acme.demo.dogtagpki.org/privkey.pem

Installing External SSL Certificate

Remove the current route:

$ oc delete routes/acme

Prepare a configuration file (e.g. acme-route-secure.yaml):

apiVersion: v1
kind: Route
metadata:
  labels:
    app: acme
  name: acme
spec:
  host: acme.demo.dogtagpki.org
  port:
    targetPort: 8443-tcp
  tls:
    caCertificate: |-
      -----BEGIN CERTIFICATE-----
      ...
      -----END CERTIFICATE-----
    certificate: |-
      -----BEGIN CERTIFICATE-----
      ...
      -----END CERTIFICATE-----
    destinationCACertificate: |-
      -----BEGIN CERTIFICATE-----
      ...
      -----END CERTIFICATE-----
    insecureEdgeTerminationPolicy: Redirect
    key: |-
      -----BEGIN PRIVATE KEY-----
      ...
      -----END PRIVATE KEY-----
    termination: reencrypt
  to:
    kind: Service
    name: acme
    weight: 100
  wildcardPolicy: None

Then execute the following command:

$ oc create -f acme-route-secure.yaml

To validate, open https://acme.demo.dogtagpki.org with a browser.

Updating ACME Image

To deploy newer ACME image:

$ oc import-image edewata/acme

It will automatically respawn the pods with the new image.

Testing with an ACME Client

To submit an ACME request:

$ certbot certonly --manual \
    -d server.example.com \
    --register-unsafely-without-email \
    --preferred-challenges dns \
    --server http://acme.demo.dogtagpki.org/acme/directory

See Also