From 70be2024f8fdf1bba54437515c98e41904445ba3 Mon Sep 17 00:00:00 2001 From: William Zujkowski Date: Tue, 21 Jul 2026 14:38:45 -0400 Subject: [PATCH 1/8] chore(local): add local Oracle dev/test harness Add a docker-compose-based local harness (dev signal only, never compliance evidence): moto for the RDS control-plane request-shape smoke, and gvenzl/oracle-free + oracle-19c for the SQL hardening/assessment loop, driven by a Makefile (doctor/quickstart/unit/moto-smoke). assess->harden->assess runs inside the container via docker exec (no host Instant Client). README documents the overlay-sibling-clone requirement. Security/hygiene: - All published ports bind to 127.0.0.1 only (Oracle 1521/1522, moto 5000) so the dev DB with its known local-only password is never reachable from the LAN/VPN. - moto image pinned to 5.2.2 (was :latest) for reproducibility. - local/.gitignore ignores generated reports/ (dev-signal output, never commit) and force-tracks init/*.sql past the global *.sql ignore; the referenced init seed scripts are now included so oracle-up actually seeds the non-SYS user + weak state the README describes. --- local/.gitignore | 12 +++ local/Makefile | 134 +++++++++++++++++++++++ local/README.md | 150 ++++++++++++++++++++++++++ local/docker-compose.moto.yml | 14 +++ local/docker-compose.oracle-19c.yml | 23 ++++ local/docker-compose.oracle-free.yml | 24 +++++ local/init/00_create_test_users.sql | 23 ++++ local/init/01_seed_insecure_state.sql | 22 ++++ local/scripts/moto-smoke.sh | 69 ++++++++++++ local/scripts/run-assessment-local.sh | 52 +++++++++ local/scripts/run-hardening-local.sh | 56 ++++++++++ local/scripts/wait-for-oracle.sh | 21 ++++ 12 files changed, 600 insertions(+) create mode 100644 local/.gitignore create mode 100644 local/Makefile create mode 100644 local/README.md create mode 100644 local/docker-compose.moto.yml create mode 100644 local/docker-compose.oracle-19c.yml create mode 100644 local/docker-compose.oracle-free.yml create mode 100644 local/init/00_create_test_users.sql create mode 100644 local/init/01_seed_insecure_state.sql create mode 100755 local/scripts/moto-smoke.sh create mode 100755 local/scripts/run-assessment-local.sh create mode 100755 local/scripts/run-hardening-local.sh create mode 100755 local/scripts/wait-for-oracle.sh diff --git a/local/.gitignore b/local/.gitignore new file mode 100644 index 00000000..12b09c73 --- /dev/null +++ b/local/.gitignore @@ -0,0 +1,12 @@ +# Local dev harness — ignore generated output + un-ignore the seed SQL. +# +# A global ~/.gitignore *.sql rule (guards go:embed) would otherwise hide the +# init seed scripts, and assessment/hardening output must never be committed +# (it is development signal only, and can contain live DB output). + +# Generated assessment/hardening output — never commit (dev signal only). +reports/ + +# Force-track the seed SQL despite the global *.sql ignore. +!init/ +!init/*.sql diff --git a/local/Makefile b/local/Makefile new file mode 100644 index 00000000..9bb8523c --- /dev/null +++ b/local/Makefile @@ -0,0 +1,134 @@ +# Local Oracle 19c test harness. +# DEVELOPMENT SIGNAL ONLY — not compliance evidence. +# +# All targets are local Docker + go test; nothing here touches AWS or production. +# New here? Run `make -C local doctor` then `make -C local quickstart`. + +COMPOSE_ORACLE = docker compose -f docker-compose.oracle-free.yml +COMPOSE_ORACLE19C = docker compose -f docker-compose.oracle-19c.yml +COMPOSE_MOTO = docker compose -f docker-compose.moto.yml + +ORACLE_CONN ?= APPUSER/devpw_ChangeMe1@//localhost:1521/FREEPDB1 +# Repo root (this Makefile lives in local/). +ROOT = .. + +.PHONY: help +help: quickstart + +.PHONY: quickstart +quickstart: + @echo "Local Oracle 19c test harness — 3 layers (development signal only):" + @echo "" + @echo " 0. make doctor check your machine has the prerequisites" + @echo "" + @echo " 1. UNIT TESTS (fast; no Docker) — the everyday loop:" + @echo " make unit go test ./... with test secrets/catalog wired" + @echo "" + @echo " 2. BROKER FLOW vs a mock AWS RDS control plane (moto):" + @echo " make moto-up start moto on :5000" + @echo " make moto-smoke create an Oracle instance/param+option group via the AWS API" + @echo " make moto-down stop moto" + @echo "" + @echo " 3. REAL LOCAL ORACLE for SQL hardening/assessment:" + @echo " make oracle-up start gvenzl/oracle-free (native arm64), wait healthy" + @echo " make assess run assessment SQL -> reports/ (labeled dev signal)" + @echo " make harden apply allowed hardening (idempotent)" + @echo " make assess re-assess: state should have changed" + @echo " make oracle19c-up (optional) self-built Oracle 19c EE fidelity image" + @echo "" + @echo " make down tear EVERYTHING down" + @echo "" + @echo "See README.md for the full from-a-clean-laptop guide." + +# --------------------------------------------------------------------------- +# 0. Prerequisite check +# --------------------------------------------------------------------------- +.PHONY: doctor +doctor: + @echo "== Local test harness prerequisites (macOS arm64) ==" + @ok=1; \ + printf "%-22s" "docker:"; \ + if command -v docker >/dev/null 2>&1; then \ + if docker info >/dev/null 2>&1; then echo "OK (daemon running)"; \ + else echo "INSTALLED but daemon NOT running — start Docker Desktop/Colima"; ok=0; fi; \ + else echo "MISSING — install Docker Desktop or 'brew install colima docker && colima start'"; ok=0; fi; \ + printf "%-22s" "go:"; \ + if command -v go >/dev/null 2>&1; then go version | awk '{print "OK ("$$3")"}'; \ + else echo "MISSING — 'brew install go' (needed for layer 1 unit tests)"; ok=0; fi; \ + printf "%-22s" "aws (cli):"; \ + if command -v aws >/dev/null 2>&1; then echo "OK (layer 2 moto smoke)"; \ + else echo "optional — 'brew install awscli' (only for 'make moto-smoke')"; fi; \ + printf "%-22s" "cinc-auditor:"; \ + if command -v cinc-auditor >/dev/null 2>&1; then echo "OK (overlay InSpec run)"; \ + else echo "optional — 'brew install --cask cinc-auditor' (only to run the STIG overlay locally)"; fi; \ + printf "%-22s" "sqlplus:"; \ + if command -v sqlplus >/dev/null 2>&1; then echo "OK"; \ + else echo "not needed — 'make assess/harden' run sqlplus INSIDE the container"; fi; \ + printf "%-22s" "overlay repo:"; \ + if [ -d ../../cg-oracle-database-19c-stig-overlay/hardening/sql ]; then echo "OK (sibling clone found)"; \ + else echo "MISSING for layer 3 — clone cg-oracle-database-19c-stig-overlay as a sibling of aws-broker (or set SQL_DIR=)"; fi; \ + echo ""; \ + if [ "$$ok" = "1" ]; then echo "Ready for layers 1-2. Layer 3 also needs the overlay sibling clone (see above)."; \ + else echo "Install the MISSING items above, then re-run 'make doctor'."; exit 1; fi + +# --------------------------------------------------------------------------- +# 1. Unit tests (no Docker). Wires the test secrets/catalog the suite needs. +# --------------------------------------------------------------------------- +.PHONY: unit +unit: + @echo "Wiring test config (secrets-test.yml/catalog-test.yml -> secrets.yml/catalog.yml)..." + cp $(ROOT)/secrets-test.yml $(ROOT)/secrets.yml + cp $(ROOT)/catalog-test.yml $(ROOT)/catalog.yml + cp $(ROOT)/secrets-test.yml $(ROOT)/cmd/tasks/secrets.yml + cp $(ROOT)/catalog-test.yml $(ROOT)/cmd/tasks/catalog.yml + cd $(ROOT) && go test ./... + cd $(ROOT)/cmd/tasks && go test ./... + @echo "unit tests passed (secrets.yml/catalog.yml are gitignored, left in place)." + +.PHONY: unit-oracle +unit-oracle: + cp $(ROOT)/secrets-test.yml $(ROOT)/secrets.yml + cp $(ROOT)/catalog-test.yml $(ROOT)/catalog.yml + cd $(ROOT) && go test ./services/rds/... -run Oracle -v + +# --------------------------------------------------------------------------- +# 2. Broker control-flow vs moto (mock AWS RDS control plane) +# --------------------------------------------------------------------------- +.PHONY: moto-up +moto-up: + $(COMPOSE_MOTO) up -d + @echo "moto up on http://localhost:5000 (point the AWS SDK/CLI at it via --endpoint-url / BaseEndpoint)." + +.PHONY: moto-smoke +moto-smoke: + ./scripts/moto-smoke.sh + +.PHONY: moto-down +moto-down: + -$(COMPOSE_MOTO) down -v + +# --------------------------------------------------------------------------- +# 3. Real local Oracle (gvenzl/oracle-free, native arm64) for SQL hardening +# --------------------------------------------------------------------------- +.PHONY: oracle-up +oracle-up: + $(COMPOSE_ORACLE) up -d + ./scripts/wait-for-oracle.sh + +.PHONY: oracle19c-up +oracle19c-up: + $(COMPOSE_ORACLE19C) up -d + +.PHONY: assess +assess: + ./scripts/run-assessment-local.sh "$(ORACLE_CONN)" + +.PHONY: harden +harden: + ./scripts/run-hardening-local.sh "$(ORACLE_CONN)" + +.PHONY: down +down: + -$(COMPOSE_ORACLE) down -v + -$(COMPOSE_ORACLE19C) down -v + -$(COMPOSE_MOTO) down -v diff --git a/local/README.md b/local/README.md new file mode 100644 index 00000000..66a52777 --- /dev/null +++ b/local/README.md @@ -0,0 +1,150 @@ +# Local Oracle 19c test harness + +> **⚠️ DEVELOPMENT SIGNAL ONLY — NOT COMPLIANCE EVIDENCE.** +> Everything here is for fast iteration. Authoritative STIG evidence comes only +> from running `cg-oracle-database-19c-stig-overlay` against a real brokered +> GovCloud RDS instance (the gated dev proof). + +This lets you test the Oracle work **without any AWS access**, in three layers you +can use independently. Written for **macOS on Apple Silicon (arm64)**. + +## TL;DR + +```bash +make -C local doctor # 0. check prerequisites +make -C local unit # 1. Go unit tests (fast, no Docker) +make -C local moto-up moto-smoke moto-down # 2. broker flow vs mock AWS RDS +make -C local oracle-up assess harden assess # 3. real Oracle + SQL hardening +make -C local down # tear everything down +``` + +`make -C local quickstart` prints this menu any time. + +## 0. Prerequisites (macOS arm64) + +Run `make -C local doctor` — it tells you exactly what's present/missing and the +command to install each. Summary: + +| Requirement | Needed for | How | +|------|-----------|---------| +| **Docker** (running) | layers 2 & 3 | Docker Desktop, or `brew install colima docker && colima start` | +| **go** | layer 1 (unit tests) | `brew install go` | +| `aws` CLI | layer 2 `moto-smoke` only | `brew install awscli` | +| **`cg-oracle-database-19c-stig-overlay` cloned as a sibling** | layer 3 `assess`/`harden` | `git clone` it next to `aws-broker` (so `../../cg-oracle-database-19c-stig-overlay/hardening/sql` resolves), or pass `SQL_DIR=…` | +| `cinc-auditor` | running the STIG overlay locally (optional) | `brew install --cask cinc-auditor` | +| `sqlplus` | **not required** | `assess`/`harden` run `sqlplus` *inside* the container via `docker exec` | + +You do **not** need Oracle Instant Client or `sqlplus` on your Mac — layer 3 runs +`sqlplus` inside the `cg-oracle-free` container. Your clone layout for layer 3: + +``` +git/…/ + aws-broker/ (this repo) + cg-oracle-database-19c-stig-overlay/ (sibling — provides hardening/sql/) +``` + +## 1. Unit tests (fast, no Docker) — the everyday loop + +```bash +make -C local unit # go test ./... + cmd/tasks, with test config wired +make -C local unit-oracle # just the Oracle-tagged tests, verbose +``` + +The broker's test suite needs `secrets.yml`/`catalog.yml` present; `make unit` +copies them from the committed `*-test.yml` files first (they're gitignored, so +this is safe and idempotent). This is the layer to run while editing Go code. + +## 2. Broker flow vs a mock AWS RDS control plane (moto) + +[moto](https://docs.getmoto.org/) mocks the AWS RDS **control plane** so the broker's +create / parameter-group / option-group calls can be exercised with **no real AWS**. + +```bash +make -C local moto-up # start moto on http://localhost:5000 +make -C local moto-smoke # create an Oracle instance + param + option group via the AWS API +make -C local moto-down +``` + +`moto-smoke` asserts the AWS API **accepts** the exact shape the broker builds +(`oracle-se2` 19c, encrypted, License Included, private; the `oracle-se2-19` +parameter group; the option group). To point the broker/tests themselves at moto, +override the AWS SDK endpoint (`--endpoint-url http://localhost:5000` for the CLI, +or `BaseEndpoint` in the Go client). + +> moto does **not** run an Oracle engine, apply parameters, or do a TLS handshake — +> it proves request shape, not RDS behavior. + +## 3. Real local Oracle for SQL hardening/assessment + +[`gvenzl/oracle-free`](https://github.com/gvenzl/oci-oracle-free) runs a **real +Oracle engine natively on arm64** — used to develop and idempotency-test the SQL +hardening/assessment scripts and to run the overlay's `oracledb_session` controls. +Requires the overlay repo cloned as a sibling (see Prerequisites); `sqlplus` runs +inside the container, so nothing extra on your Mac. + +```bash +make -C local oracle-up # start oracle-free, wait until healthy (first pull ~mins) +make -C local assess # run assessment SQL -> local/reports/ (labeled dev signal) +make -C local harden # apply allowed hardening (idempotent) +make -C local assess # re-assess: the DEFAULT profile limits + unified audit + # policies now report [PASS] in 90_validate +``` + +The container auto-seeds (from `init/`) a **non-`SYS`** privileged app user that +mirrors the RDS master-user privilege model, plus a deliberately-weak state. Note +`harden` only touches the DEFAULT profile + audit policies (10/20/30); the +PUBLIC-grant + network checks (40/50) are **detect-only** and never auto-remediate, +and the seeded `weak_profile`/`seed_weak` artifacts are on a non-DEFAULT profile +that hardening intentionally leaves alone. Reports land in `local/reports/` +(gitignored), each labeled *development signal only*. + +**Optional fidelity pass** — a self-built Oracle **19c EE** image (closer to the +brokered engine than 23c Free). You must build/tag `oracle/database:19.3.0-ee` +yourself from [oracle/docker-images](https://github.com/oracle/docker-images) +first, then `make -C local oracle19c-up`. It publishes on **1522** with service +`ORCLPDB1` and does **not** create `APPUSER`, so target it explicitly, e.g. +`make -C local assess ORACLE_CONN="SYS/@//localhost:1522/ORCLPDB1 as sysdba"` +(this fidelity path is for maintainers; the default `oracle-free` flow above is the +supported one). + +**Optional — run the STIG overlay** against the local DB (needs `cinc-auditor`): +see the overlay repo's `README.md`; point its `oracledb_session` inputs at +`localhost:1521/FREEPDB1` with the seeded app user. Local overlay results are dev +signal only. + +## What local CANNOT tell you (by design) + +- RDS parameter-group / option-group **effects** (moto only checks the API call is + made; it doesn't apply anything). +- **TLS/TCPS 2484**, KMS encryption, CloudWatch log exports, GovCloud + networking/partition, the RDS reboot/maintenance model. +- The exact RDS **privilege model** (`oracle-free` is 23c and grants more than RDS's + master user — develop as the seeded non-SYS user to surface RDS-only failures). + +All of the above is validated only on a live GovCloud RDS instance. + +## Layout + +``` +local/ + README.md (this file) + Makefile doctor / unit / moto-* / oracle-* / down + docker-compose.moto.yml motoserver/moto (free RDS control-plane mock) + docker-compose.oracle-free.yml gvenzl/oracle-free (native arm64) + docker-compose.oracle-19c.yml self-built oracle/database:19.3.0-ee (fidelity) + scripts/ + wait-for-oracle.sh + moto-smoke.sh layer-2 broker-shape smoke + run-assessment-local.sh + run-hardening-local.sh + init/ + 00_create_test_users.sql non-SYS privileged app user (mirrors RDS) + 01_seed_insecure_state.sql deliberately-weak state for detection tests + reports/ (gitignored) assessment/hardening output +``` + +The **authoritative SQL hardening scripts** live in the overlay repo +(`hardening/sql/`, kept out of the broker so the broker never runs STIG +validation itself); +this harness runs them for a fast local loop and is never wired into the broker +runtime. diff --git a/local/docker-compose.moto.yml b/local/docker-compose.moto.yml new file mode 100644 index 00000000..75e86f65 --- /dev/null +++ b/local/docker-compose.moto.yml @@ -0,0 +1,14 @@ +# Free AWS RDS control-plane mock for broker flow tests. +# moto is control-plane ONLY (no Oracle engine). Point the broker/tests at +# http://localhost:5000 via the aws-sdk-go-v2 rds BaseEndpoint override. +# Known gap: moto does not implement create_db_subnet_group (stub that path). +services: + moto: + image: motoserver/moto:5.2.2 + container_name: cg-moto-rds + ports: + # Bind to loopback only — keep the unauthenticated moto control plane off + # the LAN. Do NOT change to "5000:5000" (0.0.0.0). + - "127.0.0.1:5000:5000" + environment: + MOTO_PORT: "5000" diff --git a/local/docker-compose.oracle-19c.yml b/local/docker-compose.oracle-19c.yml new file mode 100644 index 00000000..a934819a --- /dev/null +++ b/local/docker-compose.oracle-19c.yml @@ -0,0 +1,23 @@ +# Fidelity Oracle 19c EE for a periodic hardening parity pass. +# NOTE: no public 19c image exists — build it once from oracle/docker-images +# (LINUX.ARM64_1919000_db_home.zip, `buildContainerImage.sh -v 19.3.0 -e`) and +# tag it oracle/database:19.3.0-ee. DEVELOPMENT SIGNAL ONLY. +services: + oracle19c: + image: oracle/database:19.3.0-ee + container_name: cg-oracle-19c-ee + ports: + # Bind to loopback only — a local dev DB with a known password must not be + # reachable from the LAN/VPN. Do NOT change to "1522:1521" (0.0.0.0). + - "127.0.0.1:1522:1521" + environment: + ORACLE_PWD: "devpw_ChangeMe1" + ORACLE_SID: "ORCLCDB" + ORACLE_PDB: "ORCLPDB1" + volumes: + - ./init:/opt/oracle/scripts/startup:ro + healthcheck: + test: ["CMD", "sqlplus", "-L", "sys/devpw_ChangeMe1@//localhost:1521/ORCLCDB", "as", "sysdba", "@/dev/null"] + interval: 30s + timeout: 10s + retries: 40 diff --git a/local/docker-compose.oracle-free.yml b/local/docker-compose.oracle-free.yml new file mode 100644 index 00000000..a1dd6c6e --- /dev/null +++ b/local/docker-compose.oracle-free.yml @@ -0,0 +1,24 @@ +# Local Oracle engine for fast SQL-hardening iteration. +# gvenzl/oracle-free is native arm64 (Apple Silicon) with a faststart tag. +# DEVELOPMENT SIGNAL ONLY: 23c Free, not RDS Oracle 19c EE. +services: + oracle: + image: gvenzl/oracle-free:23-slim-faststart + container_name: cg-oracle-free + ports: + # Bind to loopback only — a local dev DB with a known password must not be + # reachable from the LAN/VPN. Do NOT change to "1521:1521" (0.0.0.0). + - "127.0.0.1:1521:1521" + environment: + # Dev-only password; never used outside this local harness. + ORACLE_PASSWORD: "devpw_ChangeMe1" + # Create a dedicated app PDB user on first boot. + APP_USER: "APPUSER" + APP_USER_PASSWORD: "devpw_ChangeMe1" + volumes: + - ./init:/container-entrypoint-initdb.d:ro + healthcheck: + test: ["CMD", "healthcheck.sh"] + interval: 10s + timeout: 5s + retries: 30 diff --git a/local/init/00_create_test_users.sql b/local/init/00_create_test_users.sql new file mode 100644 index 00000000..36537126 --- /dev/null +++ b/local/init/00_create_test_users.sql @@ -0,0 +1,23 @@ +-- 00_create_test_users.sql — local harness only. +-- DEVELOPMENT SIGNAL ONLY. +-- +-- Creates a NON-SYS privileged application user that mirrors the RDS master-user +-- privilege model (RDS does not grant SYS/SYSDBA). Hardening scripts are developed +-- and tested as this user so RDS-only permission failures surface locally. +-- +-- gvenzl/oracle-free already creates APPUSER via APP_USER env; this adds the +-- privileged-but-not-SYS role set an RDS master user typically has. + +ALTER SESSION SET CONTAINER = FREEPDB1; + +-- Grant the RDS-master-like privilege set (NOT SYSDBA). +GRANT CREATE SESSION TO APPUSER; +GRANT CREATE USER, ALTER USER, DROP USER TO APPUSER; +GRANT CREATE ROLE, GRANT ANY ROLE TO APPUSER; +GRANT CREATE PROFILE, ALTER PROFILE, DROP PROFILE TO APPUSER; +GRANT SELECT ON SYS.DBA_USERS TO APPUSER; +GRANT SELECT ON SYS.DBA_PROFILES TO APPUSER; +GRANT SELECT ON SYS.DBA_ROLE_PRIVS TO APPUSER; +GRANT SELECT ON SYS.DBA_SYS_PRIVS TO APPUSER; +GRANT SELECT ON SYS.DBA_TAB_PRIVS TO APPUSER; +GRANT AUDIT_ADMIN TO APPUSER; diff --git a/local/init/01_seed_insecure_state.sql b/local/init/01_seed_insecure_state.sql new file mode 100644 index 00000000..2c3c6c41 --- /dev/null +++ b/local/init/01_seed_insecure_state.sql @@ -0,0 +1,22 @@ +-- 01_seed_insecure_state.sql — local harness only. +-- DEVELOPMENT SIGNAL ONLY. +-- +-- Deliberately creates a WEAK state so the assessment scripts have something to +-- detect and the hardening scripts have something to remediate. Never run outside +-- the local throwaway container. + +ALTER SESSION SET CONTAINER = FREEPDB1; + +-- Weak profile: unlimited failed logins + no password expiry (STIG findings). +CREATE PROFILE weak_profile LIMIT + FAILED_LOGIN_ATTEMPTS UNLIMITED + PASSWORD_LIFE_TIME UNLIMITED; + +-- A user on the weak profile. +CREATE USER seed_weak IDENTIFIED BY "devpw_ChangeMe1" PROFILE weak_profile; +GRANT CREATE SESSION TO seed_weak; + +-- Over-privileged grant to PUBLIC (classic STIG finding; detect-first, never +-- auto-revoked by hardening without an explicit allowlist). +-- (Left as a comment: granting to PUBLIC on a real system is dangerous; the +-- assessment detects existing PUBLIC grants rather than us creating one here.) diff --git a/local/scripts/moto-smoke.sh b/local/scripts/moto-smoke.sh new file mode 100755 index 00000000..dee1e831 --- /dev/null +++ b/local/scripts/moto-smoke.sh @@ -0,0 +1,69 @@ +#!/usr/bin/env bash +# moto-smoke.sh — exercise the broker's Oracle RDS control-plane calls against the +# moto mock (layer 2). Confirms the AWS API ACCEPTS the shape the broker builds: +# create-db-instance (oracle-se2 19c, encrypted, License Included, private) + +# create-db-parameter-group (oracle-se2-19) + create-option-group (oracle-se2 19). +# +# DEVELOPMENT SIGNAL ONLY: moto mocks the control plane — it does NOT run +# an Oracle engine, apply parameters, or do a real TLS handshake. This proves the +# request shape, not RDS behavior. Requires: `make moto-up` first, and the aws CLI. +set -euo pipefail + +# aws flags for the moto endpoint (array so it word-splits cleanly). +EP=(--endpoint-url http://localhost:5000 --region us-gov-west-1) +export AWS_ACCESS_KEY_ID=test AWS_SECRET_ACCESS_KEY=test +ID="cg-oracle-smoke-$$" +PG="cg-aws-broker-oracle-smoke-$$" +OG="cg-aws-broker-oracle-smoke-og-$$" + +if ! command -v aws >/dev/null 2>&1; then + echo "aws CLI not found — 'brew install awscli' (layer-2 smoke only)." >&2 + exit 2 +fi +if ! curl -sf http://localhost:5000/moto-api/ >/dev/null 2>&1; then + echo "moto is not up on :5000 — run 'make moto-up' first." >&2 + exit 2 +fi + +cleanup() { + aws "${EP[@]}" rds delete-db-instance --db-instance-identifier "$ID" --skip-final-snapshot >/dev/null 2>&1 || true + aws "${EP[@]}" rds delete-db-parameter-group --db-parameter-group-name "$PG" >/dev/null 2>&1 || true + aws "${EP[@]}" rds delete-option-group --option-group-name "$OG" >/dev/null 2>&1 || true +} +trap cleanup EXIT + +echo "== create-db-instance (oracle-se2, License Included, encrypted, private) ==" +aws "${EP[@]}" rds create-db-instance \ + --db-instance-identifier "$ID" \ + --db-instance-class db.t3.medium \ + --engine oracle-se2 \ + --engine-version 19.0.0.0.ru-2024-07.rur-2024-07.r1 \ + --license-model license-included \ + --allocated-storage 20 \ + --master-username APPUSER01 \ + --master-user-password "fakePw12345" \ + --db-name ORCL \ + --storage-encrypted \ + --no-publicly-accessible \ + --backup-retention-period 14 \ + --query 'DBInstance.[DBInstanceIdentifier,Engine,EngineVersion,StorageEncrypted,LicenseModel,PubliclyAccessible]' \ + --output text + +echo "== create-db-parameter-group (oracle-se2-19) ==" +aws "${EP[@]}" rds create-db-parameter-group \ + --db-parameter-group-name "$PG" \ + --db-parameter-group-family oracle-se2-19 \ + --description "cg smoke" \ + --query 'DBParameterGroup.[DBParameterGroupName,DBParameterGroupFamily]' --output text + +echo "== create-option-group (oracle-se2 19) ==" +aws "${EP[@]}" rds create-option-group \ + --option-group-name "$OG" \ + --engine-name oracle-se2 \ + --major-engine-version 19 \ + --option-group-description "cg smoke" \ + --query 'OptionGroup.[OptionGroupName,EngineName,MajorEngineVersion]' --output text + +echo "" +echo "moto smoke OK — the AWS API accepted the broker's Oracle create/param/option shapes." +echo "(development signal only: moto did not run Oracle, apply params, or do TLS.)" diff --git a/local/scripts/run-assessment-local.sh b/local/scripts/run-assessment-local.sh new file mode 100755 index 00000000..5b5b3ba0 --- /dev/null +++ b/local/scripts/run-assessment-local.sh @@ -0,0 +1,52 @@ +#!/usr/bin/env bash +# run-assessment-local.sh — run the STIG assessment SQL against the local Oracle +# container and write a clearly-labeled report. Local harness only. +# +# ⚠️ DEVELOPMENT SIGNAL ONLY — NOT COMPLIANCE EVIDENCE. +# Authoritative evidence comes from cg-oracle-database-19c-stig-overlay run against +# a real brokered GovCloud RDS Oracle instance. +# +# Runs sqlplus INSIDE the oracle-free container (docker exec), so no Oracle Instant +# Client / sqlplus is needed on the host. +set -euo pipefail + +CONN="${1:?usage: run-assessment-local.sh }" +CONTAINER="${ORACLE_CONTAINER:-cg-oracle-free}" +SQL_DIR="${SQL_DIR:-../../cg-oracle-database-19c-stig-overlay/hardening/sql}" +OUT_DIR="reports" +STAMP="$(date -u +%Y%m%dT%H%M%SZ)" +OUT="${OUT_DIR}/assessment-${STAMP}.log" + +mkdir -p "${OUT_DIR}" +{ + echo "==============================================================" + echo " DEVELOPMENT SIGNAL ONLY — NOT COMPLIANCE EVIDENCE" + echo " local Oracle assessment @ ${STAMP}" + echo "==============================================================" +} | tee "${OUT}" + +if ! docker ps --format '{{.Names}}' | grep -qx "${CONTAINER}"; then + echo "oracle container '${CONTAINER}' is not running — run 'make oracle-up' first." | tee -a "${OUT}" + exit 2 +fi +if [ ! -d "${SQL_DIR}" ]; then + echo "assessment SQL dir not found: ${SQL_DIR}" | tee -a "${OUT}" + echo "(clone cg-oracle-database-19c-stig-overlay as a SIBLING of aws-broker, or set SQL_DIR=)" | tee -a "${OUT}" + exit 2 +fi + +# Stage the SQL into the container in a fresh, world-readable dir (docker cp +# preserves host perms/owner, so reset them for the container's oracle user). +docker exec -u 0 "${CONTAINER}" rm -rf /tmp/hardening-sql >/dev/null 2>&1 || true +docker cp "${SQL_DIR}/." "${CONTAINER}:/tmp/hardening-sql" >/dev/null +docker exec -u 0 "${CONTAINER}" chmod -R a+rX /tmp/hardening-sql >/dev/null 2>&1 || true + +# Run only assessment-first scripts (00_*, 01_*, *_assess.sql, 90_validate.sql). +shopt -s nullglob +for f in "${SQL_DIR}"/00_*.sql "${SQL_DIR}"/01_*.sql "${SQL_DIR}"/*_assess.sql "${SQL_DIR}"/90_validate.sql; do + base="$(basename "${f}")" + echo ">>> ${base}" | tee -a "${OUT}" + docker exec -i "${CONTAINER}" bash -lc "cd /tmp/hardening-sql && sqlplus -S '${CONN}' @'${base}'" | tee -a "${OUT}" +done + +echo "assessment written to ${OUT} (development signal only)" diff --git a/local/scripts/run-hardening-local.sh b/local/scripts/run-hardening-local.sh new file mode 100755 index 00000000..493807ac --- /dev/null +++ b/local/scripts/run-hardening-local.sh @@ -0,0 +1,56 @@ +#!/usr/bin/env bash +# run-hardening-local.sh — apply the ALLOWED hardening SQL against local Oracle. +# Local harness only. +# +# ⚠️ DEVELOPMENT SIGNAL ONLY. Hardening is assessment-first + idempotent; +# destructive / PUBLIC-grant changes are detect-first and require an explicit +# allowlist (they are NOT applied automatically). RDS-incompatible commands skip +# with a reason. Develop as a NON-SYS user (see init/00_create_test_users.sql). +# +# Runs sqlplus INSIDE the oracle-free container (docker exec), so no Oracle Instant +# Client / sqlplus is needed on the host. +set -euo pipefail + +CONN="${1:?usage: run-hardening-local.sh }" +CONTAINER="${ORACLE_CONTAINER:-cg-oracle-free}" +SQL_DIR="${SQL_DIR:-../../cg-oracle-database-19c-stig-overlay/hardening/sql}" +OUT_DIR="reports" +STAMP="$(date -u +%Y%m%dT%H%M%SZ)" +OUT="${OUT_DIR}/hardening-${STAMP}.log" + +mkdir -p "${OUT_DIR}" +{ + echo "==============================================================" + echo " DEVELOPMENT SIGNAL ONLY — NOT COMPLIANCE EVIDENCE" + echo " local Oracle hardening @ ${STAMP}" + echo "==============================================================" +} | tee "${OUT}" + +if ! docker ps --format '{{.Names}}' | grep -qx "${CONTAINER}"; then + echo "oracle container '${CONTAINER}' is not running — run 'make oracle-up' first." | tee -a "${OUT}" + exit 2 +fi +if [ ! -d "${SQL_DIR}" ]; then + echo "hardening SQL dir not found: ${SQL_DIR}" | tee -a "${OUT}" + echo "(clone cg-oracle-database-19c-stig-overlay as a SIBLING of aws-broker, or set SQL_DIR=)" | tee -a "${OUT}" + exit 2 +fi + +# Stage the SQL into the container in a fresh, world-readable dir (docker cp +# preserves host perms/owner, so reset them for the container's oracle user). +docker exec -u 0 "${CONTAINER}" rm -rf /tmp/hardening-sql >/dev/null 2>&1 || true +docker cp "${SQL_DIR}/." "${CONTAINER}:/tmp/hardening-sql" >/dev/null +docker exec -u 0 "${CONTAINER}" chmod -R a+rX /tmp/hardening-sql >/dev/null 2>&1 || true + +# Apply remediation scripts (10_*..30_*), skipping assessment-only + rollback. +shopt -s nullglob +for f in "${SQL_DIR}"/10_*.sql "${SQL_DIR}"/20_*.sql "${SQL_DIR}"/30_*.sql; do + case "${f}" in + *_assess.sql) continue ;; + esac + base="$(basename "${f}")" + echo ">>> ${base}" | tee -a "${OUT}" + docker exec -i "${CONTAINER}" bash -lc "cd /tmp/hardening-sql && sqlplus -S '${CONN}' @'${base}'" | tee -a "${OUT}" +done + +echo "hardening applied; re-run 'make assess' to see the before/after (dev signal only)" diff --git a/local/scripts/wait-for-oracle.sh b/local/scripts/wait-for-oracle.sh new file mode 100755 index 00000000..caed1ccb --- /dev/null +++ b/local/scripts/wait-for-oracle.sh @@ -0,0 +1,21 @@ +#!/usr/bin/env bash +# wait-for-oracle.sh — block until the local Oracle container is healthy. +# Local harness only; development signal only. +set -euo pipefail + +CONTAINER="${1:-cg-oracle-free}" +TIMEOUT="${2:-300}" + +echo "Waiting up to ${TIMEOUT}s for ${CONTAINER} to become healthy..." +elapsed=0 +while true; do + status="$(docker inspect -f '{{.State.Health.Status}}' "${CONTAINER}" 2>/dev/null || echo "missing")" + case "${status}" in + healthy) echo "${CONTAINER} is healthy."; exit 0 ;; + missing) echo "container ${CONTAINER} not found"; exit 1 ;; + esac + if [ "${elapsed}" -ge "${TIMEOUT}" ]; then + echo "timed out waiting for ${CONTAINER} (last status: ${status})"; exit 1 + fi + sleep 5; elapsed=$((elapsed + 5)) +done From 5a89a39c58712e983ed3bb5810334699574d9f3a Mon Sep 17 00:00:00 2001 From: Peter Burkholder Date: Wed, 22 Jul 2026 13:11:23 -0400 Subject: [PATCH 2/8] refactor(local): use descriptive names for moto-smoke variables Rename terse variables in moto-smoke.sh for readability: EP -> MOTO_ENDPOINT, ID -> INSTANCE_ID, PG -> PARAM_GROUP, OG -> OPTION_GROUP. No behavioral change. --- local/scripts/moto-smoke.sh | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/local/scripts/moto-smoke.sh b/local/scripts/moto-smoke.sh index dee1e831..29a74f16 100755 --- a/local/scripts/moto-smoke.sh +++ b/local/scripts/moto-smoke.sh @@ -10,11 +10,11 @@ set -euo pipefail # aws flags for the moto endpoint (array so it word-splits cleanly). -EP=(--endpoint-url http://localhost:5000 --region us-gov-west-1) +MOTO_ENDPOINT=(--endpoint-url http://localhost:5000 --region us-gov-west-1) export AWS_ACCESS_KEY_ID=test AWS_SECRET_ACCESS_KEY=test -ID="cg-oracle-smoke-$$" -PG="cg-aws-broker-oracle-smoke-$$" -OG="cg-aws-broker-oracle-smoke-og-$$" +INSTANCE_ID="cg-oracle-smoke-$$" +PARAM_GROUP="cg-aws-broker-oracle-smoke-$$" +OPTION_GROUP="cg-aws-broker-oracle-smoke-og-$$" if ! command -v aws >/dev/null 2>&1; then echo "aws CLI not found — 'brew install awscli' (layer-2 smoke only)." >&2 @@ -26,15 +26,15 @@ if ! curl -sf http://localhost:5000/moto-api/ >/dev/null 2>&1; then fi cleanup() { - aws "${EP[@]}" rds delete-db-instance --db-instance-identifier "$ID" --skip-final-snapshot >/dev/null 2>&1 || true - aws "${EP[@]}" rds delete-db-parameter-group --db-parameter-group-name "$PG" >/dev/null 2>&1 || true - aws "${EP[@]}" rds delete-option-group --option-group-name "$OG" >/dev/null 2>&1 || true + aws "${MOTO_ENDPOINT[@]}" rds delete-db-instance --db-instance-identifier "$INSTANCE_ID" --skip-final-snapshot >/dev/null 2>&1 || true + aws "${MOTO_ENDPOINT[@]}" rds delete-db-parameter-group --db-parameter-group-name "$PARAM_GROUP" >/dev/null 2>&1 || true + aws "${MOTO_ENDPOINT[@]}" rds delete-option-group --option-group-name "$OPTION_GROUP" >/dev/null 2>&1 || true } trap cleanup EXIT echo "== create-db-instance (oracle-se2, License Included, encrypted, private) ==" -aws "${EP[@]}" rds create-db-instance \ - --db-instance-identifier "$ID" \ +aws "${MOTO_ENDPOINT[@]}" rds create-db-instance \ + --db-instance-identifier "$INSTANCE_ID" \ --db-instance-class db.t3.medium \ --engine oracle-se2 \ --engine-version 19.0.0.0.ru-2024-07.rur-2024-07.r1 \ @@ -50,15 +50,15 @@ aws "${EP[@]}" rds create-db-instance \ --output text echo "== create-db-parameter-group (oracle-se2-19) ==" -aws "${EP[@]}" rds create-db-parameter-group \ - --db-parameter-group-name "$PG" \ +aws "${MOTO_ENDPOINT[@]}" rds create-db-parameter-group \ + --db-parameter-group-name "$PARAM_GROUP" \ --db-parameter-group-family oracle-se2-19 \ --description "cg smoke" \ --query 'DBParameterGroup.[DBParameterGroupName,DBParameterGroupFamily]' --output text echo "== create-option-group (oracle-se2 19) ==" -aws "${EP[@]}" rds create-option-group \ - --option-group-name "$OG" \ +aws "${MOTO_ENDPOINT[@]}" rds create-option-group \ + --option-group-name "$OPTION_GROUP" \ --engine-name oracle-se2 \ --major-engine-version 19 \ --option-group-description "cg smoke" \ From 2a6cb65bbfbdc295fcdf94e73f842b7897c42bc0 Mon Sep 17 00:00:00 2001 From: Peter Burkholder Date: Wed, 22 Jul 2026 14:19:15 -0400 Subject: [PATCH 3/8] docs(local): clarify make invocation dir and use tree-style clone layout Add a note that make commands run from the repo root (with the in-local alternative), and render the layer-3 clone layout with tree connectors and a root label. --- local/README.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/local/README.md b/local/README.md index 66a52777..aaabf9d1 100644 --- a/local/README.md +++ b/local/README.md @@ -10,6 +10,9 @@ can use independently. Written for **macOS on Apple Silicon (arm64)**. ## TL;DR +> Run these from the **repo root** (`aws-broker/`); `-C local` points make at +> `local/Makefile`. If you're already in `local/`, drop `-C local` (e.g. `make doctor`). + ```bash make -C local doctor # 0. check prerequisites make -C local unit # 1. Go unit tests (fast, no Docker) @@ -38,9 +41,9 @@ You do **not** need Oracle Instant Client or `sqlplus` on your Mac — layer 3 r `sqlplus` inside the `cg-oracle-free` container. Your clone layout for layer 3: ``` -git/…/ - aws-broker/ (this repo) - cg-oracle-database-19c-stig-overlay/ (sibling — provides hardening/sql/) +/ +├── aws-broker/ (this repo) +└── cg-oracle-database-19c-stig-overlay/ (sibling — provides hardening/sql/) ``` ## 1. Unit tests (fast, no Docker) — the everyday loop From a0a3fe6356f0a583aa497f5f5509b35fe938ac57 Mon Sep 17 00:00:00 2001 From: Peter Burkholder Date: Wed, 22 Jul 2026 14:19:15 -0400 Subject: [PATCH 4/8] chore(local): drop redundant dev-signal comment in wait-for-oracle.sh The development-signal-only caveat is covered by the local/README.md banner and the harness Makefile; the per-script line was redundant. --- local/scripts/wait-for-oracle.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/local/scripts/wait-for-oracle.sh b/local/scripts/wait-for-oracle.sh index caed1ccb..f0775dfe 100755 --- a/local/scripts/wait-for-oracle.sh +++ b/local/scripts/wait-for-oracle.sh @@ -1,6 +1,5 @@ #!/usr/bin/env bash # wait-for-oracle.sh — block until the local Oracle container is healthy. -# Local harness only; development signal only. set -euo pipefail CONTAINER="${1:-cg-oracle-free}" From b110616fb143d9782c6f10eb4d75c5b39a8ceee4 Mon Sep 17 00:00:00 2001 From: Peter Burkholder Date: Wed, 22 Jul 2026 14:48:33 -0400 Subject: [PATCH 5/8] chore(local): make cgo/aws/C-compiler prerequisites explicit Layer-1 unit tests pull in go-sqlite3 (a cgo package) and segfault under the default CGO_ENABLED=0; force CGO_ENABLED=1 in the harness Makefile so 'make unit' works out of the box. Add a doctor check for a C compiler (cc/gcc/clang) as a hard layer-1 prerequisite, upgrade the aws check to 'MISSING for layer 2', and give both macOS and Linux install hints. Broaden the prereqs header/table to cover macOS arm64 and Linux. --- local/Makefile | 10 ++++++++-- local/README.md | 5 +++-- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/local/Makefile b/local/Makefile index 9bb8523c..2b5ada83 100644 --- a/local/Makefile +++ b/local/Makefile @@ -11,6 +11,9 @@ COMPOSE_MOTO = docker compose -f docker-compose.moto.yml ORACLE_CONN ?= APPUSER/devpw_ChangeMe1@//localhost:1521/FREEPDB1 # Repo root (this Makefile lives in local/). ROOT = .. +# The suite pulls in go-sqlite3 (a cgo package), so unit tests need cgo + a C +# compiler. Force it on here rather than relying on the toolchain default. +export CGO_ENABLED = 1 .PHONY: help help: quickstart @@ -45,7 +48,7 @@ quickstart: # --------------------------------------------------------------------------- .PHONY: doctor doctor: - @echo "== Local test harness prerequisites (macOS arm64) ==" + @echo "== Local test harness prerequisites (macOS arm64 / Linux) ==" @ok=1; \ printf "%-22s" "docker:"; \ if command -v docker >/dev/null 2>&1; then \ @@ -55,9 +58,12 @@ doctor: printf "%-22s" "go:"; \ if command -v go >/dev/null 2>&1; then go version | awk '{print "OK ("$$3")"}'; \ else echo "MISSING — 'brew install go' (needed for layer 1 unit tests)"; ok=0; fi; \ + printf "%-22s" "C compiler (cgo):"; \ + if command -v cc >/dev/null 2>&1 || command -v gcc >/dev/null 2>&1 || command -v clang >/dev/null 2>&1; then echo "OK (cgo builds go-sqlite3)"; \ + else echo "MISSING — layer 1 needs cgo: 'xcode-select --install' (macOS) or 'apt-get install -y gcc' (Linux)"; ok=0; fi; \ printf "%-22s" "aws (cli):"; \ if command -v aws >/dev/null 2>&1; then echo "OK (layer 2 moto smoke)"; \ - else echo "optional — 'brew install awscli' (only for 'make moto-smoke')"; fi; \ + else echo "MISSING for layer 2 — 'brew install awscli' (macOS) or 'apt-get install -y awscli' (Linux); only 'make moto-smoke' needs it"; fi; \ printf "%-22s" "cinc-auditor:"; \ if command -v cinc-auditor >/dev/null 2>&1; then echo "OK (overlay InSpec run)"; \ else echo "optional — 'brew install --cask cinc-auditor' (only to run the STIG overlay locally)"; fi; \ diff --git a/local/README.md b/local/README.md index aaabf9d1..aa64ae4f 100644 --- a/local/README.md +++ b/local/README.md @@ -23,7 +23,7 @@ make -C local down # tear everything down `make -C local quickstart` prints this menu any time. -## 0. Prerequisites (macOS arm64) +## 0. Prerequisites (macOS arm64 / Linux) Run `make -C local doctor` — it tells you exactly what's present/missing and the command to install each. Summary: @@ -32,7 +32,8 @@ command to install each. Summary: |------|-----------|---------| | **Docker** (running) | layers 2 & 3 | Docker Desktop, or `brew install colima docker && colima start` | | **go** | layer 1 (unit tests) | `brew install go` | -| `aws` CLI | layer 2 `moto-smoke` only | `brew install awscli` | +| **C compiler** (`cc`/`gcc`/`clang`) | layer 1 — cgo builds `go-sqlite3` | `xcode-select --install` (macOS) or `apt-get install -y gcc` (Linux) | +| `aws` CLI | layer 2 `moto-smoke` only | `brew install awscli` (macOS) or `apt-get install -y awscli` (Linux) | | **`cg-oracle-database-19c-stig-overlay` cloned as a sibling** | layer 3 `assess`/`harden` | `git clone` it next to `aws-broker` (so `../../cg-oracle-database-19c-stig-overlay/hardening/sql` resolves), or pass `SQL_DIR=…` | | `cinc-auditor` | running the STIG overlay locally (optional) | `brew install --cask cinc-auditor` | | `sqlplus` | **not required** | `assess`/`harden` run `sqlplus` *inside* the container via `docker exec` | From ddc8e78f0ebc18ccae79bead0fbcff94e5db9d7f Mon Sep 17 00:00:00 2001 From: Peter Burkholder Date: Wed, 22 Jul 2026 15:22:40 -0400 Subject: [PATCH 6/8] docs(local): run cinc-auditor via Docker (cincproject/auditor) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 'brew install --cask cinc-auditor' guidance was outdated: we run cinc-auditor via Docker to avoid the cinc-workstation install (needs root). Update the doctor check and README prereqs/§3 to use a 'docker run cincproject/auditor' recipe, noting the overlay derives an image with Oracle Instant Client for its sqlplus-based oracledb_session controls. --- local/Makefile | 3 +-- local/README.md | 23 ++++++++++++++++++----- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/local/Makefile b/local/Makefile index 2b5ada83..305b6d97 100644 --- a/local/Makefile +++ b/local/Makefile @@ -65,8 +65,7 @@ doctor: if command -v aws >/dev/null 2>&1; then echo "OK (layer 2 moto smoke)"; \ else echo "MISSING for layer 2 — 'brew install awscli' (macOS) or 'apt-get install -y awscli' (Linux); only 'make moto-smoke' needs it"; fi; \ printf "%-22s" "cinc-auditor:"; \ - if command -v cinc-auditor >/dev/null 2>&1; then echo "OK (overlay InSpec run)"; \ - else echo "optional — 'brew install --cask cinc-auditor' (only to run the STIG overlay locally)"; fi; \ + echo "via Docker — 'docker run cincproject/auditor ...' (see README §3); no local install (avoids cinc-workstation/root)"; \ printf "%-22s" "sqlplus:"; \ if command -v sqlplus >/dev/null 2>&1; then echo "OK"; \ else echo "not needed — 'make assess/harden' run sqlplus INSIDE the container"; fi; \ diff --git a/local/README.md b/local/README.md index aa64ae4f..eef44313 100644 --- a/local/README.md +++ b/local/README.md @@ -35,7 +35,7 @@ command to install each. Summary: | **C compiler** (`cc`/`gcc`/`clang`) | layer 1 — cgo builds `go-sqlite3` | `xcode-select --install` (macOS) or `apt-get install -y gcc` (Linux) | | `aws` CLI | layer 2 `moto-smoke` only | `brew install awscli` (macOS) or `apt-get install -y awscli` (Linux) | | **`cg-oracle-database-19c-stig-overlay` cloned as a sibling** | layer 3 `assess`/`harden` | `git clone` it next to `aws-broker` (so `../../cg-oracle-database-19c-stig-overlay/hardening/sql` resolves), or pass `SQL_DIR=…` | -| `cinc-auditor` | running the STIG overlay locally (optional) | `brew install --cask cinc-auditor` | +| `cinc-auditor` (via Docker) | running the STIG overlay locally (optional) | run it from the `cincproject/auditor` container — do **not** install cinc-workstation (needs root); see §3 | | `sqlplus` | **not required** | `assess`/`harden` run `sqlplus` *inside* the container via `docker exec` | You do **not** need Oracle Instant Client or `sqlplus` on your Mac — layer 3 runs @@ -111,10 +111,23 @@ first, then `make -C local oracle19c-up`. It publishes on **1522** with service (this fidelity path is for maintainers; the default `oracle-free` flow above is the supported one). -**Optional — run the STIG overlay** against the local DB (needs `cinc-auditor`): -see the overlay repo's `README.md`; point its `oracledb_session` inputs at -`localhost:1521/FREEPDB1` with the seeded app user. Local overlay results are dev -signal only. +**Optional — run the STIG overlay** against the local DB: point its +`oracledb_session` inputs at `localhost:1521/FREEPDB1` with the seeded app user. +Run cinc-auditor **via Docker** (image `cincproject/auditor`) rather than +installing cinc-workstation locally (the workstation install requires root and +pulls in a lot of complexity): + +```bash +docker run --rm -it --network host \ + -v "$PWD/../../cg-oracle-database-19c-stig-overlay:/share" \ + cincproject/auditor exec /share \ + --input-file /share/input.yml +``` + +The overlay's `oracledb_session` controls call `sqlplus`, so its README uses a +derived image (`cincproject/auditor` + Oracle Instant Client). See the overlay +repo's `README.md` for the authoritative image build and inputs. Local overlay +results are dev signal only. ## What local CANNOT tell you (by design) From d75f7eca38726ff8509668f941721aff7c791f23 Mon Sep 17 00:00:00 2001 From: Peter Burkholder Date: Thu, 23 Jul 2026 11:18:55 -0400 Subject: [PATCH 7/8] fix(local): poll for moto readiness in moto-smoke The single-shot curl check raced 'make moto-up': the container's HTTP listener can lag a beat behind 'docker compose up -d' returning, so back-to-back moto-up && moto-smoke failed on the first miss. Poll :5000 once per second up to MOTO_TIMEOUT (default 10s) instead. Co-authored-by: OpenCode Agent --- local/scripts/moto-smoke.sh | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/local/scripts/moto-smoke.sh b/local/scripts/moto-smoke.sh index 29a74f16..3e76b8d0 100755 --- a/local/scripts/moto-smoke.sh +++ b/local/scripts/moto-smoke.sh @@ -20,10 +20,16 @@ if ! command -v aws >/dev/null 2>&1; then echo "aws CLI not found — 'brew install awscli' (layer-2 smoke only)." >&2 exit 2 fi -if ! curl -sf http://localhost:5000/moto-api/ >/dev/null 2>&1; then - echo "moto is not up on :5000 — run 'make moto-up' first." >&2 - exit 2 -fi +MOTO_TIMEOUT="${MOTO_TIMEOUT:-10}" +elapsed=0 +until curl -sf http://localhost:5000/moto-api/ >/dev/null 2>&1; do + if [ "$elapsed" -ge "$MOTO_TIMEOUT" ]; then + echo "moto is not up on :5000 after ${MOTO_TIMEOUT}s — run 'make moto-up' first." >&2 + exit 2 + fi + sleep 1 + elapsed=$((elapsed + 1)) +done cleanup() { aws "${MOTO_ENDPOINT[@]}" rds delete-db-instance --db-instance-identifier "$INSTANCE_ID" --skip-final-snapshot >/dev/null 2>&1 || true From 49a7c55a6761b399138b9d56c8c462b59fbffd2e Mon Sep 17 00:00:00 2001 From: Peter Burkholder Date: Thu, 23 Jul 2026 12:31:55 -0400 Subject: [PATCH 8/8] chore(local): drop the Oracle 19c EE fidelity image MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit No local SE2 image is possible on arm64 — Oracle's buildContainerImage.sh supports only 19c EE and 26ai Free ('-s' errors out). An EE image is the wrong edition (exposes EE-only TDE/FGA that the brokered SE2 lacks and the design compensates for), risking a misleading local pass. Edition-accurate STIG conformance is validated on a real brokered RDS SE2 instance regardless, so the harness keeps only the freely-pullable gvenzl/oracle-free (23c) engine. Remove docker-compose.oracle-19c.yml, the oracle19c-up target + COMPOSE_ORACLE19C var + down/quickstart references, and retitle the README to state the 23c engine vs 19c-on-RDS target. Rationale captured in the README §3 note (docs/oracle19c/ updates deferred to #537, where those files live). Co-authored-by: OpenCode Agent --- local/Makefile | 7 ------- local/README.md | 25 +++++++++++++++---------- local/docker-compose.oracle-19c.yml | 23 ----------------------- 3 files changed, 15 insertions(+), 40 deletions(-) delete mode 100644 local/docker-compose.oracle-19c.yml diff --git a/local/Makefile b/local/Makefile index 305b6d97..80f362e0 100644 --- a/local/Makefile +++ b/local/Makefile @@ -5,7 +5,6 @@ # New here? Run `make -C local doctor` then `make -C local quickstart`. COMPOSE_ORACLE = docker compose -f docker-compose.oracle-free.yml -COMPOSE_ORACLE19C = docker compose -f docker-compose.oracle-19c.yml COMPOSE_MOTO = docker compose -f docker-compose.moto.yml ORACLE_CONN ?= APPUSER/devpw_ChangeMe1@//localhost:1521/FREEPDB1 @@ -37,7 +36,6 @@ quickstart: @echo " make assess run assessment SQL -> reports/ (labeled dev signal)" @echo " make harden apply allowed hardening (idempotent)" @echo " make assess re-assess: state should have changed" - @echo " make oracle19c-up (optional) self-built Oracle 19c EE fidelity image" @echo "" @echo " make down tear EVERYTHING down" @echo "" @@ -120,10 +118,6 @@ oracle-up: $(COMPOSE_ORACLE) up -d ./scripts/wait-for-oracle.sh -.PHONY: oracle19c-up -oracle19c-up: - $(COMPOSE_ORACLE19C) up -d - .PHONY: assess assess: ./scripts/run-assessment-local.sh "$(ORACLE_CONN)" @@ -135,5 +129,4 @@ harden: .PHONY: down down: -$(COMPOSE_ORACLE) down -v - -$(COMPOSE_ORACLE19C) down -v -$(COMPOSE_MOTO) down -v diff --git a/local/README.md b/local/README.md index eef44313..eaca057c 100644 --- a/local/README.md +++ b/local/README.md @@ -1,4 +1,4 @@ -# Local Oracle 19c test harness +# Local Oracle test harness (23c engine; targets 19c on RDS) > **⚠️ DEVELOPMENT SIGNAL ONLY — NOT COMPLIANCE EVIDENCE.** > Everything here is for fast iteration. Authoritative STIG evidence comes only @@ -8,6 +8,11 @@ This lets you test the Oracle work **without any AWS access**, in three layers you can use independently. Written for **macOS on Apple Silicon (arm64)**. +> **Engine vs. target.** The local DB is `gvenzl/oracle-free` (Oracle **23c**); +> the target is **19c SE2** on RDS. No local 19c image exists for arm64 +> (see [§3](#3-real-local-oracle-for-sql-hardeningassessment)). + + ## TL;DR > Run these from the **repo root** (`aws-broker/`); `-C local` points make at @@ -102,14 +107,15 @@ and the seeded `weak_profile`/`seed_weak` artifacts are on a non-DEFAULT profile that hardening intentionally leaves alone. Reports land in `local/reports/` (gitignored), each labeled *development signal only*. -**Optional fidelity pass** — a self-built Oracle **19c EE** image (closer to the -brokered engine than 23c Free). You must build/tag `oracle/database:19.3.0-ee` -yourself from [oracle/docker-images](https://github.com/oracle/docker-images) -first, then `make -C local oracle19c-up`. It publishes on **1522** with service -`ORCLPDB1` and does **not** create `APPUSER`, so target it explicitly, e.g. -`make -C local assess ORACLE_CONN="SYS/@//localhost:1522/ORCLPDB1 as sysdba"` -(this fidelity path is for maintainers; the default `oracle-free` flow above is the -supported one). +> **Why not a local Oracle 19c image?** The brokered product is Oracle 19c +> **Standard Edition 2 (SE2)** (License Included is SE2-only on RDS). On arm64, +> Oracle's `buildContainerImage.sh` supports **only** 19c Enterprise Edition and +> 26ai Free — there is no way to build a local SE2 image (`-s` errors out). An EE +> image would be the *wrong edition*: it exposes EE-only features (Oracle-native +> TDE, Fine-Grained Auditing) that SE2 lacks and the design compensates for, so it +> risks a misleading pass. Since edition-accurate STIG conformance is validated on +> a real brokered RDS SE2 instance regardless, this harness uses only the +> freely-pullable `gvenzl/oracle-free` engine and accepts that it is not 19c. **Optional — run the STIG overlay** against the local DB: point its `oracledb_session` inputs at `localhost:1521/FREEPDB1` with the seeded app user. @@ -148,7 +154,6 @@ local/ Makefile doctor / unit / moto-* / oracle-* / down docker-compose.moto.yml motoserver/moto (free RDS control-plane mock) docker-compose.oracle-free.yml gvenzl/oracle-free (native arm64) - docker-compose.oracle-19c.yml self-built oracle/database:19.3.0-ee (fidelity) scripts/ wait-for-oracle.sh moto-smoke.sh layer-2 broker-shape smoke diff --git a/local/docker-compose.oracle-19c.yml b/local/docker-compose.oracle-19c.yml deleted file mode 100644 index a934819a..00000000 --- a/local/docker-compose.oracle-19c.yml +++ /dev/null @@ -1,23 +0,0 @@ -# Fidelity Oracle 19c EE for a periodic hardening parity pass. -# NOTE: no public 19c image exists — build it once from oracle/docker-images -# (LINUX.ARM64_1919000_db_home.zip, `buildContainerImage.sh -v 19.3.0 -e`) and -# tag it oracle/database:19.3.0-ee. DEVELOPMENT SIGNAL ONLY. -services: - oracle19c: - image: oracle/database:19.3.0-ee - container_name: cg-oracle-19c-ee - ports: - # Bind to loopback only — a local dev DB with a known password must not be - # reachable from the LAN/VPN. Do NOT change to "1522:1521" (0.0.0.0). - - "127.0.0.1:1522:1521" - environment: - ORACLE_PWD: "devpw_ChangeMe1" - ORACLE_SID: "ORCLCDB" - ORACLE_PDB: "ORCLPDB1" - volumes: - - ./init:/opt/oracle/scripts/startup:ro - healthcheck: - test: ["CMD", "sqlplus", "-L", "sys/devpw_ChangeMe1@//localhost:1521/ORCLCDB", "as", "sysdba", "@/dev/null"] - interval: 30s - timeout: 10s - retries: 40