Bring your own 2-QWAC
Supply a 2-QWAC TLS-binding certificate, its issuer chain and its matching private key. Ordinary TLS QWACs, PSD2 certificates with incompatible key usages and electronic seal certificates are not substitutes. These commands require no remote enrollment, issuer account or paid service. A qualified issuer remains responsible for issuing your real certificate.
Preparation commands belong to the existing production qwacamole-agent CLI
(installation) and work for
both Caddy and Nginx, without a
deployment configuration, Nginx or devtools. Each production binding configuration
owns one literal hostname; use separate state/configuration for multiple sites.
Local key and CSR
Section titled “Local key and CSR”umask 077mkdir -m 0700 qwac-inputqwacamole-agent keygen -out qwac-input/signing-key.pem -bits 3072qwacamole-agent csr -key qwac-input/signing-key.pem -out qwac-input/request.pem \ -country HU -organization 'Example Kft.' -registration NTRHU-12345678 \ -common-name example.com -locality Budapest -province Budapest \ -street 'Example utca 1' -postal-code 1000 \ -hostname example.com -hostname www.example.comkeygen supports RSA 2048, 3072 (default) and 4096 bits, writing unencrypted
PKCS#8 PEM. csr leaves the existing key unchanged and verifies the generated
PKCS#10 signature before writing. Both create mode 0600 files, refuse existing
paths (including symlinks), and never print private keys. Parent directories must
be private. Required subject fields are country, registered organization, distinct
organizationIdentifier and common name; locality, province, street and postal
code are optional. Repeat -hostname for all lowercase literal DNS SANs. Use the
registration syntax your issuer requires.
The CSR requests digitalSignature, QNCP-w-gen and the tls-binding EKU. Requested fields/extensions do not guarantee compliant issuance. QcCompliance and website QcType are issuer assertions checked on the issued certificate. Send only the CSR to your issuer. Keep the signing key local and out of Git, logs, tickets and web roots; protect backups with your secret-storage policy. Supported key inputs are unencrypted PKCS#1 or PKCS#8 RSA PEM. Encrypted PEM, PKCS#12, DER keys, EC keys and HSM/remote references are unsupported. Bindings use RS256.
Inspect and import
Section titled “Inspect and import”Save the leaf certificate followed by its intermediates in PEM. The root may be included or omitted; select it separately through an authenticated source and your issuer-verification process:
qwacamole-agent inspect-qwac \ -chain qwac-input/issued-chain.pem -key qwac-input/signing-key.pem \ -trust /etc/qwacamole/trust/qwac-root.pem \ -hostname example.com -hostname www.example.comqwacamole-agent import-qwac \ -chain qwac-input/issued-chain.pem -key qwac-input/signing-key.pem \ -trust /etc/qwacamole/trust/qwac-root.pem -hostname example.com \ -out /etc/qwacamole/credentials-v1Repeat -chain for separate files in leaf-first order and -hostname for every
hostname the integration will bind. Both integrations and these commands share
the core validation path. Public JSON reports subject, DNS names, validated
hostnames, validity, leaf/authority SHA-256 fingerprints, chain size, algorithm
and validation scope. Failure returns nonzero and an actionable error without a
success report or private key output.
Checks cover strict PEM parsing, key matching and minimum RSA 2048-bit size,
QNCP-w-gen 0.4.0.194112.1.6, exclusively tls-binding EKU
0.4.0.194115.1.0, legal-person subject/issuer fields, AIA/CRL locations,
digitalSignature with no encryption/agreement/CA usages (contentCommitment may
also be present), QcCompliance and website-only QcType, statement encoding and
duplicates, every hostname, all chain certificate dates, issuer signatures and
Go X.509 path verification to the explicit authority. Missing intermediates,
wrong order and untrusted roots reject. No AIA fetching or system-root fallback
occurs. Exactly one CA certificate is supported in the separate trust file,
preserving the existing single-authority binding-store policy.
Profile references: ETSI TS 119 411-5 V2.1.1, EN 319 412-4 V1.4.1, EN 319 412-5 V2.6.1. This supported local profile check is not a complete issuance audit. Trusting an operator-supplied root does not independently establish EU trusted-list qualification, revocation status or native browser trust. Production has no trusted-list/revocation retrieval service. Existing Chrome QWAC metadata and Firefox compiled-authority checks remain separate from ordinary TLS trust. Synthetic devtools authorities have no legal qualification and are enabled only in the explicit isolated test setup. These commands never modify browser/system trust or the supplied trust file.
Import validates before creating a new mode 0700 directory containing a
normalized full chain.pem and PKCS#8 key.pem, both mode 0600. Its parent must
already exist and be writable only by trusted operators. Existing destinations
reject; a write failure cleans only this invocation’s new directory. Source files
stay unchanged. Public output contains absolute-path Caddy and agent field
snippets referencing the separately supplied trust file. Import copies the key
but does not select or activate credentials, change configuration, or expand trust.
Initial integration configuration
Section titled “Initial integration configuration”The quickstart setup commands copy credentials to fixed private paths. Use generated-setup maintenance for those installations. The stable-pointer configuration below is an advanced manual arrangement.
For replacement without changing configured paths, create a stable pointer:
ln -s /etc/qwacamole/credentials-v1 /etc/qwacamole/currentIn both the Caddy issuer and handler’s binding configuration, retain the
existing hostname, state directory and lifecycle settings and supply:
{ "chain_files": ["/etc/qwacamole/current/chain.pem"], "key_file": "/etc/qwacamole/current/key.pem", "trust_files": ["/etc/qwacamole/trust/qwac-root.pem"]}The Caddyfile qwac block uses chain_files, key_file, trust_files.
Follow the complete Caddy setup, keeping QWAC as the sole TLS
issuer, and start Caddy normally.
In the full dedicated Certbot/Nginx agent configuration, use:
{ "qwac_chain_files": ["/etc/qwacamole/current/chain.pem"], "qwac_key_file": "/etc/qwacamole/current/key.pem", "qwac_trust_file": "/etc/qwacamole/trust/qwac-root.pem"}Render/validate Nginx, start the agent and obtain/deploy ordinary TLS through the
existing Certbot hook. validate checks the complete deployment; inspect-qwac
checks only the supplied credential. Both integrations accept a chain with the root included or omitted, while
requiring an independently selected trust anchor. Full-chain and separate-file
configurations are supported. Weak RSA keys, incompatible key usages and malformed
or mixed website/seal QCStatements are rejected.
Credentials in generated setups
Section titled “Credentials in generated setups”Setup is a fresh-install command. It refuses changed credentials or configuration on a repeat run. To replace credentials copied by setup, schedule maintenance: validate/import the new pair first, then stop all owning services before replacing the two fixed files. Keep the selected authority, configuration, complete binding state, TLS history and deployment journal. Do not edit a live chain/key pair.
For the Nginx quickstart, import into a new protected directory:
sudo /opt/qwacamole-agent/bin/qwacamole-agent import-qwac \ -chain /path/to/new-chain.pem -key /path/to/new-key.pem \ -trust /etc/qwacamole/authority.pem -hostname example.com \ -out /etc/qwacamole-credentials-v2sudo systemctl stop qwacamole-renew.timer qwacamole-renew.service \ qwacamole-agent.service qwacamole-nginx.serviceConfirm that both services have exited and their listeners are closed. Make a restricted backup of the complete deployment, then save and replace the pair:
sudo mkdir -m 0700 /root/qwacamole-credentials-beforesudo cp -p /etc/qwacamole/chain.pem /etc/qwacamole/key.pem /root/qwacamole-credentials-before/sudo install -m 0600 /etc/qwacamole-credentials-v2/chain.pem /etc/qwacamole/chain.pemsudo install -m 0600 /etc/qwacamole-credentials-v2/key.pem /etc/qwacamole/key.pemsudo systemctl start qwacamole-agent.service qwacamole-renew.timersudo /opt/qwacamole-agent/bin/qwacamole-agent verify -config /etc/qwacamole/agent.jsonChoose new backup/import directories for each update. If startup or verification fails, stop the same services, restore both saved files, then restart and verify. Preserve state for diagnosis; never delete binding coverage to force readiness. After an intentional update, setup continues to refuse replacing the edited files.
For packaged Caddy, use the same import command with
-trust /etc/caddy-qwac/authority.pem. Stop qwacamole-caddy.service, confirm
process/listener shutdown, back up its configuration and complete
/var/lib/caddy-qwac state, then save and replace
/etc/caddy-qwac/chain.pem and key.pem as above. Start the service and run:
sudo /opt/qwacamole-caddy/bin/caddy-qwac qwac-check \ --hostname example.com --authority /etc/caddy-qwac/authority.pemRetain the previous pair for rollback. Online replacement with atomic pointers requires the explicit manual configuration below; changing authorities remains a separate deployment/trust decision.
Replacement and rejected-update recovery
Section titled “Replacement and rejected-update recovery”Import v2 into a new credentials-v2 directory using the same explicit trust
file. Validate every bound hostname. Renewed certificates may use the same key
or a new dedicated key. Keep v1 protected for rollback. Select chain and key
together with an atomic pointer change (Linux):
ln -s /etc/qwacamole/credentials-v2 /etc/qwacamole/current.nextmv -Tf /etc/qwacamole/current.next /etc/qwacamole/currentOn macOS use mv -fh instead of GNU mv -Tf. Both pointer names must be in the
same directory/filesystem. Serialize updates and never edit live chain/key files
individually. The trust file stays outside this pointer. Selecting a pointer
alone does not change the running signer.
Force Caddy to re-read unchanged paths:
caddy reload --config /etc/caddy/Caddyfile --adapter caddyfile --forceCaddy validates/prepares the candidate before activation, then publishes through
the existing shared binding manager. After a publication failure it continues
using the previous activated credential only while the valid binding covers all
stored TLS leaves. Uncovered TLS leaves still fail closed; new TLS issuance
always requires successful publication. Reload success alone is not activation
proof: make a protected request and confirm QWAC binding configuration activated
logs and the current document’s signer against the inspected fingerprint.
For the running agent, use its private local control operation:
qwacamole-agent replace-qwac -config /etc/qwacamole/agent.jsonqwacamole-agent status -config /etc/qwacamole/agent.jsonqwacamole-agent check -config /etc/qwacamole/agent.jsonThe daemon reads its startup-configured paths, validates the candidate, then signs/verifies/durably publishes retained TLS coverage before switching signer. QWAC-only replacement does not reload Nginx. Editing the CLI’s config file does not reload the daemon; keep stable paths or use the documented full-stop migration for deployment changes. After success, keep the selected inputs on disk: restart loads those credentials and recovers the same binding/journal and TLS history.
A rejected candidate or failed publication preserves the previous in-memory signer and committed binding. Restore the pointer atomically to v1 before restart, then reload Caddy or retry the agent operation. Diagnose the reported key/profile/hostname/chain or storage/signing error and re-inspect after repair. After an uncertain outcome, confirm the active public signer and TLS coverage before another update. Old bindings expire normally; failure cannot extend signed validity. If the gate closes on expiry, repair inputs/publication and verify it reopens. Do not delete retained TLS coverage or reconstruct the deployment journal.
Changing authorities is a separate explicit trust/deployment decision. Online replacement remains same-authority even if the new chain contains another root. Import cannot migrate trust. Plan a separately reviewed new deployment/state and browser trust for an authority migration.