We zullen een Certificate Transparency log systeem bouwen voor interne PKI's met behulp van Kafka als een berichtenwachtrij en Trillian als een alleen-toevoegbare Merkle-boom. Deze opzet maakt efficiënte controle van certificaatuitgiftes mogelijk, detectie van verkeerd uitgegeven certificaten en consistentiecontroles op basis van geruchten.
Waarom Zich Bezighouden met Interne CT Logs?
Voordat we in de technische details duiken, laten we de olifant in de kamer aanpakken: Waarom zouden we ons druk maken over het implementeren van CT logs voor interne PKI's?
- Detecteer ongeautoriseerde certificaatuitgiftes
- Zorg voor naleving van interne beleidsregels
- Verbeter de mogelijkheden voor incidentrespons
- Verhoog de algehele beveiligingspositie
Zie het als een vertrouw-maar-verifieer aanpak voor je interne CA. Je vertrouwt je CA, maar je wilt ook dat het eerlijk blijft.
De Bouwstenen: Kafka en Trillian
Om ons interne CT log systeem te implementeren, gebruiken we twee krachtige tools:
1. Apache Kafka
Kafka zal dienen als onze berichtenwachtrij, die de hoge doorvoer van certificaatgegevens verwerkt. Het is als een lopende band voor je certificaten, die ervoor zorgt dat ze in volgorde en met hoge betrouwbaarheid worden verwerkt.
2. Trillian
Trillian, ontwikkeld door Google, is onze alleen-toevoegbare Merkle-boom implementatie. Het is de ruggengraat van onze CT log, die cryptografische garanties biedt voor de integriteit van de log en efficiënte bewijzen van opname mogelijk maakt.
Overzicht van de Architectuur
Laten we onze systeemarchitectuur opsplitsen:
+----------------+ +--------+ +----------+ +---------+
| Internal CA | --> | Kafka | --> | Trillian | --> | Auditor |
+----------------+ +--------+ +----------+ +---------+
| |
| |
v v
+----------------+ +--------------------+
| Monitor/Alert | | Gossip Participants|
+----------------+ +--------------------+
1. De interne CA dient nieuw uitgegeven certificaten in bij Kafka.
2. Kafka zorgt voor geordende, betrouwbare levering aan Trillian.
3. Trillian voegt de certificaten toe aan zijn Merkle-boom.
4. Auditors kunnen de consistentie van de log verifiëren en verdachte certificaten controleren.
5. Monitoringsystemen waarschuwen bij eventuele afwijkingen.
6. Gossip-deelnemers zorgen voor de consistentie van de log over meerdere instanties.
Het Systeem Implementeren
Stap 1: Kafka Instellen
Laten we eerst onze Kafka-cluster opzetten. We gebruiken Docker voor eenvoud:
version: '3'
services:
zookeeper:
image: confluentinc/cp-zookeeper:latest
environment:
ZOOKEEPER_CLIENT_PORT: 2181
ZOOKEEPER_TICK_TIME: 2000
kafka:
image: confluentinc/cp-kafka:latest
depends_on:
- zookeeper
ports:
- 9092:9092
environment:
KAFKA_BROKER_ID: 1
KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:29092,PLAINTEXT_HOST://localhost:9092
KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT
KAFKA_INTER_BROKER_LISTENER_NAME: PLAINTEXT
KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
Voer dit uit met docker-compose up -d
, en je hebt een Kafka-cluster klaar om certificaten te verwerken.
Stap 2: Trillian Configureren
Nu gaan we Trillian instellen. We moeten het vanuit de bron compileren:
git clone https://github.com/google/trillian.git
cd trillian
go build ./cmd/trillian_log_server
go build ./cmd/trillian_log_signer
Maak een MySQL-database voor Trillian aan:
CREATE DATABASE trillian;
Initialiseer het databaseschema:
mysql -u root -p trillian < storage/mysql/schema/storage.sql
Start nu de Trillian log server en ondertekenaar:
./trillian_log_server --logtostderr ...
./trillian_log_signer --logtostderr ...
Stap 3: De Certificaataangever Implementeren
We hebben een component nodig om certificaten van onze interne CA naar Kafka te sturen. Hier is een eenvoudige Go-implementatie:
package main
import (
"context"
"crypto/x509"
"encoding/pem"
"github.com/segmentio/kafka-go"
)
func submitCertificate(cert *x509.Certificate) error {
w := kafka.NewWriter(kafka.WriterConfig{
Brokers: []string{"localhost:9092"},
Topic: "ct-log-entries",
})
pemCert := pem.EncodeToMemory(&pem.Block{
Type: "CERTIFICATE",
Bytes: cert.Raw,
})
return w.WriteMessages(context.Background(),
kafka.Message{
Key: []byte(cert.SerialNumber.String()),
Value: pemCert,
},
)
}
Stap 4: Certificaten Verwerken met Trillian
Nu moeten we certificaten van Kafka consumeren en toevoegen aan Trillian:
package main
import (
"context"
"github.com/google/trillian"
"github.com/segmentio/kafka-go"
)
func processCertificates(logID int64) {
r := kafka.NewReader(kafka.ReaderConfig{
Brokers: []string{"localhost:9092"},
Topic: "ct-log-entries",
GroupID: "trillian-processor",
})
client, err := trillian.NewTrillianLogClient(...)
if err != nil {
// Handle error
}
for {
msg, err := r.ReadMessage(context.Background())
if err != nil {
// Handle error
continue
}
leaf := &trillian.LogLeaf{
LeafValue: msg.Value,
}
_, err = client.QueueLeaf(context.Background(), &trillian.QueueLeafRequest{
LogId: logID,
Leaf: leaf,
})
if err != nil {
// Handle error
}
}
}
Implementeren van Consistentie op Basis van Geruchten
Om de consistentie van onze CT log over meerdere instanties te waarborgen, implementeren we een geruchtenprotocol. Dit stelt verschillende loginstanties in staat om hun weergaven van de log te vergelijken en eventuele discrepanties te detecteren.
Overzicht van het Geruchtenprotocol
- Elke loginstantie stuurt periodiek zijn laatste Signed Tree Head (STH) naar een set peers.
- Peers vergelijken de ontvangen STH met hun eigen.
- Als er verschillen worden gedetecteerd, vragen peers om en verifiëren ze consistentiebewijzen.
- Eventuele inconsistenties activeren waarschuwingen voor verder onderzoek.
Hier is een basisimplementatie van het geruchtenprotocol:
package main
import (
"context"
"github.com/google/trillian"
"github.com/google/trillian/client"
"time"
)
type GossipParticipant struct {
LogID int64
Client trillian.TrillianLogClient
Verifier *client.LogVerifier
Peers []string
}
func (g *GossipParticipant) RunGossip() {
ticker := time.NewTicker(5 * time.Minute)
for range ticker.C {
g.gossipRound()
}
}
func (g *GossipParticipant) gossipRound() {
ctx := context.Background()
sth, err := g.Client.GetLatestSignedLogRoot(ctx, &trillian.GetLatestSignedLogRootRequest{LogId: g.LogID})
if err != nil {
// Handle error
return
}
for _, peer := range g.Peers {
peerSTH := getPeerSTH(peer) // Implementeer deze functie om STH van een peer te krijgen
if !g.Verifier.VerifyRoot(sth.SignedLogRoot, peerSTH.SignedLogRoot) {
// STH's komen niet overeen, vraag consistentiebewijs aan
proof, err := g.Client.GetConsistencyProof(ctx, &trillian.GetConsistencyProofRequest{
LogId: g.LogID,
FirstTreeSize: peerSTH.TreeSize,
SecondTreeSize: sth.TreeSize,
})
if err != nil {
// Handle error
continue
}
// Verifieer het consistentiebewijs
if !g.Verifier.VerifyConsistencyProof(proof) {
// Inconsistentie gedetecteerd! Waarschuwing geven
raiseInconsistencyAlert(g.LogID, peer)
}
}
}
}
func raiseInconsistencyAlert(logID int64, peer string) {
// Implementeer waarschuwingsmechanisme (bijv. stuur e-mail, activeer incidentrespons)
}
Auditing en Monitoring
Met ons CT log systeem op zijn plaats, moeten we auditing en monitoring implementeren om verdachte activiteiten of inconsistenties te detecteren.
Een Auditor Implementeren
De taak van de auditor is om periodiek de log te controleren op certificaten die het beleid schenden of verdacht lijken. Hier is een basisimplementatie:
package main
import (
"context"
"crypto/x509"
"encoding/pem"
"github.com/google/trillian"
"time"
)
type Auditor struct {
LogID int64
Client trillian.TrillianLogClient
}
func (a *Auditor) AuditLog() {
ticker := time.NewTicker(1 * time.Hour)
for range ticker.C {
a.auditRound()
}
}
func (a *Auditor) auditRound() {
ctx := context.Background()
leaves, err := a.Client.GetLeavesByRange(ctx, &trillian.GetLeavesByRangeRequest{
LogId: a.LogID,
StartIndex: 0,
Count: 1000, // Pas aan indien nodig
})
if err != nil {
// Handle error
return
}
for _, leaf := range leaves.Leaves {
block, _ := pem.Decode(leaf.LeafValue)
if block == nil {
// Handle error
continue
}
cert, err := x509.ParseCertificate(block.Bytes)
if err != nil {
// Handle error
continue
}
if isSuspiciousCertificate(cert) {
raiseSuspiciousCertificateAlert(cert)
}
}
}
func isSuspiciousCertificate(cert *x509.Certificate) bool {
// Implementeer controles voor verdachte certificaten
// Bijvoorbeeld:
// - Onverwachte uitgevers
// - Ongebruikelijke geldigheidsperioden
// - Verboden sleutelgebruiken
// - Onverwachte SAN's
return false
}
func raiseSuspiciousCertificateAlert(cert *x509.Certificate) {
// Implementeer waarschuwingsmechanisme voor verdachte certificaten
}
Monitoring en Waarschuwen
Om de gezondheid en prestaties van ons CT log systeem in de gaten te houden, moeten we uitgebreide monitoring en waarschuwingen implementeren. Hier zijn enkele belangrijke statistieken om bij te houden:
- Loggrootte en groeisnelheid
- Latentie van certificaatindieningen
- Foutpercentages bij certificaatverwerking
- Consistentiecontroles van het geruchtenprotocol
- Bevindingen en waarschuwingen van de auditor
Je kunt tools zoals Prometheus en Grafana gebruiken om deze statistieken te verzamelen en te visualiseren. Hier is een voorbeeld van hoe je enkele basisstatistieken kunt blootstellen met behulp van de Prometheus-clientbibliotheek voor Go:
package main
import (
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
)
var (
certificatesProcessed = promauto.NewCounter(prometheus.CounterOpts{
Name: "ct_log_certificates_processed_total",
Help: "Het totale aantal verwerkte certificaten",
})
processingLatency = promauto.NewHistogram(prometheus.HistogramOpts{
Name: "ct_log_processing_latency_seconds",
Help: "De latentie van het verwerken van certificaten",
Buckets: prometheus.DefBuckets,
})
gossipInconsistencies = promauto.NewCounter(prometheus.CounterOpts{
Name: "ct_log_gossip_inconsistencies_total",
Help: "Het totale aantal gedetecteerde inconsistenties in geruchten",
})
)
// Gebruik deze statistieken in je code:
// certificatesProcessed.Inc()
// processingLatency.Observe(duration.Seconds())
// gossipInconsistencies.Inc()
Conclusie: Vertrouw, maar Verifieer (en Log)
Het implementeren van een Certificate Transparency log voor je interne PKI lijkt misschien overdreven op het eerste gezicht. Maar in de wereld van cybersecurity, waar vertrouwen van het grootste belang is en de gevolgen van een inbreuk catastrofaal kunnen zijn, is het een kleine prijs voor gemoedsrust.
Door de kracht van Kafka te benutten voor verwerking van berichten met hoge doorvoer en Trillian voor cryptografische integriteit, hebben we een robuust systeem gecreëerd dat kan:
- Snel ongeautoriseerde of verkeerd uitgegeven certificaten detecteren
- Een onveranderlijk auditspoor van alle certificaatuitgiftes bieden
- Consistentie over meerdere loginstanties waarborgen via geruchtenprotocollen
- Proactieve monitoring en waarschuwingen voor verdachte activiteiten mogelijk maken
Onthoud, in het domein van PKI is vertrouwen goed, maar verificatie beter. Door dit interne CT log systeem te implementeren, verbeter je niet alleen je beveiligingspositie; je bouwt een fundament van verifieerbaar vertrouwen dat de toets van audits en de tand des tijds kan doorstaan.
"In God we trust. All others must bring data." - W. Edwards Deming
Ga nu en log die certificaten! Je toekomstige zelf (en je auditors) zullen je dankbaar zijn.