{"id":51351272,"url":"https://github.com/nmicic/ephrun","last_synced_at":"2026-07-02T16:06:21.863Z","repository":{"id":366931317,"uuid":"1278382824","full_name":"nmicic/ephrun","owner":"nmicic","description":"  Tools for encrypting ELF binaries and running them on untrusted Linux hosts via memfd, keyring, and capsule-based key delivery","archived":false,"fork":false,"pushed_at":"2026-06-23T22:41:40.000Z","size":141,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-24T00:21:52.769Z","etag":null,"topics":["argon2id","binary-protection","cloud-security","container-security","elf","encryption","keyutils","libsodium","linux","linux-keyring","memfd","secure-deployment","security","x25519"],"latest_commit_sha":null,"homepage":null,"language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/nmicic.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-06-23T18:31:28.000Z","updated_at":"2026-06-23T22:41:40.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/nmicic/ephrun","commit_stats":null,"previous_names":["nmicic/ephrun"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/nmicic/ephrun","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nmicic%2Fephrun","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nmicic%2Fephrun/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nmicic%2Fephrun/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nmicic%2Fephrun/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nmicic","download_url":"https://codeload.github.com/nmicic/ephrun/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nmicic%2Fephrun/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35053590,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-02T02:00:06.368Z","response_time":173,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["argon2id","binary-protection","cloud-security","container-security","elf","encryption","keyutils","libsodium","linux","linux-keyring","memfd","secure-deployment","security","x25519"],"created_at":"2026-07-02T16:06:21.244Z","updated_at":"2026-07-02T16:06:21.839Z","avatar_url":"https://github.com/nmicic.png","language":"C","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ephrun — Encrypted ELF Execution System\n\nEncrypted binary distribution and execution system for Linux using X25519 + XSalsa20-Poly1305 (libsodium sealed boxes), Linux kernel keyring for key storage, and memfd-based in-memory execution.\n\n## Threat Model — Short Version\n\nephrun protects encrypted artifacts at rest and raises the extraction bar on\nuntrusted Linux hosts. It is **not** a DRM system and does **not** defend a\nrunning workload against a hostile root user, compromised kernel, ptrace-capable\nattacker, process-memory scraping, live runtime instrumentation, or a loader\nattack that affects a dynamically linked `elfdec-run` before `main()` starts\n(`STATIC=1` is the deployment hardening path for that case). See\n`SECURITY.md` for the full threat model.\n\n## Architecture\n\n```\ngenkey          → pub.bin + priv.bin   (X25519 keypair)\nelfenc_pack     → .elfenc file         (sealed encrypted ELF)\nkcap_pack       → capsule.bin          (KCAP3 code-protected key wrapper)\nkcap_unpack     → capsule.bin → priv   (cross-platform CLI unwrap, for tooling)\nelfdec-run      → executes .elfenc     (decrypt + memfd + fexecve, Linux only)\n\nKey distribution:\n  add_keyring*.sh        → local keyring injection\n  elfdec-ssh-pushkey.sh  → remote via SSH\n  keypushd / keypush_send → remote via UDP sealed box\n```\n\n## Directory Structure\n\n```\nephrun/           Core encryption/decryption tools\nkeypush/             UDP key distribution (daemon + sender)\n```\n\n---\n\n## Quick Start\n\n```bash\n# Install dependencies (Ubuntu/Debian)\nsudo apt-get install -y build-essential libsodium-dev libkeyutils-dev\n\n# Build everything (top-level Makefile)\nmake                            # builds core tools + keypush\n\n# Or build from ephrun/ directory\ncd ephrun\nmake                            # builds all tools for this platform\nmake STATIC=1                   # static linking (for deployment to remote machines)\n\n# Generate keypair\n./genkey                        # creates pub.bin + priv.bin\n\n# Build and encrypt a test binary\ngcc -O2 hello.c -o hello\n./elfenc_pack pub.bin hello hello.elfenc\n\n# Run it (simplest mode — file-based keys)\nmkdir -p ~/.elfenc\ncp pub.bin priv.bin ~/.elfenc/\nchmod 600 ~/.elfenc/priv.bin\n./elfdec-run ./hello.elfenc     # → \"hello from encrypted ELF!\"\n```\n\n---\n\n## The 4 Key Sourcing Modes\n\n`elfdec-run` tries to obtain the private key in this priority order.\nThe first one that succeeds is used; the rest are skipped.\n\n```\nPriority 1:  ELFDEC_CODE     → capsule (code-protected key)\nPriority 2:  ELFDEC_KEYID    → keyring by numeric key ID\nPriority 3:  ELFDEC_LABEL    → keyring by label search\nPriority 4:  ELFDEC_KEYPATH  → file-based (priv.bin + pub.bin)\n     or:     ~/.elfenc/       → default file fallback\n```\n\n---\n\n### Mode 1 — Capsule (ELFDEC_CODE)\n\nA capsule wraps the private key with a password/code. The raw `priv.bin`\nnever touches the target disk — only the encrypted capsule is deployed.\nAt runtime, `elfdec-run` decrypts the capsule in memory using the code.\n\n**Crypto:** XChaCha20-Poly1305 AEAD with Argon2id (M=64 MiB, T=3, P=1 by default) key derivation — `K = Argon2id(code, salt)`.\n\n\u003e **Format versions:** New capsules use the `KCAP3` format (family magic `KCAP` +\n\u003e version byte + project_id + AAD-bound 64-byte header; Argon2id parameters live\n\u003e in the header so future tuning isn't a wire-format break). Legacy `KCAP2`\n\u003e (Argon2id, fixed params) and `KCAP1` (single-pass SHA256) capsules are still\n\u003e readable by `elfdec-run` for migration, but should be rotated — re-run\n\u003e `kcap_pack` against the original `priv.bin` to produce a `KCAP3` capsule and\n\u003e deploy the new file. The capsule format is dispatched by the magic bytes, so\n\u003e old capsules keep working until you replace them.\n\n**How it works:**\n\n```\n  ┌──────────────┐\n  │   kcap_pack   │   priv.bin + --code \"mysecret\"\n  │   (build-time)│ ──────────────────────────────────▶ capsule.bin\n  └──────────────┘                                     (encrypted priv key)\n                                                           │\n       deploy capsule.bin to target                        │\n       (safe — encrypted, can't be used without code)      ▼\n                                                     ┌──────────────┐\n                                                     │  elfdec-run   │\n       ELFDEC_CODE=\"mysecret\"  ─────────────────────▶│  (runtime)    │\n                                                     │  1. finds capsule\n                                                     │  2. derives K from code\n                                                     │  3. decrypts → priv key\n                                                     │  4. decrypts .elfenc\n                                                     │  5. executes via memfd\n                                                     └──────────────┘\n```\n\n**Step-by-step example:**\n\n```bash\n# ── BUILD TIME (on trusted machine) ──\n\n# 1. Generate keypair\n./genkey                    # → pub.bin, priv.bin\n\n# 2. Encrypt your binary\n./elfenc_pack pub.bin myapp myapp.elfenc\n\n# 3. Create a capsule (wraps priv.bin with a code)\ngcc -O2 kcap_pack.c -lsodium -o kcap_pack\n\n# KCAP3 binary capsule (the only output format kcap_pack writes today):\n./kcap_pack --label prod/myapp --code \"s3cret-C0de\" --in priv.bin --out capsule.bin\n\n# `--json` is deprecated and prints a warning; KCAP3 is binary-only.\n# `--ttl` is also deprecated (TTL was a KCAP2 wrapper concern).\n\n# 4. Deploy to target: myapp.elfenc + capsule.bin (priv.bin stays here — never deployed)\n\n# ── RUNTIME (on target machine) ──\n\n# Option A: capsule on disk via ELFDEC_KEYPATH\nmkdir -p /opt/myapp/keys\ncp capsule.bin /opt/myapp/keys/     # elfdec-run looks for capsule.bin (or legacy capsule.json) here\nELFDEC_CODE=\"s3cret-C0de\" ELFDEC_KEYPATH=/opt/myapp/keys ./elfdec-run ./myapp.elfenc\n\n# Option B: capsule stored in kernel keyring (no disk file needed)\nkeyctl padd user \"elfdec_caps:prod/myapp\" @s \u003c capsule.bin\nELFDEC_CODE=\"s3cret-C0de\" ELFDEC_LABEL=\"prod/myapp\" ./elfdec-run ./myapp.elfenc\n#                                                     ▲\n#                    searches keyring for \"elfdec_caps:prod/myapp\", decrypts with code\n```\n\n**Capsule lookup order in elfdec-run:**\n1. Keyring: searches `@s` then `@u` for key named `elfdec_caps:\u003cELFDEC_LABEL\u003e`\n2. File: `$ELFDEC_KEYPATH/capsule.bin` then `$ELFDEC_KEYPATH/capsule.json`\n\n---\n\n### Mode 2 — Keyring by ID (ELFDEC_KEYID)\n\nUse when you know the exact numeric key ID (e.g. returned by `keyctl padd` or `keypushd`).\n\n```bash\n# Inject the raw private key into the session keyring\nKEYID=$(keyctl padd user \"elfdec:prod/myapp\" @s \u003c priv.bin)\nkeyctl setperm \"$KEYID\" 0x3f030000    # owner: view+read, possessor: all\nkeyctl timeout \"$KEYID\" 300            # auto-expire in 5 minutes\n\n# Run using the numeric key ID\nELFDEC_KEYID=\"$KEYID\" ./elfdec-run ./myapp.elfenc\n```\n\n---\n\n### Mode 3 — Keyring by Label (ELFDEC_LABEL)\n\nUse when the key is in the keyring with a known label. `elfdec-run` searches\nfor a key named `elfdec:\u003cELFDEC_LABEL\u003e` in `@s` (session) then `@u` (user).\n\n```bash\n# Inject key with label\nkeyctl padd user \"elfdec:prod/myapp\" @s \u003c priv.bin\nkeyctl setperm \"$(keyctl search @s user 'elfdec:prod/myapp')\" 0x3f030000\n\n# Run using label — elfdec-run searches for \"elfdec:prod/myapp\"\nELFDEC_LABEL=\"prod/myapp\" ./elfdec-run ./myapp.elfenc\n\n# If ELFDEC_LABEL is unset, the .elfenc file path is used as the label\n./elfdec-run ./myapp.elfenc   # searches for \"elfdec:\u003crealpath of myapp.elfenc\u003e\"\n```\n\n**Helper scripts for keyring injection:**\n- `add_keyring4.sh` — Full-featured: root/user detection, permission fallback loop, accessibility test\n- `add_keyring8.sh` — Concise production version: session keyring, perms, TTL, `@u` link\n\n---\n\n### Mode 4 — File-based (ELFDEC_KEYPATH or ~/.elfenc/)\n\nSimplest mode. Only `priv.bin` is needed on disk — the public key is derived\nautomatically via `crypto_scalarmult_base()`. Protect with filesystem permissions.\n\n```bash\n# Custom key directory\nsudo mkdir -p /etc/elfenc\nsudo cp priv.bin /etc/elfenc/\nsudo chmod 600 /etc/elfenc/priv.bin\n\nELFDEC_KEYPATH=/etc/elfenc ./elfdec-run ./myapp.elfenc\n\n# Default directory (~/.elfenc/) — no env vars needed\nmkdir -p ~/.elfenc\ncp priv.bin ~/.elfenc/\nchmod 600 ~/.elfenc/priv.bin\n\n./elfdec-run ./myapp.elfenc     # automatically finds ~/.elfenc/priv.bin, derives pub\n```\n\n\u003e **Note:** `pub.bin` is still needed by `elfenc_pack` at build time (encryption).\n\u003e At runtime, `elfdec-run` only needs `priv.bin` — it derives the public key.\n\n---\n\n## Environment Variables Reference\n\n| Variable | Used By | Description |\n|----------|---------|-------------|\n| `ELFDEC_CODE` | elfdec-run | Password/code to decrypt a capsule (triggers Mode 1) |\n| `ELFDEC_KEYID` | elfdec-run | Numeric key ID from kernel keyring (Mode 2) |\n| `ELFDEC_LABEL` | elfdec-run | Key label for keyring search + capsule lookup (Mode 3 / Mode 1) |\n| `ELFDEC_KEYPATH` | elfdec-run | Directory containing `{priv,pub}.bin` or `capsule.{bin,json}` (Mode 4 / Mode 1) |\n\n---\n\n## Key Distribution Methods\n\n### Local: add_keyring scripts\n\n```bash\n# Full-featured (detects root vs user, tries multiple permission masks)\nbash add_keyring4.sh\n\n# Concise production version\nbash add_keyring8.sh\n```\n\n### Remote via SSH: elfdec-ssh-pushkey.sh\n\nStreams the raw key bytes over SSH stdin into the remote kernel keyring.\nThe key never touches the remote disk.\n\n```bash\n# Push key to remote host (default TTL: 300s)\n./elfdec-ssh-pushkey.sh -H user@remote -l prod/myapp -k priv.bin\n\n# With custom TTL\n./elfdec-ssh-pushkey.sh -H user@remote -l prod/myapp -k priv.bin -t 600\n\n# Returns KEYID — use it on the remote machine:\n# ELFDEC_KEYID=\u003creturned_id\u003e /usr/local/bin/elfdec-run ./myapp.elfenc\n```\n\n### Remote via UDP: keypushd + keypush_send\n\nFor automated/programmatic key distribution over the network.\n\n**Protocol flow:**\n\n```\n ┌─────────────┐                           ┌─────────────┐\n │  keypushd    │  1. prints bootstrap JSON │  operator    │\n │  (target)    │ ◀─── stdout ────────────▶ │  (control)   │\n │              │     {ip, port, srv_pk,    │              │\n │              │      token, expires}      │              │\n │              │                           │              │\n │              │  2. sealed UDP datagram   │ keypush_send │\n │  recvfrom()  │ ◀──── network ─────────── │  (sender)    │\n │              │     crypto_box_seal(      │              │\n │              │       {label, ttl, token, │              │\n │              │        key_b64})          │              │\n │              │                           │              │\n │  add_key()   │  3. ACK/NAK JSON reply   │              │\n │  keyring @s  │ ─────── UDP ──────────▶   │              │\n └─────────────┘     {ok, keyid, ttl}       └─────────────┘\n```\n\n**The `srv_pk` (server public key) is NOT pre-generated** — keypushd creates an ephemeral\nX25519 keypair on every launch and prints the public key as base64 in the bootstrap JSON.\nThe operator copies `srv_pk` and `token` to the sender side (out-of-band: terminal, pipe, etc.).\n\n```bash\n# Build (both keypushd + keypush_send from the same directory)\ncd keypush \u0026\u0026 make\n\n# ── Step 1: Start the daemon on the TARGET machine ──\n./keypushd --bind 0.0.0.0 --port 9999 --label prod/myapp --ttl 300\n\n# Output (example):\n# {\"ip\":\"0.0.0.0\",\"port\":9999,\"srv_pk\":\"xK7b+...BASE64...\",\"token\":\"K3J7QRST...\",\"expires\":1706500000}\n#                                  ▲                             ▲\n#                                  │                             │\n#                    ephemeral pubkey (base64)          one-time auth token (base32)\n#                    copy this to --srv-pk-b64          copy this to --token\n\n# Full example with all options:\n./keypushd --bind 10.8.0.2 --port 0 --label prod/myapp --ttl 600 --window 120 --link-user --detach\n#                           ▲ port=0 picks a random free port     ▲ window = bootstrap expiry (seconds)\n\n# ── Step 2: Send the key FROM the control machine ──\ncat priv.bin | ./keypush_send \\\n    --ip 10.8.0.2 --port 9999 \\\n    --srv-pk-b64 \"xK7b+...BASE64...\" \\\n    --token \"K3J7QRST...\" \\\n    --label prod/myapp --ttl 300 --wait-ack\n\n# Output (if --wait-ack): {\"ok\":true,\"keyid\":827867509,\"ttl\":300}\n\n# ── Step 3: Run on the TARGET machine ──\nELFDEC_LABEL=\"prod/myapp\" ./elfdec-run ./myapp.elfenc\n```\n\n**keypushd CLI options:**\n\n| Flag | Default | Description |\n|------|---------|-------------|\n| `--bind IP` | *(required)* | IP address to bind UDP socket |\n| `--port N` | `0` (random) | UDP port; 0 = OS picks a free port |\n| `--label L` | *(from payload)* | Default label if sender omits it |\n| `--ttl N` | `300` | Max key TTL in seconds |\n| `--window N` | `60` | Seconds before the bootstrap token expires |\n| `--detach` | off | Fork to background after printing bootstrap JSON |\n| `--link-user` | off | Also link key into `@u` (user keyring) for persistence |\n\n**keypush_send CLI options:**\n\n| Flag | Default | Description |\n|------|---------|-------------|\n| `--ip IP` | *(required)* | Target keypushd IP address |\n| `--port N` | *(required)* | Target keypushd UDP port |\n| `--srv-pk-b64 B64` | *(required)* | Server public key (base64 from bootstrap JSON `srv_pk` field) |\n| `--token T` | *(required)* | Auth token (base32 from bootstrap JSON `token` field) |\n| `--label L` | *(required)* | Key label (e.g. `prod/myapp`) |\n| `--ttl N` | `300` | Requested key TTL in seconds (server caps it at its `--ttl` max) |\n| `--wait-ack` | off | Wait up to 3s for ACK/NAK JSON reply |\n\n---\n\n## binfmt_misc — Transparent .elfenc Execution\n\nRegister the ELFENC1 magic with the kernel so `.elfenc` files can be executed directly:\n\n```bash\nsudo bash register_elfenc.sh\n\n# Now .elfenc files work like regular executables:\nchmod +x myapp.elfenc\nELFDEC_LABEL=\"prod/myapp\" ./myapp.elfenc\n```\n\n---\n\n## File Reference\n\n### Core Tools\n\n| File | Build | Description |\n|------|-------|-------------|\n| `ephrun/genkey.c` | `gcc -O2 genkey.c -lsodium -o genkey` | Generate X25519 keypair (pub.bin + priv.bin) |\n| `ephrun/elfenc_pack.c` | `gcc -O2 elfenc_pack.c -lsodium -o elfenc_pack` | Encrypt ELF → ELFENC1 format |\n| `ephrun/kcap_pack.c` | `gcc -O2 kcap_pack.c -lsodium -o kcap_pack` | Create KCAP3 capsules (Argon2id-wrapped key, params in header) |\n| `ephrun/kcap_unpack.c` | `gcc -O2 kcap_unpack.c -lsodium -o kcap_unpack` | Cross-platform CLI: unwrap KCAP1/KCAP2/KCAP3 → priv key (for tooling/CI) |\n| `ephrun/elfdec-run.c` | `gcc -O2 -D_GNU_SOURCE elfdec-run.c -lsodium -lkeyutils -o elfdec-run` | Decrypt + execute .elfenc (4-tier key sourcing; D-16 hardened fallback, D-17 env scrub, D-18 ptrace check) |\n| `ephrun/hello.c` | `gcc -O2 hello.c -o hello` | Minimal test binary for the pipeline |\n\n### Libraries / Headers\n\n| File | Description |\n|------|-------------|\n| `ephrun/kcap.h` | Shared KCAP capsule header (KCAP1/KCAP2/KCAP3 dispatch, Argon2id + legacy SHA256 KDF, D-15 param policy floor/ceiling) |\n| `ephrun/libexec_key.h` | Header-only key loading library (file, keyring, env, capsule) |\n\n### Key Distribution\n\n| File | Description |\n|------|-------------|\n| `ephrun/add_keyring4.sh` | Full-featured local keyring setup (root/user, perm fallback, test) |\n| `ephrun/add_keyring8.sh` | Concise production keyring setup |\n| `ephrun/elfdec-ssh-pushkey.sh` | Push key to remote host via SSH stdin |\n| `keypush/keypushd.c` | UDP key receiver daemon (Linux only) |\n| `keypush/keypush_send.c` | UDP key sender (cross-platform: Linux + macOS) |\n\n### Testing \u0026 Diagnostics\n\n| File | Build | Description |\n|------|-------|-------------|\n| `test.sh` | `bash test.sh` | **Full end-to-end test suite** — builds all tools, tests all 4 key modes, negative tests |\n| `ephrun/test_key.c` | `gcc -O2 test_key.c -lkeyutils -o test_key` | Verify a key exists in the keyring |\n| `ephrun/keyring_selftest.c` | `gcc -O2 -D_GNU_SOURCE keyring_selftest.c -lsodium -lkeyutils -o keyring_selftest` | Self-test: keyring add/read + crypto_secretbox roundtrip |\n| `ephrun/keyring_crypto_test.c` | `make` (in ephrun/) | AES-256-CBC roundtrip with keyring-sourced key (OpenSSL) |\n\n### Helper Scripts\n\n| File | Description |\n|------|-------------|\n| `ephrun/install.sh` | Full install: deps + build + keygen + test encrypt/run |\n| `ephrun/elf_comp.sh` | Compile elfdec-run + install keys to /etc/elfenc |\n| `ephrun/genkey.sh` | Compile + run genkey in one step |\n| `ephrun/run.sh` | Test script: KEYID and LABEL execution modes |\n| `ephrun/register_elfenc.sh` | Register .elfenc with binfmt_misc |\n\n---\n\n## Dependencies\n\n| Package | Used by | Purpose |\n|---------|---------|---------|\n| `libsodium-dev` | all C programs | X25519, XSalsa20-Poly1305, XChaCha20, SHA256 |\n| `libkeyutils-dev` | elfdec-run, keypushd, tests | Linux kernel keyring API |\n| `libssl-dev` | keyring_crypto_test only | OpenSSL EVP (AES-256-CBC) |\n| `build-essential` | all | GCC toolchain |\n\n```bash\n# Ubuntu/Debian\nsudo apt-get install -y build-essential libsodium-dev libkeyutils-dev libssl-dev\n\n# RHEL/CentOS/Fedora\nsudo yum install -y keyutils-libs-devel libsodium-devel gcc openssl-devel\n\n# macOS (keypush_send only — keypushd and elfdec-run require Linux keyring)\nbrew install libsodium\n```\n\n## Security Features\n\n- **Transport:** X25519 + XSalsa20-Poly1305 authenticated encryption (sealed boxes)\n- **Key storage:** Linux kernel keyring (memory-protected, process-isolated, TTL support)\n- **Execution:** Sealed memfd (MFD_EXEC + F_SEAL_SHRINK/GROW/WRITE) — on the normal path, plaintext never touches a filesystem path\n- **Process hardening:** PR_SET_DUMPABLE=0, PR_SET_NO_NEW_PRIVS=1\n- **Memory safety:** sodium_memzero on all sensitive data, sodium_mlock on decrypted capsule keys\n- **Capsule encryption:** XChaCha20-Poly1305 AEAD with Argon2id key derivation; KCAP3 stores T/M/P in the header (default T=3, M=64 MiB, P=1) and AAD-binds the entire 64-byte header. KCAP2 (Argon2id, fixed params) and KCAP1 (SHA256 KDF) remain readable for migration.\n- **Build hardening:** -fstack-protector-strong, -D_FORTIFY_SOURCE=2, -Wl,-z,relro,-z,now\n- **Core dumps disabled:** RLIMIT_CORE set to zero in keypushd; PR_SET_DUMPABLE=0 in elfdec-run\n- **Portable binary formats:** ELFENC1, KCAP1, KCAP2, KCAP3 use explicit little-endian encoding for cross-architecture compatibility\n- **`/dev/shm` fallback hardening (D-16):** triggers only on `fexecve` ETXTBSY; the temp file is unlinked before `execveat(rofd, \"\", argv, envp, AT_EMPTY_PATH)` so the workload runs from an unreachable inode; on exec failure the inode is wiped via `/proc/self/fd/N`. O_EXCL guards the brief tmpfs creation window. Requires Linux ≥ 3.19.\n- **Env scrub (D-17):** `elfdec-run` strips `ELFDEC_CODE / KEYID / LABEL / KEYPATH / CAP / ALLOW_TRACE` from `envp` before any `fexecve` / `execveat`, so the workload's `/proc/\u003cpid\u003e/environ` does not leak the unwrap inputs.\n- **Ptrace defense (D-18):** `elfdec-run` reads `/proc/self/status` at startup and aborts before any decryption work if `TracerPid != 0`. `ELFDEC_ALLOW_TRACE=1` bypasses for dev. Defeated by root via `/proc` manipulation; this is bar-raising, not a security guarantee.\n\n## Detecting Docker / VM Environments\n\nWhen deploying to rented servers, it's important to know whether you're running\nin a container (weaker isolation) or a real VM. ephrun protects the binary on\ndisk, but a Docker host with root access can inspect process memory.\n\n```bash\n# Quick checks — run any/all of these on the target machine:\n\n# 1. Docker sentinel file (most reliable)\nls -la /.dockerenv 2\u003e/dev/null \u0026\u0026 echo \"DOCKER\" || echo \"not docker (by sentinel)\"\n\n# 2. Cgroup — look for \"docker\" or \"containerd\" in cgroup paths\ncat /proc/1/cgroup 2\u003e/dev/null | grep -qiE 'docker|containerd' \u0026\u0026 echo \"DOCKER\" || echo \"not docker (by cgroup)\"\n\n# 3. systemd-detect-virt (if installed)\n#    Returns \"docker\", \"lxc\", \"kvm\", \"vmware\", \"xen\", \"none\", etc.\nsystemd-detect-virt 2\u003e/dev/null || echo \"(not installed)\"\n\n# 4. Container-specific mounts\nmount | grep -q 'overlay' \u0026\u0026 echo \"likely container (overlay fs)\"\n\n# 5. PID 1 — in a container it's usually NOT systemd/init\ncat /proc/1/cmdline | tr '\\0' ' ' | head -c 200\n# systemd or /sbin/init → real VM;  /bin/sh or custom entrypoint → container\n\n# 6. DMI / product name (VMs expose hypervisor info)\ncat /sys/class/dmi/id/product_name 2\u003e/dev/null\n# \"QEMU\", \"VMware Virtual Platform\", \"VirtualBox\", etc. → real VM\n# empty or permission denied → possibly container\n\n# 7. Kernel keyring — containers often have restricted keyring access\nkeyctl show @s 2\u003e/dev/null \u0026\u0026 echo \"keyring OK\" || echo \"keyring restricted (container?)\"\n```\n\n**Summary:**\n- Container (Docker/LXC): host has root over your processes — use short key TTLs, push keys just before execution, minimize execution windows\n- Real VM (KVM/QEMU/VMware): stronger isolation — host can still snapshot RAM, but needs memory forensics to extract anything\n- Neither is bulletproof on hardware you don't own; ephrun raises the bar from \"copy a file\" to \"live memory forensics\"\n\n---\n\n## Development note\n\nThis project was AI-assisted — though in 2026, what isn't? Design, code, and\nrelease decisions remain maintainer-owned.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnmicic%2Fephrun","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnmicic%2Fephrun","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnmicic%2Fephrun/lists"}