View on GitHub

dotnet-tor

A .NET cross-platform CLI app that uses TorSharp to run a local proxy

Icon dotnet-tor

Version Downloads License

Installing or updating (same command can be used for both):

dotnet tool update -g dotnet-tor

Usage:

> dotnet tor -?
tor
  Tor proxy service

Usage:
  tor [options] [command]

Options:
  -p, --proxy <proxy>      Proxy port [default: 1337]
  -s, --socks <socks>      Socks port [default: 1338]
  -c, --control <control>  Control port [default: 1339]
  --version                Show version information
  -?, -h, --help           Show help and usage information

Commands:
  add <name> <service>  Adds a service to register on the Tor network
  config                Edits the full torrc configuration file.

The program will automatically check for updates once a day and recommend updating if there is a new version available.

Configured services are persisted in the global dotnet-config file at %userprofile%\tor\.netconfig, and on first run (after configuration), their .onion address and keys will be available in a sub-directory alongside the .netconfig. This allows the tool to self-update while preserving all configurations and services.

Exposing local HTTP APIs via Tor

After installation, you might want to expose an .NET Core HTTP service from local port 7071 over the Tor network on port 80. You could configure the service with:

> dotnet tor add api 127.0.0.1:7071 -p 80

Then start the Tor proxy normally with:

> dotnet tor

There will now be a %userprofile%\tor\.netconfig\api\hostname file with the .onion address for the service, like 2gzyxa5ihm7nsggfxnu52rck2vv4rvmdlkiu3zzui5du4xyclen53wid.onion. You can now reach web API endpoints natively via a .NET 6 client with:

using System;
using System.Net;
using System.Net.Http;

var http = new HttpClient(new HttpClientHandler
{
    Proxy = new WebProxy("socks5://127.0.0.1:1338")
});

var response = await http.GetAsync("http://2gzyxa5ihm7nsggfxnu52rck2vv4rvmdlkiu3zzui5du4xyclen53wid.onion/[endpoint]"));

The client can just use another dotnet-tor proxy running locally with default configuration values and things will Just Work™ and properly reach the destination service running anywhere in the world :).

You can even expose the local HTTPS endpoint instead. In this case, the client would need to perform custom validation (or entirely bypass it) of the certificate, but otherwise, things work as-is too.

Service-side:

> dotnet tor add api 127.0.0.1:5001 -p 443

Note that since we’re exposing the service over the default port for SSL, we don’t need to specify the port in the client:

var http = new HttpClient(new HttpClientHandler
{
    ClientCertificateOptions = ClientCertificateOption.Manual,
    ServerCertificateCustomValidationCallback = (_, _, _, _) => true,
    Proxy = new WebProxy("socks5://127.0.0.1:1338"),
});

var response = await http.SendAsync(new HttpRequestMessage(HttpMethod.Get, "https://kbu3mvegpytu4gewdgvjae7zhrzszmetmr5jdlwk5ct5pfzlbaqbdqqd.onion")
{
    Content = new StringContent("Hello World!")
});

response.EnsureSuccessStatusCode();

You can play around with a trivial echo service by installing the dotnet-echo tool and exposing it over the Tor network.

Dogfooding

CI Version Build

We also produce CI packages from branches and pull requests so you can dogfood builds as quickly as they are produced.

The CI feed is https://pkg.kzu.io/index.json.

The versioning scheme for packages is: