feat: pin mx host key in keygen

This commit is contained in:
Marek Miklewicz
2026-06-30 19:18:35 +02:00
parent bef92cee77
commit 7d3d458160
4 changed files with 139 additions and 3 deletions
+92 -1
View File
@@ -5,8 +5,24 @@ KEY_DIR="${MAIL_LOGIN_KEY_DIR:-/usr/local/hitme_plugins/mail-login/secrets}"
KEY_NAME="${MAIL_LOGIN_KEY_NAME:-mx1_ed25519}"
KEY_PATH="$KEY_DIR/$KEY_NAME"
MX_MODE="${MX_LOGIN_MODE:-1}"
SETTINGS_FILE="${MAIL_LOGIN_SETTINGS_FILE:-/usr/local/directadmin/plugins/mail-login/plugin-settings.conf}"
HELPER_COMMAND="${MAIL_LOGIN_HELPER_COMMAND:-/usr/local/hitme_plugins/mail-login-helper/bin/create-login-url}"
config_value() {
key="$1"
if [ ! -f "$SETTINGS_FILE" ]; then
return 0
fi
awk -F= -v key="$key" '
$1 == key {
value = substr($0, index($0, "=") + 1)
gsub(/^[ \t]+|[ \t]+$/, "", value)
print value
exit
}
' "$SETTINGS_FILE"
}
derive_servername() {
if [ -n "${SERVERNAME:-}" ]; then
printf '%s' "$SERVERNAME"
@@ -45,12 +61,64 @@ detect_source_ip() {
return 1
}
pin_mx_host_key() {
if [ -z "$MX_HOST" ]; then
HOST_KEY_STATUS="skipped: MAIL_LOGIN_SSH_HOST is not configured"
return
fi
if ! command -v ssh-keyscan >/dev/null 2>&1; then
HOST_KEY_STATUS="skipped: ssh-keyscan is not available"
return
fi
known_dir="$(dirname "$KNOWN_HOSTS")"
mkdir -p "$known_dir"
chmod 700 "$known_dir"
touch "$KNOWN_HOSTS"
chmod 600 "$KNOWN_HOSTS"
lookup="$MX_HOST"
if [ "$MX_PORT" != "22" ]; then
lookup="[$MX_HOST]:$MX_PORT"
fi
if ssh-keygen -F "$lookup" -f "$KNOWN_HOSTS" >/dev/null 2>&1; then
HOST_KEY_STATUS="already present for $lookup"
return
fi
tmp="$KNOWN_HOSTS.$$"
if ssh-keyscan -T "$SCAN_TIMEOUT" -p "$MX_PORT" -t ed25519 "$MX_HOST" > "$tmp" 2>/dev/null && [ -s "$tmp" ]; then
cat "$tmp" >> "$KNOWN_HOSTS"
chmod 600 "$KNOWN_HOSTS"
HOST_KEY_FINGERPRINT="$(ssh-keygen -lf "$tmp" 2>/dev/null | head -n 1 || true)"
HOST_KEY_STATUS="pinned $lookup"
rm -f "$tmp"
return
fi
rm -f "$tmp"
HOST_KEY_STATUS="failed: cannot fetch ED25519 host key for $MX_HOST:$MX_PORT"
}
SERVER_LABEL="$(clean_label "$(derive_servername)")"
if [ -z "$SERVER_LABEL" ]; then
echo "Cannot determine SERVERNAME" >&2
exit 1
fi
CONFIG_SSH_HOST="$(config_value MAIL_LOGIN_SSH_HOST || true)"
CONFIG_SSH_PORT="$(config_value MAIL_LOGIN_SSH_PORT || true)"
CONFIG_KNOWN_HOSTS="$(config_value MAIL_LOGIN_SSH_KNOWN_HOSTS || true)"
CONFIG_SCAN_TIMEOUT="$(config_value MAIL_LOGIN_CONNECT_TIMEOUT_SECONDS || true)"
MX_HOST="${MAIL_LOGIN_SSH_HOST:-$CONFIG_SSH_HOST}"
MX_PORT="${MAIL_LOGIN_SSH_PORT:-$CONFIG_SSH_PORT}"
MX_PORT="${MX_PORT:-22}"
KNOWN_HOSTS="${MAIL_LOGIN_SSH_KNOWN_HOSTS:-$CONFIG_KNOWN_HOSTS}"
KNOWN_HOSTS="${KNOWN_HOSTS:-$KEY_DIR/known_hosts}"
SCAN_TIMEOUT="${MAIL_LOGIN_CONNECT_TIMEOUT_SECONDS:-$CONFIG_SCAN_TIMEOUT}"
SCAN_TIMEOUT="${SCAN_TIMEOUT:-5}"
case "$MX_MODE" in
1|2) ;;
*)
@@ -59,6 +127,20 @@ case "$MX_MODE" in
;;
esac
case "$MX_PORT" in
*[!0-9]*|'')
echo "MAIL_LOGIN_SSH_PORT must be numeric" >&2
exit 1
;;
esac
case "$SCAN_TIMEOUT" in
*[!0-9]*|'')
echo "MAIL_LOGIN_CONNECT_TIMEOUT_SECONDS must be numeric" >&2
exit 1
;;
esac
SOURCE_IP="$(detect_source_ip || true)"
case "$SOURCE_IP" in
*[!A-Za-z0-9:./_-]*|'')
@@ -82,6 +164,10 @@ fi
chmod 600 "$KEY_PATH"
chmod 644 "$KEY_PATH.pub"
HOST_KEY_STATUS=""
HOST_KEY_FINGERPRINT=""
pin_mx_host_key
PUB_KEY="$(cat "$KEY_PATH.pub")"
BASE_OPTIONS="from=\"$SOURCE_IP\",restrict,no-agent-forwarding,no-port-forwarding,no-X11-forwarding,no-pty"
@@ -102,7 +188,12 @@ $KEY_PATH.pub
Set this in plugin-settings.conf:
MAIL_LOGIN_SSH_IDENTITY=$KEY_PATH
MAIL_LOGIN_SSH_KNOWN_HOSTS=/root/.ssh/known_hosts
MAIL_LOGIN_SSH_KNOWN_HOSTS=$KNOWN_HOSTS
MX known_hosts:
$KNOWN_HOSTS
Pinned MX host key: $HOST_KEY_STATUS
${HOST_KEY_FINGERPRINT}
Add this line to /root/.ssh/authorized_keys on mx1:
$AUTH_OPTIONS $PUB_KEY $COMMENT