mirror of
https://github.com/ekzhang/bore.git
synced 2025-12-16 20:07:51 +01:00
Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
19e7da1aad | ||
|
|
0128459a50 | ||
|
|
299ad61030 | ||
|
|
6a71c9a855 | ||
|
|
dd954c98e2 | ||
|
|
53dad89514 | ||
|
|
03f2e53f39 | ||
|
|
e137357267 | ||
|
|
4bdb00c385 | ||
|
|
2a2541e866 | ||
|
|
aa0d6e0ae5 | ||
|
|
b23beb98a2 |
2
Cargo.lock
generated
2
Cargo.lock
generated
@@ -113,7 +113,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "bore-cli"
|
||||
version = "0.5.2"
|
||||
version = "0.5.3"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "bore-cli"
|
||||
version = "0.5.2"
|
||||
version = "0.5.3"
|
||||
authors = ["Eric Zhang <ekzhang1@gmail.com>"]
|
||||
license = "MIT"
|
||||
description = "A modern, simple TCP tunnel in Rust that exposes local ports to a remote server, bypassing standard NAT connection firewalls."
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM rust:alpine as builder
|
||||
FROM rust:alpine AS builder
|
||||
WORKDIR /home/rust/src
|
||||
RUN apk --no-cache add musl-dev
|
||||
COPY . .
|
||||
|
||||
46
README.md
46
README.md
@@ -23,20 +23,48 @@ Similar to [localtunnel](https://github.com/localtunnel/localtunnel) and [ngrok]
|
||||
|
||||
## Installation
|
||||
|
||||
If you're on macOS, `bore` is packaged as a Homebrew core formula.
|
||||
### macOS
|
||||
|
||||
`bore` is packaged as a Homebrew core formula.
|
||||
|
||||
```shell
|
||||
brew install bore-cli
|
||||
```
|
||||
|
||||
### Linux
|
||||
|
||||
#### Arch Linux
|
||||
|
||||
`bore` is available in the AUR as `bore`.
|
||||
|
||||
```shell
|
||||
yay -S bore # or your favorite AUR helper
|
||||
```
|
||||
|
||||
#### Gentoo Linux
|
||||
|
||||
`bore` is available in the [gentoo-zh](https://github.com/microcai/gentoo-zh) overlay.
|
||||
|
||||
```shell
|
||||
sudo eselect repository enable gentoo-zh
|
||||
sudo emerge --sync gentoo-zh
|
||||
sudo emerge net-proxy/bore
|
||||
```
|
||||
|
||||
### Binary Distribution
|
||||
|
||||
Otherwise, the easiest way to install bore is from prebuilt binaries. These are available on the [releases page](https://github.com/ekzhang/bore/releases) for macOS, Windows, and Linux. Just unzip the appropriate file for your platform and move the `bore` executable into a folder on your PATH.
|
||||
|
||||
### Cargo
|
||||
|
||||
You also can build `bore` from source using [Cargo](https://doc.rust-lang.org/cargo/), the Rust package manager. This command installs the `bore` binary at a user-accessible path.
|
||||
|
||||
```shell
|
||||
cargo install bore-cli
|
||||
```
|
||||
|
||||
### Docker
|
||||
|
||||
We also publish versioned Docker images for each release. The image is built for an AMD 64-bit architecture. They're tagged with the specific version and allow you to run the statically-linked `bore` binary from a minimal "scratch" container.
|
||||
|
||||
```shell
|
||||
@@ -65,14 +93,14 @@ Starts a local proxy to the remote server
|
||||
Usage: bore local [OPTIONS] --to <TO> <LOCAL_PORT>
|
||||
|
||||
Arguments:
|
||||
<LOCAL_PORT> The local port to expose
|
||||
<LOCAL_PORT> The local port to expose [env: BORE_LOCAL_PORT=]
|
||||
|
||||
Options:
|
||||
-l, --local-host <HOST> The local host to expose [default: localhost]
|
||||
-t, --to <TO> Address of the remote server to expose local ports to [env: BORE_SERVER=]
|
||||
-p, --port <PORT> Optional port on the remote server to select [default: 0]
|
||||
-s, --secret <SECRET> Optional secret for authentication [env: BORE_SECRET]
|
||||
-h, --help Print help information
|
||||
-h, --help Print help
|
||||
```
|
||||
|
||||
### Self-Hosting
|
||||
@@ -85,6 +113,8 @@ bore server
|
||||
|
||||
That's all it takes! After the server starts running at a given address, you can then update the `bore local` command with option `--to <ADDRESS>` to forward a local port to this remote server.
|
||||
|
||||
It's possible to specify different IP addresses for the control server and for the tunnels. This setup is useful for cases where you might want the control server to be on a private network while allowing tunnel connections over a public interface, or vice versa.
|
||||
|
||||
The full options for the `bore server` command are shown below.
|
||||
|
||||
```shell
|
||||
@@ -93,10 +123,12 @@ Runs the remote proxy server
|
||||
Usage: bore server [OPTIONS]
|
||||
|
||||
Options:
|
||||
--min-port <MIN_PORT> Minimum accepted TCP port number [default: 1024, env: BORE_MIN_PORT]
|
||||
--max-port <MAX_PORT> Maximum accepted TCP port number [default: 65535, env: BORE_MAX_PORT]
|
||||
-s, --secret <SECRET> Optional secret for authentication [env: BORE_SECRET]
|
||||
-h, --help Print help information
|
||||
--min-port <MIN_PORT> Minimum accepted TCP port number [env: BORE_MIN_PORT=] [default: 1024]
|
||||
--max-port <MAX_PORT> Maximum accepted TCP port number [env: BORE_MAX_PORT=] [default: 65535]
|
||||
-s, --secret <SECRET> Optional secret for authentication [env: BORE_SECRET]
|
||||
--bind-addr <BIND_ADDR> IP address to bind to, clients must reach this [default: 0.0.0.0]
|
||||
--bind-tunnels <BIND_TUNNELS> IP address where tunnels will listen on, defaults to --bind-addr
|
||||
-h, --help Print help
|
||||
```
|
||||
|
||||
## Protocol
|
||||
|
||||
17
src/main.rs
17
src/main.rs
@@ -1,3 +1,5 @@
|
||||
use std::net::IpAddr;
|
||||
|
||||
use anyhow::Result;
|
||||
use bore_cli::{client::Client, server::Server};
|
||||
use clap::{error::ErrorKind, CommandFactory, Parser, Subcommand};
|
||||
@@ -47,6 +49,14 @@ enum Command {
|
||||
/// Optional secret for authentication.
|
||||
#[clap(short, long, env = "BORE_SECRET", hide_env_values = true)]
|
||||
secret: Option<String>,
|
||||
|
||||
/// IP address to bind to, clients must reach this.
|
||||
#[clap(long, default_value = "0.0.0.0")]
|
||||
bind_addr: IpAddr,
|
||||
|
||||
/// IP address where tunnels will listen on, defaults to --bind-addr.
|
||||
#[clap(long)]
|
||||
bind_tunnels: Option<IpAddr>,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -67,6 +77,8 @@ async fn run(command: Command) -> Result<()> {
|
||||
min_port,
|
||||
max_port,
|
||||
secret,
|
||||
bind_addr,
|
||||
bind_tunnels,
|
||||
} => {
|
||||
let port_range = min_port..=max_port;
|
||||
if port_range.is_empty() {
|
||||
@@ -74,7 +86,10 @@ async fn run(command: Command) -> Result<()> {
|
||||
.error(ErrorKind::InvalidValue, "port range is empty")
|
||||
.exit();
|
||||
}
|
||||
Server::new(port_range, secret.as_deref()).listen().await?;
|
||||
let mut server = Server::new(port_range, secret.as_deref());
|
||||
server.set_bind_addr(bind_addr);
|
||||
server.set_bind_tunnels(bind_tunnels.unwrap_or(bind_addr));
|
||||
server.listen().await?;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
//! Server implementation for the `bore` service.
|
||||
|
||||
use std::{io, net::SocketAddr, ops::RangeInclusive, sync::Arc, time::Duration};
|
||||
use std::net::{IpAddr, Ipv4Addr};
|
||||
use std::{io, ops::RangeInclusive, sync::Arc, time::Duration};
|
||||
|
||||
use anyhow::Result;
|
||||
use dashmap::DashMap;
|
||||
@@ -23,6 +24,12 @@ pub struct Server {
|
||||
|
||||
/// Concurrent map of IDs to incoming connections.
|
||||
conns: Arc<DashMap<Uuid, TcpStream>>,
|
||||
|
||||
/// IP address where the control server will bind to.
|
||||
bind_addr: IpAddr,
|
||||
|
||||
/// IP address where tunnels will listen on.
|
||||
bind_tunnels: IpAddr,
|
||||
}
|
||||
|
||||
impl Server {
|
||||
@@ -33,15 +40,26 @@ impl Server {
|
||||
port_range,
|
||||
conns: Arc::new(DashMap::new()),
|
||||
auth: secret.map(Authenticator::new),
|
||||
bind_addr: IpAddr::V4(Ipv4Addr::UNSPECIFIED),
|
||||
bind_tunnels: IpAddr::V4(Ipv4Addr::UNSPECIFIED),
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the IP address where tunnels will listen on.
|
||||
pub fn set_bind_addr(&mut self, bind_addr: IpAddr) {
|
||||
self.bind_addr = bind_addr;
|
||||
}
|
||||
|
||||
/// Set the IP address where the control server will bind to.
|
||||
pub fn set_bind_tunnels(&mut self, bind_tunnels: IpAddr) {
|
||||
self.bind_tunnels = bind_tunnels;
|
||||
}
|
||||
|
||||
/// Start the server, listening for new connections.
|
||||
pub async fn listen(self) -> Result<()> {
|
||||
let this = Arc::new(self);
|
||||
let addr = SocketAddr::from(([0, 0, 0, 0], CONTROL_PORT));
|
||||
let listener = TcpListener::bind(&addr).await?;
|
||||
info!(?addr, "server listening");
|
||||
let listener = TcpListener::bind((this.bind_addr, CONTROL_PORT)).await?;
|
||||
info!(addr = ?this.bind_addr, "server listening");
|
||||
|
||||
loop {
|
||||
let (stream, addr) = listener.accept().await?;
|
||||
@@ -62,7 +80,7 @@ impl Server {
|
||||
|
||||
async fn create_listener(&self, port: u16) -> Result<TcpListener, &'static str> {
|
||||
let try_bind = |port: u16| async move {
|
||||
TcpListener::bind(("0.0.0.0", port))
|
||||
TcpListener::bind((self.bind_tunnels, port))
|
||||
.await
|
||||
.map_err(|err| match err.kind() {
|
||||
io::ErrorKind::AddrInUse => "port already in use",
|
||||
@@ -120,8 +138,9 @@ impl Server {
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
let host = listener.local_addr()?.ip();
|
||||
let port = listener.local_addr()?.port();
|
||||
info!(?port, "new client");
|
||||
info!(?host, ?port, "new client");
|
||||
stream.send(ServerMessage::Hello(port)).await?;
|
||||
|
||||
loop {
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
#![allow(clippy::items_after_test_module)]
|
||||
|
||||
use std::net::SocketAddr;
|
||||
use std::time::Duration;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user