diff --git a/tools/kafka_ssl/README.md b/tools/kafka_ssl/README.md new file mode 100644 index 00000000..3615a674 --- /dev/null +++ b/tools/kafka_ssl/README.md @@ -0,0 +1,59 @@ +# SSL - certificates/keystore/truststore generate +# SSL - import key/certificate pairs into an existing keystore +# SSL - import certificate into an existing truststore + +# help commands +1. bash ssl_generate.sh -h +2. bash ssl_import.sh -h + +# help options +1. bash ssl_generate.sh -gen_keystore -h +2. bash ssl_import.sh -import_to_keystore -h + +# Example +1. Only generate a Certificate Authority (CA) key and certificate + - `bash ssl_generate.sh -gen_CARoot -c kafka-0.tigergraph.com -p 123456` + - 'kafka-0.tigergraph.com' is the CN of Certificate Authority (CA) + - '123456' is the passphrase of CA private_key + + +2. Only generate a keystore + - `bash ssl_generate.sh -gen_keystore -c kafka-0.tigergraph.com -storepass 123456` + - 'kafka-0.tigergraph.com' is the Subject CN of keystore + - '123456' is the storepass of keystore + + +3. Only generate an empty truststore + - `bash ssl_generate.sh -gen_truststore -storepass 123456` + - '123456' is the storepass of truststore + + +4. At the same time, generate CARoot/CARoot private_key, keystore, and an empty truststore + - `bash ssl_generate.sh -c kafka-0.tigergraph.com` + - 'kafka-0.tigergraph.com' is the CN + - The default passphrase of CA private_key is 'tiger123' + - The default storepass of keystore and truststore is 'tiger123' + + +5. Sign sub-certificates with an existing certificate (CARoot or other Superior certificate) + - `bash ssl_generate.sh -gen_subCA -cer ./SSL_OUTPUT/ca-root.crt -cerKey ./SSL_OUTPUT/ca-root.key -p 123456` -c tigergraph + - './SSL_OUTPUT/ca-root.crt' is the path of higher-level CA + - './SSL_OUTPUT/ca-root.key' is the path of higher-level CA private_key + - '123456' is the passphrase of higher-level CA private_key + - 'tigergraph' is the CN of your sub-certificate + + +6. Import key/certificate pairs into an existing keystore + - `bash ssl_import.sh -import_to_keystore -keystore ./SSL_OUTPUT/server.keystore -cer ./SSL_OUTPUT/ca-root.crt -cerKey ./SSL_OUTPUT/ca-root.key -storepass 123456 -p tiger123` + - './SSL_OUTPUT/server.keystore' is the path of your keystore + - './SSL_OUTPUT/ca-root.crt' is the certificate path to be imported + - './SSL_OUTPUT/ca-root.key' is the certificate private_key path to be imported + - '123456' is the storepass of keystore + - 'tiger123' is the passphrase of the certificate private_key + + +7. Import certificate into an existing truststore + - `bash ssl_import.sh -import_to_truststore -truststore ./SSL_OUTPUT/server.truststore -cer ./SSL_OUTPUT/ca-root.crt -storepass 123456` + - './SSL_OUTPUT/server.truststore' is the path of your truststore + - './SSL_OUTPUT/ca-root.crt' is the certificate path to be imported + - '123456' is the storepass of the truststore \ No newline at end of file diff --git a/tools/kafka_ssl/generate_ssl_CA/ssl_example.sh b/tools/kafka_ssl/generate_ssl_CA/ssl_example.sh new file mode 100644 index 00000000..a16466aa --- /dev/null +++ b/tools/kafka_ssl/generate_ssl_CA/ssl_example.sh @@ -0,0 +1,35 @@ +#! /bin/bash + +cd $(dirname $0) +BASE_DIR=$(pwd) + +broker_hostname=${1:-kafka-0.tigergraph.com} +client_hostname=${2:-tigergraph} +output_path=./SSL_OUTPUT + +cleanup() { +if [ ! -z "${output_path}" -a -d ${output_path} ]; then + rm -fr ${output_path} +fi +} + +# cleanup +cleanup + +## step1: Generate a Certificate Authority (CA) private_key/certificate, keystore and truststore +bash ssl_generate.sh + +## step2: generate and sign Kafka broker private_key/certificate +bash ssl_generate.sh -gen_subCA -cer ${output_path}/ca-root.crt -cerKey ${output_path}/ca-root.key -c ${broker_hostname} + +## step3: import CA key/certificate pairs to keystore +bash ssl_import.sh -import_to_keystore -keystore ${output_path}/server.keystore -cer ${output_path}/ca-root.crt -cerKey ${output_path}/ca-root.key + +## step4: import Kafka broker private_key/certificate in keystore +bash ssl_import.sh -import_to_keystore -keystore ${output_path}/server.keystore -cer ${output_path}/${broker_hostname}.crt -cerKey ${output_path}/${broker_hostname}.key + +## step5: generate and sign client private_key/certificate +bash ssl_generate.sh -gen_subCA -cer ${output_path}/ca-root.crt -cerKey ${output_path}/ca-root.key -c ${client_hostname} + +## step6: import CA certificate in trustStore +bash ssl_import.sh -import_to_truststore -truststore ${output_path}/server.truststore -cer ${output_path}/ca-root.crt \ No newline at end of file diff --git a/tools/kafka_ssl/generate_ssl_CA/ssl_generate.sh b/tools/kafka_ssl/generate_ssl_CA/ssl_generate.sh new file mode 100644 index 00000000..6e64bf12 --- /dev/null +++ b/tools/kafka_ssl/generate_ssl_CA/ssl_generate.sh @@ -0,0 +1,223 @@ +#!/bin/bash + +cd $(dirname $0) +BASE_DIR=$(pwd) + +source_file() { + file=$1 + msg="$2" + src_flag=$3 + if [ -f "$file" ]; then + if [ "$src_flag" != "false" ]; then + source $file + fi + else + echo $(tput setaf 1) "[ERROR ]: $msg" $(tput sgr0) + exit 1 + fi +} + +# source all functions +source_file utils/pretty_print "File utils/pretty_print NOT found, exit" true +source_file utils/env_utils "File utils/env_utils NOT found, exit" true +source_file utils/ssl_utils "File utils/ssl_utils NOT found, exit" true + +OSG=$(get_os) +OS=$(echo "$OSG" | cut -d' ' -f1) +version=$(echo "$OSG" | cut -d' ' -f2) +OSV="$OS$(echo "$version" | cut -d'.' -f1)" + +generate_root=${BASE_DIR}/SSL_OUTPUT +CN=kafka-0.tigergraph.com +storetype=jks +pass=tiger123 +storepass=tiger123 +storeName="" +CA="" +CAkey="" + +CARoot_flag="" +subCA_flag="" +genKeystore_flag="" +genTruststore_flag="" +help_flag="" + +opt_string="hip:c:s:o:n:" +opt_long_string="help,gen_CARoot,gen_subCA,gen_keystore,gen_truststore,passphrase:,output:,storepass:,storetype:,cer:,cerKey:,CN:,name:" +ARGS=`getopt -a -o $opt_string --long $opt_long_string -- "$@"` + +if [ $? != 0 ] ; then exit 1 ; fi +eval set -- "${ARGS}" +while : +do + case $1 in + -h|--help) + help_flag=true + ;; + --gen_CARoot) + CARoot_flag=true + ;; + --gen_subCA) + subCA_flag=true + ;; + --gen_keystore) + genKeystore_flag=true + ;; + --gen_truststore) + genTruststore_flag=true + ;; + --cer) + CA=`path_conver $2` + shift + ;; + --cerKey) + CAkey=`path_conver $2` + shift + ;; + -o|--output) + generate_root=$2 + if [ ! -d ${generate_root} ]; then + warn "The path '$generate_root' does not exist" + prog "start creating output directory..." + mkdir -p $generate_root + fi + generate_root=`path_conver $generate_root` + shift + ;; + -p|--passphrase) + pass=$2 + if [ ${#pass} -lt 6 ];then + error "Password is too short - must be at least 6 characters." + exit 1 + fi + shift + ;; + --storepass) + storepass=$2 + shift + ;; + -c|--CN) + CN=$2 + shift + ;; + -s|--storetype) + storetype=$2 + shift + ;; + -n|--name) + storeName=$2 + shift + ;; + -i|--install) + SETUP_JDK=true + SETUP_OPENSSL=true + ;; + --) + shift + break + ;; + *) + error "${bldred}Invalid option, the correct usage is described below: $txtrst" + generate_help + ;; + esac +shift +done + +if [[ ! -z $help_flag ]]; then + if [[ ! -z $CARoot_flag ]]; then + general_usage gen_CARoot + elif [[ ! -z $subCA_flag ]]; then + general_usage gen_subCA + elif [[ ! -z $genKeystore_flag ]]; then + general_usage gen_keystore + elif [[ ! -z $genTruststore_flag ]]; then + general_usage gen_truststore + else + generate_help + fi + exit 0 +else + # this script only support rhel/centos + prog "Checking operation system (OS) version ..." + check_os $OS $version + + prog "Checking root/sudo ..." + check_root + + # Using option '-i/--install' will install openjdk-1.8.0 and openssl, + # otherwise openjdk-1.8.0 and openssl will not be installed + # install openJDK + install_openJDK + # install openssl + install_openssl + + # If the command is empty, --gen_CARoot, --gen_keystore, and --gen_truststore are executed by default + total_flag=($CARoot_flag $genKeystore_flag $subCA_flag $genTruststore_flag) + if [[ -z $(IFS=,; echo "${total_flag[*]}") ]]; then + CARoot_flag=true + genKeystore_flag=true + genTruststore_flag=true + note "The input command is empty." + note "'--gen_CARoot', '--gen_keystore', and '--gen_truststore' are executed by default." + fi + + # generate root CA + if [[ ! -z $CARoot_flag ]]; then + prog "root-CA output directory: $generate_root" + prog "root-CA CN: $CN" + CA=${generate_root}/ca-root.crt + CAkey=${generate_root}/ca-root.key + + check_file ${CA} 0 + check_file ${CAkey} 0 + generate_CARoot $generate_root $CN $pass + fi + + # generate keystore + if [[ ! -z $genKeystore_flag ]]; then + if [[ -z $storeName ]]; then + keystoreName=server.keystore + else + keystoreName=${storeName}.keystore + fi + prog "keystore output directory: $generate_root" + prog "Keystore -Dname CN: $CN" + prog "keystore name: $keystoreName" + generate_keystore ${generate_root} ${storepass} ${CN} ${storetype} ${keystoreName} + keystore=${generate_root}/${keystoreName} + prog "Generate keystore: $keystore" + note "View keystore: keytool -list -v -keystore $keystore -storepass $pass" + fi + + # generate a sub-certificate using the keytool + if [[ ! -z $subCA_flag ]]; then + prog "Subordinate-CA output directory: $generate_root" + if [[ -z "$CA" || -z "$CAkey" ]]; then + error "Missing options: '-cer' or '-cerKey', exiting..." + general_usage gen_subCA + exit 1 + fi + + check_cert $CA $CAkey $pass + generate_sub_cert $generate_root $CA $CAkey $pass $CN + prog "Generate subordinate-CA: ${CN}.crt successfully" + fi + + # generate truststore + if [[ ! -z ${genTruststore_flag:-} ]]; then + if [[ -z $storeName ]]; then + truststoreName=server.truststore + else + truststoreName=${storeName}.truststore + fi + truststore="${generate_root}/${truststoreName}" + if [ ! -f "${truststore}" ]; then + prog "Generate truststore: ${truststore}" + generate_truststore "${generate_root}" "${truststoreName}" "${storepass}" "${storetype}" + else + warn "${truststore} already exists, skipping generation!" + fi + note "View truststore: keytool -list -v -keystore ${truststore} -storepass ${storepass}" + fi +fi \ No newline at end of file diff --git a/tools/kafka_ssl/generate_ssl_CA/ssl_import.sh b/tools/kafka_ssl/generate_ssl_CA/ssl_import.sh new file mode 100644 index 00000000..c546eec7 --- /dev/null +++ b/tools/kafka_ssl/generate_ssl_CA/ssl_import.sh @@ -0,0 +1,176 @@ +#!/bin/bash + +cd $(dirname $0) +BASE_DIR=$(pwd) + +source_file() { + file=$1 + msg="$2" + src_flag=$3 + if [ -f "$file" ]; then + if [ "$src_flag" != "false" ]; then + source $file + fi + else + echo $(tput setaf 1) "[ERROR ]: $msg" $(tput sgr0) + exit 1 + fi +} + +# source all functions +source_file utils/pretty_print "File utils/pretty_print NOT found, exit" true +source_file utils/env_utils "File utils/env_utils NOT found, exit" true +source_file utils/ssl_utils "File utils/ssl_utils NOT found, exit" true + +OSG=$(get_os) +OS=$(echo "$OSG" | cut -d' ' -f1) +version=$(echo "$OSG" | cut -d' ' -f2) +OSV="$OS$(echo "$version" | cut -d'.' -f1)" + +storepass=tiger123 +CA="" +CAkey="" +pass=tiger123 +keystore="" +truststore="" + +importToKeystore_flag="" +importToTruststore_flag="" +help_flag="" + +opt_string="hip:" +opt_long_string="help,passphrase:,import_to_keystore,storepass:,import_to_truststore,keystore:,truststore:,cer:,cerKey:" +ARGS=`getopt -a -o $opt_string --long $opt_long_string -- "$@"` + +if [ $? != 0 ] ; then exit 1 ; fi +eval set -- "${ARGS}" +while : +do + case $1 in + -h|--help) + help_flag=true + ;; + --import_to_keystore) + importToKeystore_flag=true + ;; + --import_to_truststore) + importToTruststore_flag=true + ;; + --cer) + CA=`path_conver $2` + if [ $? -ne 0 ]; then + error "$CA" + exit 1 + fi + shift + ;; + --cerKey) + CAkey=`path_conver $2` + if [ $? -ne 0 ]; then + error "$CAkey" + exit 1 + fi + shift + ;; + --keystore) + keystore=`path_conver $2` + if [ $? -ne 0 ]; then + error "$keystore" + exit 1 + fi + shift + ;; + --truststore) + truststore=`path_conver $2` + if [ $? -ne 0 ]; then + error "$truststore" + exit 1 + fi + shift + ;; + -p|--passphrase) + pass=$2 + if [ ${#pass} -lt 6 ];then + error "Password is too short - must be at least 6 characters." + exit 1 + fi + shift + ;; + --storepass) + storepass=$2 + shift + ;; + -i|--install) + SETUP_JDK=true + SETUP_OPENSSL=true + ;; + --) + shift + break + ;; + *) + error "${bldred}Invalid option, the correct usage is described below: $txtrst" + import_help + ;; + esac +shift +done + +if [[ ! -z $help_flag ]]; then + if [[ ! -z $importToKeystore_flag ]]; then + general_usage import_to_keystore + elif [[ ! -z $importToTruststore_flag ]]; then + general_usage import_to_truststore + else + import_help + fi + exit 0 +else + # this script only support rhel/centos + prog "Checking operation system (OS) version ..." + check_os $OS $version + + prog "Checking root/sudo ..." + check_root + + # Using option '-i/--install' will install openjdk-1.8.0 and openssl, + # otherwise openjdk-1.8.0 and openssl will not be installed + # install openJDK + install_openJDK + # install openssl + install_openssl + + # enter at least one command + total_flag=($importToKeystore_flag $importToTruststore_flag) + if [[ -z $(IFS=,; echo "${total_flag[*]}") ]]; then + importToKeystore_flag=true + importToTruststore_flag=true + note "The input command is empty." + note "'--import_to_keystore' and '--import_to_truststore' are executed by default." + fi + + # import key-cert pair to keystore + if [[ ! -z $importToKeystore_flag ]]; then + [[ -z "$CA" || -z "$CAkey" || -z "$keystore" ]] \ + && { error "'-keystore', '-cer' and '-cerKey' are required options"; general_usage import_to_keystore; exit 1; } + alias=${CA##*/} + alias=${alias%.*} + prog "Import alias is ${alias}" + check_file ${keystore} 1 + check_file ${CA} 1 + check_file ${CAkey} 1 + import_to_keystore ${keystore} ${alias} ${CAkey} ${CA} ${storepass} ${pass} + fi + + # import certificate to truststore + if [[ ! -z $importToTruststore_flag ]]; then + [[ -z "$CA" || -z "$truststore" ]] \ + && { error "'-truststore' and '-cer' are required options"; general_usage import_to_truststore; exit 1; } + alias=${CA##*/} + alias=${alias%.*} + prog "Import alias is ${alias}" + check_file ${truststore} 1 + check_file ${CA} 1 + import_to_truststore ${truststore} ${CA} ${alias} ${storepass} + fi +fi \ No newline at end of file diff --git a/tools/kafka_ssl/generate_ssl_CA/utils/env_utils b/tools/kafka_ssl/generate_ssl_CA/utils/env_utils new file mode 100644 index 00000000..9bda44f4 --- /dev/null +++ b/tools/kafka_ssl/generate_ssl_CA/utils/env_utils @@ -0,0 +1,307 @@ +#!/bin/bash + +get_os(){ + if [ -f "/etc/apt/sources.list" ]; then + if [ -f "/etc/linx-release" ]; then + os_version=$(cat /etc/linx-release | grep -o '[0-9]\.[0-9]\.[0-9]\{1,3\}' ) + echo "ROCKY $os_version" + elif [ -f "/etc/lsb-release" ]; then + os_version=$(cat /etc/lsb-release | grep "DISTRIB_RELEASE" | cut -d= -f2) + echo "UBUNTU $os_version" + elif [ -f "/etc/os-release" ]; then + os_version=$(cat /etc/os-release | grep "VERSION_ID" | cut -d= -f2) + os_version=${os_version//\"} # remove all double quotes + echo "DEBIAN $os_version" + fi + elif [ -d "/etc/yum.repos.d" ]; then + # Centos and RedHat are treated equally + if grep "Amazon Linux" /etc/system-release &>/dev/null; then + os_version=" 7.0" + else + variant="$(cat /etc/system-release | cut -d ' ' -f2)" + if [ "$variant" = "Stream" ]; then + os_version=" $(cat /etc/os-release | grep 'VERSION_ID=' | cut -d'"' -f 2)" + else + os_version="$(cat /etc/system-release | grep -o ' [0-9]\.[0-9]\{1,3\}')" + fi + fi + echo "RHEL$os_version" + elif [ -d "/etc/zypp/repos.d" ]; then + os_version=$(cat /etc/os-release | grep "VERSION_ID" | cut -d= -f2) + os_version=${os_version//\"} # remove all double quotes + echo "SUSE $os_version" + else + echo "UNKOWN OS" + fi +} + +check_os(){ + OS=$1 + version=$2 + note "OS obtained: $OS $version" + local error_msg="Unsupported OS. Current support CentOS 6.5 to 8.0; RedHat 6.5 to 9.0;" + if [ -z "$version" ]; then + error "Unknown OS version. $error_msg" + exit 1 + fi + + if [ "$OS" = "RHEL" ]; then + # the following one will end with one item array on docker centos 7.3, i.e. "${ver_arr[0]}" is "7 3" + # local ver_arr=(${version//./ }) + IFS='.' read -r -a ver_arr <<< "$version" + if [[ "${ver_arr[0]}" -lt "6" || "${ver_arr[0]}" -eq "6" && "${ver_arr[1]}" -lt "5" ]]; then + error "$error_msg" + exit 1 + else + note "OS check passed [OK]" + fi + else + error "$error_msg" + exit 1 + fi +} + +check_root(){ + if [[ $EUID -ne 0 ]]; then + error "Sudo or root rights are required." + exit 1 + fi +} + +install_openJDK() { + if [[ "${SETUP_JDK}" == "true" ]]; then + if ! which java > /dev/null 2>&1; then + prog "start install openjdk-1.8.0." + yum install -y java-1.8.0-openjdk > /dev/null 2>&1 + else + java_version=$(java -version 2>&1 | awk -F '"' '/version/ {print $2}') + if [[ ("$(echo "$java_version" | cut -d'.' -f1)" -ge 1 && "$(echo "$java_version" | cut -d'.' -f2)" -ge 8) || "$(echo "$java_version" | cut -d'.' -f1)" -ge 11 ]]; then + prog "The installed Java version is greater than 1.8.0. No update necessary." + else + prog "start update to openjdk-1.8.0." + rpm -qa | grep java | xargs rpm -e --nodeps + yum install -y java-1.8.0-openjdk > /dev/null 2>&1 + fi + fi + # check install + if command -v java &> /dev/null; then + prog "JDK Install Success..." + else + error "JDK Install Fail..." && exit 1 + fi + else + if ! which java > /dev/null 2>&1; then + error "Java environment not detected. You can choose option \"-i\" to install openjdk-1.8.0." + exit 1 + else + java_version=$(java -version 2>&1 | awk -F '"' '/version/ {print $2}') + if [[ ("$(echo "$java_version" | cut -d'.' -f1)" -ge 1 && "$(echo "$java_version" | cut -d'.' -f2)" -ge 8) || "$(echo "$java_version" | cut -d'.' -f1)" -ge 11 ]]; then + prog "The java version is openjdk-1.8.0 or later now." + else + error "This script currently only supports openjdk-1.8.0 or later." + exit 1 + fi + fi + fi +} + +install_openssl(){ + if [[ "${SETUP_OPENSSL}" == "true" ]]; then + if ! openssl version > /dev/null 2>&1;then + prog "start install openssl..." + yum -y install openssl > /dev/null 2>&1 + else + prog "Openssl is installed, skip installation." + fi + + if [ $? != 0 ]; then + error "Failed to install openssl." + exit 1 + fi + else + if ! openssl version > /dev/null 2>&1;then + error "Openssl is not installed. You can choose option \"-i\" to install openssl." + exit 1 + else + prog "Openssl is installed." + fi + fi +} + +path_conver(){ + local rel_path="$1" + local abs_path + + # If the path is already absolute, return it as-is + if [[ "$rel_path" == /* ]]; then + abs_path="$rel_path" + else + abs_path="$(pwd)/$rel_path" + fi + + # Check if the path exists before calling realpath + if [[ ! -e "$abs_path" ]]; then + echo "Path $abs_path does not exist" + return 1 + fi + + # Canonicalize the path to remove any redundant elements + abs_path="$(realpath "$abs_path")" + + echo "$abs_path" +} + +check_file(){ + file=$1 + type=$2 + if [ -f "$file" ];then + if [[ "${type}" == "0" ]]; then + error "$file already exists, exit." + exit 1 + fi + elif [[ "${type}" == "1" || "${type}" == "" ]]; then + error "$file not exist, exit." + exit 1 + fi +} + +generate_help(){ + echo + echo "Commands:" + echo " -h,-help -- Show the help" + echo " -gen_CARoot -- Generate root-CA and root-CA private_key" + echo " -gen_keystore -- Generate keystore" + echo " -gen_truststore -- Generate an empty truststore" + echo " -gen_subCA -- Generate subordinate CA through superior certificate" + echo + warn "Using option '-i' will install openjdk-1.8.0 and openssl, otherwise openjdk-1.8.0 and openssl will not be installed. + Please note that if the local machine does not have a java environment, some operations will fail." + echo + warn "If the command is empty, '--gen_CARoot', '--gen_keystore', and '--gen_truststore' are executed by default." + echo + note "Use './`basename $0` -command_name -help' for usage of command_name" + echo + exit 0 +} + +import_help(){ + echo + echo "Commands:" + echo " -h,-help -- Show the help" + echo " -import_to_keystore -- Import certificate and certificate_private_key pair to keystore" + echo " -import_to_truststore -- Import certificate to truststore" + echo + warn "Using option '-i' will install openjdk-1.8.0 and openssl, otherwise openjdk-1.8.0 and openssl will not be installed. + Please note that if the local machine does not have a java environment, some operations will fail." + echo + note "Use './`basename $0` -command_name -help' for usage of command_name" + echo + exit 0 +} + +general_usage(){ + usage_flag=$1 + case $usage_flag in + gen_CARoot) + echo + echo "Usage:" + mesg_green "./`basename $0` [--gen_CARoot] [-o ] [-c ] [-p ]" + echo "Example:" + mesg_blue "./`basename $0` --gen_CARoot -o ./SSL_OUTPUT -c kafka-0.tigergraph.com -p tiger123" + mesg_blue "./`basename $0` --gen_CARoot -c kafka-0.tigergraph.com" + echo + echo "Options:" + echo " -o,-output -- Certificate file output Path [default: ./SSL_OUTPUT]" + echo " -c,-CN -- Subject CN, accept wildcard domain name [default: kafka-0.tigergraph.com]" + echo " -p,-passphrase -- PASSPHRASE of CA private key (optional) [default: tiger123]" + echo + exit 0 + ;; + gen_keystore) + echo + echo "Usage:" + mesg_green "./`basename $0` [--gen_keystore] [-o ] [-s ] [-storepass ] [-c ] [-n ]" + echo "Example:" + mesg_blue "./`basename $0` --gen_keystore -o ./SSL_OUTPUT -s pkcs12 -storepass tiger123 -c kafka-0.tigergraph.com -n server.keystore.pk12" + mesg_blue "./`basename $0` --gen_keystore -storepass tiger123 -c kafka-0.tigergraph.com" + mesg_blue "./`basename $0` --gen_keystore -s jks -n server.keystore.jks" + mesg_blue "./`basename $0` --gen_keystore" + echo + echo "Options:" + echo " -o,-output -- Output directory [default: ./SSL_OUTPUT]" + echo " -s,-storetype -- Keystore storetype, e.g. jks, pkcs12 [default: jks]" + echo " -c,-CN -- Subject CN, accept wildcard domain name [default: kafka-0.tigergraph.com]" + echo " -storepass -- Keystore password [default: tiger123]" + echo " -n,-name -- Keystore file name, e.g. server.keystore.jks [default: server.keystore]" + echo + note "'storepass' is the password used to secure the keystore as a whole. " + echo + exit 0 + ;; + gen_subCA) + echo + echo "Usage:" + mesg_green "./`basename $0` [--gen_subCA] [-o ] [-cer ] [-cerKey ] [-p ] [-c ]" + echo "Example:" + mesg_blue "./`basename $0` --gen_subCA -o ./SSL_OUTPUT -cer ./SSL_OUTPUT/tigergraph.com.crt -cerKey ./SSL_OUTPUT/tigergraph.com.key -p tiger123 -c kafka-0.tigergraph.com" + mesg_blue "./`basename $0` --gen_subCA -cer ./SSL_OUTPUT/tigergraph.com.crt -cerKey ./SSL_OUTPUT/tigergraph.com.key -c kafka-0.tigergraph.com" + echo + echo "Options:" + echo " -o,-output -- Output directory [default: ./SSL_OUTPUT]" + echo " -cer,--cer -- Superior certificate used to sign subordinate certificate" + echo " -cerKey -- Superior certificate private key file" + echo " -p,-passphrase -- Passphrase of superior certificate private key [default: tiger123]" + echo " -c,-CN -- Subject CN, accept wildcard domain name [default: kafka-0.tigergraph.com]" + echo + exit 0 + ;; + gen_truststore) + echo + echo "Usage:" + mesg_green "./`basename $0` [--gen_truststore] [-o ] [-storepass ] [-s ] [-n ]" + echo "Example:" + mesg_blue "./`basename $0` --gen_truststore -o ./SSL_OUTPUT -storepass tiger123 -s pkcs12 -n server.truststore.pk12" + mesg_blue "./`basename $0` --gen_truststore -storepass tiger123" + mesg_blue "./`basename $0` --gen_truststore" + echo + echo "Options:" + echo " -o,-output -- Output directory [default: ./SSL_OUTPUT]" + echo " -storepass -- Truststore password [default: tiger123]" + echo " -s,-storetype -- Truststore storetype, e.g. jks, pkcs12 [default: jks]" + echo " -n,-name -- Truststore file name, e.g. server.truststore.jks [default: server.truststore]" + echo + exit 0 + ;; + import_to_keystore) + echo + echo "Usage:" + mesg_green "./`basename $0` [--import_to_keystore] [-keystore ] [-cer ] [-cerKey ] [-storepass ] [-p ]" + echo "Example:" + mesg_blue "./`basename $0` --import_to_keystore -keystore ./SSL_OUTPUT/server.keystore -cer ./SSL_OUTPUT/ca-root.crt -cerKey ./SSL_OUTPUT/ca-root.key -storepass 123456 -p tiger123" + mesg_blue "./`basename $0` --import_to_keystore -keystore ./SSL_OUTPUT/server.keystore -cer ./SSL_OUTPUT/ca-root.crt -cerKey ./SSL_OUTPUT/ca-root.key -storepass 123456" + echo + echo "Options:" + echo " -keystore -- Keystore path" + echo " -cer,--cer -- Certificate file path" + echo " -cerKey -- Certificate private key file path" + echo " -storepass -- Keystore password [default: tiger123]" + echo " -p,-passphrase -- Passphrase for the certificate private key [default: tiger123]" + echo + exit 0 + ;; + import_to_truststore) + echo + echo "Usage:" + mesg_green "./`basename $0` [--import_to_truststore] [-truststore ] [-cer ] [-storepass ]" + echo "Example:" + mesg_blue "./`basename $0` --import_to_truststore -truststore ./SSL_OUTPUT/server.truststore -cer ./SSL_OUTPUT/ca-root.crt -storepass tiger123" + echo + echo "Options:" + echo " -truststore -- Truststore path" + echo " -cer -- Certificate file path" + echo " -storepass -- Truststore password [default: tiger123]" + echo + exit 0 + ;; + esac +} \ No newline at end of file diff --git a/tools/kafka_ssl/generate_ssl_CA/utils/pretty_print b/tools/kafka_ssl/generate_ssl_CA/utils/pretty_print new file mode 100644 index 00000000..972e689c --- /dev/null +++ b/tools/kafka_ssl/generate_ssl_CA/utils/pretty_print @@ -0,0 +1,52 @@ +#!/bin/bash + +txtbld=$(tput bold) +bldblk=${txtbld}$(tput setaf 0) # black +bldred=${txtbld}$(tput setaf 1) # red +bldgre=${txtbld}$(tput setaf 2) # green +bldyel=${txtbld}$(tput setaf 3) # yellow +bldblu=${txtbld}$(tput setaf 4) # blue +bldmag=${txtbld}$(tput setaf 5) # magenta +bldcya=${txtbld}$(tput setaf 6) # cyan +bldwhi=${txtbld}$(tput setaf 7) # white +txtrst=$(tput sgr0) # Reset + +error(){ + echo "${bldred}[ERROR ]: $* $txtrst" +} + +warn(){ + echo "${bldyel}[WARNING ]: $* $txtrst" +} + +note(){ + echo "${bldcya}[NOTE ]: $* $txtrst" +} + +prog(){ + echo "${bldgre}[PROGRESS]: $(date +"%T") $* $txtrst" +} + +mesg_red(){ + echo "${bldred}$* $txtrst" +} + +mesg_green(){ + echo "${bldgre}$* $txtrst" +} + +mesg_yellow(){ + echo "${bldyel}$* $txtrst" +} + +mesg_blue(){ + echo "${bldblu}$* $txtrst" +} + +mesg_cyan(){ + echo "${bldcya}$* $txtrst" +} + +mesg_mag(){ + echo "${bldmag}$* $txtrst" +} diff --git a/tools/kafka_ssl/generate_ssl_CA/utils/ssl_utils b/tools/kafka_ssl/generate_ssl_CA/utils/ssl_utils new file mode 100644 index 00000000..1a7dbfd0 --- /dev/null +++ b/tools/kafka_ssl/generate_ssl_CA/utils/ssl_utils @@ -0,0 +1,268 @@ +#!/bin/bash + +generate_CARoot() { + local generate_root=$1 + local CN=$2 + local ca_passphrase=$3 + local ca_key=${generate_root}/ca-root.key + local ca_cert=${generate_root}/ca-root.crt + local ca_days=365 + + if [ ! -d ${generate_root} ]; then + mkdir -p ${generate_root} + fi + + cd $generate_root + if [ -z "$CN" ]; then + error "subject CN is empty" + exit 1 + fi + + # Generate the private key + openssl genrsa -aes256 -passout pass:"$ca_passphrase" -out "$ca_key" 4096 + # Generate the self-signed certificate + openssl req -x509 -new -nodes -key "$ca_key" -sha256 -days "$ca_days" -subj "/CN=$CN" -passin pass:"$ca_passphrase" -out "$ca_cert" + prog "CARoot path: ${generate_root}/ca-root.crt" + prog "private_key path: ${generate_root}/ca-root.key" + prog "passphrase for private_key: ${ca_passphrase}" +} + +check_cert() { + local cert_path=$1 + local key_path=$2 + local key_pass=$3 + + if [ ! -f $cert_path ];then + error "Certificate file $cert_path does not exist." + exit 1 + elif [ ! -f $key_path ];then + error "Certificate private-key file $key_path does not exist." + exit 1 + fi + + prog "Start checking if the private key can be decrypted with the given password..." + if ! openssl rsa -in "${key_path}" -passin "pass:${key_pass}" -check > /dev/null 2>&1; then + error "The private key is incorrect." + error "Certificate private-key file: $key_path" + error "private-key passphrase: ${key_pass}" + exit 1 + fi + + prog "Start checking if the certificate matches the private key..." + if ! openssl x509 -noout -modulus -in "${cert_path}" | openssl md5 > /tmp/cert.md5; then + error "Failed to compute the certificate md5." >&2 + exit 1 + fi + + if ! openssl rsa -noout -modulus -in "${key_path}" -passin "pass:${key_pass}" | openssl md5 > /tmp/key.md5; then + error "Failed to compute the private key md5." >&2 + exit 1 + fi + + if ! diff /tmp/cert.md5 /tmp/key.md5 > /dev/null; then + error "The certificate and private key do not match." + error "Certificate file: ${cert_path}" + error "Certificate private-key file: ${key_path}" + exit 1 + fi + + prog "The certificate, private key, and password are all correct." + rm /tmp/cert.md5 /tmp/key.md5 +} + +### generate an empty keystore +# 1. default storetype is JKS +# 2. validity is 365 +generate_keystore(){ + local genRoot=$1 + local pass=$2 + local CN=$3 + local storetype=$4 + local keystoreName=$5 + + if [ ! -d ${genRoot} ]; then + mkdir -p ${genRoot} + fi + + cd $genRoot + prog "Start creating keystore..." + case $storetype in + pkcs12|PKCS12|pk12|PK12|p12|P12) + prog "The storetype of keystore is pkcs12." + keytool -genkey -keystore ${keystoreName} -validity 365 -storepass $pass -keypass $pass -dname "CN=$CN" -alias $CN -storetype pkcs12 + ;; + *) + prog "The default storetype of keystore is JKS." + keytool -keystore ${keystoreName} -validity 365 -genkey -keyalg RSA -dname "CN=$CN" -alias $CN -storepass ${pass} -keypass ${pass} -storetype jks + ;; + esac + + if [ $? -ne 0 ]; then + exit 1 + fi +} + +check_keystore(){ + local keystore=$1 + local pass=$2 + local keystoreType="" + + if [ -f "$keystore" ]; then + keystoreType=$(keytool -list -v -keystore $keystore -storepass $pass |& awk '/Keystore type/{print $NF}') + else + error "$keystore does not exist" + exit 1 + fi + + if [[ $keystoreType != "jks" && $keystoreType != "PKCS12" ]]; then + error "$keystore is not a supported keystore type (JKS or PKCS12)" + exit 1 + fi +} + +generate_sub_cert() { + local generate_root=$1 + local parent_cert=$2 # path to the parent certificate file + local parent_key=$3 # path to the parent private key file + local parent_key_pass=$4 # password for the parent private key file + local sub_name=$5 # name for the sub-certificate + + local sub_key=${sub_name}.key + local sub_cert=${sub_name}.crt + + if [ ! -d ${generate_root} ]; then + mkdir -p ${generate_root} + fi + + cd ${generate_root} + if [ -f ${sub_cert} -o -f ${sub_key} ]; then + error "${sub_cert} or ${sub_key} already exists" + exit 1 + fi + + # Create a new RSA private key for the sub-certificate + openssl genrsa -out $sub_key 2048 + + # Create a certificate signing request (CSR) for the sub-certificate + openssl req -new -key $sub_key -out /tmp/sub.csr -subj "/CN=${sub_name}" + + # Sign the CSR with the parent certificate and private key to generate the sub-certificate + prog "sign sub-certificate through $parent_cert..." + openssl x509 -req -in /tmp/sub.csr -CA $parent_cert -CAkey $parent_key -passin pass:$parent_key_pass -out $sub_cert -days 365 -sha256 -CAcreateserial + + # Remove the temporary files + rm /tmp/sub.csr +} + +# import keycert pair to keystore +import_to_keystore() { + local keystore=$1 + local alias=$2 + local keyfile=$3 + local certfile=$4 + local keystorepass=$5 + local keypass=$6 + local password_file="/tmp/password" + + # Check if the keystore exists and the keystore password is correct + if ! keytool -list -keystore "$keystore" -storepass "$keystorepass" >/dev/null 2>&1; then + error "Keystore not found or incorrect keystore password" + exit 1 + fi + + # Check that the key password matches the private key file + if [ -n "$keypass" ]; then + if ! openssl rsa -in "$keyfile" -passin pass:"$keypass" -noout >/dev/null 2>&1; then + error "Incorrect key password" + exit 1 + fi + fi + + # Export the key/certificate pair to a PKCS12 file + printf "%s" "$keypass" > "$password_file" + if openssl pkcs12 -export -in "$certfile" -inkey "$keyfile" -out /tmp/keycert.p12 -name "$alias" -passin file:"$password_file" -passout pass:"$keypass"; then + prog "Successfully exported key/certificate pair to PKCS12 file" + else + error "Error exporting key/certificate pair to PKCS12 file" + exit 1 + fi + rm -f "$password_file" + + # Import the key/certificate pair into the keystore + printf "%s\n%s\n" "$keypass" "$keystorepass" | keytool -importkeystore -srckeystore /tmp/keycert.p12 -srcstoretype PKCS12 -srcalias "$alias" \ + -destalias "$alias" -destkeystore "$keystore" -deststoretype JKS -deststorepass "$keystorepass" \ + -destkeypass "$keypass" -storepass "$keystorepass" -keypass "$keypass" -noprompt + + if [ $? -eq 0 ]; then + prog "Successfully imported key/certificate pair to keystore" + else + error "Error importing key/certificate pair to keystore" + exit 1 + fi + + note "To check whether the certificate already exists, run:" + note "keytool -list -v -keystore $keystore -storepass $keystorepass" +} + +# generate an empty truststore +generate_truststore(){ + local generate_root=$1 + local truststoreName=$2 + local pass=$3 + local storetype=$4 + local firstFile=${generate_root}/firstCA.crt + + if [ ! -d ${generate_root} ]; then + mkdir -p ${generate_root} + fi + + cd $generate_root + if [ ! -f "$firstFile" ]; then + openssl req -nodes -new -x509 -keyout firstCA.key -out firstCA.crt -days 365 -subj "/CN=myFirstCA" + fi + + prog "Start creating truststore and import one CA..." + case $storetype in + pkcs12|PKCS12|pk12|PK12|p12|P12) + prog "The storetype of keystore is pkcs12." + keytool -keystore ${truststoreName} -alias myfirstCA -import -file ${firstFile} -storepass ${pass} -keypass ${pass} -storetype pkcs12 -noprompt + ;; + *) + prog "The default storetype of keystore is JKS." + keytool -keystore ${truststoreName} -alias myfirstCA -import -file ${firstFile} -storepass ${pass} -keypass ${pass} -storetype jks -noprompt + ;; + esac + + if [ $? -ne 0 ]; then + rm -rf firstCA* + exit 1 + fi + + keytool -delete -alias myfirstCA -keystore ${truststoreName} -storepass ${pass} + rm -rf firstCA* +} + +import_to_truststore() { + local truststore=$1 # path to the truststore file + local import_file=$2 # path to the certificate file to be imported + local alias=$3 # alias for the certificate entry + local storepass=$4 # truststore password + + # Check if the truststore password is correct + keytool -list -keystore "$truststore" -storepass "$storepass" >/dev/null 2>&1 + if [ $? -ne 0 ]; then + error "Incorrect truststore password" + exit 1 + fi + + # Import the certificate into the truststore + keytool -import -noprompt -trustcacerts -alias "$alias" -file "$import_file" -keystore "$truststore" -storepass "$storepass" + if [ $? -ne 0 ]; then + error "Error importing certificate to truststore" + warn "If alias already exists, you can rename the import-certificate name and import it again." + exit 1 + fi + + prog "Certificate successfully imported to truststore" + note "How to check whether the certificate already exists: keytool -list -v -keystore ${truststore} -storepass ${storepass}" +} \ No newline at end of file