Skip to content

security: keep automatic HTTPS and remote agent control loopback-only, harden app-side local CA - #67

Open
devin-ai-integration[bot] wants to merge 1 commit into
mainfrom
devin/1786554293-security-hardening
Open

security: keep automatic HTTPS and remote agent control loopback-only, harden app-side local CA#67
devin-ai-integration[bot] wants to merge 1 commit into
mainfrom
devin/1786554293-security-hardening

Conversation

@devin-ai-integration

Copy link
Copy Markdown

Summary

Security scan of the repo (secrets, injection, dependencies, CORS, debug endpoints, auth) turned up four real issues, all in the local-HTTPS and remote-agent paths. Most scan categories came back clean: no SQL anywhere, no CORS middleware, no debug/admin endpoints, npm audit clean, and the Rust audit gate already handles the only advisory (RUSTSEC-2023-0071, RSA not compiled). XPC entry points already authenticate peers (same-UID + pinned production signature), the guest vsock listeners already reject non-host CIDs, and exec paths use argv arrays, not shells.

1. Automatic HTTPS was reachable from the LAN (high). Both TLS proxies use NWListener, which has no bind-address knob, so unlike the plaintext proxy (explicit 127.0.0.1 bind) they accepted any peer — anyone on the network could reach every container/k8s backend by sending a Host: *.dory.local header to the HTTPS port. Loopback bind is not an option: :443 only binds unprivileged on the wildcard address, which is exactly why LoopbackTCPForwarder already listens wildcard and filters peers. Both proxies now do the same, failing closed on anything not recognisably loopback:

private func accept(_ client: NWConnection) {
    guard Self.isLoopbackPeer(client.endpoint) else { client.cancel(); return }   // new
    ...
}
// .ipv4 → isLoopback | .ipv6 → isLoopback or ::ffff:127.0.0.1 | .name → "localhost" | else false

2. dory-agent --daemon <addr> could serve unauthenticated Exec on a routable address (high). The control protocol has no in-band auth by design; safety comes entirely from the loopback default plus doryd's SSH tunnel, previously enforced only by a comment. daemon::serve now enforces it at both ends — mirroring the guest server's is_host_peer policy:

if !is_loopback_listen_addr(&listener.local_addr()?) { return Err(InvalidInput, "...") }
let (stream, peer) = listener.accept().await?;
if !is_loopback_peer(&peer) { continue; }

3. The app-side LocalCA (Dory/Net/LocalCA.swift) was materially weaker than its DorydKit twin (medium). It signs the CA the user is asked to trust in their login keychain, yet wrote ca.key/leaf keys with default (world-readable) permissions, interpolated caller-supplied domains straight into the openssl subjectAltName string, and passed the PKCS#12 passphrase in argv (visible via ps). Brought in line with DorydKit/LocalCA.swift: umask(0o177) around CA generation plus 0600 keys, validateCertificateName on the domain and every extra SAN (rejects commas, path separators, empty labels), and -passout env:DORY_LOCALCA_P12_PASS via a new optional environment: argument on Shell.run.

4. Hardcoded PKCS#12 passphrase "dory" (low, but a literal secret). AppStore.startTLS() issued and loaded the TLS identity with a compiled-in password; it now generates a per-start random one via AppStore.ephemeralIdentityPassword(), the same 24-byte pattern NetworkingController already uses.

All four are locked in by new test-security-contracts.sh assertions (including a repo-wide grep for password: "dory"), plus Rust unit tests for the loopback predicates and Swift tests for the peer filter, key permissions, SAN validation, and password randomness.

Verified on Linux: cargo fmt --check, cargo clippy --workspace --all-targets, cargo test --workspace (all green, new daemon tests included), test-rust-security.sh PASS, test-security-contracts.sh PASS. The Swift changes cannot be compiled here (Network/Security/Darwin are macOS-only) and rely on CI.

Link to Devin session: https://app.devin.ai/sessions/9247634b01b94efda78e09735c0e2fd1
Requested by: @Augani

…, harden local CA

Co-Authored-By: Augustus Otu <Augani7@gmail.com>
@Augani Augani self-assigned this Aug 12, 2026
@devin-ai-integration

Copy link
Copy Markdown
Author

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment, CI, and merge conflict monitoring

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant