bore

repository·main·Indexed 11 days ago

https://github.com/ekzhang/bore

A modern, simple TCP tunnel written in Rust that exposes local ports to a remote server, bypassing NAT and firewalls. Version 0.6.0 provides a CLI for local port forwarding and a self-hostable server, featuring HMAC-SHA256 authentication and a programmatic API for integration with Tokio.

Tokens
4.3K
Snippets
19
Records
22
Agent score
95%

What's inside bore

  1. How the bore protocol works

    main

    The bore protocol uses a control port (default 7835) to manage connections:

    1. Initialization: The client sends a "Hello" message to the server on the control port, requesting to proxy a specific remote port.
    2. Acknowledgement: The server acknowledges and begins listening for external TCP connections on that remote port.
    3. Connection Handshake: When an external connection hits the remote port, the server generates a unique UUID and sends it back to the client.
    4. Acceptance: The client opens a separate TCP stream to the server and sends an "Accept" message containing that UUID.
    5. Proxying: The server then proxies traffic between the two connections.

    Note: To prevent memory leaks, the server discards incoming connections if the client does not "Accept" them within 10 seconds.

  2. Authenticate tunnels with a secret

    main

    To prevent unauthorized users from using your self-hosted bore server, you can require a secret. The client must provide the same secret used by the server to pass an HMAC-based handshake.

    1. Start the server with a secret:

    bore server --secret my_secret_string

    2. Connect the client with the same secret:

    bore local <LOCAL_PORT> --to <TO> --secret my_secret_string

    Alternatively, you can use the BORE_SECRET environment variable on both the client and server to avoid passing the secret as a CLI argument.

    # on the server
    bore server --secret my_secret_string
    
    # on the client
    bore local <LOCAL_PORT> --to <TO> --secret my_secret_string
  3. Run a `bore` server for self-hosting

    main

    You can self-host your own bore server to manage your own tunnels. Start the server by running:

    bore server

    Once running, clients can connect to it using bore local <PORT> --to <YOUR_SERVER_ADDRESS>.

    Server Options

    • --min-port <MIN_PORT>: Minimum accepted TCP port number (default: 1024). Set via BORE_MIN_PORT.
    • --max-port <MAX_PORT>: Maximum accepted TCP port number (default: 65535). Set via BORE_MAX_PORT.
    • -s, --secret <SECRET>: Optional secret for authentication. Set via BORE_SECRET.
    • --bind-addr <BIND_ADDR>: IP address the control server binds to (default: 0.0.0.0). Set via BIND_ADDR.
    • --bind-tunnels <BIND_TUNNELS>: IP address where tunnels will listen. Defaults to the value of --bind-addr.
    bore server
  4. Install bore-cli

    main

    You can install bore using several methods depending on your operating system and preference:

    macOS

    Use Homebrew:

    brew install bore-cli

    Linux

    • Arch Linux: Use an AUR helper like yay:
      yay -S bore
    - **Gentoo Linux**: Use the `gentoo-zh` overlay:
      ```bash
    sudo eselect repository enable gentoo-zh
    sudo emerge --sync gentoo-zh
    sudo emerge net-proxy/bore

    Rust/Cargo

    If you have Rust installed, you can build from source:

    cargo install bore-cli

    Docker

    Run the statically-linked binary from a minimal container:

    docker run -it --init --rm --network host ekzhang/bore <ARGS>

    Binary Distribution

    Download prebuilt binaries for macOS, Windows, and Linux from the releases page, unzip them, and move the bore executable to a folder on your PATH.

    cargo install bore-cli
  5. Expose a local port using `bore local`

    main

    To forward a port from your local machine to a remote server, use the bore local command. This requires a positional argument for the <LOCAL_PORT> and a mandatory --to option specifying the remote server address.

    Example: Forwarding localhost:8000 to bore.pub

    bore local 8000 --to bore.pub

    This will expose your local port at localhost:8000 to the public internet at bore.pub:<PORT>, where the port number is assigned randomly.

    Options

    • -l, --local-host <HOST>: The local host to expose (default: localhost). Useful for exposing ports on your LAN instead of just the loopback address.
    • -t, --to <TO>: Address of the remote server (e.g., bore.pub). Can be set via BORE_SERVER env var.
    • -p, --port <PORT>: Optional specific port on the remote server to select (default: 0, which is random). The command fails if the requested port is unavailable.
    • -s, --secret <SECRET>: Optional secret for authentication. Can be set via BORE_SECRET env var.
    bore local 5000 --to bore.pub
  6. Use bore programmatically with Tokio

    main

    The bore crate provides implementations for both a server network daemon and a client local forwarding proxy. These components are exposed as public modules (server and client) and can be integrated into your own Rust applications using a tokio 1.0 runtime.

    // Example conceptual usage with Tokio 1.0
    #[tokio::main]
    async fn main() -> Result<(), Box<dyn std::error::Error>> {
        // Use bore::server or bore::client modules here
        Ok(())
    }
  7. Configure bore via environment variables

    main

    The bore CLI supports several configuration options through environment variables:

    VariableDescription
    BORE_LOCAL_PORTThe local port to expose for the local command.
    BORE_SERVERThe address of the remote server for the local command.
    BORE_SECRETThe authentication secret used by both local and server commands.
    BORE_MIN_PORTThe minimum accepted TCP port for the server command.
    BORE_MAX_PORTThe maximum accepted TCP port for the server command.
  8. Initialize a new Client with `Client::new`

    main

    Use Client::new to establish a control connection to a remote bore server and request a port forwarding tunnel.

    Parameters:

    • local_host: The hostname or IP address of the local service you want to forward.
    • local_port: The port number of the local service.
    • to: The address of the remote bore server.
    • port: The desired remote port (if 0, the server will typically assign one).
    • secret: An optional &str used for authentication if the server requires it.

    Returns: Returns a Result<Client>. On success, the client is connected and ready to start listening for incoming connections via .listen().

    use bore::Client;
    
    // Example: Forwarding localhost:8080 to a remote server at 1.2.3.4
    let client = Client::new(
        "127.0.0.1", 
        8080, 
        "1.2.3.4:5000", 
        0, 
        Some("my-secret-token")
    ).await?;
    
    println!("Remote port assigned: {}", client.remote_port());
  9. Initialize a new `Server` instance

    main

    To host TCP tunnels, create a new Server instance by providing a RangeInclusive<u16> representing the allowed port range for forwarding and an optional &str secret for client authentication. The server defaults to binding the control interface and tunnel interface to 0.0.0.0 (unspecified IPv4).

    use std::ops::RangeInclusive;
    use bore::Server;
    
    // Create a server that can use ports 8000 through 9000
    // and requires a secret for authentication
    let mut server = Server::new(8000..=9000, Some("my-secret-key"));
  10. Configure server binding addresses

    main

    You can explicitly set the IP addresses where the control server and the actual TCP tunnels will listen using set_bind_addr and set_bind_tunnels.

    use std::net::IpAddr;
    use std::str::FromStr;
    
    // Set the control server to listen on a specific IP
    server.set_bind_addr(IpAddr::from_str("127.0.0.1").unwrap());
    
    // Set the tunnels to listen on a specific IP
    server.set_bind_tunnels(IpAddr::from_str("10.0.0.1").unwrap());
  11. Explore the bore public modules

    main

    The library is organized into several public modules that define its core functionality:

    • auth: Handles authentication mechanisms for the tunnel.
    • client: Contains the implementation for the client local forwarding proxy.
    • server: Contains the implementation for the server network daemon.
    • shared: Contains shared logic and data structures used by both client and server.
  12. Use the Authenticator to secure connections

    main

    The Authenticator struct provides a mechanism for securing connections between a bore client and server using a shared secret. It uses HMAC-SHA256 to perform a challenge-response handshake.

    To use it, you initialize an Authenticator with a secret string. The server uses server_handshake to issue a UUID challenge and validate the client's response, while the client uses client_handshake to receive the challenge and provide the correct HMAC tag.

    use bore_cli::auth::Authenticator;
    use uuid::Uuid;
    
    // Initialize with a shared secret
    let auth = Authenticator::new("my_shared_secret");
    
    // Example of manual challenge-response validation
    let challenge = Uuid::new_v4();
    let tag = auth.answer(&challenge);
    
    assert!(auth.validate(&challenge, &tag));