A containerised ZAP mints a new root CA each run; which flag stops that, and what must the file hold?
answer
- no stored root means a new one
- the home directory is the state
- -certload imports instead of generating
- the PEM needs both sections
- only the full dump round-trips
basics
~20 sLoad a stored root with -certload. The file must be a PEM holding both a CERTIFICATE section and a PRIVATE KEY section, so only a -certfulldump export can be loaded back — a -certpubdump export is rejected.
solid answer
~40 sZAP's root certificate authority lives in a key store held by the `network` add-on's options, which means it lives in ZAP's home directory. When the add-on starts and finds no stored key store, it **generates a new root** — so a container with a throwaway home mints a fresh authority on every run, and whatever trusted yesterday's root does not trust today's. `-certload <path>` imports one instead. The catch is what the import requires: it extracts a certificate section *and* a private key section from the PEM and fails with a no-private-key-section error if the second is missing. The option's own one-line help says only that it loads the Root CA certificate, which understates it. In practice that means the `-certfulldump` output round-trips and the `-certpubdump` output does not.
code
bash · 7 lines# Keep the root stable across container runs.
if [ -f "$ZAP_ROOT_PEM" ]; then
zap.sh -cmd -certload "$ZAP_ROOT_PEM"
else
# First run: ZAP generates a root, export both halves.
zap.sh -cmd -certpubdump "$ZAP_ROOT_CER" -certfulldump "$ZAP_ROOT_PEM"
figo deeper
Know the cause before the cure: ZAP generates its own root when it cannot find a stored one, so a throwaway home directory means a new authority on every run.
Explain the round trip: -certload imports a PEM that must carry both a certificate section and a private key section, which is why only the full dump can be loaded back.
Bring the operating shape: persist the file, load it explicitly, export the public half for whatever must trust it, and treat the stored file as the credential that it is.
Weigh the trade openly. A stable interception identity is convenient and is a long-lived secret; decide where it lives, who holds it, and how it is retired before standardising the practice.
## Why the authority changes ZAP does not ship a certificate authority; it makes one. The `network` add-on keeps the root in a key store held by its server-certificate options, which is to say in ZAP's home directory. On startup it looks for that key store, and: - if one is there, it uses it (and warns if the certificate has expired); - if there is none, it **generates a new root** — a fresh key pair and a self-signed certificate — and stores it. That is unremarkable on a workstation, where the home directory persists and the root is created once. It is the whole problem in a container, where the home directory is usually thrown away with the container. Each run starts with no key store, each run generates a new authority, and anything that was told to trust the previous one now sees a certificate from an authority it has never heard of. The symptom is a trust failure that appears intermittent but is actually perfectly regular: it happens on every run after the first. ## The fix, and the requirement its help text does not mention `-certload <path>` imports a root instead of generating one. It reads the file, extracts the certificate, extracts the private key, builds a key store from the two and installs it. The requirement hides in that sentence. The import needs **both** sections: | PEM section | needed by `-certload` | produced by `-certpubdump` | produced by `-certfulldump` | |---|---|---|---| | `-----BEGIN CERTIFICATE-----` | yes | yes | yes | | `-----BEGIN PRIVATE KEY-----` | yes | no | yes | Feed it a public dump and it fails with a message saying no private key section was found in the file, naming the tokens it expected. The option's shipped one-line help says only *"Loads the Root CA certificate from the specified file name"* — perfectly true, and quiet about the half that makes the difference. This is a small instance of a pattern worth expecting generally: **the artifact a reader copies from is often narrower than the code that consumes it**, and the gap only shows up at runtime. The requirement also explains the wording of the other two flags. `-certpubdump` calls itself suitable for importing into browsers; `-certfulldump` calls itself suitable for importing into ZAP. They are not two levels of detail — they are two destinations, and only one of them is a round trip. ## The pattern the project uses itself One of the project's own container entry scripts does exactly the load-or-create dance: if the private file already exists in the mounted working directory, it adds `-certload` pointing at it; otherwise it adds both dump flags, writing the public file for whatever must trust the proxy and the full file for the next run to load. That is the shape to copy: 1. Mount a directory that survives the container. 2. If the stored root is there, start ZAP with `-certload`. 3. If it is not, let ZAP generate one and export both halves — the public certificate for the clients that must trust it, the full export so the next run can load it. 4. Distribute only the public export. ## What this buys, and what it does not What it buys is **stability of identity**: the authority is the same from run to run, so whatever you configured to trust it stays configured. In a pipeline that means a device, an image, or a test client can be prepared once instead of on every execution, and a trust error becomes a real signal again instead of background noise. What it does not buy is anything about the *strength* of the arrangement. A stored root is a stored private key, and the file that makes the round trip possible is precisely the file that can sign for the authority. Keeping it around is a deliberate trade — you are choosing a durable identity, and a durable identity is a durable secret. Decide where that file lives, who can read it, and what happens to it when the pipeline is decommissioned, before you write the first `-certload`. ## Things that will bite you anyway - **A readable path is not a writable one.** The export flags check that the destination is writable and report an error rather than failing silently; the load flag checks the source is readable. Both report through the command line, so a headless run shows the reason. - **The key is stored unencrypted in the exported file.** There is no passphrase to supply on load and none to set on export. - **Loading does not change what clients already trust.** Restoring the root fixes the mismatch going forward; anything that was told to trust an earlier generated root still holds that stale entry. - **An expired stored root is loaded, not replaced, in a headless run.** The add-on logs a warning about the expiry; only an interactive session offers to generate a fresh one. A long-lived stored root will eventually need deliberate replacement.
- Why is a -certpubdump file rejected by -certload?Because the import extracts a private key section as well as a certificate section, and fails with a no-private-key-section error when it is missing. The public dump deliberately contains no key, so it cannot restore an authority — only prove one.
- Does mounting ZAP's home directory achieve the same thing as -certload?It can, because the root lives in a key store under that home. The flag is the explicit version: it names the file, works with an ephemeral home, and makes the dependency visible in the command rather than implicit in a volume.
- What happens on a headless run if the stored root has expired?It is still loaded and applied, with a warning logged about the expiry date. Only an interactive session offers to generate a replacement, so a long-lived stored root has to be rotated deliberately.
saying these in an interview costs you the question
- Thinks ZAP ships a fixed root certificate authority
- Tries to restore an authority from the public dump
- Assumes the load option takes an encrypted key file
- Believes loading a root updates clients that trusted the old one
- Treats a stored root file as ordinary build configuration