mirror of
https://github.com/dredx/prole.git
synced 2026-09-23 10:13:58 +00:00
Fix YAML parsing errors in init_authority.sh and improve SSH key management. Resolved 'invalid Yaml document separator' and 'could not find expected :' errors in etc/init_authority.sh by using printf to inject multi-line SSH keys safely. Added 'ansible' user setup. Updated etc/init_openbao.sh to store ansible keys. Created prole/tests/test_init_authority.sh for validation.
This commit is contained in:
parent
f908585f23
commit
5ab6381982
233
etc/init_authority.sh
Executable file
233
etc/init_authority.sh
Executable file
@ -0,0 +1,233 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# init_authority.sh
|
||||
# Purpose:
|
||||
# - Ensure 'authority' namespace exists
|
||||
# - Spin up 'dog' container (Ubuntu 24) as primary KDC
|
||||
# - Configure KDC with root password and SSH keys from OpenBao
|
||||
# - Configure Samba AD authority and administrator account
|
||||
# - Save autogenerated administrator password to OpenBao
|
||||
|
||||
SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
||||
|
||||
# Load environment and config via prole_cfg.sh
|
||||
# shellcheck disable=SC1090
|
||||
source "$SCRIPT_DIR/prole_cfg.sh"
|
||||
|
||||
ACTION=${1:-start}
|
||||
NAMESPACE_AUTH="authority"
|
||||
KDC_NAME="dog"
|
||||
KDC_IMAGE="ubuntu:24.04"
|
||||
|
||||
log() { printf '%s\n' "$*"; }
|
||||
err() { printf '%s\n' "$*" >&2; }
|
||||
|
||||
ensure_tools() {
|
||||
for t in kubectl curl jq base64; do
|
||||
command -v "$t" >/dev/null || { err "Missing required tool: $t"; exit 1; }
|
||||
done
|
||||
}
|
||||
|
||||
ensure_namespace() {
|
||||
if ! kubectl get namespace "$NAMESPACE_AUTH" >/dev/null 2>&1; then
|
||||
log "Creating namespace '$NAMESPACE_AUTH' ..."
|
||||
kubectl create namespace "$NAMESPACE_AUTH"
|
||||
fi
|
||||
}
|
||||
|
||||
get_openbao_url() {
|
||||
if [[ -n "${PROLE_OPENBAO_URL:-}" ]]; then
|
||||
echo "$PROLE_OPENBAO_URL"
|
||||
elif curl -sS "http://127.0.0.1:18200/v1/sys/health" >/dev/null 2>&1; then
|
||||
echo "http://127.0.0.1:18200"
|
||||
else
|
||||
echo "http://openbao.${NAMESPACE}.svc.cluster.local:8200"
|
||||
fi
|
||||
}
|
||||
|
||||
get_openbao_token() {
|
||||
if [[ -f "$PROLE_SERVICE/secrets/openbao-root-token" ]]; then
|
||||
cat "$PROLE_SERVICE/secrets/openbao-root-token"
|
||||
else
|
||||
echo "${OPENBAO_ROOT_TOKEN:-root}"
|
||||
fi
|
||||
}
|
||||
|
||||
read_from_bao() {
|
||||
local path="$1"
|
||||
local key="$2"
|
||||
local url
|
||||
url=$(get_openbao_url)
|
||||
local token
|
||||
token=$(get_openbao_token)
|
||||
|
||||
curl -sS -H "X-Vault-Token: $token" "$url/v1/kv/data/$path" | jq -r ".data.data.\"$key\"" || echo ""
|
||||
}
|
||||
|
||||
write_to_bao() {
|
||||
local path="$1"
|
||||
local data="$2"
|
||||
local url
|
||||
url=$(get_openbao_url)
|
||||
local token
|
||||
token=$(get_openbao_token)
|
||||
|
||||
curl -sS -H "X-Vault-Token: $token" -H 'Content-Type: application/json' \
|
||||
-X POST "$url/v1/kv/data/$path" -d "{\"data\":$data}" >/dev/null
|
||||
}
|
||||
|
||||
deploy_dog() {
|
||||
log "Deploying '$KDC_NAME' (Ubuntu 24.04) in namespace '$NAMESPACE_AUTH' ..."
|
||||
|
||||
# Fetch secrets from OpenBao
|
||||
local root_pass
|
||||
root_pass=$(read_from_bao "prole/admin" "root_password")
|
||||
if [[ -z "$root_pass" || "$root_pass" == "null" ]]; then
|
||||
root_pass=$(openssl rand -hex 16)
|
||||
log "Generated new root password for dog server."
|
||||
# We need to preserve existing data in prole/admin if we write to it
|
||||
local admin_data
|
||||
admin_data=$(get_openbao_url | xargs -I {} curl -sS -H "X-Vault-Token: $(get_openbao_token)" "{}/v1/kv/data/prole/admin" | jq -r ".data.data")
|
||||
if [[ "$admin_data" == "null" ]]; then
|
||||
admin_data="{}"
|
||||
fi
|
||||
local new_admin_data
|
||||
new_admin_data=$(echo "$admin_data" | jq -c ". + {\"root_password\":\"$root_pass\"}")
|
||||
write_to_bao "prole/admin" "$new_admin_data"
|
||||
fi
|
||||
|
||||
local ssh_pub
|
||||
ssh_pub=$(read_from_bao "prole/admin" "admin_public_key_b64" | base64 -d 2>/dev/null || true)
|
||||
|
||||
local ansible_ssh_pub
|
||||
# Try to read ansible key from OpenBao, fallback to local file if it exists
|
||||
ansible_ssh_pub=$(read_from_bao "prole/admin" "ansible_public_key_b64" | base64 -d 2>/dev/null || true)
|
||||
if [[ -z "$ansible_ssh_pub" && -f "$HOME/.ssh/id_ed25519_ansible.pub" ]]; then
|
||||
ansible_ssh_pub=$(cat "$HOME/.ssh/id_ed25519_ansible.pub")
|
||||
fi
|
||||
|
||||
# Properly indent SSH keys for the YAML manifest
|
||||
# Use 10 spaces to be safe inside the 'args' block's literal scalar
|
||||
local ssh_pub_indented
|
||||
ssh_pub_indented=$(printf '%s' "$ssh_pub" | sed 's/^/ /')
|
||||
local ansible_ssh_pub_indented
|
||||
ansible_ssh_pub_indented=$(printf '%s' "$ansible_ssh_pub" | sed 's/^/ /')
|
||||
|
||||
# Use a separate variable for the YAML to help with debugging and clarity
|
||||
local manifest
|
||||
manifest=$(cat <<EOF
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: $KDC_NAME
|
||||
labels:
|
||||
app: $KDC_NAME
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: $KDC_NAME
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: $KDC_NAME
|
||||
spec:
|
||||
containers:
|
||||
- name: dog
|
||||
image: $KDC_IMAGE
|
||||
command: ["/bin/bash", "-c"]
|
||||
args:
|
||||
- |
|
||||
apt-get update && apt-get install -y openssh-server krb5-kdc krb5-admin-server samba winbind libpam-winbind libnss-winbind sudo
|
||||
mkdir -p /run/sshd
|
||||
echo "root:\$root_pass" | chpasswd
|
||||
|
||||
# Setup root SSH
|
||||
mkdir -p /root/.ssh
|
||||
printf '%s\n' "$ssh_pub" > /root/.ssh/authorized_keys
|
||||
chmod 600 /root/.ssh/authorized_keys
|
||||
|
||||
# Setup ansible user
|
||||
id ansible >/dev/null 2>&1 || useradd -m -s /bin/bash ansible
|
||||
echo 'ansible ALL=(ALL) NOPASSWD:ALL' > /etc/sudoers.d/90-ansible
|
||||
chmod 440 /etc/sudoers.d/90-ansible
|
||||
|
||||
# Setup ansible SSH
|
||||
mkdir -p /home/ansible/.ssh
|
||||
printf '%s\n' "$ansible_ssh_pub" > /home/ansible/.ssh/authorized_keys
|
||||
chmod 600 /home/ansible/.ssh/authorized_keys
|
||||
chown -R ansible:ansible /home/ansible/.ssh
|
||||
|
||||
/usr/sbin/sshd -D
|
||||
ports:
|
||||
- containerPort: 22
|
||||
- containerPort: 88
|
||||
- containerPort: 464
|
||||
- containerPort: 389
|
||||
- containerPort: 445
|
||||
EOF
|
||||
)
|
||||
|
||||
# Debug: log the manifest to a file if needed
|
||||
# echo "$manifest" > /tmp/dog-manifest.yaml
|
||||
|
||||
if ! echo "$manifest" | kubectl apply -n "$NAMESPACE_AUTH" -f -; then
|
||||
err "Kubectl apply failed. Manifest was:"
|
||||
err "$manifest"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
kubectl rollout status deployment/$KDC_NAME -n "$NAMESPACE_AUTH" --timeout=120s
|
||||
}
|
||||
|
||||
configure_authority() {
|
||||
log "Configuring Samba AD and Kerberos on '$KDC_NAME' ..."
|
||||
|
||||
local pod_name
|
||||
pod_name=$(kubectl get pod -n "$NAMESPACE_AUTH" -l app=$KDC_NAME -o jsonpath='{.items[0].metadata.name}')
|
||||
|
||||
# Provision Samba AD (simplified for version 1)
|
||||
# In a real scenario, this would involve samba-tool domain provision
|
||||
# We'll generate a password and save it to OpenBao
|
||||
|
||||
local admin_pass
|
||||
admin_pass=$(openssl rand -base64 16)
|
||||
|
||||
# Mocking the configuration steps for now as per instructions
|
||||
kubectl exec -n "$NAMESPACE_AUTH" "$pod_name" -- bash -c "echo 'Setting up AD...'"
|
||||
|
||||
log "Saving administrator password to OpenBao ..."
|
||||
write_to_bao "prole/authority" "{\"administrator_password\":\"$admin_pass\"}"
|
||||
log "Administrator password saved to kv/prole/authority/administrator_password"
|
||||
|
||||
local kdc_ip
|
||||
kdc_ip=$(kubectl get svc -n "$NAMESPACE_AUTH" "$KDC_NAME" -o jsonpath='{.spec.clusterIP}' 2>/dev/null || echo "dog.${NAMESPACE_AUTH}.svc.cluster.local")
|
||||
|
||||
log "----------------------------------------------------------"
|
||||
log "Kerberos Configuration for prole-db nodes (Version 1):"
|
||||
log "KDC Server: $kdc_ip"
|
||||
log "Realm: ${KRB5_REALM:-PROLE.ORG}"
|
||||
log "Admin Server: $kdc_ip"
|
||||
log "----------------------------------------------------------"
|
||||
}
|
||||
|
||||
case "$ACTION" in
|
||||
start)
|
||||
ensure_tools
|
||||
ensure_namespace
|
||||
deploy_dog
|
||||
configure_authority
|
||||
;;
|
||||
status)
|
||||
kubectl get pods,svc -n "$NAMESPACE_AUTH"
|
||||
;;
|
||||
stop)
|
||||
kubectl delete namespace "$NAMESPACE_AUTH"
|
||||
;;
|
||||
*)
|
||||
err "Usage: $0 {start|stop|status}"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
@ -268,9 +268,15 @@ init_openbao_kv_and_store_admin_key() {
|
||||
echo "Admin key pair in OpenBao kv/prole/admin is already up to date."
|
||||
else
|
||||
echo "Writing admin key pair to kv/prole/admin ..."
|
||||
# Preserve other fields (like root_password or ansible_public_key_b64)
|
||||
local current_json
|
||||
current_json=$(echo "$existing_data" | jq -c ".data.data")
|
||||
local new_json
|
||||
new_json=$(echo "$current_json" | jq -c ". + {\"admin_private_key_b64\":\"$priv\",\"admin_public_key_b64\":\"$pub\"}")
|
||||
|
||||
curl -sS -H "X-Vault-Token: $token" -H 'Content-Type: application/json' \
|
||||
-X POST "$svc/v1/kv/data/prole/admin" \
|
||||
-d "{\"data\":{\"admin_private_key_b64\":\"$priv\",\"admin_public_key_b64\":\"$pub\"}}" >/dev/null
|
||||
-d "{\"data\":$new_json}" >/dev/null
|
||||
echo "Stored admin key pair in OpenBao kv/prole/admin."
|
||||
fi
|
||||
else
|
||||
@ -281,6 +287,24 @@ init_openbao_kv_and_store_admin_key() {
|
||||
echo "Stored admin key pair in OpenBao kv/prole/admin."
|
||||
fi
|
||||
|
||||
# Store Ansible public key if it exists
|
||||
local ansible_pub_path="$HOME/.ssh/id_ed25519_ansible.pub"
|
||||
if [[ -f "$ansible_pub_path" ]]; then
|
||||
echo "Storing Ansible public key in OpenBao kv/prole/admin ..."
|
||||
local a_pub
|
||||
a_pub=$(base64 <"$ansible_pub_path" | tr -d '\n')
|
||||
|
||||
local current_admin_data
|
||||
current_admin_data=$(curl -sS -H "X-Vault-Token: $token" "$svc/v1/kv/data/prole/admin" | jq -c ".data.data" 2>/dev/null || echo "{}")
|
||||
local updated_admin_data
|
||||
updated_admin_data=$(echo "$current_admin_data" | jq -c ". + {\"ansible_public_key_b64\":\"$a_pub\"}")
|
||||
|
||||
curl -sS -H "X-Vault-Token: $token" -H 'Content-Type: application/json' \
|
||||
-X POST "$svc/v1/kv/data/prole/admin" \
|
||||
-d "{\"data\":$updated_admin_data}" >/dev/null
|
||||
echo "Ansible public key stored."
|
||||
fi
|
||||
|
||||
# Store User SSH keys if they exist
|
||||
local user_key_priv="$HOME/.ssh/id_prole_ed25519"
|
||||
local user_key_pub="$HOME/.ssh/id_prole_ed25519.pub"
|
||||
@ -532,7 +556,8 @@ case "$ACTION" in
|
||||
|
||||
# Ensure port-forward is running for OpenBao
|
||||
echo "Ensuring port-forward for OpenBao is active ..."
|
||||
"$SCRIPT_DIR/init_port_forwards.sh" restart openbao &
|
||||
"$SCRIPT_DIR/init_port_forwards.sh" stop openbao || true
|
||||
"$SCRIPT_DIR/init_port_forwards.sh" start openbao &
|
||||
sleep 2
|
||||
|
||||
max_retries=15
|
||||
|
||||
@ -2,7 +2,7 @@ apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: prole-krb5-conf
|
||||
namespace: prole-chrisfu-abacababa
|
||||
namespace: prole-chrisfu-abracadabra
|
||||
data:
|
||||
krb5.conf: |
|
||||
[libdefaults]
|
||||
|
||||
109
prole/tests/test_init_authority.sh
Normal file
109
prole/tests/test_init_authority.sh
Normal file
@ -0,0 +1,109 @@
|
||||
#!/usr/bin/env bash
|
||||
# prole/tests/test_init_authority.sh
|
||||
# Validates the YAML generation of init_authority.sh
|
||||
|
||||
SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
||||
PROLE_HOME=$(cd "$SCRIPT_DIR/../.." && pwd)
|
||||
INIT_AUTHORITY="$PROLE_HOME/etc/init_authority.sh"
|
||||
|
||||
if [[ ! -f "$INIT_AUTHORITY" ]]; then
|
||||
echo "ERROR: $INIT_AUTHORITY not found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Mock environment
|
||||
export PROLE_SERVICE="$PROLE_HOME/etc"
|
||||
export NAMESPACE="test-namespace"
|
||||
export PROLE_HOME="$PROLE_HOME"
|
||||
|
||||
TMP_DIR=$(mktemp -d)
|
||||
trap 'rm -rf "$TMP_DIR"' EXIT
|
||||
|
||||
# Mock other tools to avoid actual execution
|
||||
mock_tool() {
|
||||
cat <<M_EOF > "$TMP_DIR/$1"
|
||||
#!/usr/bin/env bash
|
||||
# LOG THE CALL
|
||||
echo "Mocked $1 called with \$@" >> "$TMP_DIR/mock_calls.log"
|
||||
case "\$1" in
|
||||
*v1/kv/data/prole/admin)
|
||||
echo '{"data":{"data":{"root_password":"root", "admin_public_key_b64":"LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlJQklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEFNSUlCQ2dLQ0FRRUF6S3Z4CkNvS0tLS0tRU5EIFBVQkxJQyBLRVktLS0tLQo="}}}'
|
||||
;;
|
||||
*)
|
||||
if [[ "$1" == "base64" ]]; then
|
||||
/usr/bin/base64 "\$@"
|
||||
elif [[ "$1" == "jq" ]]; then
|
||||
/usr/bin/jq "\$@"
|
||||
elif [[ "$1" == "kubectl" ]]; then
|
||||
case "\$*" in
|
||||
*apply*)
|
||||
if [[ ! -t 0 ]]; then
|
||||
cat > "$TMP_DIR/generated_manifest.yaml"
|
||||
fi
|
||||
# If the manifest is NOT valid YAML, we want to simulate the failure
|
||||
if python3 -c "import yaml, sys; yaml.safe_load(open('$TMP_DIR/generated_manifest.yaml'))" 2>/dev/null; then
|
||||
echo "deployment.apps/dog created"
|
||||
exit 0
|
||||
else
|
||||
echo "error: error parsing STDIN: error converting YAML to JSON: yaml: line 34: could not find expected ':'" >&2
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
*rollout*)
|
||||
echo "deployment \"dog\" successfully rolled out"
|
||||
exit 0
|
||||
;;
|
||||
*get*pod*)
|
||||
echo "dog-pod-123"
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
else
|
||||
exit 0
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
M_EOF
|
||||
chmod +x "$TMP_DIR/$1"
|
||||
}
|
||||
|
||||
mock_tool curl
|
||||
mock_tool kubectl
|
||||
export PATH="$TMP_DIR:/usr/bin:/bin"
|
||||
|
||||
# We'll run a subshell where we override read_from_bao if possible,
|
||||
# but init_authority.sh sources prole_cfg.sh and defines its own functions.
|
||||
# Instead, we will rely on the fact that it uses curl/jq which we mocked.
|
||||
|
||||
echo "Running init_authority.sh start (mocked)..."
|
||||
# We need to suppress the errors from configure_authority which follows deploy_dog
|
||||
# Also ensure the PATH is exported to subshells
|
||||
export PATH
|
||||
bash "$INIT_AUTHORITY" start > "$TMP_DIR/out" 2>&1 || true
|
||||
|
||||
if [[ -f "$TMP_DIR/generated_manifest.yaml" ]]; then
|
||||
echo "Validating generated YAML..."
|
||||
# Use python3 to check if it's valid YAML
|
||||
if python3 -c "import yaml, sys; yaml.safe_load(open('$TMP_DIR/generated_manifest.yaml'))" 2>"$TMP_DIR/yaml_error"; then
|
||||
echo "SUCCESS: YAML is valid."
|
||||
else
|
||||
echo "FAILURE: YAML is invalid."
|
||||
cat "$TMP_DIR/yaml_error"
|
||||
echo "--- Generated YAML ---"
|
||||
cat "$TMP_DIR/generated_manifest.yaml"
|
||||
echo "----------------------"
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
echo "FAILURE: No YAML manifest generated."
|
||||
echo "--- Output ---"
|
||||
cat "$TMP_DIR/out"
|
||||
echo "--------------"
|
||||
echo "--- Mock calls ---"
|
||||
cat "$TMP_DIR/mock_calls.log" || echo "No mock calls log"
|
||||
echo "------------------"
|
||||
exit 1
|
||||
fi
|
||||
Loading…
Reference in New Issue
Block a user