Ilija Matoski

    ← Articles

    Real HTTPS on *.localhost, and the Firefox catch

    Cookies and Origin checks only behave like production if the hostnames do, so every local site gets real HTTPS from a local CA, except Firefox has its own idea about which CAs to trust.

    I run a whole platform on a laptop behind Caddy, with a CLI I wrote called stack, and I wanted it on real hostnames instead of a pile of ports.

    Every site and portal gets its own hostname with HTTPS, because cookies, Origin checks and the rules about which host signs in whom only behave like production if the hostnames do.

    Caddy serves all of them from a local CA, so we need to get its root into the places browsers look, with the following commands.

    stack trust # install the root CA
    stack trust --print # show the commands instead of running them

    It goes in the OS store, but the rest depends on the platform, so let’s take a look at what --print shows on each one, with a single Firefox profile.

    On macOS it needs sudo and goes in the System keychain, which Safari, Chrome and Edge read.

    $ stack trust --print
    $ sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain /Users/you/.stack/proxy/caddy-ca/root.crt
    would add to /Users/you/Library/Application Support/Firefox/Profiles/abc123.default-release/user.js: user_pref("security.enterprise_roots.enabled", true);

    On Windows it goes into your own user’s store, so you don’t need admin, Windows just asks you to confirm.

    > stack trust --print
    $ certutil -user -addstore Root C:\Users\you\.stack\proxy\caddy-ca\root.crt
    would add to C:\Users\you\AppData\Roaming\Mozilla\Firefox\Profiles\abc123.default-release\user.js: user_pref("security.enterprise_roots.enabled", true);

    So the last line is the Firefox catch, it has its own store, and on macOS and Windows stack trust adds that line to user.js in each profile, which tells it to trust the OS store.

    Linux needs sudo too, and there Firefox and Chrome get the CA imported straight into their NSS databases, one for each Firefox profile, and the one Chrome uses in ~/.pki/nssdb if you have it.

    $ stack trust --print
    $ sudo cp /home/you/.stack/proxy/caddy-ca/root.crt /usr/local/share/ca-certificates/stack-caddy-root.crt
    $ sudo update-ca-certificates
    $ certutil -A -d sql:/home/you/.mozilla/firefox/abc123.default-release -t C,, -n "stack local CA" -i /home/you/.stack/proxy/caddy-ca/root.crt
    $ certutil -A -d sql:/home/you/.pki/nssdb -t C,, -n "stack local CA" -i /home/you/.stack/proxy/caddy-ca/root.crt

    If you don’t have certutil on Linux, the certutil lines are replaced with a message telling you to install libnss3-tools or nss and run stack trust again.

    Restart the browsers afterwards, or you’ll still see the warning.

    You do that once per machine, the CA lives in ~/.stack outside the Docker volumes so resets and new versions keep it, and only stack destroy removes it, after which you need to run stack trust again.

    On macOS and Windows stack doctor lists any Firefox profile that still needs the line.

    Keep that folder private, because its root.key can sign certificates your browsers will accept.