Skip to content
 

Controlling client-to-client connections in OpenVPN

OpenVPN has a feature called client-to-client to be used on the server, that permits, as the name says, client-to-client connections. This allows connectivity between any pair of clients, but it is implemented internally to the OpenVPN server, and packets are not exposed to the operating system.
In some instances you might want to have a better control over which clients can talk to which clients, and using client-to-client does not allow for that (at least currently).

How to solve the problem? The answer is: do NOT use client-to-client in the server's configuration file! That may sound strange at first, but it does in fact make sense, at least in routed mode. If client-to-client is not enabled, the server becomes a sort of "router on a stick" (even if the clients are on the same subnet!), meaning that packets are decapsulated, come "out" of the server's tun interface and are then (if needed) routed back into the same tun interface (but encapsulated to be sent to the right destination).
To confirm that this is true, run tcpdump on the server while client-to-client is disabled, no firewall is in effect, and two clients are pinging each other:

server:~# tcpdump -v -n -i tun0
...
15:31:13.265709 IP (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto ICMP (1), length 84) 10.180.0.2 > 10.180.0.3: ICMP echo request, id 56579, seq 1, length 64
15:31:13.266836 IP (tos 0x0, ttl 63, id 0, offset 0, flags [DF], proto ICMP (1), length 84) 10.180.0.2 > 10.180.0.3: ICMP echo request, id 56579, seq 1, length 64
15:31:13.269941 IP (tos 0x0, ttl 64, id 55097, offset 0, flags [none], proto ICMP (1), length 84) 10.180.0.3 > 10.180.0.2: ICMP echo reply, id 56579, seq 1, length 64
15:31:13.269964 IP (tos 0x0, ttl 63, id 55097, offset 0, flags [none], proto ICMP (1), length 84) 10.180.0.3 > 10.180.0.2: ICMP echo reply, id 56579, seq 1, length 64
15:31:14.286694 IP (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto ICMP (1), length 84) 10.180.0.2 > 10.180.0.3: ICMP echo request, id 56579, seq 2, length 64
15:31:14.286728 IP (tos 0x0, ttl 63, id 0, offset 0, flags [DF], proto ICMP (1), length 84) 10.180.0.2 > 10.180.0.3: ICMP echo request, id 56579, seq 2, length 64
15:31:14.290280 IP (tos 0x0, ttl 64, id 55098, offset 0, flags [none], proto ICMP (1), length 84) 10.180.0.3 > 10.180.0.2: ICMP echo reply, id 56579, seq 2, length 64
15:31:14.290301 IP (tos 0x0, ttl 63, id 55098, offset 0, flags [none], proto ICMP (1), length 84) 10.180.0.3 > 10.180.0.2: ICMP echo reply, id 56579, seq 2, length 64
...

You see each packet twice (but note the decremented TTL), meaning that it really goes out and then back into tun0. This gives us a hook where we can insert suitable iptables rules in the FORWARD chain. For example, if clients are on the 10.180.0.0/24 subnet, you could do this:

# allow already established connections (listed first for efficiency)
# iptables -A FORWARD -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
# allow communication between 10.180.0.2 and 10.180.0.8, only if initiated by 10.180.0.2
# iptables -A FORWARD -s 10.180.0.2 -d 10.180.0.8 -m conntrack --ctstate NEW -j ACCEPT
# allow full communication between 10.180.0.4 and 10.180.0.11
# iptables -A FORWARD -s 10.180.0.4 -d 10.180.0.11 -j ACCEPT
# iptables -A FORWARD -s 10.180.0.11 -d 10.180.0.4 -j ACCEPT
# deny everything else
# iptables -A FORWARD -j DROP     # or set a default policy

This works fine; the only issue is a few extra packets are generated by the server. These are ICMP redirects that it sends to the clients when it sees that they are sending data to each other via the server instead of communicating directly since they are in the same "subnet". Of course, this is because the server does not really know what's going on, and the generation of these ICMP redirect packets for the tun interface can be turned off on the server by writing 0 into the two virtual files /proc/sys/net/ipv4/conf/all/send_redirects and /proc/sys/net/ipv4/conf/tun0/send_redirects (replace tun0 with the interface used by the OpenVPN server). Despite writing 0 in the "all" file as well, this does not turn off redirects for the other interfaces, since (from ip-sysctl.txt):

send_redirects - BOOLEAN
    Send redirects, if router.
    send_redirects for the interface will be enabled if at least one of
    conf/{all,interface}/send_redirects is set to TRUE,
    it will be disabled otherwise
    Default: TRUE

So the other interfaces, having 1 in their specific file, will still send redirects.

All the above is applicable when running in routed mode only. If you're using bridged mode, there doesn't seem to be an easy method to control client-to-client connections. In other words, it's all (if you enable client-to-client in the config) or nothing (if you disable it).

Update 10/06/2010: it seems that it's possible to control client to client connections even in tap (bridged) mode, using OpenVPN's internal packet filter, although it's not straightforward. A future article will examine that.

Update 18/06/2010: the article is here.

16 Comments

  1. cesar daniel says:

    Gracias, este articulo es exactamente lo que estaba buscado, excelente trabajo.
    thanks, this article was exactly what I was looking for, great job

  2. OderWat says:

    I know this entry is pretty old even so it helped me a lot configuring my vpn so that just selected client can talk to each other. most of them can only see one machine besides the server.

    But I had problems which are not mentioned here:

    1. I needed to enable ip forwarding (this may be obvious but I thought first it would word without because it is in the same network)
    2. After removing "client-to-client" the clients had no routes for the other ips in the network anymore. I solved that by adding 'push "route 10.x.0.0 255.255.255.0"' into the openvpn server config. which seems to work. I am not sure if that is the right/best solution though.

    • waldner says:

      1. Right, IP forwarding is not mentioned explicitly, although the fact that the TTL is decremented and that iptables rules are in the FORWARD chain should hint in that direction, but it's always better to be clear, so thanks for pointing that out.

      2. This is a bit strange, "client-to-client" should have no effect on the routes that the client installs, which should be determined only by 'push "route ...."' statements in the server config and 'route ....' statements in the client config, at least when using "topology subnet" (which is what should be used anyway).

  3. Applewaite says:

    Hi Guys I have a OpenVPN server installed in bridged mode.

    1/
    When Clients connect I want them to not see the LAN at xxx.xxx.xxx.0/24 but only the VPN server at xxx.xxx.xxx.www which they will use for internet access. That is VPN clients must not be able to access regular LAN clients and regular LAN clients must not be able to access VPN connected clients.

    2/
    Additionally I have ssh and a webserver running on the OpenVPN Server, and would like to block these two ports from being accessed by the vpn clients which have a dhcp range of xxx.xxx.xxx.yyy - xxx.xxx.xxx.zzz.

    Can someone tell me how to achieve this with some simple and concise rulesets.

    Thanks.

    So far I think I have accomplished 2/ but no cannot accomplish 1/ no matter what rule I try.

    Here is what I have so far...

    iptables -A INPUT -p udp --dport 1194 -j ACCEPT
    iptables -A INPUT -p tcp -m iprange --src-range 172.25.26.90-172.25.26.99 --dst 172.25.26.252 --dport 22 -j DROP
    iptables -A INPUT -p tcp -m iprange --src-range 172.25.26.90-172.25.26.99 --dst 172.25.26.252 --dport 80 -j DROP
    iptables -A INPUT -m iprange --src-range 172.25.26.90-172.25.26.99 ! --dst 172.25.26.252 -j DROP
    iptables -A INPUT -i tap0 -j ACCEPT

    It's the fourth rule that I was trying to block the vpn clients from seeing each other and the regular lan clients, and vice versa.

    I've disabled client-to-client by removing it from the server config file but strangely clients can still ping/see each other.

    from the openvpn site...
    # Uncomment this directive to allow different
    # clients to be able to "see" each other.
    # By default, clients will only see the server.
    # To force clients to only see the server, you
    # will also need to appropriately firewall the
    # server's TUN/TAP interface.
    ;client-to-client

    • waldner says:

      In bridged mode, iptables rules are ineffective. You have to use OpenVPN's built-in packet filtering capabilities, as explained in http://backreference.org/2010/06/18/openvpns-built-in-packet-filter/. If you read the comments, you'll see that there was a bug in OpenVPN, which was later fixed.
      I suppose another option is messing around with ebtables, but I don't know how practical that would be (probably very little).

      And anyway, what's the point of using bridged mode if the VPN clients should not be able to access the LAN? That's basically the main reason (or among the main reasons) for using bridged mode.

      • Applewaite says:

        You are right there. I've thrown away bridge and had to learn how to do the routed method. Currently Everyone is able to ping everyone from vpn to lan, vpn to vpn and so on. I will fiddle around with the IPtables now to see how I can cordon off each client to it's own sandbox. I may be back for assistance If I can't figure it out if you don't mind.

        Thanks.

      • Applewaite says:

        What I am trying to achieve is

        1/
        blocking the vpn network from seeing the LAN,

        2/
        stop a vpn client from seeing another vpn client.

        3/
        Vpn clients can talk to the internet.

        4/
        LAN clients cannot see the vpn clients.

        I've put together these rules. They are the ones I settled on after a number of combinations, and was sure they should work, I can't see why they don't as I am not achieving #3, only all the rest.

        172.25.90.0 = VPN Network

        iptables -A FORWARD --dst 172.25.90.0/24 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
        iptables -A FORWARD --dst 10.0.0.0/8 -j DROP
        iptables -A FORWARD --dst 172.16.0.0/12 -j DROP
        iptables -A FORWARD --dst 192.168.0.0/16 -j DROP
        iptables -A FORWARD --src 172.25.90.0/24 -m conntrack --ctstate NEW,RELATED,ESTABLISHED -j ACCEPT
        iptables -A FORWARD -j DROP

        • waldner says:

          For the VPN clients to reach the internet (I guess through your VPN gateway) you have to do a few things.

          First, the VPN clients need to install a route (to the whole Internet or whatever) pointing to the VPN gateway. Depending on the route you want them to have, this can be achieved by pushing one or more specific routes to the clients, or directly pushing "redirect-gateway" (if you use this one, I suggest you also use the "def1" flag - the man has all the details). This is done by editing the VPN server config.
          Second, you'll want to NAT the traffic received from the VPN calient and going to the Internet, so you need something like

          iptables -t nat -A POSTROUTING -o eth1 -s 172.25.90.0/24 -j MASQUERADE

          where eth1 is the Internet-facing interface on the VPN gateway. Of course, you'll also need to enable IP forwarding and allow this traffic in the FORWARD chain (if you're not doing it already).

          See this page for some examples: http://community.openvpn.net/openvpn/wiki/BridgingAndRouting

  4. Darrell Dillman says:

    Dinamtko, I think you are correct about adding the routes. Would these routes be added in the server.conf file and then pushed to the clients? Would I need to push all the routes in /30net topology or could they be summarized? Is is possible to push /30net routes to just a few select clients?

    Like I had posted earlier, I found a work around with add another VPN. However, I am still wondering what the fix is. thx.

  5. Dinamtko says:

    Darrell, when you use openvpn in server mode and disable client-to-client you have to manually add routes between the clients and then they will be able to ping themselves again, so you can filter them on the tunX interface.

    • waldner says:

      I think (but I have not verified) that what you say is correct, but only when using the "point to point" or "net30" topology. If you use the "subnet" topology, clients should automatically have a route for the whole /24 or whatever client network pointing out the tun interface.
      I will do further tests to check this.

  6. Darrell Dillman says:

    To anyone finding this post.

    I was not able to restrict clients using the posted method. Im not sure why, but it might have to-do with the fact my VPN(s) are running virtually in a OpenVZ container. However, I did find a work around. I created 3 VPNs (Family,Biz and Personal). My Personal VPN has access to everything, with no firewalls. The Family and Biz VPNs are heavily firewalled allowing mostly only traffic into the VPNs, but not out. This allows me to monitor and maintain the machines connected, without exposing my local network.

    So I have three VPNs, running in OpenVZ containers under Proxmox, each using 30-40 megs of memory and almost 0 CPU. Priceless!

  7. Darrell Dillman says:

    Im not trying to block 10.5.x.x > 10.5.x.x. Im try ping to allow it to a few select clients. I think I might have a routing issue. If I do a trace route 10.5.0.5 the path is out eth0 NOT tun0. This is a problem.

    What option(s) in my server.conf file do I need, to tell my clients how to reach the entire 10.5.0.0 subnet? All my clients have static IPs. Each static IP is on it own /30 subnet (that makes 65 possible subnets). I believe this maybe what is causing my issue.

  8. Darrell Dillman says:

    The blocking is not a issue, it's the pinging. I'm not able to ping between .21 and .58. I'm thinking it's a routing issue now. If I do a trace route 10.5.0.58 it routes out the Internet interface not the VPN. The server must not be pushing the correct route info down to the client. All the clients are on the /30 subnet as described in the openvpn how to. Some how think I need to advertise a route for all vpn clients that points to the VPN.

  9. Darrell Dillman says:

    This article was exactly what I was looking for. However, I cant seem to make it work on my routed OpenVPN. Below is my iptables, do they look correct? Im trying to ping to&from 10.5.0.21 and 10.5.0.58 and block all other 10.5.x.x > 10.5.x.x. traffic.

    Chain FORWARD (policy ACCEPT)
    target prot opt source destination
    ACCEPT all -- anywhere anywhere state ESTABLISHED
    ACCEPT all -- anywhere anywhere state RELATED
    ACCEPT all -- 192.168.100.0/24 10.5.0.0/24
    ACCEPT udp -- 10.5.0.0/24 192.168.100.0/24 udp spt:iax
    ACCEPT tcp -- 10.5.0.58 mythtv tcp multiport ports ssh
    ACCEPT all -- 10.5.0.21 10.5.0.58
    ACCEPT all -- 10.5.0.58 10.5.0.21
    DROP all -- 10.5.0.0/24 192.168.100.0/24

    Thanks for posting this

    • waldner says:

      Since you don't have a rule that explicitly blocks traffic from 10.5.xx to 10.5.xx, the default policy is used which is ACCEPT. I think you have to either change the default policy to DROP, or add another rule at the end that explicitly blocks traffic from 10.5.x.x to 10.5.x.x.