feat(core): initialize zarya modular command station
This commit is contained in:
commit
3c61779a4b
221
bin/dep
Executable file
221
bin/dep
Executable file
|
|
@ -0,0 +1,221 @@
|
||||||
|
#!/bin/bash
|
||||||
|
set -euo pipefail
|
||||||
|
ZARYA_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
|
source "${ZARYA_DIR}/lib/common.sh"
|
||||||
|
COMMAND="${1:-help}"
|
||||||
|
|
||||||
|
parse_run_targets() {
|
||||||
|
local raw_input="$*"
|
||||||
|
local normalized
|
||||||
|
normalized=$(echo "${raw_input}" | tr ',' ' ')
|
||||||
|
local targets=()
|
||||||
|
for item in ${normalized}; do
|
||||||
|
if [[ "${item}" =~ ^([0-9]+)-([0-9]+)$ ]]; then
|
||||||
|
local start="${BASH_REMATCH[1]}"
|
||||||
|
local end="${BASH_REMATCH[2]}"
|
||||||
|
for ((i=10#$start; i<=10#$end; i++)); do
|
||||||
|
targets+=($(printf "%02d" "${i}"))
|
||||||
|
done
|
||||||
|
elif [[ "${item}" =~ ^[0-9]+$ ]]; then
|
||||||
|
targets+=($(printf "%02d" "$((10#$item))"))
|
||||||
|
else
|
||||||
|
log_err "Некорректный идентификатор плейбука: '${item}'"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
printf "%s\n" "${targets[@]}" | sort -u -n
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve_playbook() {
|
||||||
|
local id="$1"
|
||||||
|
local found
|
||||||
|
found=$(find "${REPO_ROOT}/ansible/playbooks" -maxdepth 1 -name "${id}_*.yml" | head -n1)
|
||||||
|
if [[ -z "${found}" ]]; then
|
||||||
|
log_err "Плейбук с префиксом [${id}] не найден в ansible/playbooks/"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "${found}"
|
||||||
|
}
|
||||||
|
|
||||||
|
print_playbooks_catalog() {
|
||||||
|
echo -e "${BLUE}=== Каталог доступных плейбуков (ansible/playbooks/) ===${NC}"
|
||||||
|
printf "%-4s %-28s %-12s %s\n" "ID" "PLAYBOOK" "HOSTS" "DESCRIPTION"
|
||||||
|
echo "----------------------------------------------------------------------------------"
|
||||||
|
for pb in $(ls -1 "${REPO_ROOT}/ansible/playbooks"/[0-9][0-9]_*.yml 2>/dev/null | sort); do
|
||||||
|
local file_name
|
||||||
|
file_name=$(basename "${pb}")
|
||||||
|
local id="${file_name:0:2}"
|
||||||
|
local hosts
|
||||||
|
hosts=$(awk '/hosts:/ {print $2; exit}' "${pb}" | tr -d '"'"'")
|
||||||
|
local desc
|
||||||
|
desc=$(awk '/- name:/ {print substr($0, index($0,$3)); exit}' "${pb}")
|
||||||
|
printf "%-4s %-28s %-12s %s\n" "${id}" "${file_name%.yml}" "${hosts}" "${desc}"
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
case "${COMMAND}" in
|
||||||
|
git-credential)
|
||||||
|
if [[ -f "${RAM_CACHE}" ]]; then
|
||||||
|
token=$(grep '^export ATLAS_PUSH_TOKEN=' "${RAM_CACHE}" 2>/dev/null | cut -d= -f2- | tr -d '"'\'' ' || true)
|
||||||
|
if [[ -n "${token}" ]]; then
|
||||||
|
echo "username=atlas"
|
||||||
|
echo "password=${token}"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
start|login)
|
||||||
|
shift || true
|
||||||
|
hours="${1:-1}"
|
||||||
|
if ! [[ "${hours}" =~ ^[0-9]+$ ]] || (( hours < 0 || hours > 24 )); then
|
||||||
|
log_err "Недопустимый TTL сессии: '${hours}'. Допустимо: 0 (бессрочно) или 1-24 (часы)."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
ensure_prerequisites
|
||||||
|
load_secrets_to_ram "${hours}"
|
||||||
|
unseal_vault
|
||||||
|
if [[ "${hours}" -eq 0 ]]; then
|
||||||
|
log_ok "Рабочая сессия Sovereign C2 открыта (Бессрочно)."
|
||||||
|
else
|
||||||
|
log_ok "Рабочая сессия Sovereign C2 открыта (TTL: ${hours} ч)."
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
clean|logout)
|
||||||
|
rm -f "${RAM_CACHE}" /dev/shm/.atlas_core
|
||||||
|
log_ok "Секреты и SSH-ключи безопасно удалены из RAM."
|
||||||
|
;;
|
||||||
|
unseal)
|
||||||
|
ensure_prerequisites
|
||||||
|
unseal_vault
|
||||||
|
;;
|
||||||
|
up)
|
||||||
|
shift || true
|
||||||
|
target="${1:-all}"
|
||||||
|
case "${target}" in
|
||||||
|
host) run_isolated "${ZARYA_DIR}/layers/00_host.sh" ;;
|
||||||
|
net) run_isolated "${ZARYA_DIR}/layers/01_net.sh" ;;
|
||||||
|
tier0) run_isolated "${ZARYA_DIR}/layers/02_tier0.sh" ;;
|
||||||
|
talos) run_isolated "${ZARYA_DIR}/layers/03_talos.sh" ;;
|
||||||
|
apps) "${ZARYA_DIR}/layers/04_apps.sh" ;;
|
||||||
|
all)
|
||||||
|
run_isolated "${ZARYA_DIR}/layers/00_host.sh"
|
||||||
|
run_isolated "${ZARYA_DIR}/layers/01_net.sh"
|
||||||
|
run_isolated "${ZARYA_DIR}/layers/02_tier0.sh"
|
||||||
|
run_isolated "${ZARYA_DIR}/layers/03_talos.sh"
|
||||||
|
"${ZARYA_DIR}/layers/04_apps.sh"
|
||||||
|
log_ok "Вся инфраструктура полностью развернута."
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
log_err "Неизвестный слой: ${target}. Доступны: host, net, tier0, talos, apps, all"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
;;
|
||||||
|
run)
|
||||||
|
shift || true
|
||||||
|
if [[ $# -eq 0 ]] || [[ "${1:-}" == "list" ]]; then
|
||||||
|
print_playbooks_catalog
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
targets=$(parse_run_targets "$*")
|
||||||
|
log_info "Очередь выполнения плейбуков: $(echo ${targets} | tr '\n' ' ')"
|
||||||
|
for id in ${targets}; do
|
||||||
|
pb_path=$(resolve_playbook "${id}")
|
||||||
|
log_info "==> [RUN ${id}] Исполнение: $(basename "${pb_path}")..."
|
||||||
|
run_isolated ansible-playbook -i "${REPO_ROOT}/ansible/inventory.yml" "${pb_path}"
|
||||||
|
done
|
||||||
|
log_ok "Все выбранные плейбуки успешно выполнены."
|
||||||
|
;;
|
||||||
|
exec)
|
||||||
|
shift || true
|
||||||
|
if [[ $# -eq 0 ]]; then
|
||||||
|
log_err "Использование: dep exec <команда> [аргументы...]"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
run_isolated "$@"
|
||||||
|
;;
|
||||||
|
a|apply)
|
||||||
|
unseal_vault
|
||||||
|
shift || true
|
||||||
|
run_isolated bash -c "cd '${REPO_ROOT}/tofu' && tofu apply $*"
|
||||||
|
;;
|
||||||
|
plan)
|
||||||
|
shift || true
|
||||||
|
run_isolated bash -c "cd '${REPO_ROOT}/tofu' && tofu plan $*"
|
||||||
|
;;
|
||||||
|
tf)
|
||||||
|
shift || true
|
||||||
|
run_isolated bash -c "cd '${REPO_ROOT}/tofu' && tofu $*"
|
||||||
|
;;
|
||||||
|
pods)
|
||||||
|
shift || true
|
||||||
|
kpods "$@"
|
||||||
|
;;
|
||||||
|
sync)
|
||||||
|
log_info "Принудительная конвергенция FluxCD..."
|
||||||
|
flux reconcile source git atlas-repo
|
||||||
|
flux reconcile kustomization infra-sync
|
||||||
|
flux reconcile kustomization apps-sync
|
||||||
|
log_ok "Синхронизация завершена."
|
||||||
|
;;
|
||||||
|
gc)
|
||||||
|
"${ZARYA_DIR}/layers/gc.sh"
|
||||||
|
;;
|
||||||
|
state)
|
||||||
|
shift || true
|
||||||
|
target="${1:-all}"
|
||||||
|
if [[ "${target}" == "--snapshot" ]]; then
|
||||||
|
shift || true
|
||||||
|
"${ZARYA_DIR}/state/collect_state.sh" "$@"
|
||||||
|
else
|
||||||
|
case "${target}" in
|
||||||
|
host|pve) bash "${ZARYA_DIR}/state/01_pve.sh" ;;
|
||||||
|
net|vyos) bash "${ZARYA_DIR}/state/03_vyos.sh" ;;
|
||||||
|
vps|edge) bash "${ZARYA_DIR}/state/02_vps.sh" ;;
|
||||||
|
tier0) bash "${ZARYA_DIR}/state/04_tier0.sh" ;;
|
||||||
|
k8s|talos) bash "${ZARYA_DIR}/state/05_k8s.sh" ;;
|
||||||
|
workstation) bash "${ZARYA_DIR}/state/00_workstation.sh" ;;
|
||||||
|
all)
|
||||||
|
for mod in 00_workstation 01_pve 02_vps 03_vyos 04_tier0 05_k8s; do
|
||||||
|
bash "${ZARYA_DIR}/state/${mod}.sh"
|
||||||
|
done
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
log_err "Неизвестный таргет: ${target}. Доступны: host, net, vps, tier0, k8s, workstation, all"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
reset|nuke)
|
||||||
|
"${ZARYA_DIR}/layers/reset.sh"
|
||||||
|
;;
|
||||||
|
help|--help|-h)
|
||||||
|
cat <<EOF
|
||||||
|
Sovereign C2 Orchestrator (dep)
|
||||||
|
Управление сессией:
|
||||||
|
dep start [0-24] Открытие смены (TTL в часах, по дефолту 1ч, 0 = бессрочно)
|
||||||
|
dep clean Закрытие смены (уничтожение RAM-кэша и ключа SSH)
|
||||||
|
dep unseal Ручной ансил OpenBao + синк токена в K8s
|
||||||
|
Исполнение и деплой:
|
||||||
|
dep up [layer] Сквозной макро-деплой: host, net, tier0, talos, apps, all
|
||||||
|
dep run <selector> Точечный запуск плейбуков: 06 | 01-03 | 01,03,06
|
||||||
|
dep run list Каталог всех плейбуков с описанием
|
||||||
|
dep exec <команда...> Вызов любого бинарника строго внутри RAM-изоляции run_isolated
|
||||||
|
Инфраструктура (IaC):
|
||||||
|
dep a [-auto-approve] OpenTofu apply с авто-ансилом Vault
|
||||||
|
dep plan OpenTofu plan
|
||||||
|
dep tf <args...> Прямой вызов OpenTofu в изолированном контексте
|
||||||
|
Кластер и состояние:
|
||||||
|
dep pods [args...] Просмотр подов через kpods (включая kpods -w)
|
||||||
|
dep sync Принудительный синк FluxCD
|
||||||
|
dep gc Сборка мусора и сброс бэкоффа подов
|
||||||
|
dep state [target] Живой вывод статуса в терминал (host, net, vps, tier0, k8s, all)
|
||||||
|
dep reset Полное уничтожение инфраструктуры (MFA через YubiKey)
|
||||||
|
EOF
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
log_err "Неизвестная команда: ${COMMAND}. Используйте 'dep help' для справки."
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
67
bin/kpods
Executable file
67
bin/kpods
Executable file
|
|
@ -0,0 +1,67 @@
|
||||||
|
#!/bin/bash
|
||||||
|
set -u
|
||||||
|
|
||||||
|
watch_mode=false
|
||||||
|
args=()
|
||||||
|
for arg in "$@"; do
|
||||||
|
if [[ "$arg" == "-w" || "$arg" == "--watch" ]]; then
|
||||||
|
watch_mode=true
|
||||||
|
else
|
||||||
|
args+=("$arg")
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
render_pods() {
|
||||||
|
local q="${1:-}"
|
||||||
|
local raw
|
||||||
|
if [[ "$q" == "all" || "$q" == "-A" || "$q" == "-a" ]]; then
|
||||||
|
raw=$(kubectl get pods -A 2>/dev/null || true)
|
||||||
|
else
|
||||||
|
if [[ -n "$q" ]]; then
|
||||||
|
raw=$(kubectl get pods -A 2>/dev/null | awk -v q="$q" 'NR==1 || tolower($1) ~ tolower(q) || tolower($2) ~ tolower(q)' || true)
|
||||||
|
else
|
||||||
|
raw=$(kubectl get pods -A 2>/dev/null | awk 'NR==1 || ($1 !~ /^(kube-system|flux-system|cert-manager|external-secrets)$/ && $4 != "Completed")' || true)
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "$raw" | awk '
|
||||||
|
BEGIN {
|
||||||
|
ERR = "\033[38;2;255;46;76m"
|
||||||
|
RUN = "\033[38;2;180;190;254m"
|
||||||
|
WARN = "\033[38;2;250;179;135m"
|
||||||
|
HEAD = "\033[38;2;147;154;183m"
|
||||||
|
RST = "\033[0m"
|
||||||
|
}
|
||||||
|
NR==1 {
|
||||||
|
print HEAD $0 RST
|
||||||
|
next
|
||||||
|
}
|
||||||
|
{
|
||||||
|
if ($0 ~ /CrashLoopBackOff|Error|OOMKilled|ImagePullBackOff/) {
|
||||||
|
print ERR $0 RST
|
||||||
|
}
|
||||||
|
else if ($0 ~ /ContainerCreating|Pending|Terminating|Init:/ || ($0 ~ /Running/ && $2 ~ /^0\//)) {
|
||||||
|
print WARN $0 RST
|
||||||
|
}
|
||||||
|
else if ($0 ~ /Running/) {
|
||||||
|
print RUN $0 RST
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
print $0
|
||||||
|
}
|
||||||
|
}'
|
||||||
|
}
|
||||||
|
|
||||||
|
if [[ "$watch_mode" == true ]]; then
|
||||||
|
trap 'tput cnorm 2>/dev/null || true; echo ""; exit 0' INT TERM
|
||||||
|
tput civis 2>/dev/null || true
|
||||||
|
clear
|
||||||
|
while true; do
|
||||||
|
tput cup 0 0 2>/dev/null || true
|
||||||
|
render_pods "${args[@]:-}"
|
||||||
|
tput ed 2>/dev/null || true
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
else
|
||||||
|
render_pods "${args[@]:-}"
|
||||||
|
fi
|
||||||
51
bin/z
Executable file
51
bin/z
Executable file
|
|
@ -0,0 +1,51 @@
|
||||||
|
#!/bin/bash
|
||||||
|
set -euo pipefail
|
||||||
|
ZARYA_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
|
source "${ZARYA_DIR}/lib/common.sh"
|
||||||
|
|
||||||
|
SUBCMD="${1:-help}"
|
||||||
|
shift || true
|
||||||
|
|
||||||
|
case "${SUBCMD}" in
|
||||||
|
p|pod|pods) exec "${ZARYA_DIR}/cmd/pods.sh" "$@" ;;
|
||||||
|
e|err|errors) exec "${ZARYA_DIR}/cmd/errors.sh" "$@" ;;
|
||||||
|
l|log|logs) exec "${ZARYA_DIR}/cmd/logs.sh" "$@" ;;
|
||||||
|
n|net) exec "${ZARYA_DIR}/cmd/net.sh" "$@" ;;
|
||||||
|
d|df|disks) exec "${ZARYA_DIR}/cmd/disks.sh" "$@" ;;
|
||||||
|
unseal) exec "${ZARYA_DIR}/cmd/unseal.sh" "$@" ;;
|
||||||
|
login|start) exec "${ZARYA_DIR}/cmd/session.sh" "start" "$@" ;;
|
||||||
|
logout|clean) exec "${ZARYA_DIR}/cmd/session.sh" "clean" "$@" ;;
|
||||||
|
up) exec "${ZARYA_DIR}/cmd/up.sh" "$@" ;;
|
||||||
|
run) exec "${ZARYA_DIR}/cmd/run.sh" "$@" ;;
|
||||||
|
a|apply) exec "${ZARYA_DIR}/cmd/a.sh" "$@" ;;
|
||||||
|
snap) exec "${ZARYA_DIR}/cmd/snap.sh" "$@" ;;
|
||||||
|
pack) exec "${ZARYA_DIR}/cmd/pack.sh" "$@" ;;
|
||||||
|
reset|nuke) exec "${ZARYA_DIR}/layers/reset.sh" "$@" ;;
|
||||||
|
gc) exec "${ZARYA_DIR}/layers/gc.sh" "$@" ;;
|
||||||
|
help|--help|-h)
|
||||||
|
cat <<EOF
|
||||||
|
Zarya C2 Operator Interface (z)
|
||||||
|
Команды диагностики:
|
||||||
|
z pods [-w] [-n <ns>] [-s <query>] [-A] Просмотр подов
|
||||||
|
z errors Мгновенный триаж сломанных сервисов
|
||||||
|
z logs <service> [-f] [-t <N>] Логи любого сервиса без ввода неймспейса
|
||||||
|
z net Сетевой радар доступности узлов (1 сек)
|
||||||
|
z disks Срез пулов astra/terra и ZVOL томов K8s
|
||||||
|
Управление и сессия:
|
||||||
|
z login [TTL] Открытие смены (YubiKey + RAM)
|
||||||
|
z logout Мгновенная очистка RAM
|
||||||
|
z unseal Распечатывание OpenBao Vault
|
||||||
|
Деплой и IaC:
|
||||||
|
z up [layer] Макро-слои (host, net, tier0, talos, apps, all)
|
||||||
|
z run <selector> Плейбуки Ansible (01, 02)
|
||||||
|
z a [-auto-approve] OpenTofu apply в RAM-изоляции
|
||||||
|
Телеметрия и LLM:
|
||||||
|
z snap Снятие полного .md аудита в snapshots/
|
||||||
|
z pack [code|state|atlas] Сборка контекста Repomix
|
||||||
|
EOF
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
log_err "Неизвестная команда: ${SUBCMD}. Введите 'z help'."
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
6
cmd/a.sh
Executable file
6
cmd/a.sh
Executable file
|
|
@ -0,0 +1,6 @@
|
||||||
|
#!/bin/bash
|
||||||
|
set -euo pipefail
|
||||||
|
ZARYA_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
|
source "${ZARYA_DIR}/lib/common.sh"
|
||||||
|
unseal_vault
|
||||||
|
run_isolated bash -c "cd '${REPO_ROOT}/tofu' && tofu apply $*"
|
||||||
15
cmd/disks.sh
Executable file
15
cmd/disks.sh
Executable file
|
|
@ -0,0 +1,15 @@
|
||||||
|
#!/bin/bash
|
||||||
|
set -uo pipefail
|
||||||
|
ZARYA_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
|
source "${ZARYA_DIR}/lib/common.sh"
|
||||||
|
|
||||||
|
echo -e "${BLUE}=== ФИЗИЧЕСКИЕ ПУЛЫ ХОСТА PROXMOX (ZFS) ===${NC}"
|
||||||
|
ssh -n -o BatchMode=yes -o ConnectTimeout=2 proxmox "zpool list -o name,size,alloc,free,cap,health" 2>/dev/null || log_err "PVE недоступен"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo -e "${BLUE}=== ПОЛЬЗОВАТЕЛЬСКИЕ ДАТАСЕТЫ TIER-0 (/astra/data) ===${NC}"
|
||||||
|
ssh -n -o BatchMode=yes -o ConnectTimeout=2 proxmox "zfs list -o name,used,avail,refer,mountpoint -r astra/data" 2>/dev/null || true
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo -e "${BLUE}=== ТОМА ДИСКОВ KUBERNETES (ZVOL) ===${NC}"
|
||||||
|
kubectl get pvc -A -o custom-columns=NAMESPACE:.metadata.namespace,NAME:.metadata.name,STATUS:.status.phase,VOLUME:.spec.volumeName,SIZE:.status.capacity.storage 2>/dev/null || log_err "K8s API недоступен"
|
||||||
38
cmd/errors.sh
Executable file
38
cmd/errors.sh
Executable file
|
|
@ -0,0 +1,38 @@
|
||||||
|
#!/bin/bash
|
||||||
|
set -uo pipefail
|
||||||
|
ZARYA_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
|
source "${ZARYA_DIR}/lib/common.sh"
|
||||||
|
|
||||||
|
has_errors=false
|
||||||
|
echo -e "${YELLOW}=== ТРИАЖ АВАРИЙ И СБОЕВ (K8s / Host / VyOS) ===${NC}"
|
||||||
|
|
||||||
|
bad_pods=$(kubectl get pods -A --field-selector=status.phase!=Running,status.phase!=Succeeded --no-headers 2>/dev/null | grep -v 'Completed' || true)
|
||||||
|
if [[ -n "${bad_pods}" ]]; then
|
||||||
|
has_errors=true
|
||||||
|
echo -e "${RED}[K8S] Проблемные поды:${NC}"
|
||||||
|
echo "${bad_pods}" | awk '{printf " %-16s %-36s %s\n", $1, $2, $3}'
|
||||||
|
fi
|
||||||
|
|
||||||
|
pve_failed=$(ssh -n -o BatchMode=yes -o ConnectTimeout=1 proxmox "systemctl --failed --no-legend --plain" 2>/dev/null || echo "UNREACHABLE")
|
||||||
|
if [[ "${pve_failed}" == "UNREACHABLE" ]]; then
|
||||||
|
has_errors=true
|
||||||
|
echo -e "${RED}[PVE] Гипервизор недоступен по SSH${NC}"
|
||||||
|
elif [[ -n "${pve_failed}" ]]; then
|
||||||
|
has_errors=true
|
||||||
|
echo -e "${RED}[PVE] Упавшие systemd-сервисы:${NC}"
|
||||||
|
echo "${pve_failed}" | awk '{print " " $1}'
|
||||||
|
fi
|
||||||
|
|
||||||
|
vyos_failed=$(ssh -n -o BatchMode=yes -o ConnectTimeout=1 vyos_router "systemctl --failed --no-legend --plain" 2>/dev/null || echo "UNREACHABLE")
|
||||||
|
if [[ "${vyos_failed}" == "UNREACHABLE" ]]; then
|
||||||
|
has_errors=true
|
||||||
|
echo -e "${RED}[VyOS] Роутер недоступен по SSH${NC}"
|
||||||
|
elif [[ -n "${vyos_failed}" ]]; then
|
||||||
|
has_errors=true
|
||||||
|
echo -e "${RED}[VyOS] Упавшие systemd-сервисы:${NC}"
|
||||||
|
echo "${vyos_failed}" | awk '{print " " $1}'
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$has_errors" == false ]]; then
|
||||||
|
echo -e "${GREEN}✔ Все системы в норме. Сбоев не обнаружено.${NC}"
|
||||||
|
fi
|
||||||
35
cmd/logs.sh
Executable file
35
cmd/logs.sh
Executable file
|
|
@ -0,0 +1,35 @@
|
||||||
|
#!/bin/bash
|
||||||
|
set -uo pipefail
|
||||||
|
ZARYA_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
|
source "${ZARYA_DIR}/lib/common.sh"
|
||||||
|
|
||||||
|
query="${1:-}"
|
||||||
|
if [[ -z "${query}" ]]; then
|
||||||
|
log_err "Использование: z logs <сервис> [-f] [-t <строки>]"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
shift
|
||||||
|
|
||||||
|
follow_arg=""
|
||||||
|
tail_val="50"
|
||||||
|
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
-f|--follow) follow_arg="-f"; shift ;;
|
||||||
|
-t|--tail) tail_val="$2"; shift 2 ;;
|
||||||
|
*) shift ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
pod_info=$(kubectl get pods -A --no-headers 2>/dev/null | awk -v q="${query}" 'tolower($1) ~ tolower(q) || tolower($2) ~ tolower(q) {print $1, $2; exit}')
|
||||||
|
|
||||||
|
if [[ -z "${pod_info}" ]]; then
|
||||||
|
log_err "Под с именем '${query}' не найден ни в одном namespace."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
ns=$(echo "${pod_info}" | awk '{print $1}')
|
||||||
|
pod=$(echo "${pod_info}" | awk '{print $2}')
|
||||||
|
|
||||||
|
log_info "Логи ${ns}/${pod} (tail: ${tail_val}):"
|
||||||
|
exec kubectl logs -n "${ns}" "${pod}" --tail="${tail_val}" ${follow_arg}
|
||||||
32
cmd/net.sh
Executable file
32
cmd/net.sh
Executable file
|
|
@ -0,0 +1,32 @@
|
||||||
|
#!/bin/bash
|
||||||
|
set -uo pipefail
|
||||||
|
ZARYA_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
|
source "${ZARYA_DIR}/lib/common.sh"
|
||||||
|
|
||||||
|
echo -e "${BLUE}=== СЕТЕВОЙ РАДАР ИНФРАСТРУКТУРЫ (Таймаут 1 сек) ===${NC}"
|
||||||
|
printf "%-22s %-20s %-12s %s\n" "УЗЕЛ" "АДРЕС" "СТАТУС" "ЗАДЕРЖКА"
|
||||||
|
echo "------------------------------------------------------------------"
|
||||||
|
|
||||||
|
check_target() {
|
||||||
|
local name="$1"
|
||||||
|
local host="$2"
|
||||||
|
local port="$3"
|
||||||
|
local start end dur
|
||||||
|
start=$(date +%s%N)
|
||||||
|
if nc -z -w 1 "${host}" "${port}" 2>/dev/null; then
|
||||||
|
end=$(date +%s%N)
|
||||||
|
dur=$(( (end - start) / 1000000 ))
|
||||||
|
printf "%-22s %-20s \033[32m%-12s\033[0m %d ms\n" "${name}" "${host}:${port}" "UP" "${dur}"
|
||||||
|
else
|
||||||
|
printf "%-22s %-20s \033[31m%-12s\033[0m -\n" "${name}" "${host}:${port}" "DOWN"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
check_target "Proxmox Host (PVE)" "192.168.1.177" 8006
|
||||||
|
check_target "Edge Router (VyOS)" "192.168.1.2" 22
|
||||||
|
check_target "Technitium DNS" "10.0.10.11" 53
|
||||||
|
check_target "OpenBao Vault" "10.0.10.12" 8200
|
||||||
|
check_target "Forgejo GitOps" "10.0.10.13" 3000
|
||||||
|
check_target "NFS Storage (NAS)" "10.0.10.14" 2049
|
||||||
|
check_target "Traefik L2 VIP" "10.0.30.200" 443
|
||||||
|
check_target "Perimeter VPS" "153.76.194.24" 443
|
||||||
28
cmd/pack.sh
Executable file
28
cmd/pack.sh
Executable file
|
|
@ -0,0 +1,28 @@
|
||||||
|
#!/bin/bash
|
||||||
|
set -euo pipefail
|
||||||
|
ZARYA_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
|
mode="${1:-state}"
|
||||||
|
|
||||||
|
case "${mode}" in
|
||||||
|
state)
|
||||||
|
echo "==> Снятие свежего аудита..."
|
||||||
|
"${ZARYA_DIR}/state/collect_state.sh" all >/dev/null 2>&1 || true
|
||||||
|
echo "==> Упаковка среза состояния Repomix (zarya-state.xml)..."
|
||||||
|
repomix --config "${ZARYA_DIR}/repomix.state.json"
|
||||||
|
echo "✔ Файл zarya-state.xml готов для отправки в LLM."
|
||||||
|
;;
|
||||||
|
code)
|
||||||
|
echo "==> Упаковка кодовой базы zarya (zarya-code.xml)..."
|
||||||
|
repomix --config "${ZARYA_DIR}/repomix.config.json"
|
||||||
|
echo "✔ Файл zarya-code.xml готов."
|
||||||
|
;;
|
||||||
|
atlas)
|
||||||
|
echo "==> Упаковка чистой инфраструктуры atlas (atlas-code.xml)..."
|
||||||
|
cd "${ATLAS_DIR}" && repomix
|
||||||
|
echo "✔ Файл atlas/repomix-output.xml готов."
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Использование: z pack [state|code|atlas]"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
60
cmd/pods.sh
Executable file
60
cmd/pods.sh
Executable file
|
|
@ -0,0 +1,60 @@
|
||||||
|
#!/bin/bash
|
||||||
|
set -u
|
||||||
|
watch_mode=false
|
||||||
|
ns_filter=""
|
||||||
|
query=""
|
||||||
|
all_namespaces=false
|
||||||
|
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
-w|--watch) watch_mode=true; shift ;;
|
||||||
|
-n|--namespace) ns_filter="$2"; shift 2 ;;
|
||||||
|
-s|--service) query="$2"; shift 2 ;;
|
||||||
|
-A|--all) all_namespaces=true; shift ;;
|
||||||
|
all) all_namespaces=true; shift ;;
|
||||||
|
*) query="$1"; shift ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
render_pods() {
|
||||||
|
local raw
|
||||||
|
if [[ "$all_namespaces" == true ]]; then
|
||||||
|
raw=$(kubectl get pods -A -o wide 2>/dev/null || true)
|
||||||
|
elif [[ -n "$ns_filter" ]]; then
|
||||||
|
raw=$(kubectl get pods -n "$ns_filter" -o wide 2>/dev/null || true)
|
||||||
|
elif [[ -n "$query" ]]; then
|
||||||
|
raw=$(kubectl get pods -A -o wide 2>/dev/null | awk -v q="$query" 'NR==1 || tolower($1) ~ tolower(q) || tolower($2) ~ tolower(q)' || true)
|
||||||
|
else
|
||||||
|
raw=$(kubectl get pods -A -o wide 2>/dev/null | awk 'NR==1 || ($1 !~ /^(kube-system|flux-system|cert-manager|external-secrets)$/ && $4 != "Completed")' || true)
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "$raw" | awk '
|
||||||
|
BEGIN {
|
||||||
|
ERR = "\033[38;2;255;46;76m"
|
||||||
|
RUN = "\033[38;2;180;190;254m"
|
||||||
|
WARN = "\033[38;2;250;179;135m"
|
||||||
|
HEAD = "\033[38;2;147;154;183m"
|
||||||
|
RST = "\033[0m"
|
||||||
|
}
|
||||||
|
NR==1 { print HEAD $0 RST; next }
|
||||||
|
{
|
||||||
|
if ($0 ~ /CrashLoopBackOff|Error|OOMKilled|ImagePullBackOff/) { print ERR $0 RST }
|
||||||
|
else if ($0 ~ /ContainerCreating|Pending|Terminating|Init:/ || ($0 ~ /Running/ && $2 ~ /^0\//)) { print WARN $0 RST }
|
||||||
|
else if ($0 ~ /Running/) { print RUN $0 RST }
|
||||||
|
else { print $0 }
|
||||||
|
}'
|
||||||
|
}
|
||||||
|
|
||||||
|
if [[ "$watch_mode" == true ]]; then
|
||||||
|
trap 'tput cnorm 2>/dev/null || true; echo ""; exit 0' INT TERM
|
||||||
|
tput civis 2>/dev/null || true
|
||||||
|
clear
|
||||||
|
while true; do
|
||||||
|
tput cup 0 0 2>/dev/null || true
|
||||||
|
render_pods
|
||||||
|
tput ed 2>/dev/null || true
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
else
|
||||||
|
render_pods
|
||||||
|
fi
|
||||||
30
cmd/run.sh
Executable file
30
cmd/run.sh
Executable file
|
|
@ -0,0 +1,30 @@
|
||||||
|
#!/bin/bash
|
||||||
|
set -euo pipefail
|
||||||
|
ZARYA_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
|
source "${ZARYA_DIR}/lib/common.sh"
|
||||||
|
|
||||||
|
resolve_playbook() {
|
||||||
|
local id="$1"
|
||||||
|
local found
|
||||||
|
found=$(find "${REPO_ROOT}/ansible/playbooks" -maxdepth 1 -name "${id}_*.yml" | head -n1)
|
||||||
|
if [[ -z "${found}" ]]; then
|
||||||
|
log_err "Плейбук с префиксом [${id}] не найден в ansible/playbooks/"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "${found}"
|
||||||
|
}
|
||||||
|
|
||||||
|
if [[ $# -eq 0 ]] || [[ "${1:-}" == "list" ]]; then
|
||||||
|
echo -e "${BLUE}=== Каталог плейбуков (${REPO_ROOT}/ansible/playbooks/) ===${NC}"
|
||||||
|
for pb in $(ls -1 "${REPO_ROOT}/ansible/playbooks"/[0-9][0-9]_*.yml 2>/dev/null | sort); do
|
||||||
|
echo " $(basename "${pb}")"
|
||||||
|
done
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
for id in "$@"; do
|
||||||
|
pb_path=$(resolve_playbook "${id}")
|
||||||
|
log_info "==> Исполнение: $(basename "${pb_path}")..."
|
||||||
|
run_isolated ansible-playbook -i "${REPO_ROOT}/ansible/inventory.yml" "${pb_path}"
|
||||||
|
done
|
||||||
|
log_ok "Плейбуки выполнены."
|
||||||
21
cmd/session.sh
Executable file
21
cmd/session.sh
Executable file
|
|
@ -0,0 +1,21 @@
|
||||||
|
#!/bin/bash
|
||||||
|
set -euo pipefail
|
||||||
|
ZARYA_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
|
source "${ZARYA_DIR}/lib/common.sh"
|
||||||
|
|
||||||
|
action="${1:-start}"
|
||||||
|
shift || true
|
||||||
|
|
||||||
|
case "${action}" in
|
||||||
|
start|login)
|
||||||
|
hours="${1:-1}"
|
||||||
|
ensure_prerequisites
|
||||||
|
load_secrets_to_ram "${hours}"
|
||||||
|
unseal_vault
|
||||||
|
log_ok "Сессия Zarya активна (TTL: ${hours} ч)."
|
||||||
|
;;
|
||||||
|
clean|logout)
|
||||||
|
rm -f "${RAM_CACHE}" /dev/shm/.atlas_core
|
||||||
|
log_ok "RAM-кэш и ключи уничтожены."
|
||||||
|
;;
|
||||||
|
esac
|
||||||
5
cmd/snap.sh
Executable file
5
cmd/snap.sh
Executable file
|
|
@ -0,0 +1,5 @@
|
||||||
|
#!/bin/bash
|
||||||
|
set -uo pipefail
|
||||||
|
ZARYA_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
|
target="${1:-all}"
|
||||||
|
"${ZARYA_DIR}/state/collect_state.sh" "${target}"
|
||||||
6
cmd/unseal.sh
Executable file
6
cmd/unseal.sh
Executable file
|
|
@ -0,0 +1,6 @@
|
||||||
|
#!/bin/bash
|
||||||
|
set -euo pipefail
|
||||||
|
ZARYA_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
|
source "${ZARYA_DIR}/lib/common.sh"
|
||||||
|
ensure_prerequisites
|
||||||
|
unseal_vault
|
||||||
25
cmd/up.sh
Executable file
25
cmd/up.sh
Executable file
|
|
@ -0,0 +1,25 @@
|
||||||
|
#!/bin/bash
|
||||||
|
set -euo pipefail
|
||||||
|
ZARYA_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
|
source "${ZARYA_DIR}/lib/common.sh"
|
||||||
|
|
||||||
|
target="${1:-all}"
|
||||||
|
case "${target}" in
|
||||||
|
host) run_isolated "${ZARYA_DIR}/layers/00_host.sh" ;;
|
||||||
|
net) run_isolated "${ZARYA_DIR}/layers/01_net.sh" ;;
|
||||||
|
tier0) run_isolated "${ZARYA_DIR}/layers/02_tier0.sh" ;;
|
||||||
|
talos) run_isolated "${ZARYA_DIR}/layers/03_talos.sh" ;;
|
||||||
|
apps) "${ZARYA_DIR}/layers/04_apps.sh" ;;
|
||||||
|
all)
|
||||||
|
run_isolated "${ZARYA_DIR}/layers/00_host.sh"
|
||||||
|
run_isolated "${ZARYA_DIR}/layers/01_net.sh"
|
||||||
|
run_isolated "${ZARYA_DIR}/layers/02_tier0.sh"
|
||||||
|
run_isolated "${ZARYA_DIR}/layers/03_talos.sh"
|
||||||
|
"${ZARYA_DIR}/layers/04_apps.sh"
|
||||||
|
log_ok "Инфраструктура Atlas развернута."
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
log_err "Неизвестный слой: ${target}. Доступны: host, net, tier0, talos, apps, all"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
14
config.env
Normal file
14
config.env
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
#!/bin/bash
|
||||||
|
# Atlas C2 Configuration SSOT
|
||||||
|
|
||||||
|
KDBX_PATH="${HOME}/Projects/atlas-secrets.kdbx"
|
||||||
|
RAM_CACHE="/dev/shm/.atlas_secrets_cache"
|
||||||
|
RAM_KEY="/dev/shm/.atlas_core"
|
||||||
|
|
||||||
|
VAULT_ADDR="http://10.0.10.12:8200"
|
||||||
|
VYOS_ROUTER_IP="192.168.1.2"
|
||||||
|
NET_PREFIX="10.0.0.0/16"
|
||||||
|
GIT_HOST="git.svo88.space"
|
||||||
|
PVE_ENDPOINT="https://192.168.1.177:8006/"
|
||||||
|
|
||||||
|
DEFAULT_SESSION_TTL=1
|
||||||
26
layers/00_host.sh
Executable file
26
layers/00_host.sh
Executable file
|
|
@ -0,0 +1,26 @@
|
||||||
|
#!/bin/bash
|
||||||
|
set -euo pipefail
|
||||||
|
source "$(dirname "${BASH_SOURCE[0]}")/../lib/common.sh"
|
||||||
|
ensure_prerequisites
|
||||||
|
load_secrets_to_ram
|
||||||
|
|
||||||
|
log_info "[Layer 0: PVE Host] Подготовка хоста, шаблонов и базовых директорий ZFS..."
|
||||||
|
ansible-playbook -i "${REPO_ROOT}/ansible/inventory.yml" "${REPO_ROOT}/ansible/playbooks/01_pve_host_day0.yml"
|
||||||
|
|
||||||
|
log_info "[Layer 0: PVE Host] Инициализация compute-скелетов ВМ и LXC через OpenTofu..."
|
||||||
|
cd "${REPO_ROOT}/tofu" && tofu apply \
|
||||||
|
-target=proxmox_virtual_environment_vm.vyos_router \
|
||||||
|
-target=proxmox_virtual_environment_container.dns0 \
|
||||||
|
-target=proxmox_virtual_environment_container.vault0 \
|
||||||
|
-target=proxmox_virtual_environment_container.git0 \
|
||||||
|
-target=proxmox_virtual_environment_container.storage0 \
|
||||||
|
-target=proxmox_virtual_environment_container.registry0 \
|
||||||
|
-target=proxmox_virtual_environment_vm.talos_master_1 \
|
||||||
|
-target=proxmox_virtual_environment_vm.talos_worker_1 \
|
||||||
|
-target=proxmox_virtual_environment_vm.talos_worker_2 \
|
||||||
|
-auto-approve
|
||||||
|
|
||||||
|
log_info "[Layer 0: PVE Host] Применение постоянных ZFS-маунтов (mp) на созданные LXC..."
|
||||||
|
ansible-playbook -i "${REPO_ROOT}/ansible/inventory.yml" "${REPO_ROOT}/ansible/playbooks/01_pve_host_day0.yml"
|
||||||
|
|
||||||
|
log_ok "Layer 0 готов: хост настроен, ВМ и LXC созданы, ZFS смонтирован."
|
||||||
9
layers/01_net.sh
Executable file
9
layers/01_net.sh
Executable file
|
|
@ -0,0 +1,9 @@
|
||||||
|
#!/bin/bash
|
||||||
|
set -euo pipefail
|
||||||
|
source "$(dirname "${BASH_SOURCE[0]}")/../lib/common.sh"
|
||||||
|
ensure_prerequisites
|
||||||
|
load_secrets_to_ram
|
||||||
|
|
||||||
|
log_info "[Layer 1: VyOS & Perimeter] Накатка конфигурации сети..."
|
||||||
|
ansible-playbook -i "${REPO_ROOT}/ansible/inventory.yml" "${REPO_ROOT}/ansible/playbooks/02_vyos_network_day0.yml"
|
||||||
|
log_ok "Layer 1 готов."
|
||||||
28
layers/02_tier0.sh
Executable file
28
layers/02_tier0.sh
Executable file
|
|
@ -0,0 +1,28 @@
|
||||||
|
#!/bin/bash
|
||||||
|
set -euo pipefail
|
||||||
|
source "$(dirname "${BASH_SOURCE[0]}")/../lib/common.sh"
|
||||||
|
ensure_prerequisites
|
||||||
|
load_secrets_to_ram
|
||||||
|
|
||||||
|
log_info "[Layer 2: Tier-0 Air-Gap] Развертывание базовой инфраструктуры (Registry, DNS, Vault)..."
|
||||||
|
for pb in 03_zot_registry.yml 04_technitium_dns.yml 05_openbao_vault.yml; do
|
||||||
|
ansible-playbook -i "${REPO_ROOT}/ansible/inventory.yml" "${REPO_ROOT}/ansible/playbooks/${pb}"
|
||||||
|
done
|
||||||
|
|
||||||
|
log_info "[Layer 2: Tier-0 Air-Gap] Распечатывание OpenBao Vault..."
|
||||||
|
unseal_vault
|
||||||
|
|
||||||
|
log_info "[Layer 2: Tier-0 Air-Gap] Наполнение Vault секретами через OpenTofu..."
|
||||||
|
cd "${REPO_ROOT}/tofu" && tofu apply -auto-approve
|
||||||
|
|
||||||
|
log_info "[Layer 2: Tier-0 Air-Gap] Развертывание зависимых сервисов (Forgejo, NAS Storage)..."
|
||||||
|
for pb in 06_forgejo_git.yml 07_nas_storage.yml; do
|
||||||
|
ansible-playbook -i "${REPO_ROOT}/ansible/inventory.yml" "${REPO_ROOT}/ansible/playbooks/${pb}"
|
||||||
|
done
|
||||||
|
|
||||||
|
log_info "[Layer 2: Tier-0 Air-Gap] Автоматическая первичная синхронизация Git в Forgejo (SEC-03 RAM auth)..."
|
||||||
|
CURRENT_BRANCH=$(git -C "${REPO_ROOT}" branch --show-current 2>/dev/null || echo "master")
|
||||||
|
git -C "${REPO_ROOT}" push -u http://10.0.10.13:3000/atlas/atlas.git "${CURRENT_BRANCH}" 2>/dev/null || \
|
||||||
|
git -C "${REPO_ROOT}" push http://10.0.10.13:3000/atlas/atlas.git "${CURRENT_BRANCH}" 2>/dev/null || true
|
||||||
|
|
||||||
|
log_ok "Layer 2 готов. Базовые сервисы, секреты и GitOps-репозиторий развернуты."
|
||||||
17
layers/03_talos.sh
Executable file
17
layers/03_talos.sh
Executable file
|
|
@ -0,0 +1,17 @@
|
||||||
|
#!/bin/bash
|
||||||
|
set -euo pipefail
|
||||||
|
source "$(dirname "${BASH_SOURCE[0]}")/../lib/common.sh"
|
||||||
|
ensure_prerequisites
|
||||||
|
load_secrets_to_ram
|
||||||
|
unseal_vault
|
||||||
|
|
||||||
|
log_info "[Layer 3: Talos & Kubernetes] Проверка конфигураций машинных манифестов..."
|
||||||
|
if [[ ! -f "${REPO_ROOT}/talos/clusterconfig/controlplane.yaml" ]]; then
|
||||||
|
log_info "Генерация машинных конфигураций Talos..."
|
||||||
|
cd "${REPO_ROOT}" && talos/generate_config.sh
|
||||||
|
fi
|
||||||
|
|
||||||
|
log_info "[Layer 3: Talos & Kubernetes] Бутстрап кластера..."
|
||||||
|
cd "${REPO_ROOT}/talos" && ./bootstrap.sh
|
||||||
|
sync_k8s_vault_token
|
||||||
|
log_ok "Layer 3 готов."
|
||||||
8
layers/04_apps.sh
Executable file
8
layers/04_apps.sh
Executable file
|
|
@ -0,0 +1,8 @@
|
||||||
|
#!/bin/bash
|
||||||
|
set -euo pipefail
|
||||||
|
source "$(dirname "${BASH_SOURCE[0]}")/../lib/common.sh"
|
||||||
|
ensure_prerequisites
|
||||||
|
|
||||||
|
log_info "[Layer 4: Apps] Применение манифестов FluxCD..."
|
||||||
|
kubectl apply -f "${REPO_ROOT}/kubernetes/bootstrap/sync.yaml"
|
||||||
|
log_ok "Layer 4 готов."
|
||||||
37
layers/gc.sh
Executable file
37
layers/gc.sh
Executable file
|
|
@ -0,0 +1,37 @@
|
||||||
|
#!/bin/bash
|
||||||
|
set -uo pipefail
|
||||||
|
source "$(dirname "${BASH_SOURCE[0]}")/../lib/common.sh"
|
||||||
|
ensure_prerequisites
|
||||||
|
|
||||||
|
log_info "🧹 Запуск генеральной очистки кластера (dep gc)..."
|
||||||
|
|
||||||
|
# 1. Удаление зомби-нод в статусе NotReady
|
||||||
|
NOT_READY_NODES=$(kubectl get nodes --no-headers 2>/dev/null | grep 'NotReady' | awk '{print $1}' || true)
|
||||||
|
if [[ -n "${NOT_READY_NODES}" ]]; then
|
||||||
|
for node in ${NOT_READY_NODES}; do
|
||||||
|
log_warn "Удаляем мертвую ноду: ${node}"
|
||||||
|
kubectl delete node "${node}" --timeout=5s 2>/dev/null || true
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 2. Мгновенная зачистка зависших в Terminating
|
||||||
|
kubectl get pods -A --no-headers 2>/dev/null | awk '/Terminating/ {print $1, $2}' | while read -r ns pod; do
|
||||||
|
log_warn "Принудительно удаляем: ${ns}/${pod}"
|
||||||
|
kubectl -n "${ns}" delete pod "${pod}" --force --grace-period=0 2>/dev/null || true
|
||||||
|
done
|
||||||
|
|
||||||
|
# 3. Сброс бэкоффа для упавших подов (CrashLoopBackOff, Error, OOMKilled, CreateContainerConfigError)
|
||||||
|
kubectl get pods -A --no-headers 2>/dev/null | grep -E 'CrashLoopBackOff|Error|OOMKilled|CreateContainerConfigError|ImagePullBackOff' | awk '{print $1, $2}' | while read -r ns pod; do
|
||||||
|
log_info "Сбрасываем упавший под: ${ns}/${pod}"
|
||||||
|
kubectl -n "${ns}" delete pod "${pod}" --force --grace-period=0 2>/dev/null || true
|
||||||
|
done
|
||||||
|
|
||||||
|
# 4. Очистка завершенных одноразовых Job
|
||||||
|
COMPLETED_JOBS=$(kubectl get jobs -A --field-selector status.successful=1 --no-headers 2>/dev/null | awk '{print $1, $2}' || true)
|
||||||
|
if [[ -n "${COMPLETED_JOBS}" ]]; then
|
||||||
|
echo "${COMPLETED_JOBS}" | while read -r ns job; do
|
||||||
|
kubectl -n "${ns}" delete job "${job}" >/dev/null 2>&1 || true
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
log_ok "Кластер очищен. Все сервисы пересоздаются против живых БД и NFS."
|
||||||
60
layers/reset.sh
Executable file
60
layers/reset.sh
Executable file
|
|
@ -0,0 +1,60 @@
|
||||||
|
#!/bin/bash
|
||||||
|
set -euo pipefail
|
||||||
|
source "$(dirname "${BASH_SOURCE[0]}")/../lib/common.sh"
|
||||||
|
RESET_WIPE_HISTORY="${RESET_WIPE_HISTORY:-false}"
|
||||||
|
|
||||||
|
log_warn "ВНИМАНИЕ! Инициирована процедура полного уничтожения инфраструктуры Proxmox VE."
|
||||||
|
log_info "Очистка текущей RAM-сессии. Требуется физическая повторная аутентификация..."
|
||||||
|
rm -f "${RAM_CACHE}" /dev/shm/.atlas_core
|
||||||
|
|
||||||
|
read -s -p "Master Password для подтверждения сноса: " KP_PASS
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
yk_args=()
|
||||||
|
if ykman info >/dev/null 2>&1; then
|
||||||
|
yk_args=("-y" "2")
|
||||||
|
log_info "Приготовьтесь к аппаратному подтверждению (касание YubiKey)..."
|
||||||
|
fi
|
||||||
|
|
||||||
|
tmp_verify="/dev/shm/.verify_$$"
|
||||||
|
if ! echo "${KP_PASS}" | timeout 15 keepassxc-cli export "${yk_args[@]}" "${KDBX_PATH}" -f xml > "${tmp_verify}" 2>/dev/null; then
|
||||||
|
log_err "Аппаратная проверка провалена или таймаут касания. Доступ заблокирован."
|
||||||
|
rm -f "${tmp_verify}"
|
||||||
|
unset KP_PASS
|
||||||
|
if [[ "${RESET_WIPE_HISTORY}" == "true" ]]; then
|
||||||
|
history -c 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
rm -f "${tmp_verify}"
|
||||||
|
unset KP_PASS
|
||||||
|
log_ok "Аппаратная проверка YubiKey и пароля подтверждена."
|
||||||
|
|
||||||
|
read -r -p "Введите 'CONFIRM-DESTRUCTION' для безвозвратного уничтожения: " confirm
|
||||||
|
if [[ "${confirm}" != "CONFIRM-DESTRUCTION" ]]; then
|
||||||
|
log_warn "Отмена операции."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
log_info "Снятие защиты и уничтожение виртуальных машин и контейнеров на Proxmox..."
|
||||||
|
ssh proxmox bash << 'SSHEOF'
|
||||||
|
for id in 100 201 202 203; do
|
||||||
|
qm stop $id --skiplock 1 2>/dev/null || true
|
||||||
|
qm destroy $id --purge 1 --destroy-unreferenced-disks 1 2>/dev/null || true
|
||||||
|
done
|
||||||
|
for id in 101 102 103 104 105; do
|
||||||
|
pct set $id -protection 0 2>/dev/null || true
|
||||||
|
pct stop $id --skiplock 1 2>/dev/null || true
|
||||||
|
pct destroy $id --purge 1 --force 1 2>/dev/null || true
|
||||||
|
done
|
||||||
|
SSHEOF
|
||||||
|
|
||||||
|
log_info "Очистка локального стейта OpenTofu..."
|
||||||
|
rm -f "${REPO_ROOT}/tofu/terraform.tfstate"* "${REPO_ROOT}/tofu/.terraform.tfstate.lock.info"
|
||||||
|
rm -f "${RAM_CACHE}" /dev/shm/.atlas_core
|
||||||
|
|
||||||
|
if [[ "${RESET_WIPE_HISTORY}" == "true" ]]; then
|
||||||
|
history -c 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
log_ok "Сервер зачищен до голого железа."
|
||||||
11
lib/common.sh
Executable file
11
lib/common.sh
Executable file
|
|
@ -0,0 +1,11 @@
|
||||||
|
#!/bin/bash
|
||||||
|
set -euo pipefail
|
||||||
|
ZARYA_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
|
export ATLAS_DIR="${ATLAS_DIR:-$HOME/Projects/atlas}"
|
||||||
|
export REPO_ROOT="${ATLAS_DIR}"
|
||||||
|
|
||||||
|
source "${ZARYA_DIR}/config.env" 2>/dev/null || true
|
||||||
|
source "${ZARYA_DIR}/lib/log.sh"
|
||||||
|
source "${ZARYA_DIR}/lib/net.sh"
|
||||||
|
source "${ZARYA_DIR}/lib/vault.sh"
|
||||||
|
source "${ZARYA_DIR}/lib/session.sh"
|
||||||
71
lib/kdbx_parser.py
Normal file
71
lib/kdbx_parser.py
Normal file
|
|
@ -0,0 +1,71 @@
|
||||||
|
import sys
|
||||||
|
import xml.etree.ElementTree as ET
|
||||||
|
import os
|
||||||
|
dump_file = sys.argv[1]
|
||||||
|
cache_file = sys.argv[2]
|
||||||
|
key_file = sys.argv[3]
|
||||||
|
ttl_sec = sys.argv[4]
|
||||||
|
pve_endpoint = sys.argv[5]
|
||||||
|
vault_addr = sys.argv[6]
|
||||||
|
tree = ET.parse(dump_file)
|
||||||
|
root = tree.getroot()
|
||||||
|
for h in root.findall(".//History"):
|
||||||
|
h.clear()
|
||||||
|
for g in root.findall(".//Group"):
|
||||||
|
name = g.find("./Name")
|
||||||
|
if name is not None and name.text == "Recycle Bin":
|
||||||
|
g.clear()
|
||||||
|
entries = {}
|
||||||
|
for entry in root.findall(".//Entry"):
|
||||||
|
title = ""
|
||||||
|
username = ""
|
||||||
|
password = ""
|
||||||
|
notes = ""
|
||||||
|
for string in entry.findall("./String"):
|
||||||
|
key_elem = string.find("./Key")
|
||||||
|
val_elem = string.find("./Value")
|
||||||
|
if key_elem is not None and val_elem is not None and key_elem.text:
|
||||||
|
k = key_elem.text
|
||||||
|
v = val_elem.text or ""
|
||||||
|
if k == "Title": title = v.strip()
|
||||||
|
elif k == "UserName": username = v.strip()
|
||||||
|
elif k == "Password": password = v.strip()
|
||||||
|
elif k == "Notes": notes = v.strip()
|
||||||
|
if title and title not in entries:
|
||||||
|
entries[title] = {
|
||||||
|
"username": username,
|
||||||
|
"password": password,
|
||||||
|
"notes": notes
|
||||||
|
}
|
||||||
|
ssh_loaded = False
|
||||||
|
for title in ("Atlas Core Key", "AtlasCoreKey", "Atlas Core"):
|
||||||
|
entry_data = entries.get(title, {})
|
||||||
|
for field in ("password", "notes"):
|
||||||
|
val = entry_data.get(field, "")
|
||||||
|
if "-----BEGIN" in val or "PRIVATE KEY" in val:
|
||||||
|
with open(key_file, "w") as kf:
|
||||||
|
kf.write(val.strip() + "\n")
|
||||||
|
os.chmod(key_file, 0o600)
|
||||||
|
ssh_loaded = True
|
||||||
|
break
|
||||||
|
if ssh_loaded:
|
||||||
|
break
|
||||||
|
if not ssh_loaded:
|
||||||
|
sys.stderr.write("Внимание: приватный SSH-ключ не найден в полях Password/Notes записи Atlas Core Key.\n")
|
||||||
|
with open(cache_file, "w") as f:
|
||||||
|
f.write(f"# TTL_SECONDS={ttl_sec}\n")
|
||||||
|
f.write(f"export PROXMOX_VE_API_TOKEN={repr(entries.get('ProxmoxAPI', {}).get('password', ''))}\n")
|
||||||
|
f.write(f"export TF_VAR_proxmox_api_token={repr(entries.get('ProxmoxAPI', {}).get('password', ''))}\n")
|
||||||
|
f.write(f"export TF_VAR_state_passphrase={repr(entries.get('TofuState', {}).get('password', ''))}\n")
|
||||||
|
f.write(f"export PROXMOX_VE_ENDPOINT={repr(pve_endpoint)}\n")
|
||||||
|
f.write(f"export VAULT_ADDR={repr(vault_addr)}\n")
|
||||||
|
f.write(f"export VAULT_TOKEN={repr(entries.get('OpenBaoToken', {}).get('password', ''))}\n")
|
||||||
|
f.write(f"export VAULT_UNSEAL_KEY={repr(entries.get('OpenBaoUnseal', {}).get('password', ''))}\n")
|
||||||
|
f.write(f"export TECHNITIUM_PASSWORD={repr(entries.get('Technitium', {}).get('password', 'admin'))}\n")
|
||||||
|
f.write(f"export TF_VAR_vaultwarden_admin_token={repr(entries.get('VaultwardenAdmin', {}).get('password', ''))}\n")
|
||||||
|
f.write(f"export TF_VAR_forgejo_runner_token={repr(entries.get('ForgejoRunnerToken', {}).get('password', ''))}\n")
|
||||||
|
f.write(f"export TF_VAR_forgejo_oidc_client_id={repr(entries.get('ForgejoOIDC', {}).get('username', 'forgejo'))}\n")
|
||||||
|
f.write(f"export TF_VAR_forgejo_oidc_secret={repr(entries.get('ForgejoOIDC', {}).get('password', ''))}\n")
|
||||||
|
f.write(f"export ATLAS_PUSH_TOKEN={repr(entries.get('AtlasPushToken', {}).get('password', ''))}\n")
|
||||||
|
f.write(f"export VYOS_WG_PRIVATE_KEY={repr(entries.get('VyosWG', {}).get('password', ''))}\n")
|
||||||
|
os.chmod(cache_file, 0o600)
|
||||||
13
lib/log.sh
Normal file
13
lib/log.sh
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
#!/bin/bash
|
||||||
|
# Log formatting and color definitions
|
||||||
|
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
BLUE='\033[0;34m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
NC='\033[0m'
|
||||||
|
|
||||||
|
log_info() { echo -e "${BLUE}==>${NC} $*"; }
|
||||||
|
log_ok() { echo -e "${GREEN}✔${NC} $*"; }
|
||||||
|
log_warn() { echo -e "${YELLOW}⚠${NC} $*"; }
|
||||||
|
log_err() { echo -e "${RED}✖${NC} $*"; }
|
||||||
27
lib/net.sh
Normal file
27
lib/net.sh
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
#!/bin/bash
|
||||||
|
# Network routing and terminal prerequisites
|
||||||
|
ensure_prerequisites() {
|
||||||
|
local target_ip="${VAULT_ADDR#*//}"
|
||||||
|
target_ip="${target_ip%%:*}"
|
||||||
|
if ! ip route get "${target_ip}" 2>/dev/null | grep -q "${VYOS_ROUTER_IP}"; then
|
||||||
|
log_info "Восстанавливаем маршрут к ${NET_PREFIX} через VyOS (${VYOS_ROUTER_IP})..."
|
||||||
|
sudo ip route replace "${NET_PREFIX}" via "${VYOS_ROUTER_IP}" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
# Регистрация Git Credential Helper (доменный и прямой IP Tier-0)
|
||||||
|
local target_helper="!${REPO_ROOT}/deploy.sh git-credential"
|
||||||
|
local current_helper
|
||||||
|
current_helper=$(git config --global --get credential."https://${GIT_HOST}".helper 2>/dev/null || true)
|
||||||
|
if [[ "${current_helper}" != "${target_helper}" ]]; then
|
||||||
|
git config --global credential."https://${GIT_HOST}".helper "${target_helper}"
|
||||||
|
fi
|
||||||
|
local direct_helper
|
||||||
|
direct_helper=$(git config --global --get credential."http://10.0.10.13:3000".helper 2>/dev/null || true)
|
||||||
|
if [[ "${direct_helper}" != "${target_helper}" ]]; then
|
||||||
|
git config --global credential."http://10.0.10.13:3000".helper "${target_helper}"
|
||||||
|
fi
|
||||||
|
# Нативная привязка IdentityFile для всех C2-хостов и LXC-контейнеров
|
||||||
|
local ssh_hosts="proxmox pve0 vyos vyos_router 10.0.* dns0 vault0 git0 storage0 registry0"
|
||||||
|
if [[ -f "${HOME}/.ssh/config" ]] && ! grep -q "IdentityFile ${RAM_KEY}" "${HOME}/.ssh/config"; then
|
||||||
|
printf "\nHost %s\n IdentityFile %s\n IdentitiesOnly yes\n" "${ssh_hosts}" "${RAM_KEY}" >> "${HOME}/.ssh/config"
|
||||||
|
fi
|
||||||
|
}
|
||||||
71
lib/session.sh
Normal file
71
lib/session.sh
Normal file
|
|
@ -0,0 +1,71 @@
|
||||||
|
#!/bin/bash
|
||||||
|
# Session state in RAM tmpfs and YubiKey integration
|
||||||
|
|
||||||
|
load_secrets_to_ram() {
|
||||||
|
local hours="${1:-$DEFAULT_SESSION_TTL}"
|
||||||
|
local now
|
||||||
|
now=$(date +%s)
|
||||||
|
|
||||||
|
ensure_prerequisites
|
||||||
|
|
||||||
|
# Валидация сессии по наличию непустых кэша секретов и SSH-ключа
|
||||||
|
if [[ -f "${RAM_CACHE}" ]] && [[ -f "${RAM_KEY}" ]] && [[ -s "${RAM_KEY}" ]]; then
|
||||||
|
local created age cached_ttl
|
||||||
|
created=$(stat -c %Y "${RAM_CACHE}" 2>/dev/null || echo 0)
|
||||||
|
age=$(( now - created ))
|
||||||
|
cached_ttl=$(grep '^# TTL_SECONDS=' "${RAM_CACHE}" 2>/dev/null | cut -d= -f2 || echo 3600)
|
||||||
|
|
||||||
|
if [[ "${cached_ttl}" -ne 0 ]] && (( age >= cached_ttl )); then
|
||||||
|
log_warn "Время сессии истекло ($(( age / 60 )) мин >= $(( cached_ttl / 60 )) мин). Инвалидация кэша..."
|
||||||
|
rm -f "${RAM_CACHE}" "${RAM_KEY}"
|
||||||
|
else
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
local ttl_seconds
|
||||||
|
if [[ "${hours}" -eq 0 ]]; then
|
||||||
|
ttl_seconds=0
|
||||||
|
else
|
||||||
|
ttl_seconds=$(( hours * 3600 ))
|
||||||
|
fi
|
||||||
|
|
||||||
|
log_info "🔐 Вход в защищенный контур C2..."
|
||||||
|
read -s -p "Master Password: " KP_PASS
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
local yk_args=()
|
||||||
|
if ykman info >/dev/null 2>&1; then
|
||||||
|
yk_args=("-y" "2")
|
||||||
|
log_info "YubiKey обнаружен. Приготовьтесь к аппаратному подтверждению (1 касание)..."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Атомарный 1-Touch экспорт всей базы KDBX в RAM (ровно 1 касание YubiKey на всю сессию)
|
||||||
|
local tmp_dump="/dev/shm/.kdbx_dump_${now}_$$.xml"
|
||||||
|
rm -f "${tmp_dump}"
|
||||||
|
touch "${tmp_dump}"
|
||||||
|
chmod 600 "${tmp_dump}"
|
||||||
|
|
||||||
|
if ! echo "${KP_PASS}" | keepassxc-cli export "${yk_args[@]}" "${KDBX_PATH}" -f xml > "${tmp_dump}" 2>/dev/null; then
|
||||||
|
log_err "Ошибка расшифровки базы KDBX (неверный пароль или таймаут касания YubiKey)"
|
||||||
|
rm -f "${tmp_dump}"
|
||||||
|
unset KP_PASS
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
unset KP_PASS
|
||||||
|
|
||||||
|
python3 "${REPO_ROOT}/scripts/lib/kdbx_parser.py" "${tmp_dump}" "${RAM_CACHE}" "${RAM_KEY}" "${ttl_seconds}" "${PVE_ENDPOINT}" "${VAULT_ADDR}"
|
||||||
|
rm -f "${tmp_dump}"
|
||||||
|
log_ok "База расшифрована за 1 касание. Ключ и секреты в RAM (TTL: ${hours}ч)"
|
||||||
|
}
|
||||||
|
|
||||||
|
run_isolated() {
|
||||||
|
ensure_prerequisites
|
||||||
|
load_secrets_to_ram
|
||||||
|
(
|
||||||
|
set -a
|
||||||
|
source "${RAM_CACHE}"
|
||||||
|
set +a
|
||||||
|
"$@"
|
||||||
|
)
|
||||||
|
}
|
||||||
46
lib/vault.sh
Normal file
46
lib/vault.sh
Normal file
|
|
@ -0,0 +1,46 @@
|
||||||
|
#!/bin/bash
|
||||||
|
# OpenBao Vault operations and Kubernetes Secret synchronization
|
||||||
|
|
||||||
|
sync_k8s_vault_token() {
|
||||||
|
if kubectl --request-timeout=2s cluster-info >/dev/null 2>&1; then
|
||||||
|
log_info "Синхронизация токена OpenBao в Kubernetes..."
|
||||||
|
local bao_tok
|
||||||
|
bao_tok=$(grep '^export VAULT_TOKEN=' "${RAM_CACHE}" 2>/dev/null | cut -d= -f2- | tr -d '"'"'")
|
||||||
|
if [[ -n "${bao_tok}" ]]; then
|
||||||
|
kubectl create namespace external-secrets --dry-run=client -o yaml | kubectl apply -f - >/dev/null 2>&1 || true
|
||||||
|
kubectl -n external-secrets create secret generic openbao-token \
|
||||||
|
--from-literal=token="${bao_tok}" \
|
||||||
|
--dry-run=client -o yaml | kubectl apply -f - >/dev/null 2>&1
|
||||||
|
kubectl -n external-secrets rollout restart deployment external-secrets >/dev/null 2>&1 || true
|
||||||
|
log_ok "Токен OpenBao в Kubernetes актуализирован."
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
unseal_vault() {
|
||||||
|
local unseal_key target_vault_addr seal_status resp
|
||||||
|
unseal_key=$(grep '^export VAULT_UNSEAL_KEY=' "${RAM_CACHE}" 2>/dev/null | cut -d= -f2- | tr -d '"'"'")
|
||||||
|
target_vault_addr=$(grep '^export VAULT_ADDR=' "${RAM_CACHE}" 2>/dev/null | cut -d= -f2- | tr -d '"'"'")
|
||||||
|
target_vault_addr="${target_vault_addr:-$VAULT_ADDR}"
|
||||||
|
|
||||||
|
seal_status=$(curl -s --connect-timeout 2 "${target_vault_addr}/v1/sys/seal-status" 2>/dev/null || echo "")
|
||||||
|
if [[ -z "${seal_status}" ]]; then
|
||||||
|
log_warn "OpenBao (${target_vault_addr}) недоступен. Пропуск ансила."
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if echo "${seal_status}" | grep -q '"sealed":true'; then
|
||||||
|
log_info "OpenBao запечатан. Ансил..."
|
||||||
|
resp=$(printf '{"key":"%s"}' "${unseal_key}" | curl -s -X POST -H "Content-Type: application/json" --data-binary @- "${target_vault_addr}/v1/sys/unseal")
|
||||||
|
if echo "${resp}" | grep -q '"sealed":false'; then
|
||||||
|
log_ok "OpenBao успешно распечатан."
|
||||||
|
sync_k8s_vault_token
|
||||||
|
else
|
||||||
|
log_err "Ошибка ансила OpenBao: ${resp}"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
log_ok "OpenBao распечатан."
|
||||||
|
sync_k8s_vault_token
|
||||||
|
fi
|
||||||
|
}
|
||||||
25
repomix.config.json
Normal file
25
repomix.config.json
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
{
|
||||||
|
"output": {
|
||||||
|
"filePath": "zarya-code.xml",
|
||||||
|
"style": "xml",
|
||||||
|
"removeComments": false,
|
||||||
|
"removeEmptyLines": true,
|
||||||
|
"copyToClipboard": true
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"bin/**",
|
||||||
|
"cmd/**",
|
||||||
|
"lib/**",
|
||||||
|
"layers/**",
|
||||||
|
"state/*.sh",
|
||||||
|
"config.env"
|
||||||
|
],
|
||||||
|
"ignore": {
|
||||||
|
"useGitignore": true,
|
||||||
|
"useDefaultPatterns": true,
|
||||||
|
"customPatterns": [
|
||||||
|
"state/snapshots/**",
|
||||||
|
"*.xml"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
16
repomix.state.json
Normal file
16
repomix.state.json
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
{
|
||||||
|
"output": {
|
||||||
|
"filePath": "zarya-state.xml",
|
||||||
|
"style": "xml",
|
||||||
|
"removeComments": false,
|
||||||
|
"removeEmptyLines": true,
|
||||||
|
"copyToClipboard": true
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"state/snapshots/**"
|
||||||
|
],
|
||||||
|
"ignore": {
|
||||||
|
"useGitignore": false,
|
||||||
|
"useDefaultPatterns": true
|
||||||
|
}
|
||||||
|
}
|
||||||
80
state/00_workstation.sh
Executable file
80
state/00_workstation.sh
Executable file
|
|
@ -0,0 +1,80 @@
|
||||||
|
#!/bin/bash
|
||||||
|
set -uo pipefail
|
||||||
|
|
||||||
|
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||||
|
|
||||||
|
echo "## 0. Рабочая станция администратора (Operator Environment)"
|
||||||
|
|
||||||
|
echo "### Инфраструктурные SSH-хосты (~/.ssh/config):"
|
||||||
|
echo '```text'
|
||||||
|
if [[ -f ~/.ssh/config ]]; then
|
||||||
|
awk '/^Host (router|proxmox|vyos|10\.|fin21|ded|incserv)/, /^$/' ~/.ssh/config
|
||||||
|
else
|
||||||
|
echo "Файл ~/.ssh/config не найден"
|
||||||
|
fi
|
||||||
|
echo '```'
|
||||||
|
|
||||||
|
echo "### Рабочие алиасы и функции оператора (Active Workflow Aliases):"
|
||||||
|
echo '```bash'
|
||||||
|
if [[ -f ~/.bashrc ]] && grep -q '# >>> ATLAS C2 WORKFLOW >>>' ~/.bashrc; then
|
||||||
|
sed -n '/# >>> ATLAS C2 WORKFLOW >>>/,/# <<< ATLAS C2 WORKFLOW <<</p' ~/.bashrc | grep -v '>>>'
|
||||||
|
else
|
||||||
|
echo "# Маркер # >>> ATLAS C2 WORKFLOW >>> не найден в ~/.bashrc"
|
||||||
|
fi
|
||||||
|
echo '```'
|
||||||
|
|
||||||
|
echo "### Статус RAM-кэша секретов (/dev/shm/.atlas_secrets_cache):"
|
||||||
|
echo '```text'
|
||||||
|
if [[ -f /dev/shm/.atlas_secrets_cache ]]; then
|
||||||
|
stat -c "Размер: %s байт | Права: %a | Создан: %y" /dev/shm/.atlas_secrets_cache
|
||||||
|
else
|
||||||
|
echo "Кэш секретов в RAM отсутствует"
|
||||||
|
fi
|
||||||
|
echo '```'
|
||||||
|
|
||||||
|
echo "### Проверка Git Credential Helper (git.svo88.space):"
|
||||||
|
echo '```text'
|
||||||
|
printf "protocol=https\nhost=git.svo88.space\n" | git credential fill 2>&1 | awk -F= '/^username/ {print "Username:", $2} /^password/ {print "Password: [PRESENT in RAM]"}' || echo "Credential helper не вернул учетных данных"
|
||||||
|
echo '```'
|
||||||
|
|
||||||
|
echo "### Ядро, память, ZRAM и параметры Coredump:"
|
||||||
|
echo '```text'
|
||||||
|
echo -n "Kernel: "; uname -r
|
||||||
|
echo -n "Swapon: "; swapon --show 2>&1 || echo "swapon недоступен"
|
||||||
|
zramctl 2>&1 || echo "zramctl недоступен"
|
||||||
|
echo "Coredump config:"
|
||||||
|
cat /etc/systemd/coredump.conf.d/*.conf 2>/dev/null || grep -E "Storage|ProcessSizeMax" /etc/systemd/coredump.conf 2>/dev/null || echo "Дефолтная конфигурация coredump"
|
||||||
|
echo '```'
|
||||||
|
|
||||||
|
echo "### Токены безопасности и ключи в ssh-agent:"
|
||||||
|
echo '```text'
|
||||||
|
echo "Active SSH Agent keys:"
|
||||||
|
ssh-add -l 2>&1 || true
|
||||||
|
echo "YubiKey / FIDO2 Status:"
|
||||||
|
ykman info 2>/dev/null || echo "Токен YubiKey не обнаружен"
|
||||||
|
echo '```'
|
||||||
|
|
||||||
|
echo "### Зеркала пакетов Pacman (Mirrorlist):"
|
||||||
|
echo '```text'
|
||||||
|
grep -E "^Server|^\[" /etc/pacman.conf /etc/pacman.d/* 2>/dev/null | head -n 15 || echo "Нет данных о зеркалах"
|
||||||
|
echo '```'
|
||||||
|
|
||||||
|
echo "### Маршрутизация на хосте (Routing to C2):"
|
||||||
|
echo '```text'
|
||||||
|
ip route show | grep -E "10.0.|192.168." || echo "Маршруты к ЦОД не обнаружены"
|
||||||
|
echo '```'
|
||||||
|
|
||||||
|
echo "### Версии локальных инструментов:"
|
||||||
|
echo '```text'
|
||||||
|
echo -n "OpenTofu: "; tofu version 2>/dev/null | head -n1 || echo "not installed"
|
||||||
|
echo -n "Kubectl: "; kubectl version --client -o yaml 2>/dev/null | grep gitVersion | head -n1 || echo "not installed"
|
||||||
|
echo -n "Talosctl: "; talosctl version --client --short 2>/dev/null || echo "not installed"
|
||||||
|
echo '```'
|
||||||
|
|
||||||
|
echo "### Состояние локального Git-репозитория (SSOT Source):"
|
||||||
|
echo '```text'
|
||||||
|
git -C "${REPO_ROOT}" status -sb
|
||||||
|
echo "Последний коммит: $(git -C "${REPO_ROOT}" log -1 --format='%h - %s (%cr)')"
|
||||||
|
echo "Remote URL: $(git -C "${REPO_ROOT}" remote -v 2>/dev/null | head -n1 | awk '{print $1, $2}' || echo 'no remote')"
|
||||||
|
echo '```'
|
||||||
|
echo "---"
|
||||||
53
state/01_pve.sh
Executable file
53
state/01_pve.sh
Executable file
|
|
@ -0,0 +1,53 @@
|
||||||
|
#!/bin/bash
|
||||||
|
set -uo pipefail
|
||||||
|
SSH_CMD="ssh -n -o BatchMode=yes -o ConnectTimeout=3 proxmox"
|
||||||
|
echo "## 1. Гипервизор Proxmox VE (192.168.1.177)"
|
||||||
|
echo "### Память хоста, Uptime и Температура CPU:"
|
||||||
|
echo '```text'
|
||||||
|
timeout 4s ${SSH_CMD} "uptime; echo ''; free -h; echo ''; sensors 2>/dev/null | grep -E 'Tctl|Package|temp1' || cat /sys/class/thermal/thermal_zone*/temp 2>/dev/null | awk '{print \"CPU Temp: \" \$1/1000 \"°C\"}'" 2>&1 || echo "PVE недоступен"
|
||||||
|
echo '```'
|
||||||
|
echo "### Здоровье физических дисков (SMART):"
|
||||||
|
echo '```text'
|
||||||
|
timeout 5s ${SSH_CMD} "for d in sda sdb sdc sdd sde sdf; do echo -n \"/dev/\$d: \"; smartctl -H /dev/\$d 2>/dev/null | grep -E 'result|PASSED|FAILED' || echo 'N/A'; done" 2>&1 || echo "SMART недоступен"
|
||||||
|
echo '```'
|
||||||
|
echo "### Статус шифрования ZFS (Encryption & Keys):"
|
||||||
|
echo '```text'
|
||||||
|
timeout 4s ${SSH_CMD} "zfs get encryption,keystatus,keyformat,keylocation astra terra 2>/dev/null" 2>&1 || echo "Ошибка сбора шифрования ZFS"
|
||||||
|
echo '```'
|
||||||
|
echo "### Здоровье и емкость ZFS-пулов:"
|
||||||
|
echo '```text'
|
||||||
|
timeout 4s ${SSH_CMD} "zpool list -o name,size,alloc,free,frag,cap,health; echo ''; zfs list -o name,used,avail,refer,mountpoint -r astra terra" 2>&1 || echo "Ошибка сбора ZFS"
|
||||||
|
echo '```'
|
||||||
|
echo "### Физическая емкость каталогов на /terra:"
|
||||||
|
echo '```text'
|
||||||
|
timeout 5s ${SSH_CMD} "du -sh /terra/* 2>/dev/null" 2>&1 || echo "Ошибка чтения /terra"
|
||||||
|
echo '```'
|
||||||
|
echo "### Блочные устройства хоста и тома ZVOL (lsblk):"
|
||||||
|
echo '```text'
|
||||||
|
timeout 4s ${SSH_CMD} "lsblk -o NAME,FSTYPE,SIZE,MOUNTPOINTS" 2>&1 || echo "Ошибка сбора lsblk"
|
||||||
|
echo '```'
|
||||||
|
echo "### Виртуальные диски K8s и ВМ (ZVOLs на astra):"
|
||||||
|
echo '```text'
|
||||||
|
timeout 4s ${SSH_CMD} "zfs list -t volume -o name,volsize,used,referenced" 2>&1 || echo "Ошибка чтения ZVOL"
|
||||||
|
echo '```'
|
||||||
|
echo "### Модули ядра (nfsd, zfs):"
|
||||||
|
echo '```text'
|
||||||
|
timeout 4s ${SSH_CMD} "lsmod | grep -E '^(nfsd|zfs) '" 2>&1 || echo "Модули не загружены"
|
||||||
|
echo '```'
|
||||||
|
echo "### Теги сетевых карт ВМ 100 (VyOS) в Proxmox:"
|
||||||
|
echo '```text'
|
||||||
|
timeout 4s ${SSH_CMD} "qm config 100 2>/dev/null | grep -E '^net[0-9]'" 2>&1 || echo "Ошибка чтения qm config 100"
|
||||||
|
echo '```'
|
||||||
|
echo "### Точки монтирования LXC-контейнеров (/etc/pve/lxc/*.conf):"
|
||||||
|
echo '```text'
|
||||||
|
timeout 4s ${SSH_CMD} "grep -H -E '^mp[0-9]:' /etc/pve/lxc/*.conf 2>/dev/null" 2>&1 || echo "Ошибка чтения lxc conf"
|
||||||
|
echo '```'
|
||||||
|
echo "### Флаги автостарта ВМ и Контейнеров (onboot):"
|
||||||
|
echo '```text'
|
||||||
|
timeout 4s ${SSH_CMD} "grep -H -E '^onboot:' /etc/pve/qemu-server/*.conf /etc/pve/lxc/*.conf 2>/dev/null" 2>&1 || echo "Ошибка чтения onboot"
|
||||||
|
echo '```'
|
||||||
|
echo "### Виртуальные машины (QEMU) и Контейнеры (LXC):"
|
||||||
|
echo '```text'
|
||||||
|
timeout 4s ${SSH_CMD} "qm list; echo ''; pct list" 2>&1 || echo "Ошибка получения списка ВМ/LXC"
|
||||||
|
echo '```'
|
||||||
|
echo "---"
|
||||||
25
state/02_vps.sh
Executable file
25
state/02_vps.sh
Executable file
|
|
@ -0,0 +1,25 @@
|
||||||
|
#!/bin/bash
|
||||||
|
set -uo pipefail
|
||||||
|
SSH_CMD="ssh -n -o BatchMode=yes -o ConnectTimeout=4 fin21"
|
||||||
|
echo "## 2. Периметр: Белый VPS (Host: fin21, IP: 153.76.194.24)"
|
||||||
|
echo "### WireGuard wg0 на VPS:"
|
||||||
|
echo '```text'
|
||||||
|
timeout 4s ${SSH_CMD} "sudo wg show wg0" 2>&1 || echo "VPS или WireGuard недоступен"
|
||||||
|
echo '```'
|
||||||
|
echo "### Статус системных сервисов (Edge Stack):"
|
||||||
|
echo '```text'
|
||||||
|
timeout 4s ${SSH_CMD} "sudo systemctl is-active wg-quick@wg0 haproxy xray nginx | paste -d' ' -s - | awk '{print \"WireGuard:\", \$1, \"| HAProxy:\", \$2, \"| Xray:\", \$3, \"| Nginx:\", \$4}'" 2>&1 || echo "Ошибка проверки systemd"
|
||||||
|
echo '```'
|
||||||
|
echo "### Прослушиваемые сокеты (Ports 80, 443, 4443, 8443, 51820):"
|
||||||
|
echo '```text'
|
||||||
|
timeout 4s ${SSH_CMD} "sudo ss -tulpn | grep -E '80|443|4443|8443|51820'" 2>&1 || echo "Ошибка ss"
|
||||||
|
echo '```'
|
||||||
|
echo "### Тест аварийной заглушки Nginx (127.0.0.1:8443):"
|
||||||
|
echo '```text'
|
||||||
|
timeout 3s ${SSH_CMD} "curl -k -s https://127.0.0.1:8443" 2>&1 || echo "Nginx fallback не отвечает"
|
||||||
|
echo '```'
|
||||||
|
echo "### Конфигурация SSH-демона (sshd runtime):"
|
||||||
|
echo '```text'
|
||||||
|
timeout 4s ${SSH_CMD} "sudo sshd -T | grep -E 'passwordauthentication|permitrootlogin'" 2>&1 || echo "Ошибка проверки sshd"
|
||||||
|
echo '```'
|
||||||
|
echo "---"
|
||||||
57
state/03_vyos.sh
Executable file
57
state/03_vyos.sh
Executable file
|
|
@ -0,0 +1,57 @@
|
||||||
|
#!/bin/bash
|
||||||
|
set -uo pipefail
|
||||||
|
|
||||||
|
SSH_CMD="ssh -n -o BatchMode=yes -o ConnectTimeout=3 vyos"
|
||||||
|
|
||||||
|
echo "## 3. Маршрутизатор VyOS (192.168.1.2)"
|
||||||
|
|
||||||
|
echo "### Сетевые интерфейсы и L2-транк (VLANs 10, 20, 30):"
|
||||||
|
echo '```text'
|
||||||
|
timeout 4s ${SSH_CMD} "/opt/vyatta/bin/vyatta-op-cmd-wrapper show interfaces" 2>&1 || echo "Ошибка получения интерфейсов"
|
||||||
|
echo '```'
|
||||||
|
|
||||||
|
echo "### ARP-таблица K8s-интерфейса eth1 (проверка связности с нодами):"
|
||||||
|
echo '```text'
|
||||||
|
timeout 4s ${SSH_CMD} "ip neigh show dev eth1 2>/dev/null" 2>&1 || echo "Ошибка чтения ARP"
|
||||||
|
echo '```'
|
||||||
|
|
||||||
|
echo "### DHCP-сервер и выданные лизы (Options 3, 6):"
|
||||||
|
echo '```text'
|
||||||
|
timeout 4s ${SSH_CMD} "/opt/vyatta/bin/vyatta-op-cmd-wrapper show dhcp server leases" 2>&1 || echo "Ошибка получения DHCP лизов"
|
||||||
|
echo '```'
|
||||||
|
|
||||||
|
echo "### Перехват DNS через DNAT (Rule 53 -> 127.0.0.42:53):"
|
||||||
|
echo '```text'
|
||||||
|
timeout 4s ${SSH_CMD} "/opt/vyatta/bin/vyatta-op-cmd-wrapper show nat destination rules" 2>&1 || echo "Ошибка получения NAT правил"
|
||||||
|
echo '```'
|
||||||
|
|
||||||
|
echo "### Контейнеры маршрутизатора (Sing-Box & Zapret2):"
|
||||||
|
echo '```text'
|
||||||
|
timeout 4s ${SSH_CMD} "/opt/vyatta/bin/vyatta-op-cmd-wrapper show container" 2>&1 || echo "Ошибка получения контейнеров"
|
||||||
|
echo '```'
|
||||||
|
|
||||||
|
echo "### Наличие /dev/net/tun внутри контейнера sing-box:"
|
||||||
|
echo '```text'
|
||||||
|
timeout 4s ${SSH_CMD} "sudo podman exec sing-box ls -l /dev/net/tun 2>/dev/null" 2>&1 || echo "Контейнер sing-box не запущен или tun отсутствует"
|
||||||
|
echo '```'
|
||||||
|
|
||||||
|
echo "### Задержка VLESS-канала через Clash API (Main-1-out -> fin21):"
|
||||||
|
echo '```text'
|
||||||
|
timeout 4s ${SSH_CMD} "curl -s http://127.0.0.1:9090/proxies/Main-1-out 2>/dev/null" 2>&1 || echo "Clash API недоступен"
|
||||||
|
echo '```'
|
||||||
|
|
||||||
|
echo "### Статус таймера автосинхронизации Git (singbox-sync):"
|
||||||
|
echo '```text'
|
||||||
|
timeout 4s ${SSH_CMD} "sudo systemctl status singbox-sync.timer --no-pager; echo ''; sudo journalctl -u singbox-sync.service -n 5 --no-pager" 2>&1 || echo "Ошибка проверки таймера"
|
||||||
|
echo '```'
|
||||||
|
|
||||||
|
echo "### Статус WireGuard туннеля wg0 и пинг до VPS:"
|
||||||
|
echo '```text'
|
||||||
|
timeout 4s ${SSH_CMD} "sudo wg show wg0; echo ''; /opt/vyatta/bin/vyatta-op-cmd-wrapper ping 10.8.0.1 count 2" 2>&1 || echo "VyOS или туннель недоступен"
|
||||||
|
echo '```'
|
||||||
|
|
||||||
|
echo "### Таблица маршрутизации (show ip route):"
|
||||||
|
echo '```text'
|
||||||
|
timeout 4s ${SSH_CMD} "/opt/vyatta/bin/vyatta-op-cmd-wrapper show ip route" 2>&1 || echo "Ошибка получения маршрутов"
|
||||||
|
echo '```'
|
||||||
|
echo "---"
|
||||||
32
state/04_tier0.sh
Executable file
32
state/04_tier0.sh
Executable file
|
|
@ -0,0 +1,32 @@
|
||||||
|
#!/bin/bash
|
||||||
|
set -uo pipefail
|
||||||
|
|
||||||
|
SSH_VYOS="ssh -n -o BatchMode=yes -o ConnectTimeout=3 vyos"
|
||||||
|
SSH_STORAGE="ssh -n -o BatchMode=yes -o ConnectTimeout=3 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -J vyos root@10.0.10.14"
|
||||||
|
|
||||||
|
echo "## 4. Сервисы Tier-0 (Air-Gap фундамент)"
|
||||||
|
|
||||||
|
echo "### Technitium DNS (10.0.10.11:53):"
|
||||||
|
echo "* **Внутренний домен (*.svo88.space -> 10.0.30.200):** $(timeout 3s ${SSH_VYOS} "dig @10.0.10.11 vw.svo88.space +short" 2>&1 | tr '\n' ' ')"
|
||||||
|
echo "* **Внешний резолв (google.com):** $(timeout 3s ${SSH_VYOS} "dig @10.0.10.11 google.com +short" 2>&1 | head -n1)"
|
||||||
|
|
||||||
|
echo "### OpenBao Vault (10.0.10.12:8200):"
|
||||||
|
echo '```json'
|
||||||
|
timeout 3s ${SSH_VYOS} "curl -s http://10.0.10.12:8200/v1/sys/seal-status" 2>&1 || echo '{"error": "Vault недоступен"}'
|
||||||
|
echo '```'
|
||||||
|
|
||||||
|
echo "### Zot OCI Registry (10.0.10.15:5000):"
|
||||||
|
echo '```json'
|
||||||
|
timeout 3s ${SSH_VYOS} "curl -s http://10.0.10.15:5000/v2/_catalog" 2>&1 || echo '{"error": "Zot недоступен"}'
|
||||||
|
echo '```'
|
||||||
|
|
||||||
|
echo "### Forgejo GitOps Server (10.0.10.13:3000):"
|
||||||
|
echo '```json'
|
||||||
|
timeout 3s ${SSH_VYOS} "curl -s http://10.0.10.13:3000/api/v1/version" 2>&1 || echo '{"error": "Forgejo недоступен"}'
|
||||||
|
echo '```'
|
||||||
|
|
||||||
|
echo "### Ядерный NFS nfsd (storage0 - 10.0.10.14):"
|
||||||
|
echo '```text'
|
||||||
|
timeout 4s ${SSH_STORAGE} "echo '== Активные экспорты ядра (exportfs):'; exportfs -v; echo ''; echo '== Емкость смонтированных ZFS-датасетов:'; df -h /media /torrents /backups /models; echo ''; echo '== Права точки монтирования OpenCloud:'; ls -ld /backups/opencloud-users" 2>&1 || echo "NFS (storage0) недоступен"
|
||||||
|
echo '```'
|
||||||
|
echo "---"
|
||||||
77
state/05_k8s.sh
Executable file
77
state/05_k8s.sh
Executable file
|
|
@ -0,0 +1,77 @@
|
||||||
|
#!/bin/bash
|
||||||
|
set -uo pipefail
|
||||||
|
|
||||||
|
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||||
|
TALOSCONFIG="${REPO_ROOT}/talos/clusterconfig/talosconfig"
|
||||||
|
KUBECONFIG_PATH="${HOME}/.kube/config"
|
||||||
|
|
||||||
|
echo "## 5. Kubernetes Платформа & Нагрузки"
|
||||||
|
|
||||||
|
echo "### Участники кластера Talos (OS-level):"
|
||||||
|
echo '```text'
|
||||||
|
timeout 5s talosctl --talosconfig="${TALOSCONFIG}" -n 10.0.20.10 -e 10.0.20.10 get members 2>&1 || echo "Talos API недоступен"
|
||||||
|
echo '```'
|
||||||
|
|
||||||
|
echo "### Ноды кластера K8s:"
|
||||||
|
echo '```text'
|
||||||
|
timeout 5s kubectl --kubeconfig="${KUBECONFIG_PATH}" get nodes -o wide 2>&1 || echo "K8s API недоступен"
|
||||||
|
echo '```'
|
||||||
|
|
||||||
|
echo "### Топологические лейблы нод (Proxmox-CSI topology):"
|
||||||
|
echo '```text'
|
||||||
|
timeout 5s kubectl --kubeconfig="${KUBECONFIG_PATH}" get nodes -L topology.kubernetes.io/zone,topology.kubernetes.io/region 2>&1 || echo "Не удалось получить лейблы нод"
|
||||||
|
echo '```'
|
||||||
|
|
||||||
|
echo "### Статус ExternalSecrets (Синхронизация из Vault):"
|
||||||
|
echo '```text'
|
||||||
|
timeout 5s kubectl --kubeconfig="${KUBECONFIG_PATH}" get clustersecretstores,externalsecrets -A 2>&1 || echo "Не удалось получить ExternalSecrets"
|
||||||
|
echo '```'
|
||||||
|
|
||||||
|
echo "### Сетевой ingress, сертификаты TLS и L2 Fast-Path VIP:"
|
||||||
|
echo '```text'
|
||||||
|
timeout 5s kubectl --kubeconfig="${KUBECONFIG_PATH}" get certificates,ingresses -A 2>&1 || echo "Не удалось получить Ingress/TLS"
|
||||||
|
echo ''
|
||||||
|
timeout 5s kubectl --kubeconfig="${KUBECONFIG_PATH}" -n kube-system get ciliuml2announcementpolicies,ciliumloadbalancerippools 2>&1 || echo "Не удалось получить Cilium L2"
|
||||||
|
echo ''
|
||||||
|
timeout 3s curl -k -s -o /dev/null -w "Traefik VIP (10.0.30.200:443) HTTP Status: %{http_code}\n" https://10.0.30.200 2>&1 || echo "Traefik VIP недоступен"
|
||||||
|
echo '```'
|
||||||
|
|
||||||
|
echo "### Диски и тома (PVC / PV):"
|
||||||
|
echo '```text'
|
||||||
|
timeout 5s kubectl --kubeconfig="${KUBECONFIG_PATH}" get pvc,pv -A 2>&1 || echo "Не удалось получить PVC"
|
||||||
|
echo '```'
|
||||||
|
|
||||||
|
echo "### Статус DaemonSet Proxmox-CSI Node:"
|
||||||
|
echo '```text'
|
||||||
|
timeout 5s kubectl --kubeconfig="${KUBECONFIG_PATH}" -n kube-system get daemonset proxmox-csi-plugin-node 2>&1 || echo "Не удалось получить DaemonSet CSI"
|
||||||
|
echo '```'
|
||||||
|
|
||||||
|
echo "### Статус пространства мониторинга (VictoriaMetrics & Grafana):"
|
||||||
|
echo '```text'
|
||||||
|
timeout 5s kubectl --kubeconfig="${KUBECONFIG_PATH}" -n monitoring get statefulsets,pods,pvc 2>&1 || echo "Не удалось получить мониторинг"
|
||||||
|
echo '```'
|
||||||
|
|
||||||
|
echo "### Логи ошибок Proxmox CSI Plugin (последние строки):"
|
||||||
|
echo '```text'
|
||||||
|
timeout 5s kubectl --kubeconfig="${KUBECONFIG_PATH}" -n kube-system logs -l app.kubernetes.io/name=proxmox-csi-plugin --tail=15 --prefix=true 2>&1 || echo "Нет логов CSI"
|
||||||
|
echo '```'
|
||||||
|
|
||||||
|
echo "### Синхронизация GitOps (FluxCD):"
|
||||||
|
echo '```text'
|
||||||
|
timeout 5s kubectl --kubeconfig="${KUBECONFIG_PATH}" get gitrepositories,kustomizations,helmreleases -A 2>&1 || echo "Flux не установлен или недоступен"
|
||||||
|
echo '```'
|
||||||
|
|
||||||
|
echo "### Проблемные поды (Non-Running):"
|
||||||
|
echo '```text'
|
||||||
|
timeout 5s kubectl --kubeconfig="${KUBECONFIG_PATH}" get pods -A --field-selector=status.phase!=Running 2>&1 || echo "Все поды в статусе Running"
|
||||||
|
echo '```'
|
||||||
|
|
||||||
|
echo "### Последние события-предупреждения (Warning Events):"
|
||||||
|
echo '```text'
|
||||||
|
timeout 5s kubectl --kubeconfig="${KUBECONFIG_PATH}" get events -A --field-selector type=Warning --sort-by='.lastTimestamp' 2>&1 | tail -n 15 || echo "Нет предупреждений"
|
||||||
|
echo '```'
|
||||||
|
|
||||||
|
echo "### Реальный объем данных OpenCloud (/var/lib/opencloud-users):"
|
||||||
|
echo '```text'
|
||||||
|
timeout 10s kubectl --kubeconfig="${KUBECONFIG_PATH}" -n storage exec statefulset/opencloud -c opencloud -- du -sh /var/lib/opencloud-users 2>&1 || echo "Не удалось получить размер данных OpenCloud"
|
||||||
|
echo '```'
|
||||||
Loading…
Reference in a new issue