#!/bin/bash set -Eeuo pipefail STATE_DIR="/var/lib/k8s-mgmt-pod" GENERATED_PASSWORDS_FILE="${STATE_DIR}/generated-passwords.env" SSH_DYNAMIC_CONFIG="/etc/ssh/sshd_config.d/20-k8s-mgmt-pod-auth.conf" K8S_RUNTIME_ENV_PROFILE="/etc/profile.d/30-kubernetes-runtime-env.sh" PASSWORD_AUTH_REQUIRED=0 SSHD_PID="" WEBSSH2_PID="" NGINX_PID="" BOOTSTRAP_KUBECONFIG="${BOOTSTRAP_KUBECONFIG:-/bootstrap/kubeconfig/config}" ENABLE_BOOTSTRAP_KUBECONFIG="${ENABLE_BOOTSTRAP_KUBECONFIG:-false}" KUBERNETES_SECRETS_DIR="${KUBERNETES_SECRETS_DIR:-/var/run/kubernetes.io}" SSH_PORT="${SSH_PORT:-2222}" if [[ -z "${WEBSSH2_SSH_PORT:-}" ]]; then WEBSSH2_SSH_PORT="${SSH_PORT}" fi export WEBSSH2_SSH_PORT ensure_host_keys() { if [[ ! -f "${STATE_DIR}/ssh_host_rsa_key" ]]; then ssh-keygen -q -t rsa -b 3072 -N '' -f "${STATE_DIR}/ssh_host_rsa_key" fi if [[ ! -f "${STATE_DIR}/ssh_host_ecdsa_key" ]]; then ssh-keygen -q -t ecdsa -b 256 -N '' -f "${STATE_DIR}/ssh_host_ecdsa_key" fi if [[ ! -f "${STATE_DIR}/ssh_host_ed25519_key" ]]; then ssh-keygen -q -t ed25519 -N '' -f "${STATE_DIR}/ssh_host_ed25519_key" fi chmod 0600 "${STATE_DIR}/ssh_host_"*_key } mkdir -p "${STATE_DIR}" touch "${GENERATED_PASSWORDS_FILE}" chmod 0600 "${GENERATED_PASSWORDS_FILE}" log() { printf '[k8s-mgmt-pod] %s\n' "$*" } cleanup() { local exit_code=$? if [[ -n "${WEBSSH2_PID}" ]] && kill -0 "${WEBSSH2_PID}" 2>/dev/null; then kill "${WEBSSH2_PID}" 2>/dev/null || true fi if [[ -n "${SSHD_PID}" ]] && kill -0 "${SSHD_PID}" 2>/dev/null; then kill "${SSHD_PID}" 2>/dev/null || true fi if [[ -n "${NGINX_PID}" ]] && kill -0 "${NGINX_PID}" 2>/dev/null; then kill "${NGINX_PID}" 2>/dev/null || true fi wait || true exit "${exit_code}" } trap cleanup EXIT INT TERM store_generated_password() { local username="$1" local password="$2" if grep -q "^${username}=" "${GENERATED_PASSWORDS_FILE}"; then sed -i "s|^${username}=.*|${username}=${password}|" "${GENERATED_PASSWORDS_FILE}" else printf '%s=%s\n' "${username}" "${password}" >> "${GENERATED_PASSWORDS_FILE}" fi } read_generated_password() { local username="$1" # Return an empty string if no entry exists; never fail under `set -euo pipefail`. awk -F '=' -v username="${username}" '$1 == username { value = substr($0, index($0, "=") + 1) } END { if (value != "") print value }' "${GENERATED_PASSWORDS_FILE}" } write_authorized_keys() { local username="$1" local pubkey_content="$2" local home_dir="/home/${username}" local ssh_dir="${home_dir}/.ssh" local auth_keys="${ssh_dir}/authorized_keys" mkdir -p "${ssh_dir}" chmod 0700 "${ssh_dir}" printf '%s\n' "${pubkey_content}" > "${auth_keys}" chmod 0600 "${auth_keys}" chown -R "${username}:${username}" "${ssh_dir}" } install_kubeconfig_for_user() { local username="$1" local source_kubeconfig="$2" local home_dir="/home/${username}" local kube_dir="${home_dir}/.kube" local kube_config="${kube_dir}/config" mkdir -p "${kube_dir}" install -m 0600 -o "${username}" -g "${username}" "${source_kubeconfig}" "${kube_config}" } resolve_kubernetes_secrets_dir() { local configured_dir="${KUBERNETES_SECRETS_DIR}" local fallback_dir="/var/run/secrets/kubernetes.io/serviceaccount" if [[ -f "${configured_dir}/token" && -f "${configured_dir}/ca.crt" && -f "${configured_dir}/namespace" ]]; then printf '%s\n' "${configured_dir}" return 0 fi if [[ "${configured_dir}" != "${fallback_dir}" ]] \ && [[ -f "${fallback_dir}/token" && -f "${fallback_dir}/ca.crt" && -f "${fallback_dir}/namespace" ]]; then printf '%s\n' "${fallback_dir}" return 0 fi return 1 } install_serviceaccount_kubeconfig() { local sa_dir="" local token_file="" local ca_file="" local namespace_file="" local namespace="" local kubernetes_host="${KUBERNETES_SERVICE_HOST:-kubernetes.default.svc}" local kubernetes_port="${KUBERNETES_SERVICE_PORT_HTTPS:-443}" local server="" local generated_kubeconfig="${STATE_DIR}/incluster.kubeconfig" if ! sa_dir="$(resolve_kubernetes_secrets_dir)"; then log "Skipping in-cluster kubeconfig generation because no service-account mount was found in ${KUBERNETES_SECRETS_DIR} or /var/run/secrets/kubernetes.io/serviceaccount." return fi token_file="${sa_dir}/token" ca_file="${sa_dir}/ca.crt" namespace_file="${sa_dir}/namespace" namespace="$(tr -d '\n' < "${namespace_file}")" if [[ -z "${namespace}" ]]; then namespace="default" fi server="https://${kubernetes_host}:${kubernetes_port}" cat > "${generated_kubeconfig}" </dev/null 2>&1 || true } generate_password() { openssl rand -base64 24 | tr -d '\n' } configure_user_auth() { local username="$1" local pubkey_var_name="$2" local password_var_name="$3" local pubkey_value="${!pubkey_var_name:-}" local password_value="${!password_var_name:-}" local generated_password="" local has_pubkey=0 if [[ -n "${pubkey_value}" ]]; then write_authorized_keys "${username}" "${pubkey_value}" has_pubkey=1 log "Configured SSH public key authentication for ${username}." else remove_authorized_keys "${username}" fi if [[ -n "${password_value}" ]]; then PASSWORD_AUTH_REQUIRED=1 set_login_password "${username}" "${password_value}" log "Configured password authentication for ${username} from environment." return fi if [[ "${has_pubkey}" -eq 1 ]]; then passwd -l "${username}" >/dev/null 2>&1 || true return fi PASSWORD_AUTH_REQUIRED=1 generated_password="$(read_generated_password "${username}")" if [[ -z "${generated_password}" ]]; then generated_password="$(generate_password)" store_generated_password "${username}" "${generated_password}" log "Generated random password for ${username}: ${generated_password}" log "Store this password securely now; it is only printed when first generated." fi set_login_password "${username}" "${generated_password}" } render_sshd_auth_config() { local password_value="no" local keyboard_interactive_value="no" if [[ "${PASSWORD_AUTH_REQUIRED}" -eq 1 ]]; then password_value="yes" keyboard_interactive_value="yes" export WEBSSH2_AUTH_ALLOWED="password,publickey,keyboard-interactive" else export WEBSSH2_AUTH_ALLOWED="publickey" fi cat > "${SSH_DYNAMIC_CONFIG}" < "${K8S_RUNTIME_ENV_PROFILE}" <