Route-based VPNs » History » Version 5
Noel Kuntze, 21.03.2019 00:46
Show two example scripts, one for the GW side. Name the two scripts distinctly. Use key instead of okey and ikey.
1 | 1 | Tobias Brunner | h1. Route-based VPNs |
---|---|---|---|
2 | 1 | Tobias Brunner | |
3 | 1 | Tobias Brunner | {{>toc}} |
4 | 1 | Tobias Brunner | |
5 | 1 | Tobias Brunner | Generally IPsec processing is based on policies. After regular route lookups are done the OS kernel consults its SPD(Security Policy Database) for a matching policy and if one is found that is associated with an IPsec SA(Security Association) the packet is processed (e.g. encrypted and sent as ESP packet). Refer to [[IPsecDocumentation]] for details. |
6 | 1 | Tobias Brunner | |
7 | 1 | Tobias Brunner | Depending on the operating system it is also possible to configure route-based VPNs. Here IPsec processing does not (only) depend on negotiated policies but may e.g. be controlled by routing packets to a specific interface. |
8 | 1 | Tobias Brunner | |
9 | 1 | Tobias Brunner | Most of these approaches also allow easy capture of plaintext traffic, which, depending on the operating system, might not be that straight-forward with policy-based VPNs (see [[CorrectTrafficDump]]). Another advantage this approach could have is that the MTU can be specified for the tunneling devices allowing to fragment packets before tunneling them in case PMTUD does not work properly. |
10 | 1 | Tobias Brunner | |
11 | 1 | Tobias Brunner | h2. VTI Devices on Linux |
12 | 1 | Tobias Brunner | |
13 | 1 | Tobias Brunner | _*Disclaimer:* VTI devices are supported since the Linux 3.6 kernel, but some important changes were added later (3.15+). The information below might not be accurate for older kernel versions._ |
14 | 1 | Tobias Brunner | |
15 | 1 | Tobias Brunner | VTI devices act like a wrapper around existing IPsec policies. This means you can't just route arbitrary packets to a VTI device to get them tunneled, the established IPsec policies have to match too. However, you can negotiate _0.0.0.0/0_ traffic selectors on both ends to allow tunneling anything that's routed via VTI device. |
16 | 1 | Tobias Brunner | |
17 | 1 | Tobias Brunner | To make this work, that is, to prevent packets not routed via VTI device from matching the policies (if _0.0.0.0/0_ is used every packet would match) marks are used. Only packets that are marked accordingly will match the policies and get tunneled, for other packets the policies are ignored. Whenever a packet is routed to a VTI device it automatically gets the configured mark applied so it will match the policy and get tunneled. |
18 | 1 | Tobias Brunner | |
19 | 1 | Tobias Brunner | It's important to note that VTI tunnel devices are a local feature, no additional encapsulation (like with GRE, see below) is added, so the other end does not have to be aware that VTI devices are used in addition to regular IPsec policies. |
20 | 1 | Tobias Brunner | |
21 | 1 | Tobias Brunner | A VTI device may be created with the following command: |
22 | 1 | Tobias Brunner | |
23 | 1 | Tobias Brunner | <pre> |
24 | 1 | Tobias Brunner | ip tunnel add <name> local <local IP> remote <remote IP> mode vti key <number equaling the mark> |
25 | 1 | Tobias Brunner | </pre> |
26 | 1 | Tobias Brunner | |
27 | 1 | Tobias Brunner | _<name>_ can be any valid device name (e.g. _ipsec0_, _vti0_ etc.). But note that the @ip@ command treats names starting with _vti_ special in some instances (e.g. when retrieving device statistics). The IPs are the endpoints of the IPsec tunnel. The number at the end has to match the mark configured for the connection. It is also possible to configure different marks for in- and outbound traffic using _ikey/okey <mark>_, but that is usually not required. |
28 | 1 | Tobias Brunner | |
29 | 1 | Tobias Brunner | After creating the device it has to be enabled (@ip link set <name> up@) and then routes may be installed (routing protocols may also be used). To avoid duplicate policy lookups it is also recommended to set @sysctl -w net.ipv4.conf.<name>.disable_policy=1@. All of this also works for IPv6. |
30 | 1 | Tobias Brunner | |
31 | 1 | Tobias Brunner | {{collapse(Examples) |
32 | 1 | Tobias Brunner | <pre> |
33 | 1 | Tobias Brunner | ip tunnel add vti0 local 192.168.0.1 remote 192.168.0.2 mode vti key 42 |
34 | 1 | Tobias Brunner | ip tunnel add ipsec0 local 192.168.0.1 remote 192.168.0.2 mode vti key 0x01000201 |
35 | 1 | Tobias Brunner | sysctl -w net.ipv4.conf.vti0.disable_policy=1 |
36 | 1 | Tobias Brunner | ip link set vti0 up |
37 | 1 | Tobias Brunner | ip route add 10.1.0.0/16 dev vti0 |
38 | 1 | Tobias Brunner | sysctl -w net.ipv4.conf.ipsec0.disable_policy=1 |
39 | 1 | Tobias Brunner | ip link set ipsec0 up |
40 | 1 | Tobias Brunner | ip route add 10.2.0.0/16 dev ipsec0 |
41 | 1 | Tobias Brunner | ip route add 10.3.0.0/16 dev ipsec0 |
42 | 1 | Tobias Brunner | </pre> |
43 | 1 | Tobias Brunner | }} |
44 | 1 | Tobias Brunner | |
45 | 1 | Tobias Brunner | Statistics on VTI devices may be displayed with @ip -s tunnel show [<name>]@. Note that specifying a name will not show any statistics if the device name starts with _vti_. |
46 | 1 | Tobias Brunner | |
47 | 1 | Tobias Brunner | A VTI device may be removed again with @ip tunnel del <name>@. |
48 | 1 | Tobias Brunner | |
49 | 1 | Tobias Brunner | h3. Configuration |
50 | 1 | Tobias Brunner | |
51 | 4 | Noel Kuntze | *First, the route installation by the IKE daemon must be disabled. To do this, set _charon.install_routes = 0_ in [[strongswan.conf]].* |
52 | 1 | Tobias Brunner | |
53 | 1 | Tobias Brunner | Then configure a regular site-to-site connection either with the traffic selectors set to _0.0.0.0/0_ on both ends (_local|remote_ts=0.0.0.0.0/0_ in [[swanctl.conf]] or _left|rightsubnet=0.0.0.0/0_ in [[ipsec.conf]]) or set to specific subnets. As mentioned above, only traffic that matches these traffic selectors will then actually be forwarded, other packets will be rejected with an ICMP error message (_destination unreachable/destination host unreachable_). |
54 | 1 | Tobias Brunner | |
55 | 1 | Tobias Brunner | The most important configuration option is the mark (_mark_in|out_ in [[swanctl.conf]], _mark_ in [[ipsec.conf]]). After applying the optional mask (default is _0xffffffff_) to the mark set on the VTI device, and applied by it to the routed packets, the value has to match the configured mark. |
56 | 1 | Tobias Brunner | So referring to the example above, to match the mark on _vti0_ configure _mark_in_ = _mark_out_ = _42_ and to match the mark on _ipsec0_ set the value to _0x01000201_ (but something like _0x00000001/0x0000000f_ would also work). |
57 | 1 | Tobias Brunner | |
58 | 1 | Tobias Brunner | h3. Sharing VTI Devices |
59 | 1 | Tobias Brunner | |
60 | 1 | Tobias Brunner | VTI devices may be shared by multiple IPsec SAs (e.g. in roadwarrior scenarios, to capture traffic or lower the MTU) by setting the remote endpoint of the VTI device to 0.0.0.0. For instance: |
61 | 1 | Tobias Brunner | |
62 | 1 | Tobias Brunner | <pre> |
63 | 1 | Tobias Brunner | ip tunnel add ipsec0 local 192.168.0.1 remote 0.0.0.0 mode vti key 42 |
64 | 1 | Tobias Brunner | </pre> |
65 | 1 | Tobias Brunner | |
66 | 1 | Tobias Brunner | Then assuming [[VirtualIP|virtual IPs]] for roadwarriors are assigned from the _10.0.1.0/24_ subnet a matching route may be installed with @ip route add 10.0.1.0/24 dev ipsec0@. |
67 | 1 | Tobias Brunner | |
68 | 1 | Tobias Brunner | h3. Connection-specific VTI Devices |
69 | 1 | Tobias Brunner | |
70 | 1 | Tobias Brunner | With a custom [[updown]] script it is also possible to setup connection-specific VTI devices. |
71 | 1 | Tobias Brunner | |
72 | 1 | Tobias Brunner | For instance, to create a VTI device on a roadwarrrior client that receives a [[VirtualIP|dynamic virtual IP]] (courtesy of Endre Szabó): |
73 | 1 | Tobias Brunner | |
74 | 3 | Noel Kuntze | This does not work when two roadwarriors are connected from the same IP. The kernel rejects the creation of a VTI where |
75 | 3 | Noel Kuntze | the remote and local addresses of the VTI are already in use by another VTI. |
76 | 3 | Noel Kuntze | |
77 | 5 | Noel Kuntze | |
78 | 5 | Noel Kuntze | {{collapse(Example script for RWs) |
79 | 1 | Tobias Brunner | <pre> |
80 | 1 | Tobias Brunner | #!/bin/bash |
81 | 1 | Tobias Brunner | |
82 | 1 | Tobias Brunner | # set charon.install_virtual_ip = no to prevent the daemon from also installing the VIP |
83 | 1 | Tobias Brunner | |
84 | 1 | Tobias Brunner | set -o nounset |
85 | 1 | Tobias Brunner | set -o errexit |
86 | 1 | Tobias Brunner | |
87 | 1 | Tobias Brunner | VTI_IF="vti${PLUTO_UNIQUEID}" |
88 | 1 | Tobias Brunner | |
89 | 1 | Tobias Brunner | case "${PLUTO_VERB}" in |
90 | 1 | Tobias Brunner | up-client) |
91 | 1 | Tobias Brunner | ip tunnel add "${VTI_IF}" local "${PLUTO_ME}" remote "${PLUTO_PEER}" mode vti \ |
92 | 5 | Noel Kuntze | key "${PLUTO_MARK_OUT%%/*}" |
93 | 1 | Tobias Brunner | ip link set "${VTI_IF}" up |
94 | 1 | Tobias Brunner | ip addr add "${PLUTO_MY_SOURCEIP}" dev "${VTI_IF}" |
95 | 1 | Tobias Brunner | ip route add "${PLUTO_PEER_CLIENT}" dev "${VTI_IF}" |
96 | 1 | Tobias Brunner | sysctl -w "net.ipv4.conf.${VTI_IF}.disable_policy=1" |
97 | 1 | Tobias Brunner | ;; |
98 | 1 | Tobias Brunner | down-client) |
99 | 1 | Tobias Brunner | ip tunnel del "${VTI_IF}" |
100 | 1 | Tobias Brunner | ;; |
101 | 1 | Tobias Brunner | esac |
102 | 5 | Noel Kuntze | </pre> |
103 | 5 | Noel Kuntze | }} |
104 | 5 | Noel Kuntze | |
105 | 5 | Noel Kuntze | In the following script, it is assumed that only the RW's assigned IPv4 VIP is supposed to be reachable over the assigned tunnel. |
106 | 5 | Noel Kuntze | |
107 | 5 | Noel Kuntze | {{collapse(Example script for GWs) |
108 | 5 | Noel Kuntze | <pre> |
109 | 5 | Noel Kuntze | #!/bin/bash |
110 | 5 | Noel Kuntze | |
111 | 5 | Noel Kuntze | # set charon.install_virtual_ip = no to prevent the daemon from also installing the VIP |
112 | 5 | Noel Kuntze | |
113 | 5 | Noel Kuntze | set -o nounset |
114 | 5 | Noel Kuntze | set -o errexit |
115 | 5 | Noel Kuntze | |
116 | 5 | Noel Kuntze | VTI_IF="vti${PLUTO_UNIQUEID}" |
117 | 5 | Noel Kuntze | |
118 | 5 | Noel Kuntze | VTI_IF="vti${PLUTO_UNIQUEID}" |
119 | 5 | Noel Kuntze | |
120 | 5 | Noel Kuntze | case "${PLUTO_VERB}" in |
121 | 5 | Noel Kuntze | up-client) |
122 | 5 | Noel Kuntze | ip tunnel add "${VTI_IF}" local "${PLUTO_ME}" remote "${PLUTO_PEER}" mode vti \ |
123 | 5 | Noel Kuntze | key "${PLUTO_MARK_OUT%%/*}" |
124 | 5 | Noel Kuntze | ip link set "${VTI_IF}" up |
125 | 5 | Noel Kuntze | ip route add "${PLUTO_PEER_SOURCEIP}" dev "${VTI_IF}" |
126 | 5 | Noel Kuntze | sysctl -w "net.ipv4.conf.${VTI_IF}.disable_policy=1" |
127 | 5 | Noel Kuntze | ;; |
128 | 5 | Noel Kuntze | down-client) |
129 | 5 | Noel Kuntze | ip tunnel del "${VTI_IF}" |
130 | 5 | Noel Kuntze | ;; |
131 | 5 | Noel Kuntze | esac |
132 | 5 | Noel Kuntze | |
133 | 1 | Tobias Brunner | </pre> |
134 | 1 | Tobias Brunner | }} |
135 | 1 | Tobias Brunner | |
136 | 1 | Tobias Brunner | If there is more than one subnet in the remote traffic selector this might cause conflicts as the _updown_ script will be called for each combination of local and remote subnet. |
137 | 1 | Tobias Brunner | |
138 | 1 | Tobias Brunner | h2. Marks on Linux |
139 | 1 | Tobias Brunner | |
140 | 1 | Tobias Brunner | One of the core features of VTI devices, dynamically specifying which traffic to tunnel, can actually be replicated directly with marks and firewall rules. By configuring connections with marks and then selectively marking packets directly with Netfilter rules via @MARK@ target in the @PREROUTING@ or @FORWARD@ only specific traffic will get tunneled. |
141 | 1 | Tobias Brunner | |
142 | 1 | Tobias Brunner | This may also be used to create multiple identical tunnels for which firewall rules dynamically decide which traffic is tunneled though which IPsec SA (e.g. for {{tc(ikev2/net2net-psk-dscp, QoS/DiffServ)}}). |
143 | 1 | Tobias Brunner | |
144 | 1 | Tobias Brunner | h2. GRE |
145 | 1 | Tobias Brunner | |
146 | 1 | Tobias Brunner | An alternative to VTI devices is using GRE(Generic Routing Encapsulation), which is a generic point-to-point tunneling protocol that adds an additional encapsulation layer (at least 4 bytes). But it provides a portable way of creating route-based VPNs (running a routing protocol on-top is also easy). |
147 | 1 | Tobias Brunner | |
148 | 1 | Tobias Brunner | While VTI devices depend on site-to-site IPsec connections in tunnel mode, GRE uses a host-to-host connection that can also be run in transport mode (avoiding additional overhead). But while VTI devices may be used by only one of the hosts, GRE must be used by both of them. |
149 | 1 | Tobias Brunner | |
150 | 1 | Tobias Brunner | Creating a GRE tunnel on Linux can be done as follows: |
151 | 1 | Tobias Brunner | |
152 | 1 | Tobias Brunner | <pre> |
153 | 1 | Tobias Brunner | ip tunnel add <name> local <local IP> remote <remote IP> mode gre |
154 | 1 | Tobias Brunner | </pre> |
155 | 1 | Tobias Brunner | |
156 | 1 | Tobias Brunner | <name> can be any valid interface name (e.g. _ipsec0_, _gre0_ etc.). But note that the @ip@ command treats names starting with _gre_ special in some instances (e.g. when retrieving device statistics). The IPs are the endpoints of the IPsec tunnel. |
157 | 1 | Tobias Brunner | |
158 | 1 | Tobias Brunner | After creating the device it has to be enabled (@ip link set <name> up@) and then routes may be installed. |
159 | 1 | Tobias Brunner | |
160 | 1 | Tobias Brunner | {{collapse(Example) |
161 | 1 | Tobias Brunner | <pre> |
162 | 1 | Tobias Brunner | ip tunnel add ipsec0 local 192.168.0.1 remote 192.168.0.2 mode gre |
163 | 1 | Tobias Brunner | ip link set ipsec0 up |
164 | 1 | Tobias Brunner | ip route add 10.1.0.0/16 dev ipsec0 |
165 | 1 | Tobias Brunner | ip route add 10.2.0.0/16 dev ipsec0 |
166 | 1 | Tobias Brunner | </pre> |
167 | 1 | Tobias Brunner | }} |
168 | 1 | Tobias Brunner | |
169 | 1 | Tobias Brunner | Statistics on GRE devices may be displayed with @ip -s tunnel show [<name>]@. Note that specifying a name will not show any statistics if the device name starts with _gre_. |
170 | 1 | Tobias Brunner | |
171 | 1 | Tobias Brunner | A GRE device may be removed again with @ip tunnel del <name>@. |
172 | 1 | Tobias Brunner | |
173 | 1 | Tobias Brunner | h3. Configuration |
174 | 1 | Tobias Brunner | |
175 | 1 | Tobias Brunner | As mentioned above, a host-to-host IPsec connection in transport mode can be used. The traffic selectors may even be limited to just the GRE protocol (_local|remote_ts=dynamic[gre]_ in [[swanctl.conf]] or _left|rightsubnet=%dynamic[gre]_ in [[ipsec.conf]]). |
176 | 1 | Tobias Brunner | |
177 | 1 | Tobias Brunner | h2. libipsec And TUN Devices |
178 | 1 | Tobias Brunner | |
179 | 1 | Tobias Brunner | Based on our own userland IPsec implementation and the [[kernel-libipsec]] plugin it is possible to create route-based VPNs with TUN devices. Similar to VTI devices the negotiated IPsec policies have to match the traffic routed via TUN device. |
180 | 1 | Tobias Brunner | In particular because packets have to be copied between kernel and userland it is not as efficient as the solutions above (also read the notes on [[kernel-libipsec]]). |
181 | 2 | Noel Kuntze | |
182 | 2 | Noel Kuntze | h2. Problems |
183 | 2 | Noel Kuntze | |
184 | 2 | Noel Kuntze | Make sure to disable the [[connmark]] plugin when running the VTI. Otherwise, it will insert iptables rules into the @*mangle@ table that prevent the VTI from working. |