Skip to content

Hide any server behind a deny all firewall

License

Notifications You must be signed in to change notification settings

84codes/sparoid

Repository files navigation

SPAroid

Hide any server behind a deny all firewall, but open up the firewall for a single IP when a single correctly AES encrypted and HMAC authenticated UDP packet arrives. It allows you to hide eg. SSH for the wide internet, but still allow you to connect by just sending the UDP packet before the SSH connection attempt. Without VPN or jumphosts or other heavy weight solutions that are hard to scale.

Inspiration comes from fwknop, but is implemented in Crystal.

The server listens on an UDP port, when it receives a message it tries to verify it (HMAC), decrypt it (AES-256-CBC), then verify the nounce (that it haven't been seen before, reply attack), that the timestamp is within 5s. The IP that the packet arrives from and the IP that's in the packet (and the FW will be opened for), doesn't have to match, so a third party server can open the FW for another client.

If all checks passes the firewall is opened for the IP in the message. After 15s the port is closed again.

Direct nftables integration is also available, set the nftables-cmd and sparoid will interact with it directly without having to spawn a new process, which saves on resources.

Installation

Ubuntu:

curl -fsSL https://packagecloud.io/cloudamqp/sparoid/gpgkey | gpg --dearmor | sudo tee /etc/apt/trusted.gpg.d/sparoid.gpg >/dev/null
# or with apt-key on older systems
# wget -qO- https://packagecloud.io/cloudamqp/sparoid/gpgkey | sudo apt-key add -
echo "deb https://packagecloud.io/cloudamqp/sparoid/ubuntu/ $(lsb_release -cs) main" |\
  sudo tee /etc/apt/sources.list.d/sparoid.list
sudo apt-get update
sudo apt-get install -y sparoid

Usage

Server

With nftables:

cat > /etc/sparoid.ini << EOF
bind = 0.0.0.0
port = 8484
key = $SPAROID_KEY
hmac-key = $SPAROID_HMAC_KEY
nftables-cmd = add element inet filter sparoid { %s }
EOF

cat > /etc/nftables.conf << EOF
#!/usr/sbin/nft -f
flush ruleset

table inet filter {
  chain prerouting {
    type filter hook prerouting priority -300
    udp dport 8484 meter rate-limit-sparoid { ip saddr limit rate over 1/second burst 1 packets } counter drop
    udp dport 8484 notrack
  }

  chain input {
    type filter hook input priority 0; policy drop;
    iif lo accept

    ct state invalid counter drop
    ct state established,related accept

    udp dport 8484 accept
    ip saddr @jumphosts tcp dport ssh accept
    ip saddr @sparoid tcp dport ssh accept
  }

  set sparoid {
    type ipv4_addr
    flags timeout
    timeout 5s
  }

  set jumphosts {
    type ipv4_addr
    elements = { 10.10.10.10 }
  }
}

include "/etc/nftables/*.nft"
EOF

systemctl restart nftables.service sparoid.service

With iptables:

iptables -A INPUT -p tcp --dport 22 -j DROP # block connections to port 22
cat > config.ini << EOF
bind = 0.0.0.0
port = 8484
key = $key
hmac-key = $hmac_key
open-cmd = iptables -I INPUT -p tcp --dport 22 -s %s -j ACCEPT
close-cmd = iptables -D INPUT -p tcp --dport 22 -s %s -j ACCEPT
EOF
bin/sparoid-server --config config.ini

Client

bin/sparoid keygen > ~/.sparoid.ini # will output a key and a hmac_key that will be used below

bin/sparoid send hidden.example.co
ssh hidden.example.co

The Sparoid client has integration with the OpenSSH client, just add the following to your ~/.ssh/config:

Host *
  ProxyCommand sparoid connect -h %h -P %p
  ProxyUseFdpass yes

It will then automatically send a UDP packet before connecting.