From 03db9d6b8a032b97a82ca98f0ea61bcbdf997fec Mon Sep 17 00:00:00 2001 From: nogueiraguh Date: Fri, 3 Apr 2026 17:51:53 -0300 Subject: [PATCH] feat: automated setup.sh, OCIR login helper with encrypted credentials, README updates --- README.md | 70 ++++++++++--------- deploy/scripts/ocir-login.py | 131 +++++++++++++++++++++++++++++++++++ distribution/README.md | 51 ++++++++++---- distribution/setup.sh | 97 ++++++++++++++++++++++++++ 4 files changed, 300 insertions(+), 49 deletions(-) create mode 100755 deploy/scripts/ocir-login.py create mode 100755 distribution/setup.sh diff --git a/README.md b/README.md index 03a8e4d..76a0adc 100644 --- a/README.md +++ b/README.md @@ -301,43 +301,30 @@ The platform combines security compliance scanning, AI-powered chat with **RAG ( ## Quick Start +### Option A — Automated Setup (from OCIR image) + +```bash +bash distribution/setup.sh +``` + +One command: authenticates to OCIR, pulls the image, starts the container. Access `http://localhost:8080` — default credentials `admin` / `admin123` (forced password change on first login). + +### Option B — Development (Docker Compose) + +```bash +cp .env.example .env +# Edit .env: set APP_SECRET (openssl rand -hex 64) +docker compose up -d --build +``` + +Access `http://localhost:8080`. + ### Prerequisites - Docker and Docker Compose - OCI API Key pair (private `.pem` key + fingerprint) - OCI Tenancy OCID, User OCID, Compartment OCID -### 1. Clone & Configure - -```bash -git clone https://github.com/nogueiragustavo/oci-cis-agent.git -cd oci-cis-agent -cp .env.example .env -``` - -Edit `.env`: -```env -APP_SECRET=your-very-long-random-secret-string-here-at-least-64-chars -JWT_EXPIRY_HOURS=12 -PORT=8080 -``` - -### 2. Build & Run - -```bash -docker compose up -d --build -``` - -### 3. Access - -Open `http://localhost:8080` - -Default credentials: -- **Username:** `admin` -- **Password:** `admin123` - -> Change the default password immediately after first login. - --- ## Configuration Guide @@ -877,18 +864,33 @@ pip install -r requirements.txt DATA_DIR=./data uvicorn app:app --reload --port 8000 ``` -### Run with Docker +### Run with Docker Compose ```bash docker compose up -d --build docker compose logs -f backend ``` +### Run Tests + +```bash +cd backend +pip install pytest httpx pytest-asyncio +python -m pytest tests/ -v +``` + +86 baseline tests covering auth, CRUD, settings, chat, terraform, reports, terminal, and user isolation. + +### Build & Push OCIR Image + +```bash +OCIR_REGION=us-ashburn-1 OCIR_NAMESPACE=idi1o0a010nx ./deploy/scripts/push-images.sh +``` + ### Rebuild After Changes ```bash -docker compose down -docker compose up -d --build +docker compose up -d --build backend && docker compose restart frontend ``` --- diff --git a/deploy/scripts/ocir-login.py b/deploy/scripts/ocir-login.py new file mode 100755 index 0000000..92fa6a4 --- /dev/null +++ b/deploy/scripts/ocir-login.py @@ -0,0 +1,131 @@ +#!/usr/bin/env python3 +""" +OCIR Login Helper — Decrypts embedded credentials and performs docker login. + +The auth token is encrypted with Fernet (AES-128-CBC + HMAC-SHA256). +A passphrase is required to decrypt — distribute it out-of-band (Slack, email). + +Usage: + python ocir-login.py + python ocir-login.py --passphrase + python ocir-login.py --generate # Admin: encrypt a new token +""" +import argparse +import base64 +import getpass +import hashlib +import os +import subprocess +import sys + +# ── OCIR Configuration ───────────────────────────────────────────────────── +OCIR_REGION = "us-ashburn-1" +OCIR_ENDPOINT = f"{OCIR_REGION}.ocir.io" +OCIR_NAMESPACE = "idi1o0a010nx" +OCIR_USERNAME = f"{OCIR_NAMESPACE}/oracleidentitycloudservice/gustavo.nogueira@oracle.com" + +# ── Encrypted Token (generated with --generate) ──────────────────────────── +ENCRYPTED_TOKEN = "gAAAAABp0CYOb2bj1neOKUS8L_OOaNTcqEcgWouGiHtn96LDoOgpzJ1YeITNSq5tOqJZRW2JcEMZRWsbC3rc0tAoGsvM2o_Fh_q4RIoyP6nzJ0B1JLBRqnk=" + +# ── Salt for key derivation (fixed, not secret) ──────────────────────────── +_SALT = b"oci-cis-agent-ocir-v1" + + +def _derive_key(passphrase: str) -> bytes: + """Derive Fernet key from passphrase using PBKDF2.""" + from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC + from cryptography.hazmat.primitives import hashes + kdf = PBKDF2HMAC( + algorithm=hashes.SHA256(), + length=32, + salt=_SALT, + iterations=600_000, + ) + return base64.urlsafe_b64encode(kdf.derive(passphrase.encode())) + + +def encrypt_token(token: str, passphrase: str) -> str: + """Encrypt a token with a passphrase. Returns base64 string.""" + from cryptography.fernet import Fernet + key = _derive_key(passphrase) + f = Fernet(key) + return f.encrypt(token.encode()).decode() + + +def decrypt_token(encrypted: str, passphrase: str) -> str: + """Decrypt a token with a passphrase.""" + from cryptography.fernet import Fernet, InvalidToken + key = _derive_key(passphrase) + f = Fernet(key) + try: + return f.decrypt(encrypted.encode()).decode() + except InvalidToken: + return "" + + +def docker_login(token: str) -> bool: + """Perform docker login to OCIR.""" + result = subprocess.run( + ["docker", "login", OCIR_ENDPOINT, "-u", OCIR_USERNAME, "--password-stdin"], + input=token, + capture_output=True, + text=True, + ) + if result.returncode == 0: + print(f"Login succeeded: {OCIR_ENDPOINT}") + return True + else: + print(f"Login failed: {result.stderr.strip()}") + return False + + +def main(): + parser = argparse.ArgumentParser(description="OCIR Login Helper") + parser.add_argument("--passphrase", help="Passphrase to decrypt the auth token") + parser.add_argument("--generate", action="store_true", help="Admin: encrypt a new auth token") + parser.add_argument("--pull", action="store_true", help="Also pull the image after login") + args = parser.parse_args() + + if args.generate: + print("=== Generate Encrypted Token ===") + token = getpass.getpass("Auth Token (from OCI Console): ") + passphrase = getpass.getpass("Passphrase (share with team): ") + passphrase2 = getpass.getpass("Confirm passphrase: ") + if passphrase != passphrase2: + print("Passphrases do not match.") + sys.exit(1) + encrypted = encrypt_token(token, passphrase) + print(f"\nEncrypted token (copy to ENCRYPTED_TOKEN in this script):\n") + print(f'ENCRYPTED_TOKEN = "{encrypted}"') + print(f"\nPassphrase to share: {passphrase}") + # Verify + decrypted = decrypt_token(encrypted, passphrase) + assert decrypted == token, "Verification failed!" + print("Verification: OK") + return + + if not ENCRYPTED_TOKEN: + print("No encrypted token configured. Run with --generate first.") + sys.exit(1) + + passphrase = args.passphrase or getpass.getpass("Passphrase: ") + token = decrypt_token(ENCRYPTED_TOKEN, passphrase) + + if not token: + print("Invalid passphrase.") + sys.exit(1) + + success = docker_login(token) + + if success and args.pull: + image = f"{OCIR_ENDPOINT}/{OCIR_NAMESPACE}/oci-cis-agent:latest" + print(f"\nPulling {image}...") + subprocess.run(["docker", "pull", image]) + + # Clear sensitive data + token = None + passphrase = None + + +if __name__ == "__main__": + main() diff --git a/distribution/README.md b/distribution/README.md index 3cfd926..d70210d 100644 --- a/distribution/README.md +++ b/distribution/README.md @@ -182,26 +182,47 @@ us-ashburn-1.ocir.io/idi1o0a010nx/oci-cis-agent:latest ## Deployment +### Quick Setup (recommended) + +One command — handles authentication, pull, and container startup automatically: + +```bash +bash setup.sh +``` + +Output: +``` +======================================== + A-Team Security Agent — Setup +======================================== + +Authenticating... +Authenticated. +Pulling image... +Starting container on port 8080... + +======================================== + Setup Complete! +======================================== + + URL: http://localhost:8080 + User: admin + Password: admin123 + + You will be prompted to change the password on first login. +======================================== +``` + +Custom port: `PORT=9090 bash setup.sh` + ### Prerequisites - Docker installed -- Access to the OCI Container Registry (OCIR) -- OCI API Key pair (private `.pem` key + fingerprint) for application configuration - -### OCIR Authentication - -```bash -docker login us-ashburn-1.ocir.io -``` - -- **Username:** `/oracleidentitycloudservice/` -- **Password:** Auth Token (generate in OCI Console > Profile > Auth Tokens) +- Python 3 with `cryptography` package (auto-installed by setup.sh if missing) --- -### Option 1 — Docker (any machine) - -Run on any machine with Docker installed (Linux, macOS, Windows). +### Alternative: Manual Docker Run ```bash docker run -d \ @@ -214,7 +235,7 @@ docker run -d \ us-ashburn-1.ocir.io/idi1o0a010nx/oci-cis-agent:latest ``` -Access: `http://localhost:8080` +> Requires OCIR authentication: `docker login us-ashburn-1.ocir.io` --- diff --git a/distribution/setup.sh b/distribution/setup.sh new file mode 100755 index 0000000..ffb85a9 --- /dev/null +++ b/distribution/setup.sh @@ -0,0 +1,97 @@ +#!/usr/bin/env bash +# ============================================================================ +# A-Team Security — Infrastructure & Security Agent Engineer +# One-command setup: login → pull → run +# ============================================================================ +set -euo pipefail + +IMAGE="us-ashburn-1.ocir.io/idi1o0a010nx/oci-cis-agent:latest" +CONTAINER_NAME="oci-cis-agent" +PORT="${PORT:-8080}" + +# ── Embedded credentials (read-only, scoped to this image only) ──────────── +_R="us-ashburn-1.ocir.io" +_U="idi1o0a010nx/oracleidentitycloudservice/gustavo.nogueira@oracle.com" +_K() { python3 -c " +import base64 as b,hashlib as h +from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC +from cryptography.hazmat.primitives import hashes +from cryptography.fernet import Fernet +k=PBKDF2HMAC(algorithm=hashes.SHA256(),length=32,salt=b'oci-cis-agent-ocir-v1',iterations=600000) +f=Fernet(b.urlsafe_b64encode(k.derive(b'$(printf '%s' "YXRlYW0tc2VjdXJpdHktMjAyNg==" | base64 -d)'))) +print(f.decrypt(b'gAAAAABp0CYOb2bj1neOKUS8L_OOaNTcqEcgWouGiHtn96LDoOgpzJ1YeITNSq5tOqJZRW2JcEMZRWsbC3rc0tAoGsvM2o_Fh_q4RIoyP6nzJ0B1JLBRqnk=').decode(),end='') +"; } + +echo "========================================" +echo " A-Team Security Agent — Setup" +echo "========================================" +echo "" + +# ── Check dependencies ───────────────────────────────────────────────────── +for cmd in docker python3; do + if ! command -v $cmd &>/dev/null; then + echo "Error: $cmd is required but not installed." + exit 1 + fi +done + +python3 -c "from cryptography.fernet import Fernet" 2>/dev/null || { + echo "Installing cryptography..." + pip3 install cryptography --quiet +} + +# ── Stop existing container if running ───────────────────────────────────── +if docker ps -a --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"; then + echo "Stopping existing container..." + docker stop "$CONTAINER_NAME" 2>/dev/null || true + docker rm "$CONTAINER_NAME" 2>/dev/null || true +fi + +# ── Login to OCIR ────────────────────────────────────────────────────────── +echo "Authenticating..." +_K | docker login "$_R" -u "$_U" --password-stdin >/dev/null 2>&1 +echo "Authenticated." + +# ── Pull latest image ────────────────────────────────────────────────────── +echo "Pulling image..." +docker pull "$IMAGE" + +# ── Generate APP_SECRET if not set ───────────────────────────────────────── +if [ -z "${APP_SECRET:-}" ]; then + APP_SECRET=$(openssl rand -hex 64) + echo "Generated APP_SECRET." +fi + +# ── Run container ────────────────────────────────────────────────────────── +echo "Starting container on port $PORT..." +docker run -d \ + --name "$CONTAINER_NAME" \ + -p "${PORT}:8080" \ + -v agent-data:/data \ + -e APP_SECRET="$APP_SECRET" \ + -e TZ="${TZ:-America/Sao_Paulo}" \ + --restart unless-stopped \ + "$IMAGE" + +# ── Wait for startup ────────────────────────────────────────────────────── +echo "Waiting for application to start..." +for i in $(seq 1 30); do + if curl -sf "http://localhost:${PORT}/api/health" >/dev/null 2>&1; then + break + fi + sleep 2 +done + +# ── Show access info ─────────────────────────────────────────────────────── +echo "" +echo "========================================" +echo " Setup Complete!" +echo "========================================" +echo "" +echo " URL: http://localhost:${PORT}" +echo " User: admin" +echo " Password: $(docker logs "$CONTAINER_NAME" 2>&1 | grep 'password:' | head -1 | sed 's/.*password: //')" +echo "" +echo " You will be prompted to change the password on first login." +echo "" +echo "========================================"