mirror of
https://github.com/bitwarden/browser
synced 2025-12-10 21:33:27 +00:00
[PM-7846] Implement a rust based native messaging proxy and IPC system (#9894)
* [PM-7846] Implement a rust based native messaging proxy and IPC system
* Only build desktop_proxy
* Bundle the desktop_proxy file
* Make sys deps optional for the proxy
* Restore accidentally deleted after-sign
* Update native cache to contain dist folder
* Add some test logging
* Native module cache seems very aggressive
* Fix invalid directory
* Fix debug print
* Remove cache force
* Remove cache debug code
* Only log to file in debug builds
* Place the binary in the correct place for mac and make sure it's signed
* Fix platform paths
* Test unsigned appx
* Revert "Test unsigned appx"
This reverts commit e47535440a.
* Fix comment
* Remove logs
* Use debug builds in native code, and test private path on MacOS
* Add connected message
* Update IPC API comments
* Update linux to also use XDG_ dir
* Update main.rs comment
* Improve docs and split some tasks spawned into separate functions
* Update send docs and return number of elements sent
* Mark `listen` as async to ensure it runs in a tokio context, handle errors better
* Add log on client channel closed
* Move binary to MacOS folder, and sign it manually so it gets the correct entitlements
* Fix some review comments
* Run prettier
* Added missing zbus_polkit dep
* Extract magic number and increase it to match spec
* Comment fix
* Use Napi object, combine nativeBinding export, always log to file
* Missed one comment
* Remove unnecessary generics
* Correct comment
* Select only codesigning identities
* Filter certificates
* Also add local dev cert
* Remove log
* Fix package ID
* debug_assert won't run the pop() in release mode
* Better error messages
* Fix review comments
* Remove unnecessary comment
* Update napi generated TS file
* Temporary fix for DDG
This commit is contained in:
137
apps/desktop/desktop_native/proxy/src/main.rs
Normal file
137
apps/desktop/desktop_native/proxy/src/main.rs
Normal file
@@ -0,0 +1,137 @@
|
||||
use std::path::Path;
|
||||
|
||||
use desktop_core::ipc::{MESSAGE_CHANNEL_BUFFER, NATIVE_MESSAGING_BUFFER_SIZE};
|
||||
use futures::{SinkExt, StreamExt};
|
||||
use log::*;
|
||||
use tokio_util::codec::LengthDelimitedCodec;
|
||||
|
||||
fn init_logging(log_path: &Path, level: log::LevelFilter) {
|
||||
use simplelog::{ColorChoice, CombinedLogger, Config, SharedLogger, TermLogger, TerminalMode};
|
||||
|
||||
let config = Config::default();
|
||||
|
||||
let mut loggers: Vec<Box<dyn SharedLogger>> = Vec::new();
|
||||
loggers.push(TermLogger::new(
|
||||
level,
|
||||
config.clone(),
|
||||
TerminalMode::Stderr,
|
||||
ColorChoice::Auto,
|
||||
));
|
||||
|
||||
match std::fs::File::create(log_path) {
|
||||
Ok(file) => {
|
||||
loggers.push(simplelog::WriteLogger::new(level, config, file));
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("Can't create file: {}", e);
|
||||
}
|
||||
}
|
||||
|
||||
if let Err(e) = CombinedLogger::init(loggers) {
|
||||
eprintln!("Failed to initialize logger: {}", e);
|
||||
}
|
||||
}
|
||||
|
||||
/// Bitwarden IPC Proxy.
|
||||
///
|
||||
/// This proxy allows browser extensions to communicate with a desktop application using Native
|
||||
/// Messaging. This method allows an extension to send and receive messages through the use of
|
||||
/// stdin/stdout streams.
|
||||
///
|
||||
/// However, this also requires the browser to start the process in order for the communication to
|
||||
/// occur. To overcome this limitation, we implement Inter-Process Communication (IPC) to establish
|
||||
/// a stable communication channel between the proxy and the running desktop application.
|
||||
///
|
||||
/// Browser extension <-[native messaging]-> proxy <-[ipc]-> desktop
|
||||
///
|
||||
#[tokio::main(flavor = "current_thread")]
|
||||
async fn main() {
|
||||
let sock_path = desktop_core::ipc::path("bitwarden");
|
||||
|
||||
let log_path = {
|
||||
let mut path = sock_path.clone();
|
||||
path.set_extension("bitwarden.log");
|
||||
path
|
||||
};
|
||||
|
||||
init_logging(&log_path, LevelFilter::Info);
|
||||
|
||||
info!("Starting Bitwarden IPC Proxy.");
|
||||
|
||||
// Different browsers send different arguments when the app starts:
|
||||
//
|
||||
// Firefox:
|
||||
// - The complete path to the app manifest. (in the form `/Users/<user>/Library/.../Mozilla/NativeMessagingHosts/com.8bit.bitwarden.json`)
|
||||
// - (in Firefox 55+) the ID (as given in the manifest.json) of the add-on that started it (in the form `{[UUID]}`).
|
||||
//
|
||||
// Chrome on Windows:
|
||||
// - Origin of the extension that started it (in the form `chrome-extension://[ID]`).
|
||||
// - Handle to the Chrome native window that started the app.
|
||||
//
|
||||
// Chrome on Linux and Mac:
|
||||
// - Origin of the extension that started it (in the form `chrome-extension://[ID]`).
|
||||
|
||||
let args: Vec<_> = std::env::args().skip(1).collect();
|
||||
info!("Process args: {:?}", args);
|
||||
|
||||
// Setup two channels, one for sending messages to the desktop application (`out`) and one for receiving messages from the desktop application (`in`)
|
||||
let (in_send, in_recv) = tokio::sync::mpsc::channel(MESSAGE_CHANNEL_BUFFER);
|
||||
let (out_send, mut out_recv) = tokio::sync::mpsc::channel(MESSAGE_CHANNEL_BUFFER);
|
||||
|
||||
let mut handle = tokio::spawn(desktop_core::ipc::client::connect(
|
||||
sock_path, out_send, in_recv,
|
||||
));
|
||||
|
||||
// Create a new codec for reading and writing messages from stdin/stdout.
|
||||
let mut stdin = LengthDelimitedCodec::builder()
|
||||
.max_frame_length(NATIVE_MESSAGING_BUFFER_SIZE)
|
||||
.native_endian()
|
||||
.new_read(tokio::io::stdin());
|
||||
let mut stdout = LengthDelimitedCodec::builder()
|
||||
.max_frame_length(NATIVE_MESSAGING_BUFFER_SIZE)
|
||||
.native_endian()
|
||||
.new_write(tokio::io::stdout());
|
||||
|
||||
loop {
|
||||
tokio::select! {
|
||||
// IPC client has finished, so we should exit as well.
|
||||
_ = &mut handle => {
|
||||
break;
|
||||
}
|
||||
|
||||
// Receive messages from IPC and print to STDOUT.
|
||||
msg = out_recv.recv() => {
|
||||
match msg {
|
||||
Some(msg) => {
|
||||
debug!("OUT: {}", msg);
|
||||
stdout.send(msg.into()).await.unwrap();
|
||||
}
|
||||
None => {
|
||||
info!("Channel closed, exiting.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// Listen to stdin and send messages to ipc processor.
|
||||
msg = stdin.next() => {
|
||||
match msg {
|
||||
Some(Ok(msg)) => {
|
||||
let m = String::from_utf8(msg.to_vec()).unwrap();
|
||||
debug!("IN: {}", m);
|
||||
in_send.send(m).await.unwrap();
|
||||
}
|
||||
Some(Err(e)) => {
|
||||
error!("Error parsing input: {}", e);
|
||||
break;
|
||||
}
|
||||
None => {
|
||||
info!("Received EOF, exiting.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user