openfortivpn icon indicating copy to clipboard operation
openfortivpn copied to clipboard

Use VPN only for certain domains

Open ayaka14732 opened this issue 3 years ago • 7 comments

I am wondering if it is possible to use openfortivpn only for certain domains. For example, route *.myorganisation.com but not others.

ayaka14732 avatar Sep 17 '21 06:09 ayaka14732

Well, openfortivpn mainly opens an SSL tunnel.

Then it does apply changes to the DNS and routing configuration of the client, according to the instructions sent by the router. You are free to further modify routes yourself, see for example How to add specific routes using pppd and option --no-routes.

DimitriPapadopoulos avatar Sep 17 '21 06:09 DimitriPapadopoulos

@DimitriPapadopoulos Thank you!

I understand that if I have a list of expected domains, like

a.myorganisation.com        192.168.1.10
b.myorganisation.com        192.168.1.11

I can resolve them in advance, and add specific routes using pppd.

However, I am wondering if it is still possible when the domain names *.myorganisation.com (* can be any subdomain) are not known in advance.

ayaka14732 avatar Sep 17 '21 06:09 ayaka14732

Just to clarify, is this a discussion about name resolution (DNS) or routing?

And where and when would the information "not known in advance" come from?

DimitriPapadopoulos avatar Sep 17 '21 07:09 DimitriPapadopoulos

For example, assume that the organisation contains the following:

(name server)               192.168.1.2

a.myorganisation.com        93.184.216.34
b.myorganisation.com        93.184.216.35
...                         ...

x.myorganisation.com        192.168.1.10
y.myorganisation.com        192.168.1.11
...                         ...

Currently, after running openfortivpn and opening a certain application of the organisation, assume that the following things will happen:

  1. the application send an request to *.myorganisation.com (* can be any subdomain)
  2. connected to the 192.168.1.2 (the name server) via ppp0
  3. the domain name is resolved by 192.168.1.2 (the name server)
  4. connected to the corresponding IP address via ppp0

and,

  1. the application send an request to google.com (or any domain outside the organisation)
  2. connected to the 192.168.1.2 (the name server) via ppp0
  3. the domain name is resolved by 192.168.1.2 (the name server)
  4. connected to the corresponding IP address via ppp0

Now what I want to achieve is:

  1. the application send an request to *.myorganisation.com (* can be any subdomain)
  2. we detect that the domain is ended with .myorganisation.com, so that should be resolved by 192.168.1.2 (the name server)
  3. connected to the 192.168.1.2 (the name server) via ppp0
  4. the domain name is resolved by 192.168.1.2 (the name server)
  5. connected to the corresponding IP address via ppp0

and,

  1. the application send an request to google.com (or any domain outside the organisation)
  2. we detect that the domain is not ended with .myorganisation.com, so that should be resolved by the original name server before running openfortivpn
  3. connected to the original name server via the original interface
  4. the domain name is resolved by the original name server
  5. connected to the corresponding IP address via the original interface

ayaka14732 avatar Sep 17 '21 07:09 ayaka14732

OK. You want to use a different DNS server for different domains. I believe this is sometimes referred to as split DNS.

https://github.com/adrienverge/openfortivpn/search?q=%22split+DNS%22&type=issues

DimitriPapadopoulos avatar Sep 17 '21 07:09 DimitriPapadopoulos

Currently, I run a custom Python DNS server to solve this issue:

import dnslib
import socket
import subprocess

upstream_internet = ('10.0.10.254', 53)
upstream_intranet = ('192.168.1.2', 53)
intranet_domain_suffix = '.myorganisation.com.'

socket_server = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
socket_server.bind(('127.0.0.1', 53))
socket_client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

while True:
    # Receive
    dns_query, addr = socket_server.recvfrom(512)
    dns_query_record = dnslib.DNSRecord.parse(dns_query)
    is_intranet = any(
        str(question.qname).endswith(intranet_domain_suffix)
        for question in dns_query_record.questions
        if dnslib.QTYPE.get(question.qtype) == 'A'
    )

    # Dispatch
    upstream_addr = upstream_intranet if is_intranet else upstream_internet
    socket_client.sendto(dns_query, upstream_addr)
    dns_answer = socket_client.recv(512)
    dns_answer_record = dnslib.DNSRecord.parse(dns_answer)

    # Add route
    if is_intranet:
        for rr in dns_answer_record.rr:
            if dnslib.QTYPE.get(rr.rtype) == 'A':
                ipv4_addr = repr(rr.rdata)
                subprocess.run(['ip', 'r', 'add', ipv4_addr, 'dev', 'ppp0'])

    # Response
    socket_server.sendto(dns_answer, addr)

ayaka14732 avatar Sep 18 '21 03:09 ayaka14732

Instead of a python script you could use something like dnsmasq with custom server stanzas. Ex. server=/myorganization.com/192.168.x.x would resolve all queries for *.myorganization.com using 192.168.x.x. Although maybe not - I'm not quite sure why you're adding routes in your script - that seems a bit weird. It seems you could push routes from FG as usual.

mulderr avatar Oct 03 '23 09:10 mulderr