Audit Logging

Complexity: Medium
Duration: 1-2 hours setup
Compliance: NIS2, ISO 27001, BSI Basic Protection

Compliance-conformant logging of all PKI operations for audits and forensics.


Architecture

flowchart LR subgraph SOURCES["SOURCES"] S1[CA Operations] S2[API Requests] S3[Admin Actions] S4[System Events] end subgraph COLLECT["COLLECTION"] C1[Syslog] C2[Filebeat] C3[Fluentd] end subgraph STORE["STORAGE"] ST1[(Elasticsearch)] ST2[(Loki)] ST3[S3/Archive] end subgraph ANALYZE["ANALYSIS"] A1[Kibana] A2[Grafana] end S1 & S2 & S3 & S4 --> C1 & C2 C1 & C2 & C3 --> ST1 & ST2 ST1 --> A1 ST2 --> A2 ST1 & ST2 --> ST3 style ST1 fill:#e3f2fd style A1 fill:#e8f5e9


Audit Events

Category Event Criticality
———-——-————-
Certificate Issued Info
Certificate Renewed Info
Certificate Revoked High
CA CRL generated Info
CA CA key used Medium
Admin Login Medium
Admin Configuration change High
System Service start/stop Medium
Security Failed authentication High

Log Format

Structured JSON Format

{
  "timestamp": "2024-12-15T10:30:00.000Z",
  "level": "INFO",
  "event_type": "certificate_issued",
  "source": "ca-service",
  "actor": {
    "id": "operator-01",
    "ip": "10.0.0.50",
    "role": "pki-operator"
  },
  "subject": {
    "type": "certificate",
    "serial": "01:23:45:67:89:AB:CD:EF",
    "cn": "server.example.com",
    "validity_days": 365
  },
  "details": {
    "algorithm": "ML-DSA-65",
    "mode": "Hybrid",
    "issuer_serial": "AA:BB:CC:DD",
    "request_id": "REQ-2024-12345"
  },
  "result": "success"
}

Syslog Format

Dec 15 10:30:00 ca-server pki-ca[12345]: [INFO] certificate_issued actor=operator-01 serial=01:23:45:67 cn=server.example.com algo=ML-DSA-65 result=success

Linux: Rsyslog + ELK

Rsyslog Configuration

# /etc/rsyslog.d/50-pki.conf
 
# PKI logs in separate file
:programname, isequal, "pki-ca" /var/log/pki/ca.log
:programname, isequal, "pki-ocsp" /var/log/pki/ocsp.log
 
# JSON template for Elasticsearch
template(name="pki-json" type="list") {
    constant(value="{")
    constant(value="\"timestamp\":\"")
    property(name="timereported" dateFormat="rfc3339")
    constant(value="\",\"host\":\"")
    property(name="hostname")
    constant(value="\",\"program\":\"")
    property(name="programname")
    constant(value="\",\"message\":\"")
    property(name="msg" format="json")
    constant(value="\"}\n")
}
 
# Send to Elasticsearch
module(load="omelasticsearch")
action(type="omelasticsearch"
    server="elasticsearch.example.com"
    serverport="9200"
    template="pki-json"
    searchIndex="pki-audit"
    bulkmode="on"
    queue.type="linkedlist"
    queue.size="10000"
    queue.dequeuebatchsize="300"
    action.resumeRetryCount="-1")

Filebeat Configuration

# /etc/filebeat/filebeat.yml
filebeat.inputs:
  - type: log
    enabled: true
    paths:
      - /var/log/pki/*.log
    json.keys_under_root: true
    json.add_error_key: true
    fields:
      log_type: pki-audit

output.elasticsearch:
  hosts: ["elasticsearch.example.com:9200"]
  index: "pki-audit-%{+yyyy.MM.dd}"

setup.template.name: "pki-audit"
setup.template.pattern: "pki-audit-*"

Application Logging (C#)

// PkiAuditLogger.cs
using Microsoft.Extensions.Logging;
using System.Text.Json;
 
public class PkiAuditLogger
{
    private readonly ILogger _logger;
 
    public PkiAuditLogger(ILogger<PkiAuditLogger> logger)
    {
        _logger = logger;
    }
 
    public void LogCertificateIssued(
        X509Certificate2 cert,
        string actor,
        string requestId,
        CryptoMode mode)
    {
        var auditEvent = new
        {
            event_type = "certificate_issued",
            timestamp = DateTimeOffset.UtcNow,
            actor = new { id = actor },
            subject = new
            {
                serial = cert.SerialNumber,
                cn = cert.GetNameInfo(X509NameType.SimpleName, false),
                not_after = cert.NotAfter
            },
            details = new
            {
                algorithm = cert.SignatureAlgorithm.FriendlyName,
                mode = mode.ToString(),
                request_id = requestId
            },
            result = "success"
        };
 
        _logger.LogInformation(
            "AUDIT: {Event}",
            JsonSerializer.Serialize(auditEvent));
    }
 
    public void LogCertificateRevoked(
        string serial,
        X509RevocationReason reason,
        string actor)
    {
        var auditEvent = new
        {
            event_type = "certificate_revoked",
            timestamp = DateTimeOffset.UtcNow,
            actor = new { id = actor },
            subject = new { serial },
            details = new { reason = reason.ToString() },
            result = "success"
        };
 
        _logger.LogWarning(
            "AUDIT: {Event}",
            JsonSerializer.Serialize(auditEvent));
    }
}

Elasticsearch Index Template

PUT _index_template/pki-audit
{
  "index_patterns": ["pki-audit-*"],
  "template": {
    "settings": {
      "number_of_shards": 1,
      "number_of_replicas": 1,
      "index.lifecycle.name": "pki-audit-policy"
    },
    "mappings": {
      "properties": {
        "timestamp": { "type": "date" },
        "level": { "type": "keyword" },
        "event_type": { "type": "keyword" },
        "source": { "type": "keyword" },
        "actor.id": { "type": "keyword" },
        "actor.ip": { "type": "ip" },
        "actor.role": { "type": "keyword" },
        "subject.type": { "type": "keyword" },
        "subject.serial": { "type": "keyword" },
        "subject.cn": { "type": "text" },
        "result": { "type": "keyword" }
      }
    }
  }
}

ILM Policy (Retention)

PUT _ilm/policy/pki-audit-policy
{
  "policy": {
    "phases": {
      "hot": {
        "actions": {
          "rollover": {
            "max_size": "10GB",
            "max_age": "7d"
          }
        }
      },
      "warm": {
        "min_age": "30d",
        "actions": {
          "shrink": { "number_of_shards": 1 },
          "forcemerge": { "max_num_segments": 1 }
        }
      },
      "cold": {
        "min_age": "90d",
        "actions": {
          "allocate": { "require": { "data": "cold" } }
        }
      },
      "delete": {
        "min_age": "365d",
        "actions": { "delete": {} }
      }
    }
  }
}

Kibana Dashboards

Audit Overview

{
  "title": "PKI Audit Dashboard",
  "panels": [
    {
      "title": "Events per Day",
      "type": "visualization",
      "visState": {
        "type": "line",
        "aggs": [
          { "type": "date_histogram", "field": "timestamp", "interval": "1d" }
        ]
      }
    },
    {
      "title": "Events by Type",
      "type": "visualization",
      "visState": {
        "type": "pie",
        "aggs": [
          { "type": "terms", "field": "event_type" }
        ]
      }
    },
    {
      "title": "Revocations",
      "type": "visualization",
      "visState": {
        "type": "metric",
        "aggs": [
          { "type": "count", "filter": { "term": { "event_type": "certificate_revoked" } } }
        ]
      }
    }
  ]
}

Compliance Requirements

Standard Requirement Implementation
———-————-—————-
NIS2 Art. 21 Incident logging Log all PKI events
ISO 27001 A.12.4 Log integrity Tamper-proof storage
BSI C5 Retention Min. 1 year
GDPR Art. 30 Processing records Access logs

Log Archiving

#!/bin/bash
# /usr/local/bin/archive-pki-logs.sh
 
# Archive logs older than 90 days
find /var/log/pki -name "*.log" -mtime +90 -exec gzip {} \;
 
# Upload archive to S3
aws s3 sync /var/log/pki/*.gz s3://pki-audit-archive/$(date +%Y/%m)/
 
# Delete local archives older than 365 days
find /var/log/pki -name "*.gz" -mtime +365 -delete

Checklist

# Checkpoint Done
——————
1 Log format defined (JSON)
2 All PKI events logged
3 Central collection (ELK/Loki)
4 Retention policy configured
5 Dashboard created
6 Archiving set up
7 Compliance review


« <- Revocation Check | -> Alerting Setup »


Wolfgang van der Stille @ EMSR DATA d.o.o. - Post-Quantum Cryptography Professional

Zuletzt geändert: on 2026/01/30 at 01:39 AM