entropyxyz / entropy-core

Protocol and cryptography development.
https://docs.entropy.xyz/
GNU Affero General Public License v3.0
9 stars 1 forks source link

Allowing insecure api url creates potential footgun for validators #825

Open JesseAbram opened 4 months ago

JesseAbram commented 4 months ago

823 allows for an unsafe url for the get_api and get_rpc function. This is needed as our devops spins up the binaries in a docker container and this is safe, however allows for validators to spin their binaries up on different machines and communicate over a network which could create a potential man in the middle attack. They should not be doing this, and we can document this, but this does allow for that

Figuring out how to stop this in the rust code base would be ideal

vitropy commented 4 months ago

Figuring out how to stop this in the rust code base would be ideal

Perhaps this is too low-level but it does seem possible to set a TTL value for a TCP connection from Rust code. Elsewhere, I suggested this as a way to ensure communication between two endpoints is restricted to a certain logical distance:

[I]f you want to enforce a certain logical distance between two endpoints you could perhaps limit the time to live (TTL) value set in the UDP datagram or TCP segment carrying the endpoint's communication payload. This is what I've done for the EC2 instance's IMDS; the "hop limit" (which is really just a TTL setting) is set to 1 meaning that only machines immediately next to the IMDS itself (i.e., its immediate neighbors) can talk to it.

ok-john commented 3 months ago

You could force validators to use a private IP address space as per RFC 1918.

     10.0.0.0        -   10.255.255.255  (10/8 prefix)
     172.16.0.0      -   172.31.255.255  (172.16/12 prefix)
     192.168.0.0     -   192.168.255.255 (192.168/16 prefix)

However, this would still allow other machines connected to that private network communicate, so you could force the validators to use the local IPv4/6 of 127.0.0.1/[::1/128] assuming all your validators assign that standard ip to the lo loop back interface.

$ ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever

pnet is a pretty cool rust library that lets you get all the information on network interfaces, you could loop through that and check if any network interface meets the criteria of "safe" when a validator is spinning up and automatically choose any "safe" network interface, or give validators the option to choose from a list of "safe" interfaces.

use pnet::datalink::{self, NetworkInterface};
use pnet::ipnetwork::IpNetwork;
use std::net::{IpAddr, Ipv6Addr};

fn is_private_or_loopback_ip(ip: &IpAddr) -> bool {
    match ip {
        IpAddr::V4(ipv4) => ipv4.is_private() || ipv4.is_loopback(),
        IpAddr::V6(ipv6) => is_unique_local(ipv6) || ipv6.is_loopback(),
    }
}

fn is_unique_local(ipv6: &Ipv6Addr) -> bool {
    // Unique local addresses (ULA) in IPv6 have a prefix of FC00::/7
    // This means the first 7 bits are 1111110, so the first byte should be in the range 0xfc00 to 0xfdff
    let first_byte = ipv6.segments()[0] >> 8; // Get the first byte
    first_byte & 0xfe == 0xfc
}

fn get_private_and_local_ips() -> Vec<IpNetwork> {
    // Fetch a list of network interfaces available on the system
    let interfaces = datalink::interfaces();
    let mut private_and_local_ips = Vec::new();

    // Loop through the interfaces and filter those with private or loopback IPs
    for interface in interfaces {
        let filtered_ips: Vec<_> = interface
            .ips
            .iter()
            .filter(|ip_network| is_private_or_loopback_ip(&ip_network.ip()))
            .collect();

        private_and_local_ips.extend(filtered_ips.into_iter().cloned());
    }
    private_and_local_ips
}

fn main() {
    let private_and_local_ips = get_private_and_local_ips();
    for ip in private_and_local_ips {
        println!("Private/Local IP: {}", ip);
    }
}

On my system this returns:

./target/release/pnet-testing 
Private/Local IP: 127.0.0.1/8
Private/Local IP: ::1/128
Private/Local IP: 172.31.8.60/20
Private/Local IP: 192.168.2.1/24
Private/Local IP: 192.168.2.1/32