diff --git a/README.md b/README.md
index a65ecaba74f49036972b4c5ae7877c84c0a70d7d..482e4738e4bdd788b17f9dcd0f060883c09aa2bc 100644
--- a/README.md
+++ b/README.md
@@ -7,30 +7,21 @@ Installation
 ------------
 
 Edit soctools-inventory and add the desired docker containers to be deployed. The playbook has been tested on CentOS 7.
+Edit settings in group_vars/all/main.yml.
 
-Run the ansible playbook:
+To build the Docker images needed, run the ansible playbook:
+`ansible-playbook -i soctools-inventory buildimages.yml`
 
+To build the CA needed for host and user certificates, run the ansible playbook:
+`ansible-playbook -i soctools-inventory buildca.yml`
+User certificates are exported in roles/ca/files/CA/private.
 
+To start and stop the cluster, run the ansible playbook soctools.yml:
 `ansible-playbook -i soctools-inventory soctools.yml -t start` to start the cluster.
 `ansible-playbook -i soctools-inventory soctools.yml -t stop` to stop the cluster.
 
 The NiFi interface should now be available on port 443 on the server.
 
-This will install the following docker images:
- * zookeeper:latest
- * nginx:latest
- * apache/nifi:latest
-
-
-Building images
----------------
-
-Images that are not offical Docker images can be built from scratch by running:
-
-`ansible-playbook -i inventories/build/hosts.yml build_images.yml`
-
-Edit the files under inventories/deploy/group_vars to specify that built images should be used. Currently only NiFi is built from scratch.
-
 License
 -------
 
diff --git a/build_images.yml b/build_images.yml
deleted file mode 100644
index 2f3bfdf3fb3c21fb648d3d04962ced5401573aba..0000000000000000000000000000000000000000
--- a/build_images.yml
+++ /dev/null
@@ -1,34 +0,0 @@
----
-- name: Create containers
-  hosts: localhost
-  tasks:
-    - docker_container:
-        name: "{{ item }}"
-        image: "{{ base_image }}" 
-        command: ["sleep", "1d"]
-      loop:
-        - nifi-image
-      
-- name: Configure NiFi container
-  hosts: nifi-image
-  vars:
-    - docker:
-  roles:
-    - name: nifi
-
-- name: Create images
-  hosts: localhost
-  tasks:
-    - name: create a docker image from the container
-      command: "/usr/bin/docker commit
-       -c '{{ item.expose  }}'
-       -c '{{ item.cmd  }}'
-       {{ item.image  }}
-       {{ item.name  }}"
-      loop:                                                     
-        - { expose: 'EXPOSE 8080 8443 10000 8000', cmd: 'CMD [\"/opt/nifi/nifi-current/scripts/start.sh\", \"run\"]', image: 'nifi-image', name: 'nifi-soctools' }
-    - name: Save images to file
-      command: "/usr/bin/docker save -o {{ docker_image_path }}/{{ item }}.tar {{ item }}"
-      loop:
-        - nifi-soctools
-  
\ No newline at end of file
diff --git a/buildca.yml b/buildca.yml
new file mode 100644
index 0000000000000000000000000000000000000000..b718286139aa329e0a343f4ff60624cdfc6b3c34
--- /dev/null
+++ b/buildca.yml
@@ -0,0 +1,7 @@
+---
+
+- name: Build certification authority
+  hosts: dsldev
+  roles:
+    - ca
+
diff --git a/buildimages.yml b/buildimages.yml
new file mode 100644
index 0000000000000000000000000000000000000000..d30f905968d72cffd2aaf2973fcf65a33a3107b2
--- /dev/null
+++ b/buildimages.yml
@@ -0,0 +1,7 @@
+---
+
+- name: Build docker images
+  hosts: dsldev
+  roles:
+    - build
+
diff --git a/group_vars/all/main.yml b/group_vars/all/main.yml
index f4d24e3ef95886e920e827f00547e4061caad0be..722254e81e1e51cb8fbbae11fd3939228c2cbdfe 100644
--- a/group_vars/all/main.yml
+++ b/group_vars/all/main.yml
@@ -1,15 +1,33 @@
 ---
 
-soctools_netname: "dslxnifinet"
+soctools_netname: "dslnifinet"
+
+repo: gn43-dsl
+version: 7
+suffix: al
+
+temp_root: "/tmp/centosbuild"
+
+openjdk_img: "{{repo}}/openjdk:{{version}}{{suffix}}"
 
 zookeeper_name: "dsoclab-zookeeper"
-zookeeper_img: "gn43-dsl/zookeeper:latest"
+zookeeper_img: "{{repo}}/zookeeper:{{version}}{{suffix}}"
 
-nifi_img: "gn43-dsl/nifi:1.9.2-2s"
+nifi_img: "{{repo}}/nifi:{{version}}{{suffix}}"
 
 nginx_name: "dsoclab-nginx"
-nginx_img: "gn43-dsl/nginx:latest"
+nginx_img: "{{repo}}/nginx:{{version}}{{suffix}}"
 
 dslproxy: "dsldev.gn4-3-wp8-soc.sunet.se"
-kspass: "Testiranje"
-tspass: "Testiranje"
+
+kspass: "Testing003"
+tspass: "Testing003"
+
+javamem: "384m"
+
+ca_cn: "dsldev test ca"
+
+nifiadmin:
+  - [ "Bozidar Proevski", "Pass001" ]
+  - [ "Arne Oslebo", "Pass002" ]
+
diff --git a/roles/build/defaults/main.yml b/roles/build/defaults/main.yml
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/roles/build/handlers/main.yml b/roles/build/handlers/main.yml
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/roles/build/meta/main.yml b/roles/build/meta/main.yml
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/roles/build/tasks/centos.yml b/roles/build/tasks/centos.yml
new file mode 100644
index 0000000000000000000000000000000000000000..e8d492754d00e7521abc4cdf06f66b929449efd6
--- /dev/null
+++ b/roles/build/tasks/centos.yml
@@ -0,0 +1,96 @@
+---
+
+- name: Check for CentOS image
+  docker_image_info:
+    name: "{{repo}}/centos:{{version}}{{suffix}}"
+  register: centosimg
+
+- name: Assert CentOS image
+  assert:
+    that: centosimg.images | length == 0
+    fail_msg: "CentOS image already exists"
+
+- name: Create etc tree in build directory
+  file:
+    path: '{{ temp_root}}/{{ item.path }}'
+    state: directory
+    mode: '{{ item.mode }}'
+  with_filetree: templates/etcroot/
+  when: item.state == 'directory'
+
+- name: Populate etc tree in build directory
+  template:
+    src: '{{ item.src }}'
+    dest: '{{ temp_root}}/{{ item.path }}'
+    force: yes
+  with_filetree: templates/etcroot
+  when: item.state == 'file'
+
+- name: Create dev tree in build directory
+  command: mknod -m {{ item.mode }} {{ item.dev }} {{ item.type }} {{ item.major }} {{ item.minor }}
+  args:
+    creates: "{{ item.dev }}"
+  with_items:
+    - { mode: 600, dev: "{{temp_root}}/dev/console", type: c, major: 5, minor: 1 }
+    - { mode: 600, dev: "{{temp_root}}/dev/initctl", type: p, major: '', minor: '' }
+    - { mode: 666, dev: "{{temp_root}}/dev/full",    type: c, major: 1, minor: 7 }
+    - { mode: 666, dev: "{{temp_root}}/dev/null",    type: c, major: 1, minor: 3 }
+    - { mode: 666, dev: "{{temp_root}}/dev/ptmx",    type: c, major: 5, minor: 2 }
+    - { mode: 666, dev: "{{temp_root}}/dev/random",  type: c, major: 1, minor: 8 }
+    - { mode: 666, dev: "{{temp_root}}/dev/tty",     type: c, major: 5, minor: 0 }
+    - { mode: 666, dev: "{{temp_root}}/dev/tty0",    type: c, major: 4, minor: 0 }
+    - { mode: 666, dev: "{{temp_root}}/dev/urandom", type: c, major: 1, minor: 9 }
+    - { mode: 666, dev: "{{temp_root}}/dev/zero",    type: c, major: 1, minor: 5 }
+
+- name: Install centos-release in build directory
+  yum:
+    installroot: "{{ temp_root}}"
+    name: centos-release
+    state: present 
+ 
+- name: Install Core CentOS in build directory
+  yum:
+    installroot: "{{ temp_root}}"
+    name:
+      - "@Core"
+      - yum-plugin-ovl.noarch
+      - epel-release
+    state: present
+
+- name: Clean yum cache
+  command: 'yum --installroot="{{ temp_root}}" -y clean all'
+
+- name: Remove unneeded directories
+  file:
+    path: "{{temp_root}}/{{item}}"
+    state: absent
+  with_items:
+    - usr/lib/locale
+    - usr/share/locale
+    - usr/lib64/gconv
+    - usr/bin/localedef
+    - usr/sbin/build-locale-archive
+    - usr/share/cracklib
+    - usr/share/i18n
+    - var/cache/yum
+    - sbin/sln
+    - etc/ld.so.cache
+    - var/cache/ldconfig
+    - usr/share/backgrounds
+
+- name: Create needed directories
+  file:
+    path: "{{temp_root}}/{{item}}"
+    state: directory
+  with_items:
+    - var/cache/yum
+    - var/cache/ldconfig
+
+- name: Import image in docker
+  shell: tar --numeric-owner -c -C {{temp_root }} . | docker import - {{repo}}/centos:{{version}}{{suffix}}
+
+- name: Remove temp directory
+  file:
+    path: "{{temp_root}}"
+    state: absent
+
diff --git a/roles/build/tasks/main.yml b/roles/build/tasks/main.yml
new file mode 100644
index 0000000000000000000000000000000000000000..2c9da2f792a97aa9c9399e4e52eb31651cdfb4f9
--- /dev/null
+++ b/roles/build/tasks/main.yml
@@ -0,0 +1,7 @@
+---
+
+- include: centos.yml
+- include: nginx.yml
+- include: openjdk.yml
+- include: zookeeper.yml
+- include: nifi.yml
diff --git a/roles/build/tasks/nginx.yml b/roles/build/tasks/nginx.yml
new file mode 100644
index 0000000000000000000000000000000000000000..bf91a869f63923cc57407c93ee59c3233dde8588
--- /dev/null
+++ b/roles/build/tasks/nginx.yml
@@ -0,0 +1,10 @@
+---
+
+- name: Configure the nginx Dockerfile
+  template:
+    src: nginx/Dockerfile.j2
+    dest: "{{role_path}}/files/nginxDockerfile"
+
+- name: Build nginx image
+  command: docker build -t {{repo}}/nginx:{{version}}{{suffix}} -f {{role_path}}/files/nginxDockerfile {{role_path}}/files
+
diff --git a/roles/build/tasks/nifi.yml b/roles/build/tasks/nifi.yml
new file mode 100644
index 0000000000000000000000000000000000000000..423978d987b5ced99a995ddc5a3e733cf56504da
--- /dev/null
+++ b/roles/build/tasks/nifi.yml
@@ -0,0 +1,11 @@
+---
+
+- name: Configure the nifi Dockerfile
+  template:
+    src: nifi/Dockerfile.j2
+    dest: "{{role_path}}/files/nifiDockerfile"
+
+- name: Build nifi image
+  command: docker build -t {{repo}}/nifi:{{version}}{{suffix}} -f {{role_path}}/files/nifiDockerfile {{role_path}}/files
+
+
diff --git a/roles/build/tasks/openjdk.yml b/roles/build/tasks/openjdk.yml
new file mode 100644
index 0000000000000000000000000000000000000000..8754ac7fdf7d6147ab522f936b8888a5fd5a7e60
--- /dev/null
+++ b/roles/build/tasks/openjdk.yml
@@ -0,0 +1,11 @@
+---
+
+- name: Configure the openjdk Dockerfile
+  template:
+    src: openjdk/Dockerfile.j2
+    dest: "{{role_path}}/files/openjdkDockerfile"
+
+- name: Build openjdk image
+  command: docker build -t {{repo}}/openjdk:{{version}}{{suffix}} -f {{role_path}}/files/openjdkDockerfile {{role_path}}/files
+
+
diff --git a/roles/build/tasks/zookeeper.yml b/roles/build/tasks/zookeeper.yml
new file mode 100644
index 0000000000000000000000000000000000000000..a61a6b397f8d1d34559da24f290df8ea93b85f94
--- /dev/null
+++ b/roles/build/tasks/zookeeper.yml
@@ -0,0 +1,11 @@
+---
+
+- name: Configure the zookeeper Dockerfile
+  template:
+    src: zookeeper/Dockerfile.j2
+    dest: "{{role_path}}/files/zookeeperDockerfile"
+
+- name: Build zookeeper image
+  command: docker build -t {{repo}}/zookeeper:{{version}}{{suffix}} -f {{role_path}}/files/zookeeperDockerfile {{role_path}}/files
+
+
diff --git a/roles/build/templates/etcroot/etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7 b/roles/build/templates/etcroot/etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
new file mode 100644
index 0000000000000000000000000000000000000000..47f6d4d6bde72ae92dc3ca8131a2b8306534549f
--- /dev/null
+++ b/roles/build/templates/etcroot/etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
@@ -0,0 +1,30 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v1.4.5 (GNU/Linux)
+
+mQINBFOn/0sBEADLDyZ+DQHkcTHDQSE0a0B2iYAEXwpPvs67cJ4tmhe/iMOyVMh9
+Yw/vBIF8scm6T/vPN5fopsKiW9UsAhGKg0epC6y5ed+NAUHTEa6pSOdo7CyFDwtn
+4HF61Esyb4gzPT6QiSr0zvdTtgYBRZjAEPFVu3Dio0oZ5UQZ7fzdZfeixMQ8VMTQ
+4y4x5vik9B+cqmGiq9AW71ixlDYVWasgR093fXiD9NLT4DTtK+KLGYNjJ8eMRqfZ
+Ws7g7C+9aEGHfsGZ/SxLOumx/GfiTloal0dnq8TC7XQ/JuNdB9qjoXzRF+faDUsj
+WuvNSQEqUXW1dzJjBvroEvgTdfCJfRpIgOrc256qvDMp1SxchMFltPlo5mbSMKu1
+x1p4UkAzx543meMlRXOgx2/hnBm6H6L0FsSyDS6P224yF+30eeODD4Ju4BCyQ0jO
+IpUxmUnApo/m0eRelI6TRl7jK6aGqSYUNhFBuFxSPKgKYBpFhVzRM63Jsvib82rY
+438q3sIOUdxZY6pvMOWRkdUVoz7WBExTdx5NtGX4kdW5QtcQHM+2kht6sBnJsvcB
+JYcYIwAUeA5vdRfwLKuZn6SgAUKdgeOtuf+cPR3/E68LZr784SlokiHLtQkfk98j
+NXm6fJjXwJvwiM2IiFyg8aUwEEDX5U+QOCA0wYrgUQ/h8iathvBJKSc9jQARAQAB
+tEJDZW50T1MtNyBLZXkgKENlbnRPUyA3IE9mZmljaWFsIFNpZ25pbmcgS2V5KSA8
+c2VjdXJpdHlAY2VudG9zLm9yZz6JAjUEEwECAB8FAlOn/0sCGwMGCwkIBwMCBBUC
+CAMDFgIBAh4BAheAAAoJECTGqKf0qA61TN0P/2730Th8cM+d1pEON7n0F1YiyxqG
+QzwpC2Fhr2UIsXpi/lWTXIG6AlRvrajjFhw9HktYjlF4oMG032SnI0XPdmrN29lL
+F+ee1ANdyvtkw4mMu2yQweVxU7Ku4oATPBvWRv+6pCQPTOMe5xPG0ZPjPGNiJ0xw
+4Ns+f5Q6Gqm927oHXpylUQEmuHKsCp3dK/kZaxJOXsmq6syY1gbrLj2Anq0iWWP4
+Tq8WMktUrTcc+zQ2pFR7ovEihK0Rvhmk6/N4+4JwAGijfhejxwNX8T6PCuYs5Jiv
+hQvsI9FdIIlTP4XhFZ4N9ndnEwA4AH7tNBsmB3HEbLqUSmu2Rr8hGiT2Plc4Y9AO
+aliW1kOMsZFYrX39krfRk2n2NXvieQJ/lw318gSGR67uckkz2ZekbCEpj/0mnHWD
+3R6V7m95R6UYqjcw++Q5CtZ2tzmxomZTf42IGIKBbSVmIS75WY+cBULUx3PcZYHD
+ZqAbB0Dl4MbdEH61kOI8EbN/TLl1i077r+9LXR1mOnlC3GLD03+XfY8eEBQf7137
+YSMiW5r/5xwQk7xEcKlbZdmUJp3ZDTQBXT06vavvp3jlkqqH9QOE8ViZZ6aKQLqv
+pL+4bs52jzuGwTMT7gOR5MzD+vT0fVS7Xm8MjOxvZgbHsAgzyFGlI1ggUQmU7lu3
+uPNL0eRx4S1G4Jn5
+=OGYX
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/roles/build/templates/etcroot/etc/sysconfig/network b/roles/build/templates/etcroot/etc/sysconfig/network
new file mode 100644
index 0000000000000000000000000000000000000000..c357c745966bc2a8a9b5ddaf8f0d04a926b3ecf5
--- /dev/null
+++ b/roles/build/templates/etcroot/etc/sysconfig/network
@@ -0,0 +1,2 @@
+NETWORKING=yes
+HOSTNAME=localhost.localdomain
diff --git a/roles/build/templates/etcroot/etc/yum.conf b/roles/build/templates/etcroot/etc/yum.conf
new file mode 100644
index 0000000000000000000000000000000000000000..b1183d4fac5cd39f03f12d6679ee803f3ca1c174
--- /dev/null
+++ b/roles/build/templates/etcroot/etc/yum.conf
@@ -0,0 +1,27 @@
+[main]
+cachedir=/var/cache/yum/$basearch/$releasever
+keepcache=0
+debuglevel=2
+logfile=/var/log/yum.log
+exactarch=1
+obsoletes=1
+gpgcheck=1
+plugins=1
+installonly_limit=5
+bugtracker_url=http://bugs.centos.org/set_project.php?project_id=23&ref=http://bugs.centos.org/bug_report_page.php?category=yum
+distroverpkg=centos-release
+tsflags=nodocs
+group_package_types=mandatory
+
+#  This is the default, if you make this bigger yum won't see if the metadata
+# is newer on the remote and so you'll "gain" the bandwidth of not having to
+# download the new metadata and "pay" for it by yum not having correct
+# information.
+#  It is esp. important, to have correct metadata, for distributions like
+# Fedora which don't keep old packages around. If you don't like this checking
+# interupting your command line usage, it's much better to have something
+# manually check the metadata once an hour (yum-updatesd will do this).
+# metadata_expire=90m
+
+# PUT YOUR REPOS HERE OR IN separate files named file.repo
+# in /etc/yum.repos.d
diff --git a/roles/build/templates/etcroot/etc/yum.repos.d/CentOS-Base.repo b/roles/build/templates/etcroot/etc/yum.repos.d/CentOS-Base.repo
new file mode 100644
index 0000000000000000000000000000000000000000..00b1af7ea3e0a10e3ea8946b8093fe6c9d2be861
--- /dev/null
+++ b/roles/build/templates/etcroot/etc/yum.repos.d/CentOS-Base.repo
@@ -0,0 +1,44 @@
+# CentOS-Base.repo
+#
+# The mirror system uses the connecting IP address of the client and the
+# update status of each mirror to pick mirrors that are updated to and
+# geographically close to the client.  You should use this for CentOS updates
+# unless you are manually picking other mirrors.
+#
+# If the mirrorlist= does not work for you, as a fall back you can try the 
+# remarked out baseurl= line instead.
+#
+#
+
+[base]
+name=CentOS-$releasever - Base
+mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=os&infra=$infra
+#baseurl=http://mirror.centos.org/centos/$releasever/os/$basearch/
+gpgcheck=1
+gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
+
+#released updates 
+[updates]
+name=CentOS-$releasever - Updates
+mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=updates&infra=$infra
+#baseurl=http://mirror.centos.org/centos/$releasever/updates/$basearch/
+gpgcheck=1
+gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
+
+#additional packages that may be useful
+[extras]
+name=CentOS-$releasever - Extras
+mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=extras&infra=$infra
+#baseurl=http://mirror.centos.org/centos/$releasever/extras/$basearch/
+gpgcheck=1
+gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
+
+#additional packages that extend functionality of existing packages
+[centosplus]
+name=CentOS-$releasever - Plus
+mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=centosplus&infra=$infra
+#baseurl=http://mirror.centos.org/centos/$releasever/centosplus/$basearch/
+gpgcheck=1
+enabled=0
+gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
+
diff --git a/roles/build/templates/etcroot/etc/yum.repos.d/CentOS-CR.repo b/roles/build/templates/etcroot/etc/yum.repos.d/CentOS-CR.repo
new file mode 100644
index 0000000000000000000000000000000000000000..08f0751a93b6bf7307003f9d7506a498f0568c53
--- /dev/null
+++ b/roles/build/templates/etcroot/etc/yum.repos.d/CentOS-CR.repo
@@ -0,0 +1,28 @@
+# CentOS-CR.repo
+#
+# The Continuous Release ( CR )  repository contains rpms that are due in the next
+# release for a specific CentOS Version ( eg. next release in CentOS-7 ); these rpms
+# are far less tested, with no integration checking or update path testing having
+# taken place. They are still built from the upstream sources, but might not map 
+# to an exact upstream distro release.
+#
+# These packages are made available soon after they are built, for people willing 
+# to test their environments, provide feedback on content for the next release, and
+# for people looking for early-access to next release content.
+#
+# The CR repo is shipped in a disabled state by default; its important that users 
+# understand the implications of turning this on. 
+#
+# NOTE: We do not use a mirrorlist for the CR repos, to ensure content is available
+#       to everyone as soon as possible, and not need to wait for the external
+#       mirror network to seed first. However, many local mirrors will carry CR repos
+#       and if desired you can use one of these local mirrors by editing the baseurl
+#       line in the repo config below.
+#
+
+[cr]
+name=CentOS-$releasever - cr
+baseurl=http://mirror.centos.org/centos/$releasever/cr/$basearch/
+gpgcheck=1
+gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
+enabled=0
diff --git a/roles/build/templates/etcroot/etc/yum.repos.d/CentOS-Debuginfo.repo b/roles/build/templates/etcroot/etc/yum.repos.d/CentOS-Debuginfo.repo
new file mode 100644
index 0000000000000000000000000000000000000000..ca526ad0a2633035063dcc0240c7b2b1ee9b947d
--- /dev/null
+++ b/roles/build/templates/etcroot/etc/yum.repos.d/CentOS-Debuginfo.repo
@@ -0,0 +1,21 @@
+# CentOS-Debug.repo
+#
+# The mirror system uses the connecting IP address of the client and the
+# update status of each mirror to pick mirrors that are updated to and
+# geographically close to the client.  You should use this for CentOS updates
+# unless you are manually picking other mirrors.
+#
+
+# All debug packages from all the various CentOS-7 releases
+# are merged into a single repo, split by BaseArch
+#
+# Note: packages in the debuginfo repo are currently not signed
+#
+
+[base-debuginfo]
+name=CentOS-7 - Debuginfo
+baseurl=http://debuginfo.centos.org/7/$basearch/
+gpgcheck=1
+gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-Debug-7
+enabled=0
+#
diff --git a/roles/build/templates/etcroot/etc/yum.repos.d/CentOS-Media.repo b/roles/build/templates/etcroot/etc/yum.repos.d/CentOS-Media.repo
new file mode 100644
index 0000000000000000000000000000000000000000..95996444ca27685ec9203133a8b52c8fcb923335
--- /dev/null
+++ b/roles/build/templates/etcroot/etc/yum.repos.d/CentOS-Media.repo
@@ -0,0 +1,22 @@
+# CentOS-Media.repo
+#
+#  This repo can be used with mounted DVD media, verify the mount point for
+#  CentOS-7.  You can use this repo and yum to install items directly off the
+#  DVD ISO that we release.
+#
+# To use this repo, put in your DVD and use it with the other repos too:
+#  yum --enablerepo=c7-media [command]
+#  
+# or for ONLY the media repo, do this:
+#
+#  yum --disablerepo=\* --enablerepo=c7-media [command]
+
+[c7-media]
+name=CentOS-$releasever - Media
+baseurl=file:///media/CentOS/
+        file:///media/cdrom/
+        file:///media/cdrecorder/
+gpgcheck=1
+enabled=0
+gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
+
diff --git a/roles/build/templates/etcroot/etc/yum.repos.d/CentOS-Sources.repo b/roles/build/templates/etcroot/etc/yum.repos.d/CentOS-Sources.repo
new file mode 100644
index 0000000000000000000000000000000000000000..7484071cd9bcbb6211938ec8f23814e043e30b23
--- /dev/null
+++ b/roles/build/templates/etcroot/etc/yum.repos.d/CentOS-Sources.repo
@@ -0,0 +1,42 @@
+# CentOS-Sources.repo
+#
+# The mirror system uses the connecting IP address of the client and the
+# update status of each mirror to pick mirrors that are updated to and
+# geographically close to the client.  You should use this for CentOS updates
+# unless you are manually picking other mirrors.
+#
+# If the mirrorlist= does not work for you, as a fall back you can try the 
+# remarked out baseurl= line instead.
+#
+#
+
+[base-source]
+name=CentOS-$releasever - Base Sources
+baseurl=http://vault.centos.org/centos/$releasever/os/Source/
+gpgcheck=1
+enabled=0
+gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
+
+#released updates 
+[updates-source]
+name=CentOS-$releasever - Updates Sources
+baseurl=http://vault.centos.org/centos/$releasever/updates/Source/
+gpgcheck=1
+enabled=0
+gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
+
+#additional packages that may be useful
+[extras-source]
+name=CentOS-$releasever - Extras Sources
+baseurl=http://vault.centos.org/centos/$releasever/extras/Source/
+gpgcheck=1
+enabled=0
+gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
+
+#additional packages that extend functionality of existing packages
+[centosplus-source]
+name=CentOS-$releasever - Plus Sources
+baseurl=http://vault.centos.org/centos/$releasever/centosplus/Source/
+gpgcheck=1
+enabled=0
+gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
diff --git a/roles/build/templates/etcroot/etc/yum.repos.d/CentOS-Vault.repo b/roles/build/templates/etcroot/etc/yum.repos.d/CentOS-Vault.repo
new file mode 100644
index 0000000000000000000000000000000000000000..8e62a4a4dbf4d300136dd0e7e9e10ad608bfbbc2
--- /dev/null
+++ b/roles/build/templates/etcroot/etc/yum.repos.d/CentOS-Vault.repo
@@ -0,0 +1,254 @@
+# CentOS Vault contains rpms from older releases in the CentOS-7
+# tree.
+
+# C7.0.1406
+[C7.0.1406-base]
+name=CentOS-7.0.1406 - Base
+baseurl=http://vault.centos.org/7.0.1406/os/$basearch/
+gpgcheck=1
+gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
+enabled=0
+
+[C7.0.1406-updates]
+name=CentOS-7.0.1406 - Updates
+baseurl=http://vault.centos.org/7.0.1406/updates/$basearch/
+gpgcheck=1
+gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
+enabled=0
+
+[C7.0.1406-extras]
+name=CentOS-7.0.1406 - Extras
+baseurl=http://vault.centos.org/7.0.1406/extras/$basearch/
+gpgcheck=1
+gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
+enabled=0
+
+[C7.0.1406-centosplus]
+name=CentOS-7.0.1406 - CentOSPlus
+baseurl=http://vault.centos.org/7.0.1406/centosplus/$basearch/
+gpgcheck=1
+gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
+enabled=0
+
+[C7.0.1406-fasttrack]
+name=CentOS-7.0.1406 - Fasttrack
+baseurl=http://vault.centos.org/7.0.1406/fasttrack/$basearch/
+gpgcheck=1
+gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
+enabled=0
+
+# C7.1.1503
+[C7.1.1503-base]
+name=CentOS-7.1.1503 - Base
+baseurl=http://vault.centos.org/7.1.1503/os/$basearch/
+gpgcheck=1
+gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
+enabled=0
+
+[C7.1.1503-updates]
+name=CentOS-7.1.1503 - Updates
+baseurl=http://vault.centos.org/7.1.1503/updates/$basearch/
+gpgcheck=1
+gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
+enabled=0
+
+[C7.1.1503-extras]
+name=CentOS-7.1.1503 - Extras
+baseurl=http://vault.centos.org/7.1.1503/extras/$basearch/
+gpgcheck=1
+gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
+enabled=0
+
+[C7.1.1503-centosplus]
+name=CentOS-7.1.1503 - CentOSPlus
+baseurl=http://vault.centos.org/7.1.1503/centosplus/$basearch/
+gpgcheck=1
+gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
+enabled=0
+
+[C7.1.1503-fasttrack]
+name=CentOS-7.1.1503 - Fasttrack
+baseurl=http://vault.centos.org/7.1.1503/fasttrack/$basearch/
+gpgcheck=1
+gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
+enabled=0
+
+# C7.2.1511
+[C7.2.1511-base]
+name=CentOS-7.2.1511 - Base
+baseurl=http://vault.centos.org/7.2.1511/os/$basearch/
+gpgcheck=1
+gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
+enabled=0
+
+[C7.2.1511-updates]
+name=CentOS-7.2.1511 - Updates
+baseurl=http://vault.centos.org/7.2.1511/updates/$basearch/
+gpgcheck=1
+gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
+enabled=0
+
+[C7.2.1511-extras]
+name=CentOS-7.2.1511 - Extras
+baseurl=http://vault.centos.org/7.2.1511/extras/$basearch/
+gpgcheck=1
+gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
+enabled=0
+
+[C7.2.1511-centosplus]
+name=CentOS-7.2.1511 - CentOSPlus
+baseurl=http://vault.centos.org/7.2.1511/centosplus/$basearch/
+gpgcheck=1
+gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
+enabled=0
+
+[C7.2.1511-fasttrack]
+name=CentOS-7.2.1511 - Fasttrack
+baseurl=http://vault.centos.org/7.2.1511/fasttrack/$basearch/
+gpgcheck=1
+gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
+enabled=0
+
+# C7.3.1611
+[C7.3.1611-base]
+name=CentOS-7.3.1611 - Base
+baseurl=http://vault.centos.org/7.3.1611/os/$basearch/
+gpgcheck=1
+gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
+enabled=0
+
+[C7.3.1611-updates]
+name=CentOS-7.3.1611 - Updates
+baseurl=http://vault.centos.org/7.3.1611/updates/$basearch/
+gpgcheck=1
+gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
+enabled=0
+
+[C7.3.1611-extras]
+name=CentOS-7.3.1611 - Extras
+baseurl=http://vault.centos.org/7.3.1611/extras/$basearch/
+gpgcheck=1
+gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
+enabled=0
+
+[C7.3.1611-centosplus]
+name=CentOS-7.3.1611 - CentOSPlus
+baseurl=http://vault.centos.org/7.3.1611/centosplus/$basearch/
+gpgcheck=1
+gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
+enabled=0
+
+[C7.3.1611-fasttrack]
+name=CentOS-7.3.1611 - Fasttrack
+baseurl=http://vault.centos.org/7.3.1611/fasttrack/$basearch/
+gpgcheck=1
+gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
+enabled=0
+
+# C7.4.1708
+[C7.4.1708-base]
+name=CentOS-7.4.1708 - Base
+baseurl=http://vault.centos.org/7.4.1708/os/$basearch/
+gpgcheck=1
+gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
+enabled=0
+
+[C7.4.1708-updates]
+name=CentOS-7.4.1708 - Updates
+baseurl=http://vault.centos.org/7.4.1708/updates/$basearch/
+gpgcheck=1
+gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
+enabled=0
+
+[C7.4.1708-extras]
+name=CentOS-7.4.1708 - Extras
+baseurl=http://vault.centos.org/7.4.1708/extras/$basearch/
+gpgcheck=1
+gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
+enabled=0
+
+[C7.4.1708-centosplus]
+name=CentOS-7.4.1708 - CentOSPlus
+baseurl=http://vault.centos.org/7.4.1708/centosplus/$basearch/
+gpgcheck=1
+gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
+enabled=0
+
+[C7.4.1708-fasttrack]
+name=CentOS-7.4.1708 - Fasttrack
+baseurl=http://vault.centos.org/7.4.1708/fasttrack/$basearch/
+gpgcheck=1
+gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
+enabled=0
+
+# C7.5.1804
+[C7.5.1804-base]
+name=CentOS-7.5.1804 - Base
+baseurl=http://vault.centos.org/7.5.1804/os/$basearch/
+gpgcheck=1
+gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
+enabled=0
+
+[C7.5.1804-updates]
+name=CentOS-7.5.1804 - Updates
+baseurl=http://vault.centos.org/7.5.1804/updates/$basearch/
+gpgcheck=1
+gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
+enabled=0
+
+[C7.5.1804-extras]
+name=CentOS-7.5.1804 - Extras
+baseurl=http://vault.centos.org/7.5.1804/extras/$basearch/
+gpgcheck=1
+gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
+enabled=0
+
+[C7.5.1804-centosplus]
+name=CentOS-7.5.1804 - CentOSPlus
+baseurl=http://vault.centos.org/7.5.1804/centosplus/$basearch/
+gpgcheck=1
+gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
+enabled=0
+
+[C7.5.1804-fasttrack]
+name=CentOS-7.5.1804 - Fasttrack
+baseurl=http://vault.centos.org/7.5.1804/fasttrack/$basearch/
+gpgcheck=1
+gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
+enabled=0
+
+# C7.6.1810
+[C7.6.1810-base]
+name=CentOS-7.6.1810 - Base
+baseurl=http://vault.centos.org/7.6.1810/os/$basearch/
+gpgcheck=1
+gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
+enabled=0
+
+[C7.6.1810-updates]
+name=CentOS-7.6.1810 - Updates
+baseurl=http://vault.centos.org/7.6.1810/updates/$basearch/
+gpgcheck=1
+gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
+enabled=0
+
+[C7.6.1810-extras]
+name=CentOS-7.6.1810 - Extras
+baseurl=http://vault.centos.org/7.6.1810/extras/$basearch/
+gpgcheck=1
+gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
+enabled=0
+
+[C7.6.1810-centosplus]
+name=CentOS-7.6.1810 - CentOSPlus
+baseurl=http://vault.centos.org/7.6.1810/centosplus/$basearch/
+gpgcheck=1
+gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
+enabled=0
+
+[C7.6.1810-fasttrack]
+name=CentOS-7.6.1810 - Fasttrack
+baseurl=http://vault.centos.org/7.6.1810/fasttrack/$basearch/
+gpgcheck=1
+gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
+enabled=0
diff --git a/roles/build/templates/etcroot/etc/yum.repos.d/CentOS-fasttrack.repo b/roles/build/templates/etcroot/etc/yum.repos.d/CentOS-fasttrack.repo
new file mode 100644
index 0000000000000000000000000000000000000000..355d3a09750647d060a47a703351e93e29c732ee
--- /dev/null
+++ b/roles/build/templates/etcroot/etc/yum.repos.d/CentOS-fasttrack.repo
@@ -0,0 +1,10 @@
+#CentOS-fasttrack.repo
+
+[fasttrack]
+name=CentOS-7 - fasttrack
+mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=fasttrack&infra=$infra
+#baseurl=http://mirror.centos.org/centos/$releasever/fasttrack/$basearch/
+gpgcheck=1
+enabled=0
+gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
+
diff --git a/roles/build/templates/etcroot/etc/yum/vars/basearch b/roles/build/templates/etcroot/etc/yum/vars/basearch
new file mode 100644
index 0000000000000000000000000000000000000000..1c09346681a674c5830116f760375ef17db8dd93
--- /dev/null
+++ b/roles/build/templates/etcroot/etc/yum/vars/basearch
@@ -0,0 +1 @@
+x86_64
diff --git a/roles/build/templates/etcroot/etc/yum/vars/contentdir b/roles/build/templates/etcroot/etc/yum/vars/contentdir
new file mode 100644
index 0000000000000000000000000000000000000000..b813af90afcad8c097e56518f3a6859b164f4dff
--- /dev/null
+++ b/roles/build/templates/etcroot/etc/yum/vars/contentdir
@@ -0,0 +1 @@
+centos
diff --git a/roles/build/templates/etcroot/etc/yum/vars/infra b/roles/build/templates/etcroot/etc/yum/vars/infra
new file mode 100644
index 0000000000000000000000000000000000000000..169684cb8daf1f929c4f3ee40fb5fed873c9205f
--- /dev/null
+++ b/roles/build/templates/etcroot/etc/yum/vars/infra
@@ -0,0 +1 @@
+stock
diff --git a/roles/build/templates/etcroot/etc/yum/vars/releasever b/roles/build/templates/etcroot/etc/yum/vars/releasever
new file mode 100644
index 0000000000000000000000000000000000000000..7f8f011eb73d6043d2e6db9d2c101195ae2801f2
--- /dev/null
+++ b/roles/build/templates/etcroot/etc/yum/vars/releasever
@@ -0,0 +1 @@
+7
diff --git a/roles/build/templates/nginx/Dockerfile.j2 b/roles/build/templates/nginx/Dockerfile.j2
new file mode 100644
index 0000000000000000000000000000000000000000..d8932f769c3e5ab10e6e123e4db2a51eb84a4560
--- /dev/null
+++ b/roles/build/templates/nginx/Dockerfile.j2
@@ -0,0 +1,16 @@
+FROM {{repo}}/centos:{{version}}{{suffix}}
+
+RUN yum update -y; \
+    yum install -y wget unzip curl nginx nginx-all-modules
+
+RUN yum clean all
+
+RUN ln -sf /dev/stdout /var/log/nginx/access.log \
+        && ln -sf /dev/stderr /var/log/nginx/error.log
+
+EXPOSE 80 443
+
+STOPSIGNAL SIGTERM
+
+CMD ["nginx", "-g", "daemon off;"]
+
diff --git a/roles/build/templates/nifi/Dockerfile.j2 b/roles/build/templates/nifi/Dockerfile.j2
new file mode 100644
index 0000000000000000000000000000000000000000..f7a48b9f2a48edbe5ca46b4176750abdf67c339d
--- /dev/null
+++ b/roles/build/templates/nifi/Dockerfile.j2
@@ -0,0 +1,97 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+FROM {{repo}}/openjdk:{{version}}{{suffix}} 
+#LABEL maintainer="Apache NiFi <dev@nifi.apache.org>"
+#LABEL site="https://nifi.apache.org"
+
+ARG UID=1000
+ARG GID=1000
+ARG NIFI_VERSION=1.9.2
+ARG BASE_URL=https://archive.apache.org/dist
+ARG MIRROR_BASE_URL=${MIRROR_BASE_URL:-${BASE_URL}}
+ARG NIFI_BINARY_PATH=${NIFI_BINARY_PATH:-/nifi/${NIFI_VERSION}/nifi-${NIFI_VERSION}-bin.zip}
+ARG NIFI_TOOLKIT_BINARY_PATH=${NIFI_TOOLKIT_BINARY_PATH:-/nifi/${NIFI_VERSION}/nifi-toolkit-${NIFI_VERSION}-bin.zip}
+
+ENV NIFI_BASE_DIR=/opt/nifi
+ENV NIFI_HOME ${NIFI_BASE_DIR}/nifi-current
+ENV NIFI_TOOLKIT_HOME ${NIFI_BASE_DIR}/nifi-toolkit-current
+
+ENV NIFI_PID_DIR=${NIFI_HOME}/run
+ENV NIFI_LOG_DIR=${NIFI_HOME}/logs
+
+# ADD sh/ ${NIFI_BASE_DIR}/scripts/
+
+# Setup NiFi user and create necessary directories
+RUN groupadd -g ${GID} nifi || groupmod -n nifi `getent group ${GID} | cut -d: -f1` \
+    && useradd --shell /bin/bash -u ${UID} -g ${GID} -m nifi \
+    && mkdir -p ${NIFI_BASE_DIR} \
+    && chown -R nifi:nifi ${NIFI_BASE_DIR} \
+    && yum -y install jq xmlstarlet procps-ng
+
+USER nifi
+
+# Download, validate, and expand Apache NiFi Toolkit binary.
+RUN curl -fSL ${MIRROR_BASE_URL}/${NIFI_TOOLKIT_BINARY_PATH} -o ${NIFI_BASE_DIR}/nifi-toolkit-${NIFI_VERSION}-bin.zip \
+    && echo "$(curl ${BASE_URL}/${NIFI_TOOLKIT_BINARY_PATH}.sha256) *${NIFI_BASE_DIR}/nifi-toolkit-${NIFI_VERSION}-bin.zip" | sha256sum -c - \
+    && unzip ${NIFI_BASE_DIR}/nifi-toolkit-${NIFI_VERSION}-bin.zip -d ${NIFI_BASE_DIR} \
+    && rm ${NIFI_BASE_DIR}/nifi-toolkit-${NIFI_VERSION}-bin.zip \
+    && mv ${NIFI_BASE_DIR}/nifi-toolkit-${NIFI_VERSION} ${NIFI_TOOLKIT_HOME} \
+    && ln -s ${NIFI_TOOLKIT_HOME} ${NIFI_BASE_DIR}/nifi-toolkit-${NIFI_VERSION}
+
+# Download, validate, and expand Apache NiFi binary.
+RUN curl -fSL ${MIRROR_BASE_URL}/${NIFI_BINARY_PATH} -o ${NIFI_BASE_DIR}/nifi-${NIFI_VERSION}-bin.zip \
+    && echo "$(curl ${BASE_URL}/${NIFI_BINARY_PATH}.sha256) *${NIFI_BASE_DIR}/nifi-${NIFI_VERSION}-bin.zip" | sha256sum -c - \
+    && unzip ${NIFI_BASE_DIR}/nifi-${NIFI_VERSION}-bin.zip -d ${NIFI_BASE_DIR} \
+    && rm ${NIFI_BASE_DIR}/nifi-${NIFI_VERSION}-bin.zip \
+    && mv ${NIFI_BASE_DIR}/nifi-${NIFI_VERSION} ${NIFI_HOME} \
+    && mkdir -p ${NIFI_HOME}/conf \
+    && mkdir -p ${NIFI_HOME}/database_repository \
+    && mkdir -p ${NIFI_HOME}/flowfile_repository \
+    && mkdir -p ${NIFI_HOME}/content_repository \
+    && mkdir -p ${NIFI_HOME}/provenance_repository \
+    && mkdir -p ${NIFI_HOME}/state \
+    && mkdir -p ${NIFI_LOG_DIR} \
+    && ln -s ${NIFI_HOME} ${NIFI_BASE_DIR}/nifi-${NIFI_VERSION}
+
+VOLUME ${NIFI_LOG_DIR} \
+       ${NIFI_HOME}/conf \
+       ${NIFI_HOME}/database_repository \
+       ${NIFI_HOME}/flowfile_repository \
+       ${NIFI_HOME}/content_repository \
+       ${NIFI_HOME}/provenance_repository \
+       ${NIFI_HOME}/state
+
+# Clear nifi-env.sh in favour of configuring all environment variables in the Dockerfile
+RUN echo "#!/bin/sh\n" > $NIFI_HOME/bin/nifi-env.sh
+
+# Web HTTP(s) & Socket Site-to-Site Ports
+EXPOSE 8080 8443 10000 8000
+
+WORKDIR ${NIFI_HOME}
+
+# Apply configuration and start NiFi
+#
+# We need to use the exec form to avoid running our command in a subshell and omitting signals,
+# thus being unable to shut down gracefully:
+# https://docs.docker.com/engine/reference/builder/#entrypoint
+#
+# Also we need to use relative path, because the exec form does not invoke a command shell,
+# thus normal shell processing does not happen:
+# https://docs.docker.com/engine/reference/builder/#exec-form-entrypoint-example
+ENTRYPOINT ["/bin/bash"]
diff --git a/roles/build/templates/openjdk/Dockerfile.j2 b/roles/build/templates/openjdk/Dockerfile.j2
new file mode 100644
index 0000000000000000000000000000000000000000..57f6886ac0a42e5a48d9ac7f850e4e7a1677f3e1
--- /dev/null
+++ b/roles/build/templates/openjdk/Dockerfile.j2
@@ -0,0 +1,11 @@
+FROM {{repo}}/centos:{{version}}{{suffix}}
+
+RUN yum update -y; \
+    yum install -y wget unzip curl java-1.8.0-openjdk-headless.x86_64
+
+RUN ln -svT "/usr/lib/jvm/java-1.8.0-openjdk-$(rpm -q --queryformat "%{VERSION}-%{RELEASE}.%{ARCH}\n" java-1.8.0-openjdk-headless)" /docker-java-home
+ENV JAVA_HOME /docker-java-home/jre
+
+RUN yum clean all
+
+CMD ["/bin/bash"]
diff --git a/roles/build/templates/zookeeper/Dockerfile.j2 b/roles/build/templates/zookeeper/Dockerfile.j2
new file mode 100644
index 0000000000000000000000000000000000000000..209069c8137b1ee53bac1fbecda2efc9f388e9e2
--- /dev/null
+++ b/roles/build/templates/zookeeper/Dockerfile.j2
@@ -0,0 +1,34 @@
+FROM {{repo}}/openjdk:{{version}}{{suffix}}
+#LABEL maintainer="Apache NiFi <dev@nifi.apache.org>"
+#LABEL site="https://nifi.apache.org"
+
+#ARG UID=1000
+#ARG GID=1000
+ARG ZOOKEEPER_VERSION=3.5.5
+ARG BASE_URL=https://archive.apache.org/dist
+ARG MIRROR_BASE_URL=${MIRROR_BASE_URL:-${BASE_URL}}
+ARG ZOOKEEPER_BINARY_PATH=${ZOOKEEPER_BINARY_PATH:-/zookeeper/zookeeper-${ZOOKEEPER_VERSION}/apache-zookeeper-${ZOOKEEPER_VERSION}-bin.tar.gz}
+
+ENV ZOOKEEPER_BASE_DIR=/opt
+
+#ENV ZOOKEEPER_PID_DIR=${ZOOKEEPER_HOME}/run
+#ENV ZOOKEEPER_LOG_DIR=${ZOOKEEPER_HOME}/logs
+
+#  USER nifi
+
+# Download, validate, and expand Apache NiFi binary.
+RUN curl -fSL ${MIRROR_BASE_URL}/${ZOOKEEPER_BINARY_PATH} -o ${ZOOKEEPER_BASE_DIR}/apache-zookeeper-${ZOOKEEPER_VERSION}-bin.tar.gz \
+#    && echo "$(curl ${BASE_URL}/${ZOOKEEPER_BINARY_PATH}.sha512) *${ZOOKEEPER_BASE_DIR}/apache-zookeeper-${ZOOKEEPER_VERSION}-bin.tar.gz" | sha256sum -c - \
+    && tar -xzf ${ZOOKEEPER_BASE_DIR}/apache-zookeeper-${ZOOKEEPER_VERSION}-bin.tar.gz -C ${ZOOKEEPER_BASE_DIR} \
+    && mv ${ZOOKEEPER_BASE_DIR}/apache-zookeeper-${ZOOKEEPER_VERSION}-bin ${ZOOKEEPER_BASE_DIR}/zookeeper \
+    && rm ${ZOOKEEPER_BASE_DIR}/apache-zookeeper-${ZOOKEEPER_VERSION}-bin.tar.gz \
+    && cp ${ZOOKEEPER_BASE_DIR}/zookeeper/conf/zoo_sample.cfg ${ZOOKEEPER_BASE_DIR}/zookeeper/conf/zoo.cfg
+
+# Web HTTP(s) & Socket Site-to-Site Ports
+EXPOSE 2181 2888 3888
+
+WORKDIR ${ZOOKEEPER_BASE_DIR}/zookeeper
+
+ENTRYPOINT ["/opt/zookeeper/bin/zkServer.sh"]
+CMD ["start-foreground"]
+
diff --git a/roles/build/vars/main.yml b/roles/build/vars/main.yml
new file mode 100644
index 0000000000000000000000000000000000000000..cd21505a47e530a967e3c44bd2a772d1b8d08bd7
--- /dev/null
+++ b/roles/build/vars/main.yml
@@ -0,0 +1,2 @@
+---
+
diff --git a/roles/ca/defaults/main.yml b/roles/ca/defaults/main.yml
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/roles/ca/files/easyrsa/COPYING.md b/roles/ca/files/easyrsa/COPYING.md
new file mode 100644
index 0000000000000000000000000000000000000000..39bce088fa8f52d581be241e3bcaf33dd14b74da
--- /dev/null
+++ b/roles/ca/files/easyrsa/COPYING.md
@@ -0,0 +1,33 @@
+Easy-RSA -- A Shell-based CA Utility
+====================================
+
+Copyright (C) 2013 by the Open-Source OpenVPN development community
+
+Easy-RSA 3 license: GPLv2
+-------------------------
+
+All the Easy-RSA code contained in this project falls under a GPLv2 license with
+full text available in the Licensing/ directory. Additional components used by
+this project fall under additional licenses:
+
+Additional licenses for external components
+-------------------------------------------
+
+The following components are under different licenses; while not part of the
+Easy-RSA source code, these components are used by Easy-RSA or provided in
+platform distributions as described below:
+
+### OpenSSL
+
+  OpenSSL is not linked by Easy-RSA, nor is it currently provided in any release
+  package by Easy-RSA. However, Easy-RSA is tightly coupled with OpenSSL, so
+  effective use of this code will require your acceptance and installation of
+  OpenSSL.
+
+### Additional Windows Components
+
+  The Windows binary package includes mksh/Win32 and unxutils binary components,
+  with full licensing details available in the distro/windows/Licensing/
+  subdirectory of this project. mksh/Win32 is under a MirOS license (with some
+  additional component licenses present there) and unxutils is under a GPLv2
+  license.
diff --git a/roles/ca/files/easyrsa/ChangeLog b/roles/ca/files/easyrsa/ChangeLog
new file mode 100644
index 0000000000000000000000000000000000000000..ead1d7ca012ffe6beb7cc98847ebe8c7ee116cae
--- /dev/null
+++ b/roles/ca/files/easyrsa/ChangeLog
@@ -0,0 +1,121 @@
+Easy-RSA 3 ChangeLog
+
+3.0.6 (2019-02-01)
+   * Certifcates that are revoked now move to a revoked subdirectory (#63)
+   * EasyRSA no longer clobbers non-EASYRSA environment variables (#277)
+   * More sane string checking, allowingn for commas in CN (#267)
+   * Support for reasonCode in CRL (#280)
+   * Better handling for capturing passphrases (#230, others)
+   * Improved LibreSSL/MacOS support
+   * Adds support to renew certificates up to 30 days before expiration (#286)
+     - This changes previous behavior allowing for certificate creation using
+       duplicate CNs.
+
+3.0.5 (2018-09-15)
+   * Fix #17 & #58: use AES256 for CA key
+   * Also, don't use read -s, use stty -echo
+   * Fix broken "nopass" option
+   * Add -r to read to stop errors reported by shellcheck (and to behave)
+   * remove overzealous quotes around $pkcs_opts (more SC errors)
+   * Support for LibreSSL
+   * EasyRSA version will be reported in certificate comments
+   * Client certificates now expire in 3 year (1080 days) by default
+
+3.0.4 (2018-01-21)
+    * Remove use of egrep (#154)
+    * Integrate with Travis-CI (#165)
+    * Remove "local" from variable assignment (#165)
+        * Other changes related to Travis-CI fixes
+	* Assign values to variables defined previously w/local
+    * Finally(?) fix the subjectAltName issues I presented earlier (really
+    fixes #168 
+
+3.0.3 (2017-08-22)
+    * Include mktemp windows binary
+    * copy CSR extensions into signed certificate
+
+
+3.0.2 (2017-08-21)
+    * add missing windows binaries
+
+
+3.0.1 (2015-10-25)
+    * correct some packaging errors
+
+
+3.0.0 (2015-09-07)
+
+    * cab4a07 Fix typo: Hellman
+        (ljani: Github)
+
+    * 171834d Fix typo: Default
+        (allo-: Github)
+
+    * 8b42eea Make aes256 default, replacing 3des
+        (keros: Github)
+    
+    * f2f4ac8 Make -utf8 default
+        (roubert: Github)
+
+	
+3.0.0-rc2 (2014/07/27)
+
+    * 1551e5f docs: fix typo
+        (Josh Cepek <josh.cepek@usa.net>)
+
+    * 7ae44b3 Add KNOWN_ISSUES to stage next -rc release
+        (Josh Cepek <josh.cepek@usa.net>)
+
+    * a0d58b2 Update documentation
+        (Josh Cepek <josh.cepek@usa.net>)
+
+    * 5758825 Fix vars.example with proper path to extensions.temp
+        (Josh Cepek <josh.cepek@usa.net>)
+
+    * 89f369c Add support to change private key passphrases
+        (Josh Cepek <josh.cepek@usa.net>)
+
+    * 49d7c10 Improve docs: add Upgrade-Notes; add online support refs
+        (Josh Cepek <josh.cepek@usa.net>)
+
+    * fcc4547 Add build-dist packaging script; update Building docs
+        (Josh Cepek <josh.cepek@usa.net>)
+
+    * f74d08e docs: update Hacking.md with layout & git conventions
+        (Josh Cepek <josh.cepek@usa.net>)
+
+    * 0754f23 Offload temp file removal to a clean_temp() function
+        (Josh Cepek <josh.cepek@usa.net>)
+
+    * 1c90df9 Fix incorrect handling of invalid --use-algo option
+        (Josh Cepek <josh.cepek@usa.net>)
+
+    * c86289b Fix batch-mode handling with changes in e75ad75
+        (Josh Cepek <josh.cepek@usa.net>)
+
+    * e75ad75 refine how booleans are evaluated
+        (Eric F Crist <ecrist@secure-computing.net>)
+
+    * cc19823 Merge PKCS#7 feature from pull req #14
+        (Author: Luiz Angelo Daros de Luca <luizluca@tre-sc.gov.br>)
+        (Modified-By: Josh Cepek <josh.cepek@usa.net>)
+
+    * 8b1fe01 Support OpenSSL-0.9.8 with the EXTRA_EXTS feature
+        (Josh Cepek <josh.cepek@usa.net>)
+
+    * d5516d5 Windows: make builds easier by using a matching dir structure
+        (Josh Cepek <josh.cepek@usa.net>)
+
+    * dc2e6dc Windows: improve external checks and env-var help
+        (Josh Cepek <josh.cepek@usa.net>)
+
+3.0.0-rc1 (2013/12/01)
+
+    * The 3.x release is a nearly complete re-write of the 2.x codebase
+
+    * Initial 3.x series code by Josh Cepek <josh.cepek@usa.net> -- continuing
+    maintenance by the OpenVPN community development team and associated
+    contributors
+
+    * Add ECDSA (elliptic curve) support, thanks to Steffan Karger
+    <steffan@karger.me>
diff --git a/roles/ca/files/easyrsa/README.md b/roles/ca/files/easyrsa/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..c064076fcd926c9d4d1356e0d1bf70839b12eea2
--- /dev/null
+++ b/roles/ca/files/easyrsa/README.md
@@ -0,0 +1,52 @@
+# Overview
+
+easy-rsa is a CLI utility to build and manage a PKI CA. In laymen's terms,
+this means to create a root certificate authority, and request and sign 
+certificates, including sub-CAs and certificate revocation lists (CRL).
+
+# Downloads
+
+If you are looking for release downloads, please see the releases section on
+GitHub. Releases are also available as source checkouts using named tags.
+
+# Documentation
+
+For 3.x project documentation and usage, see the [README.quickstart.md](README.quickstart.md) file or
+the more detailed docs under the doc/ directory. The .md files are in Markdown
+format and can be converted to html files as desired for release packages, or
+read as-is in plaintext.
+
+# Getting help using easy-rsa
+
+Currently, Easy-RSA development co-exists with OpenVPN even though they are
+separate projects. The following resources are good places as of this writing to
+seek help using Easy-RSA:
+
+The [openvpn-users mailing list](https://lists.sourceforge.net/lists/listinfo/openvpn-users)
+is a good place to post usage or help questions.
+
+You can also try IRC at Freenode/#openvpn for general support or Freenode/#easyrsa for development discussion.
+
+# Branch structure
+
+The easy-rsa master branch is currently tracking development for the 3.x release
+cycle. Please note that, at any given time, master may be broken.  Feel free to
+create issues against master, but have patience when using the master branch.  It
+is recommended to use a release, and priority will be given to bugs identified in
+the most recent release.
+
+The prior 2.x and 1.x versions are available as release branches for
+tracking and possible back-porting of relevant fixes. Branch layout is:
+
+    master         <- 3.x, at present
+    v3.x.x            pre-release branches, used for staging branches
+    release/2.x
+    release/1.x
+
+LICENSING info for 3.x is in the [COPYING.md](COPYING.md) file
+
+# Code style, standards
+
+We are attempting to adhere to the POSIX standard, which can be found here:
+
+http://pubs.opengroup.org/onlinepubs/9699919799/
diff --git a/roles/ca/files/easyrsa/README.quickstart.md b/roles/ca/files/easyrsa/README.quickstart.md
new file mode 100644
index 0000000000000000000000000000000000000000..15c8182770537b347884e87a7ee2154e70dcb833
--- /dev/null
+++ b/roles/ca/files/easyrsa/README.quickstart.md
@@ -0,0 +1,99 @@
+Easy-RSA 3 Quickstart README
+============================
+
+This is a quickstart guide to using Easy-RSA version 3. Detailed help on usage
+and specific commands can be found by running ./easyrsa -h.  Additional
+documentation can be found in the doc/ directory.
+
+If you're upgrading from the Easy-RSA 2.x series, there are Upgrade-Notes
+available, also under the doc/ path.
+
+Setup and signing the first request
+-----------------------------------
+
+Here is a quick run-though of what needs to happen to start a new PKI and sign
+your first entity certificate:
+
+1. Choose a system to act as your CA and create a new PKI and CA:
+
+        ./easyrsa init-pki
+        ./easyrsa build-ca
+
+2. On the system that is requesting a certificate, init its own PKI and generate
+   a keypair/request. Note that init-pki is used _only_ when this is done on a
+   separate system (or at least a separate PKI dir.) This is the recommended
+   procedure. If you are not using this recommended procedure, skip the next
+   import-req step.
+
+        ./easyrsa init-pki
+        ./easyrsa gen-req EntityName
+
+3. Transport the request (.req file) to the CA system and import it. The name
+   given here is arbitrary and only used to name the request file.
+
+        ./easyrsa import-req /tmp/path/to/import.req EntityName
+
+4. Sign the request as the correct type. This example uses a client type:
+
+        ./easyrsa sign-req client EntityName
+
+5. Transport the newly signed certificate to the requesting entity. This entity
+   may also need the CA cert (ca.crt) unless it had a prior copy.
+
+6. The entity now has its own keypair, signed cert, and the CA.
+
+Signing subsequent requests
+---------------------------
+
+Follow steps 2-6 above to generate subsequent keypairs and have the CA return
+signed certificates.
+
+Revoking certs and creating CRLs
+--------------------------------
+
+This is a CA-specific task.
+
+To permanently revoke an issued certificate, provide the short name used during
+import:
+
+        ./easyrsa revoke EntityName
+
+To create an updated CRL that contains all revoked certs up to that point:
+
+        ./easyrsa gen-crl
+
+After generation, the CRL will need to be sent to systems that reference it.
+
+Generating Diffie-Hellman (DH) params
+-------------------------------------
+
+After initializing a PKI, any entity can create DH params that needs them. This
+is normally only used by a TLS server. While the CA PKI can generate this, it
+makes more sense to do it on the server itself to avoid the need to send the
+files to another system after generation.
+
+DH params can be generated with:
+
+        ./easyrsa gen-dh
+
+Showing details of requests or certs
+------------------------------------
+
+To show the details of a request or certificate by referencing the short
+EntityName, use one of the following commands. It is an error to call these
+without a matching file.
+
+        ./easyrsa show-req EntityName
+        ./easyrsa show-cert EntityName
+
+Changing private key passphrases
+--------------------------------
+
+RSA and EC private keys can be re-encrypted so a new passphrase can be supplied
+with one of the following commands depending on the key type:
+
+        ./easyrsa set-rsa-pass EntityName
+        ./easyrsa set-ec-pass EntityName
+
+Optionally, the passphrase can be removed completely with the 'nopass' flag.
+Consult the command help for details.
diff --git a/roles/ca/files/easyrsa/doc/EasyRSA-Advanced.md b/roles/ca/files/easyrsa/doc/EasyRSA-Advanced.md
new file mode 100644
index 0000000000000000000000000000000000000000..170165cfaa3e805baa5134483955eaff165c35b1
--- /dev/null
+++ b/roles/ca/files/easyrsa/doc/EasyRSA-Advanced.md
@@ -0,0 +1,115 @@
+Easy-RSA Advanced Reference
+=============================
+
+This is a technical reference for advanced users familiar with PKI processes. If
+you need a more detailed description, see the `EasyRSA-Readme` or `Intro-To-PKI`
+docs instead.
+
+Configuration Reference
+-----------------------
+
+#### Configuration Sources
+
+  There are 3 possible ways to perform external configuration of Easy-RSA,
+  selected in the following order where the first defined result wins:
+
+  1. Commmand-line option
+  2. Environmental variable
+  3. 'vars' file, if one is present (see `vars Autodetection` below)
+  4. Built-in default
+
+  Note that not every possible config option can be set everywhere, although any
+  env-var can be added to the 'vars' file even if it's not shown by default.
+
+#### vars Autodetection
+
+  A 'vars' file is a file named simply `vars` (without an extension) that
+  Easy-RSA will source for configuration. This file is specifically designed
+  *not* to replace variables that have been set with a higher-priority method
+  such as CLI opts or env-vars.
+
+  The following locations are checked, in this order, for a vars file. Only the
+  first one found is used:
+
+  1. The file referenced by the --vars CLI option
+  2. The file referenced by the env-var named `EASYRSA_VARS_FILE`
+  3. The directory referenced by the `EASYRSA_PKI` env-var
+  4. The default PKI directory at $PWD/pki
+  4. The directory referenced by the `EASYRSA` env-var
+  5. The directory containing the easyrsa program
+
+  Defining the env-var `EASYRSA_NO_VARS` will override the sourcing of the vars
+  file in all cases, including defining it subsequently as a global option.
+
+#### OpenSSL Config
+
+  Easy-RSA is tightly coupled to the OpenSSL config file (.cnf) for the
+  flexibility the script provides. It is required that this file be available,
+  yet it is possible to use a different OpenSSL config file for a particular
+  PKI, or even change it for a particular invocation.
+
+  The OpenSSL config file is searched for in the following order:
+
+  1. The env-var `EASYRSA_SSL_CONF`
+  2. The 'vars' file (see `vars Autodetection` above)
+  3. The `EASYRSA_PKI` directory with a filename of `openssl-easyrsa.cnf`
+  4. The `EASYRSA` directory with a filename of `openssl-easyrsa.cnf`
+
+Advanced extension handling
+---------------------------
+
+Normally the cert extensions are selected by the cert type given on the CLI
+during signing; this causes the matching file in the x509-types subdirectory to
+be processed for OpenSSL extensions to add. This can be overridden in a
+particular PKI by placing another x509-types dir inside the `EASYRSA_PKI` dir
+which will be used instead.
+
+The file named `COMMON` in the x509-types dir is appended to every cert type;
+this is designed for CDP usage, but can be used for any extension that should
+apply to every signed cert.
+
+Additionally, the contents of the env-var `EASYRSA_EXTRA_EXTS` is appended with
+its raw text added to the OpenSSL extensions. The contents are appended as-is to
+the cert extensions; invalid OpenSSL configs will usually result in failure.
+
+Environmental Variables Reference
+---------------------------------
+
+A list of env-vars, any matching global option (CLI) to set/override it, and a
+possible terse description is shown below:
+
+ *  `EASYRSA` - should point to the Easy-RSA top-level dir, where the easyrsa script is located.
+ *  `EASYRSA_OPENSSL` - command to invoke openssl
+ *  `EASYRSA_SSL_CONF` - the openssl config file to use
+ *  `EASYRSA_PKI` (CLI: `--pki-dir`) - dir to use to hold all PKI-specific files, defaults to $PWD/pki.
+ *  `EASYRSA_DN` (CLI: `--dn-mode`) - set to the string `cn_only` or `org` to
+    alter the fields to include in the req DN
+ *  `EASYRSA_REQ_COUNTRY` (CLI: `--req-c`) - set the DN country with org mode
+ *  `EASYRSA_REQ_PROVINCE` (CLI: `--req-st`) - set the DN state/province with
+    org mode
+ *  `EASYRSA_REQ_CITY` (CLI: `--req-city`) - set the DN city/locality with org
+    mode
+ *  `EASYRSA_REQ_ORG` (CLI: `--req-org`) - set the DN organization with org mode
+ *  `EASYRSA_REQ_EMAIL` (CLI: `--req-email`) - set the DN email with org mode
+ *  `EASYRSA_REQ_OU` (CLI: `--req-ou`) - set the DN organizational unit with org
+    mode
+ *  `EASYRSA_KEY_SIZE` (CLI: `--key-size`) - set the keysize in bits to generate
+ *  `EASYRSA_ALGO` (CLI: `--use-algo`) - set the crypto alg to use: rsa or ec
+ *  `EASYRSA_CURVE` (CLI: `--curve`) - define the named EC curve to use
+ *  `EASYRSA_EC_DIR` - dir to store generated ecparams
+ *  `EASYRSA_CA_EXPIRE` (CLI: `--days`) - set the CA expiration time in days
+ *  `EASYRSA_CERT_EXPIRE` (CLI: `--days`) - set the issued cert expiration time
+    in days
+ *  `EASYRSA_CRL_DAYS` (CLI: `--days`) - set the CRL 'next publish' time in days
+ *  `EASYRSA_NS_SUPPORT` (CLI: `--ns-cert`) - string 'yes' or 'no' fields to
+    include the deprecated Netscape extensions
+ *  `EASYRSA_NS_COMMENT` (CLI: `--ns-comment`) - string comment to include when
+    using the deprecated Netscape extensions
+ *  `EASYRSA_TEMP_FILE` - a temp file to use when dynamically creating req/cert
+    extensions
+ *  `EASYRSA_REQ_CN` (CLI: `--req-cn`) - default CN, necessary to set in BATCH
+    mode
+ *  `EASYRSA_DIGEST` (CLI: `--digest`) - set a hash digest to use for req/cert
+    signing
+ *  `EASYRSA_BATCH` (CLI: `--batch`) - enable batch (no-prompt) mode; set
+    env-var to non-zero string to enable (CLI takes no options)
diff --git a/roles/ca/files/easyrsa/doc/EasyRSA-Readme.md b/roles/ca/files/easyrsa/doc/EasyRSA-Readme.md
new file mode 100644
index 0000000000000000000000000000000000000000..bece589771f11ecdc75dc8d4dbcfbc231c2fc5d6
--- /dev/null
+++ b/roles/ca/files/easyrsa/doc/EasyRSA-Readme.md
@@ -0,0 +1,235 @@
+Easy-RSA 3 Documentation Readme
+===============================
+
+This document explains how Easy-RSA 3 and each of its assorted features work.
+
+If you are looking for a quickstart with less background or detail, an
+implementation-specific Howto or Readme may be available in this (the `doc/`)
+directory.
+
+Easy-RSA Overview
+-----------------
+
+Easy-RSA is a utility for managing X.509 PKI, or Public Key Infrastructure. A
+PKI is based on the notion of trusting a particular authority to authenticate a
+remote peer; for more background on how PKI works, see the `Intro-To-PKI`
+document.
+
+The code is written in platform-neutral POSIX shell, allowing use on a wide
+range of host systems. The official Windows release also comes bundled with the
+programs necessary to use Easy-RSA. The shell code attempts to limit the number
+of external programs it depends on. Crypto-related tasks use openssl as the
+functional backend.
+
+Feature Highlights
+------------------
+
+Here's a non-exhaustive list of the more notable Easy-RSA features:
+
+ *  Easy-RSA is able to manage multiple PKIs, each with their own independent
+    configuration, storage directory, and X.509 extension handling.
+ *  Multiple Subject Name (X.509 DN field) formatting options are supported. For
+    VPNs, this means a cleaner commonName only setup can be used.
+ *  A single backend is used across all supported platforms, ensuring that no
+    platform is 'left out' of the rich features. Unix-alikes (BSD, Linux, etc)
+    and Windows are all supported.
+ *  Easy-RSA's X.509 support includes CRL, CDP, keyUsage/eKu attributes, and
+    additional features. The included support can be changed or extended as an
+    advanced feature.
+ *  Interactive and automated (batch) modes of operation
+ *  Flexible configuration: features can be enabled through command-line
+    options, environment variables, a config file, or a combination of these.
+ *  Built-in defaults allow Easy-RSA to be used without first editing a config
+    file.
+
+Obtaining and Using Easy-RSA
+----------------------------
+
+#### Download and extraction (installation)
+
+  Easy-RSA's main program is a script, supported by a couple of config files. As
+  such, there is no formal "installation" required. Preparing to use Easy-RSA is
+  as simple as downloading the compressed package (.tar.gz for Linux/Unix or
+  .zip for Windows) and extract it to a location of your choosing. There is no
+  compiling or OS-dependent setup required.
+
+  You should install and run Easy-RSA as a non-root (non-Administrator) account
+  as root access is not required.
+
+#### Running Easy-RSA
+
+  Invoking Easy-RSA is done through your preferred shell. Under Windows, you
+  will use the `EasyRSA Start.bat` program to provide a POSIX-shell environment
+  suitable for using Easy-RSA.
+
+  The basic format for running commands is:
+
+    ./easyrsa command [ cmd-opts ]
+
+  where `command` is the name of a command to run, and `cmd-opts` are any
+  options to supply to the command. Some commands have mandatory or optional
+  cmd-opts. Note the leading `./` component of the command: this is required in
+  Unix-like environments and may be a new concept to some Windows users.
+
+  General usage and command help can be shown with:
+
+    ./easyrsa help [ command ]
+
+  When run without any command, general usage and a list of available commands
+  are shown; when a command is supplied, detailed help output for that command
+  is shown.
+
+Configuring Easy-RSA
+--------------------
+
+Easy-RSA 3 no longer needs any configuration file prior to operation, unlike
+earlier versions. However, the `vars.example` file contains many commented
+options that can be used to control non-default behavior as required. Reading
+this file will provide an idea of the basic configuration available. Note that
+a vars file must be named just `vars` (without an extension) to actively use it.
+
+Additionally, some options can be defined at runtime with options on the
+command-line. A full list can be shown with:
+
+    ./easyrsa help options
+
+Any of these options can appear before the command as required as shown below:
+
+    ./easyrsa [options] command [ cmd-opts ]
+
+For experts, additional configuration flexibility is available by way of
+env-vars and custom X.509 extensions. Consult the `EasyRSA-Advanced`
+documentation for details
+
+Getting Started: The Basics
+---------------------------
+
+Some of the terms used here will be common to those familiar with how PKI works.
+Instead of describing PKI basics, please consult the document `Intro-To-PKI` if
+you need a more basic description of how a PKI works.
+
+#### Creating an Easy-RSA PKI
+
+  In order to do something useful, Easy-RSA needs to first initialize a
+  directory for the PKI. Multiple PKIs can be managed with a single installation
+  of Easy-RSA, but the default directory is called simply "pki" unless otherwise
+  specified.
+
+  To create or clear out (re-initialize) a new PKI, use the command:
+
+    ./easyrsa init-pki
+
+  which will create a new, blank PKI structure ready to be used. Once created,
+  this PKI can be used to make a new CA or generate keypairs.
+
+#### The PKI Directory Structure
+
+  An Easy-RSA PKI contains the following directory structure:
+
+  * private/ - dir with private keys generated on this host
+  * reqs/ - dir with locally generated certificate requests (for a CA imported
+    requests are stored here)
+
+  In a clean PKI no files will exist until, just the bare directories. Commands
+  called later will create the necessary files depending on the operation.
+
+  When building a CA, a number of new files are created by a combination of
+  Easy-RSA and (indirectly) openssl. The important CA files are:
+
+  * `ca.crt` - This is the CA certificate
+  * `index.txt` - This is the "master database" of all issued certs
+  * `serial` - Stores the next serial number (serial numbers increment)
+  * `private/ca.key` - This is the CA private key (security-critical)
+  * `certs_by_serial/` - dir with all CA-signed certs by serial number
+  * `issued/` - dir with issued certs by commonName
+
+#### After Creating a PKI
+
+  Once you have created a PKI, the next useful step will be to either create a
+  CA, or generate keypairs for a system that needs them. Continue with the
+  relevant section below.
+
+Using Easy-RSA as a CA
+----------------------
+
+#### Building the CA
+
+  In order to sign requests to produce certificates, you need a CA. To create a
+  new CA in a PKI you have created, run:
+
+    ./easyrsa build-ca
+
+  Be sure to use a strong passphrase to protect the CA private key. Note that
+  you must supply this passphrase in the future when performing signing
+  operations with your CA, so be sure to remember it.
+
+  During the creation process, you will also select a name for the CA called the
+  Common Name (CN.) This name is purely for display purposes and can be set as
+  you like.
+
+#### Importing requests to the CA
+
+  Once a CA is built, the PKI is intended to be used to import requests from
+  external systems that are requesting a signed certificate from this CA. In
+  order to sign the request, it must first be imported so Easy-RSA knows about
+  it. This request file must be a standard CSR in PKCS#10 format.
+
+  Regardless of the file name to import, Easy-RSA uses a "short name" defined
+  during import to refer to this request. Importing works like this:
+
+    ./easyrsa import-req /path/to/request.req nameOfRequest
+
+  The nameOfRequest should normally refer to the system or person making the
+  request.
+
+#### Signing a request
+
+  Once Easy-RSA has imported a request, it can be reviewed and signed. Every
+  certificate needs a "type" which controls what extensions the certificate gets
+  Easy-RSA ships with 3 possible types: `client`, `server`, and `ca`, described
+  below:
+
+  * client - A TLS client, suitable for a VPN user or web browser (web client)
+  * server - A TLS server, suitable for a VPN or web server
+  * ca - A subordinate CA, used when chaining multiple CAs together
+
+  Additional types of certs may be defined by local sites as needed; see the
+  advanced documentation for details.
+
+#### Revoking and publishing CRLs
+
+  If an issue certificate needs to be revoked, this can be done as follows:
+
+    ./easyrsa revoke nameOfRequest
+
+  To generate a CRL suitable for publishing to systems that use it, run:
+
+    ./easyrsa gen-crl
+
+  Note that this will need to be published or sent to systems that rely on an
+  up-to-date CRL as the certificate is still otherwise valid.
+
+Using Easy-RSA to generate keypairs & requests
+----------------------------------------------
+
+Easy-RSA can generate a keypair and certificate request in PKCS#10 format. This
+request is what a CA needs in order to generate and return a signed certificate.
+
+Ideally you should never generate entity keypairs for a client or server in a
+PKI you are using for your CA. It is best to separate this process and generate
+keypairs only on the systems you plan to use them.
+
+Easy-RSA can generate a keypair and request with the following command:
+
+    ./easyrsa gen-req nameOfRequest
+
+You will then be given a chance to modify the Subject details of your request.
+Easy-RSA uses the short name supplied on the command-line by default, though you
+are free to change it if necessary. After providing a passphrase and Subject
+details, the keypair and request files will be shown.
+
+In order to obtain a signed certificate, the request file must be sent to the
+CA for signing; this step is obviously not required if a single PKI is used as
+both the CA and keypair/request generation as the generated request is already
+"imported."
+
diff --git a/roles/ca/files/easyrsa/doc/EasyRSA-Upgrade-Notes.md b/roles/ca/files/easyrsa/doc/EasyRSA-Upgrade-Notes.md
new file mode 100644
index 0000000000000000000000000000000000000000..6cc6df29ffc42fd9db232c08709299e4482359b4
--- /dev/null
+++ b/roles/ca/files/easyrsa/doc/EasyRSA-Upgrade-Notes.md
@@ -0,0 +1,58 @@
+Upgrading to Easy-RSA 3 from earlier versions
+=========
+
+People upgrading to Easy-RSA 3 from a 2.x version should note some important
+changes starting with version 3. For a better overview of version 3 in general,
+see the Readme in the doc/ directory.
+
+List of important changes
+----
+
+ * nsCertType extensions are no longer included by default. Use of such
+   "Netscape" attributes have been deprecated upstream and their use is
+   discouraged. Configure `EASYRSA_NS_SUPPORT` in vars if you want to enable
+   this legacy behavior.
+
+   Notably, this is important for OpenVPN deployments relying on the
+   `--ns-cert-type` directive. Either have OpenVPN use the preferred
+   `--remote-cert-tls` option, or enable legacy NS extensions.
+
+ * The default request Subject (or DN, Distinguished Name) includes just the
+   commonName. This is more suitable for VPNs and environments that don't wish
+   to include info about the Country/State/City/Org/OU in certs. Configure
+   `EASYRSA_DN` in vars if you want to enable the legacy behavior.
+
+ * The 3.0 release lacks PKCS#11 (smartcard/token) support. This is anticipated
+   to be supported in a future point-release to target each platform's need.
+
+ * The -utf8 option has been added for all supported commands.  This should be
+   backwards compatible with ASCII strings.
+
+ * The default private key encryption has been changed from 3des to aes256.
+
+
+Some new concepts
+----
+
+Easy-RSA 3 has some new concepts compared to the prior v2 series.
+
+### Request-Import-Sign workflow
+
+  v3 is now designed to support keypairs generated on the target system where
+  they will be used, thus improving security as no keys need to be transferred
+  between hosts. The old workflow of generating everything in a single PKI is
+  still supported as well.
+
+  The recommended workflow when using Easy-RSA as a CA is to import requests,
+  sign them, and return the issued & CA certs. Each requesting system can use
+  Easy-RSA without a CA to generate keypairs & requests.
+
+### "Org"-style DN flexibility
+
+  When using Easy-RSA in the "org" DN mode, it is no longer required to match
+  some of the field values. This improves flexibility, and enables easier remote
+  generation as the requester doesn't need to know the CA's values in advance.
+
+  Previously in v2, the Country, State, and Org values all had to match or a
+  request couldn't be signed. If you want the old behavior you can change the
+  OpenSSL config to require it or simply look over the DN at signing time.
diff --git a/roles/ca/files/easyrsa/doc/Hacking.md b/roles/ca/files/easyrsa/doc/Hacking.md
new file mode 100644
index 0000000000000000000000000000000000000000..d1b7f6ad7d1a358127008642af82edd4f802cc40
--- /dev/null
+++ b/roles/ca/files/easyrsa/doc/Hacking.md
@@ -0,0 +1,142 @@
+Easy-RSA 3 Hacking Guide
+===
+
+This document is aimed at programmers looking to improve on the existing
+codebase.
+
+Compatibility
+---
+
+The `easyrsa` code is written in POSIX shell (and any cases where it is not is
+considered a bug to be fixed.) The only exceptions are the `local` keyword and
+the construct `export FOO=baz`, both well-supported.
+
+As such, modifications to the code should also be POSIX; platform-specific code
+should be placed under the `distro/` dir and listed by target platform.
+
+Coding conventions
+---
+
+While there aren't strict syntax standards associated with the project, please
+follow the existing format and flow when possible; however, specific exceptions
+can be made if there is a significant reason or benefit.
+
+Do try to:
+
+  * Keep variables locally-scoped when possible
+  * Comment sections of code for readability
+  * Use the conventions for prefixes on global variables
+  * Set editors for tab stops of 8 spaces
+  * Use tabs for code indents; use aligned spaces for console text
+
+Keeping code, docs, and examples in sync
+---
+
+Changes that adjust, add, or remove features should have relevant docs, help
+output, and examples updated at the same time.
+
+Release versioning
+---
+
+A point-release bump (eg: 3.0 to 3.1) is required when the frontend interface
+changes in a non-backwards compatible way. Always assume someone has an
+automated process that relies on the current functionality for official
+(non-beta, non-rc) releases. A possible exception exists for bugfixes that do
+break backwards-compatibility; caution is to be used in such cases.
+
+The addition of a new command may or may not require a point-release depending
+on the significance of the feature; the same holds true for additional optional
+arguments to commands.
+
+Project layout
+---
+
+The project's files are structured as follows:
+
+  * `easyrsa3/` is the primary project code. On Linux/Unix-alikes, all the core
+    code and supporting files are stored here.
+  * `Licensing/` is for license docs.
+  * `build/` is for build information and scripts.
+  * `contrib/` is for externally-contributed files, such as useful external
+    scripts or interfaces for other systems/languages.
+  * `distro/` is for distro-specific supporting files, such as the Windows
+    frontend wrappers. Code components that are not platform-neutral should go
+    here.
+  * `doc/` is for documentation. Much of this is in Markdown format which can be
+    easily converted to HTML for easy viewing under Windows.
+  * `release-keys/` list current and former KeyIDs used to sign release packages
+    (not necessarily git tags) available for download.
+  * The top-level dir includes files for basic project info and reference
+    appropriate locations for more detail.
+
+As a brief note, it is actually possible to take just the easyrsa3/ dir and end
+up with a functional project; the remaining structure includes docs, build prep,
+distro-specific wrappers, and contributed files.
+
+Git conventions
+---
+
+As of Easy-RSA 3, the following git conventions should be used. These are mostly
+useful for people with repo access in order to keep a standard meaning to commit
+messages and merge actions.
+
+### Signed-off-by: and related commit message lines
+
+  Committers with push access should ensure a `Signed-off-by:` line exists at
+  the end of the commit message with their name on it. This indicates that the
+  committer has reviewed the changes to the commit in question and approve of
+  the feature and code in question. It also helps verify the code came from an
+  acceptable source that won't cause issues with the license.
+
+  This can be automatically added by git using `git commit -s`.
+
+  Additional references can be included as well. If multiple people reviewed the
+  change, the committer may add their names in additional `Signed-off-by:`
+  lines; do get permission from that person before using their name, however ;)
+
+  The following references may be useful as well:
+
+  * `Signed-off-by:` -- discussed above, indicates review of the commit
+  * `Author:` -- references an author of a particular feature, in full or
+    significant part
+  * `Changes-by:` -- indicates the listed party contributed changes or
+    modifications to a feature
+  * `Acked-by:` -- indicates review of the feature, code, and/or functional
+    correctness
+
+### Merging from external sources (forks, patches, etc)
+
+  Contributions can come in many forms: GitHub "pull requests" from cloned
+  repos, references to external repos, patches to the ML, or others. Those won't
+  necessary have `Signed-off-by:` lines or may contain less info in the commit
+  message than is desirable to explain the changes.
+
+  The committing author to this project should make a merge-commit in this case
+  with the appropriate details provided there. If additional code changes are
+  necessary, this can be done on a local branch prior to merging back into the
+  mainline branch.
+
+  This merge-commit should list involved contributors with `Author:` or similar
+  lines as required. The individual commits involved in a merge also retain the
+  original committer; regardless, the merge-commit message should give a clear
+  indication of what the entire set of commits does as a whole.
+
+### Tagging
+
+  Tags should follow the convention:
+
+    vM.m.p
+
+  where `M` is the major version, `m` is the minor "point-release" version, and
+  `p` is the patch-level. Suffixes of `-rc#`, `-beta#`, etc can be added for
+  pre-release versions as required.
+
+  Currently tags are taken from the mainline development branch in question. The
+  ChangeLog should thus be updated prior to tagging. Tags should also be
+  annotated with an appropriate commit message and signed-off. This can be done
+  as shown below (don't use `-s` unless you intend to use GPG with git.)
+
+    git tag -a v1.2.3
+
+  Corresponding release downloads can be uploaded to release distribution points
+  as required.
diff --git a/roles/ca/files/easyrsa/doc/Intro-To-PKI.md b/roles/ca/files/easyrsa/doc/Intro-To-PKI.md
new file mode 100644
index 0000000000000000000000000000000000000000..ea56629e7a2d7cae6768cd7849625b6fc6c40ebe
--- /dev/null
+++ b/roles/ca/files/easyrsa/doc/Intro-To-PKI.md
@@ -0,0 +1,97 @@
+Introduction to PKI
+===================
+
+This document is designed to give you a brief introduction into how a PKI, or
+Public Key Infrastructure, works.
+
+Terminology Used
+----------------
+
+To avoid confusion, the following terms will be used throughout the Easy-RSA
+documentation. Short forms may be substituted for longer forms as convenient.
+
+ *  **PKI**: Public Key Infrastructure. This describes the collection of files
+    and associations between the CA, keypairs, requests, and certificates.
+ *  **CA**: Certificate Authority. This is the "master cert" at the root of a
+    PKI.
+ *  **cert**: Certificate. A certificate is a request that has been signed by a
+    CA. The certificate contains the public key, some details describing the
+    cert itself, and a digital signature from the CA.
+ *  **request**: Certificate Request (optionally 'req'.) This is a request for a
+    certificate that is then send to a CA for signing. A request contains the
+    desired cert information along with a digital signature from the private
+    key.
+ *  **keypair**: A keypair is an asymmetric cryptographic pair of keys. These
+    keys are split into two parts: the public and private keys. The public key
+    is included in a request and certificate.
+
+The CA
+------
+
+The heart of a PKI is the CA, or Certificate Authority, and this is also the
+most security-sensitive. The CA private key is used to sign all issued
+certificates, so its security is critical in keeping the entire PKI safe. For
+this reason, it is highly recommended that the CA PKI structure be kept on a
+system dedicated for such secure usage; it is not a great idea to keep the CA
+PKI mixed in with one used to generate end-entity certificates, such as clients
+or servers (VPN or web servers.)
+
+To start a new PKI, the CA is first created on the secure environment.
+Depending on security needs, this could be managed under a locked down account,
+dedicated system, or even a completely offline system or using removable media
+to improve security (after all, you can't suffer an online break-in if your
+system or PKI is not online.) The exact steps to create a CA are described in a
+separate section. When creating a new CA, the CA keypair (private and public
+keys) are created, as well as the file structure necessary to support signing
+issued certificates.
+
+Once a CA has been created, it can receive certificate requests from
+end-entities. These entity certificates are issued to consumers of X509
+certificates, such as a client or server of a VPN, web, or email system.  The
+certificate requests and certificates are not security-sensitive, and can be
+transferred in whatever means convenient, such as email, flash drive, etc. For
+better security, it is a good idea to verify the received request matches the
+sender's copy, such as by verifying the expected checksum against the sender's
+original.
+
+Keypairs and requests
+---------------------
+
+Individual end-entities do not need a full CA set up and will only need to
+create a keypair and associated certificate request. The private key is not used
+anywhere except on this entity, and should never leave that system. It is wise
+to secure this private key with a strong passphrase, because if lost or stolen
+the holder of the private key can make connections appearing as the certificate
+holder.
+
+Once a keypair is generated, the certificate request is created and digitally
+signed using the private key. This request will be sent to a CA for signing, and
+a signed certificate will be returned.
+
+How requests become certificates
+--------------------------------
+
+After a CA signs the certificate request, a signed certificate is produced. In
+this step, the CA's private key is used to digitally sign the entity's public
+key so that any system trusting the CA certificate can implicitly trust the
+newly issued certificate. This signed certificate is then sent back to the
+requesting entity. The issued certificate is not security-sensitive and can be
+sent over plaintext transmission methods.
+
+Verifying an issued certificate
+-------------------------------
+
+After 2 entities have created keypairs, sent their requests to the CA, and
+received a copy of their signed certificates and the CA's own certificate, they
+can mutually authenticate with one-another. This process does not require the 2
+entities to have previously exchanged any kind of security information directly.
+
+During a TLS handshake each side of the connection presents their own cert chain
+to the remote end. Each side checks the validity of the cert received against
+their own copy of the CA cert. By trusting the CA root cert, the peer they are
+talking to can be authenticated.
+
+The remote end proves it "really is" the entity identified by the cert by
+signing a bit of data using its own private key. Only the holder of the private
+key is able to do this, allowing the remote end to verify the authenticity of
+the system being connected to.
diff --git a/roles/ca/files/easyrsa/easyrsa b/roles/ca/files/easyrsa/easyrsa
new file mode 100755
index 0000000000000000000000000000000000000000..5df2c238a57a3896c124d227194811900592eddc
--- /dev/null
+++ b/roles/ca/files/easyrsa/easyrsa
@@ -0,0 +1,1714 @@
+#!/bin/sh
+
+# Easy-RSA 3 -- A Shell-based CA Utility
+#
+# Copyright (C) 2018 by the Open-Source OpenVPN development community.
+# A full list of contributors can be found in the ChangeLog.
+#
+# This code released under version 2 of the GNU GPL; see COPYING and the
+# Licensing/ directory of this project for full licensing details.
+
+# Help/usage output to stdout
+usage() {
+	# command help:
+	print "
+Easy-RSA 3 usage and overview
+
+USAGE: easyrsa [options] COMMAND [command-options]
+
+A list of commands is shown below. To get detailed usage and help for a
+command, run:
+  ./easyrsa help COMMAND
+
+For a listing of options that can be supplied before the command, use:
+  ./easyrsa help options
+
+Here is the list of commands available with a short syntax reminder. Use the
+'help' command above to get full usage details.
+
+  init-pki
+  build-ca [ cmd-opts ]
+  gen-dh
+  gen-req <filename_base> [ cmd-opts ]
+  sign-req <type> <filename_base>
+  build-client-full <filename_base> [ cmd-opts ]
+  build-server-full <filename_base> [ cmd-opts ]
+  revoke <filename_base> [cmd-opts]
+  renew <filename_base> [cmd-opts]
+  build-serverClient-full <filename_base> [ cmd-opts ]
+  gen-crl
+  update-db
+  show-req <filename_base> [ cmd-opts ]
+  show-cert <filename_base> [ cmd-opts ]
+  show-ca [ cmd-opts ]
+  import-req <request_file_path> <short_basename>
+  export-p7 <filename_base> [ cmd-opts ]
+  export-p12 <filename_base> [ cmd-opts ]
+  set-rsa-pass <filename_base> [ cmd-opts ]
+  set-ec-pass <filename_base> [ cmd-opts ]
+"
+
+	# collect/show dir status:
+	err_source="Not defined: vars autodetect failed and no value provided"
+	work_dir="${EASYRSA:-$err_source}"
+	pki_dir="${EASYRSA_PKI:-$err_source}"
+	print "\
+DIRECTORY STATUS (commands would take effect on these locations)
+  EASYRSA: $work_dir
+      PKI: $pki_dir
+"
+} # => usage()
+
+# Detailed command help
+# When called with no args, calls usage(), otherwise shows help for a command
+cmd_help() {
+	text=""
+	opts=""
+	case "$1" in
+		init-pki|clean-all) text="
+  init-pki [ cmd-opts ]
+      Removes & re-initializes the PKI dir for a clean PKI" ;;
+		build-ca) text="
+  build-ca [ cmd-opts ]
+      Creates a new CA"
+      			opts="
+        nopass  - do not encrypt the CA key (default is encrypted)
+        subca   - create a sub-CA keypair and request (default is a root CA)" ;;
+		gen-dh) text="
+  gen-dh
+      Generates DH (Diffie-Hellman) parameters" ;;
+		gen-req) text="
+  gen-req <filename_base> [ cmd-opts ]
+      Generate a standalone keypair and request (CSR)
+
+      This request is suitable for sending to a remote CA for signing."
+      			opts="
+        nopass  - do not encrypt the private key (default is encrypted)" ;;
+		sign|sign-req) text="
+  sign-req <type> <filename_base>
+      Sign a certificate request of the defined type. <type> must be a known
+      type such as 'client', 'server', 'serverClient', or 'ca' (or a user-added type.)
+
+      This request file must exist in the reqs/ dir and have a .req file
+      extension. See import-req below for importing reqs from other sources." ;;
+		build|build-client-full|build-server-full|build-serverClient-full) text="
+  build-client-full <filename_base> [ cmd-opts ]
+  build-server-full <filename_base> [ cmd-opts ]
+  build-serverClient-full <filename_base> [ cmd-opts ]
+      Generate a keypair and sign locally for a client and/or server
+
+      This mode uses the <filename_base> as the X509 CN."
+			opts="
+        nopass  - do not encrypt the private key (default is encrypted)" ;;
+		revoke) text="
+  revoke <filename_base> [reason]
+      Revoke a certificate specified by the filename_base, with an optional 
+      revocation reason that is one of: 
+        unspecified
+        keyCompromise
+        CACompromise
+        affiliationChanged
+        superseded
+        cessationOfOperation
+        certificateHold";;
+		renew) text="
+  renew <filename_base> [ cmd-opts ]
+      Renew a certificate specified by the filename_base"
+				opts="
+        nopass  - do not encrypt the private key (default is encrypted)" ;;
+		gen-crl) text="
+  gen-crl
+      Generate a CRL" ;;
+		update-db) text="
+  update-db
+      Update the index.txt database
+
+      This command will use the system time to update the status of issued
+      certificates." ;;
+      		show-req|show-cert) text="
+  show-req  <filename_base> [ cmd-opts ]
+  show-cert <filename_base> [ cmd-opts ]
+      Shows details of the req or cert referenced by filename_base
+
+      Human-readable output is shown, including any requested cert options when
+      showing a request."
+      			opts="
+          full   - show full req/cert info, including pubkey/sig data" ;;
+		show-ca) text="
+  show-ca [ cmd-opts ]
+      Shows details of the CA cert
+
+      Human-readable output is shown."
+			opts="
+          full   - show full cert info, including pubkey/sig data" ;;
+		import-req) text="
+  import-req <request_file_path> <short_basename>
+      Import a certificate request from a file
+
+      This will copy the specified file into the reqs/ dir in
+      preparation for signing.
+      The <short_basename> is the filename base to create.
+
+      Example usage:
+        import-req /some/where/bob_request.req bob" ;;
+		export-p12) text="
+  export-p12 <filename_base> [ cmd-opts ]
+      Export a PKCS#12 file with the keypair specified by <filename_base>"
+			opts="
+        noca  - do not include the ca.crt file in the PKCS12 output
+        nokey - do not include the private key in the PKCS12 output" ;;
+		export-p7) text="
+  export-p7 <filename_base> [ cmd-opts ]
+      Export a PKCS#7 file with the pubkey specified by <filename_base>"
+			opts="
+        noca  - do not include the ca.crt file in the PKCS7 output" ;;
+		set-rsa-pass|set-ec-pass) text="
+  set-rsa-pass <filename_base> [ cmd-opts ]
+  set-ec-pass <filename_base> [ cmd-opts ]
+      Set a new passphrase on an RSA or EC key for the listed <filename_base>."
+                        opts="
+        nopass - use no password and leave the key unencrypted
+        file   - (advanced) treat the file as a raw path, not a short-name" ;;
+		altname|subjectaltname|san) text="
+  --subject-alt-name=SAN_FORMAT_STRING
+      This global option adds a subjectAltName to the request or issued
+      certificate. It MUST be in a valid format accepted by openssl or
+      req/cert generation will fail. Note that including multiple such names
+      requires them to be comma-separated; further invocations of this
+      option will REPLACE the value.
+
+      Examples of the SAN_FORMAT_STRING shown below:
+        DNS:alternate.example.net
+        DNS:primary.example.net,DNS:alternate.example.net
+        IP:203.0.113.29
+        email:alternate@example.net" ;;
+		options)
+			opt_usage ;;
+		"")
+			usage ;;
+		*) text="
+  Unknown command: '$1' (try without commands for a list of commands)" ;;
+	esac
+
+	# display the help text
+	print "$text"
+	[ -n "$opts" ] && print "
+      cmd-opts is an optional set of command options from this list:
+$opts"
+} # => cmd_help()
+
+# Options usage
+opt_usage() {
+	print "
+Easy-RSA Global Option Flags
+
+The following options may be provided before the command. Options specified
+at runtime override env-vars and any 'vars' file in use. Unless noted,
+non-empty values to options are mandatory.
+
+General options:
+
+--batch         : set automatic (no-prompts when possible) mode
+--pki-dir=DIR   : declares the PKI directory
+--vars=FILE     : define a specific 'vars' file to use for Easy-RSA config
+
+Certificate & Request options: (these impact cert/req field values)
+
+--days=#        : sets the signing validity to the specified number of days
+--digest=ALG    : digest to use in the requests & certificates
+--dn-mode=MODE  : DN mode to use (cn_only or org)
+--keysize=#     : size in bits of keypair to generate
+--req-cn=NAME   : default CN to use
+--subca-len=#   : path length of signed sub-CA certs; must be >= 0 if used
+--subject-alt-name : Add a subjectAltName. For more info and syntax, see:
+                     ./easyrsa help altname
+--use-algo=ALG  : crypto alg to use: choose rsa (default) or ec
+--curve=NAME    : for elliptic curve, sets the named curve to use
+--copy-ext      : Copy included request X509 extensions (namely subjAltName
+
+Organizational DN options: (only used with the 'org' DN mode)
+  (values may be blank for org DN options)
+
+--req-c=CC        : country code (2-letters)
+--req-st=NAME     : State/Province
+--req-city=NAME   : City/Locality
+--req-org=NAME    : Organization
+--req-email=NAME  : Email addresses
+--req-ou=NAME     : Organizational Unit
+
+Deprecated features:
+
+--ns-cert=YESNO       : yes or no to including deprecated NS extensions
+--ns-comment=COMMENT  : NS comment to include (value may be blank)
+"
+} # => opt_usage()
+
+# Wrapper around printf - clobber print since it's not POSIX anyway
+# shellcheck disable=SC1117
+print() { printf "%s\n" "$*"; }
+
+# Exit fatally with a message to stderr
+# present even with EASYRSA_BATCH as these are fatal problems
+die() {
+	print "
+Easy-RSA error:
+
+$1" 1>&2
+	clean_temp;
+	prog_exit "${2:-1}"
+} # => die()
+
+# non-fatal warning output
+warn() {
+	[ ! "$EASYRSA_BATCH" ] && \
+		print "
+$1" 1>&2
+} # => warn()
+
+# informational notices to stdout
+notice() {
+	[ ! "$EASYRSA_BATCH" ] && \
+		print "
+$1"
+} # => notice()
+
+# yes/no case-insensitive match (operates on stdin pipe)
+# Returns 0 when input contains yes, 1 for no, 2 for no match
+# If both strings are present, returns 1; first matching line returns.
+awk_yesno() {
+	#shellcheck disable=SC2016
+	awkscript='
+BEGIN {IGNORECASE=1; r=2}
+{       if(match($0,"no")) {r=1; exit}
+        if(match($0,"yes")) {r=0; exit}
+} END {exit r}'
+	awk "$awkscript"
+} # => awk_yesno()
+
+# intent confirmation helper func
+# returns without prompting in EASYRSA_BATCH
+confirm() {
+	[ "$EASYRSA_BATCH" ] && return
+	prompt="$1"
+	value="$2"
+	msg="$3"
+	input=""
+	print "
+$msg
+
+Type the word '$value' to continue, or any other input to abort."
+	printf %s "  $prompt"
+	#shellcheck disable=SC2162
+	read input
+	[ "$input" = "$value" ] && return
+	notice "Aborting without confirmation."
+	exit 9
+} # => confirm()
+
+# remove temp files
+clean_temp() {
+	for f in "$EASYRSA_TEMP_CONF" "$EASYRSA_TEMP_EXT" \
+		"$EASYRSA_TEMP_FILE_2" "$EASYRSA_TEMP_FILE_3" "$EASYRSA_TEMP_FILE_4"
+	do	[ -f "$f" ] && rm "$f" 2>/dev/null
+	done
+} # => clean_temp()
+
+prog_exit() {
+	ESTAT=0
+	[ -n "$1" ] && ESTAT=$1
+	(stty echo 2>/dev/null) || set -o echo
+	echo "" # just to get a clean line
+	exit "$ESTAT"
+} # => prog_exit()
+
+# Make LibreSSL safe config file from OpenSSL config file
+make_ssl_config() {
+sed \
+       -e "s\`ENV::EASYRSA\`EASYRSA\`g" \
+       -e "s\`\$dir\`$EASYRSA_PKI\`g" \
+       -e "s\`\$EASYRSA_PKI\`$EASYRSA_PKI\`g" \
+       -e "s\`\$EASYRSA_CERT_EXPIRE\`$EASYRSA_CERT_EXPIRE\`g" \
+       -e "s\`\$EASYRSA_CRL_DAYS\`$EASYRSA_CRL_DAYS\`g" \
+       -e "s\`\$EASYRSA_DIGEST\`$EASYRSA_DIGEST\`g" \
+       -e "s\`\$EASYRSA_KEY_SIZE\`$EASYRSA_KEY_SIZE\`g" \
+       -e "s\`\$EASYRSA_DIGEST\`$EASYRSA_DIGEST\`g" \
+       -e "s\`\$EASYRSA_DN\`$EASYRSA_DN\`g" \
+       -e "s\`\$EASYRSA_REQ_COUNTRY\`$EASYRSA_REQ_COUNTRY\`g" \
+       -e "s\`\$EASYRSA_REQ_PROVINCE\`$EASYRSA_REQ_PROVINCE\`g" \
+       -e "s\`\$EASYRSA_REQ_CITY\`$EASYRSA_REQ_CITY\`g" \
+       -e "s\`\$EASYRSA_REQ_ORG\`$EASYRSA_REQ_ORG\`g" \
+       -e "s\`\$EASYRSA_REQ_OU\`$EASYRSA_REQ_OU\`g" \
+       -e "s\`\$EASYRSA_REQ_CN\`$EASYRSA_REQ_CN\`g" \
+       -e "s\`\$EASYRSA_REQ_EMAIL\`$EASYRSA_REQ_EMAIL\`g" \
+	"$EASYRSA_SSL_CONF" > "$EASYRSA_SAFE_CONF" || die "\
+Failed to update $EASYRSA_SAFE_CONF"
+} # => make_ssl_config()
+
+vars_source_check() {
+	# Check for defined EASYRSA_PKI
+	[ -n "$EASYRSA_PKI" ] || die "\
+EASYRSA_PKI env-var undefined"
+} # => vars_source_check()
+
+# Verify supplied curve exists and generate curve file if needed
+verify_curve() {
+	if ! "$EASYRSA_OPENSSL" ecparam -name "$EASYRSA_CURVE" > /dev/null; then
+		die "\
+Curve $EASYRSA_CURVE not found. Run openssl ecparam -list_curves to show a
+list of supported curves."
+	fi
+
+	# Check that the ecparams dir exists
+	[ -d "$EASYRSA_EC_DIR" ] || mkdir "$EASYRSA_EC_DIR" || die "\
+Failed creating ecparams dir (permissions?) at:
+$EASYRSA_EC_DIR"
+
+	# Check that the required ecparams file exists
+	out="$EASYRSA_EC_DIR/${EASYRSA_CURVE}.pem"
+	[ -f "$out" ] && return 0
+	"$EASYRSA_OPENSSL" ecparam -name "$EASYRSA_CURVE" -out "$out" || die "\
+Failed to generate ecparam file (permissions?) when writing to:
+$out"
+
+	# Explicitly return success for caller
+	return 0
+}
+
+verify_ssl_lib () {
+	# make safessl-easyrsa.cnf
+	make_ssl_config
+
+	# Verify EASYRSA_OPENSSL command gives expected output
+	if [ -z "$EASYRSA_SSL_OK" ]; then
+		val="$("$EASYRSA_OPENSSL" version)"
+		case "${val%% *}" in
+			OpenSSL|LibreSSL)
+				notice "\
+Using SSL: $EASYRSA_OPENSSL $("$EASYRSA_OPENSSL" version)" ;;
+			*) die "\
+Missing or invalid OpenSSL
+Expected to find openssl command at: $EASYRSA_OPENSSL" ;;
+		esac
+	fi
+	EASYRSA_SSL_OK=1
+
+	# Verify EASYRSA_SSL_CONF file exists
+	[ -f "$EASYRSA_SSL_CONF" ] || die "\
+The OpenSSL config file cannot be found.
+Expected location: $EASYRSA_SSL_CONF"
+} # => verify_ssl_lib ()
+
+# Basic sanity-check of PKI init and complain if missing
+verify_pki_init() {
+	help_note="Run easyrsa without commands for usage and command help."
+
+	# check that the pki dir exists
+	vars_source_check
+	[ -d "$EASYRSA_PKI" ] || die "\
+EASYRSA_PKI does not exist (perhaps you need to run init-pki)?
+Expected to find the EASYRSA_PKI at: $EASYRSA_PKI
+$help_note"
+
+	# verify expected dirs present:
+	for i in private reqs; do
+		[ -d "$EASYRSA_PKI/$i" ] || die "\
+Missing expected directory: $i (perhaps you need to run init-pki?)
+$help_note"
+	done
+
+	# verify ssl lib
+	verify_ssl_lib
+} # => verify_pki_init()
+
+# Verify core CA files present
+verify_ca_init() {
+	help_note="Run without commands for usage and command help."
+
+	# First check the PKI has been initialized
+	verify_pki_init
+
+	# verify expected files present:
+	for i in serial index.txt ca.crt private/ca.key; do
+		if [ ! -f "$EASYRSA_PKI/$i" ]; then
+			[ "$1" = "test" ] && return 1
+			die "\
+Missing expected CA file: $i (perhaps you need to run build-ca?)
+$help_note"
+		fi
+	done
+
+	# When operating in 'test' mode, return success.
+	# test callers don't care about CA-specific dir structure
+	[ "$1" = "test" ] && return 0
+
+	# verify expected CA-specific dirs:
+	for i in issued certs_by_serial \
+		 revoked/certs_by_serial revoked/private_by_serial revoked/reqs_by_serial \
+		 renewed/certs_by_serial renewed/private_by_serial renewed/reqs_by_serial ;
+	do
+		[ -d "$EASYRSA_PKI/$i" ] || die "\
+Missing expected CA dir: $i (perhaps you need to run build-ca?)
+$help_note"
+	done
+
+	# explicitly return success for callers
+	return 0
+
+} # => verify_ca_init()
+
+# init-pki backend:
+init_pki() {
+
+	# If EASYRSA_PKI exists, confirm before we rm -rf (skiped with EASYRSA_BATCH)
+	if [ -e "$EASYRSA_PKI" ]; then
+		confirm "Confirm removal: " "yes" "
+WARNING!!!
+
+You are about to remove the EASYRSA_PKI at: $EASYRSA_PKI
+and initialize a fresh PKI here."
+		# now remove it:
+		rm -rf "$EASYRSA_PKI" || die "Removal of PKI dir failed. Check/correct errors above"
+	fi
+
+	# new dirs:
+	for i in private reqs; do
+		mkdir -p "$EASYRSA_PKI/$i" || die "Failed to create PKI file structure (permissions?)"
+	done
+	
+	if [ ! -f "$EASYRSA_SSL_CONF" ] && [ -f "$EASYRSA/openssl-easyrsa.cnf" ];
+	then
+		cp "$EASYRSA/openssl-easyrsa.cnf" "$EASYRSA_SSL_CONF"
+	fi
+
+	notice "\
+init-pki complete; you may now create a CA or requests.
+Your newly created PKI dir is: $EASYRSA_PKI
+"
+	return 0
+} # => init_pki()
+
+hide_read_pass()
+{
+	(stty -echo 2>/dev/null) || set +o echo
+	read -r "$@"
+	(stty echo 2>/dev/null) || set -o echo
+} # => hide_read_pass()
+
+# build-ca backend:
+build_ca() {
+	opts=""
+	sub_ca=""
+	nopass=""
+	crypto="-aes256"
+	crypto_opts=""
+	while [ -n "$1" ]; do
+		case "$1" in
+			subca) sub_ca=1 ;;
+			nopass) nopass=1 ;;
+			*) warn "Ignoring unknown command option: '$1'" ;;
+		esac
+		shift
+	done
+
+	verify_pki_init
+	[ "$EASYRSA_ALGO" = "ec" ] && verify_curve
+
+	# setup for the simpler sub-CA situation and overwrite with root-CA if needed:
+	out_file="$EASYRSA_PKI/reqs/ca.req"
+	out_key="$EASYRSA_PKI/private/ca.key"
+	if [ ! $sub_ca ]; then
+		out_file="$EASYRSA_PKI/ca.crt"
+		opts="$opts -x509 -days $EASYRSA_CA_EXPIRE "
+	fi
+
+	# Test for existing CA, and complain if already present
+	if verify_ca_init test; then
+		die "\
+Unable to create a CA as you already seem to have one set up.
+If you intended to start a new CA, run init-pki first."
+	fi
+	# If a private key exists here, a sub-ca was created but not signed.
+	# Notify the user and require a signed ca.crt or a init-pki:
+	[ -f "$out_key" ] && \
+		die "\
+A CA private key exists but no ca.crt is found in your PKI dir of:
+$EASYRSA_PKI
+Refusing to create a new CA keypair as this operation would overwrite your
+current CA keypair. If you intended to start a new CA, run init-pki first."
+
+	# create necessary files and dirs:
+	err_file="Unable to create necessary PKI files (permissions?)"
+	for i in issued certs_by_serial \
+		 revoked/certs_by_serial revoked/private_by_serial revoked/reqs_by_serial \
+		 renewed/certs_by_serial renewed/private_by_serial renewed/reqs_by_serial;
+	do
+		mkdir -p "$EASYRSA_PKI/$i" || die "$err_file"
+	done
+	printf "" > "$EASYRSA_PKI/index.txt" || die "$err_file"
+	print "01" > "$EASYRSA_PKI/serial" || die "$err_file"
+
+	# Default CN only when not in global EASYRSA_BATCH mode:
+	# shellcheck disable=SC2015
+	[ "$EASYRSA_BATCH" ] && opts="$opts -batch" || export EASYRSA_REQ_CN="Easy-RSA CA"
+
+	out_key_tmp="$(mktemp "$out_key.XXXXXXXXXX")"; EASYRSA_TEMP_FILE_2="$out_key_tmp"
+	out_file_tmp="$(mktemp "$out_file.XXXXXXXXXX")"; EASYRSA_TEMP_FILE_3="$out_file_tmp"
+	# Get password from user if necessary
+	if [ ! $nopass ]; then
+		out_key_pass_tmp="$(mktemp)"; EASYRSA_TEMP_FILE_4="$out_key_pass_tmp"
+		echo
+		printf "Enter New CA Key Passphrase: "
+		hide_read_pass kpass
+		echo
+		printf "Re-Enter New CA Key Passphrase: "
+		hide_read_pass kpass2
+		echo
+		# shellcheck disable=2154
+		if [ "$kpass" = "$kpass2" ];
+		then
+			printf "%s" "$kpass" > "$out_key_pass_tmp"
+		else
+			die "Passphrases do not match."
+		fi
+	fi
+
+	# create the CA key using AES256
+	[ ! $nopass ] && crypto_opts="$crypto -passout file:$out_key_pass_tmp"
+	if [ "$EASYRSA_ALGO" = "rsa" ]; then
+		#shellcheck disable=SC2086
+		"$EASYRSA_OPENSSL" genrsa -out "$out_key_tmp" $crypto_opts "$EASYRSA_ALGO_PARAMS"
+	elif [ "$EASYRSA_ALGO" = "ec" ]; then
+		#shellcheck disable=SC2086
+		"$EASYRSA_OPENSSL" ecparam -in "$EASYRSA_ALGO_PARAMS" -genkey | \
+			"$EASYRSA_OPENSSL" ec -out "$out_key_tmp" $crypto_opts
+	fi
+
+	# make safessl-easyrsa.cnf
+	make_ssl_config
+
+	# create the CA keypair:
+	[ ! $nopass ] && crypto_opts="-passin file:$out_key_pass_tmp"
+	#shellcheck disable=SC2086
+	"$EASYRSA_OPENSSL" req -utf8 -new -key "$out_key_tmp" \
+		-config "$EASYRSA_SAFE_CONF" -keyout "$out_key_tmp" -out "$out_file_tmp" $crypto_opts $opts || \
+		die "Failed to build the CA"
+
+	mv "$out_key_tmp" "$out_key"; EASYRSA_TEMP_FILE_2=
+	mv "$out_file_tmp" "$out_file"; EASYRSA_TEMP_FILE_3=
+	[ -f "$out_key_pass_tmp" ] && rm "$out_key_pass_tmp" && EASYRSA_TEMP_FILE_4=
+
+	# Success messages
+	if [ $sub_ca ]; then
+		notice "\
+NOTE: Your sub-CA request is at $out_file
+and now must be sent to your parent CA for signing. Place your resulting cert
+at $EASYRSA_PKI/ca.crt prior to signing operations.
+"
+	else	notice "\
+CA creation complete and you may now import and sign cert requests.
+Your new CA certificate file for publishing is at:
+$out_file
+"
+	fi
+	return 0
+} # => build_ca()
+
+# gen-dh backend:
+gen_dh() {
+	verify_pki_init
+
+	out_file="$EASYRSA_PKI/dh.pem"
+	"$EASYRSA_OPENSSL" dhparam -out "$out_file" "$EASYRSA_KEY_SIZE" || \
+		die "Failed to build DH params"
+	notice "\
+DH parameters of size $EASYRSA_KEY_SIZE created at $out_file
+"
+	return 0
+} # => gen_dh()
+
+# gen-req backend:
+gen_req() {
+	# pull filename base and use as default interactive CommonName:
+	[ -n "$1" ] || die "\
+Error: gen-req must have a file base as the first argument.
+Run easyrsa without commands for usage and commands."
+	key_out="$EASYRSA_PKI/private/$1.key"
+	req_out="$EASYRSA_PKI/reqs/$1.req"
+	[ ! "$EASYRSA_BATCH" ] && EASYRSA_REQ_CN="$1"
+	shift
+
+	# function opts support
+	opts=
+	while [ -n "$1" ]; do
+		case "$1" in
+			nopass) opts="$opts -nodes" ;;
+			# batch flag supports internal callers needing silent operation
+			batch) EASYRSA_BATCH=1 ;;
+			*) warn "Ignoring unknown command option: '$1'" ;;
+		esac
+		shift
+	done
+
+	verify_pki_init
+	[ "$EASYRSA_ALGO" = "ec" ] && verify_curve
+
+	# don't wipe out an existing private key without confirmation
+	[ -f "$key_out" ] && confirm "Confirm key overwrite: " "yes" "\
+
+WARNING!!!
+
+An existing private key was found at $key_out
+Continuing with key generation will replace this key."
+
+	# When EASYRSA_EXTRA_EXTS is defined, append it to openssl's [req] section:
+	if [ -n "$EASYRSA_EXTRA_EXTS" ]; then
+		# Setup & insert the extra ext data keyed by a magic line
+		extra_exts="
+req_extensions = req_extra
+[ req_extra ]
+$EASYRSA_EXTRA_EXTS"
+		#shellcheck disable=SC2016
+		awkscript='
+{if ( match($0, "^#%EXTRA_EXTS%") )
+	{ while ( getline<"/dev/stdin" ) {print} next }
+ {print}
+}'
+		print "$extra_exts" | \
+			awk "$awkscript" "$EASYRSA_SSL_CONF" \
+			> "$EASYRSA_TEMP_CONF" \
+			|| die "Copying SSL config to temp file failed"
+		# Use this new SSL config for the rest of this function
+		EASYRSA_SSL_CONF="$EASYRSA_TEMP_CONF"
+	fi
+
+	# make safessl-easyrsa.cnf
+	make_ssl_config
+
+	key_out_tmp="$(mktemp "$key_out.XXXXXXXXXX")"; EASYRSA_TEMP_FILE_2="$key_out_tmp"
+	req_out_tmp="$(mktemp "$req_out.XXXXXXXXXX")"; EASYRSA_TEMP_FILE_3="$req_out_tmp"
+	# generate request
+	[ $EASYRSA_BATCH ] && opts="$opts -batch"
+	# shellcheck disable=2086,2148
+	"$EASYRSA_OPENSSL" req -utf8 -new -newkey "$EASYRSA_ALGO":"$EASYRSA_ALGO_PARAMS" \
+		-config "$EASYRSA_SAFE_CONF" -keyout "$key_out_tmp" -out "$req_out_tmp" $opts \
+		|| die "Failed to generate request"
+	mv "$key_out_tmp" "$key_out"; EASYRSA_TEMP_FILE_2=
+	mv "$req_out_tmp" "$req_out"; EASYRSA_TEMP_FILE_3=
+	notice "\
+Keypair and certificate request completed. Your files are:
+req: $req_out
+key: $key_out
+"
+	return 0
+} # => gen_req()
+
+# common signing backend
+sign_req() {
+	crt_type="$1"
+	opts=""
+	req_in="$EASYRSA_PKI/reqs/$2.req"
+	crt_out="$EASYRSA_PKI/issued/$2.crt"
+
+	# Randomize Serial number
+	i=""
+	serial=""
+	check_serial=""
+	for i in 1 2 3 4 5; do
+		"$EASYRSA_OPENSSL" rand -hex -out "$EASYRSA_PKI/serial" 16
+		serial="$(cat "$EASYRSA_PKI/serial")"
+		check_serial="$("$EASYRSA_OPENSSL" ca -config "$EASYRSA_SSL_CONF" -status "$serial" 2>&1)"
+		case "$check_serial" in
+			*"not present in db"*) break ;;
+			*) continue ;;
+		esac
+	done
+
+	# Support batch by internal caller:
+	[ "$3" = "batch" ] && EASYRSA_BATCH=1
+
+	verify_ca_init
+
+	# Check argument sanity:
+	[ -n "$2" ] || die "\
+Incorrect number of arguments provided to sign-req:
+expected 2, got $# (see command help for usage)"
+
+	# Cert type must exist under the EASYRSA_EXT_DIR
+	[ -r "$EASYRSA_EXT_DIR/$crt_type" ] || die "\
+Unknown cert type '$crt_type'"
+
+	# Request file must exist
+	[ -f "$req_in" ] || die "\
+No request found for the input: '$2'
+Expected to find the request at: $req_in"
+
+	# Confirm input is a cert req
+	verify_file req "$req_in" || die "\
+The certificate request file is not in a valid X509 request format.
+Offending file: $req_in"
+
+	# Display the request subject in an easy-to-read format
+	# Confirm the user wishes to sign this request
+	confirm "Confirm request details: " "yes" "
+You are about to sign the following certificate.
+Please check over the details shown below for accuracy. Note that this request
+has not been cryptographically verified. Please be sure it came from a trusted
+source or that you have verified the request checksum with the sender.
+
+Request subject, to be signed as a $crt_type certificate for $EASYRSA_CERT_EXPIRE days:
+
+$(display_dn req "$req_in")
+"	# => confirm end
+
+	# Generate the extensions file for this cert:
+	{
+		# Append first any COMMON file (if present) then the cert-type extensions
+		cat "$EASYRSA_EXT_DIR/COMMON"
+		cat "$EASYRSA_EXT_DIR/$crt_type"
+		# copy req extensions
+		[ "$EASYRSA_CP_EXT" ] && print "copy_extensions = copy"
+
+		# Support a dynamic CA path length when present:
+		[ "$crt_type" = "ca" ] && [ -n "$EASYRSA_SUBCA_LEN" ] && \
+			print "basicConstraints = CA:TRUE, pathlen:$EASYRSA_SUBCA_LEN"
+
+		# Deprecated Netscape extension support, if enabled
+		if print "$EASYRSA_NS_SUPPORT" | awk_yesno; then
+			[ -n "$EASYRSA_NS_COMMENT" ] && \
+				print "nsComment = \"$EASYRSA_NS_COMMENT\""
+			case "$crt_type" in
+				serverClient)	print "nsCertType = serverClient" ;;
+				server)		print "nsCertType = server" ;;
+				client)		print "nsCertType = client" ;;
+				ca)		print "nsCertType = sslCA" ;;
+			esac
+		fi
+
+		# If type is server and no subjectAltName was requested,
+		# add one to the extensions file
+		if [ "$crt_type" = 'server' ];
+		then
+			echo "$EASYRSA_EXTRA_EXTS" |
+				grep -q subjectAltName ||
+				default_server_san "$req_in"
+		fi
+
+		# Add any advanced extensions supplied by env-var:
+		[ -n "$EASYRSA_EXTRA_EXTS" ] && print "$EASYRSA_EXTRA_EXTS"
+
+		: # needed to keep die from inherting the above test
+	} > "$EASYRSA_TEMP_EXT" || die "\
+Failed to create temp extension file (bad permissions?) at:
+$EASYRSA_TEMP_EXT"
+
+	# make safessl-easyrsa.cnf
+	make_ssl_config
+
+	# sign request
+	# shellcheck disable=SC2086
+	crt_out_tmp="$(mktemp "$crt_out.XXXXXXXXXX")"; EASYRSA_TEMP_FILE_2="$crt_out_tmp"
+	"$EASYRSA_OPENSSL" ca -utf8 -in "$req_in" -out "$crt_out_tmp" -config "$EASYRSA_SAFE_CONF" \
+		-extfile "$EASYRSA_TEMP_EXT" -days "$EASYRSA_CERT_EXPIRE" -batch $opts \
+		|| die "signing failed (openssl output above may have more detail)"
+	mv "$crt_out_tmp" "$crt_out"; EASYRSA_TEMP_FILE_2=
+	notice "\
+Certificate created at: $crt_out
+"
+	return 0
+} # => sign_req()
+
+# common build backend
+# used to generate+sign in 1 step
+build_full() {
+	verify_ca_init
+
+	# pull filename base:
+	[ -n "$2" ] || die "\
+Error: didn't find a file base name as the first argument.
+Run easyrsa without commands for usage and commands."
+	crt_type="$1" name="$2"
+	req_out="$EASYRSA_PKI/reqs/$2.req"
+	key_out="$EASYRSA_PKI/private/$2.key"
+	crt_out="$EASYRSA_PKI/issued/$2.crt"
+	shift 2
+
+	# function opts support
+	req_opts=
+	while [ -n "$1" ]; do
+		case "$1" in
+			nopass) req_opts="$req_opts nopass" ;;
+			*) warn "Ignoring unknown command option: '$1'" ;;
+		esac
+		shift
+	done
+
+	# abort on existing req/key/crt files
+	err_exists="\
+file already exists. Aborting build to avoid overwriting this file.
+If you wish to continue, please use a different name or remove the file.
+Matching file found at: "
+	[ -f "$req_out" ] && die "Request $err_exists $req_out"
+	[ -f "$key_out" ] && die "Key $err_exists $key_out"
+	[ -f "$crt_out" ] && die "Certificate $err_exists $crt_out"
+
+	# create request
+	EASYRSA_REQ_CN="$name"
+	#shellcheck disable=SC2086
+	gen_req "$name" batch $req_opts
+
+	# Sign it
+	sign_req "$crt_type" "$name" batch
+
+} # => build_full()
+
+# revoke backend
+revoke() {
+	verify_ca_init
+
+	# pull filename base:
+	[ -n "$1" ] || die "\
+Error: didn't find a file base name as the first argument.
+Run easyrsa without commands for usage and command help."
+	crt_in="$EASYRSA_PKI/issued/$1.crt"
+
+	opts=""
+	if [ "$2" ]; then
+		opts="$opts -crl_reason $2"
+	fi
+	
+	verify_file x509 "$crt_in" || die "\
+Unable to revoke as the input file is not a valid certificate. Unexpected
+input in file: $crt_in"
+
+	# confirm operation by displaying DN:
+	confirm "Continue with revocation: " "yes" "
+Please confirm you wish to revoke the certificate with the following subject:
+
+$(display_dn x509 "$crt_in")
+"	# => confirm end
+
+	# referenced cert must exist:
+	[ -f "$crt_in" ] || die "\
+Unable to revoke as no certificate was found. Certificate was expected
+at: $crt_in"
+
+	# make safessl-easyrsa.cnf
+	make_ssl_config
+
+	# shellcheck disable=SC2086
+	"$EASYRSA_OPENSSL" ca -utf8 -revoke "$crt_in" -config "$EASYRSA_SAFE_CONF" $opts || die "\
+Failed to revoke certificate: revocation command failed."
+
+	# move revoked files so we can reissue certificates with the same name
+	move_revoked "$1"
+
+	notice "\
+IMPORTANT!!!
+
+Revocation was successful. You must run gen-crl and upload a CRL to your
+infrastructure in order to prevent the revoked cert from being accepted.
+"	# => notice end
+	return 0
+} #= revoke()
+
+# move-revoked
+# moves revoked certificates to an alternative folder
+# allows reissuing certificates with the same name
+move_revoked() {
+	verify_ca_init
+
+	[ -n "$1" ] || die "\
+Error: didn't find a file base name as the first argument.
+Run easyrsa without commands for usage and command help."
+
+	crt_in="$EASYRSA_PKI/issued/$1.crt"
+	key_in="$EASYRSA_PKI/private/$1.key"
+	req_in="$EASYRSA_PKI/reqs/$1.req"
+
+	verify_file x509 "$crt_in" || die "\
+Unable to move revoked input file. The file is not a valid certificate. Unexpected
+input in file: $crt_in"
+
+	verify_file req "$req_in" || die "\
+Unable to move request. The file is not a valid request. Unexpected
+input in file: $req_in"
+
+	# get the serial number of the certificate -> serial=XXXX
+	cert_serial="$("$EASYRSA_OPENSSL" x509 -in "$crt_in" -noout -serial)"
+	# remove the serial= part -> we only need the XXXX part
+	cert_serial=${cert_serial##*=}
+
+	crt_by_serial="$EASYRSA_PKI/certs_by_serial/$cert_serial.pem"
+	crt_by_serial_revoked="$EASYRSA_PKI/revoked/certs_by_serial/$cert_serial.crt"
+	key_by_serial_revoked="$EASYRSA_PKI/revoked/private_by_serial/$cert_serial.key"
+	req_by_serial_revoked="$EASYRSA_PKI/revoked/reqs_by_serial/$cert_serial.req"
+
+
+	# move crt, key and req file to revoked folders
+	mv "$crt_in" "$crt_by_serial_revoked"
+	mv "$req_in" "$req_by_serial_revoked"
+
+	# only move the key if we have it
+	if [ -e "$key_in" ]
+	then
+		mv "$key_in" "$key_by_serial_revoked"
+	fi
+
+	# move the rest of the files (p12, p7, ...)
+	# shellcheck disable=SC2231
+	for file in $EASYRSA_PKI/private/$1\.???
+	do
+		# get file extension
+		file_ext="${file##*.}"
+
+		[ -f "$file" ] && mv "$file" "$EASYRSA_PKI/revoked/private_by_serial/$cert_serial.$file_ext"
+	done
+
+	# remove the dublicate certificate in the certs_by_serial folder
+	rm "$crt_by_serial"
+
+	return 0
+
+} #= move_revoked()
+
+# renew backend
+renew() {
+	verify_ca_init
+
+	# pull filename base:
+	[ -n "$1" ] || die "\
+Error: didn't find a file base name as the first argument.
+Run easyrsa without commands for usage and command help."
+	crt_in="$EASYRSA_PKI/issued/$1.crt"
+
+	opts=""
+	if [ "$2" ]; then
+		opts="$2"
+	fi
+
+	verify_file x509 "$crt_in" || die "\
+Unable to renew as the input file is not a valid certificate. Unexpected
+input in file: $crt_in"
+
+	# confirm operation by displaying DN:
+	confirm "Continue with renew: " "yes" "
+Please confirm you wish to renew the certificate with the following subject:
+
+$(display_dn x509 "$crt_in")
+"	# => confirm end
+
+	# referenced cert must exist:
+	[ -f "$crt_in" ] || die "\
+Unable to renew as no certificate was found. Certificate was expected
+at: $crt_in"
+
+	# make safessl-easyrsa.cnf
+	make_ssl_config
+
+	# Check if old cert is expired or expires within 30 days
+	expire_date=$(
+		"$EASYRSA_OPENSSL" x509 -in "$crt_in" -noout -enddate |
+		sed 's/^notAfter=//'
+		)
+        case $(uname) in
+        	"Darwin"|*"BSD")
+                	expire_date=$(date -j -f '%b %d %T %Y %Z' "$expire_date" +%s)
+                	allow_renew_date=$(date -j -v"+${EASYRSA_CERT_RENEW}d" +%s)
+                	;;
+          	*)
+                	# This works on Windows, too, since uname doesn't exist and this is catch-all
+                	expire_date=$(date -d "$expire_date" +%s)
+                	allow_renew_date=$(date -d "+${EASYRSA_CERT_RENEW}day" +%s)
+                	;;
+        esac
+
+	[ "$expire_date" -lt "$allow_renew_date" ] || die "\
+Certificate expires in more than $EASYRSA_CERT_RENEW days.
+Renewal not allowed."
+
+	# Extract certificate usage from old cert
+	cert_ext_key_usage=$(
+		"$EASYRSA_OPENSSL" x509 -in "$crt_in" -noout -text |
+		sed -n "/X509v3 Extended Key Usage:/{n;s/^ *//g;p;}"
+		)
+	case $cert_ext_key_usage in
+		"TLS Web Client Authentication")
+			cert_type=client
+			;;
+		"TLS Web Server Authentication")
+			cert_type=server
+			;;
+		"TLS Web Server Authentication, TLS Web Client Authentication")
+			cert_type=serverClient
+			;;
+	esac
+
+	# Use SAN from --subject-alt-name if set else use SAN from old cert
+	echo "$EASYRSA_EXTRA_EXTS" | grep -q subjectAltName || \
+	{
+		san=$(
+			"$EASYRSA_OPENSSL" x509 -in "$crt_in" -noout -text |
+			sed -n "/X509v3 Subject Alternative Name:/{n;s/ //g;p;}"
+			)
+		[ -n "$san" ] && export EASYRSA_EXTRA_EXTS="\
+$EASYRSA_EXTRA_EXTS
+subjectAltName = $san"
+	}
+
+	# move renewed files so we can reissue certificate with the same name
+	# FIXME: Modify revoke() to also work on the renewed certs subdir
+	move_renewed "$1"
+
+	# renew certificate
+	# shellcheck disable=SC2086
+	build_full $cert_type $1 $opts || die "\
+Failed to renew certificate: renew command failed."
+
+	notice "\
+IMPORTANT!!!
+
+Renew was successful.
+You may want to revoke the old certificate once the new one has been deployed.
+"	# => notice end
+	return 0
+} #= renew()
+
+# move-renewed
+# moves renewed certificates to an alternative folder
+# allows reissuing certificates with the same name
+move_renewed() {
+	verify_ca_init
+
+	[ -n "$1" ] || die "\
+Error: didn't find a file base name as the first argument.
+Run easyrsa without commands for usage and command help."
+
+	crt_in="$EASYRSA_PKI/issued/$1.crt"
+	key_in="$EASYRSA_PKI/private/$1.key"
+	req_in="$EASYRSA_PKI/reqs/$1.req"
+
+	verify_file x509 "$crt_in" || die "\
+Unable to move renewed input file. The file is not a valid certificate. Unexpected
+input in file: $crt_in"
+
+	verify_file req "$req_in" || die "\
+Unable to move request. The file is not a valid request. Unexpected
+input in file: $req_in"
+
+	# get the serial number of the certificate -> serial=XXXX
+	cert_serial="$("$EASYRSA_OPENSSL" x509 -in "$crt_in" -noout -serial)"
+	# remove the serial= part -> we only need the XXXX part
+	cert_serial=${cert_serial##*=}
+
+	crt_by_serial="$EASYRSA_PKI/certs_by_serial/$cert_serial.pem"
+	crt_by_serial_renewed="$EASYRSA_PKI/renewed/certs_by_serial/$cert_serial.crt"
+	key_by_serial_renewed="$EASYRSA_PKI/renewed/private_by_serial/$cert_serial.key"
+	req_by_serial_renewed="$EASYRSA_PKI/renewed/reqs_by_serial/$cert_serial.req"
+
+
+	# move crt, key and req file to renewed folders
+	mv "$crt_in" "$crt_by_serial_renewed"
+	mv "$req_in" "$req_by_serial_renewed"
+
+	# only move the key if we have it
+	if [ -e "$key_in" ]
+	then
+		mv "$key_in" "$key_by_serial_renewed"
+	fi
+
+	# move the rest of the files (p12, p7, ...)
+	# shellcheck disable=SC2231
+	for file in $EASYRSA_PKI/private/$1\.???
+	do
+		# get file extension
+		file_ext="${file##*.}"
+
+		[ -f "$file" ] && mv "$file" "$EASYRSA_PKI/renewed/private_by_serial/$cert_serial.$file_ext"
+	done
+
+	# remove the duplicate certificate in the certs_by_serial folder
+	rm "$crt_by_serial"
+
+	return 0
+
+} #= move_renewed()
+
+# gen-crl backend
+gen_crl() {
+	verify_ca_init
+
+	# make safessl-easyrsa.cnf
+	make_ssl_config
+
+	out_file="$EASYRSA_PKI/crl.pem"
+	out_file_tmp="$(mktemp "$out_file.XXXXXXXXXX")"; EASYRSA_TEMP_FILE_2="$out_file_tmp"
+	"$EASYRSA_OPENSSL" ca -utf8 -gencrl -out "$out_file_tmp" -config "$EASYRSA_SAFE_CONF" || die "\
+CRL Generation failed.
+"
+	mv "$out_file_tmp" "$out_file"; EASYRSA_TEMP_FILE_2=
+
+	notice "\
+An updated CRL has been created.
+CRL file: $out_file
+"
+	return 0
+} # => gen_crl()
+
+# import-req backend
+import_req() {
+	verify_pki_init
+
+	# pull passed paths
+	in_req="$1" short_name="$2"
+	out_req="$EASYRSA_PKI/reqs/$2.req" 
+
+	[ -n "$short_name" ] || die "\
+Unable to import: incorrect command syntax.
+Run easyrsa without commands for usage and command help."
+
+	verify_file req "$in_req" || die "\
+The input file does not appear to be a certificate request. Aborting import.
+Offending file: $in_req"
+
+	# destination must not exist
+	[ -f "$out_req" ] && die "\
+Unable to import the request as the destination file already exists.
+Please choose a different name for your imported request file.
+Existing file at: $out_req"
+	
+	# now import it
+	cp "$in_req" "$out_req"
+
+	notice "\
+The request has been successfully imported with a short name of: $short_name
+You may now use this name to perform signing operations on this request.
+"
+	return 0
+} # => import_req()
+
+# export pkcs#12 or pkcs#7
+export_pkcs() {
+	pkcs_type="$1"
+	shift
+
+	[ -n "$1" ] || die "\
+Unable to export p12: incorrect command syntax.
+Run easyrsa without commands for usage and command help."
+
+	short_name="$1"
+	crt_in="$EASYRSA_PKI/issued/$1.crt"
+	key_in="$EASYRSA_PKI/private/$1.key"
+	crt_ca="$EASYRSA_PKI/ca.crt"
+	shift
+
+	verify_pki_init
+
+	# opts support
+	want_ca=1
+	want_key=1
+	while [ -n "$1" ]; do
+		case "$1" in
+			noca) want_ca="" ;;
+			nokey) want_key="" ;;
+			*) warn "Ignoring unknown command option: '$1'" ;;
+		esac
+		shift
+	done
+
+	pkcs_opts=
+	if [ $want_ca ]; then
+		verify_file x509 "$crt_ca" || die "\
+Unable to include CA cert in the $pkcs_type output (missing file, or use noca option.)
+Missing file expected at: $crt_ca"
+		pkcs_opts="$pkcs_opts -certfile $crt_ca"
+	fi
+
+	# input files must exist
+	verify_file x509 "$crt_in" || die "\
+Unable to export $pkcs_type for short name '$short_name' without the certificate.
+Missing cert expected at: $crt_in"
+
+	case "$pkcs_type" in
+	p12)
+		pkcs_out="$EASYRSA_PKI/private/$short_name.p12"
+
+		if [ $want_key ]; then
+			[ -f "$key_in" ] || die "\
+Unable to export p12 for short name '$short_name' without the key
+(if you want a p12 without the private key, use nokey option.)
+Missing key expected at: $key_in"
+		else
+			pkcs_opts="$pkcs_opts -nokeys"
+		fi
+
+		# export the p12:
+		# shellcheck disable=SC2086
+		"$EASYRSA_OPENSSL" pkcs12 -in "$crt_in" -inkey "$key_in" -export \
+			-out "$pkcs_out" $pkcs_opts || die "\
+Export of p12 failed: see above for related openssl errors."
+	;;
+	p7)
+		pkcs_out="$EASYRSA_PKI/issued/$short_name.p7b"
+
+		# export the p7:
+		# shellcheck disable=SC2086
+		"$EASYRSA_OPENSSL" crl2pkcs7 -nocrl -certfile "$crt_in" \
+			-out "$pkcs_out" $pkcs_opts || die "\
+Export of p7 failed: see above for related openssl errors."
+	;;
+esac
+
+	notice "\
+Successful export of $pkcs_type file. Your exported file is at the following
+location: $pkcs_out
+"
+	return 0
+} # => export_pkcs()
+
+# set-pass backend
+set_pass() {
+	verify_pki_init
+
+	# key type, supplied internally from frontend command call (rsa/ec)
+	key_type="$1"
+
+	# values supplied by the user:
+	raw_file="$2"
+	file="$EASYRSA_PKI/private/$raw_file.key"
+	[ -n "$raw_file" ] || die "\
+Missing argument to 'set-$key_type-pass' command: no name/file supplied.
+See help output for usage details."
+
+	# parse command options
+	shift 2
+	crypto="-aes256"
+	while [ -n "$1" ]; do
+		case "$1" in
+			nopass)	crypto="" ;;
+			file)	file="$raw_file" ;;
+			*)	warn "Ignoring unknown command option: '$1'" ;;
+		esac
+		shift
+	done
+
+	[ -f "$file" ] || die "\
+Missing private key: expected to find the private key component at:
+$file"
+
+	notice "\
+If the key is currently encrypted you must supply the decryption passphrase.
+${crypto:+You will then enter a new PEM passphrase for this key.$NL}"
+
+	EASYRSA_TEMP_FILE_2="$file.temp"
+
+	"$EASYRSA_OPENSSL" "$key_type" -in "$file" -out "$EASYRSA_TEMP_FILE_2" $crypto || die "\
+Failed to change the private key passphrase. See above for possible openssl
+error messages."
+
+	mv "$EASYRSA_TEMP_FILE_2" "$file" || die "\
+Failed to change the private key passphrase. See above for error messages."
+
+	notice "Key passphrase successfully changed"
+	
+} # => set_pass()
+
+# update-db backend
+update_db() {
+	verify_ca_init
+
+	"$EASYRSA_OPENSSL" ca -utf8 -updatedb -config "$EASYRSA_SSL_CONF" || die "\
+Failed to perform update-db: see above for related openssl errors."
+	return 0
+} # => update_db()
+
+# display cert DN info on a req/X509, passed by full pathname
+display_dn() {
+	format="$1" path="$2"
+	print "$("$EASYRSA_OPENSSL" "$format" -in "$path" -noout -subject -nameopt multiline)"
+} # => display_dn()
+
+# generate default SAN from req/X509, passed by full pathname
+default_server_san() {
+	path="$1"
+	cn=$(
+		"$EASYRSA_OPENSSL" req -in "$path" -noout -subject -nameopt sep_multiline |
+		awk -F'=' '/^  *CN=/{print $2}'
+		)
+	echo "$cn" | grep -E -q '^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$'
+	#shellcheck disable=SC2181
+	if [ $? -eq 0 ]; then
+		print "subjectAltName = IP:$cn"
+	else
+		print "subjectAltName = DNS:$cn"
+	fi
+} # => default_server_san()
+
+# verify a file seems to be a valid req/X509
+verify_file() {
+	format="$1" 
+	path="$2"
+	"$EASYRSA_OPENSSL" "$format" -in "$path" -noout 2>/dev/null || return 1
+	return 0
+} # => verify_file()
+
+# show-* command backend
+# Prints req/cert details in a readable format
+show() {
+	type="$1" 
+	name="$2" 
+	in_file=""
+	format=""
+	[ -n "$name" ] || die "\
+Missing expected filename_base argument.
+Run easyrsa without commands for usage help."
+	shift 2
+
+	# opts support
+	opts="-${type}opt no_pubkey,no_sigdump"
+	while [ -n "$1" ]; do
+		case "$1" in
+			full) 
+				opts=""
+				;;
+			*) 
+				warn "Ignoring unknown command option: '$1'" 
+				;;
+		esac
+		shift
+	done
+
+	# Determine cert/req type
+	if [ "$type" = "cert" ]; then
+		verify_ca_init
+		in_file="$EASYRSA_PKI/issued/${name}.crt"
+		format="x509"
+	else
+		verify_pki_init
+		in_file="$EASYRSA_PKI/reqs/${name}.req"
+		format="req"
+	fi
+
+	# Verify file exists and is of the correct type
+	[ -f "$in_file" ] || die "\
+No such $type file with a basename of '$name' is present.
+Expected to find this file at:
+$in_file"
+	verify_file $format "$in_file" || die "\
+This file is not a valid $type file:
+$in_file"
+
+	notice "\
+Showing $type details for '$name'.
+This file is stored at:
+$in_file
+"
+	"$EASYRSA_OPENSSL" $format -in "$in_file" -noout -text\
+		-nameopt multiline $opts || die "\
+OpenSSL failure to process the input"
+} # => show()
+
+# show-ca command backend
+# Prints CA cert details in a readable format
+show_ca() {
+	# opts support
+	opts="-certopt no_pubkey,no_sigdump"
+	while [ -n "$1" ]; do
+		case "$1" in
+			full) opts= ;;
+			*) warn "Ignoring unknown command option: '$1'" ;;
+		esac
+		shift
+	done
+
+	verify_ca_init
+	in_file="$EASYRSA_PKI/ca.crt"
+	format="x509"
+
+	# Verify file exists and is of the correct type
+	[ -f "$in_file" ] || die "\
+No such $type file with a basename of '$name' is present.
+Expected to find this file at:
+$in_file"
+	verify_file $format "$in_file" || die "\
+This file is not a valid $type file:
+$in_file"
+
+	notice "\
+Showing $type details for 'ca'.
+This file is stored at:
+$in_file
+"
+	"$EASYRSA_OPENSSL" $format -in "$in_file" -noout -text\
+		-nameopt multiline $opts || die "\
+OpenSSL failure to process the input"
+} # => show_ca()
+
+# vars setup
+# Here sourcing of 'vars' if present occurs. If not present, defaults are used
+# to support running without a sourced config format
+vars_setup() {
+	# Try to locate a 'vars' file in order of location preference.
+	# If one is found, source it
+	vars=
+
+	# set up program path
+	prog_vars="${0%/*}/vars"
+	# set up PKI path
+	pki_vars="${EASYRSA_PKI:-$PWD/pki}/vars"
+
+	# command-line path:
+	if [ -f "$EASYRSA_VARS_FILE" ]; then
+		vars="$EASYRSA_VARS_FILE"
+	# PKI location, if present:
+	elif [ -f "$pki_vars" ]; then
+		vars="$pki_vars"
+	# EASYRSA, if defined:
+	elif [ -n "$EASYRSA" ] && [ -f "$EASYRSA/vars" ]; then
+		vars="$EASYRSA/vars"
+	# program location:
+	elif [ -f "$prog_vars" ]; then
+		vars="$prog_vars"
+	fi
+	
+	# If a vars file was located, source it
+	# If $EASYRSA_NO_VARS is defined (not blank) this is skipped
+	if [ -z "$EASYRSA_NO_VARS" ] && [ -n "$vars" ]; then
+		#shellcheck disable=SC2034
+		EASYRSA_CALLER=1 
+		# shellcheck disable=SC1090
+		. "$vars"
+		notice "\
+Note: using Easy-RSA configuration from: $vars"
+	fi
+	
+	# Set defaults, preferring existing env-vars if present
+	set_var EASYRSA		"${0%/*}"
+	set_var EASYRSA_OPENSSL	openssl
+	set_var EASYRSA_PKI	"$PWD/pki"
+	set_var EASYRSA_DN	cn_only
+	set_var EASYRSA_REQ_COUNTRY	"US"
+	set_var EASYRSA_REQ_PROVINCE	"California"
+	set_var EASYRSA_REQ_CITY	"San Francisco"
+	set_var EASYRSA_REQ_ORG		"Copyleft Certificate Co"
+	set_var EASYRSA_REQ_EMAIL	me@example.net
+	set_var EASYRSA_REQ_OU		"My Organizational Unit"
+	set_var EASYRSA_ALGO		rsa
+	set_var EASYRSA_KEY_SIZE	2048
+	set_var EASYRSA_CURVE		secp384r1
+	set_var EASYRSA_EC_DIR		"$EASYRSA_PKI/ecparams"
+	set_var EASYRSA_CA_EXPIRE	3650
+	set_var EASYRSA_CERT_EXPIRE	1080 # new default of 36 months	
+	set_var EASYRSA_CERT_RENEW	30
+	set_var EASYRSA_CRL_DAYS	180
+	set_var EASYRSA_NS_SUPPORT	no
+	set_var EASYRSA_NS_COMMENT	"Easy-RSA (v3.0.6) Generated Certificate"
+	set_var EASYRSA_TEMP_CONF	"$EASYRSA_PKI/openssl-easyrsa.temp"
+	set_var EASYRSA_TEMP_EXT	"$EASYRSA_PKI/extensions.temp"
+	set_var EASYRSA_TEMP_FILE_2	""
+	set_var EASYRSA_TEMP_FILE_3	""
+	set_var EASYRSA_REQ_CN		ChangeMe
+	set_var EASYRSA_DIGEST		sha256
+
+	set_var EASYRSA_SSL_CONF	"$EASYRSA_PKI/openssl-easyrsa.cnf"
+	set_var EASYRSA_SAFE_CONF	"$EASYRSA_PKI/safessl-easyrsa.cnf"
+
+	# Same as above for the x509-types extensions dir
+	if [ -d "$EASYRSA_PKI/x509-types" ]; then
+		set_var EASYRSA_EXT_DIR		"$EASYRSA_PKI/x509-types"
+	else	
+		#TODO: This should be removed.  Not really suitable for packaging.
+		set_var EASYRSA_EXT_DIR		"$EASYRSA/x509-types"
+	fi
+
+	# EASYRSA_ALGO_PARAMS must be set depending on selected algo
+	if [ "ec" = "$EASYRSA_ALGO" ]; then
+		EASYRSA_ALGO_PARAMS="$EASYRSA_EC_DIR/${EASYRSA_CURVE}.pem"
+	elif [ "rsa" = "$EASYRSA_ALGO" ]; then
+		EASYRSA_ALGO_PARAMS="${EASYRSA_KEY_SIZE}"
+	else
+		die "Alg '$EASYRSA_ALGO' is invalid: must be 'rsa' or 'ec'"
+	fi
+
+	# Setting OPENSSL_CONF prevents bogus warnings (especially useful on win32)
+	export OPENSSL_CONF="$EASYRSA_SAFE_CONF"
+} # vars_setup()
+
+# variable assignment by indirection when undefined; merely exports
+# the variable when it is already defined (even if currently null)
+# Sets $1 as the value contained in $2 and exports (may be blank)
+set_var() {
+	var=$1
+	shift
+	value="$*"
+	eval "export $var=\"\${$var-$value}\""
+} #=> set_var()
+
+########################################
+# Invocation entry point:
+
+NL='
+'
+
+# Be secure with a restrictive umask
+[ -z "$EASYRSA_NO_UMASK" ] && umask 077
+
+# Parse options
+while :; do
+	# Separate option from value:
+	opt="${1%%=*}"
+	val="${1#*=}"
+	empty_ok="" # Empty values are not allowed unless excepted
+
+	case "$opt" in
+	--days)
+		export EASYRSA_CERT_EXPIRE="$val"
+		export EASYRSA_CA_EXPIRE="$val"
+		export EASYRSA_CRL_DAYS="$val"
+		;;
+	--pki-dir)
+		export EASYRSA_PKI="$val" ;;
+	--use-algo)
+		export EASYRSA_ALGO="$val" ;;
+	--keysize)
+		export EASYRSA_KEY_SIZE="$val" ;;
+	--curve)
+		export EASYRSA_CURVE="$val" ;;
+	--dn-mode)
+		export EASYRSA_DN="$val" ;;
+	--req-cn)
+		export EASYRSA_REQ_CN="$val" ;;
+	--digest)
+		export EASYRSA_DIGEST="$val" ;;
+	--req-c)
+		empty_ok=1
+		export EASYRSA_REQ_COUNTRY="$val" ;;
+	--req-st)
+		empty_ok=1
+		export EASYRSA_REQ_PROVINCE="$val" ;;
+	--req-city)
+		empty_ok=1
+		export EASYRSA_REQ_CITY="$val" ;;
+	--req-org)
+		empty_ok=1
+		export EASYRSA_REQ_ORG="$val" ;;
+	--req-email)
+		empty_ok=1
+		export EASYRSA_REQ_EMAIL="$val" ;;
+	--req-ou)
+		empty_ok=1
+		export EASYRSA_REQ_OU="$val" ;;
+	--ns-cert)
+		export EASYRSA_NS_SUPPORT="$val" ;;
+	--ns-comment)
+		empty_ok=1
+		export EASYRSA_NS_COMMENT="$val" ;;
+	--batch)
+		empty_ok=1
+		export EASYRSA_BATCH=1 ;;
+	--subca-len)
+		export EASYRSA_SUBCA_LEN="$val" ;;
+	--vars)
+		export EASYRSA_VARS_FILE="$val" ;;
+	--copy-ext)
+		empty_ok=1
+		export EASYRSA_CP_EXT=1 ;;
+	--subject-alt-name)
+		export EASYRSA_EXTRA_EXTS="\
+$EASYRSA_EXTRA_EXTS
+subjectAltName = $val" ;;
+	*)
+		break ;;
+	esac
+
+	# fatal error when no value was provided
+	if [ ! $empty_ok ] && { [ "$val" = "$1" ] || [ -z "$val" ]; }; then
+		die "Missing value to option: $opt"
+	fi
+
+	shift
+done
+
+# Intelligent env-var detection and auto-loading:
+vars_setup
+
+# Register clean_temp and prog_exit on SIGHUP, SIGINT, SIGQUIT, and SIGABRT
+trap "clean_temp; prog_exit 1" 1 
+trap "clean_temp; prog_exit 2" 2
+trap "clean_temp; prog_exit 3" 3
+trap "clean_temp; prog_exit 6" 6
+trap "clean_temp; prog_exit 15" 15
+
+# determine how we were called, then hand off to the function responsible
+cmd="$1"
+[ -n "$1" ] && shift # scrape off command
+case "$cmd" in
+	init-pki|clean-all)
+		init_pki "$@"
+		;;
+	build-ca)
+		build_ca "$@"
+		;;
+	gen-dh)
+		gen_dh
+		;;
+	gen-req)
+		gen_req "$@"
+		;;
+	sign|sign-req)
+		sign_req "$@"
+		;;
+	build-client-full)
+		build_full client "$@"
+		;;
+	build-server-full)
+		build_full server "$@"
+		;;
+	build-serverClient-full)
+		build_full serverClient "$@"
+		;;
+	gen-crl)
+		gen_crl
+		;;
+	revoke)
+		revoke "$@"
+		;;
+	renew)
+		renew "$@"
+		;;
+	import-req)
+		import_req "$@"
+		;;
+	export-p12)
+		export_pkcs p12 "$@"
+		;;
+	export-p7)
+		export_pkcs p7 "$@"
+		;;
+	set-rsa-pass)
+		set_pass rsa "$@"
+		;;
+	set-ec-pass)
+		set_pass ec "$@"
+		;;
+	update-db)
+		update_db
+		;;
+	show-req)
+		show req "$@"
+		;;
+	show-cert)
+		show cert "$@"
+		;;
+	show-ca)
+		show_ca "$@"
+		;;
+	""|help|-h|--help|--usage)
+		cmd_help "$1"
+		exit 0
+		;;
+	*)
+		die "Unknown command '$cmd'. Run without commands for usage help."
+		;;
+esac
+
+# vim: ft=sh nu ai sw=8 ts=8 noet
diff --git a/roles/ca/files/easyrsa/gpl-2.0.txt b/roles/ca/files/easyrsa/gpl-2.0.txt
new file mode 100644
index 0000000000000000000000000000000000000000..1f963da0d1ca40ea60730c03befcbbc6771740c3
--- /dev/null
+++ b/roles/ca/files/easyrsa/gpl-2.0.txt
@@ -0,0 +1,340 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                            NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
+
diff --git a/roles/ca/files/easyrsa/mktemp.txt b/roles/ca/files/easyrsa/mktemp.txt
new file mode 100644
index 0000000000000000000000000000000000000000..4fe81eff37c9b4afa60d3bed39a7a204c50b0b9b
--- /dev/null
+++ b/roles/ca/files/easyrsa/mktemp.txt
@@ -0,0 +1,20 @@
+Mktemp is distributed under the following ISC-style license:
+
+   Copyright (c) 1996-1997, 2000-2001, 2008, 2010
+	Todd C. Miller <Todd.Miller@courtesan.com>
+   Copyright (c) 1996, David Mazieres <dm@uun.org>
+   Copyright (c) 2008, Damien Miller <djm@openbsd.org>
+
+   Permission to use, copy, modify, and distribute this software for any
+   purpose with or without fee is hereby granted, provided that the above
+   copyright notice and this permission notice appear in all copies.
+
+   THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+   WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+   MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+   ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+From https://www.mktemp.org/mktemp/license.html
diff --git a/roles/ca/files/easyrsa/openssl-easyrsa.cnf b/roles/ca/files/easyrsa/openssl-easyrsa.cnf
new file mode 100644
index 0000000000000000000000000000000000000000..11394147697414e4d5fac6329cfdc8210ab98d8a
--- /dev/null
+++ b/roles/ca/files/easyrsa/openssl-easyrsa.cnf
@@ -0,0 +1,140 @@
+# For use with Easy-RSA 3.1 and OpenSSL or LibreSSL
+
+RANDFILE		= $ENV::EASYRSA_PKI/.rnd
+
+####################################################################
+[ ca ]
+default_ca	= CA_default		# The default ca section
+
+####################################################################
+[ CA_default ]
+
+dir		= $ENV::EASYRSA_PKI	# Where everything is kept
+certs		= $dir			# Where the issued certs are kept
+crl_dir		= $dir			# Where the issued crl are kept
+database	= $dir/index.txt	# database index file.
+new_certs_dir	= $dir/certs_by_serial	# default place for new certs.
+
+certificate	= $dir/ca.crt	 	# The CA certificate
+serial		= $dir/serial 		# The current serial number
+crl		= $dir/crl.pem 		# The current CRL
+private_key	= $dir/private/ca.key	# The private key
+RANDFILE	= $dir/.rand		# private random number file
+
+x509_extensions	= basic_exts		# The extentions to add to the cert
+
+# This allows a V2 CRL. Ancient browsers don't like it, but anything Easy-RSA
+# is designed for will. In return, we get the Issuer attached to CRLs.
+crl_extensions	= crl_ext
+
+default_days	= $ENV::EASYRSA_CERT_EXPIRE	# how long to certify for
+default_crl_days= $ENV::EASYRSA_CRL_DAYS	# how long before next CRL
+default_md	= $ENV::EASYRSA_DIGEST		# use public key default MD
+preserve	= no			# keep passed DN ordering
+
+# This allows to renew certificates which have not been revoked
+unique_subject	= no
+
+# A few difference way of specifying how similar the request should look
+# For type CA, the listed attributes must be the same, and the optional
+# and supplied fields are just that :-)
+policy		= policy_anything
+
+# For the 'anything' policy, which defines allowed DN fields
+[ policy_anything ]
+countryName		= optional
+stateOrProvinceName	= optional
+localityName		= optional
+organizationName	= optional
+organizationalUnitName	= optional
+commonName		= supplied
+name			= optional
+emailAddress		= optional
+
+####################################################################
+# Easy-RSA request handling
+# We key off $DN_MODE to determine how to format the DN
+[ req ]
+default_bits		= $ENV::EASYRSA_KEY_SIZE
+default_keyfile 	= privkey.pem
+default_md		= $ENV::EASYRSA_DIGEST
+distinguished_name	= $ENV::EASYRSA_DN
+x509_extensions		= easyrsa_ca	# The extentions to add to the self signed cert
+
+# A placeholder to handle the $EXTRA_EXTS feature:
+#%EXTRA_EXTS%	# Do NOT remove or change this line as $EXTRA_EXTS support requires it
+
+####################################################################
+# Easy-RSA DN (Subject) handling
+
+# Easy-RSA DN for cn_only support:
+[ cn_only ]
+commonName		= Common Name (eg: your user, host, or server name)
+commonName_max		= 64
+commonName_default	= $ENV::EASYRSA_REQ_CN
+
+# Easy-RSA DN for org support:
+[ org ]
+countryName			= Country Name (2 letter code)
+countryName_default		= $ENV::EASYRSA_REQ_COUNTRY
+countryName_min			= 2
+countryName_max			= 2
+
+stateOrProvinceName		= State or Province Name (full name)
+stateOrProvinceName_default	= $ENV::EASYRSA_REQ_PROVINCE
+
+localityName			= Locality Name (eg, city)
+localityName_default		= $ENV::EASYRSA_REQ_CITY
+
+0.organizationName		= Organization Name (eg, company)
+0.organizationName_default	= $ENV::EASYRSA_REQ_ORG
+
+organizationalUnitName		= Organizational Unit Name (eg, section)
+organizationalUnitName_default	= $ENV::EASYRSA_REQ_OU
+
+commonName			= Common Name (eg: your user, host, or server name)
+commonName_max			= 64
+commonName_default		= $ENV::EASYRSA_REQ_CN
+
+emailAddress			= Email Address
+emailAddress_default		= $ENV::EASYRSA_REQ_EMAIL
+emailAddress_max		= 64
+
+####################################################################
+# Easy-RSA cert extension handling
+
+# This section is effectively unused as the main script sets extensions
+# dynamically. This core section is left to support the odd usecase where
+# a user calls openssl directly.
+[ basic_exts ]
+basicConstraints	= CA:FALSE
+subjectKeyIdentifier	= hash
+authorityKeyIdentifier	= keyid,issuer:always
+
+# The Easy-RSA CA extensions
+[ easyrsa_ca ]
+
+# PKIX recommendations:
+
+subjectKeyIdentifier=hash
+authorityKeyIdentifier=keyid:always,issuer:always
+
+# This could be marked critical, but it's nice to support reading by any
+# broken clients who attempt to do so.
+basicConstraints = CA:true
+
+# Limit key usage to CA tasks. If you really want to use the generated pair as
+# a self-signed cert, comment this out.
+keyUsage = cRLSign, keyCertSign
+
+# nsCertType omitted by default. Let's try to let the deprecated stuff die.
+# nsCertType = sslCA
+
+# CRL extensions.
+[ crl_ext ]
+
+# Only issuerAltName and authorityKeyIdentifier make any sense in a CRL.
+
+# issuerAltName=issuer:copy
+authorityKeyIdentifier=keyid:always,issuer:always
+
diff --git a/roles/ca/files/easyrsa/vars.example b/roles/ca/files/easyrsa/vars.example
new file mode 100644
index 0000000000000000000000000000000000000000..f03ea6e3899e30478c3bd0fa83fb3e48aa450542
--- /dev/null
+++ b/roles/ca/files/easyrsa/vars.example
@@ -0,0 +1,210 @@
+# Easy-RSA 3 parameter settings
+
+# NOTE: If you installed Easy-RSA from your distro's package manager, don't edit
+# this file in place -- instead, you should copy the entire easy-rsa directory
+# to another location so future upgrades don't wipe out your changes.
+
+# HOW TO USE THIS FILE
+#
+# vars.example contains built-in examples to Easy-RSA settings. You MUST name
+# this file 'vars' if you want it to be used as a configuration file. If you do
+# not, it WILL NOT be automatically read when you call easyrsa commands.
+#
+# It is not necessary to use this config file unless you wish to change
+# operational defaults. These defaults should be fine for many uses without the
+# need to copy and edit the 'vars' file.
+#
+# All of the editable settings are shown commented and start with the command
+# 'set_var' -- this means any set_var command that is uncommented has been
+# modified by the user. If you're happy with a default, there is no need to
+# define the value to its default.
+
+# NOTES FOR WINDOWS USERS
+#
+# Paths for Windows  *MUST* use forward slashes, or optionally double-esscaped
+# backslashes (single forward slashes are recommended.) This means your path to
+# the openssl binary might look like this:
+# "C:/Program Files/OpenSSL-Win32/bin/openssl.exe"
+
+# A little housekeeping: DON'T EDIT THIS SECTION
+# 
+# Easy-RSA 3.x doesn't source into the environment directly.
+# Complain if a user tries to do this:
+if [ -z "$EASYRSA_CALLER" ]; then
+	echo "You appear to be sourcing an Easy-RSA 'vars' file." >&2
+	echo "This is no longer necessary and is disallowed. See the section called" >&2
+	echo "'How to use this file' near the top comments for more details." >&2
+	return 1
+fi
+
+# DO YOUR EDITS BELOW THIS POINT
+
+# This variable is used as the base location of configuration files needed by
+# easyrsa.  More specific variables for specific files (e.g., EASYRSA_SSL_CONF)
+# may override this default.
+#
+# The default value of this variable is the location of the easyrsa script
+# itself, which is also where the configuration files are located in the
+# easy-rsa tree.
+
+#set_var EASYRSA	"${0%/*}"
+
+# If your OpenSSL command is not in the system PATH, you will need to define the
+# path to it here. Normally this means a full path to the executable, otherwise
+# you could have left it undefined here and the shown default would be used.
+#
+# Windows users, remember to use paths with forward-slashes (or escaped
+# back-slashes.) Windows users should declare the full path to the openssl
+# binary here if it is not in their system PATH.
+
+#set_var EASYRSA_OPENSSL	"openssl"
+#
+# This sample is in Windows syntax -- edit it for your path if not using PATH:
+#set_var EASYRSA_OPENSSL	"C:/Program Files/OpenSSL-Win32/bin/openssl.exe"
+
+# Edit this variable to point to your soon-to-be-created key directory.  By
+# default, this will be "$PWD/pki" (i.e. the "pki" subdirectory of the
+# directory you are currently in).
+#
+# WARNING: init-pki will do a rm -rf on this directory so make sure you define
+# it correctly! (Interactive mode will prompt before acting.)
+
+#set_var EASYRSA_PKI		"$PWD/pki"
+
+# Define X509 DN mode.
+# This is used to adjust what elements are included in the Subject field as the DN
+# (this is the "Distinguished Name.")
+# Note that in cn_only mode the Organizational fields further below aren't used.
+#
+# Choices are:
+#   cn_only  - use just a CN value
+#   org      - use the "traditional" Country/Province/City/Org/OU/email/CN format
+
+#set_var EASYRSA_DN	"cn_only"
+
+# Organizational fields (used with 'org' mode and ignored in 'cn_only' mode.)
+# These are the default values for fields which will be placed in the
+# certificate.  Don't leave any of these fields blank, although interactively
+# you may omit any specific field by typing the "." symbol (not valid for
+# email.)
+
+#set_var EASYRSA_REQ_COUNTRY	"US"
+#set_var EASYRSA_REQ_PROVINCE	"California"
+#set_var EASYRSA_REQ_CITY	"San Francisco"
+#set_var EASYRSA_REQ_ORG	"Copyleft Certificate Co"
+#set_var EASYRSA_REQ_EMAIL	"me@example.net"
+#set_var EASYRSA_REQ_OU		"My Organizational Unit"
+
+# Choose a size in bits for your keypairs. The recommended value is 2048.  Using
+# 2048-bit keys is considered more than sufficient for many years into the
+# future. Larger keysizes will slow down TLS negotiation and make key/DH param
+# generation take much longer. Values up to 4096 should be accepted by most
+# software. Only used when the crypto alg is rsa (see below.)
+
+#set_var EASYRSA_KEY_SIZE	2048
+
+# The default crypto mode is rsa; ec can enable elliptic curve support.
+# Note that not all software supports ECC, so use care when enabling it.
+# Choices for crypto alg are: (each in lower-case)
+#  * rsa
+#  * ec
+
+#set_var EASYRSA_ALGO		rsa
+
+# Define the named curve, used in ec mode only:
+
+#set_var EASYRSA_CURVE		secp384r1
+
+# In how many days should the root CA key expire?
+
+#set_var EASYRSA_CA_EXPIRE	3650
+
+# In how many days should certificates expire?
+
+#set_var EASYRSA_CERT_EXPIRE	1080
+
+# How many days until the next CRL publish date?  Note that the CRL can still be
+# parsed after this timeframe passes. It is only used for an expected next
+# publication date.
+
+# How many days before its expiration date a certificate is allowed to be
+# renewed?
+#set_var EASYRSA_CERT_RENEW	30
+
+#set_var EASYRSA_CRL_DAYS	180
+
+# Support deprecated "Netscape" extensions? (choices "yes" or "no".) The default
+# is "no" to discourage use of deprecated extensions. If you require this
+# feature to use with --ns-cert-type, set this to "yes" here. This support
+# should be replaced with the more modern --remote-cert-tls feature.  If you do
+# not use --ns-cert-type in your configs, it is safe (and recommended) to leave
+# this defined to "no".  When set to "yes", server-signed certs get the
+# nsCertType=server attribute, and also get any NS_COMMENT defined below in the
+# nsComment field.
+
+#set_var EASYRSA_NS_SUPPORT	"no"
+
+# When NS_SUPPORT is set to "yes", this field is added as the nsComment field.
+# Set this blank to omit it. With NS_SUPPORT set to "no" this field is ignored.
+
+#set_var EASYRSA_NS_COMMENT	"Easy-RSA Generated Certificate"
+
+# A temp file used to stage cert extensions during signing. The default should
+# be fine for most users; however, some users might want an alternative under a
+# RAM-based FS, such as /dev/shm or /tmp on some systems.
+
+#set_var EASYRSA_TEMP_FILE	"$EASYRSA_PKI/extensions.temp"
+
+# !!
+# NOTE: ADVANCED OPTIONS BELOW THIS POINT
+# PLAY WITH THEM AT YOUR OWN RISK
+# !!
+
+# Broken shell command aliases: If you have a largely broken shell that is
+# missing any of these POSIX-required commands used by Easy-RSA, you will need
+# to define an alias to the proper path for the command.  The symptom will be
+# some form of a 'command not found' error from your shell. This means your
+# shell is BROKEN, but you can hack around it here if you really need. These
+# shown values are not defaults: it is up to you to know what you're doing if
+# you touch these.
+#
+#alias awk="/alt/bin/awk"
+#alias cat="/alt/bin/cat"
+
+# X509 extensions directory:
+# If you want to customize the X509 extensions used, set the directory to look
+# for extensions here. Each cert type you sign must have a matching filename,
+# and an optional file named 'COMMON' is included first when present. Note that
+# when undefined here, default behaviour is to look in $EASYRSA_PKI first, then
+# fallback to $EASYRSA for the 'x509-types' dir.  You may override this
+# detection with an explicit dir here.
+#
+#set_var EASYRSA_EXT_DIR	"$EASYRSA/x509-types"
+
+# OpenSSL config file:
+# If you need to use a specific openssl config file, you can reference it here.
+# Normally this file is auto-detected from a file named openssl-easyrsa.cnf from the
+# EASYRSA_PKI or EASYRSA dir (in that order.) NOTE that this file is Easy-RSA
+# specific and you cannot just use a standard config file, so this is an
+# advanced feature.
+
+#set_var EASYRSA_SSL_CONF	"$EASYRSA/openssl-easyrsa.cnf"
+
+# Default CN:
+# This is best left alone. Interactively you will set this manually, and BATCH
+# callers are expected to set this themselves.
+
+#set_var EASYRSA_REQ_CN		"ChangeMe"
+
+# Cryptographic digest to use.
+# Do not change this default unless you understand the security implications.
+# Valid choices include: md5, sha1, sha256, sha224, sha384, sha512
+
+#set_var EASYRSA_DIGEST		"sha256"
+
+# Batch mode. Leave this disabled unless you intend to call Easy-RSA explicitly
+# in batch mode without any user input, confirmation on dangerous operations,
+# or most output. Setting this to any non-blank string enables batch mode.
+
+#set_var EASYRSA_BATCH		""
+
diff --git a/roles/ca/files/easyrsa/x509-types/COMMON b/roles/ca/files/easyrsa/x509-types/COMMON
new file mode 100644
index 0000000000000000000000000000000000000000..3e9b633f4a05d87ffcb17ae54a534607b9b7ccaa
--- /dev/null
+++ b/roles/ca/files/easyrsa/x509-types/COMMON
@@ -0,0 +1,7 @@
+# X509 extensions added to every signed cert
+
+# This file is included for every cert signed, and by default does nothing.
+# It could be used to add values every cert should have, such as a CDP as
+# demonstrated in the following example:
+
+#crlDistributionPoints = URI:http://example.net/pki/my_ca.crl
diff --git a/roles/ca/files/easyrsa/x509-types/ca b/roles/ca/files/easyrsa/x509-types/ca
new file mode 100644
index 0000000000000000000000000000000000000000..ef525b6f0d51964348bccdca28253ee96831bf58
--- /dev/null
+++ b/roles/ca/files/easyrsa/x509-types/ca
@@ -0,0 +1,13 @@
+# X509 extensions for a ca
+
+# Note that basicConstraints will be overridden by Easy-RSA when defining a
+# CA_PATH_LEN for CA path length limits. You could also do this here
+# manually as in the following example in place of the existing line:
+#
+# basicConstraints = CA:TRUE, pathlen:1
+
+basicConstraints = CA:TRUE
+subjectKeyIdentifier = hash
+authorityKeyIdentifier = keyid:always,issuer:always
+keyUsage = cRLSign, keyCertSign
+
diff --git a/roles/ca/files/easyrsa/x509-types/client b/roles/ca/files/easyrsa/x509-types/client
new file mode 100644
index 0000000000000000000000000000000000000000..a7d81af4e5cbb04905e1477e22483cad50ee620d
--- /dev/null
+++ b/roles/ca/files/easyrsa/x509-types/client
@@ -0,0 +1,8 @@
+# X509 extensions for a client
+
+basicConstraints = CA:FALSE
+subjectKeyIdentifier = hash
+authorityKeyIdentifier = keyid,issuer:always
+extendedKeyUsage = clientAuth
+keyUsage = digitalSignature
+
diff --git a/roles/ca/files/easyrsa/x509-types/code-signing b/roles/ca/files/easyrsa/x509-types/code-signing
new file mode 100644
index 0000000000000000000000000000000000000000..05de2a51296a4c9df51789aaf5c0499431ff2aab
--- /dev/null
+++ b/roles/ca/files/easyrsa/x509-types/code-signing
@@ -0,0 +1,8 @@
+# X509 extensions for a client
+
+basicConstraints = CA:FALSE
+subjectKeyIdentifier = hash
+authorityKeyIdentifier = keyid,issuer:always
+extendedKeyUsage = codeSigning
+keyUsage = digitalSignature
+
diff --git a/roles/ca/files/easyrsa/x509-types/server b/roles/ca/files/easyrsa/x509-types/server
new file mode 100644
index 0000000000000000000000000000000000000000..bc024be5740f16425aaee497d9ed7d036a99dea7
--- /dev/null
+++ b/roles/ca/files/easyrsa/x509-types/server
@@ -0,0 +1,8 @@
+# X509 extensions for a server
+
+basicConstraints = CA:FALSE
+subjectKeyIdentifier = hash
+authorityKeyIdentifier = keyid,issuer:always
+extendedKeyUsage = serverAuth
+keyUsage = digitalSignature,keyEncipherment
+
diff --git a/roles/ca/files/easyrsa/x509-types/serverClient b/roles/ca/files/easyrsa/x509-types/serverClient
new file mode 100644
index 0000000000000000000000000000000000000000..774fe5140ca335dfd185344ae35cc42fe91d7051
--- /dev/null
+++ b/roles/ca/files/easyrsa/x509-types/serverClient
@@ -0,0 +1,8 @@
+# X509 extensions for a client/server
+
+basicConstraints = CA:FALSE
+subjectKeyIdentifier = hash
+authorityKeyIdentifier = keyid,issuer:always
+extendedKeyUsage = serverAuth,clientAuth
+keyUsage = digitalSignature,keyEncipherment
+
diff --git a/roles/ca/handlers/main.yml b/roles/ca/handlers/main.yml
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/roles/ca/meta/main.yml b/roles/ca/meta/main.yml
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/roles/ca/tasks/main.yml b/roles/ca/tasks/main.yml
new file mode 100644
index 0000000000000000000000000000000000000000..3a5ffcd959aab707804b1d8af145e574e8dba882
--- /dev/null
+++ b/roles/ca/tasks/main.yml
@@ -0,0 +1,133 @@
+---
+
+- name: Check for existing CA folder
+  stat:
+    path: roles/ca/files/CA
+  register: capath
+
+- name: build ca root key and cert
+  command: roles/ca/files/easyrsa/easyrsa {{item}}
+  with_items:
+    - "init-pki"
+    - "build-ca nopass"
+    #- "gen-dh"
+  environment:
+    EASYRSA_BATCH: 1
+    EASYRSA_REQ_CN: "{{ ca_cn }}"
+    EASYRSA_PKI: roles/ca/files/CA
+  when: not capath.stat.exists
+
+- name: Copy cert to truststore
+  copy:
+    src: roles/ca/files/CA/ca.crt
+    dest: "roles/ca/files/truststore/{{ ca_cn }}.crt"
+
+- name: Remove previous truststore
+  file:
+    path: roles/ca/files/truststore/cacerts.jks
+    state: absent
+
+- name: Generate truststore
+  command: >
+    docker run --rm -v {{role_path}}/files/truststore/:/opt/cafiles/:z 
+    gn43-dsl/openjdk:7al keytool -import -noprompt -trustcacerts 
+    -alias "{{item}}" -file "/opt/cafiles/{{item}}.crt" -keystore /opt/cafiles/cacerts.jks -storepass "{{tspass}}"
+  with_items:
+    - "{{ ca_cn }}"
+    #- GN43WP8T31_CA
+
+- name: Check for existing host certificates
+  command: roles/ca/files/easyrsa/easyrsa show-cert {{item}}
+  with_items:
+    - "{{ groups['nificontainers'] }}"
+  environment:
+    EASYRSA_BATCH: 1
+    EASYRSA_PKI: roles/ca/files/CA
+  register: hostcerts
+  ignore_errors: true
+
+- name: Generate host certificates
+  command: > 
+    roles/ca/files/easyrsa/easyrsa 
+    --subject-alt-name="DNS:{{item}},DNS:{{dslproxy}}"
+    build-serverClient-full {{item}} nopass
+  with_items:
+    - "{{ groups['nificontainers'] }}"
+  environment:
+    EASYRSA_BATCH: 1
+    EASYRSA_PKI: roles/ca/files/CA
+  ignore_errors: true 
+  loop_control:
+    index_var: my_idx
+  when: hostcerts.results[my_idx].rc != 0
+
+#- name: Export host certificates
+#  command: > 
+#    expect -c '
+#      spawn roles/ca/files/easyrsa/easyrsa export-p12 {{item}};
+#      expect "Enter Export Password:"; send "{{kspass}}\r";
+#      expect "Verifying - Enter Export Password:"; send "{{kspass}}\r";
+#      expect eof'
+#  with_items:
+#    - "{{ groups['nificontainers'] }}"
+#    #- "export-p12 {{ groups['nificontainers'] }} nopass"
+#  environment:
+#    EASYRSA_BATCH: 1
+#    EASYRSA_PKI: roles/ca/files/CA
+
+- name: Export host certificates
+  expect:
+    command: roles/ca/files/easyrsa/easyrsa export-p12 {{item}}
+    responses:
+      Enter Export Password: "{{kspass}}"
+  with_items:
+    - "{{ groups['nificontainers'] }}"
+  environment:
+    EASYRSA_BATCH: 1
+    EASYRSA_PKI: roles/ca/files/CA
+
+- name: Copy nifi host certs to nifi role
+  copy:
+    src: roles/ca/files/CA/private/{{item}}.p12
+    dest: roles/nifi/files/{{item}}.p12
+  with_items:
+    - "{{ groups['nificontainers'] }}"
+
+- name: Copy nifi truststore to nifi role
+  copy:
+    src: roles/ca/files/truststore/cacerts.jks
+    dest: roles/nifi/files/cacerts.jks
+
+- name: Check for existing user certificates
+  command: roles/ca/files/easyrsa/easyrsa show-cert {{item[0] | regex_escape()}}
+  with_items:
+    - "{{nifiadmin}}"
+  environment:
+    EASYRSA_BATCH: 1
+    EASYRSA_PKI: roles/ca/files/CA
+  register: usercerts
+  ignore_errors: true
+
+- name: Generate user certificates
+  command: roles/ca/files/easyrsa/easyrsa build-client-full {{item[0] | regex_escape()}} nopass
+  with_items:
+    - "{{nifiadmin}}"
+  environment:
+    EASYRSA_BATCH: 1
+    EASYRSA_PKI: roles/ca/files/CA
+  ignore_errors: true
+  loop_control:
+    index_var: my_idx
+  when: usercerts.results[my_idx].rc != 0
+
+- name: Export user certificates
+  expect:
+    command: roles/ca/files/easyrsa/easyrsa export-p12 "{{item[0]}}"
+    responses:
+      Enter Export Password: "{{item[1]}}"
+  with_items:
+    - "{{nifiadmin}}"
+  environment:
+    EASYRSA_BATCH: 1
+    EASYRSA_PKI: roles/ca/files/CA
+
diff --git a/roles/ca/vars/main.yml b/roles/ca/vars/main.yml
new file mode 100644
index 0000000000000000000000000000000000000000..cd21505a47e530a967e3c44bd2a772d1b8d08bd7
--- /dev/null
+++ b/roles/ca/vars/main.yml
@@ -0,0 +1,2 @@
+---
+
diff --git a/roles/nifi/files/cacerts.jks b/roles/nifi/files/cacerts.jks
deleted file mode 100644
index dfb92a5907916710460d0eae2eb40a9f955bf153..0000000000000000000000000000000000000000
Binary files a/roles/nifi/files/cacerts.jks and /dev/null differ
diff --git a/roles/nifi/files/dsoclab-nifi-1.p12 b/roles/nifi/files/dsoclab-nifi-1.p12
deleted file mode 100644
index 9a7f342959bb02b2dcadc7fa54c8442ccdde8f1f..0000000000000000000000000000000000000000
Binary files a/roles/nifi/files/dsoclab-nifi-1.p12 and /dev/null differ
diff --git a/roles/nifi/files/dsoclab-nifi-2.p12 b/roles/nifi/files/dsoclab-nifi-2.p12
deleted file mode 100644
index 4f49a68103b434d45df8b05ba7188f8c0bf01274..0000000000000000000000000000000000000000
Binary files a/roles/nifi/files/dsoclab-nifi-2.p12 and /dev/null differ
diff --git a/roles/nifi/files/dsoclab-nifi-3.p12 b/roles/nifi/files/dsoclab-nifi-3.p12
deleted file mode 100644
index 934b318117c4ed12695d20f98a62d16c02f07bc1..0000000000000000000000000000000000000000
Binary files a/roles/nifi/files/dsoclab-nifi-3.p12 and /dev/null differ
diff --git a/roles/nifi/files/flow-dsoclab-nifi-1.xml.gz b/roles/nifi/files/flow-dsoclab-nifi-1.xml.gz
deleted file mode 100644
index 8837382d99acb4fc3a9ea87e2a508ac56a1f0e2d..0000000000000000000000000000000000000000
Binary files a/roles/nifi/files/flow-dsoclab-nifi-1.xml.gz and /dev/null differ
diff --git a/roles/nifi/files/flow-dsoclab-nifi-2.xml.gz b/roles/nifi/files/flow-dsoclab-nifi-2.xml.gz
deleted file mode 100644
index 8837382d99acb4fc3a9ea87e2a508ac56a1f0e2d..0000000000000000000000000000000000000000
Binary files a/roles/nifi/files/flow-dsoclab-nifi-2.xml.gz and /dev/null differ
diff --git a/roles/nifi/files/flow-dsoclab-nifi-3.xml.gz b/roles/nifi/files/flow-dsoclab-nifi-3.xml.gz
deleted file mode 100644
index 8837382d99acb4fc3a9ea87e2a508ac56a1f0e2d..0000000000000000000000000000000000000000
Binary files a/roles/nifi/files/flow-dsoclab-nifi-3.xml.gz and /dev/null differ
diff --git a/roles/nifi/tasks/main.yml b/roles/nifi/tasks/main.yml
index fd1ef9f4c4ff7c3d028dff73c69675d55611e5c9..b1f8c59c1787f8f1212e087690dcc494adee37e6 100644
--- a/roles/nifi/tasks/main.yml
+++ b/roles/nifi/tasks/main.yml
@@ -19,6 +19,13 @@
   tags:
     - start
 
+- name: Configure NiFi boostrap properties
+  template:
+    src: bootstrap.conf.j2
+    dest: conf/bootstrap.conf
+  tags:
+    - start
+
 - name: Configure NiFi properties for secure servers
   template:
     src: nifi.properties.j2
diff --git a/roles/nifi/templates/authorizers.xml.j2 b/roles/nifi/templates/authorizers.xml.j2
index 2b3114aa17f134451569b18b1450610f14d4465f..fd8fcc7870a6c57f3c0c9340dd9efa75a92da8f4 100644
--- a/roles/nifi/templates/authorizers.xml.j2
+++ b/roles/nifi/templates/authorizers.xml.j2
@@ -49,7 +49,7 @@
         <property name="Users File">./conf/users.xml</property>
         <property name="Legacy Authorized Users File"></property>
 
-        <property name="Initial User Identity 1">CN=bozidar_proevski</property>
+        <property name="Initial User Identity 1">CN={{ nifiadmin[0][0] }}</property>
 {% for nifi in groups['nificontainers'] %}
         <property name="Initial User Identity {{ loop.index +1 }}">CN={{ nifi }}</property>
 {% endfor %}
@@ -252,7 +252,7 @@
         <class>org.apache.nifi.authorization.FileAccessPolicyProvider</class>
         <property name="User Group Provider">file-user-group-provider</property>
         <property name="Authorizations File">./conf/authorizations.xml</property>
-        <property name="Initial Admin Identity">CN=bozidar_proevski</property>
+        <property name="Initial Admin Identity">CN={{ nifiadmin[0][0] }}</property>
         <property name="Legacy Authorized Users File"></property>
 {% for nifi in groups['nificontainers'] %}
         <property name="Node Identity {{ loop.index }}">CN={{ nifi }}</property>
diff --git a/roles/nifi/templates/bootstrap.conf.j2 b/roles/nifi/templates/bootstrap.conf.j2
new file mode 100644
index 0000000000000000000000000000000000000000..4b92c44d8610e7291762a55674ada2be6d18b549
--- /dev/null
+++ b/roles/nifi/templates/bootstrap.conf.j2
@@ -0,0 +1,81 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# Java command to use when running NiFi
+java=java
+
+# Username to use when running NiFi. This value will be ignored on Windows.
+run.as=
+
+# Configure where NiFi's lib and conf directories live
+lib.dir=./lib
+conf.dir=./conf
+
+# How long to wait after telling NiFi to shutdown before explicitly killing the Process
+graceful.shutdown.seconds=20
+
+# Disable JSR 199 so that we can use JSP's without running a JDK
+java.arg.1=-Dorg.apache.jasper.compiler.disablejsr199=true
+
+# JVM memory settings
+java.arg.2=-Xms{{ javamem }}
+java.arg.3=-Xmx{{ javamem }}
+
+# Enable Remote Debugging
+#java.arg.debug=-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=8000
+
+java.arg.4=-Djava.net.preferIPv4Stack=true
+
+# allowRestrictedHeaders is required for Cluster/Node communications to work properly
+java.arg.5=-Dsun.net.http.allowRestrictedHeaders=true
+java.arg.6=-Djava.protocol.handler.pkgs=sun.net.www.protocol
+
+# The G1GC is still considered experimental but has proven to be very advantageous in providing great
+# performance without significant "stop-the-world" delays.
+java.arg.13=-XX:+UseG1GC
+
+#Set headless mode by default
+java.arg.14=-Djava.awt.headless=true
+
+# Master key in hexadecimal format for encrypted sensitive configuration values
+nifi.bootstrap.sensitive.key=
+
+# Sets the provider of SecureRandom to /dev/urandom to prevent blocking on VMs
+java.arg.15=-Djava.security.egd=file:/dev/urandom
+
+# Requires JAAS to use only the provided JAAS configuration to authenticate a Subject, without using any "fallback" methods (such as prompting for username/password)
+# Please see https://docs.oracle.com/javase/8/docs/technotes/guides/security/jgss/single-signon.html, section "EXCEPTIONS TO THE MODEL"
+java.arg.16=-Djavax.security.auth.useSubjectCredsOnly=true
+
+###
+# Notification Services for notifying interested parties when NiFi is stopped, started, dies
+###
+
+# XML File that contains the definitions of the notification services
+notification.services.file=./conf/bootstrap-notification-services.xml
+
+# In the case that we are unable to send a notification for an event, how many times should we retry?
+notification.max.attempts=5
+
+# Comma-separated list of identifiers that are present in the notification.services.file; which services should be used to notify when NiFi is started?
+#nifi.start.notification.services=email-notification
+
+# Comma-separated list of identifiers that are present in the notification.services.file; which services should be used to notify when NiFi is stopped?
+#nifi.stop.notification.services=email-notification
+
+# Comma-separated list of identifiers that are present in the notification.services.file; which services should be used to notify when NiFi dies?
+#nifi.dead.notification.services=email-notification