diff --git a/deployment/navigation-maps/JD-FD-PRIMARY.json b/deployment/navigation-maps/JD-FD-PRIMARY.json index b39cbcb..636f291 100644 --- a/deployment/navigation-maps/JD-FD-PRIMARY.json +++ b/deployment/navigation-maps/JD-FD-PRIMARY.json @@ -9,7 +9,7 @@ { "code": "JD-HUB-01", "name": "京东应用入口", "bind": "loopback:8088", "owner": "systemd", "version": "2.0.0", "state": "DEPLOYED_AND_VERIFIED", "source_commit": "09c93b55ad73bf3af52eebdbfea61731386e264d" }, { "code": "JD-SEN-01", "name": "状态变化哨兵", "bind": "timer:15m", "owner": "systemd" }, { "code": "JD-ROUTE-01", "name": "已登记下游节点 SSH 路由", "bind": "private-keys", "owner": "root", "registered_targets": ["AW-GZ-001"] }, - { "code": "JD-ACT-01", "name": "固定动作执行桥", "bind": "unix-socket", "owner": "root", "actions": ["inspect-services"] }, + { "code": "JD-ACT-01", "name": "固定动作执行桥", "bind": "unix-socket", "owner": "root", "actions": ["inspect-services", "health-check", "inspect-code-channel-owner-auth"] }, { "code": "JD-OWNER-ACCESS-01", "name": "冰朔登录入口恢复执行器", "bind": "unix-socket", "owner": "root", "actions": ["restore-owner-password-login"] }, { "code": "JD-ARCH-PROVISION-01", "name": "已批准新架构首次安装器", "bind": "unix-socket", "owner": "root", "actions": ["provision-approved-architecture"], "state": "ACTIVE" }, { "code": "JD-LAN-01", "name": "光湖·来光者导航只读召回服务", "bind": "loopback:3924", "owner": "systemd", "architecture": "GLS-0231", "state": "DEPLOYED_AND_VERIFIED", "source_commit": "f4a4b5996c83b55d99172f3196f4a0d77ed5f3e9" }, diff --git a/server-tools/lake-lamp-authz/action-broker.js b/server-tools/lake-lamp-authz/action-broker.js index 351bb31..4a2dfee 100644 --- a/server-tools/lake-lamp-authz/action-broker.js +++ b/server-tools/lake-lamp-authz/action-broker.js @@ -7,14 +7,29 @@ const { execFile } = require("node:child_process"); const SOCKET_PATH = process.env.LAKE_LAMP_ACTION_SOCKET || "/run/guanghu/action-broker.sock"; const SSH_CONFIG = process.env.LAKE_LAMP_SSH_CONFIG || "/etc/guanghu/action-broker-ssh-config"; -const ACTIONS = Object.freeze({ - "JD-FD-PRIMARY:inspect-services": () => run("/usr/bin/ssh", [ +const REMOTE_COMMANDS = Object.freeze({ + "JD-FD-PRIMARY:inspect-services": + "printf 'HOST='; hostname; printf 'SSH='; systemctl is-active ssh 2>/dev/null || systemctl is-active sshd 2>/dev/null; printf 'LIGHTHOUSE='; systemctl is-active guanghu-enterprise-lighthouse.service 2>/dev/null || true", + "JD-FD-PRIMARY:health-check": + "set -u; printf 'HOST='; hostname; printf 'HLCC='; systemctl is-active hlcc-jd-candidate.service 2>/dev/null || true; printf 'HLCC_HTTP='; curl -fsS --max-time 5 http://127.0.0.1:3340/api/healthz 2>/dev/null || printf 'unavailable\\n'; printf 'LIGHTHOUSE='; systemctl is-active guanghu-enterprise-lighthouse.service 2>/dev/null || true", + "JD-FD-PRIMARY:inspect-code-channel-owner-auth": + "set -eu; state=/var/lib/guanghu/personas/guanghu/hlcc-v16.0.1; db=\"$state/data/hlcc.db\"; config=\"$state/config/app.ini\"; printf 'HOST='; hostname; printf 'HLCC='; systemctl is-active hlcc-jd-candidate.service 2>/dev/null || true; printf 'DB_PRESENT='; test -f \"$db\" && printf 'yes\\n' || printf 'no\\n'; printf 'CONFIG_PRESENT='; test -f \"$config\" && printf 'yes\\n' || printf 'no\\n'; test -f \"$db\"; /usr/bin/sqlite3 -readonly -header -separator '|' \"$db\" \"select lower_name,is_active,is_admin,prohibit_login,login_type,passwd_hash_algo,length(passwd) as passwd_length,length(salt) as salt_length,updated_unix from user where lower_name='bingshuo';\"; /usr/bin/sqlite3 -readonly -header -separator '|' \"$db\" \"select lower_name,name,website,is_private,is_empty,default_branch,updated_unix from repository where lower_name in ('fifth-domain','guanghu-ice-heart') order by lower_name;\"" +}); + +function remoteAction(command) { + return () => run("/usr/bin/ssh", [ "-F", SSH_CONFIG, "-o", "BatchMode=yes", "-o", "ConnectTimeout=10", "enterprise-lighthouse", - "printf 'HOST='; hostname; printf 'SSH='; systemctl is-active ssh 2>/dev/null || systemctl is-active sshd 2>/dev/null; printf 'LIGHTHOUSE='; systemctl is-active guanghu-enterprise-lighthouse.service 2>/dev/null || true" - ]) + command, + ]); +} + +const ACTIONS = Object.freeze({ + "JD-FD-PRIMARY:inspect-services": remoteAction(REMOTE_COMMANDS["JD-FD-PRIMARY:inspect-services"]), + "JD-FD-PRIMARY:health-check": remoteAction(REMOTE_COMMANDS["JD-FD-PRIMARY:health-check"]), + "JD-FD-PRIMARY:inspect-code-channel-owner-auth": remoteAction(REMOTE_COMMANDS["JD-FD-PRIMARY:inspect-code-channel-owner-auth"]), }); function run(file, args) { @@ -50,4 +65,4 @@ if (require.main === module) { }); } -module.exports = { ACTIONS }; +module.exports = { ACTIONS, REMOTE_COMMANDS }; diff --git a/server-tools/lake-lamp-authz/action-broker.test.js b/server-tools/lake-lamp-authz/action-broker.test.js new file mode 100644 index 0000000..bcda4bc --- /dev/null +++ b/server-tools/lake-lamp-authz/action-broker.test.js @@ -0,0 +1,23 @@ +"use strict"; + +const test = require("node:test"); +const assert = require("node:assert/strict"); +const { ACTIONS, REMOTE_COMMANDS } = require("./action-broker"); + +test("the JD action broker implements every read-only server-login action", () => { + assert.equal(typeof ACTIONS["JD-FD-PRIMARY:inspect-services"], "function"); + assert.equal(typeof ACTIONS["JD-FD-PRIMARY:health-check"], "function"); + assert.equal(typeof ACTIONS["JD-FD-PRIMARY:inspect-code-channel-owner-auth"], "function"); +}); + +test("owner authentication inspection exposes state but never password material", () => { + const command = REMOTE_COMMANDS["JD-FD-PRIMARY:inspect-code-channel-owner-auth"]; + assert.match(command, /is_active/); + assert.match(command, /prohibit_login/); + assert.match(command, /passwd_hash_algo/); + assert.match(command, /length\(passwd\)/); + assert.match(command, /length\(salt\)/); + assert.doesNotMatch(command, /select\s+passwd(?:\s|,)/i); + assert.doesNotMatch(command, /select\s+salt(?:\s|,)/i); + assert.doesNotMatch(command, /access_token|authorization|secret_key/i); +}); diff --git a/server-tools/lake-lamp-authz/server.js b/server-tools/lake-lamp-authz/server.js index cc98501..710cf05 100644 --- a/server-tools/lake-lamp-authz/server.js +++ b/server-tools/lake-lamp-authz/server.js @@ -10,7 +10,12 @@ const { sendSmtpMail } = require("./smtp-mailer"); const { executeRegisteredAction } = require("./action-client"); const DEFAULT_ACTIONS = Object.freeze({ - "server-login": ["read-navigation-map", "inspect-services", "health-check"], + "server-login": [ + "read-navigation-map", + "inspect-services", + "health-check", + "inspect-code-channel-owner-auth", + ], "server-ops": [ "read-navigation-map", "inspect-services",