Требования к SSL-сертификатам
Для реализации шифрования между узлами ADH выполните шаги, описанные ниже.
-
Создайте сертификат для каждого узла, используя алгоритм RSA. Длина ключа должна быть 2048 бит, значение CN должно содержать FQDN соответствующего узла. Не рекомендуется использовать алгоритм DSA, а также алгоритм ECDSA из-за того, что этот алгоритм не очень широко поддерживается сертификационными центрами.
Длина ключа, равная 2048 битам, является необходимой и достаточной для реализации надежного шифрования.
-
Создайте хранилище ключей trustore.jks, которое будет содержать необходимые сертификаты. Путь к этому хранилищу должен быть указан для запуска SSL.
ПРИМЕЧАНИЕ-
Сертификаты должны быть подписаны ключом, принадлежащим одному из доверенных корневых сертификатов, включенных в хранилище сертификатов Java.
-
Самоподписанные сертификаты разрешены. В этом случае корневой сертификат должен быть добавлен в хранилище trustore.jks.
-
Nginx требует сертификат не Java-формата. Если ваш кластер использует Nginx (с Hive Tez UI или Airflow), сгенерируйте ключи и сертификаты другого типа. SSL-сертификат для вашего хоста следует добавить в хранилище доверенных системных сертификатов.
-
Учетная запись, используемая для установки сертификатов, должна иметь права на запись в следующие директории:
-
/etc/pki/tls/certs/
-
/etc/pki/java/
-
-
В качестве alias ключа в keystore.jks используйте FQDN, а не короткое имя.
-
-
Включите трафик между узлами в соответствии с приведенной ниже таблицей.
Приведенный ниже скрипт создает все необходимые сущности и может быть использован для справки:
#!/bin/bash
set -euo pipefail
echo "# SSL preparation script started"
if [ -f ./crtdistr-config.cfg ]; then
echo "## Config found"
else
echo "## Error: configuration file ./crtdistr-config.cfg not found"
exit 1
fi
HOSTS_MAKE="${HOSTS-}"
source ./crtdistr-config.cfg
if [ -n "$HOSTS_MAKE" ]; then
echo "## HOSTS provided from MakeFile"
read -r -a HOSTS <<< "$HOSTS_MAKE"
fi
SSH_HOSTS=()
SSH_OPTS='-o GlobalKnownHostsFile=/dev/null -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -o LogLevel=error'
TMP_DIR="/tmp/crtdistr"
CA_KEY="$TMP_DIR/ca_admprom.key"
CA_CERT="$TMP_DIR/ca_certs.crt"
CA_ALIAS="root-ca"
get_cert_paths() {
local host="$1"
if ssh $SSH_OPTS "$host" 'grep "^ID=" /etc/os-release' 2>/dev/null | grep -qE 'astra'; then
CA_CERTS_PATH="/usr/local/share/ca-certificates/"
CA_UPDATE_CMD="update-ca-certificates"
HOST_CERT_NAME="host_cert.crt"
JAVA_CA_PATH="/usr/lib/jvm/java-arenadata-openjdk-8/jre/lib/security/cacerts"
JAVA_CA_PATH_17="/usr/lib/jvm/java-arenadata-openjdk-17/lib/security/cacerts"
elif ssh $SSH_OPTS "$host" 'grep "^ID=" /etc/os-release' 2>/dev/null | grep -qE 'ubuntu'; then
CA_CERTS_PATH="/usr/local/share/ca-certificates"
CA_UPDATE_CMD="update-ca-certificates"
HOST_CERT_NAME="host_cert.crt"
JAVA_CA_PATH="/usr/lib/jvm/java-arenadata-openjdk-8/jre/lib/security/cacerts"
JAVA_CA_PATH_17="/usr/lib/jvm/java-arenadata-openjdk-17/lib/security/cacerts"
elif ssh $SSH_OPTS "$host" 'grep "^ID=" /etc/os-release' 2>/dev/null | grep -qE 'rhel|centos|altlinux|redos'; then
CA_CERTS_PATH="/etc/pki/ca-trust/source/anchors"
CA_UPDATE_CMD="update-ca-trust"
HOST_CERT_NAME="host_cert.cert"
JAVA_CA_PATH="/usr/lib/jvm/java-arenadata-openjdk-8/jre/lib/security/cacerts"
JAVA_CA_PATH_17="/usr/lib/jvm/java-arenadata-openjdk-17/lib/security/cacerts"
else
CA_CERTS_PATH=""
CA_UPDATE_CMD=""
HOST_CERT_NAME=""
JAVA_CA_PATH=""
JAVA_CA_PATH_17=""
fi
}
echo "# Pre-check"
for HOST in "${HOSTS[@]}"; do
echo "## Checking ssh connection to host $HOST"
if ssh $SSH_OPTS -q "$HOST" "exit"; then
get_cert_paths "$HOST"
if [[ -z "$CA_CERTS_PATH" ]]; then
echo "### Unknown OS type on $HOST, skipped"
elif [[ -z "$JAVA_CA_PATH" ]]; then
echo "### Java cacerts path not detected on $HOST, skipped"
else
echo "### $HOST is OK"
SSH_HOSTS+=("$HOST")
fi
else
echo "### SSH connection failed for $HOST"
fi
done
if [[ "${#SSH_HOSTS[@]}" -eq 0 ]]; then
echo "## No reachable hosts found"
exit 1
fi
rm -rf "$TMP_DIR"
mkdir -p "$TMP_DIR"
echo "# Creating local CA"
openssl genrsa -out "$CA_KEY" 4096
openssl req -x509 \
-nodes \
-key "$CA_KEY" \
-days 1095 \
-out "$CA_CERT" \
-subj "/CN=marsnet.local CA"
echo "# Creating server certificates"
for SSH_HOST in "${SSH_HOSTS[@]}"; do
echo "## Generate key and CSR for $SSH_HOST"
openssl req \
-newkey rsa:4096 \
-nodes \
-keyout "$TMP_DIR/$SSH_HOST.key" \
-out "$TMP_DIR/$SSH_HOST.host.csr" \
-subj "/C=RU/ST=Denial/L=MSK/O=AD/CN=$SSH_HOST"
echo "## Sign certificate for $SSH_HOST"
openssl x509 \
-req \
-in "$TMP_DIR/$SSH_HOST.host.csr" \
-CA "$CA_CERT" \
-CAkey "$CA_KEY" \
-CAcreateserial \
-out "$TMP_DIR/$SSH_HOST.crt" \
-days 365 \
-extfile <(printf "basicConstraints=critical,CA:FALSE\nkeyUsage=digitalSignature,keyEncipherment\nextendedKeyUsage=serverAuth,clientAuth\nsubjectAltName=DNS:$SSH_HOST\n")
done
echo "# Distributing certificates"
for SSH_HOST in "${SSH_HOSTS[@]}"; do
echo "## Copy files to $SSH_HOST"
scp $SSH_OPTS "$TMP_DIR/$SSH_HOST.crt" "$SSH_HOST:/tmp/"
scp $SSH_OPTS "$TMP_DIR/$SSH_HOST.key" "$SSH_HOST:/tmp/"
scp $SSH_OPTS "$CA_CERT" "$SSH_HOST:/tmp/"
for node in "${SSH_HOSTS[@]}"; do
if [[ "$SSH_HOST" != "$node" ]]; then
scp $SSH_OPTS "$TMP_DIR/$node.crt" "$SSH_HOST:/tmp/$node.crt"
fi
done
done
echo "# Configuring remote hosts"
for SSH_HOST in "${SSH_HOSTS[@]}"; do
echo "## Install certificate and build keystore/truststore on $SSH_HOST"
get_cert_paths "$SSH_HOST"
ssh $SSH_OPTS "$SSH_HOST" "
set -eo pipefail
export KEYSTOREPASS='$KEYSTOREPASS'
export TRUSTSTOREPASS='$TRUSTSTOREPASS'
sudo rm -f /etc/ssl/host.pem /etc/ssl/host.key /etc/ssl/ca_certs.crt
sudo mkdir -p /etc/ssl
sudo cp /tmp/$SSH_HOST.crt /etc/ssl/host.pem
sudo cp /tmp/$SSH_HOST.key /etc/ssl/host.key
sudo cp /tmp/ca_certs.crt /etc/ssl/ca_certs.crt
set +u
source /usr/lib/bigtop-utils/bigtop-detect-javahome
set -u
# Удаление старых алиасов в keystore
sudo \$JAVA_HOME/bin/keytool -delete \
-alias $SSH_HOST \
-keystore $KEYSTOREPATH \
-storepass \$KEYSTOREPASS >/dev/null 2>&1 || true
sudo \$JAVA_HOME/bin/keytool -delete \
-alias $CA_ALIAS \
-keystore $KEYSTOREPATH \
-storepass \$KEYSTOREPASS >/dev/null 2>&1 || true
# Удаление старого алиаса CA в truststore
sudo \$JAVA_HOME/bin/keytool -delete \
-alias $CA_ALIAS \
-keystore $TRUSTSTOREPATH \
-storepass \$TRUSTSTOREPASS >/dev/null 2>&1 || true
sudo cp /etc/ssl/host.pem $CA_CERTS_PATH/$HOST_CERT_NAME
sudo cp /tmp/ca_certs.crt $CA_CERTS_PATH/ca_certs.crt
sudo $CA_UPDATE_CMD
sudo chown root:hadoop /etc/ssl/host.key
sudo chmod 640 /etc/ssl/host.key
sudo setfacl -m u:airflow:r /etc/ssl/host.key
openssl pkcs12 -export \
-in /tmp/$SSH_HOST.crt \
-inkey /tmp/$SSH_HOST.key \
-certfile /tmp/ca_certs.crt \
-name $SSH_HOST \
-out /tmp/$SSH_HOST.p12 \
-password pass:\$KEYSTOREPASS
sudo \$JAVA_HOME/bin/keytool -importkeystore \
-srckeystore /tmp/$SSH_HOST.p12 \
-srcstoretype PKCS12 \
-srcstorepass \$KEYSTOREPASS \
-destkeystore $KEYSTOREPATH \
-deststoretype JKS \
-deststorepass \$KEYSTOREPASS \
-alias $SSH_HOST \
-noprompt
sudo \$JAVA_HOME/bin/keytool -import \
-noprompt \
-alias $CA_ALIAS \
-file /tmp/ca_certs.crt \
-keystore $KEYSTOREPATH \
-storepass \$KEYSTOREPASS \
-trustcacerts
sudo \$JAVA_HOME/bin/keytool -import \
-noprompt \
-alias $CA_ALIAS \
-file /tmp/ca_certs.crt \
-keystore $TRUSTSTOREPATH \
-storepass \$TRUSTSTOREPASS \
-trustcacerts
# Для Haproxy
sudo bash -c 'cat /etc/ssl/host.key /etc/ssl/host.pem /etc/ssl/ca_certs.crt > /etc/ssl/serverkey.pem'
sudo chmod 600 /etc/ssl/serverkey.pem
"
# Импорт сертификатов всех узлов в truststore с удалением старых алиасов
for node in "${SSH_HOSTS[@]}"; do
ssh $SSH_OPTS "$SSH_HOST" "
set +u
source /usr/lib/bigtop-utils/bigtop-detect-javahome
set -u
sudo \$JAVA_HOME/bin/keytool -delete \
-alias $node \
-keystore $TRUSTSTOREPATH \
-storepass $TRUSTSTOREPASS >/dev/null 2>&1 || true
sudo \$JAVA_HOME/bin/keytool -import \
-noprompt \
-alias $node \
-file /tmp/$node.crt \
-keystore $TRUSTSTOREPATH \
-storepass $TRUSTSTOREPASS \
-trustcacerts
"
done
# Импорт truststore в системные Java cacerts с предварительным удалением алиасов
ssh $SSH_OPTS "$SSH_HOST" "
set -eo pipefail
set +u
source /usr/lib/bigtop-utils/bigtop-detect-javahome
set -u
# Удаление всех известных алиасов из целевых cacerts перед импортом
for ALIAS in $CA_ALIAS ${SSH_HOSTS[@]}; do
sudo \$JAVA_HOME/bin/keytool -delete \
-alias \$ALIAS \
-keystore $JAVA_CA_PATH_17 \
-storepass changeit >/dev/null 2>&1 || true
sudo \$JAVA_HOME/bin/keytool -delete \
-alias \$ALIAS \
-keystore $JAVA_CA_PATH \
-storepass changeit >/dev/null 2>&1 || true
done
# Импорт в Java 17 cacerts
sudo \$JAVA_HOME/bin/keytool -importkeystore \
-noprompt \
-srckeystore $TRUSTSTOREPATH \
-srcstorepass $TRUSTSTOREPASS \
-destkeystore $JAVA_CA_PATH_17 \
-deststorepass changeit || true
# Импорт в Java 8 cacerts
sudo \$JAVA_HOME/bin/keytool -importkeystore \
-noprompt \
-srckeystore $TRUSTSTOREPATH \
-srcstorepass $TRUSTSTOREPASS \
-destkeystore $JAVA_CA_PATH \
-deststorepass changeit || true
sudo rm -f /tmp/$SSH_HOST.p12 /tmp/*.crt /tmp/*.key
" || {
echo "Failed to configure certs on $SSH_HOST"
exit 1
}
done
echo "# Cleanup local temp files"
rm -rf "$TMP_DIR"
echo "# All done successfully!"
#!/usr/bin/env sh
# Пароль KEYSTORE
KEYSTOREPASS='bigdata'
# Путь создания KEYSTORE
KEYSTOREPATH='/etc/ssl/keystore.jks'
# Пароль TRUSTORE
TRUSTSTOREPASS='bigdata'
# Путь создания TRUSTORE
TRUSTSTOREPATH='/etc/ssl/truststore.jks'
# DNS-имена хостов для выполнения раскладки сертификатов
HOSTS=(
"prefix-adh-1.ru-central1.internal"
"prefix-adh-2.ru-central1.internal"
"prefix-adh-3.ru-central1.internal"
"prefix-adps-1.ru-central1.internal"
)
Права доступа к хранилищам ключей
После генерации файлов keystore и truststore необходимо предоставить этим файлам достаточные права доступа, чтобы ADH-сервисы могли получить к ним доступ.
Ниже приведен пример предоставления прав доступа (минимально необходимые права с точки зрения безопасности):
$ sudo chown root:hadoop "$KEYSTOREPATH"
$ sudo chmod 640 "$KEYSTOREPATH"
$ sudo chown root:hadoop "$TRUSTSTOREPATH"
$ sudo chmod 640 "$TRUSTSTOREPATH"
Однако права доступа 640 могут оказаться недостаточными для некоторых сервисов ADH, таких как Zookeeper и Airflow2.
В частности, возможна следующая ошибка:
java.io.FileNotFoundException: /etc/ssl/keystore.jks (Permission denied)
Ниже приведены два способа для обхода этой ошибки.
Вы можете предоставить пользователям zookeeper и airflow права на чтение keystore-файла, добавив соответствующие записи в ACL.
Например:
$ sudo setfacl -m u:zookeeper:r "$KEYSTOREPATH"
$ sudo setfacl -m u:airflow:r "$KEYSTOREPATH"
Вы можете разрешить всем пользователям в системе получать доступ к keystore-файлу. С точки зрения безопасности такой подход менее предпочтителен. Например:
$ sudo chmod 644 "$KEYSTOREPATH"
Использование портов для HTTPS
Информация о портах, используемых сервисами ADH для HTTPS-соединений, доступна в разделе Требования к сети.