Add TTY menu, Web UI, split Dockerfiles (host + ansible)
This commit is contained in:
@@ -0,0 +1,174 @@
|
||||
#!/bin/sh
|
||||
# ───────────────────────────────────────────────────────────
|
||||
# tty-menu.sh — Serial Console Menu for the Alpine Docker Host
|
||||
#
|
||||
# Launched by agetty on ttyS0. The user interacts directly
|
||||
# with the Ansible container from the serial console.
|
||||
#
|
||||
# Options:
|
||||
# 1-4 Run playbooks (docker run ansible-node)
|
||||
# 5 Download reports as tar.gz
|
||||
# 6 View latest report summary
|
||||
# 7 Shell into Ansible container
|
||||
# 8 Shell on Docker host
|
||||
# 0 Shutdown VM
|
||||
# ───────────────────────────────────────────────────────────
|
||||
|
||||
PLAYBOOKS_DIR="/ansible/playbooks"
|
||||
REPORTS_DIR="/ansible/reports"
|
||||
INVENTORY="/ansible/inventory/inventory.ini"
|
||||
IMAGE="ansible-node"
|
||||
WEB_PORT="8080"
|
||||
|
||||
# ── Colour helpers ────────────────────────────────────────
|
||||
GREEN='\033[0;32m'
|
||||
BLUE='\033[0;34m'
|
||||
YELLOW='\033[1;33m'
|
||||
RED='\033[0;31m'
|
||||
CYAN='\033[0;36m'
|
||||
BOLD='\033[1m'
|
||||
NC='\033[0m'
|
||||
|
||||
clear
|
||||
|
||||
while true; do
|
||||
# Determine IP for web UI hint
|
||||
IP=$(ip -4 addr show scope global 2>/dev/null | \
|
||||
grep -oP '(?<=inet\s)\d+(\.\d+){3}' | head -1)
|
||||
[ -z "$IP" ] && IP="(no network)"
|
||||
|
||||
echo ""
|
||||
echo -e "${GREEN}${BOLD}╔══════════════════════════════════════════════════╗${NC}"
|
||||
echo -e "${GREEN}${BOLD}║ IEC 62443-3-3 SL2 Compliance Validator ║${NC}"
|
||||
echo -e "${GREEN}${BOLD}╠══════════════════════════════════════════════════╣${NC}"
|
||||
echo -e "${GREEN}${BOLD}║${NC} Web UI: ${CYAN}http://${IP}:${WEB_PORT}${GREEN} ${NC}${GREEN}${BOLD}║${NC}"
|
||||
echo -e "${GREEN}${BOLD}╠══════════════════════════════════════════════════╣${NC}"
|
||||
echo -e "${GREEN}${BOLD}║${NC} ${GREEN}${BOLD}║${NC}"
|
||||
echo -e "${GREEN}${BOLD}║${NC} ${BOLD}1)${NC} Run all tests (site.yml) ${GREEN}${BOLD}║${NC}"
|
||||
echo -e "${GREEN}${BOLD}║${NC} ${BOLD}2)${NC} Run FR1 — Auth & Identification ${GREEN}${BOLD}║${NC}"
|
||||
echo -e "${GREEN}${BOLD}║${NC} ${BOLD}3)${NC} Run FR2 — Use Control ${GREEN}${BOLD}║${NC}"
|
||||
echo -e "${GREEN}${BOLD}║${NC} ${BOLD}4)${NC} Run FR5 — Restricted Data Flow ${GREEN}${BOLD}║${NC}"
|
||||
echo -e "${GREEN}${BOLD}║${NC} ${GREEN}${BOLD}║${NC}"
|
||||
echo -e "${GREEN}${BOLD}║${NC} ${BOLD}5)${NC} Create reports archive (tar.gz) ${GREEN}${BOLD}║${NC}"
|
||||
echo -e "${GREEN}${BOLD}║${NC} ${BOLD}6)${NC} View latest report summary ${GREEN}${BOLD}║${NC}"
|
||||
echo -e "${GREEN}${BOLD}║${NC} ${GREEN}${BOLD}║${NC}"
|
||||
echo -e "${GREEN}${BOLD}║${NC} ${BOLD}7)${NC} Shell — Ansible container ${GREEN}${BOLD}║${NC}"
|
||||
echo -e "${GREEN}${BOLD}║${NC} ${BOLD}8)${NC} Shell — Docker host ${GREEN}${BOLD}║${NC}"
|
||||
echo -e "${GREEN}${BOLD}║${NC} ${GREEN}${BOLD}║${NC}"
|
||||
echo -e "${GREEN}${BOLD}║${NC} ${BOLD}0)${NC} Shutdown VM ${GREEN}${BOLD}║${NC}"
|
||||
echo -e "${GREEN}${BOLD}╚══════════════════════════════════════════════════╝${NC}"
|
||||
echo ""
|
||||
printf " Choice [0-8]: "
|
||||
read -r CHOICE
|
||||
|
||||
docker_run() {
|
||||
local pb="$1"; shift
|
||||
echo ""
|
||||
echo -e "${YELLOW}Running: ${pb}${NC}"
|
||||
echo "═══════════════════════════════════════════"
|
||||
docker run --rm -it \
|
||||
-v "${PLAYBOOKS_DIR}:/ansible/playbooks:ro" \
|
||||
-v "${REPORTS_DIR}:/ansible/reports" \
|
||||
-v "$(dirname "${INVENTORY}"):/ansible/inventory:ro" \
|
||||
"${IMAGE}" \
|
||||
"/ansible/playbooks/${pb}" \
|
||||
-i /ansible/inventory/inventory.ini "$@"
|
||||
echo ""
|
||||
echo -e "${GREEN}Done. Reports: ${REPORTS_DIR}${NC}"
|
||||
echo "Press Enter to continue..."
|
||||
read -r _
|
||||
}
|
||||
|
||||
case "$CHOICE" in
|
||||
1) docker_run "site.yml" ;;
|
||||
2) docker_run "suites/fr1_auth.yml" ;;
|
||||
3) docker_run "suites/fr2_use_control.yml" ;;
|
||||
4) docker_run "suites/fr5_data_flow.yml" ;;
|
||||
5)
|
||||
echo ""
|
||||
echo -e "${YELLOW}Available reports:${NC}"
|
||||
ls -lh "${REPORTS_DIR}"/*.json 2>/dev/null || echo " No reports yet."
|
||||
echo ""
|
||||
printf " Enter filename (or Enter for all as tar.gz): "
|
||||
read -r FN
|
||||
if [ -n "$FN" ]; then
|
||||
OUT="/tmp/${FN}"
|
||||
cp "${REPORTS_DIR}/${FN}" "$OUT" 2>/dev/null && \
|
||||
echo -e " Written: ${GREEN}${OUT}${NC}" || \
|
||||
echo -e "${RED} Not found: ${FN}${NC}"
|
||||
else
|
||||
OUTF="/tmp/reports-$(date +%Y%m%d-%H%M).tar.gz"
|
||||
cd "${REPORTS_DIR}" && tar czf "$OUTF" *.json 2>/dev/null && \
|
||||
echo -e " Created: ${GREEN}${OUTF}${NC} ($(du -sh "$OUTF" | cut -f1))"
|
||||
fi
|
||||
echo " Transfer via: scp ansible@<host>:/tmp/reports-*.tar.gz ."
|
||||
echo ""
|
||||
echo "Press Enter to continue..."
|
||||
read -r _
|
||||
;;
|
||||
6)
|
||||
LATEST=$(ls -t "${REPORTS_DIR}"/*.json 2>/dev/null | head -1)
|
||||
if [ -z "$LATEST" ]; then
|
||||
echo -e "${RED} No reports yet.${NC}"
|
||||
else
|
||||
echo ""
|
||||
echo -e "${YELLOW}Latest: $(basename "$LATEST")${NC}"
|
||||
echo "═══════════════════════════════════════════"
|
||||
python3 -c "
|
||||
import json
|
||||
with open('$LATEST') as f:
|
||||
r = json.load(f)
|
||||
s = r['summary']
|
||||
print(f'Total: {s[\"total\"]} | Passed: {s[\"passed\"]} | Failed: {s[\"failed\"]}')
|
||||
print(f'Compliance rate: {s[\"passed\"]/s[\"total\"]*100:.1f}%')
|
||||
print()
|
||||
for t in r['results']:
|
||||
icon = '\u2705' if t['passed'] == True else ('\u274c' if t['passed'] == False else '\U0001f50d')
|
||||
print(f' {icon} [{t[\"test_id\"]}] {t[\"description\"]}')
|
||||
print()
|
||||
if r.get('failures'):
|
||||
print('Failures:')
|
||||
for f in r['failures']:
|
||||
print(f' \u274c {f[\"test_id\"]}: {f[\"description\"]}')
|
||||
print(f' Expected: {f[\"expected\"]}')
|
||||
print(f' Actual: {f[\"actual\"]}')
|
||||
print(f' Fix: {f[\"remediation\"]}')
|
||||
" 2>/dev/null || echo " Error reading report"
|
||||
fi
|
||||
echo ""
|
||||
echo "Press Enter to continue..."
|
||||
read -r _
|
||||
;;
|
||||
7)
|
||||
echo ""
|
||||
echo -e "${YELLOW}Ansible container shell (type 'exit' to return)${NC}"
|
||||
echo "═══════════════════════════════════════════"
|
||||
docker run --rm -it \
|
||||
-v "${PLAYBOOKS_DIR}:/ansible/playbooks:ro" \
|
||||
-v "${REPORTS_DIR}:/ansible/reports" \
|
||||
-v "$(dirname "${INVENTORY}"):/ansible/inventory:ro" \
|
||||
"${IMAGE}" /bin/bash 2>/dev/null || \
|
||||
docker run --rm -it \
|
||||
-v "${PLAYBOOKS_DIR}:/ansible/playbooks:ro" \
|
||||
-v "${REPORTS_DIR}:/ansible/reports" \
|
||||
-v "$(dirname "${INVENTORY}"):/ansible/inventory:ro" \
|
||||
"${IMAGE}" /bin/sh
|
||||
;;
|
||||
8)
|
||||
echo ""
|
||||
echo -e "${YELLOW}Host shell (type 'exit' to return to menu)${NC}"
|
||||
echo "═══════════════════════════════════════════"
|
||||
/bin/bash 2>/dev/null || /bin/sh
|
||||
;;
|
||||
0)
|
||||
echo ""
|
||||
echo -e "${RED}Shutting down...${NC}"
|
||||
sudo poweroff 2>/dev/null || poweroff
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
echo -e "${RED}Invalid choice${NC}"
|
||||
sleep 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
Reference in New Issue
Block a user