Introduction 

This document is intended to provide an overview on configuring Aqua Enterprise for SSL and GRPC with mutual authentication using your own self-signed certificates for secure communication.


By defaultAqua generates and provides a pair of self-signed certificates for the Aqua console and gateway which enables end to end encryption for HTTP and gRPC communications.
 
Once you’ve completed the deployment steps documented below, you will have:  

  • Configured SSL communication to the Postgres database
  • Configured mutual authentication between console/gateway and gateway/enforcer 
  • Configured the console for SSL
  • Configured the scanner for SSL communication with the console 



Pre-Requisites 

You will need: 

  • Docker or Kubernetes environment and cluster to deploy Aqua into Knowledge of PKI and OpenSSL certificate creation.
  • The ability to create and access a hosted Postgres DB- either on-prem, cloud (Azure DBaaS, GKE Cloud DB, AWS RDS PostgresDB).
  • An understanding of your existing cloud environment  internal and external ingress points for remote gateways.

 


1. SSL Connection to Postgres


As a best practice, the console and gateway should utilize secure connections to the Aqua databasesThis can be accomplished with or without client certificates depending on cloud platform DBaaS or Postgres configuration.



Option 1 – Connect without client certificates


In most cases, we can simply set the SCALOCK_DBSSL and SCALOCK_AUDIT_DBSSL environment variables to enable Postgres  SSL mode to requirewhich ensures encrypted connections.  

 
a.  Include the following, and optional AQUA_PUBSUB_DBSSL environment variable, for active-active server mode, in both the console and gateway deployments: 

        env: 

        - name: SCALOCK_DBSSL 

          value: "require" 

        - name: SCALOCK_AUDIT_DBSSL 

          value: "require" 

  - name: AQUA_PUBSUB_DBSSL 

    Value: "require" 


b.  Validate the sslmode and database connection from the console and gateway logs: 

        

kubectl logs -f <aqua console pod id> -n aqua 

 

INFOHost [aquadb-ssl.postgres.database.azure.com] database [scalock] connection established successfully with SSL mode [require] 

INFOLocking the postgres database... 

INFOChecking postgres version... 

 

Option 2 – Connect with client certificates 


For a more secure connection, we can utilize client certificates with ‘verify-ca’ and ‘verify-full’ sslmodes to verify the server’s certificate and match the hostname to the certificate’s Common Name (CN) or Subject Alternate Name (SAN). The client certificates need to be mounted to /etc/ssl/certs directory inside the console and gateway. Here we will use Azure Database for PostgreSQL as an example. 


a.  From the Azure Database for PostgreSQL server, select Connection security and toggle Enforce SSL connection to ENABLED: 


 A screenshot of a cell phone screen with text

Description automatically generated 

 


b.  Download the Azure Database for PostgreSQL certificate from the following: 
https://www.digicert.com/CACerts/BaltimoreCyberTrustRoot.crt.pem 

c.  Add the certificate to Kubernetes as a secret: 


kubectl create secret generic aqua-db-cert \ 

  --from-file=<path>/BaltimoreCyberTrustRoot.crt.pem 

 

d.  Update the console and gateway deployments to include the secret as a volume and volumeMount to /etc/ssl/certs: 

 

            volumeMounts: 

            - mountPath: /var/run/docker.sock 

              name: docker-socket-mount 

            - name: db-cert 

              mountPath: /etc/ssl/certs 

              readOnly: true 

            ports: 

            - containerPort: 8080 

            - containerPort: 8443 

          volumes: 

          - name: docker-socket-mount 

            hostPath: 

              path: /var/run/docker.sock 

          - name: db-cert 

            secret: 

              secretName: aqua-db-cert 

 

e.  Update both deployments with the appropriate sslmode, for Azure Database for PostgreSQL we will use verify-full: 

 

        env: 

        - name: SCALOCK_DBSSL 

          value: "verify-full" 

        - name: SCALOCK_AUDIT_DBSSL 

          value: "verify-full" 

  - name: AQUA_PUBSUB_DBSSL 

    Value: "verify-full" 

 

f.  We can validate the sslmode and database connection from the console and gateway logs: 

        

kubectl logs -f <aqua console pod id> -n aqua 

 

INFOHost [aquadb-ssl.postgres.database.azure.com] database [scalock] connection established successfully with SSL mode [verify-full] 

INFOLocking the postgres database... 

INFOChecking postgres version... 

 

 


2. GRPC Mutual Authentication


Aqua supports GRPC communication protocol between the console and gateway as well as the gateway and enforcer which is encrypted by default. For additional security, we can configure GRPC with mutual authentication between components using our own certificates. Each component receives its own RSA certificate based on the same root CA. Here we will use this gist as a reference with Openssl: 
 
 

a.  Create the root key and certificate: 

  

        openssl genrsa -des3 -out rootCA.key 4096 

        # openssl req -x509 -new -nodes -key rootCA.key -sha256 -days 1024 

          -out rootCA.crt 

 

b.  Create the keys for the console, gateway and enforcer: 

        openssl genrsa -out aqua-web_443.key

        # openssl genrsa -out aqua-gateway.key

        # openssl genrsa -out aqua-enforcer.key

 

c.  Create a CSR for the console. Specify the public FQDN for the CN (e.g. aqua-web.yourdomain.com) for gRPC when prompted by the following openssl command. Also include the service name only (e.g. aqua-web) for local scanners as well as any other DNS names or IP addresses that will be used to access the console as alt_names in the config file. 


openssl req -new -sha256 -key aqua-web_443.key -out aqua-web_443.csr -config req.conf 

 

# cat req.conf 

 

req ] 

default_bits       = 2048 

distinguished_name = req_distinguished_name 

req_extensions     = req_ext 

req_distinguished_name ] 

countryName                 = Country Name (2 letter code) 

stateOrProvinceName         = State or Province Name (full name) 

localityName               = Locality Name (eg, city) 

organizationName           = Organization Name (eg, company) 

commonName                 = Common Name (e.g. server FQDN or YOUR name) 

req_ext ] 

subjectAltName = @alt_names 

[alt_names] 

DNS.1   = <console public DNS> [optional] 

URI.1   = aqua-web:443 

URI.2   = <Internal/External FQDN:8443 of GRPC Ingress Point> #optional for remote gateways to connect 

DNS.2   = aqua-web       <required for Scanners> 

  

 

 

Note A: If your organization uses a PKI & certificate generation platform that does not use OpenSSL, and you are not familiar with how DNS/URI/IP combinations can be used as SANS we recommend that you use RSA PKCS1 certificates with DNS Names.  The gateway requires the URI. 

We also support PKCS8 certificates with DNS names, URI and IP format if using an alternative certificate configuration. 

Please refer to the OpenSSL doc for more detailed information: https://www.openssl.org/docs/man1.0.2/man5/x509v3_config.html#Subject-Alternative-Name 

Here is an explanation of the DNS1..entries in the certificate req.conf file above: 

  • DNS.1 – This can be used for optional DNS or IP address that is used for the ingress (either in docker or a K8s public ingress gateway). See Note B below. 
  • URI.1  This is the K8s service name for the Aqua Console. In most situations aqua-web:443 is standard. This will be used for Aqua Gateways in the console’s cluster to communicate via gRPC. See Note B below. 
  • URI.2 – This can be used for the remote gateway connection outside of the consoles cluster – this is optional. 
  • DNS.2 – This is used for Aqua Scanners deployed locally in the console’s cluster to communicate over https. There is no requirement to specify the port number in this case. 


If req.conf does not include the correct DNS/URI names, you may see an error like the following in the gateway or scanner logs:


2020-09-10 16:51:02.862    ERROR    grpcserver/consoleclient.go:172    Failed connecting to management console    {"attempt": 1, "sleep": "1s", "error": "failed creating stream: rpc error: code = Unavailable desc = all SubConns are in TransientFailurelatest connection error: connection error: desc = \"transport: authentication handshake failed: x509: certificate is valid for 51.105.240.174, 172.18.0.2:443, 172.18.0.2, not 172.18.0.2:8443\"",


You will need to correct the req.confrebuild the server certificate with the secret used by the console, and restart the gateway component.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      


Note BIf you are deploying on docker without orchestration, you will likely need to use the local docker network assigned IP or local hostname of your aqua console container or VM.


If you are unfamiliar with Subject Alternate Names please refer to this Wikipedia article -  there are also other documentation references available on the internet. 
 


d.  Create a CSR for the gateway. If only enforcers in the gateway’s cluster will be connecting we can create the CSR for the gateway and enforcer using service name similar to the following: 


openssl req -new -sha256 -key aqua-gateway.key -subj "/C=US/ST=MA/O=aqua/CN=aqua-gateway" -out aqua-gateway.csr 

openssl req -new -sha256 -key aqua-enforcer.key -subj "/C=US/ST=MA/O=aqua/CN=aqua-agent" -out aqua-enforcer.csr 

 

 

If there are multiple clusters and enforcers will connect to the gateway using both the Kubernetes service name and exposed DNS name, we will need to create the gateway CSR using SANs as we did for the console in step c. above. Specify the service name (e.g. aqua-gateway) for the CN in the following command: 

 

openssl req -new -sha256 -key aqua-gateway.key -out aqua-gateway.csr -config gateway.conf 

 

# cat gateway.conf 

 

req ] 

default_bits       = 2048 

distinguished_name = req_distinguished_name 

req_extensions     = req_ext 

req_distinguished_name ] 

countryName                 = Country Name (2 letter code) 

stateOrProvinceName         = State or Province Name (full name) 

localityName               = Locality Name (eg, city) 

organizationName           = Organization Name (eg, company) 

commonName                 = Common Name (e.g. server FQDN or YOUR name) 

req_ext ] 

subjectAltName = @alt_names 

[alt_names] 

DNS.1   = aqua-gateway 

DNS.2   = <gateway public DNS> 

 

 

e. Create the certificates signed by the root certificate and key created in step a. above: 

 

    I.  Console with SANs: 


openssl x509 -req -in aqua-web_443.csr -CA rootCA.crt -CAkey rootCA.key -CAcreateserial -out aqua-web_443.crt -days 500 -sha256 -extensions req_ext -extfile req.conf 

 

    II.  Gateway without SANs: 


openssl x509 -req -in aqua-gateway.csr -CA rootCA.crt -CAkey rootCA.key -CAcreateserial -out aqua-gateway.crt -days 500 -sha256 

 

    III.  Gateway with SANs: 


openssl x509 -req -in aqua-gateway.csr -CA rootCA.crt -CAkey rootCA.key -CAcreateserial -out aqua-gateway.crt -days 500 -sha256 -extensions req_ext -extfile gateway.conf 


    IV.  Enforcer: 


openssl x509 -req -in aqua-enforcer.csr -CA rootCA.crt -CAkey rootCA.key -CAcreateserial -out aqua-enforcer.crt -days 500 -sha256 

 

 

f.  Optionally validate the certificate is issued by our rootCA, the CN and any SANs: 

        openssl x509 -in aqua-web_443.crt -text -noout 

 

A screenshot of a cell phone

Description automatically generated 

 

g.  Create Kubernetes secrets for each of the components, including the key, certificate and rootCA: 


kubectl create secret generic aqua-grpc-web \ 

--from-file=/home/rootCA.crt \ 

--from-file=/home/aqua-web_443.crt \ 

--from-file=/home/aqua-web_443.key 

 

kubectl create secret generic aqua-grpc-gateway \ 

--from-file=/home/rootCA.crt \ 

--from-file=/home/aqua-gateway.crt \ 

--from-file=/home/aqua-gateway.key 

 

kubectl create secret generic aqua-grpc-enforcer \ 

--from-file=/home/rootCA.crt \ 

--from-file=/home/aqua-enforcer.crt \ 

--from-file=/home/aqua-enforcer.key 

 

h.  Add the aqua-grpc-web secret as a volume and volumeMount in the console deployment along with the AQUA_PUBLIC_KEY, AQUA_PRIVATE_KEY and AQUA_ROOT_CA environment variables: 

        

        env: 

        - name: AQUA_PUBLIC_KEY 

          value: "/opt/aquasec/ssl/aqua-web_443.crt" 

        - name: AQUA_PRIVATE_KEY 

          value: "/opt/aquasec/ssl/aqua-web_443.key" 

        - name: AQUA_ROOT_CA 

          value: "/opt/aquasec/ssl/rootCA.crt" 

        volumeMounts: 

        - mountPath: /var/run/docker.sock 

          name: docker-socket-mount 

        - mountPath: /opt/aquasec/ssl 

          name: mutual-auth-keys 

          readOnly: true 

        ports: 

        - containerPort: 8080 

        - containerPort: 8443 

      volumes: 

      - name: docker-socket-mount 

        hostPath: 

          path: /var/run/docker.sock 

      - name: mutual-auth-keys 

        secret: 

          secretName: aqua-grpc-web 

 

i.  Repeat for the gateway deployment and include AQUA_VERIFY_ENFORCER environment variable: 

  

        env: 

        - name: AQUA_PUBLIC_KEY 

          value: "/opt/aquasec/ssl/aqua-gateway.crt" 

        - name: AQUA_PRIVATE_KEY 

          value: "/opt/aquasec/ssl/aqua-gateway.key" 

        - name: AQUA_ROOT_CA 

          value: "/opt/aquasec/ssl/rootCA.crt" 

        - name: AQUA_VERIFY_ENFORCER 

          value: "1" 

        ports: 

        - containerPort: 8443 

        volumeMounts: 

        - mountPath: /opt/aquasec/ssl 

          name: mutual-auth-keys 

      volumes: 

      - name: mutual-auth-keys 

        secret: 

          secretName: aqua-grpc-gateway 

 

j.  Repeat for the enforcer daemonset: 

         

        env:  

        - name: AQUA_PUBLIC_KEY 

          value: "/opt/aquasec/ssl/aqua-enforcer.crt 

        - name: AQUA_PRIVATE_KEY 

          value: "/opt/aquasec/ssl/aqua-enforcer.key 

        - name: AQUA_ROOT_CA 

          value: "/opt/aquasec/ssl/rootCA.crt 

        - name: AQUA_GRPC_ONLY_MODE 

          value: "1" 

        volumeMounts: 

        - mountPath: /opt/aquasec/ssl 

          name: mutual-auth-keys 

      volumes: 

      - name: mutual-auth-keys 

        secret: 

          secretName: aqua-grpc-enforcer 

 

 

3. Scanner SSL Connection to Console 


The scanners require a connection to the console to pull registry details as well as to post scan results. This can use HTTP/HTTPS without the console’s certificate (e.g. --no-verify flag) or HTTPS with the console’s certificate. The console’s certificate or root certificate needs to be mounted to /etc/ssl/certs directory inside the scanner. 

a.  Create a Kubernetes secret using the console’s public certificate created in step 2. e. I. (e.g. aqua-web_443.crt): 

kubectl create secret generic aqua-scanner \ 

  --from-file=/home/aqua-web_443.crt 

 

b.  Update the scanner deployment to include the aqua-scanner secret as a volume and volumeMount to /etc/ssl/certs: 


        volumeMounts: 

        - mountPath: "/etc/ssl/certs" 

          name: aqua-scanner 

          readOnly: true 

      volumes: 

      - name: aqua-scanner 

        secret: 

          secretNameaqua-scanner 

 

 c. Update the –host argument to point to the HTTPS address of the console (e.g. https://aqua-web:443): 

      

containers: 

      - name: aqua-scanner 

        image: registry.aquasec.com/scanner:5.0.20181 

        args: ["daemon", "--host", "https://aqua-web:443", "--user", "scanner", "--password", "mypassword"] 

 

  

If you have any questions or require any assistance, please always feel free to contact your Aqua account representative or the Aqua Customer Success team at https://support.aquasec.com or support@aquasec.com