|
| 1 | +# Secret File Permissions |
| 2 | + |
| 3 | +The node reads several sensitive configuration values from files via the |
| 4 | +`*_FILE` environment variables. To avoid silently accepting insecure secret |
| 5 | +mounts, the node validates the file **type and permissions** before reading |
| 6 | +the contents and rejects files that violate the policy. |
| 7 | + |
| 8 | +This applies to the file-backed variants of: |
| 9 | + |
| 10 | +| Variable | Policy | Sensitivity | |
| 11 | +| ------------------------------------------- | ------------- | ---------------------------------------- | |
| 12 | +| `CARTESI_AUTH_MNEMONIC_FILE` | strict-secret | signing material (mnemonic) | |
| 13 | +| `CARTESI_AUTH_PRIVATE_KEY_FILE` | strict-secret | signing material (private key) | |
| 14 | +| `CARTESI_BLOCKCHAIN_HTTP_AUTHORIZATION_FILE`| credential | outbound authorization header | |
| 15 | +| `CARTESI_DATABASE_CONNECTION_FILE` | credential | database connection string (DSN) | |
| 16 | +| `CARTESI_BLOCKCHAIN_HTTP_ENDPOINT_FILE` | regular-file | endpoint without embedded credentials | |
| 17 | + |
| 18 | +## Policy tiers |
| 19 | + |
| 20 | +### strict-secret (signing material) |
| 21 | + |
| 22 | +Applies to the mnemonic and private-key files. |
| 23 | + |
| 24 | +The file: |
| 25 | + |
| 26 | +- **must be a regular file** (not a directory, device, pipe, etc.); |
| 27 | +- **must not be readable, writable, or executable by group or world** |
| 28 | + (`mode & 0o077` must be `0`, i.e. owner-only, such as `0400` or `0600`); |
| 29 | +- **must be owned by the current effective user** on POSIX systems. |
| 30 | + |
| 31 | +On violation the node refuses to start. This is a hard failure, not a warning. |
| 32 | + |
| 33 | +### credential (authorization headers, DSNs) |
| 34 | + |
| 35 | +Applies to files carrying passwords or authorization tokens. |
| 36 | + |
| 37 | +The file: |
| 38 | + |
| 39 | +- **must be a regular file**; |
| 40 | +- **must not be world-readable or world-writable** |
| 41 | + (`mode & 0o007` must be `0`). |
| 42 | + |
| 43 | +Group-readable files are accepted (e.g. `0640`), so a shared group can mount |
| 44 | +credentials when needed. |
| 45 | + |
| 46 | +### regular-file (non-secret endpoint files) |
| 47 | + |
| 48 | +Applies to endpoint files that do not carry credentials. |
| 49 | + |
| 50 | +The file: |
| 51 | + |
| 52 | +- **must be a regular file**. |
| 53 | + |
| 54 | +No permission restrictions beyond that. This tier keeps URL convenience files |
| 55 | +from being conflated with private key material. |
| 56 | + |
| 57 | +## Non-POSIX platforms |
| 58 | + |
| 59 | +On Windows and other platforms that do not expose POSIX mode semantics, only the |
| 60 | +regular-file check is enforced; mode and ownership checks are skipped. Treat |
| 61 | +this as best-effort hardening, not cross-platform parity. |
| 62 | + |
| 63 | +## Required file modes |
| 64 | + |
| 65 | +When mounting secrets into the container, use owner-only permissions for signing |
| 66 | +material: |
| 67 | + |
| 68 | +```sh |
| 69 | +chmod 600 /run/secrets/auth_mnemonic |
| 70 | +chmod 600 /run/secrets/auth_private_key |
| 71 | +``` |
| 72 | + |
| 73 | +and group-only or owner-only permissions for credential files: |
| 74 | + |
| 75 | +```sh |
| 76 | +chmod 640 /run/secrets/blockchain_http_authorization |
| 77 | +chmod 640 /run/secrets/database_connection |
| 78 | +``` |
| 79 | + |
| 80 | +The node runs as a non-root user (`cartesi`, uid 102, gid 102). Secret files must |
| 81 | +be readable by that user. |
| 82 | + |
| 83 | +## Docker Compose |
| 84 | + |
| 85 | +Docker Compose mounts secrets into `/run/secrets/<name>`. By default they are |
| 86 | +**owned by `root`** and **world-readable (`0444`)**, which the `strict-secret` |
| 87 | +policy rejects. Mount signing secrets with an explicit mode and ownership so |
| 88 | +they are readable only by the node user, for example: |
| 89 | + |
| 90 | +```yaml |
| 91 | +services: |
| 92 | + node: |
| 93 | + secrets: |
| 94 | + - source: auth_mnemonic |
| 95 | + target: auth_mnemonic |
| 96 | + uid: "102" |
| 97 | + gid: "102" |
| 98 | + mode: 0400 |
| 99 | + |
| 100 | +secrets: |
| 101 | + auth_mnemonic: |
| 102 | + file: test/secrets/auth_mnemonic.txt |
| 103 | +``` |
| 104 | +
|
| 105 | +> Note: the `uid`, `gid`, and `mode` fields on service secrets require a recent |
| 106 | +> Docker Compose version. Verify support in your environment; if they are |
| 107 | +> unsupported, mount the secret as a bind volume with the host file owned by uid |
| 108 | +> 102 and mode `0400` instead. |
| 109 | + |
| 110 | +The node rejects insecure secret files at startup with a message such as: |
| 111 | + |
| 112 | +```text |
| 113 | +failed to parse CARTESI_AUTH_MNEMONIC_FILE: file "/run/secrets/auth_mnemonic" |
| 114 | +is accessible by group or others; expected owner-only permissions |
| 115 | +``` |
| 116 | + |
| 117 | +Error messages never include the file contents. |
0 commit comments