#!/usr/bin/env bash

# Copyright Materialize, Inc. and contributors. All rights reserved.
#
# Use of this software is governed by the Business Source License
# included in the LICENSE file at the root of this repository.
#
# As of the Change Date specified in that file, in accordance with
# the Business Source License, use of this software will be governed
# by the Apache License, Version 2.0.

# This code creates TLS certificates for the various TLS-enabled services that
# appear throughout our mzcompose infrastructure. See test/kafka-auth for a
# representative example.
#
# For each service, we generate:
#
#   * A PEM-formatted certificate and key, signed by the "ca" CA, in the files
#     SERVICE.crt and SERVICE.key. The certificate is valid for the hostnames
#     `SERVICE` and `*.SERVICE.local`. The latter wildcard is useful when it's
#     necessary to run multiple copies of the service at different hostnames
#     using the same TLS certificate (e.g., `replica1.SERVICE.local` and
#     `replica2.SERVICE.local`). The `.local` suffix is required as TLS wildcard
#     certificates are not valid for top-level domains (e.g., `*.SERVICE`).
#   * A PKCS#12-formatted archive containing the above certificate and key in a
#     file named SERVICE.p12.
#
# For Java services like Kafka, we additionally generate:
#
#   * A Java KeyStore named SERVICE.keystore.jks that contains ca.crt,
#     SERVICE.crt, and SERVICE.key.
#
#   * A PEM-formatted certificate and key, signed by the "ca-selective" CA, in
#     the files materialized-SERVICE.crt and materialized-SERVICE.key.
#
#   * A Java TrustStore named SERVICE.truststore.jks that contains ca.crt and
#     materialized-SERVICE.crt.
#
# The idea is that you configure services to use SERVICE.key as a client
# certificate when communicating with other services, and to trust ca.crt
# (or SERVICE.truststore.jks for Java services) when other services connect to it.
# The certificates signed by "ca" are then useful when you want a certificate that
# can connect to any other service.
#
# The certificates signed by "ca-selective" are useful when you want to provide
# Materialize with a certificate that can talk to one service but not another. For
# example, the materialize-kafka.key file enables communication with Kafka
# but not the Schema Registry.

set -euo pipefail

export SSL_SECRET=mzmzmz

mkdir secrets

# Create CA
openssl req \
    -x509 \
    -days 36500 \
    -newkey rsa:4096 \
    -keyout secrets/ca.key \
    -out secrets/ca.crt \
    -sha256 \
    -batch \
    -subj "/CN=MZ RSA CA" \
    -passin pass:$SSL_SECRET \
    -passout pass:$SSL_SECRET

# Create an alternative CA, used for certain tests
openssl req \
    -x509 \
    -days 36500 \
    -newkey rsa:4096 \
    -keyout secrets/ca-selective.key \
    -out secrets/ca-selective.crt \
    -sha256 \
    -batch \
    -subj "/CN=MZ RSA CA" \
    -passin pass:$SSL_SECRET \
    -passout pass:$SSL_SECRET

# create_cert CLIENT-NAME CA-NAME COMMON-NAME
create_cert() {
    local client_name=$1
    local ca_name=$2
    local common_name=$3

    # Create key & CSR.
    openssl req -nodes \
        -newkey rsa:2048 \
        -keyout secrets/"$client_name".key \
        -out tmp/"$client_name".csr \
        -sha256 \
        -batch \
        -subj "/CN=$common_name" \
        -addext "subjectAltName = DNS:$common_name, DNS:*.$common_name.local" \
        -passin pass:$SSL_SECRET \
        -passout pass:$SSL_SECRET \

    # Sign the CSR.
    openssl x509 -req \
        -CA secrets/"$ca_name".crt \
        -CAkey secrets/"$ca_name".key \
        -in tmp/"$client_name".csr \
        -out secrets/"$client_name".crt \
        -sha256 \
        -days 36500 \
        -CAcreateserial \
        -copy_extensions copy \
        -passin pass:$SSL_SECRET \

    # Export key and certificate as a PKCS#12 archive for import into JKSs.
    openssl pkcs12 \
        -export \
        -in secrets/"$client_name".crt \
        -name "$client_name" \
        -inkey secrets/"$client_name".key \
        -passin pass:$SSL_SECRET \
        -certfile secrets/"$ca_name".crt \
        -out tmp/"$client_name".p12 \
        -passout pass:$SSL_SECRET

    # Export key and certificate as a PKCS#12 archive with newer cipher
    # suites for use by OpenSSL v3+.
    openssl pkcs12 \
        -export \
        -keypbe AES-256-CBC \
        -certpbe AES-256-CBC \
        -in secrets/"$client_name".crt \
        -name "$client_name" \
        -inkey secrets/"$client_name".key \
        -passin pass:$SSL_SECRET \
        -certfile secrets/"$ca_name".crt \
        -out secrets/"$client_name".p12 \
        -passout pass:$SSL_SECRET
}

for i in materialized producer postgres certuser balancerd frontegg-mock sql-server
do
    create_cert $i "ca" $i

done

for i in kafka kafka1 kafka2 schema-registry
do
    create_cert $i "ca" $i
    create_cert "materialized-$i" "ca-selective" "materialized"

    # Create JKS
    keytool -importkeystore \
        -deststorepass $SSL_SECRET \
        -destkeypass $SSL_SECRET \
        -srcstorepass $SSL_SECRET \
        -destkeystore secrets/$i.keystore.jks \
        -srckeystore tmp/$i.p12 \
        -srcstoretype PKCS12

    # Import CA
    keytool \
        -alias CARoot \
        -import \
        -file secrets/ca.crt \
        -keystore secrets/$i.keystore.jks \
        -noprompt -storepass $SSL_SECRET -keypass $SSL_SECRET

    # Create truststore and import CA cert
    keytool \
        -keystore secrets/$i.truststore.jks \
        -alias CARoot \
        -import \
        -file secrets/ca.crt \
        -noprompt -storepass $SSL_SECRET -keypass $SSL_SECRET

    # Create truststore and import a cert using the alternative CA
    keytool \
        -keystore secrets/$i.truststore.jks \
        -alias Selective \
        -import \
        -file secrets/materialized-$i.crt \
        -noprompt -storepass $SSL_SECRET -keypass $SSL_SECRET

done

# Ensure files are readable for any user.
chmod -R a+r secrets/
# The PostgreSQL key must be only user accessible to satisfy PostgreSQL's
# security checks.
cp secrets/postgres.key secrets/postgres-world-readable.key
chmod -R og-rwx secrets/postgres.key
