Access Server Double-NAT notes
------------------------------

"Double-NAT" is a feature that allows the client view of the VPN
network numbering (including pushed local IP endpoint, pushed
routes, pushed DNS or WINS servers, etc.) to differ from the server
view, and is accomplished by a stateless, one-to-one NAT
transformation of packets sent to or received by the client.

Double NAT allows multiple clients to use non-unique local subnets,
and translates those subnets into unique, non-conflicting subnets
before they enter the OAS routing stack.  Double NAT also allows
clients to access remote VPN resources using a translated IP address
or subnet, to avoid conflicting with local IP numbering.

Configuration
-------------

Two types of NAT rules may be defined, Source NAT (SNAT) and
Destination NAT (DNAT).  SNAT refers to addresses owned by the
client while DNAT refers to addresses that are remote to the
client and reachable via the VPN.

DNAT rules may be defined as user or group properties
records.  For example:

  "client_dnat.0" : "5.5.0.0/16(192.168.0.0)"

indicates that the 5.5.0.0/16 subnet on the server should be aliased
so that the client views it as 192.168.0.0/16.  client_dnat
rules may be placed in groups and inherited by users that are
members of the group.

A DNAT rule may also be encapsulated within an access_to entry.
For example:

    "access_to.0": "+SUBNET:10.245.227.40(9.9.9.9)",

indicates that the server-side resource 10.245.227.40 should be
accessible via a client-alias of 9.9.9.9, and further, that
the server should add an ACL rule allowing this access.  This
usage is also permissible in groups, and allows members of
the group to inherit both the ACL and DNAT rules.

SNAT rules, since they refer to client-side resources, may
only be defined at user scope (not group scope).  For example:

  "client_snat.0" : "5.5.64.0/24(10.10.0.0)"

indicates that the client owns the 10.10.0.0/24 subnet, but
that it should be translated to 5.5.64.0/24 from the view
of the server.

As a convenience feature, SNAT rules can also be encapsulated
in c2s_route entries, for example:

  "c2s_route.0": "5.64.0.0/24(10.40.0.1)"

indicates that the client will act as a gateway for 10.40.0.1/24,
but that the subnet should be translated to 5.64.0.0/24 from the
view of the server.

Admin UI Considerations
-----------------------

There are two levels of support that can be provided in the Admin UI:

(a) The admin UI already supports the user/group properties
    "access_to" and "c2s_route".  It would need to allow the
    parenthetical extension to both of these properties where
    the alias IP base is specified.  For example, where
    previously "c2s_route" or "access_to" might specify
    "5.64.0.0/24", now it would also be syntactically correct
    to specify "5.64.0.0/24(10.40.0.1)".

(b) The Admin UI could fully support client_dnat and client_snat
    user properties attributes.  This would require more
    significant UI changes.

Single Client Test Case
-----------------------

The following test case exercises several different aspects of the
Double-NAT functionality:

(a) Using SNAT to renumber the 5.5.0.0/16 subnet used for client IP
    address assignment to 192.168.0.0/16 for the 'test' user.

(b) Using DNAT so that the 'test' user may refer to the server side
    resource 10.245.227.40 using an IP alias of 9.9.9.9.

(c) Using SNAT and DNAT together on the same transformation
    ( 5.5.0.0/16(192.168.0.0) ) to test the ability of translated
    addresses to be used as both source and destination
    addresses.

(d) Using an access_to rule to grant ACL access to a remote
    resource 10.245.227.40 while simultaneously specifing
    a client-side DNAT alias for this resource (9.9.9.9).

(e) Defining a DNAT rule in a group, where members of the group
    will inherit the rule.

When running this test, it's important to cycle reroute_gw
through both on and off states to test the different code
paths for pushed route generation.

     "vpn.client.routing.reroute_gw": "true",

and

     "vpn.client.routing.reroute_gw": "false",

In this test I had a web server running on 10.245.227.40 (on a second
server), so that I could test TCP connectivity through the NAT
transformation.

Tests on client after VPN session established:

(a) Verify SNAT transformation by pinging the gateway:
    ping 192.168.0.1

(b) Verify DNAT transformation by pinging the remote resource:
    ping 9.9.9.9

(c) Verify TCP connectivity to the remote web server:
    curl http://9.9.9.9

For the test case, the User Properties DB is configured as such:

{
  "__DEFAULT__": {
    "prop_autogenerate": "true",
    "type": "user_default"
  },
  "openvpn": {
    "prop_superuser": "true",
    "type": "user_compile"
  },
  "mygroup": {
    "group_declare": "true",
    "access_to.0": "+SUBNET:10.245.227.40(9.9.9.9)",
    "client_dnat.0" : "5.5.0.0/16(192.168.0.0)",
    "prop_autologin": "true",
    "type": "group"
  },
  "test": {
    "client_snat.0" : "5.5.0.0/16(192.168.0.0)",
    "conn_group": "mygroup",
    "type": "user_compile"
  }
}

and the Config DB is configured as such:

{
  "Default": {
    "admin_ui.https.ip_address": "eth0",
    "admin_ui.https.port": "943",
    "auth.ldap.0.name": "My LDAP servers",
    "auth.ldap.0.ssl_verify": "never",
    "auth.ldap.0.timeout": "4",
    "auth.ldap.0.use_ssl": "never",
    "auth.module.type": "local",
    "auth.pam.0.service": "openvpnas",
    "auth.radius.0.acct_enable": "false",
    "auth.radius.0.name": "My Radius servers",
    "cs.https.ip_address": "eth0",
    "cs.https.port": "943",
    "host.name": "ami3.yonan.net",
    "sa.initial_run_groups.0": "web_group",
    "sa.initial_run_groups.1": "openvpn_group",
    "vpn.client.routing.inter_client": "false",
    "vpn.client.routing.reroute_dns": "true",
    "vpn.client.routing.reroute_gw": "false",
    "vpn.daemon.0.client.netmask_bits": "20",
    "vpn.daemon.0.client.network": "5.5.0.0",
    "vpn.daemon.0.listen.ip_address": "eth0",
    "vpn.daemon.0.listen.port": "443",
    "vpn.daemon.0.listen.protocol": "tcp",
    "vpn.daemon.0.server.ip_address": "eth0",
    "vpn.server.daemon.enable": "true",
    "vpn.server.daemon.tcp.n_daemons": "1",
    "vpn.server.daemon.tcp.port": "443",
    "vpn.server.daemon.udp.n_daemons": "1",
    "vpn.server.daemon.udp.port": "1194",
    "vpn.server.group_pool.0": "5.5.16.0/20",
    "vpn.server.port_share.enable": "true",
    "vpn.server.port_share.ip_address": "1.2.3.4",
    "vpn.server.port_share.port": "1234",
    "vpn.server.port_share.service": "admin+client",
    "vpn.server.routing.private_access": "nat",
    "vpn.tls_refresh.do_reauth": "true",
    "vpn.tls_refresh.interval": "360"
  }
}

Overlapping Subnets Test Case
-----------------------------

In this test case, we have three clients that each act as a gateway
for their local 192.168.0.0/24 networks.  Internally in the OAS, we
will number each subnet uniquely, i.e. 5.64.G.0/24 where G
is the gateway number.  But neither the gateways, nor the clients
that access them, will ever see the 5.64 numbering.  All clients
will access a given gateway via its native numbering, i.e.
192.168.0.0/24.  The determination of which gateway a client will
access depends on which group the client is a member of.
Clients that are members of group1 will see gateway1, members of
group2 will see gateway2, etc.

  "gateway1": {
    "c2s_route.0" : "5.64.1.0/24(192.168.0.0)",
    "type": "user_compile"
  },

  "gateway2": {
    "c2s_route.0" : "5.64.2.0/24(192.168.0.0)",
    "type": "user_compile"
  },

  "gateway3": {
    "c2s_route.0" : "5.64.3.0/24(192.168.0.0)",
    "type": "user_compile"
  },

We also have three groups for clients needing to access the
gateways, where each group is dedicated to reaching one of
the gateways on 192.168.0.0.

  "group1": {
    "group_declare": "true",
    "access_to.0": "+GROUP:gateway1",
    "client_dnat.0": "5.64.1.0/24(192.168.0.0)",
    "type": "group"
  },

  "group2": {
    "group_declare": "true",
    "access_to.0": "+GROUP:gateway2",
    "client_dnat.0": "5.64.2.0/24(192.168.0.0)",
    "type": "group"
  },

  "group3": {
    "group_declare": "true",
    "access_to.0": "+GROUP:gateway3",
    "client_dnat.0": "5.64.3.0/24(192.168.0.0)",
    "type": "group"
  },

Now, any client that is a member of group1 will be able to access
gateway1 via 192.168.0.0/24, any client that is a member of group2
will be able to access gateway2 via 192.168.0.0/24, etc.

Debugging
---------

Setting verbosity to 6 or higher on the OpenVPN client ("verb 6")
will cause it to output information on DNAT and SNAT transformations.

Admin UI Implementation
-----------------------

When handling user input for c2s_route or access_to lists in userprop DB,
use this method on the given string to validate it:

  from pyovpn.util.ipv4 import IPv4
  IPv4.NATSpec(string)

An exception will be raised if validation fails.

string can be an ordinary subnet/netbits spec such as:

  5.64.3.0/24

or an extended double NAT spec such as:

  5.64.3.0/24(192.168.0.0)

Single client, single gateway test
----------------------------------

This test allows members of group2 to access 10.10.0.0/24 on gateway2.
Within the AS, the subnet is referred to as 5.64.2.0/24.

{
  "__DEFAULT__": {
    "prop_autogenerate": "true",
    "prop_autologin": "true",
    "type": "user_default"
  },
  "gateway2": {
    "c2s_route.0": "5.64.2.0/24",
    "client_snat.0" : "5.64.2.0/24(10.10.0.0)",
    "prop_reroute_gw_override": "disable",
    "type": "user_compile"
  },
  "group2": {
    "access_to.0": "+GROUP:gateway2",
    "client_dnat.0" : "5.64.2.0/24(10.10.0.0)",
    "group_declare": "true",
    "type": "group"
  },
  "openvpn": {
    "prop_superuser": "true",
    "type": "user_compile"
  },
  "james": {
    "conn_group": "group2",
    "type": "user_connect"
  }
}

Note that both gateway2 and group2 can be alternatively defined without the
use of client_snat or client_dnat:

  "gateway2": {
    "c2s_route.0": "5.64.2.0/24(10.10.0.0)",
    "prop_reroute_gw_override": "disable",
    "type": "user_compile"
  },

  "group2": {
    "access_to.0": "+GROUP:gateway2",
    "access_to.1": "+SUBNET:5.64.2.0/24(10.10.0.0)",
    "group_declare": "true",
    "type": "group"
  },
