Skip to content
This repository has been archived by the owner on Nov 27, 2023. It is now read-only.

Commit

Permalink
Ansible automation
Browse files Browse the repository at this point in the history
  • Loading branch information
sabre1041 committed Oct 26, 2017
1 parent 5580c87 commit 43391b4
Show file tree
Hide file tree
Showing 20 changed files with 490 additions and 37 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.DS_Store
79 changes: 45 additions & 34 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,84 +7,95 @@ _This repository is currently undergoing active development. Functionality may b

The OpenShift Container Platform ecosystem contains mechanisms for securely managing container images. This includes but is not limited to [image signing](https://docs.openshift.com/container-platform/3.6/admin_guide/image_signatures.html) and [image scanning](https://docs.openshift.com/container-platform/3.6/security/container_content.html#security-content-scanning).

The goal of this repository is to contain tools necessary to perform these taks within OpenShift.
A set of Ansible tools is available to aid in the automation and configuration of the target environment.

## Prerequisites
## Image Signing

To facilitate image signing and scanning, the following prerequisite sections must be completed.
Image signing is a way to apply a digital signature to a container image. This guide describes how an automated process ca be created an implemented within OpenShift. The goal is to produce an environment that allows for only the execution of signed images from trusted sources (Red Hat Container Catalog [RHCC]) along with assets that are created within an organization or group.

### Create a GPG Keypair
## Architecture

A GPG Keypair is required in order to sign and validate images. Execute the following command to create a new keypair:
_Diagram Coming Soon_

```
gpg2 --gen-key
```
The signing architecture utilizes a typical OpenShift environment by specifying dedicated nodes for performing image signing actions. The key difference between image signing nodes and the rest of the nodes in the environment is relaxing the image requirement policies to allow for signing actions to occur on these nodes.

**Note: Only keys without a passphrase are currently supported.**
Two ways to ensure only image signing workloads are scheduled onto these dedicated nodes is through [Node Selectors](https://docs.openshift.com/container-platform/latest/admin_guide/scheduling/node_selector.html#admin-guide-sched-selector-config) placed on image signing resources and [tainting](https://docs.openshift.com/container-platform/latest/admin_guide/scheduling/taints_tolerations.html) image signing nodes. Image signing resources, such as jobs, are configured with tolerations to allow execution on the tainted nodes.

Make a note of the location of the key pair as it will be needed in subsequent steps.
## Setup and Configuration

## Base Infrastructure
A set of Ansible playbooks and roles is available to automate the configuration of an environment to sign and allow exclusive access to only signed images. The following actions are performed in the automation tooling:

Several steps must be completed in order to satisfied the necessary access and permissions within the OpenShift cluster. By default, the infrastructure components are designed to run within the _openshift-infra_ project. Templates are available that allow the destination to be customized if necessary.
* Creation of GPG keys
* Configuration of OpenShift host machines to enable signature verification along with trusted sources
* Deployment of OpenShift cluster resources to host image signing resources

A user with `cluster-admin` privileges must be used to configure the base infrastructure components as they modify cluster level resources.
### Ansible Automation

Create a new service account and assign it _cluster-admin_ privileges along with adding it to the _privileged_ SCC.

```
oc process -f policy/sa-rolebinding-template.yml | oc apply -f-
oc adm policy add-scc-to-user privileged -n openshift-infra -z imagemanager
```

#### Inventory File Configuration

The inventory file is broken down into 3 (three) host groups:

* gpg - Machine responsible for creating GPG keys
* image_signers - OpenShift nodes responsible for signing images
* nodes - All OpenShift nodes

## Base Image
#### Playbook Execution

Image signing and image scanning actions each utilizes the [Atomic Command Line Tool](https://github.com/projectatomic/atomic). To support running within OpenShift, a base container is available within the [image-sign-scan-base](image-sign-scan-base) folder.
The OpenShift environment can be configured by executing the `image-signing-setup.yml` playbook in the `ansible/playbooks` folder.

To process and instantiate the template the base image components, execute the following command:
Execute the following command from within the _playbooks_ directory to initiate the setup:

```
oc process -f image-sign-scan-base/image-sign-scan-base.yml | oc apply -f-
ansible-playbook -i hosts image-signing-setup.yml
```

A new image will be created as a foundational component for subsequent sections
Once the playbook completes, the OpenShift environment will be configured to handle image signing and execution.

## Image Signing
## Testing the Architecture

This section describes how to setup an OpenShift environment to support the signing of images
Login to the cluster and create a new project:

### Add GPG Secret
```
oc new-project project
```

To support image signing, a GPG key pair was previously created. The files produced will be used in an Secret and injected into the signing process.
Verifying a signed image requires additional permissions than what is created for the default service. As part of the Ansible automation, a custom ClusterRole called `signature-viewer` was created with the necessary permissions.

Either reference the folder containing the GPG components or copy the files to a new directory. Execute the following command to create the secret:
Execute the following command to add the cluster role to the service account:

```
oc secrets new gpg <folder_containing_resources>
oc adm policy add-cluster-role-to-user signature-viewer system:serviceaccount:<project>:default
```

Execute the build of the image. Once the build completes, a job can be created to sign the image as outlined in the subsequent section.

### Create a job to Sign an Image

A template to create a job is available to sign an image within the OpenShift registry and available in the _sign-image_ folder.
Since the environment has been configured to only allow signed images to run, the image previously created in the prior section must be signed before it is allowed to run.

A template was created in the `image-signer` project as part of the Ansible provisioning that will create a job to sign the image that is stored the OpenShift registry.

The template requires the following parameters:

|Template Parameter|Description|Default Value|
|--------------------------|----------------|-----------------|
|SERVICE_ACCOUNT_NAME|Name of the Service Account to use|imagemanager|
|NAMESPACE|Namespace in which to create the job|openshift-infra|
|NAMESPACE|Namespace in which to create the job|image-signer|
|IMAGE_TO_SIGN|Image location (Ex: _docker-registry.default.svc:5000/test-project/test-image:latest_)| |
|SIGN_BY|Identity of the signer| |
|GPG_SECRET| Name of the secret containing the GPG keypair| |
|GPG_SECRET| Name of the secret containing the GPG keypair| gpg |
|NODE_SELECTOR_KEY| Node selector key | image_signer |
|NODE_SELECTOR_VALUE| Node selector value | true |

Instantiate the template

```
oc process -p <param>... -f sign-image/sign-image-template.yml | oc apply -f-
oc new-app -n image-signer --template=sign-image-template -p <params>
```

The image will be signed and pushed back to the OpenShift Registry where it can now be run within the cluster.

## Image Scanning

Coming soon
8 changes: 8 additions & 0 deletions ansible/playbooks/hosts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Machine responsible for generating GPG key (Only First machine will be used)
[gpg]

Dedicated OpenShift nodes for signing images
[image_signers]

All OpenShift Nodes in the cluster
[nodes]
16 changes: 16 additions & 0 deletions ansible/playbooks/image-signing-setup.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
---

# Generate a GPG Key
- hosts: gpg[0]
roles:
- { role: gpg-generate, when: (generate_gpg_key is not defined) or (generate_gpg_key is defined and generate_gpg_key) }

- hosts: nodes:!image_signers
roles:
- node-config

- hosts: masters[0]
roles:
- ocp-setup


1 change: 1 addition & 0 deletions ansible/playbooks/roles
22 changes: 22 additions & 0 deletions ansible/roles/gpg-generate/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
gpg-generate
============

Role to generate GPG Keys to sign and validate images

_Note_: This role does enable the Extra Packages for Enterprise Linux (EPEL) repository on the machine used to generate GPG keys

# Overview

Generates GPG keychains and exports the public key. Transfers these assets to the control node.

# Role Variables

The following variables dictate the execution of the role:

|Variable|Description|Default Value|
|--------|-----------|-------------|
|`gpg_key_gen_user_name`| GPG Username | `openshift` |
|`gpg_key_gen_user_email` | GPG Email Address (Also used for identity) | `openshift@example.com` |
| `gpg_key_gen_length` | GPG Key Length | `2048` |
|`gpg_key_gen_passphrase_enable` | Whether to enable a passphrase on the key (Should remain false) | `false` |
|`gpg_local_base_dir`|Location where to store GPG Keys fetched from remote host | `{{ playbook_dir }}/gpg_remote`|
23 changes: 23 additions & 0 deletions ansible/roles/gpg-generate/defaults/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
---

epel_repo_url: "https://dl.fedoraproject.org/pub/epel/epel-release-latest-{{ ansible_distribution_major_version }}.noarch.rpm"
epel_repo_gpg_key_url: "https://dl.fedoraproject.org/pub/epel/RPM-GPG-KEY-EPEL-{{ ansible_distribution_major_version }}"
epel_repofile_path: "/etc/yum.repos.d/epel.repo"

# Create a custom GPG Directory
gpg_home: ~/.gnupg-openshift

gpg_key_gen_user_name: openshift
gpg_key_gen_user_email: openshift@example.com
gpg_key_gen_type: 1
gpg_key_gen_length: 2048
gpg_key_gen_expire_date: 0
gpg_key_gen_passphrase: "{{ lookup('password', '/tmp/passwordfile') }}"
gpg_key_gen_passphrase_enable: false
gpg_key_gen_uid: "{{ gpg_key_gen_user_name }} <{{ gpg_key_gen_user_email }}>"
gpg_key_gen_path: "{{ gpg_key_gen_uid | hash('md5') }}"

# Directory to Fetch Generated Files Locally
gpg_local_base_dir: "{{ playbook_dir }}/gpg_remote"
gpg_core_files_dir: "{{ gpg_local_base_dir }}/gpg_config"
gpg_publickey_dir: "{{ gpg_local_base_dir }}/public_key"
8 changes: 8 additions & 0 deletions ansible/roles/gpg-generate/files/gpg.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Options for GnuPG

#keyserver hkp://keys.gnupg.net
use-agent

auto-key-locate cert pka ldap hkps://hkp://keys.gnupg.net
keyserver hkp://keys.gnupg.net
require-cross-certification
20 changes: 20 additions & 0 deletions ansible/roles/gpg-generate/tasks/epel.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
---

- name: Check if EPEL repo is already configured.
stat: path={{ epel_repofile_path }}
register: epel_repofile_result

- name: Install EPEL repo.
become: true
yum:
name: "{{ epel_repo_url }}"
state: present
register: result
when: not epel_repofile_result.stat.exists

- name: Import EPEL GPG key.
become: true
rpm_key:
key: "{{ epel_repo_gpg_key_url }}"
state: present
when: not epel_repofile_result.stat.exists
118 changes: 118 additions & 0 deletions ansible/roles/gpg-generate/tasks/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
---

- include: epel.yml

- name: Install GPG Dependencies
become: true
yum:
name: "{{ item }}"
state: latest
with_items:
- gnupg2
- rng-tools
- haveged
- name: Enable haveged
become: true
service:
name: haveged
state: started
- name: Create GPG Home Directory
file:
state: directory
mode: 0700
dest: "{{ gpg_home }}/.gnupg"

- name: Set gpg pinentry mode to loopback in config file.
lineinfile:
dest: "{{ gpg_home }}/.gnupg/gpg.conf"
line: "{{ item.line }}"
regexp: "{{ item.regexp }}"
mode: 0600
create: yes
with_items:
- line: "use-agent"
regexp: "^use-agent"
- line: "pinentry-mode loopback"
regexp: "^pinentry-mode"

- name: Set gpg-agent config file to allow loopback pinentry.
lineinfile:
dest: "{{ gpg_home }}/.gnupg/gpg-agent.conf"
line: "{{ item.line }}"
regexp: "{{ item.regexp }}"
mode: 0600
create: yes
with_items:
- line: "allow-loopback-pinentry"
regexp: "^allow-loopback-pinentry"

- name: Copy GPG Configuration File
copy:
src: gpg.conf
dest: "{{ gpg_home }}/.gnupg/gpg.conf"
mode: 0600

- name: Generate GPG Template
template:
src: gpg-generate.j2
dest: "{{ gpg_home }}/gen-key-script-openshift"
mode: 0600


- name: Check existing secret key
shell: "gpg2 --list-secret-keys | grep '{{ gpg_key_gen_uid }}'"
environment:
GNUPGHOME: "{{ gpg_home }}/.gnupg"
changed_when: false
ignore_errors: true
register: gpgkeys

- name: Generate gpg key
command: "gpg2 --batch --gen-key {{ gpg_home }}/gen-key-script-openshift chdir={{ gpg_home }}"
environment:
GNUPGHOME: "{{ gpg_home }}/.gnupg"
when: gpgkeys is defined and gpgkeys.stdout == ""
register: genkey

- name: Export Public Key
command: "gpg2 --batch --yes --armor --export --output {{ gpg_home }}/{{ gpg_key_gen_user_email }} {{ gpg_key_gen_user_email }} chdir={{ gpg_home }}"
environment:
GNUPGHOME: "{{ gpg_home }}/.gnupg"

- name: Delete local folder
delegate_to: localhost
file:
state: absent
path: "{{ gpg_local_base_dir }}"

- name: Create Local Folders for GPG Content
delegate_to: localhost
file:
state: directory
path: "{{ item }}"
with_items:
- "{{ gpg_core_files_dir }}"
- "{{ gpg_publickey_dir }}"

- name: Fetch GPG Files
fetch:
src: "{{ item }}"
dest: "{{ gpg_core_files_dir }}/"
flat: true
with_items:
- "{{ gpg_home }}/.gnupg/pubring.gpg"
- "{{ gpg_home }}/.gnupg/secring.gpg"
- "{{ gpg_home }}/.gnupg/trustdb.gpg"

- name: Fetch GPG Public Key
fetch:
src: "{{ item }}"
dest: "{{ gpg_publickey_dir }}/"
flat: true
with_items:
- "{{ gpg_home }}/{{ gpg_key_gen_user_email }}"

- name: Delete GPG Directory
file:
state: absent
path: "{{ gpg_home }}"
11 changes: 11 additions & 0 deletions ansible/roles/gpg-generate/templates/gpg-generate.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
%echo Generating a basic OpenPGP key
Key-Type: 1
Key-Length: {{ gpg_key_gen_length }}
Name-Real: {{ gpg_key_gen_user_name }}
Name-Email: {{ gpg_key_gen_user_email }}
Expire-Date: {{ gpg_key_gen_expire_date }}
{% if gpg_key_gen_passphrase_enable is defined and gpg_key_gen_passphrase_enable %}
Passphrase: {{ gpg_key_gen_passphrase }}
{% endif %}
%commit
%echo done
13 changes: 13 additions & 0 deletions ansible/roles/node-config/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
node-config
=============

Configures an OpenShift node to support image signatures.

## Role Variables

This role contains a number of variables that drive the specific execution. The following variables are key to the functionality of this role:

|Variable|Description|
|--------|-----------|
|`local_gpg_publickey`| Location of the public key to configure in OpenShift |
|`ocp_registry`| Location of the OpenShift registry |
7 changes: 7 additions & 0 deletions ansible/roles/node-config/defaults/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---

gpg_publickey_file: "openshift@example.com"
local_gpg_publickey: "{{ playbook_dir }}/gpg_remote/public_key/{{ gpg_publickey_file }}"
ocp_registry: docker-registry.default.svc:5000
containers_public_key_dir: /etc/pki/containers

Loading

0 comments on commit 43391b4

Please sign in to comment.