Skip to content

Commit 1de4e6a

Browse files
committed
docs(node): note the verified working directory for relative p2p key paths
README, .env.example, and the clap help for GITLAWB_P2P_KEY now state that a relative key path is anchored at a verified working directory, and that an unsafe cwd or ancestor is refused with p2p off while HTTP stays up. Also normalizes two pre-existing em dashes in the README settings table.
1 parent 0a92145 commit 1de4e6a

4 files changed

Lines changed: 35 additions & 16 deletions

File tree

‎.env.example‎

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,15 @@ GITLAWB_KEY=/data/keys/identity.pem
99

1010
# Path to the node's persistent libp2p identity key file. Must include a
1111
# directory; the node refuses to start on a bare filename, because it will not
12-
# keep its p2p identity key in the working directory. On Unix it is created
13-
# 0600 inside a 0700 directory, and a loose key directory is tightened to 0700
14-
# on start; on other platforms no permissions are enforced. If the node logs
15-
# that it tightened a loose key directory, treat the key that was sitting there
16-
# as possibly exposed: delete it so a fresh identity is generated on the next
17-
# start. Keep it on a persistent volume so the PeerId survives redeploys.
12+
# keep its p2p identity key in the working directory. With a relative path the
13+
# node verifies the working directory (ownership and write permissions) before
14+
# using it; an unsafe cwd or ancestor is refused and p2p stays off while HTTP
15+
# remains up. On Unix it is created 0600 inside a 0700 directory, and a loose
16+
# key directory is tightened to 0700 on start; on other platforms no
17+
# permissions are enforced. If the node logs that it tightened a loose key
18+
# directory, treat the key that was sitting there as possibly exposed: delete
19+
# it so a fresh identity is generated on the next start. Keep it on a
20+
# persistent volume so the PeerId survives redeploys.
1821
# Default: ~/.gitlawb/p2p.key
1922
#GITLAWB_P2P_KEY=/data/keys/p2p.key
2023

‎README.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -391,7 +391,7 @@ Important node settings:
391391
| `GITLAWB_P2P_PORT` | libp2p QUIC/UDP port. Use `0` to disable. |
392392
| `GITLAWB_BOOTSTRAP_PEERS` | Comma-separated HTTP peer URLs. |
393393
| `GITLAWB_P2P_BOOTSTRAP` | Comma-separated libp2p multiaddrs. |
394-
| `GITLAWB_P2P_KEY` | Path to the node's persistent libp2p identity key file, which fixes the PeerId across restarts. Must name a key file inside a directory, such as `/data/keys/p2p.key` or `./keys/p2p.key`; bare filenames, directory paths, trailing `/`, and the filesystem root are refused at startup. On Unix a new key is created `0600` inside a `0700` directory; a loose key directory is tightened to `0700` on start. An existing key copied from backup with group or other bits set is rejected rather than repaired; run `chmod 600` on it before restarting. P2P stays off while HTTP remains up if the key cannot be loaded. On other platforms no permissions are enforced. Default `~/.gitlawb/p2p.key`; point it at a persistent volume when running in a container. |
394+
| `GITLAWB_P2P_KEY` | Path to the node's persistent libp2p identity key file, which fixes the PeerId across restarts. Must name a key file inside a directory, such as `/data/keys/p2p.key` or `./keys/p2p.key`; bare filenames, directory paths, trailing `/`, and the filesystem root are refused at startup. With a relative path the node verifies the working directory (ownership and write permissions) before using it. On Unix a new key is created `0600` inside a `0700` directory; a loose key directory is tightened to `0700` on start. An existing key copied from backup with group or other bits set is rejected rather than repaired; run `chmod 600` on it before restarting. P2P stays off while HTTP remains up if the key cannot be loaded, including when an unsafe ancestor or working directory is refused. On other platforms no permissions are enforced. Default `~/.gitlawb/p2p.key`; point it at a persistent volume when running in a container. |
395395
| `GITLAWB_BOOTSTRAP_DISABLE_SEEDS` | Disable embedded seed peers for isolated dev/test networks. |
396396
| `GITLAWB_REQUIRE_SIGNED_PEER_WRITES` | Require signed peer announce/sync writes. Defaults to `false` during the staged rollout below. |
397397
| `GITLAWB_ENFORCE_OWNER_PUSH` | Require the authenticated pusher to be the repo owner on `git-receive-pack`. **Defaults to `true`.** A `did:key` signature is authentication, not authorization — anyone can mint a key and sign — so with this off every signed caller may push to every repository, private ones included. Delegated and CI keys count as non-owners: a UCAN `git/push` capability is verified but not yet honored for authorization, so they cannot push while this is on. Set `false` only for a rolling upgrade; see [`docs/RUN-A-NODE.md`](docs/RUN-A-NODE.md). |

‎crates/gitlawb-node/src/config.rs‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,9 @@ pub struct Config {
120120
#[arg(long, env = "GITLAWB_P2P_PORT", default_value_t = 7546)]
121121
pub p2p_port: u16,
122122

123-
/// Path to the persistent libp2p identity key
123+
/// Path to the persistent libp2p identity key. With a relative path the
124+
/// node verifies the working directory (ownership and write permissions)
125+
/// before using it.
124126
#[arg(long, env = "GITLAWB_P2P_KEY", default_value = "~/.gitlawb/p2p.key")]
125127
pub p2p_key_path: String,
126128

‎crates/gitlawb-node/src/p2p/mod.rs‎

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -471,8 +471,9 @@ fn verify_and_create_ancestor_chain(dir: &Path, euid: u32) -> Result<()> {
471471
)
472472
};
473473
if fd < 0 {
474-
return Err(std::io::Error::last_os_error())
475-
.with_context(|| format!("failed to open the filesystem root for {}", dir.display()));
474+
return Err(std::io::Error::last_os_error()).with_context(|| {
475+
format!("failed to open the filesystem root for {}", dir.display())
476+
});
476477
}
477478
(OwnedFd(fd), PathBuf::from("/"))
478479
} else {
@@ -487,8 +488,9 @@ fn verify_and_create_ancestor_chain(dir: &Path, euid: u32) -> Result<()> {
487488
)
488489
};
489490
if fd < 0 {
490-
return Err(std::io::Error::last_os_error())
491-
.with_context(|| format!("failed to open the working directory for {}", dir.display()));
491+
return Err(std::io::Error::last_os_error()).with_context(|| {
492+
format!("failed to open the working directory for {}", dir.display())
493+
});
492494
}
493495
let display = std::env::current_dir()
494496
.map(|d| {
@@ -553,7 +555,9 @@ fn verify_and_create_ancestor_chain(dir: &Path, euid: u32) -> Result<()> {
553555
format!(
554556
"failed to create key directory component {} below {}",
555557
name.to_string_lossy(),
556-
acc.parent().map(|p| p.display().to_string()).unwrap_or_default()
558+
acc.parent()
559+
.map(|p| p.display().to_string())
560+
.unwrap_or_default()
557561
)
558562
});
559563
}
@@ -1919,7 +1923,11 @@ mod tests {
19191923
assert_eq!(keys_mode & 0o777, 0o700, "key directory must be owner-only");
19201924

19211925
let key_mode = std::fs::metadata(&path).unwrap().permissions().mode();
1922-
assert_eq!(key_mode & 0o777, 0o600, "key file must be owner-read/write only");
1926+
assert_eq!(
1927+
key_mode & 0o777,
1928+
0o600,
1929+
"key file must be owner-read/write only"
1930+
);
19231931

19241932
println!("p2p-key-multilevel: asserted");
19251933
}
@@ -2903,7 +2911,10 @@ mod tests {
29032911
"mode {mode:04o} must be refused for group write, got: {msg}"
29042912
);
29052913
// Refusal must not create the key directory or the key.
2906-
assert!(!ancestor.join("keys").exists(), "key dir must not be created");
2914+
assert!(
2915+
!ancestor.join("keys").exists(),
2916+
"key dir must not be created"
2917+
);
29072918
assert!(!path.exists(), "key must not be created");
29082919
}
29092920
}
@@ -2958,7 +2969,10 @@ mod tests {
29582969
"intermediate symlink refusal must name the symlink, got: {err:#}"
29592970
);
29602971
// The symlink target must be untouched: no keys dir, no key inside.
2961-
assert!(!real.join("keys").exists(), "symlink target must be untouched");
2972+
assert!(
2973+
!real.join("keys").exists(),
2974+
"symlink target must be untouched"
2975+
);
29622976
}
29632977

29642978
#[cfg(unix)]

0 commit comments

Comments
 (0)