Here’s the setup:

  • Fedora 41 Server host
  • Libvirt/QEMU
  • Alma 9 guest running ssh

My goal is to forward ports from the guest to the host, but change them. I set up a hook(as in the libvirt docs) and it worked on my last server. My hook looks like:

#!/bin/bash

if [ "${1}" = "Jellyfin" ]; then

   # Update the following variables to fit your setup
   GUEST_IP=192.168.101.4
   GUEST_PORT=22
   HOST_PORT=2222

   if [ "${2}" = "stopped" ] || [ "${2}" = "reconnect" ]; then
    /sbin/iptables -D FORWARD -o virbr1 -p tcp -d $GUEST_IP --dport $GUEST_PORT -j ACCEPT
    /sbin/iptables -t nat -D PREROUTING -p tcp --dport $HOST_PORT -j DNAT --to $GUEST_IP:$GUEST_PORT
   fi
   if [ "${2}" = "start" ] || [ "${2}" = "reconnect" ]; then
    /sbin/iptables -I FORWARD -o virbr1 -p tcp -d $GUEST_IP --dport $GUEST_PORT -j ACCEPT
    /sbin/iptables -t nat -I PREROUTING -p tcp --dport $HOST_PORT -j DNAT --to $GUEST_IP:$GUEST_PORT
   fi
fi

However, when I ssh to my server:2222, it doesn’t work, “Connection refused.” I can ssh from inside my server to my guest’s ip address, so I know it’s not an issue with ssh itself. The guest’s iptables rules are:

-P INPUT ACCEPT
-P FORWARD ACCEPT
-P OUTPUT ACCEPT

so that’s probably not the issue.

My server’s iptables rules include:

-A FORWARD -d 192.168.101.4/32 -o virbr1 -p tcp -m tcp --dport 22 -j ACCEPT

, so it appears the forwarding happened, but an nmap scan reveals the port is closed:

2222/tcp closed EtherNetIP-1

I’m baffled by this issue. Any help would be greatly appreciated!

  • kork349d@lemmy.ml
    link
    fedilink
    arrow-up
    3
    ·
    3 days ago

    ssh -v can be help troubleshoot connection issues. Any firewalls involved on either end?

    • potentiallynotfelixOP
      link
      fedilink
      arrow-up
      2
      ·
      3 days ago

      ssh -v returns:

      OpenSSH_9.2p1 Debian-2+deb12u4, OpenSSL 3.0.15 3 Sep 2024
      debug1: Reading configuration data /etc/ssh/ssh_config
      debug1: /etc/ssh/ssh_config line 19: include /etc/ssh/ssh_config.d/*.conf matched no files
      debug1: /etc/ssh/ssh_config line 21: Applying options for *
      debug1: Connecting to 192.168.86.73 [192.168.86.73] port 2222.
      debug1: connect to address 192.168.86.73 port 2222: Connection refused
      ssh: connect to host 192.168.86.73 port 2222: Connection refused
      
      
    • potentiallynotfelixOP
      link
      fedilink
      arrow-up
      2
      ·
      3 days ago

      No firewalls on the client, but iptables on host and guest. guest has no rules just allow all, and host rules are listed in the post.

  • germste@lemmy.ca
    link
    fedilink
    arrow-up
    3
    ·
    3 days ago

    Add or uncomment net.ipv4.ip_forward=1in /etc/sysctl.conf ans then sudo sysctl -p

    • potentiallynotfelixOP
      link
      fedilink
      arrow-up
      3
      ·
      3 days ago

      sysctl net.ipv4.ip_forward returns:

      net.ipv4.ip_forward = 1
      

      So I’m pretty sure that this is already enabled. Thanks for your answer!

    • IsoKiero@sopuli.xyz
      link
      fedilink
      English
      arrow-up
      3
      ·
      3 days ago

      The one thing I always forget, no matter how many DNAT setups or whatever I write with iptables.

  • AndrasKrigare@beehaw.org
    link
    fedilink
    arrow-up
    2
    ·
    3 days ago

    Your hook has

    /sbin/iptables -t nat -I PREROUTING -p tcp --dport $HOST_PORT -j DNAT --to $GUEST_IP:$GUEST_PORT

    But I’d didn’t think that “–to” was a flag for DNAT, I thought it was “–to-destination”

    If you ‘iptables -nvL’ and ‘iptables -t nat -nvL’ do you see both your DNAT and forwarding rules (although if the default is ACCEPT and you don’t have other rules, the FORWARD one isn’t needed), and do you see the packet count for the rules increase?

    • potentiallynotfelixOP
      link
      fedilink
      arrow-up
      2
      ·
      3 days ago

      From the iptables manpage:

      --to offset
          Set the offset from which it starts looking for any matching. If not passed, default is the packet size. 
      
      ...
      
      --to-destination ipaddr-ipaddr
          Address range to round-robin over. 
      

      This seems to do something, but the port still appears as closed.

      iptables -nvL returns:

      Chain INPUT (policy ACCEPT 0 packets, 0 bytes)
       pkts bytes target     prot opt in     out     source               destination         
      
      Chain FORWARD (policy ACCEPT 369 packets, 54387 bytes)
       pkts bytes target     prot opt in     out     source               destination         
          5   300 ACCEPT     6    --  *      virbr1  0.0.0.0/0            192.168.101.4        tcp dpt:22
         84  6689 ACCEPT     0    --  *      br-392a16e9359d  0.0.0.0/0            0.0.0.0/0            ctstate RELATED,ESTABLISHED
          7   418 DOCKER     0    --  *      br-392a16e9359d  0.0.0.0/0            0.0.0.0/0           
        146  9410 ACCEPT     0    --  br-392a16e9359d !br-392a16e9359d  0.0.0.0/0            0.0.0.0/0           
          0     0 ACCEPT     0    --  br-392a16e9359d br-392a16e9359d  0.0.0.0/0            0.0.0.0/0           
      
      Chain OUTPUT (policy ACCEPT 0 packets, 0 bytes)
       pkts bytes target     prot opt in     out     source               destination                    
      

      I’ve omitted some listings that were labelled as docker.

      iptables -t nat -nvL returns:

      Chain PREROUTING (policy ACCEPT 626 packets, 90758 bytes)
       pkts bytes target     prot opt in     out     source               destination         
          5   300 DNAT       6    --  *      *       0.0.0.0/0            0.0.0.0/0            tcp dpt:2222 to:192.168.101.4:22
      
      Chain INPUT (policy ACCEPT 0 packets, 0 bytes)
       pkts bytes target     prot opt in     out     source               destination         
      
      Chain OUTPUT (policy ACCEPT 154 packets, 12278 bytes)
       pkts bytes target     prot opt in     out     source               destination         
          0     0 DOCKER     0    --  *      *       0.0.0.0/0           !127.0.0.0/8          ADDRTYPE match dst-type LOCAL
      
      Chain POSTROUTING (policy ACCEPT 290 packets, 22404 bytes)
       pkts bytes target     prot opt in     out     source               destination         
          0     0 MASQUERADE  0    --  *      !br-392a16e9359d  172.18.0.0/16        0.0.0.0/0           
      

      I’ve also omitted some listings that were labelled as docker.

      After running the ssh command, the bytes seem to increase. After 1 ssh attempt:

          7   420 DNAT       6    --  *      *       0.0.0.0/0            0.0.0.0/0            tcp dpt:2222 to:192.168.101.4:22
      

      After another ssh attempt:

          8   480 DNAT       6    --  *      *       0.0.0.0/0            0.0.0.0/0            tcp dpt:2222 to:192.168.101.4:22
      
      • AndrasKrigare@beehaw.org
        link
        fedilink
        arrow-up
        2
        ·
        2 days ago

        For general awareness, not all flags can match all parts of an iptables command; the part you included there with “–to offset” is only valid with the string module, and not the DNAT action. That said after playing around with it a little, iptables actually does short flag matching, so ‘DNAT --to 1.2.3.4’ ‘DNAT --to-d 1.2.3.4’ and ‘DNAT --to-destination’ are all equivalent, so not the source of your issue.

        I am having trouble following the IP scheme, though. Is your Alma guest 192.168.101.4, or is that the host IP? If it’s Alma’s and you are attempting to ssh from that IP to the host with that iptables rule, what should happen is that DNAT would then redirect that connection back to Alma. If the guest doesn’t have a :22 listener, you’d get a connection refused from itself.

        • potentiallynotfelixOP
          link
          fedilink
          arrow-up
          1
          ·
          2 days ago

          192.168.101.4 is the alma guest. It’s got port 22 open and I can ssh into it from the host computer.

          iptables -nvL on Alma returns:

          Chain INPUT (policy ACCEPT 0 packets, 0 bytes)
           pkts bytes target     prot opt in     out     source               destination         
          
          Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)
           pkts bytes target     prot opt in     out     source               destination         
          
          Chain OUTPUT (policy ACCEPT 0 packets, 0 bytes)
           pkts bytes target     prot opt in     out     source               destination         
          

          I believe this means it automatically accepts connections.

          IMO this makes it unlikely that the guest is the issue.

          • AndrasKrigare@beehaw.org
            link
            fedilink
            arrow-up
            2
            ·
            edit-2
            2 days ago

            Sorry, I was looking more specifically at that DNAT rule

            8   480 DNAT       6    --  *      *       0.0.0.0/0            0.0.0.0/0            tcp dpt:2222 to:192.168.101.4:22
            

            That rule exists in the host 192.168.86.73, correct? And from the guest, 192.168.101.4 you are attempting to ssh into 192.168.86.73:2222?

            It might not be your issue (or only issue), but that DNAT rule says that if a connection comes in on port 2222, instead send it to 192.168.101.4:22. So 192.168.101.4->192.168.86.73:2222->192.168.101.4:22. I would have thought you’d want it to be a DNAT to 192.168.86.73, functionally doing port bending, so it goes 192.168.101.4->192.168.86.73:2222->192.168.86.73:22.

            That doesn’t explain the connection refused, though, based on what you’ve said; there’s some fringe possibilities, but I wouldn’t expect for your setup if you hadn’t said (like your ~/.ssh/ssh_config defining an alternate ssh port for your guest OS than 22). It’s somewhat annoying, but it might be worthwhile to do a packet capture on both ends and follow exactly where the packet is going. So a

            tcpdump -v -Nnn tcp port 22 or tcp port 2222

            • potentiallynotfelixOP
              link
              fedilink
              arrow-up
              1
              ·
              2 days ago

              yes, the host is 192.168.86.73 and it has that dnat rule.

              And from the guest

              Assuming you meant from the host, I am sshing directly to 192.168.101.4 instead of to 192.168.86.73:2222.

              The third paragraph doesn’t make sense to me. I am using port 22 on my host(192.168.86.73) for it’s own ssh.

              tcpdump returns this when I ssh to port 2222:

              20:32:29.957942 IP (tos 0x10, ttl 64, id 28091, offset 0, flags [DF], proto TCP (6), length 60)
                  192.168.86.23.53434 > 192.168.86.73.2222: Flags [S], cksum 0x5d75 (correct), seq 1900319834, win 64240, options [mss 1460,sackOK,TS val 3627223725 ecr 0,nop,wscale 7], length 0