Security hardening and release readiness
ober
b51a90382e0ed83c069eaca3f8eb4d13ec495c5b
new file mode 100644 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,41 @@ +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-test-audit: + 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 curl ca-certificates pkg-config libssl-dev openssl + + - name: Install Jerboa toolchain + run: sh support/ensure-jerboa.sh "$JERBOA_VERSION" .jerboa/bin + + - name: Test + run: make test + + - name: Audit native linkage + run: make audit + + - name: Generate SBOM/platform notes + run: make sbom + + - name: Check OpenSSL advisory floor + run: make openssl-advisory-check new file mode 100644 --- /dev/null +++ b/.github/workflows/security-baseline.yml @@ -0,0 +1,39 @@ +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 + test -f docs/release-evidence.md + test -f scripts/openssl-advisory-check.sh + test -f scripts/reproducibility-report.sh + test -f scripts/sbom.sh + 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 @@ -1,4 +1,10 @@ *.so *.wpo *.dylib +*.o .jerbuild-hashes +.jerboa/* +!.jerboa/ +!.jerboa/security.json +dist/ +tests/tmp/ new file mode 100644 --- /dev/null +++ b/.jerboa/security.json @@ -0,0 +1,52 @@ +{ + "repository": "jerboa-fuse", + "riskClass": "filesystem-native-ffi-vault", + "status": "private-experimental-lane-c", + "releaseEvidence": { + "requiredTargets": [ + "security", + "test", + "audit", + "openssl-advisory-check", + "sbom", + "reproducibility-report", + "target-evidence", + "release-evidence" + ], + "sensitiveArtifactPolicy": "no-vault-data-private-paths-host-data-secrets-or-raw-mount-output" + }, + "targetEvidence": { + "mountProofFileEnv": "JERBOA_FUSE_TARGET_MOUNT_PROOF_FILE", + "mountRequireProofEnv": "JERBOA_FUSE_REQUIRE_TARGET_MOUNT_PROOF", + "corruptionProofFileEnv": "JERBOA_FUSE_TARGET_CORRUPTION_PROOF_FILE", + "corruptionRequireProofEnv": "JERBOA_FUSE_REQUIRE_TARGET_CORRUPTION_PROOF", + "sandboxProofFileEnv": "JERBOA_FUSE_TARGET_SANDBOX_PROOF_FILE", + "sandboxRequireProofEnv": "JERBOA_FUSE_REQUIRE_TARGET_SANDBOX_PROOF", + "statusFile": "dist/target-evidence/status.txt", + "mountRequiredMarkers": [ + "target_platform_status=linux-or-freebsd-recorded", + "mount_smoke_status=target-evidence-recorded", + "unmount_teardown_status=target-evidence-recorded", + "crash_teardown_status=target-evidence-recorded", + "fuse_package_status=target-evidence-recorded" + ], + "corruptionRequiredMarkers": [ + "vault_corruption_corpus_status=target-evidence-recorded", + "wrong_passphrase_status=target-denied-recorded", + "interrupted_write_status=target-evidence-recorded", + "crash_recovery_status=target-evidence-recorded" + ], + "sandboxRequiredMarkers": [ + "service_sandbox_status=target-evidence-recorded", + "mountpoint_policy_status=target-evidence-recorded", + "allow_other_status=disabled-or-reviewed-recorded", + "negative_access_test_status=target-denied-recorded" + ] + }, + "ffiBoundary": { + "nativeLoading": "lazy-via-jerboa-ffi", + "collectSafeCalls": true, + "cShim": "src/mount_helper.c", + "cryptoProvider": "OpenSSL" + } +} 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,46 +1,60 @@ -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 - -LIBDIRS = lib:$(JH)/lib +JERBOA_VERSION ?= v0.2.3 +JERBOA_TOOL_DIR ?= $(CURDIR)/.jerboa/bin +JERBUILD ?= $(shell if [ -x "$(CURDIR)/../jerboa/dist/jerbuild" ]; then printf '%s\n' "$(CURDIR)/../jerboa/dist/jerbuild"; elif command -v jerbuild >/dev/null 2>&1; then command -v jerbuild; else printf '%s\n' "$(JERBOA_TOOL_DIR)/jerbuild"; fi) +JH = $(shell "$(JERBUILD)" --jerboa-home 2>/dev/null) +LIBDIRS = lib:$(JH)/lib CC ?= cc +DIST_DIR ?= dist/release-evidence +SBOM_DIR ?= dist/sbom +REPRO_DIR ?= dist/reproducibility +TARGET_EVIDENCE_DIR ?= dist/target-evidence +WARN_CFLAGS ?= -Wall -Wextra -Werror +HARDEN_CFLAGS ?= -D_FORTIFY_SOURCE=2 -fstack-protector-strong +HARDEN_LDFLAGS ?= UNAME_S := $(shell uname -s) ifeq ($(UNAME_S),Darwin) - SHARED_FLAG = -dynamiclib - SO_EXT = .so - LD_VAR = DYLD_LIBRARY_PATH - OPENSSL_PREFIX ?= $(shell brew --prefix openssl@3 2>/dev/null || echo /opt/homebrew/opt/openssl@3) - CFLAGS_EXTRA = -I$(OPENSSL_PREFIX)/include - LDFLAGS_EXTRA = -L$(OPENSSL_PREFIX)/lib + SHARED_FLAG = -dynamiclib + SO_EXT = .so + LD_VAR = DYLD_LIBRARY_PATH + OPENSSL_PREFIX ?= $(shell brew --prefix openssl@4 2>/dev/null || brew --prefix openssl@3 2>/dev/null || echo /opt/homebrew/opt/openssl@4) + OPENSSL_CFLAGS ?= -I$(OPENSSL_PREFIX)/include + OPENSSL_LIBS ?= -L$(OPENSSL_PREFIX)/lib -lcrypto + OPENSSL_BIN ?= $(OPENSSL_PREFIX)/bin/openssl else - SHARED_FLAG = -shared - SO_EXT = .so - LD_VAR = LD_LIBRARY_PATH - CFLAGS_EXTRA = - LDFLAGS_EXTRA = + SHARED_FLAG = -shared + SO_EXT = .so + LD_VAR = LD_LIBRARY_PATH + OPENSSL_CFLAGS ?= $(shell pkg-config --cflags openssl 2>/dev/null) + OPENSSL_LIBS ?= $(shell pkg-config --libs openssl 2>/dev/null || printf '%s\n' -lcrypto) + OPENSSL_BIN ?= openssl + HARDEN_LDFLAGS += -Wl,-z,relro -Wl,-z,now endif -CFLAGS = $(SHARED_FLAG) -fPIC -O2 $(CFLAGS_EXTRA) $(LDFLAGS_EXTRA) +CFLAGS ?= -O2 -fPIC $(WARN_CFLAGS) $(HARDEN_CFLAGS) SHIM := libjerboa_fuse_mount$(SO_EXT) -.PHONY: all build transpile test test-memfs test-secmem test-access test-vault clean shim +.PHONY: all build transpile test test-memfs test-secmem test-access test-vault clean shim ensure-jerboa-tools audit security openssl-advisory-check sbom reproducibility-report target-evidence verify release-evidence all: build +ensure-jerboa-tools: + @if ! "$(JERBUILD)" --jerboa-home >/dev/null 2>&1; then \ + echo "=== Installing Jerboa toolchain $(JERBOA_VERSION) into $(JERBOA_TOOL_DIR) ==="; \ + sh support/ensure-jerboa.sh "$(JERBOA_VERSION)" "$(JERBOA_TOOL_DIR)"; \ + fi + shim: $(SHIM) $(SHIM): src/mount_helper.c - $(CC) $(CFLAGS) -o $@ $< + $(CC) $(CFLAGS) $(OPENSSL_CFLAGS) $(SHARED_FLAG) $(HARDEN_LDFLAGS) -o $@ $< $(OPENSSL_LIBS) -transpile: - $(JERBUILD) transpile src lib --force +transpile: ensure-jerboa-tools + @$(JERBUILD) transpile src lib --force -build: shim transpile +build: ensure-jerboa-tools shim transpile ifeq ($(UNAME_S),Darwin) EXTRA_LDPATH = :$(OPENSSL_PREFIX)/lib @@ -50,7 +64,7 @@ endif RUNTEST = JERBOA_FUSE_LIB=$(CURDIR) $(LD_VAR)=$(CURDIR)$(EXTRA_LDPATH) $(JERBUILD) exec --libdirs "$(LIBDIRS)" -test: test-memfs test-secmem test-access +test: test-memfs test-secmem test-access test-vault test-memfs: build $(RUNTEST) tests/test-memfs.ss @@ -64,6 +78,90 @@ test-access: build test-vault: build $(RUNTEST) tests/test-vault.ss +audit: shim + @echo "==> Platform" + @uname -srm + @echo "==> OpenSSL" + @if [ -x "$(OPENSSL_BIN)" ] || command -v "$(OPENSSL_BIN)" >/dev/null 2>&1; then "$(OPENSSL_BIN)" version -a; else echo "openssl CLI not found; shim compile/link is the local dependency check."; fi + @echo "==> pkg-config OpenSSL (informational)" + @if command -v pkg-config >/dev/null 2>&1; then \ + if [ -n "$(OPENSSL_PREFIX)" ] && [ -d "$(OPENSSL_PREFIX)/lib/pkgconfig" ]; then \ + printf 'openssl via %s: ' "$(OPENSSL_PREFIX)/lib/pkgconfig"; \ + PKG_CONFIG_PATH="$(OPENSSL_PREFIX)/lib/pkgconfig:$${PKG_CONFIG_PATH:-}" pkg-config --modversion openssl || true; \ + else \ + printf 'openssl via default pkg-config path: '; \ + pkg-config --modversion openssl || true; \ + fi; \ + else \ + echo "pkg-config not found."; \ + fi + @echo "==> Native linkage for $(SHIM)" + @if command -v otool >/dev/null 2>&1; then \ + otool -L "$(SHIM)"; \ + elif command -v ldd >/dev/null 2>&1; then \ + ldd "$(SHIM)"; \ + else \ + echo "No otool or ldd available for linkage audit."; \ + fi + +openssl-advisory-check: + @OPENSSL="$(OPENSSL_BIN)" scripts/openssl-advisory-check.sh + +security: scripts/security-check.sh + @sh scripts/security-check.sh + +sbom: build + @SHIM="$(SHIM)" OPENSSL_BIN="$(OPENSSL_BIN)" OPENSSL_PREFIX="$(OPENSSL_PREFIX)" JERBUILD="$(JERBUILD)" CC="$(CC)" scripts/sbom.sh "$(SBOM_DIR)" + +reproducibility-report: + @JERBUILD="$(JERBUILD)" OPENSSL_PREFIX="$(OPENSSL_PREFIX)" OPENSSL_BIN="$(OPENSSL_BIN)" scripts/reproducibility-report.sh "$(REPRO_DIR)" + +target-evidence: + @JERBOA_FUSE_TARGET_EVIDENCE_DIR="$(TARGET_EVIDENCE_DIR)" scripts/target-evidence.sh + +verify: security test audit openssl-advisory-check sbom reproducibility-report target-evidence + +release-evidence: security test audit sbom target-evidence + rm -rf "$(DIST_DIR)" + mkdir -p "$(DIST_DIR)" + OPENSSL="$(OPENSSL_BIN)" scripts/openssl-advisory-check.sh "$(DIST_DIR)/openssl-advisory-check.txt" + JERBUILD="$(JERBUILD)" OPENSSL_PREFIX="$(OPENSSL_PREFIX)" OPENSSL_BIN="$(OPENSSL_BIN)" scripts/reproducibility-report.sh "$(REPRO_DIR)" + rm -rf "$(DIST_DIR)/reproducibility" "$(DIST_DIR)/sbom" "$(DIST_DIR)/target-evidence" + cp -R "$(REPRO_DIR)" "$(DIST_DIR)/reproducibility" + cp -R "$(SBOM_DIR)" "$(DIST_DIR)/sbom" + cp -R "$(TARGET_EVIDENCE_DIR)" "$(DIST_DIR)/target-evidence" + git rev-parse HEAD > "$(DIST_DIR)/git-commit.txt" + git status --short > "$(DIST_DIR)/git-status.txt" + { printf 'platform='; uname -srm; printf 'jerbuild_version='; "$(JERBUILD)" --version; if "$(JERBUILD)" --jerboa-home >/dev/null 2>&1; then printf 'jerboa_home_status=present\n'; else printf 'jerboa_home_status=missing\n'; fi; } > "$(DIST_DIR)/build-environment.txt" + ($(CC) --version || $(CC) -v) > "$(DIST_DIR)/cc-version.txt" 2>&1 || true + $(MAKE) security > "$(DIST_DIR)/security.log" 2>&1 + @if [ -x "$(OPENSSL_BIN)" ] || command -v "$(OPENSSL_BIN)" >/dev/null 2>&1; then "$(OPENSSL_BIN)" version -a > "$(DIST_DIR)/openssl-version.txt"; else echo "openssl CLI not found." > "$(DIST_DIR)/openssl-version.txt"; fi + @if command -v otool >/dev/null 2>&1; then \ + otool -L "$(SHIM)" > "$(DIST_DIR)/native-linkage.txt"; \ + elif command -v ldd >/dev/null 2>&1; then \ + ldd "$(SHIM)" > "$(DIST_DIR)/native-linkage.txt"; \ + else \ + echo "No native linkage inspector found." > "$(DIST_DIR)/native-linkage.txt"; \ + fi + { test -f .jerboa/security.json && printf '%s\n' .jerboa/security.json; find Makefile src lib tests docs scripts examples -type f \( -name 'Makefile' -o -name '*.c' -o -name '*.h' -o -name '*.sh' -o -name '*.ss' -o -name '*.sls' -o -name '*.md' \) -print; } | sort > "$(DIST_DIR)/source-inputs.txt" + @{ \ + echo "privilege_policy_status=present"; \ + grep -E 'privilege_model_status: documented|mount_permission_status: operator-granted-not-root-by-default|allow_other_status: disabled-by-default|sandbox_policy_status: documented|mountpoint_policy_status: private-owned-empty-directory|unmount_teardown_status: target-host-required|vault_secret_policy_status: no-passphrase-in-argv-or-env' docs/deployment-hardening.md; \ + } > "$(DIST_DIR)/privilege-policy.txt" + grep -q 'privilege_model_status: documented' "$(DIST_DIR)/privilege-policy.txt" + grep -q 'allow_other_status: disabled-by-default' "$(DIST_DIR)/privilege-policy.txt" + grep -q 'sandbox_policy_status: documented' "$(DIST_DIR)/privilege-policy.txt" + shasum -a 256 src/mount_helper.c "$(SHIM)" > "$(DIST_DIR)/native-sha256.txt" + sh scripts/sanitize-evidence.sh "$(DIST_DIR)" + sh scripts/sanitize-evidence.sh "$(SBOM_DIR)" + sh scripts/sanitize-evidence.sh "$(REPRO_DIR)" + sh scripts/sanitize-evidence.sh "$(TARGET_EVIDENCE_DIR)" + grep -q '^sbom_status=present$$' "$(DIST_DIR)/sbom/manifest.txt" + grep -q '^deployment_hardening_status=present$$' "$(DIST_DIR)/sbom/manifest.txt" + grep -q '^platform_notes_status=present$$' "$(DIST_DIR)/sbom/manifest.txt" + grep -q '^status=' "$(DIST_DIR)/target-evidence/status.txt" + grep -q '^status=match$$' "$(DIST_DIR)/reproducibility/result.txt" + clean: rm -f $(SHIM) src/libchez_fuse_mount.* - rm -rf lib + rm -rf lib tests/tmp .jerboa/bin new file mode 100644 --- /dev/null +++ b/README.md @@ -0,0 +1,48 @@ +# jerboa-fuse + +Experimental FUSE filesystem bindings, memory-backed filesystems, and encrypted-vault helpers for Jerboa. + +The repository contains Jerboa modules for building FUSE filesystems, an in-memory test filesystem, secure-memory helpers, access-control helpers, vault-oriented examples, and a small native mount helper. + +## Status + +Private/experimental Lane C. Do not treat this repository as production-ready until the filesystem threat model, deployment hardening policy, FFI boundary documentation, vault recovery behavior, and native dependency review are complete. + +The current code is intended for local development and review. It is not ready for unattended mounts, privileged deployment, or use with irreplaceable data. + +## Build + +```sh +make build +``` + +The build compiles `src/mount_helper.c`, which owns FUSE mount calls, secure memory helpers, blockstore file I/O, and OpenSSL-backed vault crypto wrappers. + +## Test + +```sh +make test +make audit +make security +make sbom +make openssl-advisory-check +make reproducibility-report +make target-evidence +make release-evidence +``` + +`make test` runs memfs, secure-memory, access-controller, and vault tests. It does not perform a real FUSE mount in CI; mount smoke tests require an explicit Linux/FreeBSD host with FUSE configured. + +`make release-evidence` records the local release artifact bundle under `dist/release-evidence/`, including toolchain identity, native linkage, an SBOM/platform-support bundle, deployment privilege policy markers, an OpenSSL advisory gate, source inputs, hashes, and a two-clean-build reproducibility report. Production candidates must link a patched OpenSSL line: 4.0.1+, 3.6.3+, 3.5.7+, 3.4.6+, or 3.0.21+. + +Production support is blocked until `make target-evidence` records marker- +complete target mount, vault corruption/recovery, and service-sandbox proof +files through `JERBOA_FUSE_TARGET_MOUNT_PROOF_FILE`, +`JERBOA_FUSE_TARGET_CORRUPTION_PROOF_FILE`, and +`JERBOA_FUSE_TARGET_SANDBOX_PROOF_FILE`, or equivalent release-host evidence is +attached. Proof files must not contain vault contents, passphrases, keys, +private paths, hostnames, raw mount output, or raw command output. + +## Security + +Filesystem code can expose or corrupt local data if path, mount, permission, or lifecycle handling is wrong. Review [SECURITY.md](SECURITY.md), [docs/threat-model.md](docs/threat-model.md), [docs/deployment-hardening.md](docs/deployment-hardening.md), [docs/ffi-boundary.md](docs/ffi-boundary.md), and [docs/release-evidence.md](docs/release-evidence.md) before using it outside tests. new file mode 100644 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,55 @@ +# Security Policy + +`jerboa-fuse` is private/experimental Lane C code. It must not be marketed as production-ready or used for unattended privileged mounts until the release plan and production-readiness tracker gates are complete. + +## Sensitive Data + +Treat the following as sensitive: + +- Mounted filesystem contents and metadata, including names, sizes, modes, UID/GID values, timestamps, and symlink targets. +- Vault files, vault passphrases, derived keys, plaintext block contents, and decrypted temporary buffers. +- FUSE mountpoints and any host paths used for vault files or test fixtures. +- Native crash dumps, diagnostics, and linkage logs that may reveal local paths or loaded library versions. + +Do not commit real vault files, private data mounted through FUSE, production mount paths, passphrases, keys, tokens, or operational hostnames. + +## Current Security Posture + +- Native code is restricted to `src/mount_helper.c`. +- Scheme FFI bindings load the native helper lazily through `(jerboa ffi)` and do not import raw Chez FFI directly. +- Mountpoint, filesystem-name, and vault-file paths cross C as length-checked, NUL-free UTF-8 bytevectors. +- FUSE read/write, blockstore pread/pwrite/fsync, and crypto calls are collect-safe so they do not stall the runtime during blocking native work. +- Secure-memory helpers reject null pointers, zero sizes, and oversized allocations before copying or freeing. +- Vault encryption is delegated to OpenSSL through the C shim. Release evidence rejects OpenSSL lines below the current accepted upstream-supported minimums: 4.0.1+, 3.6.3+, 3.5.7+, 3.4.6+, or 3.0.21+. +- `make sbom` records source/artifact hashes, OpenSSL/native linkage evidence, local FUSE package evidence, and explicit target-platform mount-smoke blockers. +- `make security` passes, including release-file, native-boundary, generated + artifact, private-path, and target-proof marker checks. +- `docs/deployment-hardening.md` documents the privilege model, mountpoint + policy, `allow_other` default, service sandboxing expectations, teardown + evidence, and vault secret handling. +- `make release-evidence` archives native linkage, OpenSSL advisory status, SBOM/platform support notes, source inputs, hashes, and a two-clean-build reproducibility report under `dist/release-evidence/`. + +## Required Before Production + +- Threat model reviewed and updated for the target deployment. +- Real mount smoke tests on every supported platform, including teardown after process crash. +- Filesystem operation corpus for malformed names, oversized offsets, symlink/link behavior, rename replacement, truncate, and permission edge cases. +- Vault corruption/recovery corpus for truncated headers, bad magic/version, failed authentication, interrupted writes, and bitmap/superblock mismatch. +- Target-host evidence that the deployment follows `docs/deployment-hardening.md` + for any helper that opens `/dev/fuse` or performs mounts. +- Target-host mount, vault corruption/recovery, and sandbox evidence captured + through the proof controls documented in `docs/release-evidence.md`; required + missing, incomplete, host-private, or high-confidence secret proofs fail + closed with `status=blocked-target-proof`. +- Target-host review of the SBOM/platform support notes for OpenSSL, libc/platform mount interfaces, FUSE packages, the C compiler, and Jerboa toolchain. +- External review before any public production claim. + +Target proof files are marker-only release attestations. Do not include vault +contents, passphrases, keys, private filesystem paths, hostnames, SSH clone +strings, raw mount output, or raw command output. The target-evidence script +rejects overlarge, marker-incomplete, host-private, or high-confidence secret +proof material before it is copied into release evidence. + +## Reporting + +Before public release, report issues privately to the repository owner. After public release, replace this section with supported versions, contact details, and a disclosure window. new file mode 100644 --- /dev/null +++ b/docs/deployment-hardening.md @@ -0,0 +1,91 @@ +# Deployment Hardening + +`jerboa-fuse` crosses a local filesystem and kernel mount boundary. Production +use requires a target-host deployment review; local unit tests and native +linkage checks are not enough to prove mount safety. + +Required release markers: + +- `privilege_model_status: documented` +- `mount_permission_status: operator-granted-not-root-by-default` +- `allow_other_status: disabled-by-default` +- `sandbox_policy_status: documented` +- `mountpoint_policy_status: private-owned-empty-directory` +- `unmount_teardown_status: target-host-required` +- `vault_secret_policy_status: no-passphrase-in-argv-or-env` +- `target_mount_proof_status: documented` +- `target_corruption_proof_status: documented` +- `target_sandbox_proof_status: documented` +- `proof_failure_status: blocked-target-proof` + +## Privilege Model + +Do not run the mounting process as root by default. The preferred production +shape is: + +- a dedicated unprivileged service account; +- explicit membership in the OS group or policy that grants `/dev/fuse` access; +- a private, service-owned mountpoint created before mount time; +- no `allow_other` unless a target-host review explicitly requires it; and +- service-manager teardown that unmounts on normal exit and after crashes. + +If the host requires a privileged helper to perform the mount, that helper must +be a small audited wrapper with a fixed mountpoint policy, fixed option +allowlist, and no caller-controlled shell command construction. + +## Mountpoint Policy + +Mountpoints must be absolute, owned by the service user, not world-writable, and +empty before mount. Production deployments should avoid mounting over existing +application directories. The release-host smoke must record mount, read/write +where supported, unmount, and post-crash cleanup results for every supported +platform. + +## `allow_other` + +`allow_other` is disabled by default. Enabling it changes the local threat model +because unrelated users may see names, metadata, or decrypted content through +the mount. A release may enable it only with: + +- an explicit operator configuration value; +- a documented business reason; +- host-level `/etc/fuse.conf` review where applicable; and +- tests proving permissions and teardown still behave as expected. + +## Sandboxing + +Run the process under service-manager confinement where available: + +- Linux: systemd service user, `NoNewPrivileges=true`, `PrivateTmp=true`, + restricted writable paths, a narrow `DeviceAllow` for FUSE when feasible, and + a kill/umount cleanup path. +- FreeBSD: jail or service account confinement with only the required FUSE + device and vault paths exposed. +- macOS development smoke: use macFUSE only for local validation; it is not a + production release-host substitute for Linux or FreeBSD evidence. + +## Vault Secrets + +Vault passphrases and derived keys must not appear in command-line arguments, +environment variables, logs, release evidence, or crash dumps. Interactive or +file-descriptor based secret input is preferred. Temporary decrypted data must +stay inside the mount process and must not be written to release evidence. + +## Target Evidence + +`make target-evidence` records target-host proof status and is archived by +`make release-evidence`. Production promotion requires reviewed target proof +files for the actual supported hosts: + +- Linux or FreeBSD real mount, read/write where supported, unmount, crash + teardown, and FUSE package provenance through + `JERBOA_FUSE_TARGET_MOUNT_PROOF_FILE`. +- Vault corruption/recovery coverage for malformed vault files, wrong + passphrases, interrupted writes, and crash recovery through + `JERBOA_FUSE_TARGET_CORRUPTION_PROOF_FILE`. +- Service-manager confinement, mountpoint policy, reviewed `allow_other` + posture, and negative access tests through + `JERBOA_FUSE_TARGET_SANDBOX_PROOF_FILE`. + +The matching `JERBOA_FUSE_REQUIRE_TARGET_*_PROOF=1` variables make missing or +incomplete proof fail closed with `status=blocked-target-proof`. new file mode 100644 --- /dev/null +++ b/docs/ffi-boundary.md @@ -0,0 +1,41 @@ +# FFI Boundary + +`jerboa-fuse` uses one native helper, `src/mount_helper.c`, loaded lazily by `(jerboa-fuse mount)`. + +## Native Responsibilities + +- Open `/dev/fuse`, mount, unmount, read, write, and close FUSE file descriptors. +- Copy mountpoint, filesystem-name, and vault-file paths from NUL-free bytevectors into temporary C strings. +- Perform fixed-arity blockstore open/create wrappers so Scheme never declares variadic `open(2)`. +- Retry `read`, `write`, `pread`, `pwrite`, and `fsync` around `EINTR` where retrying is safe. +- Allocate, zero, copy, and free mlock-backed secure memory with null/size guards. +- Delegate vault CSPRNG, PBKDF2-HMAC-SHA256, and AES-256-GCM to OpenSSL. + +## Scheme Responsibilities + +- Validate path strings before crossing C: non-empty, NUL-free, UTF-8, and no more than 4096 bytes. +- Validate FUSE filesystem names before crossing C: non-empty, NUL-free, UTF-8, and no more than 255 bytes. +- Validate file descriptors, block numbers, bytevector lengths, offsets, key sizes, and payload sizes before native calls. +- Keep blocking FUSE, blockstore, and crypto calls behind collect-safe FFI declarations. +- Close blockstore file descriptors through `blockstore-close!` and destroy FUSE sessions with `fuse-session-destroy!`. + +## Ownership + +- C strings created from Scheme bytevectors are owned and freed by `src/mount_helper.c` before returning. +- Scheme owns all bytevectors passed to C. C must not retain bytevector pointers after the call returns. +- Secure-memory pointers are opaque integers on the Scheme side. They must only be freed through `secmem-free!` or `secure-key-destroy!`. +- `make-secure-key` copies key material into native secure memory and zeros the source bytevector. +- `blockstore-set-key!` zeros the caller-provided master-key bytevector after moving it into secure memory. + +## Error Mapping + +- Native syscall and mount failures return `-1` and preserve `errno` for `fuse-get-errno`. +- Native crypto wrappers return `1` for success, `0` for authentication failure where applicable, and `-1` for misuse or provider failure. +- Scheme raises on API misuse, mount failures, short reads/writes, failed `fsync`, and native crypto/provider errors. +- AES-GCM authentication failure maps to `#f` for decrypt operations so callers can distinguish bad credentials/corruption from API misuse. + +## Known Gaps + +- The vault crypto boundary still uses OpenSSL through the C shim. `make openssl-advisory-check` and `make release-evidence` enforce the current accepted OpenSSL minimums: 4.0.1+, 3.6.3+, 3.5.7+, 3.4.6+, or 3.0.21+. +- CI does not mount FUSE. A production gate must run real mount/unmount smoke tests on Linux and FreeBSD hosts with FUSE configured. +- Crash-recovery and corruption behavior are not yet covered by a durable regression corpus. new file mode 100644 --- /dev/null +++ b/docs/openssl-advisory-review.md @@ -0,0 +1,50 @@ +# OpenSSL Advisory Review + +Review date: 2026-06-21 + +`jerboa-fuse` delegates vault CSPRNG, PBKDF2-HMAC-SHA256, and AES-256-GCM to +OpenSSL through `src/mount_helper.c`. A release candidate is not acceptable if +it links an OpenSSL release line below the patched versions listed by upstream +for the 2026-06-09 advisory set. + +## Accepted OpenSSL Lines + +Release evidence must pass `make openssl-advisory-check`. The current policy +accepts these upstream-supported minimums: + +- OpenSSL 4.0.1 or newer on the 4.0 line. +- OpenSSL 3.6.3 or newer on the 3.6 line. +- OpenSSL 3.5.7 or newer on the 3.5 LTS line. +- OpenSSL 3.4.6 or newer on the 3.4 line. +- OpenSSL 3.0.21 or newer on the 3.0 LTS line. + +OpenSSL 3.6.2 is rejected for production release. It predates the 2026-06-09 +security patch release set, which includes a High severity issue fixed in +3.6.3 and 4.0.1. + +Official sources: + +- https://openssl-library.org/source/ +- https://openssl-library.org/news/secadv/20260609.txt +- https://openssl-library.org/news/vulnerabilities/ + +## Local Review Result + +The local release path uses Homebrew `openssl@4` after upgrading that keg to +OpenSSL 4.0.1. + +`jerboa-fuse` does not call the PKCS#7, CMS decryption, PKCS#12, QUIC, OCSP, +or AES-OCB one-shot APIs implicated by several 2026-06-09 issues, but it must +still link a patched OpenSSL because vault passphrase derivation and encrypted +block contents are high-value boundaries. + +## Release Gate + +Before publishing: + +- Run `make clean`. +- Run `make release-evidence JERBUILD=/path/to/jerbuild`. +- Confirm `dist/release-evidence/openssl-advisory-check.txt` reports + `status=pass`. +- Confirm `dist/release-evidence/reproducibility/result.txt` reports + `status=match`. new file mode 100644 --- /dev/null +++ b/docs/release-evidence.md @@ -0,0 +1,105 @@ +# Release Evidence + +`jerboa-fuse` is private Lane C filesystem and vault code with a native C shim. +It is not production-ready until each release candidate has current build/test, +OpenSSL linkage, advisory/CVE, reproducibility, mount smoke, and vault +corruption evidence. + +Run from a clean checkout: + +```sh +make clean +make release-evidence JERBUILD=/path/to/jerbuild +``` + +The `release-evidence` target runs `make security`, `make test`, `make audit`, +`make sbom`, `make target-evidence`, the OpenSSL advisory gate, and a +two-clean-build reproducibility check. It writes untracked artifacts under +`dist/release-evidence/`: + +- `git-commit.txt` and `git-status.txt`. +- `build-environment.txt` with host-neutral OS and Jerboa toolchain identity. +- `security.log` from the repository security gate. +- `cc-version.txt` and `openssl-version.txt`. +- `native-linkage.txt` from `otool -L` or `ldd`. +- `privilege-policy.txt` with deployment hardening, `allow_other`, sandbox, and + teardown policy markers. +- `sbom/manifest.txt`, `sbom/source-sha256.txt`, + `sbom/artifact-sha256.txt`, `sbom/dependencies.txt`, + `sbom/native-linkage.txt`, and `sbom/platform-support.txt`. +- `openssl-advisory-check.txt` with the accepted OpenSSL release-line result. +- `reproducibility/result.txt` plus first/second native shim and generated + library hashes. +- `source-inputs.txt` for source, examples, docs, tests, and evidence scripts. +- `native-sha256.txt` for the C shim source and built native shim. +- `target-evidence/status.txt` plus optional copied target proof files and + `.sha256` sidecars. + +Before publishing, review OpenSSL advisories for the linked package or distro +build and document any accepted CVE or platform risk in the release notes. +The current accepted OpenSSL release lines are documented in +`docs/openssl-advisory-review.md`, and `make release-evidence` rejects older +OpenSSL versions automatically. + +The SBOM/platform support bundle is explicit about what the local host can and +cannot prove. It records OpenSSL, compiler, Jerboa toolchain, native linkage, +FUSE package evidence when available, and source/artifact hashes. It also keeps +Linux and FreeBSD real mount smoke as `blocked-not-run` until those target hosts +run mount/unmount, crash-teardown, and package provenance checks. The privilege +policy evidence records the documented production default that `allow_other` is +off, mount permission is operator-granted rather than root-by-default, and +service-manager sandboxing is required for production deployments. + +The local test suite exercises the in-memory filesystem, secure memory, access +controller, and vault lifecycle. It does not replace real Linux/FreeBSD mount +smoke, crash-teardown evidence, or a vault corruption/recovery corpus. + +Reproducibility evidence is claimed only for two clean builds in the same +toolchain environment. The automated report compares the native shim and the +generated Jerboa library manifest. Reproducibility archives hashes/status/diffs +only; raw scratch build logs and raw native shims are not retained in release +evidence. + +Release evidence is sanitized before publication. Do not preserve vault data, +passphrases, keys, private paths, hostnames, SSH clone strings, raw mount output, +or raw command output in release artifacts or target proof files. + +## Target Proof Files + +`scripts/target-evidence.sh` accepts three target proof files. Setting a file +path records the proof; setting the matching `REQUIRE` variable to `1` makes a +missing, empty, or marker-incomplete proof block with +`status=blocked-target-proof`. + +- `JERBOA_FUSE_TARGET_MOUNT_PROOF_FILE` with + `JERBOA_FUSE_REQUIRE_TARGET_MOUNT_PROOF=1` records Linux or FreeBSD real + mount evidence. Required markers: + `target_platform_status=linux-or-freebsd-recorded`, + `mount_smoke_status=target-evidence-recorded`, + `unmount_teardown_status=target-evidence-recorded`, + `crash_teardown_status=target-evidence-recorded`, and + `fuse_package_status=target-evidence-recorded`. +- `JERBOA_FUSE_TARGET_CORRUPTION_PROOF_FILE` with + `JERBOA_FUSE_REQUIRE_TARGET_CORRUPTION_PROOF=1` records vault + corruption/recovery evidence. Required markers: + `vault_corruption_corpus_status=target-evidence-recorded`, + `wrong_passphrase_status=target-denied-recorded`, + `interrupted_write_status=target-evidence-recorded`, and + `crash_recovery_status=target-evidence-recorded`. +- `JERBOA_FUSE_TARGET_SANDBOX_PROOF_FILE` with + `JERBOA_FUSE_REQUIRE_TARGET_SANDBOX_PROOF=1` records service confinement. + Required markers: + `service_sandbox_status=target-evidence-recorded`, + `mountpoint_policy_status=target-evidence-recorded`, + `allow_other_status=disabled-or-reviewed-recorded`, and + `negative_access_test_status=target-denied-recorded`. + +Proof files must be 64 KiB or smaller and marker-only. Files containing private +paths, host details, SSH clone strings, vault contents, passphrases, keys, or +high-confidence secret material fail closed before they are copied into release +evidence. + +target_mount_proof_status: documented +target_corruption_proof_status: documented +target_sandbox_proof_status: documented +proof_failure_status: blocked-target-proof new file mode 100644 --- /dev/null +++ b/docs/threat-model.md @@ -0,0 +1,65 @@ +# Threat Model + +`jerboa-fuse` lets Jerboa code present filesystem views through FUSE and provides an encrypted vault example. This is security-sensitive local system code, not a general-purpose production filesystem. + +## Assets + +- Host filesystem integrity outside the mountpoint. +- Mounted file contents and metadata. +- Vault passphrases, master keys, per-block keys, and plaintext block buffers. +- Vault file integrity and availability. +- Process boundaries between the mounting process, descendant tools, and unrelated local processes. + +## Trust Boundaries + +- Kernel FUSE requests enter Jerboa as untrusted byte-level filesystem operations. +- Mountpoint and vault-file paths cross from Scheme into native C and then into platform syscalls. +- Vault key material crosses between Scheme bytevectors, mlock-backed native memory, and OpenSSL calls. +- Access-control decisions depend on local process identity and parentage, which can race with process exit and PID reuse. + +## Primary Risks + +- Mounting at an unintended path or exposing data through `allow-other`. +- Path, name, or symlink handling that lets mounted operations escape intended policy. +- Partial writes, interrupted writes, or crashes corrupting vault metadata. +- Decryption failures or corrupt blocks being treated as empty/valid data. +- Key material remaining in Scheme heap bytevectors longer than expected. +- Native FFI misuse: null pointers, embedded NUL strings, invalid lengths, blocking calls that freeze the runtime, or variadic libc declarations. +- Unmount failures leaving active mounts after the controlling process exits. + +## Current Controls + +- Public status remains private/experimental. +- Native FFI loads lazily and rejects NUL-containing path/name inputs. +- Scheme validates file descriptors, block numbers, path lengths, key lengths, payload sizes, and offsets before native calls. +- Blockstore file I/O uses fixed native wrappers for create/open and collect-safe pread/pwrite/fsync wrappers. +- Secure-memory helpers use mlock-backed mmap where available, reject invalid sizes, and zero on destroy. +- Access-control helpers deny when locked and only trust the owner PID and cached descendants. +- Default CI runs memfs, access-control, secure-memory, vault, and native-linkage checks without performing privileged mounts. + +## Release Gates + +- Add real mount/unmount smoke tests for Linux and FreeBSD with explicit + teardown checks. +- Add hostile filesystem operation tests for long names, embedded NUL + rejection, invalid UTF-8 byte names where applicable, symlink/link behavior, + rename replacement, truncate, large offsets, and permission changes. +- Add vault corruption/recovery tests for truncated files, bad header + magic/version, bad superblock locator, bitmap mismatch, interrupted writes, + and wrong passphrase lock/unlock. +- Keep `docs/deployment-hardening.md` current for the exact `allow_other` + policy, privilege model, mountpoint policy, and service sandboxing for any + process with mount permission. +- Generate `make release-evidence` output for Jerboa, the C compiler, + libc/platform mount APIs, and OpenSSL, including advisory status, native + linkage, source inputs, hashes, and two-clean-build reproducibility.