Chapter 6. Appendix A: Red Hat Quay on OpenShift configuration files

The following yaml files were created to deploy Red Hat Quay on OpenShift. They are used throughout the deployment procedure in this document. We recommend you copy the files from this document into a directory, review the contents, and make any changes necessary for your deployment.

6.1. Red Hat Quay namespaces and secrets

quay-enterprise-namespace.yaml

apiVersion: v1
kind: Namespace 1
metadata:
  name: quay-enterprise 2

1
Identifies the Kind as Namespace
2
Namespace is set to quay-enterprise throughout the yaml files

quay-enterprise-config-secret.yaml

apiVersion: v1
kind: Secret
metadata:
  namespace: quay-enterprise
  name: quay-enterprise-config-secret

quay-enterprise-redhat-pull-secret.yaml

apiVersion: v1
kind: Secret
metadata:
  namespace: quay-enterprise
  name: redhat-pull-secret
data:
  .dockerconfigjson: <Add credentials> 1
type: kubernetes.io/dockerconfigjson

1
Change <Add credentials> to include the credentials shown from Accessing Red Hat Quay

6.2. Red Hat Quay storage

quay-storageclass.yaml

  apiVersion: storage.k8s.io/v1
  kind: StorageClass
  metadata:
    name: quay-storageclass
  parameters: 1
    type: gp2
  provisioner: kubernetes.io/aws-ebs
  reclaimPolicy: Delete

1
To encrypt the volume, add this to the parameters section (optionally replacing xfs with another filesystem type):
     encrypted: "true"
     fsType: xfs (or other fs)
     kmsKeyId:

6.3. Red Hat Quay database

db-pvc.yaml

  apiVersion: v1
  kind: PersistentVolumeClaim
  metadata:
    name: postgres-storage
    namespace: quay-enterprise
  spec:
    accessModes:
      - ReadWriteOnce
    volumeMode: Filesystem
    resources:
      requests:
        storage: 5Gi 1
    storageClassName: quay-storageclass

1
The 5Gi creates 5 gigabytes of storage for use by the Postgres database.

postgres-deployment.yaml

  apiVersion: extensions/v1beta1
  kind: Deployment
  metadata:
    name: postgres
    namespace: quay-enterprise
  spec:
    replicas: 1 1
    template:
      metadata:
        labels:
          app: postgres
      spec:
        containers:
          - name: postgres
            image: registry.access.redhat.com/rhscl/postgresql-10-rhel7:1-35
            imagePullPolicy: "IfNotPresent"
            ports:
              - containerPort: 5432
            env:
            - name: POSTGRESQL_USER
              value: "username" 2
            - name: POSTGRESQL_DATABASE
              value: "quay"
            - name: POSTGRESQL_PASSWORD
              value: "password" 3
            volumeMounts:
              - mountPath: /var/lib/pgsql/data
                name: postgredb
            serviceAccount: postgres
            serviceAccountName: postgres
        volumes:
          - name: postgredb
            persistentVolumeClaim:
              claimName: postgres-storage

1
Only one instance of the postgres database is defined here. Adjust replicas based on demand.
2
Replace "username" with a name for your Postgres user
3
Replace "password" with a password for your Postgres user

postgres-service.yaml

apiVersion: v1
kind: Service
metadata:
  name: postgres
  namespace: quay-enterprise
  labels:
    app: postgres
spec:
  type: NodePort
  ports:
   - port: 5432
  selector:
   app: postgres

6.4. Red Hat Quay authorization

quay-servicetoken-role-k8s1-6.yaml

apiVersion: rbac.authorization.k8s.io/v1beta1
kind: Role
metadata:
  name: quay-enterprise-serviceaccount
  namespace: quay-enterprise
rules:
- apiGroups:
  - ""
  resources:
  - secrets
  verbs:
  - get
  - put
  - patch
  - update
- apiGroups:
  - ""
  resources:
  - namespaces
  verbs:
  - get
- apiGroups:
  - extensions
  - apps
  resources:
  - deployments
  verbs:
  - get
  - list
  - patch
  - update
  - watch

quay-servicetoken-role-binding-k8s1-6.yaml

apiVersion: rbac.authorization.k8s.io/v1beta1
kind: RoleBinding
metadata:
  name: quay-enterprise-secret-writer
  namespace: quay-enterprise
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: quay-enterprise-serviceaccount
subjects:
- kind: ServiceAccount
  name: default

6.5. Redis database

quay-enterprise-redis.yaml

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  namespace: quay-enterprise
  name: quay-enterprise-redis
  labels:
    quay-enterprise-component: redis
spec:
  replicas: 1 1
  selector:
    matchLabels:
      quay-enterprise-component: redis
  template:
    metadata:
      namespace: quay-enterprise
      labels:
        quay-enterprise-component: redis
    spec:
      containers:
      - name: redis-master
        image: registry.access.redhat.com/rhscl/redis-32-rhel7
        imagePullPolicy: "IfNotPresent"
        ports:
        - containerPort: 6379
---
apiVersion: v1
kind: Service
metadata:
  namespace: quay-enterprise
  name: quay-enterprise-redis
  labels:
    quay-enterprise-component: redis
spec:
  ports:
    - port: 6379
  selector:
    quay-enterprise-component: redis

1
Only one instance of the redis database is defined here. Adjust replicas based on demand.

6.6. Red Hat Quay configuration pod

quay-enterprise-config.yaml

  apiVersion: extensions/v1beta1
  kind: Deployment
  metadata:
    namespace: quay-enterprise
    name: quay-enterprise-config-app
    labels:
      quay-enterprise-component: config-app
  spec:
    replicas: 1
    selector:
      matchLabels:
        quay-enterprise-component: config-app
    template:
      metadata:
        namespace: quay-enterprise
        labels:
          quay-enterprise-component: config-app
      spec:
        containers:
        - name: quay-enterprise-config-app
          image: quay.io/redhat/quay:v3.3.4
          ports:
          - containerPort: 8443
          command: ["/quay-registry/quay-entrypoint.sh"]
          args: ["config", "secret"]
        imagePullSecrets:
          - name: redhat-pull-secret

quay-enterprise-config-service-clusterip.yaml

  apiVersion: v1
  kind: Service
  metadata:
    namespace: quay-enterprise
    name: quay-enterprise-config
  spec:
    type: ClusterIP
    ports:
      - protocol: TCP
        name: https
        port: 443
        targetPort: 8443
    selector:
      quay-enterprise-component: config-app

quay-enterprise-config-route.yaml

  apiVersion: v1
  kind: Route
  metadata:
    name: quay-enterprise-config
    namespace: quay-enterprise
  spec:
    to:
      kind: Service
      name: quay-enterprise-config
    tls:
      termination: passthrough

6.7. Red Hat Quay application container

quay-enterprise-service-clusterip.yaml

  apiVersion: v1
  kind: Service
  metadata:
    namespace: quay-enterprise
    name: quay-enterprise-clusterip
  spec:
    type: ClusterIP
    ports:
      - protocol: TCP
        name: https
        port: 443
        targetPort: 8443
    selector:
      quay-enterprise-component: app

quay-enterprise-app-route.yaml

apiVersion: v1
kind: Route
metadata:
  name: quay-enterprise
  namespace: quay-enterprise
spec:
  to:
    kind: Service
    name: quay-enterprise-clusterip
  tls:
    termination: passthrough

quay-enterprise-app-rc.yaml

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  namespace: quay-enterprise
  name: quay-enterprise-app
  labels:
    quay-enterprise-component: app
spec:
  replicas: 1 1
  selector:
    matchLabels:
      quay-enterprise-component: app
  template:
    metadata:
      namespace: quay-enterprise
      labels:
        quay-enterprise-component: app
    spec:
      volumes:
        - name: configvolume
          secret:
            secretName: quay-enterprise-config-secret
      containers:
      - name: quay-enterprise-app
        image: quay.io/redhat/quay:v3.3.4
        ports:
        - containerPort: 8443
        volumeMounts:
        - name: configvolume
          readOnly: false
          mountPath: /conf/stack
      imagePullSecrets:
        - name: redhat-pull-secret

1
Only one instance of the quay container is defined here. Adjust replicas based on demand.

6.8. Clair image scanning

postgres-clair-storage.yaml

  apiVersion: v1
  kind: PersistentVolumeClaim
  metadata:
    name: postgres-clair-storage
    namespace: quay-enterprise
  spec:
    accessModes:
    - ReadWriteOnce
    resources:
      requests:
        storage: 5Gi
    storageClassName: quay-storageclass

postgres-clair-deployment.yaml

  apiVersion: extensions/v1beta1
  kind: Deployment
  metadata:
    labels:
      app: postgres-clair
    name: postgres-clair
    namespace: quay-enterprise
  spec:
    replicas: 1
    selector:
      matchLabels:
        app: postgres-clair
    template:
      metadata:
        labels:
          app: postgres-clair
      spec:
        containers:
        - env:
          - name: POSTGRESQL_USER
            value: clair 1
          - name: POSTGRESQL_DATABASE
            value: clair 2
          - name: POSTGRESQL_PASSWORD
            value: test123 3
          image: registry.access.redhat.com/rhscl/postgresql-10-rhel7:1-35
          imagePullPolicy: IfNotPresent
          name: postgres-clair
          ports:
          - containerPort: 5432
            protocol: TCP
          volumeMounts:
          - mountPath: /var/lib/pgsql/data
            name: postgredb
          serviceAccount: postgres
          serviceAccountName: postgres
        volumes:
        - name: postgredb
          persistentVolumeClaim:
            claimName: postgres-clair-storage

1
Set the username for the Clair postgres database (clair by default)
2
Set the name of the Clair postgres database
3
Set the password for the Clair postgress user

postgres-clair-service.yaml

  apiVersion: v1
  kind: Service
  metadata:
    labels:
      app: postgres-clair
    name: postgres-clair
    namespace: quay-enterprise
  spec:
    ports:
    - nodePort: 30680
      port: 5432
      protocol: TCP
      targetPort: 5432
    selector:
      app: postgres-clair
    type: NodePort

clair-config.yaml

Modify source, endpoint, key_id, and registry settings to match your environment.

  clair:
    database:
      type: pgsql
      options:
        source: host=172.30.87.93 port=5432 dbname=clair user=clair password=test123 sslmode=disable
        cachesize: 16384
    api:
      # The port at which Clair will report its health status. For example, if Clair is running at
      # https://clair.mycompany.com, the health will be reported at
      # http://clair.mycompany.com:6061/health.
      healthport: 6061

      port: 6062
      timeout: 900s

      # paginationkey can be any random set of characters. *Must be the same across all Clair
      # instances*.
      paginationkey: "XxoPtCUzrUv4JV5dS+yQ+MdW7yLEJnRMwigVY/bpgtQ="

    updater:
      # interval defines how often Clair will check for updates from its upstream vulnerability databases.
      interval: 6h
    notifier:
      attempts: 3
      renotifyinterval: 1h
      http:
        # QUAY_ENDPOINT defines the endpoint at which Quay Enterprise is running.
        # For example: https://myregistry.mycompany.com
        endpoint: https://quay-enterprise.apps.lzha0413.qe.devcluster.openshift.com/secscan/notify 1
        proxy: http://localhost:6063

  jwtproxy:
    signer_proxy:
      enabled: true
      listen_addr: :6063
      ca_key_file: /certificates/mitm.key # Generated internally, do not change.
      ca_crt_file: /certificates/mitm.crt # Generated internally, do not change.
      signer:
        issuer: security_scanner
        expiration_time: 5m
        max_skew: 1m
        nonce_length: 32
        private_key:
          type: preshared
          options:
            # The ID of the service key generated for Clair. The ID is returned when setting up
            # the key in [Quay Enterprise Setup](security-scanning.md)
            key_id: fc6c2b02c495c9b8fc674fcdbfdd2058f2f559d6bdd19d0ba70af26c0cb66a48 2
            private_key_path: /clair/config/security_scanner.pem

    verifier_proxies:
    - enabled: true
      # The port at which Clair will listen.
      listen_addr: :6060

      # If Clair is to be served via TLS, uncomment these lines. See the "Running Clair under TLS"
      # section below for more information.
      # key_file: /config/clair.key
      # crt_file: /config/clair.crt

      verifier:
        # CLAIR_ENDPOINT is the endpoint at which this Clair will be accessible. Note that the port
        # specified here must match the listen_addr port a few lines above this.
        # Example: https://myclair.mycompany.com:6060
        audience: http://clair-service:6060

        upstream: http://localhost:6062
        key_server:
          type: keyregistry
          options:
            # QUAY_ENDPOINT defines the endpoint at which Quay Enterprise is running.
            # Example: https://myregistry.mycompany.com
            registry: https://quay-enterprise.apps.lzha0413.qe.devcluster.openshift.com/keys/
1
Check that the database options match those set earlier in postgres-clair-deployment.yaml.
2
Insert the Key ID matches the value from the key generated from the Red Hat Quay Setup screen.

clair-service.yaml

  apiVersion: v1
  kind: Service
  metadata:
    name: clair-service
    namespace: quay-enterprise
  spec:
    ports:
    - name: clair-api
      port: 6060
      protocol: TCP
      targetPort: 6060
    - name: clair-health
      port: 6061
      protocol: TCP
      targetPort: 6061
    selector:
      quay-enterprise-component: clair-scanner
    type: ClusterIP

clair-deployment.yaml

  apiVersion: extensions/v1beta1
  kind: Deployment
  metadata:
    labels:
      quay-enterprise-component: clair-scanner
    name: clair-scanner
    namespace: quay-enterprise
  spec:
    replicas: 1
    selector:
      matchLabels:
        quay-enterprise-component: clair-scanner
    template:
      metadata:
        labels:
          quay-enterprise-component: clair-scanner
        namespace: quay-enterprise
      spec:
        containers:
        - image: quay.io/redhat/clair-jwt:v3.3.4
          imagePullPolicy: IfNotPresent
          name: clair-scanner
          ports:
          - containerPort: 6060
            name: clair-api
            protocol: TCP
          - containerPort: 6061
            name: clair-health
            protocol: TCP
          volumeMounts:
          - mountPath: /clair/config
            name: configvolume
          - mountPath: /etc/pki/ca-trust/source/anchors/ca.crt
            name: quay-ssl
            subPath: ca.crt
        imagePullSecrets:
        - name: redhat-pull-secret
        restartPolicy: Always
        volumes:
        - name: configvolume
          secret:
            secretName: clair-scanner-config-secret
        - name: quay-ssl
          secret:
            defaultMode: 420
            items:
            - key: ssl.cert
              path: ca.crt
            secretName: quay-enterprise-config-secret

6.9. Repository mirroring

quay-enterprise-mirror.yaml

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  namespace: quay-enterprise
  name: quay-enterprise-mirror
  labels:
    quay-enterprise-component: mirror-app
spec:
  replicas: 1
  selector:
    matchLabels:
      quay-enterprise-component: mirror-app
  template:
    metadata:
      namespace: quay-enterprise
      labels:
        quay-enterprise-component: mirror-app
    spec:
      volumes:
      - name: configvolume
        secret:
          secretName: quay-enterprise-config-secret
      containers:
      - name: quay-enterprise-mirror-app
        image: quay.io/redhat/quay:v3.3.4
        ports:
        - containerPort: 8443
        volumeMounts:
        - name: configvolume
          readOnly: false
          mountPath: /conf/stack
        command: ["/quay-registry/quay-entrypoint.sh"]
        args: ["repomirror"]
      imagePullSecrets:
        - name: redhat-pull-secret

Additional resources