Initial curated Unraid-AppOps-Gateway source from 3bd6f6fee310125bfbb4d4650389a817f498724d
This commit is contained in:
commit
1dfbca08a1
528 files changed
+131054
No files matched your search
@@ -0,0 +1,88 @@
|
||||
"""One-time, write-only import of RepoPulse's existing Gitea credential.
|
||||
|
||||
Run inside the AppOps container as its service account. The credential is read
|
||||
from stdin so it never appears in argv, environment, logs, or command output.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import hmac
|
||||
import json
|
||||
import sys
|
||||
|
||||
from sqlmodel import Session
|
||||
|
||||
from ..db.session import engine
|
||||
from .secret_store import get_secret, has_secret, secret_storage_status, set_secret
|
||||
|
||||
SECRET_REF = "REPOPULSE_GITEA_TOKEN"
|
||||
BOOTSTRAP_SECRET_REF = "ENROLLMENT_BOOTSTRAP"
|
||||
MAX_SECRET_BYTES = 8192
|
||||
|
||||
|
||||
def _import_fixed_secret(
|
||||
session: Session, token: str, *, secret_ref: str, used_by: str, purpose: str
|
||||
) -> str:
|
||||
if not secret_storage_status()["productionReady"]:
|
||||
raise ValueError("production secret storage is not ready")
|
||||
if not token or token != token.strip() or "\n" in token or "\r" in token:
|
||||
raise ValueError("invalid credential input")
|
||||
if len(token.encode("utf-8")) > MAX_SECRET_BYTES:
|
||||
raise ValueError("credential input too large")
|
||||
|
||||
existing = get_secret(session, secret_ref, touch=False)
|
||||
if existing is None and has_secret(session, secret_ref):
|
||||
raise ValueError("credential reference cannot be decrypted")
|
||||
if existing is not None:
|
||||
if hmac.compare_digest(existing, token):
|
||||
return "already_present"
|
||||
raise ValueError("credential reference already contains a different value")
|
||||
|
||||
set_secret(
|
||||
session,
|
||||
secret_ref,
|
||||
token,
|
||||
used_by=[used_by],
|
||||
metadata={"purpose": purpose},
|
||||
)
|
||||
return "imported"
|
||||
|
||||
|
||||
def import_repopulse_gitea_token(session: Session, token: str) -> str:
|
||||
return _import_fixed_secret(
|
||||
session,
|
||||
token,
|
||||
secret_ref=SECRET_REF,
|
||||
used_by="deployment:repopulse",
|
||||
purpose="repopulse_gitea_access",
|
||||
)
|
||||
|
||||
|
||||
def import_controller_bootstrap(session: Session, token: str) -> str:
|
||||
return _import_fixed_secret(
|
||||
session,
|
||||
token,
|
||||
secret_ref=BOOTSTRAP_SECRET_REF,
|
||||
used_by="deployment:enrollment_controller",
|
||||
purpose="controller_enrollment_bootstrap",
|
||||
)
|
||||
|
||||
|
||||
def main() -> int:
|
||||
raw = sys.stdin.buffer.read(MAX_SECRET_BYTES + 1)
|
||||
if len(raw) > MAX_SECRET_BYTES:
|
||||
print(json.dumps({"status": "blocked", "reason": "credential input too large"}))
|
||||
return 2
|
||||
try:
|
||||
token = raw.decode("utf-8")
|
||||
with Session(engine) as session:
|
||||
outcome = import_repopulse_gitea_token(session, token)
|
||||
except (UnicodeDecodeError, ValueError):
|
||||
print(json.dumps({"status": "blocked", "reason": "credential import rejected"}))
|
||||
return 2
|
||||
print(json.dumps({"status": outcome, "secretRef": SECRET_REF}))
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Reference in new issue
Block a user