Fixing the Unskippable Firefox SEC_ERROR_BAD_SIGNATURE for the Nextcloud AIO Interface

Since Nextcloud All-in-One (AIO) switched its mastercontainer to Caddy, I could no longer open the Nextcloud AIO admin interface in Firefox. The underlying issue is a name collision between the internal Caddy CA used by Nextcloud AIO and the internal Caddy CA used by my own reverse proxy. This post documents the solution I chose.

Back to the beginning. After opening:

https://<server-ip>:8080

Firefox failed with:

SEC_ERROR_BAD_SIGNATURE

Screenshot of Firefox showing SEC_ERROR_BAD_SIGNATURE

Unlike the usual self-signed-certificate warning, Firefox did not offer an Accept the Risk and Continue button. Chromium could bypass the error, but that was not a real solution. The underlying issue is a name collision between the internal Caddy CA used by Nextcloud AIO and the internal Caddy CA used by my own reverse proxy.

My solution: Replacing AIO's embedded Caddy CA with the root CA already used and trusted by my existing Caddy instance, which avoids needing to import another root certificate into Firefox or accepting a browser warning.

The background and upstream discussion are documented in Nextcloud AIO discussion #7937.

Why the error occurs

Newer Nextcloud AIO versions use Caddy inside the nextcloud-aio-mastercontainer to serve the admin interface on port 8080. When accessed via the server's IP address or internal domain name, Caddy issues a certificate from its own internal CA.

Caddy's default internal CA uses these names:

Caddy Local Authority - <year> ECC Root
Caddy Local Authority - ECC Intermediate

If you also operate your own Caddy reverse proxy with tls internal, it typically creates a CA with exactly the same names, but with different cryptographic keys.

The error only appears if you have imported the root certificate of your own Caddy instance into Firefox, which is the normal thing to do so that your internal sites load without warnings. Firefox then attempts to validate AIO's intermediate certificate against the trusted root with the same issuer name: the root of your own Caddy instance. Because the keys do not match, signature validation fails. Firefox reports SEC_ERROR_BAD_SIGNATURE, which cannot be overridden.

The approach

Instead of changing the CA name of my existing Caddy installation, I made Nextcloud AIO use the CA that my Firefox already trusts.

The steps are:

  1. Back up AIO's existing internal Caddy CA.
  2. Back up AIO's existing leaf certificates.
  3. Copy the existing Caddy root and intermediate certificate/key pair into AIO's Caddy PKI directory.
  4. Remove AIO's cached leaf certificates.
  5. Restart AIO.
  6. Caddy issues a new leaf certificate for the AIO interface, signed by the existing trusted CA.

All steps can be done while the mastercontainer is running; the restart at the end makes Caddy pick up the new CA.

This does not modify Nextcloud data, the Nextcloud containers, or the main AIO configuration. It only affects the internal Caddy PKI used by the mastercontainer interface.

A word on security: this puts the private key of your existing root CA in a second place. The AIO mastercontainer already has access to the Docker socket, so anyone who compromises it effectively controls the host anyway, but you should be aware of it.

Paths involved

Inside the AIO mastercontainer, the relevant paths are:

/mnt/docker-aio-config/caddy-internal/pki/authorities/local
/mnt/docker-aio-config/caddy-internal/certificates/local/<local.name>

The pki subdirectory contains:

root.crt
root.key
intermediate.crt
intermediate.key

The second path holds the leaf certificates. <local.name> is usually the IP address or local host name through which you access the AIO interface, for example:

192.168.1.10

1. Back up AIO's internal CA

Create a backup directory on the Docker host:

mkdir -p aio-caddy-ca-backup

Copy AIO's current internal CA out of the container:

docker cp \
  nextcloud-aio-mastercontainer:/mnt/docker-aio-config/caddy-internal/pki/authorities/local \
  ./aio-caddy-ca-backup/

The backup will be available at:

./aio-caddy-ca-backup/local/

2. Back up AIO's existing leaf certificates

Create another backup directory:

mkdir -p aio-caddy-leaf-backup

Copy all existing leaf certificates:

docker cp \
  nextcloud-aio-mastercontainer:/mnt/docker-aio-config/caddy-internal/certificates/local \
  ./aio-caddy-leaf-backup/

3. Replace AIO's CA with your existing Caddy CA

Set the path to the directory containing your existing Caddy CA files:

MY_CADDY_CA_DIR="/path/to/your-caddy-ca"

If you run the official Caddy Docker image, this is /data/caddy/pki/authorities/local inside the container, i.e. below wherever you mounted /data on the host.

That directory must contain these PEM-encoded files:

root.crt
root.key
intermediate.crt
intermediate.key

Copy them into the AIO mastercontainer:

docker cp "${MY_CADDY_CA_DIR}/root.crt" \
  nextcloud-aio-mastercontainer:/mnt/docker-aio-config/caddy-internal/pki/authorities/local/root.crt

docker cp "${MY_CADDY_CA_DIR}/root.key" \
  nextcloud-aio-mastercontainer:/mnt/docker-aio-config/caddy-internal/pki/authorities/local/root.key

docker cp "${MY_CADDY_CA_DIR}/intermediate.crt" \
  nextcloud-aio-mastercontainer:/mnt/docker-aio-config/caddy-internal/pki/authorities/local/intermediate.crt

docker cp "${MY_CADDY_CA_DIR}/intermediate.key" \
  nextcloud-aio-mastercontainer:/mnt/docker-aio-config/caddy-internal/pki/authorities/local/intermediate.key

Why root.key is needed too: Caddy's internal intermediate certificate is short-lived and Caddy renews it automatically, which requires the root key. Over time, AIO's Caddy and your own Caddy will each renew their own intermediate, so the two intermediates will drift apart. That is fine: both are signed by the same trusted root, and that is all Firefox checks.

4. Remove AIO's old leaf certificates

This step is important. Otherwise, Caddy may continue serving a cached leaf certificate that was signed by the old AIO CA.

Remove all cached leaf certificates:

docker exec nextcloud-aio-mastercontainer \
  sh -c 'rm -rf /mnt/docker-aio-config/caddy-internal/certificates/local/*'

If you prefer to remove only the certificate for the AIO interface (replace <local.name> with your actual name):

docker exec nextcloud-aio-mastercontainer \
  sh -c "rm -rf /mnt/docker-aio-config/caddy-internal/certificates/local/<local.name>"

5. Restart AIO

docker restart nextcloud-aio-mastercontainer

Follow the startup logs:

docker logs -f nextcloud-aio-mastercontainer

On startup, Caddy loads the replacement root and intermediate CA and generates a new leaf certificate for the AIO interface hostname or IP address.

6. Test in Firefox

Open the Nextcloud AIO mastercontainer web interface:

https://<server-ip>:8080

Firefox should now load the AIO interface without SEC_ERROR_BAD_SIGNATURE and without requiring a certificate exception.

To double-check, click the padlock in the address bar and open the certificate details. The certificate chain should end in the same root certificate that you imported into Firefox for your own Caddy instance.

Rollback

If something goes wrong:

  1. Restore the original CA backup:

bash docker cp ./aio-caddy-ca-backup/local/. \ nextcloud-aio-mastercontainer:/mnt/docker-aio-config/caddy-internal/pki/authorities/local/

  1. Remove the leaf certificates again:

bash docker exec nextcloud-aio-mastercontainer \ sh -c 'rm -rf /mnt/docker-aio-config/caddy-internal/certificates/local/*'

  1. Restart the mastercontainer:

bash docker restart nextcloud-aio-mastercontainer

Alternative upstream fix

Another fix is for Nextcloud AIO to give its embedded Caddy CA a unique name, for example:

pki {
    ca local {
        name "Nextcloud AIO"
    }
}

That prevents the collision with a separately operated Caddy instance. This change was proposed in the upstream discussion and released with Nextcloud AIO v15.0.0 Beta.

However, existing installations keep their current CA unless its PKI data is deleted and regenerated. Simply upgrading AIO does not fix the error for an installation that already has a CA. For the same reason, the workaround in this post survives AIO upgrades: Caddy keeps using the CA files it finds.

My approach instead reuses an already trusted CA, which avoids needing to import another root certificate into Firefox or accepting a browser warning.