diff options
Diffstat (limited to 'puppet/modules/site_openvpn')
-rw-r--r-- | puppet/modules/site_openvpn/README | 20 | ||||
-rw-r--r-- | puppet/modules/site_openvpn/manifests/dh_key.pp | 10 | ||||
-rw-r--r-- | puppet/modules/site_openvpn/manifests/init.pp | 238 | ||||
-rw-r--r-- | puppet/modules/site_openvpn/manifests/resolver.pp | 50 | ||||
-rw-r--r-- | puppet/modules/site_openvpn/manifests/server_config.pp | 228 | ||||
-rw-r--r-- | puppet/modules/site_openvpn/templates/add_gateway_ips.sh.erb | 11 |
6 files changed, 557 insertions, 0 deletions
diff --git a/puppet/modules/site_openvpn/README b/puppet/modules/site_openvpn/README new file mode 100644 index 00000000..cef5be23 --- /dev/null +++ b/puppet/modules/site_openvpn/README @@ -0,0 +1,20 @@ +Place to look when debugging problems +======================================== + +Log files: + + openvpn: /var/log/syslog + shorewall: /var/log/syslog + shorewall startup: /var/log/shorewall-init.log + +Check NAT masq: + + iptables -t nat --list-rules + +Check interfaces: + + ip addr ls + +Scripts: + + /usr/local/bin/add_gateway_ips.sh
\ No newline at end of file diff --git a/puppet/modules/site_openvpn/manifests/dh_key.pp b/puppet/modules/site_openvpn/manifests/dh_key.pp new file mode 100644 index 00000000..13cc0f5b --- /dev/null +++ b/puppet/modules/site_openvpn/manifests/dh_key.pp @@ -0,0 +1,10 @@ +class site_openvpn::dh_key { + + $x509_config = hiera('x509') + + file { '/etc/openvpn/keys/dh.pem': + content => $x509_config['dh'], + mode => '0644', + } + +} diff --git a/puppet/modules/site_openvpn/manifests/init.pp b/puppet/modules/site_openvpn/manifests/init.pp new file mode 100644 index 00000000..f1ecefb9 --- /dev/null +++ b/puppet/modules/site_openvpn/manifests/init.pp @@ -0,0 +1,238 @@ +# +# An openvpn gateway can support three modes: +# +# (1) limited and unlimited +# (2) unlimited only +# (3) limited only +# +# The difference is that 'unlimited' gateways only allow client certs that match +# the 'unlimited_prefix', and 'limited' gateways only allow certs that match the +# 'limited_prefix'. +# +# We potentially create four openvpn config files (thus four daemons): +# +# (1) unlimited + tcp => tcp_config.conf +# (2) unlimited + udp => udp_config.conf +# (3) limited + tcp => limited_tcp_config.conf +# (4) limited + udp => limited_udp_config.conf +# + +class site_openvpn { + tag 'leap_service' + + include site_config::x509::cert + include site_config::x509::key + include site_config::x509::ca_bundle + + include site_config::default + Class['site_config::default'] -> Class['site_openvpn'] + + include ::site_obfsproxy + + $openvpn = hiera('openvpn') + $openvpn_ports = $openvpn['ports'] + $openvpn_config = $openvpn['configuration'] + + if $::ec2_instance_id { + $openvpn_gateway_address = $::ipaddress + } else { + $openvpn_gateway_address = $openvpn['gateway_address'] + if $openvpn['second_gateway_address'] { + $openvpn_second_gateway_address = $openvpn['second_gateway_address'] + } else { + $openvpn_second_gateway_address = undef + } + } + + $openvpn_allow_unlimited = $openvpn['allow_unlimited'] + $openvpn_unlimited_prefix = $openvpn['unlimited_prefix'] + $openvpn_unlimited_tcp_network_prefix = '10.41.0' + $openvpn_unlimited_tcp_netmask = '255.255.248.0' + $openvpn_unlimited_tcp_cidr = '21' + $openvpn_unlimited_udp_network_prefix = '10.42.0' + $openvpn_unlimited_udp_netmask = '255.255.248.0' + $openvpn_unlimited_udp_cidr = '21' + + if !$::ec2_instance_id { + $openvpn_allow_limited = $openvpn['allow_limited'] + $openvpn_limited_prefix = $openvpn['limited_prefix'] + $openvpn_rate_limit = $openvpn['rate_limit'] + $openvpn_limited_tcp_network_prefix = '10.43.0' + $openvpn_limited_tcp_netmask = '255.255.248.0' + $openvpn_limited_tcp_cidr = '21' + $openvpn_limited_udp_network_prefix = '10.44.0' + $openvpn_limited_udp_netmask = '255.255.248.0' + $openvpn_limited_udp_cidr = '21' + } + + # find out the netmask in cidr format of the primary IF + # thx to https://blog.kumina.nl/tag/puppet-tips-and-tricks/ + # we can do this using an inline_template: + $factname_primary_netmask = "netmask_cidr_${::site_config::params::interface}" + $primary_netmask = inline_template('<%= scope.lookupvar(@factname_primary_netmask) %>') + + # deploy dh keys + include site_openvpn::dh_key + + if $openvpn_allow_unlimited and $openvpn_allow_limited { + $unlimited_gateway_address = $openvpn_gateway_address + $limited_gateway_address = $openvpn_second_gateway_address + } elsif $openvpn_allow_unlimited { + $unlimited_gateway_address = $openvpn_gateway_address + $limited_gateway_address = undef + } elsif $openvpn_allow_limited { + $unlimited_gateway_address = undef + $limited_gateway_address = $openvpn_gateway_address + } + + if $openvpn_allow_unlimited { + site_openvpn::server_config { 'tcp_config': + port => '1194', + proto => 'tcp', + local => $unlimited_gateway_address, + tls_remote => "\"${openvpn_unlimited_prefix}\"", + server => "${openvpn_unlimited_tcp_network_prefix}.0 ${openvpn_unlimited_tcp_netmask}", + push => "\"dhcp-option DNS ${openvpn_unlimited_tcp_network_prefix}.1\"", + management => '127.0.0.1 1000', + config => $openvpn_config + } + site_openvpn::server_config { 'udp_config': + port => '1194', + proto => 'udp', + local => $unlimited_gateway_address, + tls_remote => "\"${openvpn_unlimited_prefix}\"", + server => "${openvpn_unlimited_udp_network_prefix}.0 ${openvpn_unlimited_udp_netmask}", + push => "\"dhcp-option DNS ${openvpn_unlimited_udp_network_prefix}.1\"", + management => '127.0.0.1 1001', + config => $openvpn_config + } + } else { + tidy { '/etc/openvpn/tcp_config.conf': } + tidy { '/etc/openvpn/udp_config.conf': } + } + + if $openvpn_allow_limited { + site_openvpn::server_config { 'limited_tcp_config': + port => '1194', + proto => 'tcp', + local => $limited_gateway_address, + tls_remote => "\"${openvpn_limited_prefix}\"", + server => "${openvpn_limited_tcp_network_prefix}.0 ${openvpn_limited_tcp_netmask}", + push => "\"dhcp-option DNS ${openvpn_limited_tcp_network_prefix}.1\"", + management => '127.0.0.1 1002', + config => $openvpn_config + } + site_openvpn::server_config { 'limited_udp_config': + port => '1194', + proto => 'udp', + local => $limited_gateway_address, + tls_remote => "\"${openvpn_limited_prefix}\"", + server => "${openvpn_limited_udp_network_prefix}.0 ${openvpn_limited_udp_netmask}", + push => "\"dhcp-option DNS ${openvpn_limited_udp_network_prefix}.1\"", + management => '127.0.0.1 1003', + config => $openvpn_config + } + } else { + tidy { '/etc/openvpn/limited_tcp_config.conf': } + tidy { '/etc/openvpn/limited_udp_config.conf': } + } + + file { + '/usr/local/bin/add_gateway_ips.sh': + content => template('site_openvpn/add_gateway_ips.sh.erb'), + mode => '0755'; + } + + exec { '/usr/local/bin/add_gateway_ips.sh': + subscribe => File['/usr/local/bin/add_gateway_ips.sh'], + } + + exec { 'restart_openvpn': + command => '/etc/init.d/openvpn restart', + refreshonly => true, + subscribe => [ + File['/etc/openvpn'], + Class['Site_config::X509::Key'], + Class['Site_config::X509::Cert'], + Class['Site_config::X509::Ca_bundle'] ], + require => [ + Package['openvpn'], + File['/etc/openvpn'], + Class['Site_config::X509::Key'], + Class['Site_config::X509::Cert'], + Class['Site_config::X509::Ca_bundle'] ]; + } + + cron { 'add_gateway_ips.sh': + command => '/usr/local/bin/add_gateway_ips.sh', + user => 'root', + special => 'reboot', + } + + # setup the resolver to listen on the vpn IP + include site_openvpn::resolver + + include site_shorewall::eip + + package { + 'openvpn': ensure => latest + } + + service { + 'openvpn': + ensure => running, + hasrestart => true, + hasstatus => true, + require => [ + Package['openvpn'], + Exec['concat_/etc/default/openvpn'] ]; + } + + file { + '/etc/openvpn': + ensure => directory, + notify => Exec['restart_openvpn'], + require => Package['openvpn']; + } + + file { + '/etc/openvpn/keys': + ensure => directory, + require => Package['openvpn']; + } + + concat { + '/etc/default/openvpn': + owner => root, + group => root, + mode => 644, + warn => true, + notify => Service['openvpn']; + } + + concat::fragment { + 'openvpn.default.header': + content => template('openvpn/etc-default-openvpn.erb'), + target => '/etc/default/openvpn', + order => 01; + } + + concat::fragment { + "openvpn.default.autostart.${name}": + content => 'AUTOSTART=all', + target => '/etc/default/openvpn', + order => 10; + } + + leap::logfile { 'openvpn_tcp': } + leap::logfile { 'openvpn_udp': } + + # Because we currently do not support ipv6 and instead block it (so no leaks + # happen), we get a large number of these messages, so we ignore them (#6540) + rsyslog::snippet { '01-ignore_icmpv6_send': + content => ':msg, contains, "icmpv6_send: no reply to icmp error" ~' + } + + include site_check_mk::agent::openvpn + +} diff --git a/puppet/modules/site_openvpn/manifests/resolver.pp b/puppet/modules/site_openvpn/manifests/resolver.pp new file mode 100644 index 00000000..cea0153a --- /dev/null +++ b/puppet/modules/site_openvpn/manifests/resolver.pp @@ -0,0 +1,50 @@ +class site_openvpn::resolver { + + if $site_openvpn::openvpn_allow_unlimited { + $ensure_unlimited = 'present' + file { + '/etc/unbound/unbound.conf.d/vpn_unlimited_udp_resolver.conf': + content => "server:\n\tinterface: ${site_openvpn::openvpn_unlimited_udp_network_prefix}.1\n\taccess-control: ${site_openvpn::openvpn_unlimited_udp_network_prefix}.0/${site_openvpn::openvpn_unlimited_udp_cidr} allow\n", + owner => root, + group => root, + mode => '0644', + require => [ Class['site_config::caching_resolver'], Service['openvpn'] ], + notify => Service['unbound']; + '/etc/unbound/unbound.conf.d/vpn_unlimited_tcp_resolver.conf': + content => "server:\n\tinterface: ${site_openvpn::openvpn_unlimited_tcp_network_prefix}.1\n\taccess-control: ${site_openvpn::openvpn_unlimited_tcp_network_prefix}.0/${site_openvpn::openvpn_unlimited_tcp_cidr} allow\n", + owner => root, + group => root, + mode => '0644', + require => [ Class['site_config::caching_resolver'], Service['openvpn'] ], + notify => Service['unbound']; + } + } else { + $ensure_unlimited = 'absent' + tidy { '/etc/unbound/unbound.conf.d/vpn_unlimited_udp_resolver.conf': } + tidy { '/etc/unbound/unbound.conf.d/vpn_unlimited_tcp_resolver.conf': } + } + + if $site_openvpn::openvpn_allow_limited { + $ensure_limited = 'present' + file { + '/etc/unbound/unbound.conf.d/vpn_limited_udp_resolver.conf': + content => "server:\n\tinterface: ${site_openvpn::openvpn_limited_udp_network_prefix}.1\n\taccess-control: ${site_openvpn::openvpn_limited_udp_network_prefix}.0/${site_openvpn::openvpn_limited_udp_cidr} allow\n", + owner => root, + group => root, + mode => '0644', + require => [ Class['site_config::caching_resolver'], Service['openvpn'] ], + notify => Service['unbound']; + '/etc/unbound/unbound.conf.d/vpn_limited_tcp_resolver.conf': + content => "server\n\tinterface: ${site_openvpn::openvpn_limited_tcp_network_prefix}.1\n\taccess-control: ${site_openvpn::openvpn_limited_tcp_network_prefix}.0/${site_openvpn::openvpn_limited_tcp_cidr} allow\n", + owner => root, + group => root, + mode => '0644', + require => [ Class['site_config::caching_resolver'], Service['openvpn'] ], + notify => Service['unbound']; + } + } else { + $ensure_limited = 'absent' + tidy { '/etc/unbound/unbound.conf.d/vpn_limited_udp_resolver.conf': } + tidy { '/etc/unbound/unbound.conf.d/vpn_limited_tcp_resolver.conf': } + } +} diff --git a/puppet/modules/site_openvpn/manifests/server_config.pp b/puppet/modules/site_openvpn/manifests/server_config.pp new file mode 100644 index 00000000..15e6fb38 --- /dev/null +++ b/puppet/modules/site_openvpn/manifests/server_config.pp @@ -0,0 +1,228 @@ +# +# Cipher discussion +# ================================ +# +# We want to specify explicit values for the crypto options to prevent a MiTM from forcing +# a weaker cipher. These should be set in both the server and the client ('auth' and 'cipher' +# MUST be the same on both ends or no data will get transmitted). +# +# tls-cipher DHE-RSA-AES128-SHA +# +# dkg: For the TLS control channel, we want to make sure we choose a +# key exchange mechanism that has PFS (meaning probably some form of ephemeral +# Diffie-Hellman key exchange), and that uses a standard, well-tested cipher +# (I recommend AES, and 128 bits is probably fine, since there are some known +# weaknesses in the 192- and 256-bit key schedules). That leaves us with the +# choice of public key algorithms: /usr/sbin/openvpn --show-tls | grep DHE | +# grep AES128 | grep GCM. +# +# elijah: +# I could not get any of these working: +# * openvpn --show-tls | grep GCM +# * openvpn --show-tls | grep DHE | grep AES128 | grep SHA256 +# so, i went with this: +# * openvpn --show-tls | grep DHE | grep AES128 | grep -v SHA256 | grep -v GCM +# Also, i couldn't get any of the elliptical curve algorithms to work. Not sure how +# our cert generation interacts with the tls-cipher algorithms. +# +# note: in my tests, DHE-RSA-AES256-SHA is the one it negotiates if no value is set. +# +# auth SHA1 +# +# dkg: For HMAC digest to authenticate packets, we just want SHA256. OpenVPN lists +# a number of "digest" with names like "RSA-SHA256", but this are legacy and +# should be avoided. +# +# elijah: i am not so sure that the digest algo matters for 'auth' option, because +# i think an attacker would have to forge the digest in real time, which is still far from +# a possibility for SHA1. So, i am leaving the default for now (SHA1). +# +# cipher AES-128-CBC +# +# dkg: For the choice of cipher, we need to select an algorithm and a +# cipher mode. OpenVPN defaults to Blowfish, which is a fine algorithm - but +# our control channel is already relying on AES not being broken; if the +# control channel is cracked, then the key material for the tunnel is exposed, +# and the choice of algorithm is moot. So it makes more sense to me to rely on +# the same cipher here: AES128. As for the cipher mode, OFB seems cleaner to +# me, but CBC is more well-tested, and the OpenVPN man page (at least as of +# version 2.2.1) says "CBC is recommended and CFB and OFB should be considered +# advanced modes." +# +# note: the default is BF-CBC (blowfish) +# + +define site_openvpn::server_config( + $port, $proto, $local, $server, $push, + $management, $config, $tls_remote = undef) { + + $openvpn_configname = $name + $shortname = regsubst(regsubst($name, '_config', ''), '_', '-') + $openvpn_status_filename = "/var/run/openvpn-status-${shortname}" + + concat { + "/etc/openvpn/${openvpn_configname}.conf": + owner => root, + group => root, + mode => 644, + warn => true, + require => File['/etc/openvpn'], + before => Service['openvpn'], + notify => Exec['restart_openvpn']; + } + + if $tls_remote != undef { + openvpn::option { + "tls-remote ${openvpn_configname}": + key => 'tls-remote', + value => $tls_remote, + server => $openvpn_configname; + } + } + + # according to openvpn man page: tcp-nodelay is a "generally a good latency optimization". + if $proto == 'tcp' { + openvpn::option { + "tcp-nodelay ${openvpn_configname}": + key => 'tcp-nodelay', + server => $openvpn_configname; + } + } elsif $proto == 'udp' { + if $config['fragment'] != 1500 { + openvpn::option { + "fragment ${openvpn_configname}": + key => 'fragment', + value => $config['fragment'], + server => $openvpn_configname; + "mssfix ${openvpn_configname}": + key => 'mssfix', + server => $openvpn_configname; + } + } + } + + openvpn::option { + "ca ${openvpn_configname}": + key => 'ca', + value => "${x509::variables::local_CAs}/${site_config::params::ca_bundle_name}.crt", + server => $openvpn_configname; + "cert ${openvpn_configname}": + key => 'cert', + value => "${x509::variables::certs}/${site_config::params::cert_name}.crt", + server => $openvpn_configname; + "key ${openvpn_configname}": + key => 'key', + value => "${x509::variables::keys}/${site_config::params::cert_name}.key", + server => $openvpn_configname; + "dh ${openvpn_configname}": + key => 'dh', + value => '/etc/openvpn/keys/dh.pem', + server => $openvpn_configname; + "tls-cipher ${openvpn_configname}": + key => 'tls-cipher', + value => $config['tls-cipher'], + server => $openvpn_configname; + "auth ${openvpn_configname}": + key => 'auth', + value => $config['auth'], + server => $openvpn_configname; + "cipher ${openvpn_configname}": + key => 'cipher', + value => $config['cipher'], + server => $openvpn_configname; + "dev ${openvpn_configname}": + key => 'dev', + value => 'tun', + server => $openvpn_configname; + "tun-ipv6 ${openvpn_configname}": + key => 'tun-ipv6', + server => $openvpn_configname; + "duplicate-cn ${openvpn_configname}": + key => 'duplicate-cn', + server => $openvpn_configname; + "keepalive ${openvpn_configname}": + key => 'keepalive', + value => $config['keepalive'], + server => $openvpn_configname; + "local ${openvpn_configname}": + key => 'local', + value => $local, + server => $openvpn_configname; + "mute ${openvpn_configname}": + key => 'mute', + value => '5', + server => $openvpn_configname; + "mute-replay-warnings ${openvpn_configname}": + key => 'mute-replay-warnings', + server => $openvpn_configname; + "management ${openvpn_configname}": + key => 'management', + value => $management, + server => $openvpn_configname; + "proto ${openvpn_configname}": + key => 'proto', + value => $proto, + server => $openvpn_configname; + "push1 ${openvpn_configname}": + key => 'push', + value => $push, + server => $openvpn_configname; + "push2 ${openvpn_configname}": + key => 'push', + value => '"redirect-gateway def1"', + server => $openvpn_configname; + "push-ipv6 ${openvpn_configname}": + key => 'push', + value => '"route-ipv6 2000::/3"', + server => $openvpn_configname; + "script-security ${openvpn_configname}": + key => 'script-security', + value => '1', + server => $openvpn_configname; + "server ${openvpn_configname}": + key => 'server', + value => $server, + server => $openvpn_configname; + "server-ipv6 ${openvpn_configname}": + key => 'server-ipv6', + value => '2001:db8:123::/64', + server => $openvpn_configname; + "status ${openvpn_configname}": + key => 'status', + value => "${openvpn_status_filename} 10", + server => $openvpn_configname; + "status-version ${openvpn_configname}": + key => 'status-version', + value => '3', + server => $openvpn_configname; + "topology ${openvpn_configname}": + key => 'topology', + value => 'subnet', + server => $openvpn_configname; + "verb ${openvpn_configname}": + key => 'verb', + value => '3', + server => $openvpn_configname; + "log-append /var/log/leap/openvpn_${proto}.log": + key => 'log-append', + value => "/var/log/leap/openvpn_${proto}.log", + server => $openvpn_configname; + } + + # register openvpn services at systemd on nodes newer than wheezy + # see https://leap.se/code/issues/7798 + case $::operatingsystemrelease { + /^7.*/: { } + default: { + exec { "enable_systemd_${openvpn_configname}": + refreshonly => true, + command => "/bin/systemctl enable openvpn@${openvpn_configname}", + subscribe => File["/etc/openvpn/${openvpn_configname}.conf"], + notify => Service["openvpn@${openvpn_configname}"]; + } + service { "openvpn@${openvpn_configname}": + ensure => running + } + } + } +} diff --git a/puppet/modules/site_openvpn/templates/add_gateway_ips.sh.erb b/puppet/modules/site_openvpn/templates/add_gateway_ips.sh.erb new file mode 100644 index 00000000..e76b756b --- /dev/null +++ b/puppet/modules/site_openvpn/templates/add_gateway_ips.sh.erb @@ -0,0 +1,11 @@ +#!/bin/sh + +ip addr show dev <%= scope.lookupvar('site_config::params::interface') %> | grep -q <%= @openvpn_gateway_address %>/<%= @primary_netmask %> || + ip addr add <%= @openvpn_gateway_address %>/<%= @primary_netmask %> dev <%= scope.lookupvar('site_config::params::interface') %> + +<% if @openvpn_second_gateway_address %> +ip addr show dev <%= scope.lookupvar('site_config::params::interface') %> | grep -q <%= @openvpn_second_gateway_address %>/<%= @primary_netmask %> || + ip addr add <%= @openvpn_second_gateway_address %>/<%= @primary_netmask %> dev <%= scope.lookupvar('site_config::params::interface') %> +<% end %> + +/bin/echo 1 > /proc/sys/net/ipv4/ip_forward |