2

I'm using DDEV 1.8.0.

I run Behat tests using justinribeiro/chrome-headless. It's included in my DDEV project via an additional Docker Compose file called docker-compose.chrome.yaml. These are the contents:

---
version: '3.6'
services:
  chrome:
    image: justinribeiro/chrome-headless
    restart: unless-stopped
    container_name: ddev-${DDEV_SITENAME}-chrome
    labels:
      com.ddev.site-name: ${DDEV_SITENAME}
      com.ddev.approot: $DDEV_APPROOT
      com.ddev.app-url: $DDEV_URL
    volumes:
      - ddev-global-cache:/mnt/ddev-global-cache
    external_links:
      - "ddev-router:fps.ddev.local"
      - "ddev-router:dev.fillpdf-service.com"
    cap_add:
      - SYS_ADMIN
    ports:
      - '9222:9222'
networks:
  default:
    external:
      name: ddev_default

The chrome service does not contain mkcert, so Behat tests that use this container fail, as the headless Chrome instance rejects the SSL certificate. How can I get both Chrome and cURL/wget to recognize certificates issued by the mkcert CA?

wizonesolutions
  • 577
  • 3
  • 15

1 Answers1

7

It's fairly straightforward to get mkcert working in a third-party container. Getting it working with headless Chrome, Chromium, Firefox, and other browsers that don't use the system store in Linux is a little less intuitive.

First, let's look at the changes we have to make to get everything to work. I'm including brief explanations under each file where necessary. I will explain them together at the end.

.ddev/docker-compose.chrome.yaml:

---
version: '3.6'
services:
  chrome:
    build:
      context: ./chrome-build
    restart: unless-stopped
    container_name: ddev-${DDEV_SITENAME}-chrome
    labels:
      com.ddev.site-name: ${DDEV_SITENAME}
      com.ddev.approot: $DDEV_APPROOT
    volumes:
      - ddev-global-cache:/mnt/ddev-global-cache
    external_links:
      - "ddev-router:fps.ddev.local"
      - "ddev-router:dev.fillpdf-service.com"
    cap_add:
      - SYS_ADMIN
    ports:
      - '9222:9222'
networks:
  default:
    external:
      name: ddev_default

We changed the image option to build, and we are pointing it a new folder we're about to create.

New folder: .ddev/chrome-build/Dockerfile

.ddev/chrome-build/Dockerfile

FROM justinribeiro/chrome-headless
ADD chrome-startup.sh /

ENV MKCERT_VERSION=v1.3.0
USER root
RUN apt-get update && apt-get install -y curl openssl libnss3-tools && curl -sSL https://github.com/FiloSottile/mkcert/releases/download/$MKCERT_VERSION/mkcert-$MKCERT_VERSION-linux-amd64 -o /usr/local/bin/mkcert && chmod +x /usr/local/bin/mkcert && apt-get purge -y curl && apt-get clean && chmod +x /chrome-startup.sh
ENTRYPOINT [ "/chrome-startup.sh" ]

.ddev/chrome-build/chrome-startup.sh

#!/bin/bash
if [[ ! -f /.mkcert-configured ]]; then
  #!/usr/bin/env bash
  # Install for root (system).
  CAROOT="/mnt/ddev-global-cache/mkcert" mkcert -install

  # Install for user: chrome.
  su chrome -c 'mkdir -p $HOME/.local/share/mkcert'
  cp -R /mnt/ddev-global-cache/mkcert/* /home/chrome/.local/share/mkcert/
  chown -R chrome: /home/chrome/.local/share/mkcert
  # Create the NSS trust store BEFORE running mkcert; mkcert doesn't reliably
  # work otherwise.
  su chrome -c 'mkdir -p $HOME/.pki/nssdb'
  su chrome -c 'certutil -d sql:$HOME/.pki/nssdb -N --empty-password'
  su chrome -c 'cd $HOME && TRUST_STORES=nss mkcert -install'

  touch /.mkcert-configured
fi

# Run headless Chrome in the foreground (the original container's command).
su chrome -c 'google-chrome --headless --disable-gpu --remote-debugging-address=0.0.0.0 --remote-debugging-port=9222'

The Dockerfile builds a custom container on top of justinribeiro/chrome-headless. We install the mkcert prerequisites, mkcert itself, and the optional libnss3-tools prerequisite. It's not optional for us in this case.

We also change the user context to root because we need to be root for the startup script to work.

The chrome-startup.sh script installs mkcert. We have to install it at startup because we rely on the mounted volume containing the certificates. It is not available at build time. It installs it system-wide and then installs it for the chrome user, which headless Chrome actually runs as in this container.

One notable tricky part when installing for the chrome user is that we have to manually create the NSS DB that Firefox, headless Chrome, and Chromium use. The mkcert -install command is supposed to create this automatically, but it doesn't always. It does, however, consistently work if we create the NSS DB first.

After adding these files, simply use ddev start or ddev restart, configure Behat to use the headless Chrome on http://192.168.65.2:9222, and you should now be able to set your Behat\MinkExtension.base_url to https://mysite.ddev.local (where mysite is replaced with your project name). Headless Chrome should accept the SSL certificate now. I have not explained Behat configuration in great detail, as it's out of the scope of this answer.

Behat should now work (or continue working).

You can also test if headless Chrome recognizes your SSL certificate manually.

SSH into the container: ddev ssh -s chrome

Print the content of the website: su chrome -c 'google-chrome --headless --disable-gpu --dump-dom https://mysite.ddev.local'

If certificate validation failed, you'll see output containing this:

[0524/160648.082825:ERROR:cert_verify_proc_nss.cc(975)] CERT_PKIXVerifyCert for dev.fillpdf-service.com failed err=-8179

If you instead see a bunch of HTML, that means it's working!

wizonesolutions
  • 577
  • 3
  • 15