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  | 
