diff --git a/docs/architecture/deep-dives/lock-states.md b/docs/architecture/deep-dives/lock-states.md index f92d23094..aef2902e6 100644 --- a/docs/architecture/deep-dives/lock-states.md +++ b/docs/architecture/deep-dives/lock-states.md @@ -47,11 +47,12 @@ locked (via the `EncryptedMemoryStore` abstraction, see ### SSH agent -The [SSH agent](./ssh/agent.md) holds the private keys of the active, unlocked account and wipes -them on lock. The public keys, however, are kept on lock so that listing still works. This means -that in the AFU state a client can list the available keys, the correct key for a server can be -chosen, and a signing request can be made - which then prompts the user to unlock their vault to -approve the request. In the BFU state no keys are present, so listing returns nothing. +The [SSH agent](./ssh/bitwarden-ssh-agent-architecture.md) holds the private keys of the active, +unlocked account and wipes them on lock. The public keys, however, are kept on lock so that listing +still works. This means that in the AFU state a client can list the available keys, the correct key +for a server can be chosen, and a signing request can be made - which then prompts the user to +unlock their vault to approve the request. In the BFU state no keys are present, so listing returns +nothing. ## Why secrets are re-hydrated rather than persisted diff --git a/docs/architecture/deep-dives/ssh/bitwarden-ssh-agent-architecture.md b/docs/architecture/deep-dives/ssh/bitwarden-ssh-agent-architecture.md new file mode 100644 index 000000000..df4312d52 --- /dev/null +++ b/docs/architecture/deep-dives/ssh/bitwarden-ssh-agent-architecture.md @@ -0,0 +1,188 @@ +# Bitwarden SSH Agent architecture + +![`Bitwarden SSH Agent high level`](./ssh-agent-v2-highlevel.png#center) + +Bitwarden’s SSH Agent is primarily implemented in the Rust language, as a server that accepts ssh +client connections over the SSH Agent protocol. This server interfaces with the Desktop UI through +napi bindings and the Electron services. + +Our agent is unique from native OS ones in that: + +- true storage of keys is on a (remote) server which individual Desktop clients interface with +- users of the agent manage keys with a UI +- users of the agent (optionally) approve requests with a UI + +--- + +## End to end data flow + +![`Bitwarden SSH Agent end-to-end`](./ssh-agent-v2-e2e.png) + +When the user enables the SSH Agent feature in user settings, the SSH key vault items are retrieved +from the vault and sent to the agent. The agent server is started and it’s keystore populated with +the new keys. + +The Bitwarden SSH Agent server spawns a thread to handle any each new client connection. Each +connection results in an async callback chain. + +The Electron app replaces the keys in the agent’s keystore when there are vault changes to SSH key +items. + +--- + +## The SSH Agent + +![`Bitwarden SSH Agent`](./ssh-agent-v2.png) + +The core agent implementation itself is part of the Rust Desktop Native layer in the Desktop client. +It is an almost entirely self-contained workspace crate within desktop_native. + +The architecture of the agent was designed with these goals: + +- efficient adherence to the SSH agent protocol spec +- clean interfaces isolating the server protocol, Bitwarden’s business logic, and user interface +- maintainability +- testability + +### Modules + +#### server + +Low level agent protocol implementation. It is responsible for listening for and managing new +connections (clients), handling requests from connections and returning responses. + +#### crypto + +Cryptographic primitive type definitions for public and private key types that we support and +handling of the signing of requests and any key-based operation. + +#### storage + +Logic that handles the storage and management of data that the agent uses, effectively the SSH keys. +Currently our implementation of a key store is in-memory. + +#### authorization + +Defines the concrete policy which our agent enforces for authorization of requests. The +authorization of requests is requested by the server. + +#### approval + +Interface for our agent to get approval for requests via an external entity (in our present case, +Electron). + +#### agent + +The agent crate itself is a the orchestrator of all the above components. It starts and stops the +server, updates the storage of keys, enforces the authorization policy, and interfaces with the +external approver of requests. + +## Abstractions + +There are two critical abstractions to understand about the agent implementation. These abstractions +both serve two purposes: + +1. Low coupling ; high cohesion + +2. Testability + +### AuthPolicy + +The server requests authorization for requests that it receives from client connections. + +The server does not need to know about the business logic that goes into whether a request is +authorized. + +This interface provides an abstraction from the raw server and the agent’s business logic. + +```rust +pub trait AuthPolicy: Send + Sync { + async fn authorize(&self, request: &AuthRequest) -> Result; +} +``` + +### ApprovalRequester + +The agent requests approval from an external entity, when needed based on it’s authorization policy. + +The agent does not need to know about the logic or complexities of the external entity that it +requests approvals through. + +```rust + pub trait ApprovalRequester: Send + Sync { + async fn request_sign_approval( + &self, + request: SignApprovalRequest, + ) -> Result; + + async fn request_list_approval(&self) -> Result; +} +``` + +--- + +## Napi bindings + +The `sshagent_v2` module contains the definitions for the napi bindings which act as the bridge +between the Rust implementation of the agent, and the Electron UI. + +This layer is intentionally a pass-through. There is no unique logic. A wrapper around the agent is +provided to expose it to Electron. + +The ElectronApprovalRequester is our concrete implementation of the ApprovalRequester trait. It +simply provides a mechanism to wire-up the async callbacks that we use in the Electron services to +answer the approval requests that are cascaded up from the server. + +--- + +## Electron services + +### Main service + +The Main SSH Agent service is primarily a pass-through for exchanging request/response calls between +Render and agent. + +It maintains a request ID counter and stores the pending requests keyed by that ID. + +### Render service + +The Render SSH Agent service is the primary business logic that interfaces between the vault data, +the user interactions, and the downstream agent. + +It has the following responsibilities: + +- subscribes to changes in the user setting for enabling the ssh agent feature, and correspondingly + starts/stops the agent. +- receives the vault ssh key items when any ssh key item is changed, and updates the agent with the + new key data. +- subscribes to active account changes, and either updates the key data or stops the server + depending on the new account’s state. +- receives authorization requests via the main service, and depending on the user’s setting for + authorization requests, may prompt the user to authorize the request, or grant it based on cached + approval, or automatically approve if authorizations aren’t configured. + +--- + +## Vault lock state behavior + +This section describes how the agent behaves in the various [vault states](../lock-states.md). For +simplicity, assume only one account is active on the client and that the feature is enabled in user +settings. + +### Logged out + +The agent is not running. + +### Locked Before First Unlock (BFU) + +The agent is running. All requests result in a toast prompt to unlock the vault. + +### Locked After First Unlock (AFU) + +The agent is running. List requests are supported. Sign requests are supported- if authorization is +required based on user setting, user is prompted to unlock their vault first, then prompted to +approve the request. + +### Unlocked + +The agent is running, all functionality supported. diff --git a/docs/architecture/deep-dives/ssh/index.mdx b/docs/architecture/deep-dives/ssh/index.mdx new file mode 100644 index 000000000..ada34069f --- /dev/null +++ b/docs/architecture/deep-dives/ssh/index.mdx @@ -0,0 +1,4 @@ +# SSH Keys and SSH Agent + +This section contains pages that build an understanding of what SSH keys are, what an SSH Agent is, +and how the Bitwarden SSH Agent is architected and implemented. diff --git a/docs/architecture/deep-dives/ssh/ssh-agent-v2-e2e.png b/docs/architecture/deep-dives/ssh/ssh-agent-v2-e2e.png new file mode 100644 index 000000000..f9f254b85 Binary files /dev/null and b/docs/architecture/deep-dives/ssh/ssh-agent-v2-e2e.png differ diff --git a/docs/architecture/deep-dives/ssh/ssh-agent-v2-highlevel.png b/docs/architecture/deep-dives/ssh/ssh-agent-v2-highlevel.png new file mode 100644 index 000000000..38d99c4e1 Binary files /dev/null and b/docs/architecture/deep-dives/ssh/ssh-agent-v2-highlevel.png differ diff --git a/docs/architecture/deep-dives/ssh/ssh-agent-v2.png b/docs/architecture/deep-dives/ssh/ssh-agent-v2.png new file mode 100644 index 000000000..45c855af2 Binary files /dev/null and b/docs/architecture/deep-dives/ssh/ssh-agent-v2.png differ diff --git a/docs/architecture/deep-dives/ssh/index.md b/docs/architecture/deep-dives/ssh/what-are-ssh-keys.md similarity index 90% rename from docs/architecture/deep-dives/ssh/index.md rename to docs/architecture/deep-dives/ssh/what-are-ssh-keys.md index 55e960cd0..142a56bf1 100644 --- a/docs/architecture/deep-dives/ssh/index.md +++ b/docs/architecture/deep-dives/ssh/what-are-ssh-keys.md @@ -1,12 +1,4 @@ -# SSH Keys and Agent - -:::info - -For more information on how Bitwarden implements its SSH agent, see [SSH Agent](./agent). - -::: - -## What are SSH Keys? +# What are SSH keys? SSH keys are asymmetric key pairs that can be used for signing. Historically they were intended for logging into servers, but recently they can also be used for signing arbitrary data such as Git diff --git a/docs/architecture/deep-dives/ssh/agent.md b/docs/architecture/deep-dives/ssh/what-is-an-ssh-agent.md similarity index 99% rename from docs/architecture/deep-dives/ssh/agent.md rename to docs/architecture/deep-dives/ssh/what-is-an-ssh-agent.md index b1f05704d..306ca4681 100644 --- a/docs/architecture/deep-dives/ssh/agent.md +++ b/docs/architecture/deep-dives/ssh/what-is-an-ssh-agent.md @@ -1,4 +1,4 @@ -# SSH Agent +# What is an SSH Agent? An SSH agent acts as a program that holds a set of private keys and provides a way to sign challenges with those keys, without ever letting the private keys leave the vault. These challenges