+ perimeter
This commit is contained in:
parent
547a895837
commit
0a5fb528f9
|
|
@ -27,6 +27,7 @@ case "${SUBCMD}" in
|
||||||
reset|nuke) exec "${ZARYA_DIR}/layers/reset.sh" "$@" ;;
|
reset|nuke) exec "${ZARYA_DIR}/layers/reset.sh" "$@" ;;
|
||||||
gc) exec "${ZARYA_DIR}/layers/gc.sh" "$@" ;;
|
gc) exec "${ZARYA_DIR}/layers/gc.sh" "$@" ;;
|
||||||
init) exec "${ZARYA_DIR}/cmd/init.sh" "$@" ;;
|
init) exec "${ZARYA_DIR}/cmd/init.sh" "$@" ;;
|
||||||
|
secret|secrets) exec "${ZARYA_DIR}/cmd/secret.sh" "$@" ;;
|
||||||
help|--help|-h)
|
help|--help|-h)
|
||||||
cat <<EOF
|
cat <<EOF
|
||||||
ZARYA Operator Interface (z)
|
ZARYA Operator Interface (z)
|
||||||
|
|
@ -44,6 +45,7 @@ GitOps и оркестрация:
|
||||||
z login [TTL] Открытие смены (YubiKey + RAM)
|
z login [TTL] Открытие смены (YubiKey + RAM)
|
||||||
z logout Мгновенная очистка RAM
|
z logout Мгновенная очистка RAM
|
||||||
z unseal Распечатывание OpenBao Vault
|
z unseal Распечатывание OpenBao Vault
|
||||||
|
z secret perimeter <node> [cmd] Управление секретами периметра (ensure|show|rotate)
|
||||||
Деплой и IaC:
|
Деплой и IaC:
|
||||||
z up [layer] Макро-слои (host, net, tier0, talos, apps, all)
|
z up [layer] Макро-слои (host, net, tier0, talos, apps, all)
|
||||||
z run <selector> Плейбуки Ansible (01, 02)
|
z run <selector> Плейбуки Ansible (01, 02)
|
||||||
|
|
|
||||||
21
cmd/secret.sh
Executable file
21
cmd/secret.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"
|
||||||
|
SUBCMD="${1:-}"
|
||||||
|
shift || true
|
||||||
|
case "${SUBCMD}" in
|
||||||
|
perimeter|srv)
|
||||||
|
NODE="${1:-}"
|
||||||
|
ACTION="${2:-ensure}"
|
||||||
|
if [[ -z "${NODE}" ]]; then
|
||||||
|
log_err "Использование: z secret perimeter <нода> [ensure|show|rotate]"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
run_isolated python3 "${ZARYA_DIR}/lib/perimeter_secrets.py" "${ACTION}" "${NODE}"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
log_err "Использование: z secret perimeter <нода> [ensure|show|rotate]"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
104
lib/perimeter_secrets.py
Normal file
104
lib/perimeter_secrets.py
Normal file
|
|
@ -0,0 +1,104 @@
|
||||||
|
import base64
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import secrets
|
||||||
|
import sys
|
||||||
|
import urllib.error
|
||||||
|
import urllib.request
|
||||||
|
import uuid
|
||||||
|
from cryptography.hazmat.primitives.asymmetric import x25519
|
||||||
|
|
||||||
|
action = sys.argv[1] if len(sys.argv) > 1 else "ensure"
|
||||||
|
node = sys.argv[2] if len(sys.argv) > 2 else ""
|
||||||
|
|
||||||
|
if not node:
|
||||||
|
sys.stderr.write("Node name required\n")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
vault_addr = os.environ.get("VAULT_ADDR", "http://10.0.10.12:8200").rstrip("/")
|
||||||
|
vault_token = os.environ.get("VAULT_TOKEN", "")
|
||||||
|
|
||||||
|
if not vault_token:
|
||||||
|
sys.stderr.write("VAULT_TOKEN not found in environment\n")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
endpoint = f"{vault_addr}/v1/secret/data/perimeter/{node}"
|
||||||
|
|
||||||
|
existing_data = None
|
||||||
|
req_get = urllib.request.Request(endpoint, headers={"X-Vault-Token": vault_token})
|
||||||
|
try:
|
||||||
|
with urllib.request.urlopen(req_get) as resp:
|
||||||
|
body = json.loads(resp.read().decode())
|
||||||
|
existing_data = body.get("data", {}).get("data", {})
|
||||||
|
except urllib.error.HTTPError as e:
|
||||||
|
if e.code != 404:
|
||||||
|
sys.stderr.write(f"Vault error: {e.code} {e.reason}\n")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
if action == "show":
|
||||||
|
if not existing_data:
|
||||||
|
sys.stderr.write(f"No secrets found for {node}\n")
|
||||||
|
sys.exit(1)
|
||||||
|
public_view = {
|
||||||
|
"vless_uuid": existing_data.get("vless_uuid"),
|
||||||
|
"reality_public_key": existing_data.get("reality_public_key"),
|
||||||
|
"reality_short_id": existing_data.get("reality_short_id"),
|
||||||
|
"reality_server_name": existing_data.get("reality_server_name"),
|
||||||
|
"awg_server_public_key": existing_data.get("awg_server_public_key"),
|
||||||
|
"awg_router_public_key": existing_data.get("awg_router_public_key"),
|
||||||
|
"awg_h1": existing_data.get("awg_h1"),
|
||||||
|
"awg_h2": existing_data.get("awg_h2"),
|
||||||
|
"awg_h3": existing_data.get("awg_h3"),
|
||||||
|
"awg_h4": existing_data.get("awg_h4")
|
||||||
|
}
|
||||||
|
sys.stdout.write(json.dumps(public_view, indent=2) + "\n")
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
if action == "ensure" and existing_data:
|
||||||
|
sys.stdout.write(f"Secrets for {node} already exist in Vault (idempotent, skipped)\n")
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
v_srv = x25519.X25519PrivateKey.generate()
|
||||||
|
v_priv = base64.urlsafe_b64encode(v_srv.private_bytes_raw()).decode().rstrip("=")
|
||||||
|
v_pub = base64.urlsafe_b64encode(v_srv.public_key().public_bytes_raw()).decode().rstrip("=")
|
||||||
|
|
||||||
|
awg_srv = x25519.X25519PrivateKey.generate()
|
||||||
|
awg_s_priv = base64.b64encode(awg_srv.private_bytes_raw()).decode()
|
||||||
|
awg_s_pub = base64.b64encode(awg_srv.public_key().public_bytes_raw()).decode()
|
||||||
|
|
||||||
|
awg_rtr = x25519.X25519PrivateKey.generate()
|
||||||
|
awg_r_priv = base64.b64encode(awg_rtr.private_bytes_raw()).decode()
|
||||||
|
awg_r_pub = base64.b64encode(awg_rtr.public_key().public_bytes_raw()).decode()
|
||||||
|
|
||||||
|
payload = {
|
||||||
|
"data": {
|
||||||
|
"vless_uuid": str(uuid.uuid4()),
|
||||||
|
"reality_private_key": v_priv,
|
||||||
|
"reality_public_key": v_pub,
|
||||||
|
"reality_short_id": secrets.token_hex(4),
|
||||||
|
"reality_server_name": "ng.w.svo88.space",
|
||||||
|
"awg_server_private_key": awg_s_priv,
|
||||||
|
"awg_server_public_key": awg_s_pub,
|
||||||
|
"awg_router_private_key": awg_r_priv,
|
||||||
|
"awg_router_public_key": awg_r_pub,
|
||||||
|
"awg_jc": 4,
|
||||||
|
"awg_jmin": 40,
|
||||||
|
"awg_jmax": 70,
|
||||||
|
"awg_s1": 15,
|
||||||
|
"awg_s2": 25,
|
||||||
|
"awg_h1": secrets.randbelow(4000000000) + 100000000,
|
||||||
|
"awg_h2": secrets.randbelow(4000000000) + 100000000,
|
||||||
|
"awg_h3": secrets.randbelow(4000000000) + 100000000,
|
||||||
|
"awg_h4": secrets.randbelow(4000000000) + 100000000
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
req_post = urllib.request.Request(
|
||||||
|
endpoint,
|
||||||
|
data=json.dumps(payload).encode(),
|
||||||
|
headers={"X-Vault-Token": vault_token, "Content-Type": "application/json"}
|
||||||
|
)
|
||||||
|
|
||||||
|
with urllib.request.urlopen(req_post) as resp:
|
||||||
|
if resp.status in (200, 204):
|
||||||
|
sys.stdout.write(f"Successfully initialized and stored secrets for {node} in Vault\n")
|
||||||
Loading…
Reference in a new issue