my-homelab-configs/lib/jeannie/security.sh

192 lines
5.7 KiB
Bash

#!/usr/bin/env bash
secrets_init() {
local key_file
local key_dir
local recipient
local config_file="${SOPS_CONFIG:-${REPO_ROOT}/.sops.yaml}"
local example_file="${REPO_ROOT}/.sops.yaml.example"
require_debian_server "secrets-init"
ensure_sops_age_tools
key_file="$(sops_age_key_file)"
key_dir="$(dirname "${key_file}")"
mkdir -p "${key_dir}"
chmod 700 "${key_dir}"
if [[ ! -s "${key_file}" ]]; then
echo "Generating age identity at ${key_file}..."
age-keygen -o "${key_file}"
chmod 600 "${key_file}"
else
echo "Using existing age identity at ${key_file}."
chmod 600 "${key_file}"
fi
recipient="$(sops_age_recipient "${key_file}")"
if [[ -z "${recipient}" ]]; then
echo "Could not read the public recipient from ${key_file}." >&2
exit 1
fi
if [[ -e "${config_file}" ]]; then
if grep -q 'age1replacewithyourpublicrecipient' "${config_file}"; then
echo "${config_file} still contains the placeholder age recipient." >&2
exit 1
fi
echo "SOPS config already exists: ${config_file}"
else
if [[ ! -s "${example_file}" ]]; then
echo "Missing ${example_file}" >&2
exit 1
fi
sed "s/age1replacewithyourpublicrecipient/${recipient}/g" "${example_file}" >"${config_file}"
echo "Wrote ${config_file} with Debian host age recipient ${recipient}."
fi
echo "Private age key: ${key_file}"
echo "Public age recipient: ${recipient}"
echo "Review and commit ${config_file}; never commit ${key_file}."
}
secrets_check_file() {
local file="$1"
local key_file="$2"
case "${file}" in
*.json)
grep -q '"sops"' "${file}" || {
echo "${file} does not look SOPS-encrypted." >&2
return 1
}
;;
*)
grep -q '^sops:' "${file}" || {
echo "${file} does not look SOPS-encrypted." >&2
return 1
}
;;
esac
if [[ -s "${key_file}" ]]; then
SOPS_AGE_KEY_FILE="${key_file}" sops -d "${file}" >/dev/null
fi
}
secrets_check() {
local config_file="${SOPS_CONFIG:-${REPO_ROOT}/.sops.yaml}"
local key_file
local recipient=""
local failures=0
local found_files=0
local file
key_file="$(sops_age_key_file)"
if [[ ! -s "${config_file}" ]]; then
echo "Missing ${config_file}. Run ./jeannie secrets-init on the Debian host." >&2
failures=$((failures + 1))
elif grep -q 'age1replacewithyourpublicrecipient' "${config_file}"; then
echo "${config_file} still contains the placeholder age recipient." >&2
failures=$((failures + 1))
fi
if [[ -s "${key_file}" ]]; then
recipient="$(sops_age_recipient "${key_file}")"
if [[ -n "${recipient}" && -s "${config_file}" ]] && ! grep -q "${recipient}" "${config_file}"; then
echo "${config_file} does not include this host's age recipient ${recipient}." >&2
failures=$((failures + 1))
fi
else
echo "No local age key found at ${key_file}; encrypted file structure will be checked without decrypting."
fi
if command -v git >/dev/null 2>&1; then
while IFS= read -r file; do
[[ -n "${file}" ]] || continue
found_files=$((found_files + 1))
secrets_check_file "${REPO_ROOT}/${file}" "${key_file}" || failures=$((failures + 1))
done < <(git -C "${REPO_ROOT}" ls-files '*.secret.yaml' '*.secret.yml' '*.secret.json' '*.enc.yaml' '*.enc.yml' '*.enc.json')
fi
if ((failures > 0)); then
echo "Secret checks failed with ${failures} issue(s)." >&2
exit 1
fi
if ((found_files == 0)); then
echo "SOPS config checks passed. No encrypted secret files are committed yet."
else
echo "SOPS config checks passed for ${found_files} encrypted secret file(s)."
fi
}
security_scan() {
require_debian_server "security-scan"
"${REPO_ROOT}/scripts/security-scan" all
}
security_prepare() {
require_debian_server "security-prepare"
"${REPO_ROOT}/scripts/security-scan" prepare
}
security_zap() {
require_debian_server "security-zap"
"${REPO_ROOT}/scripts/security-scan" zap
}
security_host() {
require_debian_server "security-host"
"${REPO_ROOT}/scripts/security-scan" host
}
security_trivy() {
require_debian_server "security-trivy"
"${REPO_ROOT}/scripts/security-scan" trivy
}
security_secrets() {
require_debian_server "security-secrets"
"${REPO_ROOT}/scripts/security-scan" secrets
}
security_nuclei() {
require_debian_server "security-nuclei"
"${REPO_ROOT}/scripts/security-scan" nuclei
}
security_web() {
require_debian_server "security-web"
"${REPO_ROOT}/scripts/security-scan" web
}
security_logs() {
require_debian_server "security-logs"
"${REPO_ROOT}/scripts/security-logs"
}
security_runtime() {
require_debian_server "security-runtime"
if ! command -v kubectl >/dev/null 2>&1; then
echo "kubectl is required for security-runtime." >&2
exit 1
fi
kubectl --kubeconfig "${KUBECONFIG_PATH}" -n kube-system rollout status daemonset/tetragon --timeout=30s
kubectl --kubeconfig "${KUBECONFIG_PATH}" -n kube-system get pods -l app.kubernetes.io/name=tetragon -o wide
cat <<'EOF'
Watch recent Tetragon events:
kubectl -n kube-system logs -l app.kubernetes.io/name=tetragon -c export-stdout --tail=100 -f
Useful practice trigger:
kubectl -n security-lab exec deploy/juice-shop -- sh -c 'id; uname -a'
EOF
}
security_attack_path() {
require_debian_server "security-attack-path"
"${REPO_ROOT}/scripts/security-attack-path" "${@:2}"
}