feat: harden control panel authentication
This commit is contained in:
@@ -204,29 +204,36 @@ def call_account_model(provider: str, prompt: str):
|
||||
def audit(job: dict, status: str) -> str:
|
||||
path = os.path.join(STATE_ROOT, "logs", "audit", "goal-orchestrator.jsonl")
|
||||
head_path = os.path.join(STATE_ROOT, "logs", "audit", "goal-orchestrator-head.txt")
|
||||
lock_path = os.path.join(STATE_ROOT, "logs", "audit", "goal-orchestrator.lock")
|
||||
os.makedirs(os.path.dirname(path), exist_ok=True)
|
||||
previous = "0" * 64
|
||||
try:
|
||||
with open(head_path, encoding="utf-8") as handle:
|
||||
previous = handle.read().strip() or previous
|
||||
except OSError:
|
||||
pass
|
||||
core = {
|
||||
"timestamp": now(),
|
||||
"harness": "H5-governance",
|
||||
"goal_id": job["id"],
|
||||
"tenant": job.get("tenant", "default"),
|
||||
"actor": job.get("actor", "unknown"),
|
||||
"goal_hash": sha(str(job.get("goal", ""))),
|
||||
"status": status,
|
||||
"local_provider": job.get("local_provider", ""),
|
||||
"cloud_provider": job.get("cloud_provider", ""),
|
||||
"prev_hash": previous,
|
||||
}
|
||||
record_hash = sha(json.dumps(core, sort_keys=True, ensure_ascii=False))
|
||||
append_jsonl(path, {**core, "record_hash": record_hash})
|
||||
with open(head_path, "w", encoding="utf-8") as handle:
|
||||
handle.write(record_hash + "\n")
|
||||
with open(lock_path, "a", encoding="utf-8") as lock:
|
||||
fcntl.flock(lock.fileno(), fcntl.LOCK_EX)
|
||||
previous = "0" * 64
|
||||
try:
|
||||
with open(head_path, encoding="utf-8") as handle:
|
||||
previous = handle.read().strip() or previous
|
||||
except OSError:
|
||||
pass
|
||||
core = {
|
||||
"timestamp": now(),
|
||||
"harness": "H5-governance",
|
||||
"goal_id": job["id"],
|
||||
"tenant": job.get("tenant", "default"),
|
||||
"actor": job.get("actor", "unknown"),
|
||||
"goal_hash": sha(str(job.get("goal", ""))),
|
||||
"status": status,
|
||||
"local_provider": job.get("local_provider", ""),
|
||||
"cloud_provider": job.get("cloud_provider", ""),
|
||||
"prev_hash": previous,
|
||||
}
|
||||
record_hash = sha(json.dumps(core, sort_keys=True, ensure_ascii=False))
|
||||
append_jsonl(path, {**core, "record_hash": record_hash})
|
||||
with open(head_path, "w", encoding="utf-8") as handle:
|
||||
handle.write(record_hash + "\n")
|
||||
handle.flush()
|
||||
os.fsync(handle.fileno())
|
||||
os.chmod(head_path, 0o600)
|
||||
fcntl.flock(lock.fileno(), fcntl.LOCK_UN)
|
||||
return record_hash
|
||||
|
||||
|
||||
@@ -351,10 +358,43 @@ def run(job_path: str) -> int:
|
||||
return 2
|
||||
|
||||
|
||||
def verify_audit() -> int:
|
||||
path = os.path.join(STATE_ROOT, "logs", "audit", "goal-orchestrator.jsonl")
|
||||
previous = "0" * 64
|
||||
records = 0
|
||||
try:
|
||||
handle = open(path, encoding="utf-8")
|
||||
except OSError:
|
||||
print(json.dumps({"ok": True, "records": 0, "head": previous}))
|
||||
return 0
|
||||
with handle:
|
||||
for line in handle:
|
||||
if not line.strip():
|
||||
continue
|
||||
records += 1
|
||||
try:
|
||||
record = json.loads(line)
|
||||
except ValueError:
|
||||
print(json.dumps({"ok": False, "records": records, "reason": "invalid_json"}))
|
||||
return 3
|
||||
record_hash = str(record.pop("record_hash", ""))
|
||||
expected = sha(json.dumps(record, sort_keys=True, ensure_ascii=False))
|
||||
if record.get("prev_hash") != previous or record_hash != expected:
|
||||
print(json.dumps({"ok": False, "records": records, "reason": "chain_break"}))
|
||||
return 3
|
||||
previous = record_hash
|
||||
print(json.dumps({"ok": True, "records": records, "head": previous}))
|
||||
return 0
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--job-file", required=True)
|
||||
group = parser.add_mutually_exclusive_group(required=True)
|
||||
group.add_argument("--job-file")
|
||||
group.add_argument("--verify-audit", action="store_true")
|
||||
args = parser.parse_args()
|
||||
if args.verify_audit:
|
||||
return verify_audit()
|
||||
path = os.path.abspath(args.job_file)
|
||||
state = os.path.abspath(STATE_ROOT) + os.sep
|
||||
if not path.startswith(state):
|
||||
|
||||
Reference in New Issue
Block a user