Forum Discussion

davidfisher's avatar
Aug 11, 2018

Why does FTPS loadbalancing need 3 VIPs?

Hey All,

 

I followed this guide to do FTPS SSL offloading - https://devcentral.f5.com/articles/ftps-offload-via-irules

 

The solution 1 did not work even after multiple tries but the solution 2 worked fine, however the article doesn't explain why it needs 3 VIPs in all.

 

I am quoting the working part here. And adding my config done in the comment below this post.

 

Solution 2: Offloading SSL for FTPS

This solution will handle both load balancing and offloading of SSL for FTPS. It will NOT support Active FTPS transfers -- only Passive FTPS transfers will work (This is because of the strange way active FTPS SSL negotiations work -- the server initiates a connection to the client but the client begins the SSL handshake). This solution could support the Clear Control Channel command but currently does not. This is really a pretty experimental solution and would need to be improved to make it more robust in a production environment. The second iRule has some code commented out to replace the IP the server sends for passive transfers, but it would be easier just to configure your server to hand out the VIP address.

 

x.x.x.x: external VIP address y.y.y.y: any internal IP address that is not in use

 

1) You must be running 9.4.x as we will be using the "virtual" command to send traffic to another Virtual Server.

 

2) A source address persistence profile is defined that matches across services and across virtuals. It is applied to inbound both virtual servers so that the passive transfer connections are sent to the same server that has the control connection from that client.

 

3) Define first virtual server (where the clients actually connect to) on VIP address x.x.x.x and port 21. This virtual server needs to have a CLIENTSSL profile associated with it with a valid SSL certificate for the server (the same way you'd do it if you were offloading SSL for HTTPS). This virtual server does not need a default pool. This virtual server also needs this iRule applied to it (with y.y.y.y replaced with the actual internal IP address):

 

when CLIENT_ACCEPTED {
log local0. "client accepted"
SSL::disable
TCP::respond "220 My ftp server\r\n"
TCP::collect
}
when CLIENT_DATA {
log local0. "client data"
TCP::respond "234 AUTH TLS Successful\r\n"
TCP::payload replace 0 [TCP::payload length] ""
virtual VS2FTP
SSL::enable
TCP::release
log local0. "TCP Release Completed"
}

3) Define a second standard virtual server on an internal address (where the first virtual server connects to) named "internal-y.y.y.y-999" with FTP servers (on port 21) as pool members. Apply the persistence profile. It needs the following iRule:

 

when CLIENT_ACCEPTED {
TCP::collect
}
when CLIENT_DATA {
if { [TCP::payload] contains "PBSZ" }  {
TCP::payload replace 0 [TCP::payload length] ""
TCP::respond "200 PBSZ 0 successful\r\n"
} elseif { [TCP::payload] contains "PROT P" } {
TCP::respond "200 Protection set to Private\r\n"
TCP::payload replace 0 [TCP::payload length] ""
} elseif { [TCP::payload] contains "FEAT" } {
TCP::payload replace 0 [TCP::payload length] ""
TCP::respond "211-Features:  MDTM  REST STREAM  SIZE  AUTH TLS  PBSZ  PROT\r\n211 End\r\n"
}  
TCP::release
TCP::collect
}
when SERVER_CONNECTED {
TCP::collect
}
when SERVER_DATA {
if { [TCP::payload] contains "220 " } {
TCP::payload replace 0 [TCP::payload length] ""
} elseif { [TCP::payload] contains "Entering Passive Mode" } {
 You need to modify this section if your servers are not
 configured to hand out the VIP address for Passive transfers.
regsub {10,10,71,1} [TCP::payload] "172,16,59,163" tmpstr
TCP::payload replace 0 [TCP::payload length] $tmpstr
} 
TCP::release
TCP::collect
}

4) The third virtual server catches inbound client passive transfer connections. It is defined on VIP address x.x.x.x and all ports. It must have the same CLIENTSSL profile as the first virtual server, the same pool as the first virtual server, and the same persistence profile as the first virtual server. It needs this iRule as well:

 

when CLIENT_ACCEPTED {
SSL::disable
TCP::collect 0 0
}
when CLIENT_DATA {
SSL::enable
}
when SERVER_CONNECTED {
SSL::enable clientside
}

2 Replies

  • My Working Config

    POOLS USED:

    root@(bigip1)(cfg-sync Disconnected)(Active)(/Common)(tmos) list ltm pool ftp-port21-only 
    ltm pool ftp-port21-only {
        members {
            10.1.20.100:ftp {
                address 10.1.20.100
                session monitor-enabled
                state down
            }
        }
        monitor gateway_icmp 
    }
    

    VIRTUALS SERVERS

    The initial VS 1 which is required in the config to offload the SSL and no pool

    root@(bigip1)(cfg-sync Disconnected)(Active)(/Common)(tmos) list ltm virtual SSL-FTP 
    ltm virtual SSL-FTP {
        destination 10.128.10.102:ftp
        ip-protocol tcp
        mask 255.255.255.255
        profiles {
            ftp-ssl-profile {
                context clientside
            }
            ftp-tcp-profile { }
        }
        rules {
            ftp-ssl-irule
        }
        source 0.0.0.0/0
        translate-address enabled
        translate-port enabled
        vs-index 15
    }
    

    The Second VS needed on a different unused IP

    root@(bigip1)(cfg-sync Disconnected)(Active)(/Common)(tmos) list ltm virtual ftpvs2 
    ltm virtual ftpvs2 {
        destination 10.128.10.103:any
        ip-protocol tcp
        mask 255.255.255.255
        pool ftp-port21-only
        profiles {
            tcp { }
        }
        rules {
            ftpvs2
        }
        source 0.0.0.0/0
        translate-address enabled
        translate-port disabled
        vs-index 17
    }
    

    The third VS with the pool again.

    root@(bigip1)(cfg-sync Disconnected)(Active)(/Common)(tmos) list ltm virtual FTP-all-ports 
    ltm virtual FTP-all-ports {
        destination 10.128.10.102:any
        ip-protocol tcp
        mask 255.255.255.255
        pool ftp-port21-only
        profiles {
            ftp-ssl-profile {
                context clientside
            }
            tcp { }
        }
        rules {
            ftp-port-read
            ftpvs3
        }
        source 0.0.0.0/0
        translate-address enabled
        translate-port disabled
    }
        vs-index 16
    
  • This solution is for Passive FTP only, your first VS is for the the incoming Command connection over SSL, this forwards on to the internal VS which looks to be dealing with the actual FTP Command connection without SSL.

    As the iRule on the second VS reads the TCP Payload within the CLIENT_ACCEPTED event the traffic needs to in plaintext, decrypted, to allow it be do this. If you tried do do this in the initial VS with SSL the payload would be encrypted and you would not be able to read the contents to execute the iRule.

    The final VS is for the Passive Data connection, which could be on any port as the connection is dynamic.

    The connection flow looks a little like this

    Initial Connection

        Client
          ||
        (SSL Connection 21)
          ||
        virtual SSL-FTP
          ||
        (none-SSL Connection 21)
          ||
        virtual ftpvs2
          ||
        Backend FTP
    

    Data Connection

        Client
          ||
        (SSL Connection Dynamic port)
          ||
        virtual FTP-all-ports
          ||
        Backend FTP
    

    I think you could, with some complex iRules, do all this on a single Virtual Server however to keep the solution simple multiple Virtual Servers is often much better.