Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Amnezia PoC #1415

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/ci-nym-vpn-core-ios.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ jobs:
version: "21.12" # 3.21.12: the version on ubuntu 24.04. Don't change this!
repo-token: ${{ secrets.GITHUB_TOKEN }}

- name: Install script dependencies
run: brew install gnu-getopt

- name: Build wireguard
shell: bash
run: |
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/ci-nym-vpn-core-macos.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ jobs:
version: "21.12" # 3.21.12: the version on ubuntu 24.04. Don't change this!
repo-token: ${{ secrets.GITHUB_TOKEN }}

- name: Install script dependencies
run: brew install gnu-getopt

- name: Build wireguard
shell: bash
run: |
Expand Down
1 change: 1 addition & 0 deletions nym-vpn-core/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

56 changes: 56 additions & 0 deletions nym-vpn-core/crates/nym-gateway-probe/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Nym Gateway Probe

Probe IPv4 and IPv6 interfaces of available gateways to check for the
set that passes a set of minumum service guarantees.


## Build

These instructions assume a debian based system. Adjust accordingly for your
preffered platform.

Install required dependencies
```sh
sudo apt install libdbus-1-dev libmnl-dev libnftnl-dev protobuf-compiler clang
```


Build piece by piece
```sh
cd nym-vpn-core/
# build the prober
cargo build -p nym-gateway-probe
```

You may need to adjust your `RUSTFLAGS` or `.cargo/config.toml` to ensure that
the golang wireguard library links properly.

## Usage

```sh
Usage: nym-gateway-probe [OPTIONS]

Options:
-c, --config-env-file <CONFIG_ENV_FILE>
Path pointing to an env file describing the network
-g, --gateway <GATEWAY>
The specific gateway specified by ID
-n, --no-log
Disable logging during probe
-a, --amnezia-args <AMNEZIA_ARGS>
Arguments to be appended to the wireguard config enabling amnezia-wg configuration
-h, --help
Print help
-V, --version
Print version
```

Examples

```sh
# Run a basic probe against the node with id "qj3GgGYg..."
nym-gateway-probe -g "qj3GgGYgGZZ3HkFrtD1GU9UJ5oNXME9eD2xtmPLqYYw"

# Run a probe against the node with id "qj3GgGYg..." using amnezia with junk packets enabled.
nym-gateway-probe -g "qj3GgGYgGZZ3HkFrtD1GU9UJ5oNXME9eD2xtmPLqYYw" -a "Jc=4\nJmin=40\mJmax=70\n"
```
4 changes: 4 additions & 0 deletions nym-vpn-core/crates/nym-gateway-probe/netstack_ping/gen.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ typedef struct NetstackRequestRef {
uint8_t num_ping;
uint64_t send_timeout_sec;
uint64_t recv_timeout_sec;
struct StringRef awg_args;
} NetstackRequestRef;

// hack from: https://stackoverflow.com/a/69904977
Expand Down Expand Up @@ -225,6 +226,7 @@ type NetstackRequest struct {
num_ping uint8
send_timeout_sec uint64
recv_timeout_sec uint64
awg_args string
}

func newNetstackRequest(p C.NetstackRequestRef) NetstackRequest {
Expand All @@ -239,6 +241,7 @@ func newNetstackRequest(p C.NetstackRequestRef) NetstackRequest {
num_ping: newC_uint8_t(p.num_ping),
send_timeout_sec: newC_uint64_t(p.send_timeout_sec),
recv_timeout_sec: newC_uint64_t(p.recv_timeout_sec),
awg_args: newString(p.awg_args),
}
}
func cntNetstackRequest(s *NetstackRequest, cnt *uint) [0]C.NetstackRequestRef {
Expand All @@ -258,6 +261,7 @@ func refNetstackRequest(p *NetstackRequest, buffer *[]byte) C.NetstackRequestRef
num_ping: refC_uint8_t(&p.num_ping, buffer),
send_timeout_sec: refC_uint64_t(&p.send_timeout_sec, buffer),
recv_timeout_sec: refC_uint64_t(&p.recv_timeout_sec, buffer),
awg_args: refString(&p.awg_args, buffer),
}
}

Expand Down
7 changes: 5 additions & 2 deletions nym-vpn-core/crates/nym-gateway-probe/netstack_ping/go.mod
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
module github.com/nymtech/nym-vpn-client/nym-vpn-core/crates/nym-gateway-probe/netstack_ping

go 1.22
go 1.22.3

toolchain go1.23.1

require (
github.com/amnezia-vpn/amneziawg-go v0.2.12
golang.org/x/net v0.23.0
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173
)

require (
github.com/google/btree v1.0.1 // indirect
github.com/tevino/abool/v2 v2.1.0 // indirect
golang.org/x/crypto v0.21.0 // indirect
golang.org/x/sys v0.18.0 // indirect
golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 // indirect
Expand Down
6 changes: 4 additions & 2 deletions nym-vpn-core/crates/nym-gateway-probe/netstack_ping/go.sum
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
github.com/amnezia-vpn/amneziawg-go v0.2.12 h1:CxIQETy5kZ0ip/dFBpmnDxAcS/KuIQaJkOxDv5OQhVI=
github.com/amnezia-vpn/amneziawg-go v0.2.12/go.mod h1:d7WpNfzCRLy7ufGElJBYpD58WRmNjyLyt3IDHPY8AmM=
github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4=
github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA=
github.com/tevino/abool/v2 v2.1.0 h1:7w+Vf9f/5gmKT4m4qkayb33/92M+Um45F2BkHOR+L/c=
github.com/tevino/abool/v2 v2.1.0/go.mod h1:+Lmlqk6bHDWHqN1cbxqhwEAwMPXgc8I1SDEamtseuXY=
golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs=
Expand All @@ -10,7 +14,5 @@ golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 h1:vVKdlvoWBphwdxWKrFZEuM0k
golang.org/x/time v0.0.0-20220210224613-90d013bbcef8/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeunTOisW56dUokqW/FOteYJJ/yg=
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI=
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 h1:/jFs0duh4rdb8uIfPMv78iAJGcPKDeqAFnaLBropIC4=
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173/go.mod h1:tkCQ4FQXmpAgYVh++1cq16/dH4QJtmvpRv19DWGAHSA=
gvisor.dev/gvisor v0.0.0-20230927004350-cbd86285d259 h1:TbRPT0HtzFP3Cno1zZo7yPzEEnfu8EjLfl6IU9VfqkQ=
gvisor.dev/gvisor v0.0.0-20230927004350-cbd86285d259/go.mod h1:AVgIgHMwK63XvmAzWG9vLQ41YnVHN0du0tEC46fI7yY=
38 changes: 26 additions & 12 deletions nym-vpn-core/crates/nym-gateway-probe/netstack_ping/impl.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ import (
"strings"
"time"

"github.com/amnezia-vpn/amneziawg-go/conn"
"github.com/amnezia-vpn/amneziawg-go/device"
"github.com/amnezia-vpn/amneziawg-go/tun/netstack"
"golang.org/x/net/icmp"
"golang.org/x/net/ipv4"
"golang.zx2c4.com/wireguard/conn"
"golang.zx2c4.com/wireguard/device"
"golang.zx2c4.com/wireguard/tun/netstack"
)

type Netstack struct{}
Expand All @@ -36,6 +36,10 @@ func (Netstack) ping(req NetstackRequest) NetstackResponse {

ipc.WriteString("private_key=")
ipc.WriteString(req.private_key)
if req.awg_args != "" {
awg := strings.ReplaceAll(req.awg_args, "\\n", "\n")
ipc.WriteString(fmt.Sprintf("\n%s", awg))
}
ipc.WriteString("\npublic_key=")
ipc.WriteString(req.public_key)
ipc.WriteString("\nendpoint=")
Expand All @@ -45,6 +49,13 @@ func (Netstack) ping(req NetstackRequest) NetstackResponse {
response := NetstackResponse{false, 0, 0, 0, 0, false}

dev.IpcSet(ipc.String())

config, err := dev.IpcGet()
if err != nil {
log.Panic(err)
}
log.Printf("%s", config)

err = dev.Up()
if err != nil {
log.Panic(err)
Expand All @@ -69,15 +80,18 @@ func (Netstack) ping(req NetstackRequest) NetstackResponse {

for _, ip := range req.ping_ips {
for i := uint8(0); i < req.num_ping; i++ {
log.Printf("Pinging %s seq=%d", ip, i)
response.sent_ips += 1
rt, err := sendPing(ip, i, req.send_timeout_sec, req.recv_timeout_sec, tnet)
if err != nil {
log.Printf("Failed to send ping: %v\n", err)
continue
}
response.received_ips += 1
log.Printf("Ping latency: %v\n", rt)
func() {
defer time.Sleep(5 * time.Second)
log.Printf("Pinging %s seq=%d", ip, i)
response.sent_ips += 1
rt, err := sendPing(ip, i, req.send_timeout_sec, req.recv_timeout_sec, tnet)
if err != nil {
log.Printf("Failed to send ping: %v\n", err)
return
}
response.received_ips += 1
log.Printf("Ping latency: %v\n", rt)
}()
}
}

Expand Down
140 changes: 84 additions & 56 deletions nym-vpn-core/crates/nym-gateway-probe/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,78 +58,105 @@ pub async fn fetch_gateways_with_ipr() -> anyhow::Result<GatewayList> {
Ok(lookup_gateways().await?.into_exit_gateways())
}

pub async fn probe(entry_point: EntryPoint) -> anyhow::Result<ProbeResult> {
// Setup the entry gateways
let gateways = lookup_gateways().await?;
let entry_gateway = entry_point.lookup_gateway(&gateways).await?;
let exit_router_address = entry_gateway.ipr_address;
let authenticator = entry_gateway.authenticator_address;
let gateway_host = entry_gateway.host.clone().unwrap();
let entry_gateway_id = entry_gateway.identity();

info!("Probing gateway: {entry_gateway:?}");

// Connect to the mixnet
let mixnet_client = MixnetClientBuilder::new_ephemeral()
.request_gateway(entry_gateway_id.to_string())
.network_details(NymNetworkDetails::new_from_env())
.debug_config(mixnet_debug_config())
.build()?
.connect_to_mixnet()
.await;

let mixnet_client = match mixnet_client {
Ok(mixnet_client) => mixnet_client,
Err(err) => {
error!("Failed to connect to mixnet: {err}");
return Ok(ProbeResult {
gateway: entry_gateway_id.to_string(),
outcome: ProbeOutcome {
as_entry: Entry::fail_to_connect(),
as_exit: None,
wg: None,
},
});
pub struct Probe {
entrypoint: EntryPoint,
amnezia_args: String,
}

impl Probe {
pub fn new(entrypoint: EntryPoint) -> Self {
Self {
entrypoint,
amnezia_args: "".into(),
}
};
}

let nym_address = *mixnet_client.nym_address();
let entry_gateway = nym_address.gateway().to_base58_string();
pub fn with_amnezia(&mut self, args: &str) -> &Self {
self.amnezia_args = args.to_string();
self
}

info!("Successfully connected to entry gateway: {entry_gateway}");
info!("Our nym address: {nym_address}");
pub async fn probe(self) -> anyhow::Result<ProbeResult> {
let entry_point = self.entrypoint;

// Setup the entry gateways
let gateways = lookup_gateways().await?;
let entry_gateway = entry_point.lookup_gateway(&gateways).await?;
let exit_router_address = entry_gateway.ipr_address;
let authenticator = entry_gateway.authenticator_address;
let gateway_host = entry_gateway.host.clone().unwrap();
let entry_gateway_id = entry_gateway.identity();

info!("Probing gateway: {entry_gateway:?}");

// Connect to the mixnet
let mixnet_client = MixnetClientBuilder::new_ephemeral()
.request_gateway(entry_gateway_id.to_string())
.network_details(NymNetworkDetails::new_from_env())
.debug_config(mixnet_debug_config())
.build()?
.connect_to_mixnet()
.await;

let mixnet_client = match mixnet_client {
Ok(mixnet_client) => mixnet_client,
Err(err) => {
error!("Failed to connect to mixnet: {err}");
return Ok(ProbeResult {
gateway: entry_gateway_id.to_string(),
outcome: ProbeOutcome {
as_entry: Entry::fail_to_connect(),
as_exit: None,
wg: None,
},
});
}
};

let shared_client = Arc::new(tokio::sync::Mutex::new(Some(mixnet_client)));
let nym_address = *mixnet_client.nym_address();
let entry_gateway = nym_address.gateway().to_base58_string();

// Now that we have a connected mixnet client, we can start pinging
let shared_mixnet_client = SharedMixnetClient::from_shared(&shared_client);
let outcome = do_ping(shared_mixnet_client.clone(), exit_router_address).await;
info!("Successfully connected to entry gateway: {entry_gateway}");
info!("Our nym address: {nym_address}");

let wg_outcome = if let Some(authenticator) = authenticator {
wg_probe(authenticator, shared_client, &gateway_host)
let shared_client = Arc::new(tokio::sync::Mutex::new(Some(mixnet_client)));

// Now that we have a connected mixnet client, we can start pinging
let shared_mixnet_client = SharedMixnetClient::from_shared(&shared_client);
let outcome = do_ping(shared_mixnet_client.clone(), exit_router_address).await;

let wg_outcome = if let Some(authenticator) = authenticator {
wg_probe(
authenticator,
shared_client,
&gateway_host,
self.amnezia_args,
)
.await
.unwrap_or_default()
} else {
WgProbeResults::default()
};
} else {
WgProbeResults::default()
};

let mixnet_client = shared_mixnet_client.lock().await.take().unwrap();
mixnet_client.disconnect().await;
let mixnet_client = shared_mixnet_client.lock().await.take().unwrap();
mixnet_client.disconnect().await;

// Disconnect the mixnet client gracefully
outcome.map(|mut outcome| {
outcome.wg = Some(wg_outcome);
ProbeResult {
gateway: entry_gateway.clone(),
outcome,
}
})
// Disconnect the mixnet client gracefully
outcome.map(|mut outcome| {
outcome.wg = Some(wg_outcome);
ProbeResult {
gateway: entry_gateway.clone(),
outcome,
}
})
}
}

async fn wg_probe(
authenticator: AuthAddress,
shared_mixnet_client: Arc<Mutex<Option<MixnetClient>>>,
gateway_host: &nym_topology::NetworkAddress,
awg_args: String,
) -> anyhow::Result<WgProbeResults> {
let auth_shared_client =
nym_authenticator_client::SharedMixnetClient::from_shared(&shared_mixnet_client);
Expand Down Expand Up @@ -227,6 +254,7 @@ async fn wg_probe(
private_key: private_key_hex,
public_key: public_key_hex,
endpoint: wg_endpoint.clone(),
awg_args,
..Default::default()
};

Expand Down
Loading
Loading