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.
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
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
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.