Repository navigation
feat(hush): add a broker daemon that alone holds the BWS token #75
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 13 commits
Commits
Show all changes
15 commits
Select commit
Hold shift + click to select a range
0bfe9cc
feat(hush): add broker daemon and metadata catalog
AojdevStudio 3c38647
fix(hush): harden broker token, socket, bws, and fallback paths
AojdevStudio 9b09338
fix(hush): read token metadata from the open file so the check builds…
AojdevStudio e651b46
fix(hush): harden trusted path, dev flag, bws timeout, socket dir
AojdevStudio c107963
test(hush): pin broker test temp dirs to 0700 so a loose umask does n…
AojdevStudio c1cc927
fix(hush): let debug same-user daemons trust root-owned ancestors of bws
AojdevStudio 36dfe94
fix(hush): apply review round fixes to the broker core
AojdevStudio f9b79c5
ci(gitguardian): ignore the ADR CLI example placeholder finding
AojdevStudio 41efb02
fix(hush): bound the socket liveness probe and wake the test listener
AojdevStudio 5dca726
fix(hush): keep saturated macOS listeners alive during the liveness p…
AojdevStudio a1e8b0b
fix(hush): make the saturation test deterministic and umask-proof
AojdevStudio beb0504
fix(hush): reach queue saturation deterministically in the probe test
AojdevStudio e5dd00e
docs(gitguardian): use the documented ggshield config filename
AojdevStudio ba3057d
chore(hush): trigger a fresh GitGuardian scan after the incident disp…
AojdevStudio 1559939
fix(hush): a failed poll in the liveness probe leaves the socket in p…
AojdevStudio File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| version: 2 | ||
| secret: | ||
| ignored_matches: | ||
| - name: "ADR CLI example uses env var names, not credentials. Incident 38176923" | ||
| match: "fad2adf0dc0cd83f2d9e202b92d0846e43b7f9025f3b1d679ba9afff1902211d" | ||
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,104 @@ | ||
| //! Daemon-owned audit log. Names only, never values or tokens. | ||
|
|
||
| use super::peercred::PeerCredentials; | ||
| use anyhow::{bail, Context, Result}; | ||
| use std::fs::{OpenOptions, Permissions}; | ||
| use std::io::Write; | ||
| use std::os::unix::fs::{MetadataExt, OpenOptionsExt, PermissionsExt}; | ||
| use std::path::{Path, PathBuf}; | ||
| use std::sync::Mutex; | ||
| use std::time::SystemTime; | ||
|
|
||
| /// The daemon records every catalog request with kernel peer identity. | ||
| pub(crate) trait AuditLog: Send + Sync { | ||
| fn record_catalog(&self, peer: PeerCredentials, project: Option<&str>) -> Result<()>; | ||
| } | ||
|
|
||
| /// Append-only file log. The daemon owns this file, not the client audit log. | ||
| pub(crate) struct FileAuditLog { | ||
| file: Mutex<std::fs::File>, | ||
| } | ||
|
|
||
| impl FileAuditLog { | ||
| pub(crate) fn open(path: &Path) -> Result<Self> { | ||
| if let Some(parent) = path.parent() { | ||
| std::fs::create_dir_all(parent)?; | ||
| } | ||
| let file = OpenOptions::new() | ||
| .create(true) | ||
| .append(true) | ||
| .mode(0o600) | ||
| .open(path) | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
| .with_context(|| format!("failed to open daemon audit log at {}", path.display()))?; | ||
| let metadata = file | ||
| .metadata() | ||
| .with_context(|| format!("failed to inspect daemon audit log at {}", path.display()))?; | ||
| // SAFETY: geteuid takes no arguments and cannot fail. | ||
| let euid = unsafe { libc::geteuid() }; | ||
| if metadata.uid() != euid { | ||
| bail!("daemon audit log must be owned by the daemon user"); | ||
| } | ||
| if metadata.mode() & 0o077 != 0 { | ||
| bail!("daemon audit log must not be group or world readable or writable"); | ||
| } | ||
| // A created file can still lose bits to umask; pin 0600 for every daemon run. | ||
| file.set_permissions(Permissions::from_mode(0o600)) | ||
| .with_context(|| format!("failed to secure daemon audit log at {}", path.display()))?; | ||
| Ok(Self { | ||
| file: Mutex::new(file), | ||
| }) | ||
| } | ||
| } | ||
|
|
||
| /// Escape client-controlled text so one record stays exactly one line. | ||
| /// Newlines, carriage returns, other control bytes, and backslash become | ||
| /// escapes, so injected lines cannot look like daemon-authored records. | ||
| fn sanitize(value: &str) -> String { | ||
| let mut out = String::with_capacity(value.len()); | ||
| for ch in value.chars() { | ||
| match ch { | ||
| '\\' => out.push_str("\\\\"), | ||
| '\n' => out.push_str("\\n"), | ||
| '\r' => out.push_str("\\r"), | ||
| c if (c as u32) < 0x20 || c == '\u{7f}' => { | ||
| out.push_str(&format!("\\x{:02x}", c as u32)) | ||
| } | ||
| c => out.push(c), | ||
| } | ||
| } | ||
| out | ||
| } | ||
|
|
||
| impl AuditLog for FileAuditLog { | ||
| fn record_catalog(&self, peer: PeerCredentials, project: Option<&str>) -> Result<()> { | ||
| let line = format!( | ||
| "{} catalog uid={} gid={} pid={} project={}\n", | ||
| humantime::format_rfc3339_seconds(SystemTime::now()), | ||
| peer.uid, | ||
| peer.gid, | ||
| peer.pid, | ||
| sanitize(project.unwrap_or("-")), | ||
| ); | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
| let mut file = self | ||
| .file | ||
| .lock() | ||
| .map_err(|_| anyhow::anyhow!("audit log lock poisoned"))?; | ||
| file.write_all(line.as_bytes())?; | ||
| Ok(()) | ||
| } | ||
| } | ||
|
|
||
| /// Default daemon audit path. The `daemon` subcommand can override it. | ||
| pub(crate) fn default_audit_path() -> PathBuf { | ||
| if let Some(path) = std::env::var_os("HUSH_AUDIT_LOG") { | ||
| return PathBuf::from(path); | ||
| } | ||
| #[cfg(target_os = "macos")] | ||
| { | ||
| PathBuf::from("/var/db/hush/audit.log") | ||
| } | ||
| #[cfg(not(target_os = "macos"))] | ||
| { | ||
| PathBuf::from("/var/log/hush/audit.log") | ||
| } | ||
| } | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.