Securing Containers with Red Hat Quay and Clair - Part II

In our previous post, we discussed the issues administrators face when it comes to the security of outside container content in both development and runtime environments.

Open source components are a significant weak link in container environments, and the layered aspect of container images means careful review is required before deployment into a runtime environment. Accomplishing this without diminishing agility or speed requires tools created specifically for integration and implementation.

Red Hat Quay is the container and application registry which allows developers to securely build, analyze, and distribute container images. Quay also provides a Docker registry service, and then seamlessly integrates with Clair to implement scanning of container images for security vulnerabilities.

The Clair project is an open source engine that powers Red Hat Quay Security Scanner to detect vulnerabilities in all images within Red Hat Quay, then notify developers as those issues are discovered. 

This article assumes you have a functioning Quay environment. Our article is based on having Red Hat Quay 3 installed with Red Hat OpenShift 3.11.98 and Ansible 2.6. This also assumes that you have a supported Object Oriented Storage location established and is already being used by Quay. 


Configure Quay

Quay needs to be configured to communicate with Clair. Configuration must be done using a Quay user account with Superuser permissions.  

1. 	Login to the Quay web UI using a Superuser type user account
2. 	Navigate to:
Super User Admin Panel --> Registry Settings --> Security Scanner
3. 	Select checkbox next to "Enable Security Scanning"
4. 	Set Security Scanner Endpoint: http://clair-app-api.quay-enterprise.svc.cluster.local:6060
5. 	Create Authentication Key (record Key ID and private key):
Generate Key -> Generate Shared Key -> Continue -> Generate Key
6. 	Write private key to openshift_certs/security_scanner.pem
7. 	Click “Save Configuration Changes” button at the bottom of the page.

 

Stage container images

Depending on the organization and its security policies, Direct internet access may be allowed and the OCP infrastructure servers may be configured to download directly from Red Hat or Docker. If this is the case, the next section can be skipped.

However; if an on-Prem registry is already in place and is going to be the preferred location for image storage and retrieval. This will need to be done if the OCP servers will NOT be allowed to download directly from Red Hat or Docker.

$ docker pull quay.io/coreos/clair-jwt:v2.0.8
$ docker tag quay.io/coreos/clair-jwt:v2.0.8 quay.io/coreos/clair-jwt:latest
$ docker tag quay.io/coreos/clair-jwt:latest \
    registry.apps.ocp-dev01-bna.ssc.tsc/quay-enterprise/clair-jwt
$ docker push registry.apps.ocp-dev01-bna.ssc.tsc/quay-enterprise/clair-jwt

Create databases

 The Clair tool requires that it installs a small version of the Postgres database. This database is used for CVE (Common Vulnerabilities and Exposures)files that are downloaded from Red Hat and Docker. These files are needed for the scanning of images that will be used in the OCP environment. 

$ oc rsh <postgresql pod name>
sh-4.2$ createdb clair -O quayuser
sh-4.2$ createdb quayuser -O quayuser

Configure authentication

Configure credentials to enable Clair to communicate with the Postgresql database and Quay.

This communication is vital for the in depth scanning process that Clair will perform and compare an imported image from the developers against the CVE information housed in the Postgres database. Since this information contains sensitive information and may expose certain vulnerabilities, allowing a “service” account to perform the tasks without human intervention is necessary for the automation process. Once that process is complete, this information is then taken by Quay to show results and reports of what it has found. 

# Set Postgresql password in config/config.yml, then create secret.
# Important - URL encode any special characters in password (see https://developer.mozilla.org/en-US/docs/Glossary/percent-encoding)
$ vi config/config.yml
clair:
  database:
    type: pgsql
    options:
      # A PostgreSQL Connection string pointing to the Clair Postgres database.
      # Documentation on the format can be found at: http://www.postgresql.org/docs/9.4/static/libpq-connect.html
      source: postgres://quayuser:<POSTGRESQL_PASSWORD>@postgresql.quay-enterprise.svc.cluster.local:5432/clair?sslmode=disable
      cachesize: 16384
<snip>
$ oc create configmap clair-app --from-file=config/ -n quay-enterprise
# ca.crt and security_scanner.pem should be located in openshift_certs directory
$ cd ../openshift_certs
$ oc create secret generic ca-crt --from-file=ca.crt -n quay-enterprise
$ oc create secret generic securityscanner \
    --from-file=security_scanner.pem -n quay-enterprise
$ cd ../quaydeploy/
 

Deploy Clair

 Deploying Clair is similar to when we deployed Quay,  OCP treats this just like an internal service and therefore should be deployed as projects that will in turn create containers to run the service and start up all of the necessary processes , opening of the published ports needed for network communication and all the required internal certificate needed for secure communication. This also allows for the service to be monitored for health checks and self-healing by the OCP platform. 

$ oc create -f clair-service.yml -n quay-enterprise
$ oc create -f clair-deployment.yml -n quay-enterprise
 

Using Clair

Quay will automatically start using Clair to scan images as they are pushed to Quay once this implementation process has been completed. Clair scanning results will be displayed in the “SECURITY SCAN” column on the “Repository Tags” page for each Repository in the Quay web UI. Click the scan result for additional details such as the number of vulnerabilities, vulnerability criticality, links to advisories, etc.


Application deployment

This section shows how to deploy an application container using Quay as the container image registry. The source image is pulled from the Quay registry. Then the image resulting from the application build process is pushed in the Quay registry. Next, Quay scans the new image using Clair. Finally, the application container is pulled from the Quay registry so that it can be deployed by OpenShift. In this example, we’ll call this application “HelloWorld”. Our application is going to deploy and Apache Web Server with a greeting page. 

*NOTE* Our example is based on the assumption that you have been able to successfully import an image into your own environment or you have access to a registry that already has test images that you would like to use. Some information may need to be filled in by you the reader for functionality and to be relative to your environment. 

 

Create application deployment files

Create a directory to hold the application deployment template and subdirectory for application content. Then create the template and application content.


$ mkdir -p helloworld-app-demo/build_content
$ cd helloworld-app-demo/

 

httpd-helloworld-demo.yml

Create httpd-helloworld-demo.yml in the  helloworld-app-demo/ directory.

 

apiVersion: template.openshift.io/v1
kind: Template
labels:
  app: httpd-helloworld-demo
  template: httpd-helloworld-demo
message: |-
  The following service(s) have been created in your project: $.
  For more information about using this template, including OpenShift considerations, see https://github.com/openshift/httpd-ex/blob/master/README.md.
metadata:
  annotations:
    description: An example Apache HTTP Server (httpd) application that serves static
      content. For more information about using this template, including OpenShift
      considerations, see https://github.com/openshift/httpd-ex/blob/master/README.md.
    iconClass: icon-apache
    openshift.io/display-name: Apache HTTP Server
    openshift.io/documentation-url: https://github.com/openshift/httpd-ex
    openshift.io/long-description: This template defines resources needed to develop
      a static application served by Apache HTTP Server (httpd), including a build
      configuration and application deployment configuration.
    openshift.io/provider-display-name: Red Hat, Inc.
    openshift.io/support-url: https://access.redhat.com
    tags: quickstart,httpd
    template.openshift.io/bindable: "false"
  creationTimestamp: 2019-04-17T03:35:08Z
  name: httpd-helloworld-demo
  namespace: openshift
  resourceVersion: "1726"
  selfLink: /apis/template.openshift.io/v1/namespaces/openshift/templates/httpd-helloworld-demo
  uid: <<find the UID of the image template from the above directory.copy and paste it here>>
objects:
- apiVersion: v1
  kind: Service
  metadata:
    annotations:
      description: Exposes and load balances the application pods
    name: $
  spec:
    ports:
    - name: web
      port: 8080
      targetPort: 8080
    selector:
      name: $
- apiVersion: v1
  kind: Route
  metadata:
    name: $
  spec:
    host: $
    tls:
      termination: edge
    to:
      kind: Service
      name: $
- apiVersion: v1
  kind: ImageStream
  metadata:
    annotations:
      description: Keeps track of changes in the application image
    name: $
  spec:
    lookupPolicy:
      local: false
    tags:
    - annotations: null
      from:
        kind: DockerImage
        name:</Provide/an/image/to/be/used/here/$:latest
      generation: 1
      importPolicy: {}
      name: latest
      referencePolicy:
        type: Source
- apiVersion: v1
  kind: BuildConfig
  metadata:
    annotations:
      description: Defines how to build the application
      template.alpha.openshift.io/wait-for-ready: "true"
    name: $
  spec:
    output:
      to:
        kind: DockerImage
        name: </Provide/an/image/to/be/used/here/$:latest
    pushSecret:
      name: "quay-helloworld-pull-secret"(This needs to be created)
    source:
      contextDir: $
      git:
        ref: $
        uri: $
      type: Git
    strategy:
      sourceStrategy:
        from:
          kind: DockerImage
          name: </Provide/an/image/to/be/used/here/$/httpd-24-rhel7:latest
        forcePull: true
      type: Source
    triggers:
    - type: ImageChange
    - type: ConfigChange
    - gitlab:
        secret: $
      type: GitLab
    - generic:
        secret: $
      type: Generic
- apiVersion: v1
  kind: DeploymentConfig
  metadata:
    annotations:
      description: Defines how to deploy the application server
      template.alpha.openshift.io/wait-for-ready: "true"
    name: $
  spec:
    replicas: 1
    selector:
      name: $
    strategy:
      type: Rolling
    template:
      metadata:
        labels:
          name: $
        name: $
      spec:
        containers:
        - env: []
          image: ' '
          livenessProbe:
        	httpGet:
          	path: /
          	port: 8080
        	initialDelaySeconds: 30
        	timeoutSeconds: 3
          name: httpd-quay-demo
          ports:
          - containerPort: 8080
          readinessProbe:
        	httpGet:
          	path: /
          	port: 8080
        	initialDelaySeconds: 3
        	timeoutSeconds: 3
          resources:
        	limits:
          	memory: $
    triggers:
    - imageChangeParams:
        automatic: true
        containerNames:
        - httpd-quay-demo
        from:
          kind: ImageStreamTag
          name: $:latest
      type: ImageChange
    - type: ConfigChange
parameters:
- description: The name assigned to all of the frontend objects defined in this template.
  displayName: Name
  name: NAME
  required: true
  value: httpd-helloworld-demo
- description: The OpenShift Namespace where the ImageStream resides.
  displayName: Namespace
  name: NAMESPACE
  required: true
  value: quay-helloworld-test
- description: Maximum amount of memory the container can use.
  displayName: Memory Limit
  name: MEMORY_LIMIT
  required: true
  value: 512Mi
- description: The URL of the repository with your application source code.
  displayName: Git Repository URL
  name: SOURCE_REPOSITORY_URL
  required: true
  value: https://github.com/openshift/httpd-ex.git
- description: Set this to a branch name, tag or other ref of your repository if you
    are not using the default branch.
  displayName: Git Reference
  name: SOURCE_REPOSITORY_REF
- description: Set this to the relative path to your project if it is not in the root
    of your repository.
  displayName: Context Directory
  name: CONTEXT_DIR
- description: The exposed hostname that will route to the httpd service, if left
    blank a value will be defaulted.
  displayName: Application Hostname
  name: APPLICATION_DOMAIN
- description: GitLab trigger secret.  A difficult to guess string encoded as part
    of the webhook URL.  Not encrypted.
  displayName: GitLab Webhook Secret
  from: '[a-zA-Z0-9]'
  generate: expression
  name: GITLAB_WEBHOOK_SECRET
- description: A secret string used to configure the Generic webhook.
  displayName: Generic Webhook Secret
  from: '[a-zA-Z0-9]'
  generate: expression
  name: GENERIC_WEBHOOK_SECRET
 

build_content/index.html

Create index.html in the build_content/ directory. We want a custom index.html page to show that we were successful  in our deployment. This Page is what will be displayed when we access our Apache Web server after its been deployed

 

<html>
<header><title>Quay App Demo</title></header>
<body>
Hello <Your name>!
Let's test Quay.
</body>
</html>

 

Create Git repo

Create a new repo in Github using your own Git instance. Open a web browser and login to your Git repo using your credentials. Open the “New” menu at the top of the page, then click “New Project”.  Set Git repo access to private.


Commit files to Git

Commit application template and content to Git. This is an optional step but one that is used to demonstrate having and using a Git repo as part of the CI/CD chain.

Note - In this example, the new Git repo is named “quay-helloworld-demo”.

 

$ git init
$ git add .
$ git commit -a
$ git remote add origin git@gitprd.<your.git.repo>:<username>/quay-helloworld-demo.git
$ git push --set-upstream origin master

 

Create Quay Repository

Create a new Repository and robot account in Quay for this application. The robot account’s credentials will be used by OpenShift to pull/push images to the application’s private repository in Quay. This is for an automated CI/CD pipeline update and publishing using Github. Treat this as a type of Service account.

 

1. 	Quay web UI → Create New Repository → Enter “quay-app-demo” as the repository name → Leave Repository Visibility set to Private → Create Private Repository
2. 	Settings → User and Robot Permissions → Expand drop down menu → Create New Robot Account → Enter name for robot account → Create Robot Account
3. 	Toggle permissions for robot account to “Write” → Add Permission
4. 	Click Robot account name to view Docker login info → Docker Configuration Download ***-auth.json file and rename to config.json (place outside application template/content directory)

 

Pull secrets

Create secret using robot account’s Docker credentials and link to service accounts.

 

Note - The “--for=pull” option should not be used when linking the secret to the builder service account.

 

$ oc create secret generic quay-pull-secret \
    --from-file=".dockerconfigjson=config.json" \
    --type='kubernetes.io/dockerconfigjson' -n quay-app-test
 $ oc secrets link default quay-pull-secret --for=pull -n quay-app-test
$ oc secrets link builder quay-pull-secret -n quay-app-test

 

Git secret

Create secret for accessing GitLab with SSH private key. Add annotation to secret so that the secret will be used automatically by OpenShift when a build in this project references ssh://gitprd.ssc.tsc/*

 

Important - Modify path to ssh-privatekey as required so that it references private key used to access Git repo.

 

$ oc create secret generic gitlab-auth \
    --from-file=ssh-privatekey=~/.ssh/id_rsa \
    --type=kubernetes.io/ssh-auth \
    -n quay-app-test
$ oc annotate secret gitlab-auth \
    'build.openshift.io/source-secret-match-uri-1=\
ssh://gitprd.ssc.tsc/*' --overwrite=true -n quay-app-test

 

Deploy application

 

# Generate 2 different long random string of characters using the openssl command.
$ openssl rand -base64 40
$ openssl rand -base64 40
$ oc process -f httpd-quay-demo.yml \
 -p=SOURCE_REPOSITORY_URL=ssh://git@gitprd.ssc.tsc/<repo/project>/quay-app-demo.git \
	-p=GITLAB_WEBHOOK_SECRET=<1st long random string of characters> \
	-p=GENERIC_WEBHOOK_SECRET=<2nd long random string of characters> \
	-p=CONTEXT_DIR="build_content" \
	| oc create -n quay-app-test -f -

 

Configure webhook in Gitlab

Configure webhook integration for the application repo in the Gitlab web UI. Value of GITLAB_WEBHOOK_SECRET must match value used when deploying the application in the previous step.

 

GitLab Repo --> Settings --> Integrations
Set URL = https://master.ocp-dev01-bna.ssc.tsc:443/apis/build.openshift.io/v1/namespaces/\
quay-app-test/buildconfigs/httpd-quay-demo/webhooks/<GITLAB_WEBHOOK_SECRET>/gitlab
Secret Token = (leave blank)
Trigger: Push events
Uncheck "Enable SSL verification"
Click "Add webhook"

 

Application builds

Manually start build

A build can be manually initiated using the c start-build command.

 

$ oc start-build httpd-quay-demo -n quay-app-test

 

Rebuild application image

OpenShift will automatically rebuild the application container image anytime a new commit and push is made to the application’s Git repo.

 

# Update content
$ vi quay-app-demo/build_content/index.html
(update and save file)

 

# Note: If files are added, renamed, or moved, run “git add .”, then commit.


$ git commit -a
$ git push

 
Deploy new application image

Assuming that a new application container image has been built, it can be deployed by using oc import-image

 

$ oc import-image httpd-quay-demo -n quay-app-test


Conclusion

Congratulations on your deployment of the Red Hat Quay servers using Clair for image scanning. This software was designed to be integrated into the OpenShift platform. By following some of our examples and suggestions, you have experienced what resources are required to deploy Quay. You have learned how we deploy Quay and Clair as projects in the OCP environment. You have also been given the challenge of deploying a “HelloWorld” application using your Quay environment. While there are other ways of deploying Quay, hopefully this exercise gave you some insight for planning your supported Enterprise solution.

As containers pave the way forward for ever faster and more productive DevOps, more refined and comprehensive toolkits are required to keep environments secure. Lack of container image security leaves vulnerabilities which can be exploited, but failing to implement containers can significantly cripple an organization’s ability to move forward with high speed development and rollouts.

According to the Tripwire's 2019 The State of Container Security Report, 42% of respondents said they were delaying container adoption due to security concerns, and 82% said they were considering restructuring security responsibilities and controls to prepare for container adoption or address existing issues with container security.

However, with a combination of Red Hat Quay and Clair, you can effectively secure outside container content and prevent security vulnerabilities while driving development forward in an agile environment.


About the Author

Mike McDonough is a Senior Red Hat Architect with Stone Door Group and is responsible for designing and blueprinting OpenShift and Ansible architectures for Fortune 500 customers spanning a broad array of Red Hat technologies. With a 25+ year career in enterprise IT, Mike has deep experience in cloud architecture, automation, performance tuning and information security. Stone Door Group offers rapid adoption of Red Hat cloud technologies with their OpenShift Container Platform Accelerator. To speak to Mike, drop us a line at letsdothis@stonedoorgroup.com

References:

https://containerjournal.com/2019/03/22/the-4-most-vulnerable-areas-of-container-security-in-2019/

https://www.gartner.com/smarterwithgartner/gartner-top-10-security-projects-for-2019/

https://access.redhat.com/documentation/en-us/red_hat_quay/2.9/html-single/deploy_red_hat_quay_on_openshift/index

https://access.redhat.com/solutions/3533201

https://github.com/VeerMuchandi/QuayOnOpenShift

https://access.redhat.com/RegistryAuthentication

https://www.tripwire.com/state-of-security/devops/organizations-container-security-incident/