This commit is contained in:
ochenstarik-ui 2026-07-16 16:35:34 +00:00 committed by GitHub
commit 8d6327dee8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 1746 additions and 0 deletions

View file

@ -46,3 +46,18 @@ jobs:
- name: Test step 8 AI agent catalog
run: bash tests/test-step8-agent-catalog.sh
- name: Test Server Monitor Manager enrollment
run: bash tests/test-monitor-manager-enrollment.sh
- name: Test Server Monitor Manager Link policies
run: bash tests/test-monitor-manager-links.sh
- name: Test Server Monitor Manager lifecycle
run: bash tests/test-monitor-manager-lifecycle.sh
- name: Test Server Monitor Manager extended metrics
run: bash tests/test-monitor-manager-metrics.sh
- name: Test Server Monitor Manager control layer installer
run: bash tests/test-monitor-manager-control-layer.sh

View file

@ -51,6 +51,7 @@ The wizard intentionally requires all module scripts to be present locally. Use
| `ochenstarik-server-panel-warp-6.sh` | 3x-ui panel and local Cloudflare WARP proxy | Optional |
| `ochenstarik-server-backup-7.sh` | Protected initial snapshot and selected daily, weekly, or monthly schedules | Optional |
| `ochenstarik-server-ai-agents-8.sh` | Installs selected AI agents: Hermes, OpenClaw, OpenHands, OpenCode, Aider, AutoGPT, or Pi Coding Agent | Optional |
| `ochenstarik-server-monitor-manager.sh` | Installs the Server Monitor Manager SSH endpoint, public WireGuard Hub, or outbound-only Node | Optional; Hub needs a public UDP endpoint |
| `ochenstarik-server-uninstall.sh` | Removes project-managed settings so installation can start again | Use carefully |
Every module can be run independently from the full archive or cloned repository:
@ -63,6 +64,68 @@ sudo ./SCRIPT_NAME.sh
Replace `SCRIPT_NAME.sh` with the required filename from the table.
## Server Monitor Manager Hub and Nodes
The Hub needs a public IPv4 address or domain and an open UDP port (default `51820`). Secondary Nodes establish outbound WireGuard connections and do not need a public IP.
```bash
curl -fLO https://raw.githubusercontent.com/ochenstarik-ui/lightweight-server/main/ochenstarik-server-monitor-manager.sh
chmod 700 ochenstarik-server-monitor-manager.sh
bash -n ochenstarik-server-monitor-manager.sh
sudo ./ochenstarik-server-monitor-manager.sh hub
```
Create a separate 10-minute enrollment code for each Node on the Hub, then run the same installer with `node` on the matching server:
```bash
sudo ochenstarik-smm node-code ai-agent
sudo ochenstarik-smm node-code home
sudo ./ochenstarik-server-monitor-manager.sh node
```
Links are directional. The reverse direction requires its own rule:
```bash
sudo ochenstarik-smm link-connect ai-agent home tcp 22 120
sudo ochenstarik-smm link-disconnect ai-agent home tcp 22
sudo ochenstarik-smm nodes
sudo ochenstarik-smm links
```
The Node installer creates its private WireGuard key locally and prints an `SMMREQ1` request. In a second terminal run `sudo ochenstarik-smm node-enroll` on the Hub, paste the request at the hidden prompt, and return the resulting `SMMACK1` code to the Node installer. The enrollment code expires after 10 minutes and is consumed by the first successful registration.
Role-aware maintenance commands create a root-only backup before destructive changes:
```bash
sudo ./ochenstarik-server-monitor-manager.sh update
sudo ./ochenstarik-server-monitor-manager.sh rollback
sudo ./ochenstarik-server-monitor-manager.sh uninstall-node
sudo ./ochenstarik-server-monitor-manager.sh uninstall-hub
sudo ./ochenstarik-server-monitor-manager.sh uninstall-monitor
```
### Experimental persistent control layer
The first persistent Hub/Agent layer is available as an alpha for three-server testing. It uses self-contained single-file binaries, SQLite on the Hub, one-time enrollment codes, and outbound mTLS heartbeats from Nodes. The existing SSH/WireGuard layer remains installed as a fallback while this alpha is tested.
Install the Control service after the WireGuard Hub:
```bash
sudo ./ochenstarik-server-monitor-manager.sh install-control-hub
sudo ./ochenstarik-server-monitor-manager.sh control-code home
sudo ./ochenstarik-server-monitor-manager.sh control-device-code windows-pc
```
Copy the resulting `SMMCTL1-...` code and install the Agent on the matching Node:
```bash
sudo ./ochenstarik-server-monitor-manager.sh install-control-agent
```
Create a new code for every Node. The code contains the public Control CA certificate but never its private key. Release archives are verified against their published SHA-256 checksums before installation. The Hub listens on TCP `7443`; allow this port at the hosting provider firewall if an external firewall is used.
`control-device-code` creates a separate operator identity for the Windows application. Agent certificates cannot list all servers, change Links, or subscribe to the operator event stream. The Control service can invoke only the dedicated `link-connect` and `link-disconnect` policy wrapper through sudo; other Hub administration commands are not granted to it.
## Installation flow
1. Select the wizard dialog language.

View file

@ -49,10 +49,51 @@ sudo ./ochenstarik-server-install.sh
| `ochenstarik-server-panel-warp-6.sh` | Устанавливает 3x-ui и Cloudflare WARP, настраивает и проверяет их порты | Ubuntu 22.04/24.04/26.04 или Debian 12/13 |
| `ochenstarik-server-backup-7.sh` | Создаёт неизменяемый первичный снимок и настраивает выбранные расписания резервного копирования | Да; зависимости устанавливаются автоматически |
| `ochenstarik-server-ai-agents-8.sh` | Устанавливает выбранных AI-агентов: Hermes, OpenClaw, OpenHands, OpenCode, Aider, AutoGPT или Pi Coding Agent | Да; API-ключи настраиваются отдельно |
| `ochenstarik-server-monitor-manager.sh` | Устанавливает SSH endpoint, главный WireGuard Hub или подключаемый Node для Server Monitor Manager | Да; Hub требуется публичный UDP-адрес |
| `ochenstarik-server-uninstall.sh` | Удаляет настройки, службы и данные проекта для повторной установки с начала | Да; запускать только из сохранённой SSH-сессии |
Файлы `*.env.example` являются только примерами. Не записывайте настоящие пароли, токены и приватные SSH-ключи в Git.
## Server Monitor Manager: Hub и вторичные серверы
Главному Hub нужен белый IPv4 или домен и открытый UDP-порт (по умолчанию `51820`). Вторичные Node подключаются исходящим WireGuard-соединением и не требуют белого IP.
```bash
curl -fLO https://raw.githubusercontent.com/ochenstarik-ui/lightweight-server/main/ochenstarik-server-monitor-manager.sh
chmod 700 ochenstarik-server-monitor-manager.sh
bash -n ochenstarik-server-monitor-manager.sh
sudo ./ochenstarik-server-monitor-manager.sh hub
```
На Hub создайте отдельный enrollment-код на 10 минут для каждого Node, после чего на соответствующем сервере запустите тот же файл в режиме `node`:
```bash
sudo ochenstarik-smm node-code ai-agent
sudo ochenstarik-smm node-code home
sudo ./ochenstarik-server-monitor-manager.sh node
```
Links являются направленными:
```bash
sudo ochenstarik-smm link-connect ai-agent home tcp 22 120
sudo ochenstarik-smm link-disconnect ai-agent home tcp 22
sudo ochenstarik-smm nodes
sudo ochenstarik-smm links
```
Установщик Node создаст приватный WireGuard-ключ локально и покажет запрос `SMMREQ1`. Во втором терминале выполните на Hub `sudo ochenstarik-smm node-enroll`, вставьте запрос по скрытому приглашению и верните полученный код `SMMACK1` в установщик Node. Enrollment-код действует 10 минут и погашается при первой успешной регистрации.
Команды обслуживания учитывают установленную роль и создают root-only backup перед изменениями:
```bash
sudo ./ochenstarik-server-monitor-manager.sh update
sudo ./ochenstarik-server-monitor-manager.sh rollback
sudo ./ochenstarik-server-monitor-manager.sh uninstall-node
sudo ./ochenstarik-server-monitor-manager.sh uninstall-hub
sudo ./ochenstarik-server-monitor-manager.sh uninstall-monitor
```
## Требования
- Ubuntu Server или совместимая Debian-система с `systemd`;

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,29 @@
#!/usr/bin/env bash
set -Eeuo pipefail
cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.."
script="ochenstarik-server-monitor-manager.sh"
[[ -s "$script" ]]
grep -Fq 'readonly SMM_RELEASE_VERSION=' "$script"
grep -Fq 'server-monitor-manager-${runtime}.tar.gz' "$script"
grep -Fq 'sha256sum "$archive"' "$script"
grep -Fq 'SMMCTL1-' "$script"
grep -Fq 'SMMDEV1-' "$script"
grep -Fq 'control-code)' "$script"
grep -Fq 'install-control-hub)' "$script"
grep -Fq 'install-control-agent)' "$script"
grep -Fq 'control-device-code)' "$script"
grep -Fq 'ochenstarik-smm-policy-apply' "$script"
grep -Fq 'NoNewPrivileges=true' "$script"
grep -Fq 'ProtectSystem=strict' "$script"
grep -Fq 'SMM_EnrollToken=${token}' "$script"
grep -Fq 'systemctl enable --now ochenstarik-smm-control.service' "$script"
grep -Fq 'systemctl enable --now ochenstarik-smm-agent.service' "$script"
if grep -Eq 'CONTROL_JOIN_CODE=.*(ca\.key|control-ca\.pfx)' "$script"; then
echo 'Control join code must never contain the CA private key.' >&2
exit 1
fi
printf 'Server Monitor Manager control-layer installer checks passed.\n'

View file

@ -0,0 +1,99 @@
#!/usr/bin/env bash
set -Eeuo pipefail
cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.."
script="ochenstarik-server-monitor-manager.sh"
temp_dir="$(mktemp -d)"
trap 'rm -rf -- "$temp_dir"' EXIT
[[ -s "$script" ]]
grep -Fq 'code="SMM2-' "$script"
grep -Fq 'request_code="SMMREQ1-' "$script"
grep -Fq 'SMMACK1-' "$script"
grep -Fq 'expires=$(( $(date +%s) + 600 ))' "$script"
grep -Fq 'mv -- "$token_file" "$consuming_file"' "$script"
grep -Fq 'private_key="$(wg genkey)"' "$script"
if grep -Fq 'PRIVATE_KEY=%s' "$script"; then
echo 'Enrollment payload must not contain a private WireGuard key.' >&2
exit 1
fi
helper="$temp_dir/ochenstarik-smm-hub"
awk '
capture && /^EOF$/ { exit }
/cat > .*HUB_HELPER.*<</ { capture=1; next }
capture { print }
' "$script" > "$helper"
chmod 700 "$helper"
state="$temp_dir/state"
mock_bin="$temp_dir/bin"
mkdir -p "$state/nodes" "$state/tokens" "$mock_bin"
sed -i "s|^readonly STATE_DIR=.*|readonly STATE_DIR=\"$state\"|" "$helper"
sed -i "s|^readonly WG_CONFIG=.*|readonly WG_CONFIG=\"$temp_dir/smm0.conf\"|" "$helper"
sed -i 's/^require_root() .*/require_root() { :; }/' "$helper"
sed -i 's/ -o root -g root//g' "$helper"
public_key='AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA='
cat > "$mock_bin/wg" <<EOF
#!/usr/bin/env bash
if [[ "\${1:-}" == pubkey ]]; then
printf '%s\n' '$public_key'
exit 0
fi
exit 0
EOF
cat > "$mock_bin/ip" <<'EOF'
#!/usr/bin/env bash
exit 1
EOF
chmod 700 "$mock_bin/wg" "$mock_bin/ip"
cat > "$state/hub.conf" <<'EOF'
HUB_ENDPOINT=hub.example.test
WG_PORT=51820
HUB_ADDRESS=10.77.0.1
MESH_CIDR=10.77.0.0/24
EOF
printf 'test-hub-private-key\n' > "$state/hub.key"
: > "$state/links"
output="$(PATH="$mock_bin:$PATH" "$helper" node-code test-node)"
join_code="$(printf '%s\n' "$output" | grep '^SMM2-')"
[[ -n "$join_code" ]]
decode_base64url() {
local data="$1"
data="${data//-/+}"
data="${data//_/\/}"
case $(( ${#data} % 4 )) in
2) data+='==' ;;
3) data+='=' ;;
esac
printf '%s' "$data" | base64 -d
}
payload="$(decode_base64url "${join_code#SMM2-}")"
token="$(printf '%s\n' "$payload" | awk -F= '$1 == "TOKEN" { print $2 }')"
name="$(printf '%s\n' "$payload" | awk -F= '$1 == "NAME" { print $2 }')"
address="$(printf '%s\n' "$payload" | awk -F= '$1 == "ADDRESS" { print $2 }')"
[[ "$name" == test-node ]]
[[ "$address" == 10.77.0.2/32 ]]
[[ "$token" =~ ^[a-f0-9]{64}$ ]]
[[ "$payload" != *PRIVATE_KEY* ]]
request_payload="$(printf 'VERSION=1\nTOKEN=%s\nNAME=%s\nADDRESS=%s\nPUBLIC_KEY=%s\n' \
"$token" "$name" "$address" "$public_key")"
request_code="SMMREQ1-$(printf '%s' "$request_payload" | base64 -w0 | tr '+/' '-_' | tr -d '=')"
enroll_output="$(PATH="$mock_bin:$PATH" SMM_ENROLL_REQUEST="$request_code" "$helper" node-enroll)"
grep -Eq '^SMMACK1-[a-f0-9]{64}$' <<< "$enroll_output"
grep -Fqx 'NAME=test-node' "$state/nodes/test-node.node"
grep -Fqx "PUBLIC_KEY=$public_key" "$state/nodes/test-node.node"
if PATH="$mock_bin:$PATH" SMM_ENROLL_REQUEST="$request_code" "$helper" node-enroll >/dev/null 2>&1; then
echo 'Enrollment token was accepted more than once.' >&2
exit 1
fi
printf 'Server Monitor Manager enrollment checks passed.\n'

View file

@ -0,0 +1,22 @@
#!/usr/bin/env bash
set -Eeuo pipefail
cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.."
script="ochenstarik-server-monitor-manager.sh"
[[ -s "$script" ]]
grep -Fq 'sudo' "$script"
grep -Fq 'iputils-ping' "$script"
grep -Fq 'backup_state()' "$script"
grep -Fq 'rollback_state()' "$script"
grep -Fq 'update_installed()' "$script"
grep -Fq 'uninstall_monitor()' "$script"
grep -Fq 'uninstall_node()' "$script"
grep -Fq 'uninstall_hub()' "$script"
grep -Fq '/var/backups/${APP_NAME}' "$script"
grep -Fq 'systemctl disable --now ochenstarik-smm-firewall.timer' "$script"
grep -Fq 'nft delete table inet ochenstarik_smm' "$script"
grep -Fq 'sysctl --system' "$script"
grep -Fq 'uninstall) die "Укажите роль:' "$script"
printf 'Server Monitor Manager lifecycle checks passed.\n'

View file

@ -0,0 +1,95 @@
#!/usr/bin/env bash
set -Eeuo pipefail
cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.."
script="ochenstarik-server-monitor-manager.sh"
temp_dir="$(mktemp -d)"
trap 'rm -rf -- "$temp_dir"' EXIT
helper="$temp_dir/ochenstarik-smm-hub"
awk '
capture && /^EOF$/ { exit }
/cat > .*HUB_HELPER.*<</ { capture=1; next }
capture { print }
' "$script" > "$helper"
chmod 700 "$helper"
state="$temp_dir/state"
mock_bin="$temp_dir/bin"
nft_capture="$temp_dir/nft.conf"
mkdir -p "$state/nodes" "$state/tokens" "$mock_bin"
sed -i "s|^readonly STATE_DIR=.*|readonly STATE_DIR=\"$state\"|" "$helper"
sed -i "s|^readonly WG_CONFIG=.*|readonly WG_CONFIG=\"$temp_dir/smm0.conf\"|" "$helper"
sed -i 's/^require_root() .*/require_root() { :; }/' "$helper"
sed -i 's/ -o root -g root//g' "$helper"
cat > "$mock_bin/nft" <<'EOF'
#!/usr/bin/env bash
previous=''
for argument in "$@"; do
if [[ "$previous" == -f ]]; then
cp -- "$argument" "$NFT_CAPTURE"
fi
previous="$argument"
done
exit 0
EOF
chmod 700 "$mock_bin/nft"
cat > "$state/hub.conf" <<'EOF'
HUB_ENDPOINT=hub.example.test
WG_PORT=51820
HUB_ADDRESS=10.77.0.1
MESH_CIDR=10.77.0.0/24
EOF
printf 'test-hub-private-key\n' > "$state/hub.key"
cat > "$state/nodes/ai-agent.node" <<'EOF'
NAME=ai-agent
ADDRESS=10.77.0.2
PUBLIC_KEY=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
EOF
cat > "$state/nodes/home.node" <<'EOF'
NAME=home
ADDRESS=10.77.0.3
PUBLIC_KEY=BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB=
EOF
printf 'legacy-policy home\n' > "$state/links"
PATH="$mock_bin:$PATH" NFT_CAPTURE="$nft_capture" \
"$helper" link-connect ai-agent home tcp 2222 120
read -r source target cidr protocol port expires version < "$state/links"
[[ "$source" == ai-agent ]]
[[ "$target" == home ]]
[[ "$cidr" == 10.77.0.3/32 ]]
[[ "$protocol" == tcp ]]
[[ "$port" == 2222 ]]
(( expires > $(date +%s) ))
[[ "$version" =~ ^[0-9]+$ ]]
grep -Fq 'ip saddr 10.77.0.2 ip daddr 10.77.0.3/32 tcp dport 2222 accept' "$nft_capture"
grep -Fq '"state":"Connecting"' "$state/audit.jsonl"
grep -Fq '"state":"Active"' "$state/audit.jsonl"
if PATH="$mock_bin:$PATH" NFT_CAPTURE="$nft_capture" \
"$helper" link-connect ai-agent home tcp 70000 120 >/dev/null 2>&1; then
echo 'Invalid port was accepted.' >&2
exit 1
fi
printf 'ai-agent home 10.77.0.3/32 udp 53 1 99\n' >> "$state/links"
PATH="$mock_bin:$PATH" NFT_CAPTURE="$nft_capture" "$helper" firewall-restore
[[ "$(wc -l < "$state/links")" -eq 1 ]]
PATH="$mock_bin:$PATH" NFT_CAPTURE="$nft_capture" \
"$helper" link-disconnect ai-agent home tcp 2222
[[ ! -s "$state/links" ]]
grep -Fq '"state":"Expired"' "$state/audit.jsonl"
grep -Fq '"state":"Disconnecting"' "$state/audit.jsonl"
grep -Fq '"state":"Disabled"' "$state/audit.jsonl"
if grep -Fq 'dport 2222 accept' "$nft_capture"; then
echo 'Disconnected policy remained in nftables.' >&2
exit 1
fi
grep -Fq 'OnUnitActiveSec=1min' "$script"
printf 'Server Monitor Manager Link policy checks passed.\n'

View file

@ -0,0 +1,15 @@
#!/usr/bin/env bash
set -Eeuo pipefail
cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.."
script="ochenstarik-server-monitor-manager.sh"
for key in \
SWAP_TOTAL_KB SWAP_FREE_KB \
DISK_INODES_TOTAL DISK_INODES_FREE \
NETWORK_RX_BYTES NETWORK_TX_BYTES \
SYSTEMD_SSH SYSTEMD_WIREGUARD; do
grep -Fq "$key" "$script"
done
printf 'Server Monitor Manager extended metrics checks passed.\n'