SIEM and syslog¶
PKIFactor can forward its audit trail in real time to a SIEM collector, over syslog. Every event written to the audit trail is also sent to the collector: certificate requests, approvals, issuances, revocations, sign-ins and sign-in failures, administrative changes, deployments, backups, license and key management events, and so on.
SIEM forwarding is included with every license.
What is sent¶
| Aspect | Value |
|---|---|
| Envelope | Syslog RFC 5424, in both formats |
| Formats | CEF (Common Event Format), or Syslog + JSON |
| Transports | UDP, TCP, TCP + TLS |
| Framing over TCP and TLS | Octet counting (RFC 6587): each message is preceded by its length and a space |
| Facility | local0 to local7, auth or authpriv (default local0) |
| Character set | UTF-8 |
Syslog + JSON¶
The message body is a JSON object:
<133>1 2026-10-26T09:14:02.512031Z 3f2a9c1e7b4d PKIFactor - LOGIN_FAILED - {"event": "LOGIN_FAILED", "action": "login", "resource_type": "session", "resource_id": "", "outcome": "failure", "reason": "invalid_credentials", "details": {...}, "username": "bob@example.com", "source_ip": "192.0.2.10"}
| Field | Content |
|---|---|
event |
Event name, for example CERTIFICATE_ISSUED, CERTIFICATE_REVOKED, LOGIN_FAILED, SETTINGS_UPDATED |
action, resource_type, resource_id |
What was done, on which kind of object, and its identifier |
outcome |
success (default), failure or denied |
reason |
When the event gives one |
details |
All the event's data, with secrets masked |
username |
The actor (e-mail when known, system for background tasks) |
source_ip |
When the event carries it |
Syslog severity: notice (5), or warning (4) when the outcome is failure or denied.
CEF¶
The CEF line is the message of the RFC 5424 envelope:
<133>1 2026-10-26T09:14:02.512031Z 3f2a9c1e7b4d PKIFactor - LOGIN_FAILED - CEF:0|ZetaCert|PKIFactor|2.0.0|LOGIN_FAILED|login:session|7|suser=bob@example.com src=192.0.2.10 cs2=session cs2Label=ResourceType outcome=failure reason=invalid_credentials cs3={...} cs3Label=Details rt=Oct 26 2026 09:14:02
| CEF element | Content |
|---|---|
| Vendor, product | ZetaCert, PKIFactor |
| Version | Always 2.0.0 in this version (it is not the product version) |
| Signature ID | The event name |
| Name | action:resource_type |
| Severity | 3 for success, 7 for failure, 8 for denied |
suser |
The actor |
src |
Source IP address, when known |
cs1 / cs1Label=ResourceID |
Identifier of the object |
cs2 / cs2Label=ResourceType |
Kind of object |
outcome, reason |
As in JSON |
cs3 / cs3Label=Details |
All the event's data as JSON, secrets masked |
rt |
Time of sending, UTC |
The syslog severity of the envelope is always notice in CEF; the CEF severity carries the outcome.
Common to both formats¶
- APP-NAME is
PKIFactorand MSGID is the event name. - HOSTNAME is the host name of the backend container (in Docker, the container's short identifier unless a host name is set).
- Secrets are masked before sending: fields whose name contains
password,secret,token,api_key,private_key,passphrase,credentialand similar are replaced by***REDACTED***, and so is any value that contains a private key. - Not forwarded: the ACME server's own events (it is a separate service), network scan activity, and application logs that are not audit events.
Configure forwarding¶
- In the Administration workspace, open System > Settings, tab SIEM Integration (administrators only).
- Fill in the fields and click Save.
- Click Send test event (the button appears once forwarding is enabled and a host is saved).
| Field | Content |
|---|---|
| Enable SIEM forwarding | Turns forwarding on or off |
| Collector host | Host name or IPv4 address of the collector |
| Port | 1 to 65535, default 514 |
| Protocol | UDP (standard syslog, port 514), TCP (reliable, port 601) or TCP + TLS (encrypted, port 6514); the ports shown are the usual ones, set the one your collector uses in Port |
| Log format | CEF (Splunk, QRadar, ArcSight, Sentinel) or Syslog + JSON (ELK, Graylog, Datadog) |
| Syslog Facility | local0 to local7, auth, authpriv |
| Verify collector TLS certificate | TCP + TLS only. On by default; the collector's certificate must then be valid for the host name entered |
| CA Certificate (optional) | TCP + TLS only. Path of a PEM file of the CA that signed the collector's certificate, as seen from inside the backend container (for example /certs/siem-ca.pem). Without it, the system CA store is used |
The CA file must exist in the backend container: the default deployment mounts no directory for it, so provide the file through a volume of your own.
Saving reloads the forwarder at once: no restart is needed.
Client certificate (mutual TLS)¶
Some collectors require the sender to authenticate with a certificate. PKIFactor supports it, but the settings are not in the form: set them through the settings API, as paths of PEM files inside the backend container, then save the SIEM tab again (or send a test event) to reload the forwarder.
| Setting | Content |
|---|---|
siem_tls_client_cert |
Path of the client certificate (PEM) |
siem_tls_client_key |
Path of the client private key (PEM) |
Both must be set; with either missing, no client certificate is presented.
Status
TCP + TLS forwarding with a client certificate was proved against a real syslog collector (port 6514) in August 2026 (version 1.4.7): a LOGIN_FAILED event was received, and clients without a certificate were rejected. No recorded test covers UDP, plain TCP, or each SIEM product named in the format list.
Follow the forwarding¶
The SIEM tab shows, when forwarding is enabled:
- Connected to SIEM collector or Not connected;
- Events sent and Failures since the backend started;
- Last error, when there is one.
The Monitoring page (Oversight workspace, Fleet monitoring > Monitoring) has a SIEM / Syslog section fed by a connection check. That check sends a real test event, so the collector receives a SIEM_TEST event at each health check (every 30 minutes by default, with the revocation monitor, REVOCATION_MONITOR_INTERVAL) and after each save of the SIEM tab.
Events that could not be sent¶
When an event cannot be delivered (collector down, network failure), PKIFactor does not drop it:
- The event is written to
siem_deadletter.login the backend's log directory (theaudit_logsvolume in the Docker deployment). - Every 2 minutes PKIFactor tries again, up to 1,000 events per round, and removes from the file those that were sent.
Events kept this way also remain in the audit trail itself, which does not depend on the SIEM.
UDP gives no delivery receipt: an event sent over UDP counts as sent even if the collector never received it. Use TCP + TLS when losing events matters.
Troubleshooting¶
| Symptom | Cause and fix |
|---|---|
| The SIEM Integration tab is missing | The account is not an administrator: the Administration workspace and System > Settings are reserved to administrators |
| No Send test event button | Forwarding is not enabled or no host is saved: tick Enable SIEM forwarding, enter the host, Save |
| SIEM not configured or disabled | Same cause |
| Connection failed: ... | The collector is unreachable on that port and protocol (firewall, wrong port, collector not listening on TCP) |
| Connection failed: ... CERTIFICATE_VERIFY_FAILED ... | The collector's certificate is not trusted: set CA Certificate (optional) to the right PEM file, or check that the host name matches the certificate |
| Connection failed: ... No such file or directory | The CA, client certificate or key path does not exist in the backend container |
| The test succeeds but nothing arrives (UDP) | UDP has no receipt: check the firewall and the collector's UDP listener, or switch to TCP |
| Events arrive as one long line or are split wrongly (TCP) | The collector does not expect octet-counting framing (RFC 6587); configure its input for it |
| The collector does not parse the fields | Wrong format: match Log format with the collector's parser (CEF or JSON) |
| Failures keeps growing | Read Last error; undelivered events wait in siem_deadletter.log and are retried every 2 minutes |
SIEM_TEST events every 30 minutes |
Expected: they come from the health check on the Monitoring page |
Via the API
# Sign in (administrator)
curl -s -c jar -X POST https://clm.example.com/api/auth/login \
--data-urlencode "email=admin@example.com" --data-urlencode "password=$PASSWORD"
# Current configuration and forwarder status
curl -s -b jar https://clm.example.com/api/v1/admin/siem/config
curl -s -b jar https://clm.example.com/api/v1/admin/siem/status
# Configure TCP + TLS in JSON format (reloads the forwarder)
curl -s -b jar -X PUT https://clm.example.com/api/v1/admin/siem/config \
-H "Content-Type: application/json" \
-d '{"enabled": true, "host": "siem.example.com", "port": 6514,
"protocol": "tcp_tls", "format": "syslog_json", "facility": "local0",
"tls_verify": true, "tls_ca_cert": "/certs/siem-ca.pem"}'
# Optional client certificate (mutual TLS), then reload by sending a test event
curl -s -b jar -X PUT https://clm.example.com/api/v1/admin/settings \
-H "Content-Type: application/json" \
-d '{"settings": {"siem_tls_client_cert": "/certs/pkifactor-siem.pem",
"siem_tls_client_key": "/certs/pkifactor-siem.key"}}'
curl -s -b jar -X POST https://clm.example.com/api/v1/admin/siem/test-connection
protocol takes udp, tcp or tcp_tls; format takes cef or syslog_json. A failed test answers HTTP 502 with the reason. All routes are in /api/v1/docs, tag SIEM.