Skip to content
This repository has been archived by the owner on Jun 4, 2021. It is now read-only.

WIP: Streisand IPv6 support #1471

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
Open
7 changes: 7 additions & 0 deletions IPv6-status.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# IPv6 Status

IPv6 support has been tested on the following providers with successful results:

- DigitalOcean
- Linode
- Vultr using localhost install
2 changes: 2 additions & 0 deletions global_vars/default-site.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ streisand_ssh_private_key: "~/.ssh/id_rsa"
vpn_clients: 10

streisand_ad_blocking_enabled: no
streisand_ipv6_enabled: no

streisand_openconnect_enabled: yes
streisand_openvpn_enabled: yes
streisand_shadowsocks_enabled: yes
Expand Down
2 changes: 2 additions & 0 deletions global_vars/integration/test-site.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ streisand_admin_email_var: ""
# Take a few extra steps during server provisioning to make the client tests work
streisand_client_test: true

streisand_ipv6_enabled: no

# Only services with corresponding tests are enabled.
streisand_ad_blocking_enabled: yes
streisand_shadowsocks_enabled: yes
Expand Down
2 changes: 2 additions & 0 deletions global_vars/noninteractive/amazon-site.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ streisand_ssh_private_key: "~/.ssh/id_rsa"
vpn_clients: 10

streisand_ad_blocking_enabled: no
streisand_ipv6_enabled: yes

streisand_openconnect_enabled: yes
streisand_openvpn_enabled: yes
streisand_shadowsocks_enabled: yes
Expand Down
3 changes: 2 additions & 1 deletion global_vars/noninteractive/azure-site.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ streisand_ssh_private_key: "~/.ssh/id_rsa"

vpn_clients: 10

streisand_ad_blocking_enabled: no
streisand_ipv6_enabled: no

streisand_openconnect_enabled: yes
streisand_openvpn_enabled: yes
streisand_shadowsocks_enabled: yes
Expand Down
2 changes: 2 additions & 0 deletions global_vars/noninteractive/digitalocean-site.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ streisand_ssh_private_key: "~/.ssh/id_rsa"
vpn_clients: 10

streisand_ad_blocking_enabled: no
streisand_ipv6_enabled: yes

streisand_openconnect_enabled: yes
streisand_openvpn_enabled: yes
streisand_shadowsocks_enabled: yes
Expand Down
3 changes: 2 additions & 1 deletion global_vars/noninteractive/google-site.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ streisand_ssh_private_key: "~/.ssh/id_rsa"

vpn_clients: 10

streisand_ad_blocking_enabled: no
streisand_ipv6_enabled: no

streisand_openconnect_enabled: yes
streisand_openvpn_enabled: yes
streisand_shadowsocks_enabled: yes
Expand Down
2 changes: 2 additions & 0 deletions global_vars/noninteractive/linode-site.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ streisand_ssh_private_key: "~/.ssh/id_rsa"
vpn_clients: 10

streisand_ad_blocking_enabled: no
streisand_ipv6_enabled: yes

streisand_openconnect_enabled: yes
streisand_openvpn_enabled: yes
streisand_shadowsocks_enabled: yes
Expand Down
2 changes: 2 additions & 0 deletions global_vars/noninteractive/local-site.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ streisand_ssh_private_key: "~/.ssh/id_rsa"
vpn_clients: 10

streisand_ad_blocking_enabled: no
streisand_ipv6_enabled: yes

streisand_openconnect_enabled: yes
streisand_openvpn_enabled: yes
streisand_shadowsocks_enabled: yes
Expand Down
2 changes: 2 additions & 0 deletions global_vars/noninteractive/rackspace-site.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ streisand_ssh_private_key: "~/.ssh/id_rsa"
vpn_clients: 10

streisand_ad_blocking_enabled: no
streisand_ipv6_enabled: yes

streisand_openconnect_enabled: yes
streisand_openvpn_enabled: yes
streisand_shadowsocks_enabled: yes
Expand Down
12 changes: 12 additions & 0 deletions global_vars/vars.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
upstream_dns_servers:
- 8.8.8.8
- 8.8.4.4

upstream_dns_servers_v6:
- "2001:4860:4860::8888"
- "2001:4860:4860::8844"

streisand_client_test: no

streisand_site_vars: "{{ lookup('env','HOME') }}/.streisand/site.yml"
7 changes: 7 additions & 0 deletions playbooks/customize.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@
prompt: "Enable DNS-based ad-blocking? Press enter for default "
default: "no"
private: no
- name: streisand_ipv6_enabled
prompt: "Enable IPv6, if available? Press enter for default (no)"
default: "no"
private: no
- name: streisand_openconnect_enabled
prompt: "Enable OpenConnect? Press enter for default "
default: "yes"
Expand Down Expand Up @@ -74,6 +78,9 @@
path: "{{ streisand_site_vars }}"
regexp: "^streisand_ad_blocking_enabled: (?:yes|no)$"
line: "streisand_ad_blocking_enabled: {{ streisand_ad_blocking_enabled }}"
- lineinfile:
regexp: "^streisand_ipv6_enabled: (?:yes|no)$"
line: "streisand_ipv6_enabled: {{ streisand_ipv6_enabled }}"
- lineinfile:
path: "{{ streisand_site_vars }}"
regexp: "^streisand_openconnect_enabled: (?:yes|no)$"
Expand Down
4 changes: 4 additions & 0 deletions playbooks/roles/certificates/templates/openssl.cnf.j2
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,10 @@ subjectAltName = @alt_names
{% for item in tls_sans %}
IP.{{ loop.index }} = {{ item }}
{% endfor %}
{% if streisand_ipv6_address is defined %}
IP.1 = {{ streisand_ipv6_address }}
{% endif %}


[ req_distinguished_name ]
countryName = Country Name (2 letter code)
Expand Down
13 changes: 13 additions & 0 deletions playbooks/roles/common/tasks/set-default-variables.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,16 @@

- import_tasks: detect-public-ip.yml
when: (hostvars['127.0.0.1']['streisand_genesis_role'] is defined and ((hostvars['127.0.0.1']['streisand_genesis_role'] == "localhost") or (hostvars['127.0.0.1']['streisand_genesis_role'] == "existing-server")))

- name: If streisand_ipv6_address is undefined and IPv6 is enabled, change it to Ansible's default IPv6 address
set_fact:
streisand_ipv6_address: "{{ ansible_default_ipv6.address }}"
when:
- streisand_ipv6_address is not defined
- streisand_ipv6_enabled
- ansible_default_ipv6.address is defined

- name: If there's an IPv6 address, generate a gateway URL using it
set_fact:
streisand_gateway_url_ipv6: "https://[{{ streisand_ipv6_address }}]"
when: streisand_ipv6_address is defined
7 changes: 6 additions & 1 deletion playbooks/roles/dnsmasq/templates/dnsmasq.conf.j2
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
# dnsmasq will not automatically listen on the loopback interface. To achieve
# this, its IP address, 127.0.0.1, must be explicitly given as
# a --listen-address option.
listen-address=127.0.0.1
listen-address=::1,127.0.0.1

# Never forward plain names (without a dot or domain part)
domain-needed
Expand All @@ -21,3 +21,8 @@ no-resolv
{% for item in upstream_dns_servers %}
server={{ item }}
{% endfor %}
{% if streisand_ipv6_address is defined %}
{% for item in upstream_dns_servers_v6 %}
server={{ item }}
{% endfor %}
{% endif %}
12 changes: 9 additions & 3 deletions playbooks/roles/ip-forwarding/tasks/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,15 @@
value: 1
when: ansible_virtualization_type != 'lxc'

- name: "Add IPv4 traffic forwarding persistence service to init"
copy:
src: streisand-ipforward.sh
- name: "Enable IPv6 traffic forwarding"
sysctl:
name: net.ipv6.conf.all.forwarding
value: 1
when: (ansible_virtualization_type != 'lxc') and (streisand_ipv6_enabled)

- name: "Add IPv4/IPv6 traffic forwarding persistence service to init"
template:
src: streisand-ipforward.sh.j2
dest: /etc/init.d/streisand-ipforward
mode: 0755

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@
### END INIT INFO

echo 1 > /proc/sys/net/ipv4/ip_forward

echo 0 | tee /proc/sys/net/ipv4/conf/*/*_redirects

{% if streisand_ipv6_address is defined %}
echo 1 > /proc/sys/net/ipv6/conf/all/forwarding
{% endif %}

exit 0
6 changes: 5 additions & 1 deletion playbooks/roles/openconnect/tasks/firewall.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@
to_port: "53"
proto: "udp"
rule: "allow"
from_ip: "192.168.1.0/24"
from_ip: "{{ item.addr }}"
with_items:
- { addr: "{{ ocserv_ipv4_network }}" }
- { addr: "{{ ocserv_ipv6_network }}", create: "{{ streisand_ipv6_enabled }}" }
when: item.create | default(True) | bool

- name: Ensure UFW allows OpenConnect (ocserv)
ufw:
Expand Down
14 changes: 12 additions & 2 deletions playbooks/roles/openconnect/templates/config.j2
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,18 @@ use-occtl = true
pid-file = {{ ocserv_pid_file }}
device = vpns
default-domain = example.com
ipv4-network = 192.168.1.0
ipv4-netmask = 255.255.255.0
ipv4-network = {{ ocserv_ipv4_network }}

{% if streisand_ipv6_address is defined %}
ipv6-network = {{ ocserv_ipv6_network }}
{% for item in upstream_dns_servers_v6 %}
dns = {{ item }}
{% endfor %}
ipv6-subnet-prefix = 64
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would using ipaddr, make life easier here? {{ ocserv_ipv6_network | ipaddr('prefix') }}

{% endif %}

route = default

ping-leases = false
cisco-client-compat = true
max-clients = {{ vpn_clients + 1 }}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,9 @@ Type=oneshot
RemainAfterExit=true
ExecStart=/sbin/{{ ocserv_firewall_rule }}

{% if streisand_ipv6_enabled %}
ExecStart=/sbin/{{ ocserv_firewall_rule_v6 }}
{% endif %}

[Install]
WantedBy=multi-user.target
WantedBy=multi-user.target
12 changes: 12 additions & 0 deletions playbooks/roles/openconnect/templates/ocserv-ipv6tables.service.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[Unit]
Description=Set the firewall rules required for ocserv
After=network.target
Before=ocserv.service

[Service]
Type=oneshot
RemainAfterExit=true
ExecStart=/sbin/{{ ocserv_firewall_rule_v6 }}

[Install]
WantedBy=multi-user.target
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing a trailing newline.

4 changes: 4 additions & 0 deletions playbooks/roles/openconnect/vars/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ ocserv_path: "/etc/ocserv"
ocserv_ca: "{{ ocserv_path }}/ca"
ocserv_config_file: "{{ ocserv_path }}/ocserv.conf"
ocserv_firewall_rule: "iptables --wait {{ streisand_iptables_wait }} -t nat -A POSTROUTING -j MASQUERADE"
ocserv_firewall_rule_v6: "ip6tables --wait {{ streisand_iptables_wait }} -t nat -A POSTROUTING -j MASQUERADE"

ocserv_ipv4_network: "192.168.1.0/24"
ocserv_ipv6_network: "fda9:4efe:7e3b:03ea::/48"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ideally, we should generate a ULA per Streisand system. For ocserv we'd only want a /64, though?


ocserv_days_valid: "1825"
ocserv_pid_file: "/var/run/ocserv.pid"
Expand Down
19 changes: 16 additions & 3 deletions playbooks/roles/openvpn/tasks/firewall.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,32 @@
command: "{{ item }}"
with_items: "{{ openvpn_firewall_rules }}"

- name: Ensure UFW allows DNS requests from OpenVPN clients
- name: Allow OpenVPN over IPv6 through the firewall
command: "{{ item }}"
with_items: "{{ openvpn_ipv6_firewall_rules }}"
when: streisand_ipv6_address is defined

- name: Ensure UFW allows DNS requests from OpenVPN TCP clients
ufw:
to_port: "53"
proto: "udp"
rule: "allow"
from_ip: "10.8.0.0/24"
from_ip: "{{ item.addr }}"
with_items:
- { addr: "{{ openvpn_server_tcp_ipv4_address }}" }
- { addr: "{{ openvpn_server_tcp_ipv6_address }}", create: "{{ streisand_ipv6_enabled }}" }
when: item.create | default(True) | bool

- name: Ensure UFW allows DNS requests from OpenVPN UDP clients
ufw:
to_port: "53"
proto: "udp"
rule: "allow"
from_ip: "10.9.0.0/24"
from_ip: "{{ item.addr }}"
with_items:
- { addr: "{{ openvpn_server_udp_ipv4_address }}" }
- { addr: "{{ openvpn_server_udp_ipv6_address }}", create: "{{ streisand_ipv6_enabled }}" }
when: item.create | default(True) | bool

- name: Ensure UFW allows OpenVPN
ufw:
Expand Down
6 changes: 5 additions & 1 deletion playbooks/roles/openvpn/tasks/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,14 @@
# Add the apt key and install OpenVPN
- import_tasks: install.yml

- name: "Configure DNSMasq to listen on {{ dnsmasq_openvpn_tcp_ip }}:53 and {{ dnsmasq_openvpn_udp_ip }}:53"
- name: "Configure DNSMasq to listen on TCP and UDP ports 53"
template:
src: openvpn_dnsmasq.conf.j2
dest: /etc/dnsmasq.d/openvpn.conf
with_items:
- { src: "openvpn_dnsmasq.conf.j2", dst: "/etc/dnsmasq.d/openvpn.conf" }
- { src: "openvpn_dnsmasqv6.conf.j2", dst: "/etc/dnsmasq.d/openvpnv6.conf", create: "{{ streisand_ipv6_enabled }}" }
when: item.create | default(True) | bool
notify: Restart dnsmasq

- include_role:
Expand Down
5 changes: 3 additions & 2 deletions playbooks/roles/openvpn/templates/client-common.j2
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
dev tun
dev tun-ipv6
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be conditional on having IPv6 enabled, or do we want this always-on because it will catch IPv6 leaks or something?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll have to test this scenario to check for leaks, thanks for pointing it out

cipher {{ openvpn_cipher }}
auth {{ openvpn_auth_digest }}
resolv-retry infinite
Expand All @@ -10,7 +10,8 @@ verify-x509-name {{ openvpn_server_common_name.stdout }} name
tls-version-min 1.2
compress
verb 3
route {{ streisand_ipv4_address }} 255.255.255.255 net_gateway

#route {{ streisand_ipv4_address }} 255.255.255.255 net_gateway

<ca>
{{ openvpn_ca_contents.stdout }}
Expand Down
6 changes: 4 additions & 2 deletions playbooks/roles/openvpn/templates/client-direct-udp.ovpn.j2
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
client
remote {{ openvpn_server }} {{ openvpn_port_udp }}
proto udp
{% if streisand_ipv6_address is defined %}
remote {{ streisand_ipv6_address }} {{ openvpn_port_udp }} udp6
{% endif %}
remote {{ openvpn_server }} {{ openvpn_port_udp }} udp
{% include "client-common.j2" %}
6 changes: 4 additions & 2 deletions playbooks/roles/openvpn/templates/client-direct.ovpn.j2
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
client
remote {{ openvpn_server }} {{ openvpn_port }}
proto tcp
{% if streisand_ipv6_address is defined %}
remote {{ streisand_ipv6_address }} {{ openvpn_port}} tcp6
{% endif %}
remote {{ openvpn_server }} {{ openvpn_port }} tcp
{% include "client-common.j2" %}
6 changes: 4 additions & 2 deletions playbooks/roles/openvpn/templates/client-sslh.ovpn.j2
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
client
remote {{ openvpn_server }} {{ openvpn_port_sslh }}
proto tcp
{% if streisand_ipv6_address is defined %}
remote {{ streisand_ipv6_address }} {{ openvpn_port_sslh }} tcp6
{% endif %}
remote {{ openvpn_server }} {{ openvpn_port_sslh }} tcp
{% include "client-common.j2" %}
8 changes: 7 additions & 1 deletion playbooks/roles/openvpn/templates/etc_openvpn_server.conf.j2
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
server 10.8.0.0 255.255.255.0
push "dhcp-option DNS {{ dnsmasq_openvpn_tcp_ip }}"
proto tcp
proto tcp6

{% if streisand_ipv6_address is defined %}
server-ipv6 2001:db8:0:124::/64
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can't use 2001:db8:: for anything. Do we want a second ULA for now, or to separate subnets inside our "main" ULA?

push "dhcp-option DNS6 {{ dnsmasq_openvpn_tcp_ipv6 }}"
{% endif %}

port {{ openvpn_port }}
{% include "etc_openvpn_server_common.j2" %}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ dh none
ifconfig-pool-persist ipp.txt
push "redirect-gateway def1"

{% if streisand_ipv6_address is defined %}
push "route-ipv6 ::/0"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As a side note, do we want to push IPv6 routes to people all the time anyway, to avoid IPv6 leaks?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, we definitely should. It's being done for WireGuard as is so others should follow suite

{% endif %}

# Fix for the Windows 10 DNS leak described here:
# https://community.openvpn.net/openvpn/ticket/605
push block-outside-dns
Expand Down
Loading