Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions local/.gitignore
Original file line number Diff line number Diff line change
@@ -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
132 changes: 132 additions & 0 deletions local/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
# 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_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

.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 ""
@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 / Linux) =="
@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" "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 "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:"; \
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; \
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: 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_MOTO) down -v
172 changes: 172 additions & 0 deletions local/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
# 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
> 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)**.

> **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
> `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)
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 / Linux)

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` |
| **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` (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
`sqlplus` inside the `cg-oracle-free` container. Your clone layout for layer 3:

```
<workspace>/
├── 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*.

> **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.
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)

- 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)
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.
14 changes: 14 additions & 0 deletions local/docker-compose.moto.yml
Original file line number Diff line number Diff line change
@@ -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"
24 changes: 24 additions & 0 deletions local/docker-compose.oracle-free.yml
Original file line number Diff line number Diff line change
@@ -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
23 changes: 23 additions & 0 deletions local/init/00_create_test_users.sql
Original file line number Diff line number Diff line change
@@ -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;
Loading