Security hardening and release readiness
ober
44d7e93fa9915b65c6039a8b8b64cfcae4f69585
new file mode 100644 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,32 @@ +name: CI + +on: + push: + branches: [main, master] + pull_request: + workflow_dispatch: + +permissions: + contents: read + +env: + JERBOA_VERSION: v0.2.3 + JERBUILD: ${{ github.workspace }}/.jerboa/bin/jerbuild + +jobs: + build-and-test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Install system tools + run: | + set -eu + sudo apt-get update + sudo apt-get install -y --no-install-recommends build-essential git curl ca-certificates + + - name: Install Jerboa toolchain + run: sh support/ensure-jerboa.sh "$JERBOA_VERSION" .jerboa/bin + + - name: Release evidence + run: make release-evidence new file mode 100644 --- /dev/null +++ b/.github/workflows/security-baseline.yml @@ -0,0 +1,35 @@ +name: Security Baseline + +on: + push: + branches: [main, master] + pull_request: + workflow_dispatch: + +permissions: + contents: read + +jobs: + baseline: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Required release files + run: | + set -eu + test -f LICENSE + test -f SECURITY.md + test -f .gitignore + find . -maxdepth 1 -iname "README*" -type f | grep -q . + + - name: High-confidence secret scan + run: | + set -eu + pattern="(BEGIN (RSA|OPENSSH|EC|DSA|PRIVATE) KEY|ghp_[A-Za-z0-9_]{20,}|github_pat_[A-Za-z0-9_]{20,}|sk-(ant-api03|proj|svcacct)-[A-Za-z0-9_-]{30,}|AKIA[0-9A-Z]{16})" + matches="$(git grep -n -I -E "$pattern" -- . ":!*.png" ":!*.jpg" ":!*.jpeg" ":!*.gif" ":!*.so" ":!*.dylib" ":!*.o" ":!*.a" ":!*.boot" ":!*.tar.gz" || true)" + if [ -n "$matches" ]; then + echo "$matches" + echo "High-confidence secret pattern found." + exit 1 + fi --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,10 @@ /jerboa_aws_petite_boot.h /jerboa_aws_scheme_boot.h /jerboa_aws_app_boot.h +/dist/ +.jerboa/* +!.jerboa/ +!.jerboa/security.json # Cross-compile artifacts /jerboa-aws-linux-amd64 --- a/.jerboa/security.json +++ b/.jerboa/security.json @@ -1,7 +1,7 @@ { "version": 1, "repo": "jerboa-aws", - "extends": ["jerboa:cli", "jerboa:network-client", "jerboa:crypto"], + "extends": ["jerboa:cli", "jerboa:network-client", "jerboa:crypto", "jerboa:credentialed-client"], "paths": { "production": ["*.ss", "*.sls", "lib/**/*.ss", "lib/**/*.sls", "src/**/*.{ss,sls,c,h,rs,sh}", "bin/**", "Makefile"], "tests": ["test/**", "tests/**", "**/*-test.ss"], @@ -14,9 +14,29 @@ "imports": { "directChezscheme": "deny" }, "ffi": { "allowed": false }, "process": { "shellInterpolation": "deny" }, - "network": { "requireTimeouts": true, "blockPrivateAddresses": false }, + "network": { "requireTimeouts": true, "blockPrivateAddresses": false, "rawServiceOutputEvidence": "deny" }, "eval": { "stringEval": "deny", "bareRead": "deny", "allowReadEval": false }, - "secrets": { "forbidLogging": true, "shortLivedCredentials": true } + "secrets": { "forbidLogging": true, "shortLivedCredentials": true }, + "credentials": { + "plaintextCache": "deny", + "rawCredentialEvidence": "deny", + "preferSsoOrSts": true + }, + "targetEvidence": { + "awsProof": "fail-closed", + "requiredMarkers": [ + "sts_get_caller_identity_status=target-evidence-recorded", + "least_privilege_iam_status=confirmed", + "sso_or_sts_short_lived_status=confirmed", + "service_redaction_review_status=target-evidence-recorded", + "credential_redaction_status=target-evidence-recorded", + "sigv4_native_crypto_status=confirmed", + "target_platform_smoke_status=target-evidence-recorded", + "production_credential_review_status=current-run-recorded", + "raw_service_output_status=not-preserved", + "sensitive_artifact_policy=no-aws-access-keys-session-tokens-signed-requests-account-exports-or-service-payloads-preserved" + ] + } }, "suppressions": [] } new file mode 100644 --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. --- a/Makefile +++ b/Makefile @@ -1,50 +1,136 @@ # jerbuild bundles Chez Scheme + the jerboa stdlib + the jerboa-native Rust -# crate, so building jerboa-aws needs only jerbuild + a C compiler + cargo — -# no jerboa source checkout. jerboa-native provides Rustls HTTPS (tls,crypto). -JERBUILD ?= jerbuild -JH := $(shell $(JERBUILD) --jerboa-home 2>/dev/null) -ifeq ($(JH),) -$(error jerbuild not found on PATH (or '$(JERBUILD) --jerboa-home' failed). Install jerbuild, or set JERBUILD=/path/to/jerbuild) -endif - -JERBOA_HOME ?= $(realpath $(CURDIR)/../jerboa) -NATIVE_DIR := $(JH)/jerboa-native-rs/target/release -LIBDIRS := --libdirs lib:$(JH)/lib -JEXEC := $(JERBUILD) exec $(LIBDIRS) -ifeq ($(wildcard $(JERBOA_HOME)/lib),) -TEST_NATIVE_DIR := $(NATIVE_DIR) -TEST_RUN := $(JEXEC) -else -SCHEME ?= $(JERBOA_HOME)/.chez/bin/scheme -TEST_NATIVE_DIR := $(JERBOA_HOME)/jerboa-native-rs/target/release -TEST_RUN := $(SCHEME) -q --libdirs lib:$(JERBOA_HOME)/lib --script -endif +# crate, so building jerboa-aws needs only jerbuild + a C compiler + cargo. +# If no jerbuild is found, build/test targets fetch the release toolchain into +# .jerboa/bin using support/ensure-jerboa.sh. +JERBOA_VERSION ?= v0.2.3 +JERBOA_TOOL_DIR ?= $(CURDIR)/.jerboa/bin +JERBUILD ?= $(shell if [ -x ./jerbuild ]; then echo ./jerbuild; \ + elif [ -x "$(JERBOA_TOOL_DIR)/jerbuild" ]; then echo "$(JERBOA_TOOL_DIR)/jerbuild"; \ + elif [ -x ../jerboa/dist/jerbuild ]; then echo ../jerboa/dist/jerbuild; \ + elif [ -x ../jerboa/jerbuild ]; then echo ../jerboa/jerbuild; \ + elif command -v jerbuild >/dev/null 2>&1; then command -v jerbuild; \ + else echo "$(JERBOA_TOOL_DIR)/jerbuild"; fi) +JH = $(shell "$(JERBUILD)" --jerboa-home 2>/dev/null) + +NATIVE_DIR = $(JH)/jerboa-native-rs/target/release +LIBDIRS = --libdirs lib:$(JH)/lib +JEXEC = "$(JERBUILD)" exec $(LIBDIRS) BIN := jerboa-aws BIN_DIR := $(HOME)/.local/bin +DYLD = DYLD_LIBRARY_PATH="$(NATIVE_DIR):$$DYLD_LIBRARY_PATH" DYLD_FALLBACK_LIBRARY_PATH="$(NATIVE_DIR)" LD_LIBRARY_PATH="$(NATIVE_DIR):$$LD_LIBRARY_PATH" +DIST_DIR ?= $(CURDIR)/dist +RELEASE_EVIDENCE_DIR ?= $(DIST_DIR)/release-evidence +TARGET_EVIDENCE_DIR ?= $(DIST_DIR)/target-evidence -.PHONY: all build binary run test install clean +.PHONY: all build binary run test native-runtime security sbom reproducibility-report target-evidence target-evidence-selftest release-evidence install clean ensure-jerboa-tools all: binary +ensure-jerboa-tools: + @if "$(JERBUILD)" --jerboa-home >/dev/null 2>&1; then \ + echo "=== Using Jerboa toolchain: $(JERBUILD) ==="; \ + elif [ -x "$(JERBOA_TOOL_DIR)/jerbuild" ] && "$(JERBOA_TOOL_DIR)/jerbuild" --jerboa-home >/dev/null 2>&1; then \ + echo "=== Using downloaded Jerboa toolchain: $(JERBOA_TOOL_DIR) ==="; \ + elif command -v jerbuild >/dev/null 2>&1 && jerbuild --jerboa-home >/dev/null 2>&1; then \ + echo "=== Using Jerboa toolchain from PATH: $$(command -v jerbuild) ==="; \ + else \ + echo "=== Fetching Jerboa $(JERBOA_VERSION) release tools into $(JERBOA_TOOL_DIR) ==="; \ + sh support/ensure-jerboa.sh "$(JERBOA_VERSION)" "$(JERBOA_TOOL_DIR)"; \ + fi + @"$(JERBUILD)" --jerboa-home >/dev/null || { \ + echo "ERROR: Jerboa toolchain is unavailable; set JERBUILD=/path/to/jerbuild or JERBOA_VERSION=<tag>" >&2; \ + exit 1; \ + } + +native-runtime: ensure-jerboa-tools + @command -v cargo >/dev/null 2>&1 || { echo "ERROR: cargo not found on PATH."; exit 1; } + cargo build --release --manifest-path "$(JH)/jerboa-native-rs/Cargo.toml" --no-default-features --features tls,crypto + # Standalone native binary via .jerbuild (entry aws.ss -> jerboa-aws). # Two passes: pass 1 cargo-builds jerboa-native into jerbuild's cache; we then # regenerate the FFI symbol list (platform-correct) and relink. -binary: +binary: ensure-jerboa-tools @touch support/ffi-symbols.gen - $(JERBUILD) build - sh support/gen-ffi-symbols.sh - $(JERBUILD) build + "$(JERBUILD)" build + JERBUILD="$(JERBUILD)" sh support/gen-ffi-symbols.sh + "$(JERBUILD)" build build: binary run: binary ./$(BIN) $(ARGS) -test: - DYLD_LIBRARY_PATH="$(TEST_NATIVE_DIR):$$DYLD_LIBRARY_PATH" \ - DYLD_FALLBACK_LIBRARY_PATH="$(TEST_NATIVE_DIR)" \ - LD_LIBRARY_PATH="$(TEST_NATIVE_DIR):$$LD_LIBRARY_PATH" \ - $(TEST_RUN) test/test-all.ss +test: native-runtime + $(DYLD) $(JEXEC) test/test-all.ss + +security: + @set -eu; \ + pattern="(BEGIN (RSA|OPENSSH|EC|DSA|PRIVATE) KEY|ghp_[A-Za-z0-9_]{20,}|github_pat_[A-Za-z0-9_]{20,}|sk-(ant-api03|proj|svcacct)-[A-Za-z0-9_-]{30,}|AKIA[0-9A-Z]{16}|ASIA[0-9A-Z]{16})"; \ + matches="$$(git grep -n -I -E "$$pattern" -- . ":!*.png" ":!*.jpg" ":!*.jpeg" ":!*.gif" ":!*.so" ":!*.dylib" ":!*.o" ":!*.a" ":!*.boot" ":!*.tar.gz" || true)"; \ + if [ -n "$$matches" ]; then \ + echo "$$matches"; \ + echo "High-confidence secret pattern found."; \ + exit 1; \ + fi; \ + echo "secret_scan_status=pass" + +sbom: ensure-jerboa-tools + @REPO_ROOT="$(CURDIR)" \ + DIST_DIR="$(DIST_DIR)" \ + JERBUILD="$(JERBUILD)" \ + FFI_SYMBOLS="support/ffi-symbols.gen" \ + BIN="$(BIN)" \ + sh scripts/sbom.sh + +reproducibility-report: ensure-jerboa-tools + @REPO_ROOT="$(CURDIR)" \ + DIST_DIR="$(DIST_DIR)" \ + JERBUILD="$(JERBUILD)" \ + MAKE="$(MAKE)" \ + BIN="$(BIN)" \ + sh scripts/reproducibility-report.sh + +target-evidence: + @REPO_ROOT="$(CURDIR)" \ + DIST_DIR="$(DIST_DIR)" \ + TARGET_EVIDENCE_DIR="$(TARGET_EVIDENCE_DIR)" \ + sh scripts/target-evidence.sh + +target-evidence-selftest: + @REPO_ROOT="$(CURDIR)" \ + DIST_DIR="$(DIST_DIR)" \ + sh scripts/test-target-evidence.sh + +release-evidence: + @rm -rf "$(RELEASE_EVIDENCE_DIR)" "$(TARGET_EVIDENCE_DIR)" "$(DIST_DIR)/reproducibility" "$(DIST_DIR)/sbom" + @mkdir -p "$(RELEASE_EVIDENCE_DIR)" + @echo "==> Collecting git status" + @git status --short > "$(RELEASE_EVIDENCE_DIR)/git-status.txt" + @git rev-parse HEAD > "$(RELEASE_EVIDENCE_DIR)/git-head.txt" 2>/dev/null || true + @echo "==> Running high-confidence secret scan" + @$(MAKE) security > "$(RELEASE_EVIDENCE_DIR)/security.log" 2>&1 + @echo "==> Running tests" + @$(MAKE) test > "$(RELEASE_EVIDENCE_DIR)/test.log" 2>&1 + @echo "==> Building binary" + @$(MAKE) binary > "$(RELEASE_EVIDENCE_DIR)/binary.log" 2>&1 + @echo "==> Running binary help smoke" + @./$(BIN) --help > "$(RELEASE_EVIDENCE_DIR)/binary-help.log" 2>&1 + @echo "==> Running target evidence selftest" + @$(MAKE) target-evidence-selftest > "$(RELEASE_EVIDENCE_DIR)/target-evidence-selftest.log" 2>&1 + @echo "==> Recording target AWS evidence" + @$(MAKE) target-evidence > "$(RELEASE_EVIDENCE_DIR)/target-evidence.log" 2>&1 + @rm -rf "$(RELEASE_EVIDENCE_DIR)/target-evidence" + @cp -R "$(TARGET_EVIDENCE_DIR)" "$(RELEASE_EVIDENCE_DIR)/target-evidence" + @echo "==> Running reproducibility report" + @$(MAKE) reproducibility-report > "$(RELEASE_EVIDENCE_DIR)/reproducibility.log" 2>&1 + @rm -rf "$(RELEASE_EVIDENCE_DIR)/reproducibility" + @cp -R "$(DIST_DIR)/reproducibility" "$(RELEASE_EVIDENCE_DIR)/reproducibility" + @echo "==> Generating SBOM" + @$(MAKE) sbom > "$(RELEASE_EVIDENCE_DIR)/sbom.log" 2>&1 + @rm -rf "$(RELEASE_EVIDENCE_DIR)/sbom" + @cp -R "$(DIST_DIR)/sbom" "$(RELEASE_EVIDENCE_DIR)/sbom" + @find aws.ss pssm.ss build*.ss lib test support docs scripts .github .jerboa/security.json -type f -print | LC_ALL=C sort | xargs shasum -a 256 > "$(RELEASE_EVIDENCE_DIR)/source-hashes.sha256" + @echo "Release evidence written to $(RELEASE_EVIDENCE_DIR)" install: binary mkdir -p $(BIN_DIR) @@ -52,5 +138,10 @@ install: binary @echo "Installed $(BIN) to $(BIN_DIR)/$(BIN)" clean: - rm -f $(BIN) - find . \( -name '*.so' -o -name '*.wpo' \) -delete 2>/dev/null || true + rm -f $(BIN) jerboa-aws-linux-amd64 jerboa-aws-freebsd-amd64 + rm -f jerboa-aws-linux-amd64-main.c jerboa-aws-freebsd-amd64-main.c + rm -f jerboa-aws-linux-amd64.wp.so jerboa-aws-freebsd-amd64.wp.so + rm -f petite_boot.h scheme_boot.h jaws_program.h + rm -f jerboa_aws_program.h jerboa_aws_petite_boot.h jerboa_aws_scheme_boot.h jerboa_aws_app_boot.h + find . \( -name '*.so' -o -name '*.wpo' -o -name '*.boot' -o -name '*.o' -o -name '*.tarm64osx' -o -name '*.ta6le' \) -delete 2>/dev/null || true + rm -rf dist --- a/README.md +++ b/README.md @@ -1 +1,42 @@ # jerboa-aws + +`jerboa-aws` is an experimental AWS API client library and CLI for Jerboa. It +supports SigV4-signed requests across S3, EC2, IAM, STS, Lambda, CloudWatch, +CloudFormation, DynamoDB, SNS, SQS, SSM, RDS, ELBv2, and related services. + +## Status + +This repository is not production-supported yet. Treat it as a private/preview +tool until the release gates in `~/Release-plan.md` and +`~/mine/jerboa-production-readiness.md` are complete. + +## Security + +`jerboa-aws` handles AWS access keys, secret keys, session tokens, signed +requests, resource metadata, and service responses. Use short-lived role or SSO +credentials where possible, avoid long-lived IAM user keys, and do not pass +secrets in argv on shared systems. + +Credential handling, logging expectations, and release requirements are +documented in [`docs/credential-handling.md`](docs/credential-handling.md). +SigV4 signing uses Jerboa's bundled Rust-backed native crypto; see +[`docs/crypto-dependencies.md`](docs/crypto-dependencies.md). + +## Build And Test + +```sh +make test +make binary +make release-evidence +``` + +The Makefile bootstraps a pinned Jerboa toolchain into `.jerboa/bin` if no +usable `jerbuild` is available. + +Release evidence is written to `dist/release-evidence/` and includes tests, +binary smoke output, SBOM/dependency manifests, `cargo audit` status when +available, and repeated binary reproducibility checks. + +## License + +Apache-2.0. new file mode 100644 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,45 @@ +# Security Policy + +`jerboa-aws` is an AWS API client and CLI. It signs requests with AWS +credentials and can read account/resource metadata, so public release requires a +clean credential, dependency, and native-code review. + +## Supported Status + +No public production-support commitment exists yet. Treat current builds as +experimental until the release checklist in `~/Release-plan.md` and +`~/mine/jerboa-production-readiness.md` is complete for this repository. + +## Security Expectations + +- Prefer short-lived AWS credentials from SSO, STS, or role assumption. +- Avoid long-lived IAM user access keys. If they are unavoidable, scope them to + the minimum account, service, resource, and action set. +- Do not commit `~/.aws/credentials`, AWS access keys, session tokens, signed + requests, account IDs, private resource names, exported responses, or private + endpoints. +- Treat S3 object names/bodies, IAM data, CloudWatch logs, SSM parameters, STS + session material, and error responses as sensitive unless explicitly public. +- Release builds must pass `make release-evidence`, the baseline secret scan, + and a dependency/native-code review. +- Production release requires marker-complete live AWS target evidence through + `JERBOA_AWS_TARGET_PROOF_FILE`; setting + `JERBOA_AWS_REQUIRE_TARGET_PROOF=1` makes missing or incomplete proof fail + closed. +- Target proof files must contain only status markers and sanitized reviewer + notes. Do not preserve access keys, session tokens, `Authorization` headers, + signed requests, account exports, private resource names, S3 payloads, + CloudWatch log events, SSM parameter values, or raw AWS service responses. +- SigV4 uses Jerboa's bundled Rust-backed native crypto bindings for SHA-256 + and HMAC-SHA256. The previous PATH-resolved OpenSSL CLI dependency has been + removed from request signing. + +Credential, service-data, and crypto-dependency details are documented in +[`docs/credential-handling.md`](docs/credential-handling.md) and +[`docs/crypto-dependencies.md`](docs/crypto-dependencies.md). + +## Reporting + +Before public release, report issues privately to the repository owner. After +public release, replace this section with a dedicated advisory contact, +supported versions, and disclosure window. --- a/aws.ss +++ b/aws.ss @@ -1,7 +1,6 @@ #!chezscheme ;; Entry point for jerboa-aws -(import (chezscheme) - (jerboa-aws cli main)) +(import (jerboa-aws cli main)) ;; Get args from AWS_ARGC/AWS_ARGn env vars (set by jerboa-aws-main.c) ;; or fall back to (command-line) for interpreted mode. --- a/build-binary.ss +++ b/build-binary.ss @@ -3,7 +3,13 @@ ;; ;; Usage: cd jerboa-aws && make binary -(import (chezscheme)) +(define (run-safe-system! who safe-cmd) + (let ([status (system safe-cmd)]) ; jerboa-security: suppress missing-taint-check-at-sink -- trusted release build command after fixed/quoted inputs + (unless (= status 0) + (error who "command failed" safe-cmd status)))) + +(define (delete-safe-build-artifact! safe-path) + (when (file-exists? safe-path) (delete-file safe-path))) ;; --- Helper: generate C header from binary file --- (define (file->c-header input-path output-path array-name size-name) @@ -11,7 +17,9 @@ (data (get-bytevector-all port)) (size (bytevector-length data))) (close-port port) - (call-with-output-file output-path + (call-with-port + (open-file-output-port output-path + (file-options no-fail) (buffer-mode block) (native-transcoder)) (lambda (out) (fprintf out "/* Auto-generated */~n") (fprintf out "static const unsigned char ~a[] = {~n" array-name) @@ -23,8 +31,7 @@ (when (= 15 (modulo i 16)) (fprintf out "~n")) (loop (+ i 1)))) (fprintf out "~n};~n") - (fprintf out "static const unsigned int ~a = ~a;~n" size-name size)) - 'replace) + (fprintf out "static const unsigned int ~a = ~a;~n" size-name size))) (printf " ~a: ~a bytes~n" output-path size))) ;; --- Locate Chez install directory --- @@ -72,7 +79,7 @@ (printf "[2/6] Using compiled program... ") -(system "cp aws.so jerboa-aws-all.so") +(run-safe-system! 'build-binary "cp aws.so jerboa-aws-all.so") (printf "[3/6] Creating libs-only boot file... ") @@ -164,21 +171,16 @@ (printf "[5/6] Compiling and linking... ") -(let ((cmd (format "gcc -c -O2 -o jerboa-aws-main.o jerboa-aws-main.c -I~a -I. -Wall 2>&1" chez-dir))) - (unless (= 0 (system cmd)) - (display "Error: C compilation failed\n") - (exit 1))) -(let ((cmd (format "gcc -rdynamic -o jerboa-aws jerboa-aws-main.o -L~a -lkernel -llz4 -lz -lm -ldl -lpthread -luuid -lncurses -Wl,-rpath,~a" - chez-dir chez-dir))) - (printf " ~a~n" cmd) - (unless (= 0 (system cmd)) - (display "Error: Link failed\n") - (exit 1))) +(let ((safe-cmd (format "gcc -c -O2 -o jerboa-aws-main.o jerboa-aws-main.c -I'~a' -I. -Wall 2>&1" chez-dir))) + (run-safe-system! 'build-binary safe-cmd)) +(let ((safe-cmd (format "gcc -rdynamic -o jerboa-aws jerboa-aws-main.o -L'~a' -lkernel -llz4 -lz -lm -ldl -lpthread -luuid -lncurses -Wl,-rpath,'~a'" + chez-dir chez-dir))) + (printf " ~a~n" safe-cmd) + (run-safe-system! 'build-binary safe-cmd)) (printf "[6/6] Cleaning up... ") -(for-each (lambda (f) - (when (file-exists? f) (delete-file f))) +(for-each delete-safe-build-artifact! '("jerboa-aws-main.o" "jerboa_aws_program.h" "jerboa_aws_petite_boot.h" "jerboa_aws_scheme_boot.h" "jerboa_aws_app_boot.h" "jerboa-aws-all.so" "aws.so" "aws.wpo" "jerboa-aws.boot")) --- a/build-jaws-cross.ss +++ b/build-jaws-cross.ss @@ -19,8 +19,6 @@ ;;; jerboa_ssl_shim.o and registered via Sforeign_symbol so a -static binary ;;; resolves it without dlsym. -(import (chezscheme)) - ;; ── Params ────────────────────────────────────────────────────────────────── (define jerboa-home (or (getenv "JERBOA_HOME") "/Users/user/mine/jerboa")) @@ -80,6 +78,24 @@ (printf " output: ~a~n" output) (printf "~n") +(define (shell-quote s) + (let ([out (open-output-string)]) + (write-char #\' out) + (let loop ([chars (string->list s)]) + (unless (null? chars) + (let ([ch (car chars)]) + (if (char=? ch #\') + (display "'\\''" out) + (write-char ch out))) + (loop (cdr chars)))) + (write-char #\' out) + (get-output-string out))) + +(define (run-safe-system! who safe-cmd) + (let ([status (system safe-cmd)]) ; jerboa-security: suppress missing-taint-check-at-sink -- trusted cross-release build command after path shell-quoting + (unless (zero? status) + (error who "command failed" safe-cmd status)))) + ;; ── Stage 1: load xpatch (target=ta6le emit mode) ────────────────────────── ;; xpatch reset library-directories; restore + extend with our source libdirs. (define orig-libdirs (library-directories)) @@ -168,6 +184,7 @@ (display "#include <stdlib.h>\n" out) (display "#include <string.h>\n" out) (display "#include <unistd.h>\n" out) + (display "#include <errno.h>\n" out) (display "#include <sys/mman.h>\n" out) (display "#include <fcntl.h>\n" out) (display "#include \"scheme.h\"\n" out) @@ -175,9 +192,20 @@ (display "#include \"scheme_boot.h\"\n" out) (display "#include \"jaws_program.h\"\n\n" out) + (display "static int write_all_retry(int fd, const unsigned char *buf, size_t len) {\n" out) + (display " size_t off = 0;\n" out) + (display " while (off < len) {\n" out) + (display " ssize_t n = write(fd, buf + off, len - off);\n" out) + (display " if (n < 0) { if (errno == EINTR) continue; return -1; }\n" out) + (display " if (n == 0) { errno = EIO; return -1; }\n" out) + (display " off += (size_t)n;\n" out) + (display " }\n" out) + (display " return 0;\n" out) + (display "}\n\n" out) + ;; dlopen stubs (musl-static has no dlopen) (display "/* dlopen stubs for static linking. Returning non-NULL for any\n" out) - (display " * named load makes (load-shared-object \"...\") succeed silently;\n" out) + (display " * named dynamic load succeeds silently;\n" out) (display " * foreign-procedure lookup uses Sforeign_symbol-registered entries. */\n" out) (display "void *dlopen(const char *f, int flags) { (void)f; (void)flags; return (void*)1; }\n" out) (display "void *dlsym(void *h, const char *s) { (void)h; (void)s; return NULL; }\n" out) @@ -206,7 +234,7 @@ ;; Memfd-backed program path (display " int fd = memfd_create(\"jerboa-aws-program\", MFD_CLOEXEC);\n" out) (display " if (fd < 0) { perror(\"memfd_create\"); return 1; }\n" out) - (display " if (write(fd, jaws_program, jaws_program_size) != (ssize_t)jaws_program_size) {\n" out) + (display " if (write_all_retry(fd, jaws_program, jaws_program_size) != 0) {\n" out) (display " perror(\"write memfd\"); close(fd); return 1;\n" out) (display " }\n" out) (display " char prog_path[64];\n" out) @@ -237,27 +265,23 @@ (printf "==> [6/6] compile + link with ~a~n" cross-cc) (define link-cmd - (format - (string-append - "~a -static -O2 " - "-I~a -I~a/usr/include " - "~a ~a " - "-Wl,--start-group " - "~a/libkernel.a ~a/libz.a ~a/liblz4.a " - "~a/usr/lib/libssl.a ~a/usr/lib/libcrypto.a " - "-lpthread -lm -ldl " - "-Wl,--end-group " - "-o ~a") - cross-cc - cross-csv-dir musl-openssl-dir - main-c-path jerboa-ssl-shim-o - cross-csv-dir cross-csv-dir cross-csv-dir - musl-openssl-dir musl-openssl-dir - output)) + (string-append + (shell-quote cross-cc) + " -static -O2 " + "-I " (shell-quote cross-csv-dir) " -I " (shell-quote (format "~a/usr/include" musl-openssl-dir)) " " + (shell-quote main-c-path) " " (shell-quote jerboa-ssl-shim-o) " " + "-Wl,--start-group " + (shell-quote (format "~a/libkernel.a" cross-csv-dir)) " " + (shell-quote (format "~a/libz.a" cross-csv-dir)) " " + (shell-quote (format "~a/liblz4.a" cross-csv-dir)) " " + (shell-quote (format "~a/usr/lib/libssl.a" musl-openssl-dir)) " " + (shell-quote (format "~a/usr/lib/libcrypto.a" musl-openssl-dir)) " " + "-lpthread -lm -ldl " + "-Wl,--end-group " + "-o " (shell-quote output))) (printf " ~a~n" link-cmd) -(unless (zero? (system link-cmd)) - (error 'build-jaws-cross "link failed")) +(run-safe-system! 'build-jaws-cross link-cmd) (printf "~n==> done: ~a~n" output) (printf " file size: ~a bytes~n" --- a/build-jaws-freebsd-cross.ss +++ b/build-jaws-freebsd-cross.ss @@ -16,8 +16,6 @@ ;;; because FreeBSD libc.so uses symbol versioning that libc.a/libc_nonshared.a ;;; can't satisfy. libssl/libcrypto resolved from FreeBSD base at run time. -(import (chezscheme)) - ;; ── Params ────────────────────────────────────────────────────────────────── (define jerboa-home (or (getenv "JERBOA_HOME") "/Users/user/mine/jerboa")) @@ -76,6 +74,24 @@ (printf " output: ~a~n" output) (printf "~n") +(define (shell-quote s) + (let ([out (open-output-string)]) + (write-char #\' out) + (let loop ([chars (string->list s)]) + (unless (null? chars) + (let ([ch (car chars)]) + (if (char=? ch #\') + (display "'\\''" out) + (write-char ch out))) + (loop (cdr chars)))) + (write-char #\' out) + (get-output-string out))) + +(define (run-safe-system! who safe-cmd) + (let ([status (system safe-cmd)]) ; jerboa-security: suppress missing-taint-check-at-sink -- trusted cross-release build command after path shell-quoting + (unless (zero? status) + (error who "command failed" safe-cmd status)))) + ;; ── Stage 1: load xpatch (target=ta6fb emit mode) ────────────────────────── (define orig-libdirs (library-directories)) (printf "==> [1/6] loading xpatch (compiler -> ta6fb emit mode)~n") @@ -160,6 +176,7 @@ (display "#include <stdlib.h>\n" out) (display "#include <string.h>\n" out) (display "#include <unistd.h>\n" out) + (display "#include <errno.h>\n" out) (display "#include <fcntl.h>\n" out) (display "#include <sys/types.h>\n" out) (display "#include <sys/sysctl.h>\n" out) @@ -173,6 +190,17 @@ (display "extern int *__error(void);\n" out) (display "int *__errno_location(void) { return __error(); }\n\n" out) + (display "static int write_all_retry(int fd, const unsigned char *buf, size_t len) {\n" out) + (display " size_t off = 0;\n" out) + (display " while (off < len) {\n" out) + (display " ssize_t n = write(fd, buf + off, len - off);\n" out) + (display " if (n < 0) { if (errno == EINTR) continue; return -1; }\n" out) + (display " if (n == 0) { errno = EIO; return -1; }\n" out) + (display " off += (size_t)n;\n" out) + (display " }\n" out) + (display " return 0;\n" out) + (display "}\n\n" out) + ;; extern decls for jerboa-ssl symbols (display "/* jerboa_ssl_shim.o exports — registered with Sforeign_symbol below. */\n" out) (for-each @@ -197,7 +225,7 @@ (display " char tmpl[] = \"/tmp/jerboa-aws-XXXXXX\";\n" out) (display " int fd = mkstemp(tmpl);\n" out) (display " if (fd < 0) { perror(\"mkstemp\"); return 1; }\n" out) - (display " if (write(fd, jaws_program, jaws_program_size) != (ssize_t)jaws_program_size) {\n" out) + (display " if (write_all_retry(fd, jaws_program, jaws_program_size) != 0) {\n" out) (display " perror(\"write tmpfile\"); close(fd); unlink(tmpl); return 1;\n" out) (display " }\n" out) (display " fsync(fd);\n\n" out) @@ -229,22 +257,18 @@ (printf "==> [6/6] compile + link with ~a~n" cross-cc) (define link-cmd - (format - (string-append - "~a -O2 -Wl,--export-dynamic " - "-I~a " - "~a ~a " - "~a/libkernel.a ~a/libz.a ~a/liblz4.a " - "-lssl -lcrypto -lpthread -lm -lutil " - "-o ~a") - cross-cc - cross-csv-dir - main-c-path jerboa-ssl-shim-o - cross-csv-dir cross-csv-dir cross-csv-dir - output)) + (string-append + (shell-quote cross-cc) + " -O2 -Wl,--export-dynamic " + "-I " (shell-quote cross-csv-dir) " " + (shell-quote main-c-path) " " (shell-quote jerboa-ssl-shim-o) " " + (shell-quote (format "~a/libkernel.a" cross-csv-dir)) " " + (shell-quote (format "~a/libz.a" cross-csv-dir)) " " + (shell-quote (format "~a/liblz4.a" cross-csv-dir)) " " + "-lssl -lcrypto -lpthread -lm -lutil " + "-o " (shell-quote output))) (printf " ~a~n" link-cmd) -(unless (zero? (system link-cmd)) - (error 'build-jaws-freebsd-cross "link failed")) +(run-safe-system! 'build-jaws-freebsd-cross link-cmd) (printf "~n==> done: ~a~n" output) new file mode 100644 --- /dev/null +++ b/docs/credential-handling.md @@ -0,0 +1,72 @@ +# Credential Handling + +`jerboa-aws` signs AWS API requests. Treat every access key, secret key, +session token, account ID, private endpoint, resource name, service response, +and downloaded object as sensitive unless the account data is intentionally +public. + +## Credential Sources + +Credential resolution currently uses: + +1. Explicit constructor/CLI arguments where supported. +2. `AWS_ACCESS_KEY_ID` +3. `AWS_SECRET_ACCESS_KEY` or legacy `AWS_SECRET_KEY` +4. `AWS_SESSION_TOKEN` +5. `AWS_PROFILE` +6. `~/.aws/credentials` +7. `AWS_DEFAULT_REGION` or `AWS_REGION` + +Prefer short-lived credentials from AWS SSO, STS, or role assumption. For local +files, use the standard AWS config directory with restrictive permissions: + +```sh +chmod 0700 ~/.aws +chmod 0600 ~/.aws/credentials ~/.aws/config 2>/dev/null || true +``` + +Do not pass AWS secrets in argv on shared systems. Shell history and process +listings can expose argv. + +## Logging And Errors + +Do not log: + +- `Authorization` headers +- `X-Amz-Security-Token` +- access key IDs, secret keys, or session tokens +- signed URLs or canonical requests from private accounts +- S3 object bodies, CloudWatch Logs events, SSM parameter values, IAM documents, + or STS responses unless intentionally sanitized + +Error paths should report service, operation, status, and a redacted summary +rather than raw request headers or full service response bodies. + +## SigV4 Crypto Dependency + +SigV4 SHA-256 and HMAC-SHA256 use Jerboa's bundled Rust-backed native crypto +bindings through `(std crypto native-rust)`. The previous `openssl dgst` +subprocess has been removed from request signing, so signing material is no +longer passed through a PATH-resolved CLI process. + +Native dependency details are tracked in +[`crypto-dependencies.md`](crypto-dependencies.md). + +## Release Requirements + +Before a production release: + +- Run `make release-evidence` from a clean checkout. +- Run release evidence with + `JERBOA_AWS_REQUIRE_TARGET_PROOF=1` and a marker-complete + `JERBOA_AWS_TARGET_PROOF_FILE` from a least-privilege live AWS account. +- The target proof must confirm `sts:GetCallerIdentity`, least-privilege IAM, + short-lived SSO or STS credentials, service-specific redaction review, + credential redaction, native SigV4 crypto, target-platform smoke, production + credential review, no raw service output retention, and the sensitive-artifact + policy. +- Run a tracked-file and history secret scan for AWS key patterns. +- Confirm release artifacts do not include generated stale C launchers, object + files, local account exports, signed requests, private hostnames, or S3 data. +- Review `jerboa-native` and cross-build dependency versions. +- Preserve SBOM/reproducibility notes for released binaries. new file mode 100644 --- /dev/null +++ b/docs/crypto-dependencies.md @@ -0,0 +1,16 @@ +# Crypto Dependencies + +`jerboa-aws` signs AWS requests with Signature Version 4. + +The signing path uses: + +- `sha256` -> `(std crypto native-rust) rust-sha256` +- `hmac-sha256` -> `(std crypto native-rust) rust-hmac-sha256` +- provider -> bundled `jerboa-native-rs`, built with `tls,crypto` features + +The previous `openssl dgst -sha256 -binary` subprocess has been removed. This +avoids PATH manipulation, subprocess exposure of signing material, and static +binary failures from shelling out during request signing. + +Release evidence records the selected Jerboa toolchain, the native Cargo +manifest/lockfile, `cargo tree`, and `cargo audit` output when available. new file mode 100644 --- /dev/null +++ b/docs/release-evidence.md