Skip to main content
Version: 2.1.0

Certificate Generation

Overview

The Mata Elang v2.1 platform uses mutual TLS (mTLS) for all inter-service communication. This includes Kafka, Schema Registry, sensor-api, Logstash, OpenSearch, and the sensor's gRPC client. All certificates are generated from a single script (generate.sh) and a single configuration file (config.toml).

This guide covers how to configure, generate, and validate the TLS/mTLS certificates required by the platform.

Prerequisites

openssl and keytool (from OpenJDK/JRE) must be installed on the machine where you will generate certificates.

sudo apt update && sudo apt install openssl default-jre-headless

✅ You must already have Git and Docker installed and should have cloned the repository:

git clone https://github.com/mata-elang-stable/example-docker-deployment.git
cd example-docker-deployment

Step 1: Configure config.toml

The certificate generator reads a TOML configuration file. Start by copying the provided example:

cp config.example.toml config.toml

🔑 The default config.toml looks like this:

# Certificate Authority settings
[ca]
common_name = "mataelang-ca"
organization = "MATAELANG"
country = "ID"
state = "EastJava"
locality = "Surabaya"
days_valid = 3650 # ~10 years
key_size = 4096

# Shared password for all keystores/keys
[ssl]
password = "SecurePassword@123"

# Output directory
[output]
directory = "./ssl_certs"

# Client certificates (one [[clients]] block per service)
[[clients]]
name = "broker"
dns = ["broker", "localhost"]
ip = ["127.0.0.1"]
keystore_type = "pkcs12"
keystore_filename = "kafka.broker.keystore.pkcs12"

[[clients]]
name = "schema-registry"
dns = ["schema-registry", "localhost"]
keystore_type = "jks"

[[clients]]
name = "kafka-ui"
dns = ["kafka-ui", "localhost"]
keystore_type = "jks"

[[clients]]
name = "logstash"
dns = ["logstash", "localhost"]
keystore_type = "jks"

[[clients]]
name = "event-stream-aggr"
dns = ["event-stream-aggr", "localhost"]
keystore_type = "pkcs12"

[[clients]]
name = "sensor-api"
dns = ["sensor-api", "localhost"]
keystore_type = "pkcs12"

# OpenSearch node certificate
[opensearch]
node_name = "opensearch-node1"
dns = ["opensearch-node1", "localhost"]
ip = ["127.0.0.1"]

Configuration Sections Explained

SectionPurpose
[ca]Root Certificate Authority — signs all other certificates. RSA 4096-bit, 10-year validity.
[ssl]Single password used for every keystore, private key, and truststore.
[output]Directory where all generated certificates and keystores are written.
[[clients]]One block per service. Defines the Common Name (CN), DNS Subject Alternative Names (SANs), IP SANs, and keystore format (PKCS12 or JKS).
[opensearch]OpenSearch node certificate settings. Uses raw PEM files (not JKS/PKCS12).

Important Notes

  • ✅ For a quick start, you only need to change the password. Everything else works with the defaults.
  • ⚠️ Critical: The ssl.password value must match SSL_PASSWORD in defense_center/.env. If they differ, services will fail to decrypt their keystores at startup.

Step 2: Generate Certificates

Run the generator from the repository root:

./generate.sh

🔑 The script will display a progress log and a summary table:

╔══════════════════════════════════════════════════════════════╗
║ Generation Summary ║
╚══════════════════════════════════════════════════════════════╝

Generated: 8
Skipped (valid): 0
  • Generated: The number of new certificates created.
  • Skipped (valid): Certificates that already exist and have more than 60 days of remaining validity.

Options

OptionDescription
-h, --helpShow help message
-c, --config FILESpecify a custom config file (default: config.toml)
-o, --output DIROverride output directory (overrides [output] in config)
--forceRegenerate all certificates, ignoring idempotency checks

Examples:

# Use a custom config file
./generate.sh --config my-cluster.toml

# Regenerate everything from scratch
./generate.sh --force

# Output certificates to a specific directory
./generate.sh --output /opt/mataelang/certs

The script is idempotent — it skips certificates that are still valid (>60 days remaining). Use --force to regenerate everything.

Step 3: Verify Certificates

Use the validate.sh script to verify the generated certificates:

# Validate the CA certificate
./validate.sh ssl_certs/ca/ca.crt
# Validate all certificates in the output directory
./validate.sh --all --dir ./ssl_certs

Validation Options

OptionDescription
-h, --helpShow help message
-f, --fullShow full certificate details
-q, --quietMachine-readable output (PASS/FAIL only)
-a, --allValidate all certificates in the specified directory
-d, --dir DIRDirectory to search for certificates (used with --all)

Examples:

# Quick summary validation
./validate.sh ssl_certs/ca/ca.crt

# Full certificate details
./validate.sh --full ssl_certs/ca/ca.crt

# Machine-readable pass/fail
./validate.sh --quiet ssl_certs/ca/ca.crt

# Validate every certificate in the output tree
./validate.sh --all

Output Structure

After a successful run, the ssl_certs/ directory contains the following:

ssl_certs/
├── ca/
│ ├── ca.key # CA private key (RSA 4096-bit)
│ └── ca.crt # CA certificate (PEM, 10-year validity)
├── truststore/
│ ├── truststore.jks # Java truststore containing the CA cert
│ └── truststore_creds # Truststore password
├── broker/
│ ├── broker.p12 # PKCS12 keystore
│ ├── kafka.broker.keystore.pkcs12 # Custom filename for Kafka broker
│ ├── broker_keystore_creds # Keystore password
│ └── broker_sslkey_creds # SSL key password
├── schema-registry/
│ ├── schema-registry.p12 # PKCS12 keystore
│ ├── schema-registry-keystore.jks # JKS keystore
│ ├── schema-registry_keystore_creds
│ └── schema-registry_sslkey_creds
├── kafka-ui/
│ ├── kafka-ui.p12
│ ├── kafka-ui-keystore.jks # JKS keystore
│ ├── kafka-ui_keystore_creds
│ └── kafka-ui_sslkey_creds
├── logstash/
│ ├── logstash.p12
│ ├── logstash-keystore.jks # JKS keystore
│ ├── logstash_keystore_creds
│ └── logstash_sslkey_creds
├── event-stream-aggr/
│ ├── event-stream-aggr.p12
│ ├── event-stream-aggr_keystore_creds
│ └── event-stream-aggr_sslkey_creds
├── sensor-api/
│ ├── sensor-api.p12 # PKCS12 for gRPC server
│ ├── sensor-api.crt # PEM certificate for gRPC TLS
│ ├── sensor-api.key # PEM private key
│ ├── sensor-api_keystore_creds
│ └── sensor-api_sslkey_creds
└── opensearch/
├── opensearch-node1.pem # Node certificate (PEM)
└── opensearch-node1-key.pem # Node private key (PEM)
DirectoryFormatUsed By
ca/PEMSigning all certificates; mounted as trusted CA in every service
truststore/JKSJava-based services validating peer certificates
broker/PKCS12 + JKSKafka broker SSL configuration
schema-registry/JKSConfluent Schema Registry
kafka-ui/JKSKafka UI management interface
logstash/JKSLogstash pipeline (reads from Kafka, writes to OpenSearch)
event-stream-aggr/PKCS12Event stream aggregation service
sensor-api/PKCS12 + PEMgRPC server (sensor-api), serves TLS endpoint
opensearch/PEMOpenSearch node-to-node and HTTP encryption

How Certificates Are Used

Defense Center

The Defense Center's compose.yml (in defense_center/) mounts certificates from ../ssl_certs/ for every service:

  • sensor-api: mounts sensor-api.p12, sensor-api.crt, sensor-api.key for gRPC TLS
  • broker: mounts the entire broker/ directory for Kafka SSL configuration
  • schema-registry: mounts schema-registry-keystore.jks and truststore.jks
  • kafka-ui: mounts kafka-ui.p12 and truststore.jks
  • event-stream-aggr: mounts event-stream-aggr.p12 and ca.pem
  • opensearch-node1: mounts opensearch-node1.pem, opensearch-node1-key.pem, and ca.crt as root-ca.pem
  • logstash: mounts logstash-keystore.jks, truststore.jks, and ca.crt

Sensor

The sensor's compose.yml (in sensor_snort/) only needs the CA certificate to verify the gRPC server:

volumes:
- ../ssl_certs/ca/ca.crt:/secrets/ca.crt:ro

This is automatically configured — no manual certificate setup is required on the sensor.

Password Consistency

⚠️ The ssl.password in config.toml MUST match SSL_PASSWORD in defense_center/.env. If these values differ, the services will fail to open their keystores at startup.

Troubleshooting

keytool: command not found

Install the missing package:

sudo apt install default-jre-headless

Certificate expired

Certificates are checked for validity. If any certificate has 60 or fewer days remaining, it is automatically regenerated. To force regeneration of all certificates:

./generate.sh --force

Password mismatch

If services fail to start with keystore-related errors, verify that the password is consistent:

  1. Check ssl.password in config.toml
  2. Check SSL_PASSWORD in defense_center/.env
  3. Ensure they are identical

If they differ, update one to match the other, then regenerate certificates with ./generate.sh --force.

Permission denied on ssl_certs/

The generated files are readable by the owner by default. If needed, adjust permissions so Docker can read them:

chmod -R 755 ssl_certs/