192 lines
5.7 KiB
Bash
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}"
|
|
} |