diff options
| author | Micah <micah@leap.se> | 2016-07-12 16:45:26 -0400 | 
|---|---|---|
| committer | Micah <micah@leap.se> | 2016-07-12 16:45:26 -0400 | 
| commit | da37dd95c39f3f100020164473eed53a317fb53f (patch) | |
| tree | ce78d01244f9e67bc068475c07d54493456b2201 | |
| parent | 7ce3190986cf8e5fe037a7ccd4c1076505b117f4 (diff) | |
git subrepo clone https://leap.se/git/puppet_openvpn puppet/modules/openvpn
subrepo:
  subdir:   "puppet/modules/openvpn"
  merged:   "26d4edc"
upstream:
  origin:   "https://leap.se/git/puppet_openvpn"
  branch:   "master"
  commit:   "26d4edc"
git-subrepo:
  version:  "0.3.0"
  origin:   "https://github.com/ingydotnet/git-subrepo"
  commit:   "1e79595"
Change-Id: I596766ccfb806b3ca2d1c755c4e24c5ad3d997f9
35 files changed, 1661 insertions, 0 deletions
| diff --git a/puppet/modules/openvpn/.fixtures.yml b/puppet/modules/openvpn/.fixtures.yml new file mode 100644 index 00000000..1125ecca --- /dev/null +++ b/puppet/modules/openvpn/.fixtures.yml @@ -0,0 +1,6 @@ +fixtures: +  repositories: +    concat: git://github.com/ripienaar/puppet-concat.git +  symlinks: +    openvpn: "#{source_dir}" + diff --git a/puppet/modules/openvpn/.gitignore b/puppet/modules/openvpn/.gitignore new file mode 100644 index 00000000..6fd248b3 --- /dev/null +++ b/puppet/modules/openvpn/.gitignore @@ -0,0 +1,3 @@ +pkg +spec/fixtures +.vagrant diff --git a/puppet/modules/openvpn/.gitrepo b/puppet/modules/openvpn/.gitrepo new file mode 100644 index 00000000..0c191cd8 --- /dev/null +++ b/puppet/modules/openvpn/.gitrepo @@ -0,0 +1,11 @@ +; DO NOT EDIT (unless you know what you are doing) +; +; This subdirectory is a git "subrepo", and this file is maintained by the +; git-subrepo command. See https://github.com/git-commands/git-subrepo#readme +; +[subrepo] +	remote = https://leap.se/git/puppet_openvpn +	branch = master +	commit = 26d4edc669853a268a65d2cbbfb42c19f1333de7 +	parent = 7ce3190986cf8e5fe037a7ccd4c1076505b117f4 +	cmdver = 0.3.0 diff --git a/puppet/modules/openvpn/.rvmrc b/puppet/modules/openvpn/.rvmrc new file mode 100644 index 00000000..6fbfb7f1 --- /dev/null +++ b/puppet/modules/openvpn/.rvmrc @@ -0,0 +1,38 @@ +#!/usr/bin/env bash + +# This is an RVM Project .rvmrc file, used to automatically load the ruby +# development environment upon cd'ing into the directory + +# First we specify our desired <ruby>[@<gemset>], the @gemset name is optional, +# Only full ruby name is supported here, for short names use: +#     echo "rvm use 1.9.3" > .rvmrc +environment_id="ruby-1.9.3-p194@puppet" + +# Uncomment the following lines if you want to verify rvm version per project +# rvmrc_rvm_version="1.15.8 (stable)" # 1.10.1 seams as a safe start +# eval "$(echo ${rvm_version}.${rvmrc_rvm_version} | awk -F. '{print "[[ "$1*65536+$2*256+$3" -ge "$4*65536+$5*256+$6" ]]"}' )" || { +#   echo "This .rvmrc file requires at least RVM ${rvmrc_rvm_version}, aborting loading." +#   return 1 +# } + +# First we attempt to load the desired environment directly from the environment +# file. This is very fast and efficient compared to running through the entire +# CLI and selector. If you want feedback on which environment was used then +# insert the word 'use' after --create as this triggers verbose mode. +if [[ -d "${rvm_path:-$HOME/.rvm}/environments" +  && -s "${rvm_path:-$HOME/.rvm}/environments/$environment_id" ]] +then +  \. "${rvm_path:-$HOME/.rvm}/environments/$environment_id" +  [[ -s "${rvm_path:-$HOME/.rvm}/hooks/after_use" ]] && +    \. "${rvm_path:-$HOME/.rvm}/hooks/after_use" || true +  if [[ $- == *i* ]] # check for interactive shells +  then echo "Using: $(tput setaf 2)$GEM_HOME$(tput sgr0)" # show the user the ruby and gemset they are using in green +  else echo "Using: $GEM_HOME" # don't use colors in non-interactive shells +  fi +else +  # If the environment file has not yet been created, use the RVM CLI to select. +  rvm --create use  "$environment_id" || { +    echo "Failed to create RVM environment '${environment_id}'." +    return 1 +  } +fi diff --git a/puppet/modules/openvpn/.travis.yml b/puppet/modules/openvpn/.travis.yml new file mode 100644 index 00000000..da5c389d --- /dev/null +++ b/puppet/modules/openvpn/.travis.yml @@ -0,0 +1,29 @@ +language: ruby +bundler_args: --without development +script: "bundle exec rake spec SPEC_OPTS='--format documentation'" +rvm: +  - 1.8.7 +  - 1.9.3 +  - 2.0.0 +script: +  - "rake lint" +  - "rake spec SPEC_OPTS='--format documentation'" +env: +  - PUPPET_VERSION="~> 2.7.0" +  - PUPPET_VERSION="~> 3.0.0" +  - PUPPET_VERSION="~> 3.1.0" +  - PUPPET_VERSION="~> 3.2.0" +matrix: +  exclude: +    - rvm: 1.9.3 +      env: PUPPET_VERSION="~> 2.7.0" +    - rvm: 2.0.0 +      env: PUPPET_VERSION="~> 2.7.0" +    - rvm: 2.0.0 +      env: PUPPET_VERSION="~> 3.0.0" +    - rvm: 2.0.0 +      env: PUPPET_VERSION="~> 3.1.0" +notifications: +  email: false +  on_success: always +  on_failure: always diff --git a/puppet/modules/openvpn/Gemfile b/puppet/modules/openvpn/Gemfile new file mode 100644 index 00000000..68e10e7d --- /dev/null +++ b/puppet/modules/openvpn/Gemfile @@ -0,0 +1,7 @@ +source :rubygems + +puppetversion = ENV['PUPPET_VERSION'] +gem 'puppet', puppetversion, :require => false +gem 'puppet-lint' +gem 'rspec-puppet' +gem 'puppetlabs_spec_helper' diff --git a/puppet/modules/openvpn/Gemfile.lock b/puppet/modules/openvpn/Gemfile.lock new file mode 100644 index 00000000..9fce3f98 --- /dev/null +++ b/puppet/modules/openvpn/Gemfile.lock @@ -0,0 +1,36 @@ +GEM +  remote: http://rubygems.org/ +  specs: +    diff-lcs (1.1.3) +    facter (1.6.17) +    hiera (1.0.0) +    metaclass (0.0.1) +    mocha (0.13.1) +      metaclass (~> 0.0.1) +    puppet (3.0.2) +      facter (~> 1.6.11) +      hiera (~> 1.0.0) +    puppetlabs_spec_helper (0.4.0) +      mocha (>= 0.10.5) +      rake +      rspec (>= 2.9.0) +      rspec-puppet (>= 0.1.1) +    rake (10.0.3) +    rspec (2.12.0) +      rspec-core (~> 2.12.0) +      rspec-expectations (~> 2.12.0) +      rspec-mocks (~> 2.12.0) +    rspec-core (2.12.2) +    rspec-expectations (2.12.1) +      diff-lcs (~> 1.1.3) +    rspec-mocks (2.12.1) +    rspec-puppet (0.1.5) +      rspec + +PLATFORMS +  ruby + +DEPENDENCIES +  puppet +  puppetlabs_spec_helper +  rspec-puppet diff --git a/puppet/modules/openvpn/LICENSE b/puppet/modules/openvpn/LICENSE new file mode 100644 index 00000000..f433b1a5 --- /dev/null +++ b/puppet/modules/openvpn/LICENSE @@ -0,0 +1,177 @@ + +                                 Apache License +                           Version 2.0, January 2004 +                        http://www.apache.org/licenses/ + +   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +   1. Definitions. + +      "License" shall mean the terms and conditions for use, reproduction, +      and distribution as defined by Sections 1 through 9 of this document. + +      "Licensor" shall mean the copyright owner or entity authorized by +      the copyright owner that is granting the License. + +      "Legal Entity" shall mean the union of the acting entity and all +      other entities that control, are controlled by, or are under common +      control with that entity. For the purposes of this definition, +      "control" means (i) the power, direct or indirect, to cause the +      direction or management of such entity, whether by contract or +      otherwise, or (ii) ownership of fifty percent (50%) or more of the +      outstanding shares, or (iii) beneficial ownership of such entity. + +      "You" (or "Your") shall mean an individual or Legal Entity +      exercising permissions granted by this License. + +      "Source" form shall mean the preferred form for making modifications, +      including but not limited to software source code, documentation +      source, and configuration files. + +      "Object" form shall mean any form resulting from mechanical +      transformation or translation of a Source form, including but +      not limited to compiled object code, generated documentation, +      and conversions to other media types. + +      "Work" shall mean the work of authorship, whether in Source or +      Object form, made available under the License, as indicated by a +      copyright notice that is included in or attached to the work +      (an example is provided in the Appendix below). + +      "Derivative Works" shall mean any work, whether in Source or Object +      form, that is based on (or derived from) the Work and for which the +      editorial revisions, annotations, elaborations, or other modifications +      represent, as a whole, an original work of authorship. For the purposes +      of this License, Derivative Works shall not include works that remain +      separable from, or merely link (or bind by name) to the interfaces of, +      the Work and Derivative Works thereof. + +      "Contribution" shall mean any work of authorship, including +      the original version of the Work and any modifications or additions +      to that Work or Derivative Works thereof, that is intentionally +      submitted to Licensor for inclusion in the Work by the copyright owner +      or by an individual or Legal Entity authorized to submit on behalf of +      the copyright owner. For the purposes of this definition, "submitted" +      means any form of electronic, verbal, or written communication sent +      to the Licensor or its representatives, including but not limited to +      communication on electronic mailing lists, source code control systems, +      and issue tracking systems that are managed by, or on behalf of, the +      Licensor for the purpose of discussing and improving the Work, but +      excluding communication that is conspicuously marked or otherwise +      designated in writing by the copyright owner as "Not a Contribution." + +      "Contributor" shall mean Licensor and any individual or Legal Entity +      on behalf of whom a Contribution has been received by Licensor and +      subsequently incorporated within the Work. + +   2. Grant of Copyright License. Subject to the terms and conditions of +      this License, each Contributor hereby grants to You a perpetual, +      worldwide, non-exclusive, no-charge, royalty-free, irrevocable +      copyright license to reproduce, prepare Derivative Works of, +      publicly display, publicly perform, sublicense, and distribute the +      Work and such Derivative Works in Source or Object form. + +   3. Grant of Patent License. Subject to the terms and conditions of +      this License, each Contributor hereby grants to You a perpetual, +      worldwide, non-exclusive, no-charge, royalty-free, irrevocable +      (except as stated in this section) patent license to make, have made, +      use, offer to sell, sell, import, and otherwise transfer the Work, +      where such license applies only to those patent claims licensable +      by such Contributor that are necessarily infringed by their +      Contribution(s) alone or by combination of their Contribution(s) +      with the Work to which such Contribution(s) was submitted. If You +      institute patent litigation against any entity (including a +      cross-claim or counterclaim in a lawsuit) alleging that the Work +      or a Contribution incorporated within the Work constitutes direct +      or contributory patent infringement, then any patent licenses +      granted to You under this License for that Work shall terminate +      as of the date such litigation is filed. + +   4. Redistribution. You may reproduce and distribute copies of the +      Work or Derivative Works thereof in any medium, with or without +      modifications, and in Source or Object form, provided that You +      meet the following conditions: + +      (a) You must give any other recipients of the Work or +          Derivative Works a copy of this License; and + +      (b) You must cause any modified files to carry prominent notices +          stating that You changed the files; and + +      (c) You must retain, in the Source form of any Derivative Works +          that You distribute, all copyright, patent, trademark, and +          attribution notices from the Source form of the Work, +          excluding those notices that do not pertain to any part of +          the Derivative Works; and + +      (d) If the Work includes a "NOTICE" text file as part of its +          distribution, then any Derivative Works that You distribute must +          include a readable copy of the attribution notices contained +          within such NOTICE file, excluding those notices that do not +          pertain to any part of the Derivative Works, in at least one +          of the following places: within a NOTICE text file distributed +          as part of the Derivative Works; within the Source form or +          documentation, if provided along with the Derivative Works; or, +          within a display generated by the Derivative Works, if and +          wherever such third-party notices normally appear. The contents +          of the NOTICE file are for informational purposes only and +          do not modify the License. You may add Your own attribution +          notices within Derivative Works that You distribute, alongside +          or as an addendum to the NOTICE text from the Work, provided +          that such additional attribution notices cannot be construed +          as modifying the License. + +      You may add Your own copyright statement to Your modifications and +      may provide additional or different license terms and conditions +      for use, reproduction, or distribution of Your modifications, or +      for any such Derivative Works as a whole, provided Your use, +      reproduction, and distribution of the Work otherwise complies with +      the conditions stated in this License. + +   5. Submission of Contributions. Unless You explicitly state otherwise, +      any Contribution intentionally submitted for inclusion in the Work +      by You to the Licensor shall be under the terms and conditions of +      this License, without any additional terms or conditions. +      Notwithstanding the above, nothing herein shall supersede or modify +      the terms of any separate license agreement you may have executed +      with Licensor regarding such Contributions. + +   6. Trademarks. This License does not grant permission to use the trade +      names, trademarks, service marks, or product names of the Licensor, +      except as required for reasonable and customary use in describing the +      origin of the Work and reproducing the content of the NOTICE file. + +   7. Disclaimer of Warranty. Unless required by applicable law or +      agreed to in writing, Licensor provides the Work (and each +      Contributor provides its Contributions) on an "AS IS" BASIS, +      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +      implied, including, without limitation, any warranties or conditions +      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A +      PARTICULAR PURPOSE. You are solely responsible for determining the +      appropriateness of using or redistributing the Work and assume any +      risks associated with Your exercise of permissions under this License. + +   8. Limitation of Liability. In no event and under no legal theory, +      whether in tort (including negligence), contract, or otherwise, +      unless required by applicable law (such as deliberate and grossly +      negligent acts) or agreed to in writing, shall any Contributor be +      liable to You for damages, including any direct, indirect, special, +      incidental, or consequential damages of any character arising as a +      result of this License or out of the use or inability to use the +      Work (including but not limited to damages for loss of goodwill, +      work stoppage, computer failure or malfunction, or any and all +      other commercial damages or losses), even if such Contributor +      has been advised of the possibility of such damages. + +   9. Accepting Warranty or Additional Liability. While redistributing +      the Work or Derivative Works thereof, You may choose to offer, +      and charge a fee for, acceptance of support, warranty, indemnity, +      or other liability obligations and/or rights consistent with this +      License. However, in accepting such obligations, You may act only +      on Your own behalf and on Your sole responsibility, not on behalf +      of any other Contributor, and only if You agree to indemnify, +      defend, and hold each Contributor harmless for any liability +      incurred by, or claims asserted against, such Contributor by reason +      of your accepting any such warranty or additional liability. + +   END OF TERMS AND CONDITIONS diff --git a/puppet/modules/openvpn/Modulefile b/puppet/modules/openvpn/Modulefile new file mode 100644 index 00000000..679e7e64 --- /dev/null +++ b/puppet/modules/openvpn/Modulefile @@ -0,0 +1,11 @@ +name    'luxflux-openvpn' +version '2.1.0' +source 'https://github.com/luxflux/puppet-openvpn' +author 'luxflux' +license 'Apache 2.0' +summary 'OpenVPN server puppet module' +description 'Puppet module to manage OpenVPN servers' +project_page 'https://github.com/luxflux/puppet-openvpn' + +## Add dependencies, if any: +dependency 'ripienaar/concat', '0.2.0' diff --git a/puppet/modules/openvpn/Rakefile b/puppet/modules/openvpn/Rakefile new file mode 100644 index 00000000..14f1c246 --- /dev/null +++ b/puppet/modules/openvpn/Rakefile @@ -0,0 +1,2 @@ +require 'rubygems' +require 'puppetlabs_spec_helper/rake_tasks' diff --git a/puppet/modules/openvpn/Readme.markdown b/puppet/modules/openvpn/Readme.markdown new file mode 100644 index 00000000..6bcf49ea --- /dev/null +++ b/puppet/modules/openvpn/Readme.markdown @@ -0,0 +1,54 @@ +# OpenVPN Puppet module + +Puppet module to manage OpenVPN servers + +## Features: + +* Client-specific rules and access policies +* Generated client configurations and SSL-Certificates +* Downloadable client configurations and SSL-Certificates for easy client configuration +* Support for multiple server instances + +Tested on Ubuntu Precise Pangolin, CentOS 6, RedHat 6. + + +## Dependencies +  - [puppet-concat](https://github.com/ripienaar/puppet-concat) + + +## Example + +```puppet +  # add a server instance +  openvpn::server { 'winterthur': +    country      => 'CH', +    province     => 'ZH', +    city         => 'Winterthur', +    organization => 'example.org', +    email        => 'root@example.org', +    server       => '10.200.200.0 255.255.255.0' +  } + +  # define clients +  openvpn::client { 'client1': +    server => 'winterthur' +  } +  openvpn::client { 'client2': +    server   => 'winterthur' +  } + +  openvpn::client_specific_config { 'client1': +    server => 'winterthur', +    ifconfig => '10.200.200.50 255.255.255.0' +  } +``` + +Don't forget the [sysctl](https://github.com/luxflux/puppet-sysctl) directive ```net.ipv4.ip_forward```! + + +# Contributors + +These fine folks helped to get this far with this module: +* [@jlambert121](https://github.com/jlambert121) +* [@jlk](https://github.com/jlk) +* [@elisiano](https://github.com/elisiano) diff --git a/puppet/modules/openvpn/Vagrantfile b/puppet/modules/openvpn/Vagrantfile new file mode 100644 index 00000000..88875ff8 --- /dev/null +++ b/puppet/modules/openvpn/Vagrantfile @@ -0,0 +1,42 @@ +# -*- mode: ruby -*- +# vi: set ft=ruby : + +def server_config(config) +  config.vm.provision :puppet, :module_path => '..' do |puppet| +    puppet.manifests_path = "vagrant" +    puppet.manifest_file  = "server.pp" +  end +end + +def client_config(config) +  config.vm.provision :puppet, :module_path => '..' do |puppet| +    puppet.manifests_path = "vagrant" +    puppet.manifest_file  = "client.pp" +  end +end + +Vagrant::Config.run do |config| + +  config.vm.define :server_ubuntu do |c| +    c.vm.box = 'precise64' +    server_config c +    c.vm.network :hostonly, '10.255.255.10' +  end + +  config.vm.define :server_centos do |c| +    c.vm.box = 'centos63' + +    c.vm.provision :shell, :inline => 'if [ ! -f rpmforge-release-0.5.2-2.el6.rf.x86_64.rpm ]; then wget -q http://pkgs.repoforge.org/rpmforge-release/rpmforge-release-0.5.2-2.el6.rf.x86_64.rpm; fi' +    c.vm.provision :shell, :inline => 'yum install -y rpmforge-release-0.5.2-2.el6.rf.x86_64.rpm || exit 0' + +    server_config c +    c.vm.network :hostonly, '10.255.255.11' +  end + +  config.vm.define :client_ubuntu do |c| +    c.vm.box = 'precise64' +    client_config c +    c.vm.network :hostonly, '10.255.255.20' +  end + +end diff --git a/puppet/modules/openvpn/manifests/client.pp b/puppet/modules/openvpn/manifests/client.pp new file mode 100644 index 00000000..92c6aa4e --- /dev/null +++ b/puppet/modules/openvpn/manifests/client.pp @@ -0,0 +1,187 @@ +# == Define: openvpn::client +# +# This define creates the client certs for a specified openvpn server as well +# as creating a tarball that can be directly imported into openvpn clients +# +# +# === Parameters +# +# [*server*] +#   String.  Name of the corresponding openvpn endpoint +#   Required +# +# [*compression*] +#   String.  Which compression algorithim to use +#   Default: comp-lzo +#   Options: comp-lzo or '' (disable compression) +# +# [*dev*] +#   String.  Device method +#   Default: tun +#   Options: tun (routed connections), tap (bridged connections) +# +# [*mute*] +#   Integer.  Set log mute level +#   Default: 20 +# +# [*mute_replay_warnings*] +#   Boolean.  Silence duplicate packet warnings (common on wireless networks) +#   Default: true +# +# [*nobind*] +#   Boolean.  Whether or not to bind to a specific port number +#   Default: true +# +# [*persist_key*] +#   Boolean.  Try to retain access to resources that may be unavailable +#     because of privilege downgrades +#   Default: true +# +# [*persist_tun*] +#   Boolean.  Try to retain access to resources that may be unavailable +#     because of privilege downgrades +#   Default: true +# +# [*port*] +#   Integer.  The port the openvpn server service is running on +#   Default: 1194 +# +# [*proto*] +#   String.  What IP protocol is being used. +#   Default: tcp +#   Options: tcp or udp +# +# [*remote_host*] +#   String.  The IP or hostname of the openvpn server service +#   Default: FQDN +# +# [*resolv_retry*] +#   Integer/String. How many seconds should the openvpn client try to resolve +#     the server's hostname +#   Default: infinite +#   Options: Integer or infinite +# +# [*verb*] +#   Integer.  Level of logging verbosity +#   Default: 3 +# +# +# === Examples +# +#   openvpn::client { +#     'my_user': +#       server      => 'contractors', +#       remote_host => 'vpn.mycompany.com' +#    } +# +# * Removal: +#     Manual process right now, todo for the future +# +# +# === Authors +# +# * Raffael Schmid <mailto:raffael@yux.ch> +# * John Kinsella <mailto:jlkinsel@gmail.com> +# * Justin Lambert <mailto:jlambert@letsevenup.com> +# +# === License +# +# Copyright 2013 Raffael Schmid, <raffael@yux.ch> +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +define openvpn::client( +  $server, +  $compression = 'comp-lzo', +  $dev = 'tun', +  $mute = '20', +  $mute_replay_warnings = true, +  $nobind = true, +  $persist_key = true, +  $persist_tun = true, +  $port = '1194', +  $proto = 'tcp', +  $remote_host = $::fqdn, +  $resolv_retry = 'infinite', +  $verb = '3', +) { + +  Openvpn::Server[$server] -> +  Openvpn::Client[$name] + +  exec { +    "generate certificate for ${name} in context of ${server}": +      command  => ". ./vars && ./pkitool ${name}", +      cwd      => "/etc/openvpn/${server}/easy-rsa", +      creates  => "/etc/openvpn/${server}/easy-rsa/keys/${name}.crt", +      provider => 'shell'; +  } + +  file { +    [ "/etc/openvpn/${server}/download-configs/${name}", +      "/etc/openvpn/${server}/download-configs/${name}/keys"]: +        ensure  => directory; + +    "/etc/openvpn/${server}/download-configs/${name}/keys/${name}.crt": +      ensure  => link, +      target  => "/etc/openvpn/${server}/easy-rsa/keys/${name}.crt", +      require => Exec["generate certificate for ${name} in context of ${server}"]; + +    "/etc/openvpn/${server}/download-configs/${name}/keys/${name}.key": +      ensure  => link, +      target  => "/etc/openvpn/${server}/easy-rsa/keys/${name}.key", +      require => Exec["generate certificate for ${name} in context of ${server}"]; + +    "/etc/openvpn/${server}/download-configs/${name}/keys/ca.crt": +      ensure  => link, +      target  => "/etc/openvpn/${server}/easy-rsa/keys/ca.crt", +      require => Exec["generate certificate for ${name} in context of ${server}"]; + +    "/etc/openvpn/${server}/download-configs/${name}/${name}.conf": +      owner   => root, +      group   => root, +      mode    => '0444', +      content => template('openvpn/client.erb'), +      notify  => Exec["tar the thing ${server} with ${name}"]; +  } + +  exec { +    "tar the thing ${server} with ${name}": +      cwd         => "/etc/openvpn/${server}/download-configs/", +      command     => "/bin/rm ${name}.tar.gz; tar --exclude=\\*.conf.d -chzvf ${name}.tar.gz ${name}", +      refreshonly => true, +      require     => [  File["/etc/openvpn/${server}/download-configs/${name}/${name}.conf"], +                        File["/etc/openvpn/${server}/download-configs/${name}/keys/ca.crt"], +                        File["/etc/openvpn/${server}/download-configs/${name}/keys/${name}.key"], +                        File["/etc/openvpn/${server}/download-configs/${name}/keys/${name}.crt"] +                      ], +      notify      => Exec["generate ${name}.ovpn in ${server}"]; +  } + +  exec { +    "generate ${name}.ovpn in ${server}": +      cwd         => "/etc/openvpn/${server}/download-configs/", +      command     => "/bin/rm ${name}.ovpn; cat  ${name}/${name}.conf|perl -lne 'if(m|^ca keys/ca.crt|){ chomp(\$ca=`cat ${name}/keys/ca.crt`); print \"<ca>\n\$ca\n</ca>\"} elsif(m|^cert keys/${name}.crt|) { chomp(\$crt=`cat ${name}/keys/${name}.crt`); print \"<cert>\n\$crt\n</cert>\"} elsif(m|^key keys/${name}.key|){ chomp(\$key=`cat ${name}/keys/${name}.key`); print \"<key>\n\$key\n</key>\"} else { print} ' > ${name}.ovpn", +      refreshonly => true, +      require     => [  File["/etc/openvpn/${server}/download-configs/${name}/${name}.conf"], +                        File["/etc/openvpn/${server}/download-configs/${name}/keys/ca.crt"], +                        File["/etc/openvpn/${server}/download-configs/${name}/keys/${name}.key"], +                        File["/etc/openvpn/${server}/download-configs/${name}/keys/${name}.crt"], +                      ], +  } + +  file { "/etc/openvpn/${server}/download-configs/${name}.ovpn": +    mode    => '0400', +    require => Exec["generate ${name}.ovpn in ${server}"], +  } +} diff --git a/puppet/modules/openvpn/manifests/client_specific_config.pp b/puppet/modules/openvpn/manifests/client_specific_config.pp new file mode 100644 index 00000000..4287421a --- /dev/null +++ b/puppet/modules/openvpn/manifests/client_specific_config.pp @@ -0,0 +1,79 @@ +# == Define: openvpn::client_specific_config +# +# This define configures options which will be pushed by the server to a +# specific client only. This feature is explained here: +#  http://openvpn.net/index.php/open-source/documentation/howto.html#policy +# +# === Parameters +# +# All the parameters are explained in the openvpn documentation: +#   http://openvpn.net/index.php/open-source/documentation/howto.html#policy +# +# [*server*] +#   String.  Name of the corresponding openvpn endpoint +#   Required +# +# [*iroute*] +#   Array.  Array of iroute combinations. +#   Default: [] +# +# [*ifconfig*] +#   String.  IP configuration to push to the client. +#   Default: false +# +# [*dhcp_options] +#   Array.  DHCP options to push to the client. +#   Default: [] +# +# +# === Examples +# +#   openvpn::client_specific_config { +#     'vpn_client': +#       server       => 'contractors', +#       iroute       => ['10.0.1.0 255.255.255.0'], +#       ifconfig     => '10.10.10.1 10.10.10.2', +#       dhcp_options => ['DNS 8.8.8.8'] +#    } +# +# * Removal: +#     Manual process right now, todo for the future +# +# +# === Authors +# +# * Raffael Schmid <mailto:raffael@yux.ch> +# +# === License +# +# Copyright 2013 Raffael Schmid, <raffael@yux.ch> +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +define openvpn::client_specific_config( +  $server, +  $iroute       = [], +  $ifconfig     = false, +  $dhcp_options = [] +) { + +  Openvpn::Server[$server] -> +  Openvpn::Client[$name] -> +  Openvpn::Client_specific_config[$name] + +  file { "/etc/openvpn/${server}/client-configs/${name}": +    ensure  => present, +    content => template('openvpn/client_specific_config.erb') +  } + +} diff --git a/puppet/modules/openvpn/manifests/config.pp b/puppet/modules/openvpn/manifests/config.pp new file mode 100644 index 00000000..32b32094 --- /dev/null +++ b/puppet/modules/openvpn/manifests/config.pp @@ -0,0 +1,52 @@ +# == Class: openvpn::config +# +# This class sets up the openvpn enviornment as well as the default config file +# +# +# === Examples +# +# This class should not be directly invoked +# +# === Authors +# +# * Raffael Schmid <mailto:raffael@yux.ch> +# * John Kinsella <mailto:jlkinsel@gmail.com> +# * Justin Lambert <mailto:jlambert@letsevenup.com> +# +# === License +# +# Copyright 2013 Raffael Schmid, <raffael@yux.ch> +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +class openvpn::config { + +  if $::osfamily == 'Debian' { +    include concat::setup + +    concat { +      '/etc/default/openvpn': +        owner  => root, +        group  => root, +        mode   => 644, +        warn   => true; +    } + +    concat::fragment { +      'openvpn.default.header': +        content => template('openvpn/etc-default-openvpn.erb'), +        target  => '/etc/default/openvpn', +        order   => 01; +    } +  } +} diff --git a/puppet/modules/openvpn/manifests/init.pp b/puppet/modules/openvpn/manifests/init.pp new file mode 100644 index 00000000..7e07f025 --- /dev/null +++ b/puppet/modules/openvpn/manifests/init.pp @@ -0,0 +1,43 @@ +# == Class: openvpn +# +# This module installs the openvpn service, configures vpn endpoints, generates +# client certificates, and generates client config files +# +# +# === Examples +# +# * Installation: +#     class { 'openvpn': } +# +# +# === Authors +# +# * Raffael Schmid <mailto:raffael@yux.ch> +# * John Kinsella <mailto:jlkinsel@gmail.com> +# * Justin Lambert <mailto:jlambert@letsevenup.com> +# +# === License +# +# Copyright 2013 Raffael Schmid, <raffael@yux.ch> +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +class openvpn { + +  class {'openvpn::params': } -> +  class {'openvpn::install': } -> +  class {'openvpn::config': } ~> +  class {'openvpn::service': } -> +  Class['openvpn'] + +} diff --git a/puppet/modules/openvpn/manifests/install.pp b/puppet/modules/openvpn/manifests/install.pp new file mode 100644 index 00000000..a230373a --- /dev/null +++ b/puppet/modules/openvpn/manifests/install.pp @@ -0,0 +1,46 @@ +# == Class: openvpn +# +# This module installs the openvpn service, configures vpn endpoints, generates +# client certificates, and generates client config files +# +# +# === Examples +# +# This class should not be directly invoked +# +# +# === Authors +# +# * Raffael Schmid <mailto:raffael@yux.ch> +# * John Kinsella <mailto:jlkinsel@gmail.com> +# * Justin Lambert <mailto:jlambert@letsevenup.com> +# +# === License +# +# Copyright 2013 Raffael Schmid, <raffael@yux.ch> +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +class openvpn::install { + +  package { +    'openvpn': +      ensure => installed; +  } + +  file { +    [ '/etc/openvpn', '/etc/openvpn/keys' ]: +      ensure  => directory, +      require => Package['openvpn']; +  } +} diff --git a/puppet/modules/openvpn/manifests/params.pp b/puppet/modules/openvpn/manifests/params.pp new file mode 100644 index 00000000..33495270 --- /dev/null +++ b/puppet/modules/openvpn/manifests/params.pp @@ -0,0 +1,37 @@ +# === License +# +# Copyright 2013 Raffael Schmid, <raffael@yux.ch> +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +class openvpn::params { + +  $group = $::osfamily ? { +    'RedHat' => 'nobody', +    default  => 'nogroup' +  } + +  $easyrsa_source = $::osfamily ? { +    'RedHat'  => $::operatingsystemmajrelease ? { +      6 => '/usr/share/openvpn/easy-rsa/2.0', +      default => '/usr/share/doc/openvpn-2.2.2/easy-rsa/2.0' +    }, +    default   => '/usr/share/doc/openvpn/examples/easy-rsa/2.0' +  } + +  $link_openssl_cnf = $::osfamily ? { +    /(Debian|RedHat)/ => true, +    default           => false +  } + +} diff --git a/puppet/modules/openvpn/manifests/server.pp b/puppet/modules/openvpn/manifests/server.pp new file mode 100644 index 00000000..649048c4 --- /dev/null +++ b/puppet/modules/openvpn/manifests/server.pp @@ -0,0 +1,233 @@ +# == Define: openvpn::server +# +# This define creates the openvpn server instance and ssl certificates +# +# +# === Parameters +# +# [*country*] +#   String.  Country to be used for the SSL certificate +# +# [*province*] +#   String.  Province to be used for the SSL certificate +# +# [*city*] +#   String.  City to be used for the SSL certificate +# +# [*organization*] +#   String.  Organization to be used for the SSL certificate +# +# [*email*] +#   String.  Email address to be used for the SSL certificate +# +# [*compression*] +#   String.  Which compression algorithim to use +#   Default: comp-lzo +#   Options: comp-lzo or '' (disable compression) +# +# [*dev*] +#   String.  Device method +#   Default: tun +#   Options: tun (routed connections), tap (bridged connections) +# +# [*user*] +#   String.  Group to drop privileges to after startup +#   Default: nobody +# +# [*group*] +#   String.  User to drop privileges to after startup +#   Default: depends on your $::osfamily +# +# [*ipp*] +#   Boolean.  Persist ifconfig information to a file to retain client IP +#     addresses between sessions +#   Default: false +# +# [*local*] +#   String.  Interface for openvpn to bind to. +#   Default: $::ipaddress_eth0 +#   Options: An IP address or '' to bind to all ip addresses +# +# [*logfile*] +#   String.  Logfile for this openvpn server +#   Default: false +#   Options: false (syslog) or log file name +# +# [*port*] +#   Integer.  The port the openvpn server service is running on +#   Default: 1194 +# +# [*proto*] +#   String.  What IP protocol is being used. +#   Default: tcp +#   Options: tcp or udp +# +# [*status_log*] +#   String.  Logfile for periodic dumps of the vpn service status +#   Default: "${name}/openvpn-status.log" +# +# [*server*] +#   String.  Network to assign client addresses out of +#   Default: None.  Required in tun mode, not in tap mode +# +# [*push*] +#   Array.  Options to push out to the client.  This can include routes, DNS +#     servers, DNS search domains, and many other options. +#   Default: [] +# +# +# === Examples +# +#   openvpn::client { +#     'my_user': +#       server      => 'contractors', +#       remote_host => 'vpn.mycompany.com' +#    } +# +# * Removal: +#     Manual process right now, todo for the future +# +# +# === Authors +# +# * Raffael Schmid <mailto:raffael@yux.ch> +# * John Kinsella <mailto:jlkinsel@gmail.com> +# * Justin Lambert <mailto:jlambert@letsevenup.com> +# +# === License +# +# Copyright 2013 Raffael Schmid, <raffael@yux.ch> +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +define openvpn::server( +  $country, +  $province, +  $city, +  $organization, +  $email, +  $compression = 'comp-lzo', +  $dev = 'tun0', +  $user = 'nobody', +  $group = false, +  $ipp = false, +  $ip_pool = [], +  $local = $::ipaddress_eth0, +  $logfile = false, +  $port = '1194', +  $proto = 'tcp', +  $status_log = "${name}/openvpn-status.log", +  $server = '', +  $push = [] +) { + +  include openvpn +  Class['openvpn::install'] -> +  Openvpn::Server[$name] ~> +  Class['openvpn::service'] + +  $tls_server = $proto ? { +    /tcp/   => true, +    default => false +  } + +  $group_to_set = $group ? { +    false   => $openvpn::params::group, +    default => $group +  } + +  file { +    ["/etc/openvpn/${name}", "/etc/openvpn/${name}/client-configs", "/etc/openvpn/${name}/download-configs" ]: +      ensure  => directory; +  } + +  exec { +    "copy easy-rsa to openvpn config folder ${name}": +      command => "/bin/cp -r ${openvpn::params::easyrsa_source} /etc/openvpn/${name}/easy-rsa", +      creates => "/etc/openvpn/${name}/easy-rsa", +      notify  => Exec["fix_easyrsa_file_permissions_${name}"], +      require => File["/etc/openvpn/${name}"]; +  } + +  exec { +    "fix_easyrsa_file_permissions_${name}": +      refreshonly => true, +      command     => "/bin/chmod 755 /etc/openvpn/${name}/easy-rsa/*"; +  } + +  file { +    "/etc/openvpn/${name}/easy-rsa/vars": +      ensure  => present, +      content => template('openvpn/vars.erb'), +      require => Exec["copy easy-rsa to openvpn config folder ${name}"]; +  } + +  file { +    "/etc/openvpn/${name}/easy-rsa/openssl.cnf": +      require => Exec["copy easy-rsa to openvpn config folder ${name}"]; +  } + +  if $openvpn::params::link_openssl_cnf == true { +    File["/etc/openvpn/${name}/easy-rsa/openssl.cnf"] { +      ensure => link, +      target => "/etc/openvpn/${name}/easy-rsa/openssl-1.0.0.cnf" +    } +  } + +  exec { +    "generate dh param ${name}": +      command  => '. ./vars && ./clean-all && ./build-dh', +      cwd      => "/etc/openvpn/${name}/easy-rsa", +      creates  => "/etc/openvpn/${name}/easy-rsa/keys/dh1024.pem", +      provider => 'shell', +      require  => File["/etc/openvpn/${name}/easy-rsa/vars"]; + +    "initca ${name}": +      command  => '. ./vars && ./pkitool --initca', +      cwd      => "/etc/openvpn/${name}/easy-rsa", +      creates  => "/etc/openvpn/${name}/easy-rsa/keys/ca.key", +      provider => 'shell', +      require  => [ Exec["generate dh param ${name}"], File["/etc/openvpn/${name}/easy-rsa/openssl.cnf"] ]; + +    "generate server cert ${name}": +      command  => '. ./vars && ./pkitool --server server', +      cwd      => "/etc/openvpn/${name}/easy-rsa", +      creates  => "/etc/openvpn/${name}/easy-rsa/keys/server.key", +      provider => 'shell', +      require  => Exec["initca ${name}"]; +  } + +  file { +    "/etc/openvpn/${name}/keys": +      ensure  => link, +      target  => "/etc/openvpn/${name}/easy-rsa/keys", +      require => Exec["copy easy-rsa to openvpn config folder ${name}"]; +  } + +  if $::osfamily == 'Debian' { +    concat::fragment { +      "openvpn.default.autostart.${name}": +        content => "AUTOSTART=\"\$AUTOSTART ${name}\"\n", +        target  => '/etc/default/openvpn', +        order   => 10; +    } +  } + +  file { +    "/etc/openvpn/${name}.conf": +      owner   => root, +      group   => root, +      mode    => '0444', +      content => template('openvpn/server.erb'); +  } +} diff --git a/puppet/modules/openvpn/manifests/service.pp b/puppet/modules/openvpn/manifests/service.pp new file mode 100644 index 00000000..54e8db7d --- /dev/null +++ b/puppet/modules/openvpn/manifests/service.pp @@ -0,0 +1,36 @@ +# == Class: openvpn::config +# +# This class maintains the openvpn service +# +# +# === Examples +# +# This class should not be directly invoked +# +# === Authors +# +# * Raffael Schmid <mailto:raffael@yux.ch> +# * John Kinsella <mailto:jlkinsel@gmail.com> +# * Justin Lambert <mailto:jlambert@letsevenup.com> +# +# === License +# +# Copyright 2013 Raffael Schmid, <raffael@yux.ch> +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# lied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +class openvpn::service { +  service { +    'openvpn': +      ensure     => running, +      enable     => true, +      hasrestart => true, +      hasstatus  => true; +  } +} diff --git a/puppet/modules/openvpn/spec/classes/openvpn_config_spec.rb b/puppet/modules/openvpn/spec/classes/openvpn_config_spec.rb new file mode 100644 index 00000000..bbb63a77 --- /dev/null +++ b/puppet/modules/openvpn/spec/classes/openvpn_config_spec.rb @@ -0,0 +1,15 @@ +require 'spec_helper' +  +describe 'openvpn::config', :type => :class do +   +  it { should create_class('openvpn::config') } +   +  context "on Debian based machines" do +    let (:facts) { { :osfamily => 'Debian', :concat_basedir => '/var/lib/puppet/concat' } } + +    it { should contain_class('concat::setup') } +    it { should contain_concat('/etc/default/openvpn') } +    it { should contain_concat__fragment('openvpn.default.header') } +  end + +end diff --git a/puppet/modules/openvpn/spec/classes/openvpn_init_spec.rb b/puppet/modules/openvpn/spec/classes/openvpn_init_spec.rb new file mode 100644 index 00000000..45dcc9bf --- /dev/null +++ b/puppet/modules/openvpn/spec/classes/openvpn_init_spec.rb @@ -0,0 +1,9 @@ +require 'spec_helper' +  +describe 'openvpn', :type => :class do + +  let (:facts) { { :concat_basedir => '/var/lib/puppet/concat' } } + +  it { should create_class('openvpn') } + +end diff --git a/puppet/modules/openvpn/spec/classes/openvpn_install_spec.rb b/puppet/modules/openvpn/spec/classes/openvpn_install_spec.rb new file mode 100644 index 00000000..cdb31358 --- /dev/null +++ b/puppet/modules/openvpn/spec/classes/openvpn_install_spec.rb @@ -0,0 +1,11 @@ +require 'spec_helper' +  +describe 'openvpn::install', :type => :class do + +  it { should create_class('openvpn::install') } +  it { should contain_package('openvpn') } + +  it { should contain_file('/etc/openvpn').with('ensure' => 'directory') } +  it { should contain_file('/etc/openvpn/keys').with('ensure' => 'directory') } + +end diff --git a/puppet/modules/openvpn/spec/classes/openvpn_service_spec.rb b/puppet/modules/openvpn/spec/classes/openvpn_service_spec.rb new file mode 100644 index 00000000..f427e7f1 --- /dev/null +++ b/puppet/modules/openvpn/spec/classes/openvpn_service_spec.rb @@ -0,0 +1,13 @@ +require 'spec_helper' +  +describe 'openvpn::service', :type => :class do + +  let (:facts) { { :concat_basedir => '/var/lib/puppet/concat' } } + +  it { should create_class('openvpn::service') } +  it { should contain_service('openvpn').with( +    'ensure'  => 'running', +    'enable'  => true +  ) } + +end diff --git a/puppet/modules/openvpn/spec/defines/openvpn_client_spec.rb b/puppet/modules/openvpn/spec/defines/openvpn_client_spec.rb new file mode 100644 index 00000000..a4b580e8 --- /dev/null +++ b/puppet/modules/openvpn/spec/defines/openvpn_client_spec.rb @@ -0,0 +1,88 @@ +require 'spec_helper' +  +describe 'openvpn::client', :type => :define do +  let(:title) { 'test_client' } +  let(:params) { { 'server' => 'test_server' } } +  let(:facts) { { :fqdn => 'somehost', :concat_basedir => '/var/lib/puppet/concat' } } +  let(:pre_condition) do +    'openvpn::server { "test_server": +      country       => "CO", +      province      => "ST", +      city          => "Some City", +      organization  => "example.org", +      email         => "testemail@example.org" +    }' +  end + +  it { should contain_exec('generate certificate for test_client in context of test_server') } + +  [ 'test_client', 'test_client/keys'].each do |directory| +    it { should contain_file("/etc/openvpn/test_server/download-configs/#{directory}") } +  end + +  [ 'test_client.crt', 'test_client.key', 'ca.crt' ].each do |file| +    it { should contain_file("/etc/openvpn/test_server/download-configs/test_client/keys/#{file}").with( +      'ensure'  => 'link', +      'target'  => "/etc/openvpn/test_server/easy-rsa/keys/#{file}" +    )} +  end +   +  it { should contain_exec('tar the thing test_server with test_client').with( +    'cwd'     => '/etc/openvpn/test_server/download-configs/', +    'command' => '/bin/rm test_client.tar.gz; tar --exclude=\*.conf.d -chzvf test_client.tar.gz test_client' +  ) } + +  context "setting the minimum parameters" do +    let(:params) { { 'server' => 'test_server' } } +    let(:facts) { { :fqdn => 'somehost', :concat_basedir => '/var/lib/puppet/concat' } } + +    it { should contain_file('/etc/openvpn/test_server/download-configs/test_client/test_client.conf').with_content(/^client$/)} +    it { should contain_file('/etc/openvpn/test_server/download-configs/test_client/test_client.conf').with_content(/^ca\s+keys\/ca\.crt$/)} +    it { should contain_file('/etc/openvpn/test_server/download-configs/test_client/test_client.conf').with_content(/^cert\s+keys\/test_client.crt$/)} +    it { should contain_file('/etc/openvpn/test_server/download-configs/test_client/test_client.conf').with_content(/^key\s+keys\/test_client\.key$/)} +    it { should contain_file('/etc/openvpn/test_server/download-configs/test_client/test_client.conf').with_content(/^dev\s+tun$/)} +    it { should contain_file('/etc/openvpn/test_server/download-configs/test_client/test_client.conf').with_content(/^proto\s+tcp$/)} +    it { should contain_file('/etc/openvpn/test_server/download-configs/test_client/test_client.conf').with_content(/^remote\s+somehost\s+1194$/)} +    it { should contain_file('/etc/openvpn/test_server/download-configs/test_client/test_client.conf').with_content(/^comp-lzo$/)} +    it { should contain_file('/etc/openvpn/test_server/download-configs/test_client/test_client.conf').with_content(/^resolv-retry\s+infinite$/)} +    it { should contain_file('/etc/openvpn/test_server/download-configs/test_client/test_client.conf').with_content(/^nobind$/)} +    it { should contain_file('/etc/openvpn/test_server/download-configs/test_client/test_client.conf').with_content(/^persist-key$/)} +    it { should contain_file('/etc/openvpn/test_server/download-configs/test_client/test_client.conf').with_content(/^persist-tun$/)} +    it { should contain_file('/etc/openvpn/test_server/download-configs/test_client/test_client.conf').with_content(/^mute-replay-warnings$/)} +    it { should contain_file('/etc/openvpn/test_server/download-configs/test_client/test_client.conf').with_content(/^ns\-cert\-type\s+server$/)} +    it { should contain_file('/etc/openvpn/test_server/download-configs/test_client/test_client.conf').with_content(/^verb\s+3$/)} +    it { should contain_file('/etc/openvpn/test_server/download-configs/test_client/test_client.conf').with_content(/^mute\s+20$/)} +  end + +  context "setting all of the parameters" do +    let(:params) { {  +      'server'                => 'test_server', +      'compression'           => 'comp-something', +      'dev'                   => 'tap', +      'mute'                  => 10, +      'mute_replay_warnings'  => false, +      'nobind'                => false, +      'persist_key'           => false, +      'persist_tun'           => false, +      'port'                  => '123', +      'proto'                 => 'udp', +      'remote_host'           => 'somewhere', +      'resolv_retry'          => '2m', +      'verb'                  => '1' +    } } +    let(:facts) { { :fqdn => 'somehost', :concat_basedir => '/var/lib/puppet/concat' } } + +    it { should contain_file('/etc/openvpn/test_server/download-configs/test_client/test_client.conf').with_content(/^client$/)} +    it { should contain_file('/etc/openvpn/test_server/download-configs/test_client/test_client.conf').with_content(/^ca\s+keys\/ca\.crt$/)} +    it { should contain_file('/etc/openvpn/test_server/download-configs/test_client/test_client.conf').with_content(/^cert\s+keys\/test_client.crt$/)} +    it { should contain_file('/etc/openvpn/test_server/download-configs/test_client/test_client.conf').with_content(/^key\s+keys\/test_client\.key$/)} +    it { should contain_file('/etc/openvpn/test_server/download-configs/test_client/test_client.conf').with_content(/^dev\s+tap$/)} +    it { should contain_file('/etc/openvpn/test_server/download-configs/test_client/test_client.conf').with_content(/^proto\s+udp$/)} +    it { should contain_file('/etc/openvpn/test_server/download-configs/test_client/test_client.conf').with_content(/^remote\s+somewhere\s+123$/)} +    it { should contain_file('/etc/openvpn/test_server/download-configs/test_client/test_client.conf').with_content(/^comp-something$/)} +    it { should contain_file('/etc/openvpn/test_server/download-configs/test_client/test_client.conf').with_content(/^resolv-retry\s+2m$/)} +    it { should contain_file('/etc/openvpn/test_server/download-configs/test_client/test_client.conf').with_content(/^verb\s+1$/)} +    it { should contain_file('/etc/openvpn/test_server/download-configs/test_client/test_client.conf').with_content(/^mute\s+10$/)} +  end + +end diff --git a/puppet/modules/openvpn/spec/defines/openvpn_client_specific_config_spec.rb b/puppet/modules/openvpn/spec/defines/openvpn_client_specific_config_spec.rb new file mode 100644 index 00000000..cfdab389 --- /dev/null +++ b/puppet/modules/openvpn/spec/defines/openvpn_client_specific_config_spec.rb @@ -0,0 +1,40 @@ +require 'spec_helper' + +describe 'openvpn::client_specific_config', :type => :define do +  let(:title) { 'test_client' } +  let(:params) { { 'server' => 'test_server' } } +  let(:facts) { { :fqdn => 'somehost', :concat_basedir => '/var/lib/puppet/concat' } } +  let(:pre_condition) do +    [ +      'openvpn::server { "test_server": +        country       => "CO", +        province      => "ST", +        city          => "Some City", +        organization  => "example.org", +        email         => "testemail@example.org" +      }', +      'openvpn::client { "test_client": +        server => "test_server" +      }' +    ].join +  end + +  it { should contain_file('/etc/openvpn/test_server/client-configs/test_client') } + +  describe "setting no paramter at all" do +    it { should contain_file('/etc/openvpn/test_server/client-configs/test_client').with_content(/\A\n\z/) } +  end + +  describe "setting all parameters" do +    let(:params) do +      {:server       => 'test_server', +       :iroute       => ['10.0.1.0 255.255.255.0'], +       :ifconfig     => '10.10.10.2 255.255.255.0', +       :dhcp_options => ['DNS 8.8.8.8']} +    end + +    it { should contain_file('/etc/openvpn/test_server/client-configs/test_client').with_content(/^iroute 10.0.1.0 255.255.255.0$/) } +    it { should contain_file('/etc/openvpn/test_server/client-configs/test_client').with_content(/^ifconfig-push 10.10.10.2 255.255.255.0$/) } +    it { should contain_file('/etc/openvpn/test_server/client-configs/test_client').with_content(/^push dhcp-option DNS 8.8.8.8$/) } +  end +end diff --git a/puppet/modules/openvpn/spec/defines/openvpn_server_spec.rb b/puppet/modules/openvpn/spec/defines/openvpn_server_spec.rb new file mode 100644 index 00000000..467be6aa --- /dev/null +++ b/puppet/modules/openvpn/spec/defines/openvpn_server_spec.rb @@ -0,0 +1,165 @@ +require 'spec_helper' + +describe 'openvpn::server', :type => :define do + +  let(:title) { 'test_server' } + +  context "creating a server with the minimum parameters" do +    let(:params) { { +      'country'       => 'CO', +      'province'      => 'ST', +      'city'          => 'Some City', +      'organization'  => 'example.org', +      'email'         => 'testemail@example.org' +    } } + +    let (:facts) { { +      :ipaddress_eth0 => '1.2.3.4', +      :network_eth0   => '1.2.3.0', +      :netmask_eth0   => '255.255.255.0', +      :concat_basedir => '/var/lib/puppet/concat', +      :osfamily       => 'anything_else' +    } } + +    # Files associated with a server config +    it { should contain_file('/etc/openvpn/test_server').with('ensure' => 'directory')} +    it { should contain_file('/etc/openvpn/test_server/client-configs').with('ensure' => 'directory')} +    it { should contain_file('/etc/openvpn/test_server/download-configs').with('ensure' => 'directory')} +    it { should contain_file('/etc/openvpn/test_server/easy-rsa/vars')} +    it { should contain_file('/etc/openvpn/test_server/easy-rsa/openssl.cnf')} +    it { should contain_file('/etc/openvpn/test_server/keys').with( +      'ensure'  => 'link', +      'target'  => '/etc/openvpn/test_server/easy-rsa/keys' +    )} + +    # Execs to working with certificates +    it { should contain_exec('copy easy-rsa to openvpn config folder test_server').with( +      'command' => '/bin/cp -r /usr/share/doc/openvpn/examples/easy-rsa/2.0 /etc/openvpn/test_server/easy-rsa' +    )} +    it { should contain_exec('generate dh param test_server') } +    it { should contain_exec('initca test_server') } +    it { should contain_exec('generate server cert test_server') } + +    # VPN server config file itself +    it { should contain_file('/etc/openvpn/test_server.conf').with_content(/^mode\s+server$/) } +    it { should contain_file('/etc/openvpn/test_server.conf').with_content(/^client\-config\-dir\s+\/etc\/openvpn\/test_server\/client\-configs$/) } +    it { should contain_file('/etc/openvpn/test_server.conf').with_content(/^ca\s+\/etc\/openvpn\/test_server\/keys\/ca.crt$/) } +    it { should contain_file('/etc/openvpn/test_server.conf').with_content(/^cert\s+\/etc\/openvpn\/test_server\/keys\/server.crt$/) } +    it { should contain_file('/etc/openvpn/test_server.conf').with_content(/^key\s+\/etc\/openvpn\/test_server\/keys\/server.key$/) } +    it { should contain_file('/etc/openvpn/test_server.conf').with_content(/^dh\s+\/etc\/openvpn\/test_server\/keys\/dh1024.pem$/) } +    it { should contain_file('/etc/openvpn/test_server.conf').with_content(/^proto\s+tcp-server$/) } +    it { should contain_file('/etc/openvpn/test_server.conf').with_content(/^tls-server$/) } +    it { should contain_file('/etc/openvpn/test_server.conf').with_content(/^port\s+1194$/) } +    it { should contain_file('/etc/openvpn/test_server.conf').with_content(/^comp-lzo$/) } +    it { should contain_file('/etc/openvpn/test_server.conf').with_content(/^group\s+nogroup$/) } +    it { should contain_file('/etc/openvpn/test_server.conf').with_content(/^user\s+nobody$/) } +    it { should_not contain_file('/etc/openvpn/test_server.conf').with_content(/^log\-append\s+test_server\/openvpn\.log$/) } +    it { should contain_file('/etc/openvpn/test_server.conf').with_content(/^status\s+test_server\/openvpn\-status\.log$/) } +    it { should contain_file('/etc/openvpn/test_server.conf').with_content(/^dev\s+tun0$/) } +    it { should contain_file('/etc/openvpn/test_server.conf').with_content(/^local\s+1\.2\.3\.4$/) } +    it { should_not contain_file('/etc/openvpn/test_server.conf').with_content(/^ifconfig-pool-persist/) } +  end + +  context "creating a server setting all parameters" do +    let(:params) { { +      'country'       => 'CO', +      'province'      => 'ST', +      'city'          => 'Some City', +      'organization'  => 'example.org', +      'email'         => 'testemail@example.org', +      'compression'   => 'fake_compression', +      'port'          => '123', +      'proto'         => 'udp', +      'group'         => 'someone', +      'user'          => 'someone', +      'logfile'       => '/var/log/openvpn/test_server.log', +      'status_log'    => '/var/log/openvpn/test_server_status.log', +      'dev'           => 'tun1', +      'local'         => '2.3.4.5', +      'ipp'           => true, +      'server'        => '2.3.4.0 255.255.0.0', +      'push'          => [ 'dhcp-option DNS 172.31.0.30', 'route 172.31.0.0 255.255.0.0' ] +    } } + +    let (:facts) { { +      :ipaddress_eth0 => '1.2.3.4', +      :network_eth0   => '1.2.3.0', +      :netmask_eth0   => '255.255.255.0', +      :concat_basedir => '/var/lib/puppet/concat' +    } } + +    it { should contain_file('/etc/openvpn/test_server.conf').with_content(/^mode\s+server$/) } +    it { should contain_file('/etc/openvpn/test_server.conf').with_content(/^client\-config\-dir\s+\/etc\/openvpn\/test_server\/client\-configs$/) } +    it { should contain_file('/etc/openvpn/test_server.conf').with_content(/^ca\s+\/etc\/openvpn\/test_server\/keys\/ca.crt$/) } +    it { should contain_file('/etc/openvpn/test_server.conf').with_content(/^cert\s+\/etc\/openvpn\/test_server\/keys\/server.crt$/) } +    it { should contain_file('/etc/openvpn/test_server.conf').with_content(/^key\s+\/etc\/openvpn\/test_server\/keys\/server.key$/) } +    it { should contain_file('/etc/openvpn/test_server.conf').with_content(/^dh\s+\/etc\/openvpn\/test_server\/keys\/dh1024.pem$/) } +    it { should contain_file('/etc/openvpn/test_server.conf').with_content(/^proto\s+udp$/) } +    it { should_not contain_file('/etc/openvpn/test_server.conf').with_content(/^proto\s+tls-server$/) } +    it { should contain_file('/etc/openvpn/test_server.conf').with_content(/^port\s+123$/) } +    it { should contain_file('/etc/openvpn/test_server.conf').with_content(/^fake_compression$/) } +    it { should contain_file('/etc/openvpn/test_server.conf').with_content(/^group\s+someone$/) } +    it { should contain_file('/etc/openvpn/test_server.conf').with_content(/^user\s+someone$/) } +    it { should contain_file('/etc/openvpn/test_server.conf').with_content(/^log\-append\s+\/var\/log\/openvpn\/test_server\.log$/) } +    it { should contain_file('/etc/openvpn/test_server.conf').with_content(/^status\s+\/var\/log\/openvpn\/test_server_status\.log$/) } +    it { should contain_file('/etc/openvpn/test_server.conf').with_content(/^dev\s+tun1$/) } +    it { should contain_file('/etc/openvpn/test_server.conf').with_content(/^local\s+2\.3\.4\.5$/) } +    it { should contain_file('/etc/openvpn/test_server.conf').with_content(/^server\s+2\.3\.4\.0\s+255\.255\.0\.0$/) } +    it { should contain_file('/etc/openvpn/test_server.conf').with_content(/^push\s+dhcp-option\s+DNS\s+172\.31\.0\.30$/) } +    it { should contain_file('/etc/openvpn/test_server.conf').with_content(/^push\s+route\s+172\.31\.0\.0\s+255\.255\.0\.0$/) } +  end + +  context "when RedHat based machine" do +    let(:params) { { +      'country'       => 'CO', +      'province'      => 'ST', +      'city'          => 'Some City', +      'organization'  => 'example.org', +      'email'         => 'testemail@example.org' +    } } + +    let(:facts) { { :osfamily => 'RedHat', :concat_basedir => '/var/lib/puppet/concat' } } + +    it { should contain_file('/etc/openvpn/test_server/easy-rsa/openssl.cnf').with( +      'ensure'  => 'link', +      'target'  => '/etc/openvpn/test_server/easy-rsa/openssl-1.0.0.cnf' +    )} + +    it { should contain_exec('copy easy-rsa to openvpn config folder test_server').with( +      'command' => '/bin/cp -r /usr/share/doc/openvpn-2.2.2/easy-rsa/2.0 /etc/openvpn/test_server/easy-rsa' +    )} +    it { should contain_file('/etc/openvpn/test_server.conf').with_content(/^group\s+nobody$/) } + +  end + +  context "when Debian based machine" do +    let(:params) { { +      'country'       => 'CO', +      'province'      => 'ST', +      'city'          => 'Some City', +      'organization'  => 'example.org', +      'email'         => 'testemail@example.org' +    } } + +    let(:facts) { { :osfamily => 'Debian', :concat_basedir => '/var/lib/puppet/concat' } } + +    it { should contain_file('/etc/openvpn/test_server/easy-rsa/openssl.cnf').with( +      'ensure'  => 'link', +      'target'  => '/etc/openvpn/test_server/easy-rsa/openssl-1.0.0.cnf' +    )} + +    it { should contain_exec('copy easy-rsa to openvpn config folder test_server').with( +      'command' => '/bin/cp -r /usr/share/doc/openvpn/examples/easy-rsa/2.0 /etc/openvpn/test_server/easy-rsa' +    )} + +    # Configure to start vpn session +    it { should contain_concat__fragment('openvpn.default.autostart.test_server').with( +      'content' => "AUTOSTART=\"$AUTOSTART test_server\"\n", +      'target'  => '/etc/default/openvpn' +    )} + +    it { should contain_file('/etc/openvpn/test_server.conf').with_content(/^group\s+nogroup$/) } + +  end + +end diff --git a/puppet/modules/openvpn/spec/spec_helper.rb b/puppet/modules/openvpn/spec/spec_helper.rb new file mode 100644 index 00000000..dc7e9f4a --- /dev/null +++ b/puppet/modules/openvpn/spec/spec_helper.rb @@ -0,0 +1,2 @@ +require 'rubygems' +require 'puppetlabs_spec_helper/module_spec_helper' diff --git a/puppet/modules/openvpn/templates/client.erb b/puppet/modules/openvpn/templates/client.erb new file mode 100644 index 00000000..021ed617 --- /dev/null +++ b/puppet/modules/openvpn/templates/client.erb @@ -0,0 +1,26 @@ +client +ca keys/ca.crt +cert keys/<%= scope.lookupvar('name') %>.crt +key keys/<%= scope.lookupvar('name') %>.key +dev <%= scope.lookupvar('dev') %> +proto <%= scope.lookupvar('proto') %> +remote <%= scope.lookupvar('remote_host') %> <%= scope.lookupvar('port') %> +<% if scope.lookupvar('compression') != '' -%> +<%= scope.lookupvar('compression') %> +<% end -%> +resolv-retry <%= scope.lookupvar('resolv_retry') %> +<% if scope.lookupvar('nobind') -%> +nobind +<% end -%> +<% if scope.lookupvar('persist_key') -%> +persist-key +<% end -%> +<% if scope.lookupvar('persist_tun') -%> +persist-tun +<% end -%> +<% if scope.lookupvar('mute_replay_warnings') -%> +mute-replay-warnings +<% end -%> +ns-cert-type server +verb <%= scope.lookupvar('verb') %> +mute <%= scope.lookupvar('mute') %> diff --git a/puppet/modules/openvpn/templates/client_specific_config.erb b/puppet/modules/openvpn/templates/client_specific_config.erb new file mode 100644 index 00000000..62cc0e7a --- /dev/null +++ b/puppet/modules/openvpn/templates/client_specific_config.erb @@ -0,0 +1,10 @@ +<% scope.lookupvar('iroute').each do |route| -%> +iroute <%= route %> +<% end -%> +<% if ifconfig = scope.lookupvar('ifconfig') -%> +ifconfig-push <%= ifconfig %> +<% end -%> +<% scope.lookupvar('dhcp_options').each do |option| -%> +push dhcp-option <%= option %> +<% end -%> + diff --git a/puppet/modules/openvpn/templates/etc-default-openvpn.erb b/puppet/modules/openvpn/templates/etc-default-openvpn.erb new file mode 100644 index 00000000..310e462e --- /dev/null +++ b/puppet/modules/openvpn/templates/etc-default-openvpn.erb @@ -0,0 +1,20 @@ +# This is the configuration file for /etc/init.d/openvpn + +# +# Start only these VPNs automatically via init script. +# Allowed values are "all", "none" or space separated list of +# names of the VPNs. If empty, "all" is assumed. +# +#AUTOSTART="all" +#AUTOSTART="none" +#AUTOSTART="home office" +# +# Refresh interval (in seconds) of default status files +# located in /var/run/openvpn.$NAME.status +# Defaults to 10, 0 disables status file generation +# +#STATUSREFRESH=10 +#STATUSREFRESH=0 +# Optional arguments to openvpn's command line +OPTARGS="" +AUTOSTART="" diff --git a/puppet/modules/openvpn/templates/server.erb b/puppet/modules/openvpn/templates/server.erb new file mode 100644 index 00000000..6ef13263 --- /dev/null +++ b/puppet/modules/openvpn/templates/server.erb @@ -0,0 +1,37 @@ +mode server +client-config-dir /etc/openvpn/<%= scope.lookupvar('name') %>/client-configs +ca /etc/openvpn/<%= scope.lookupvar('name') %>/keys/ca.crt +cert /etc/openvpn/<%= scope.lookupvar('name') %>/keys/server.crt +key /etc/openvpn/<%= scope.lookupvar('name') %>/keys/server.key +dh /etc/openvpn/<%= scope.lookupvar('name') %>/keys/dh1024.pem +<% if scope.lookupvar('proto') == 'tcp' -%> +proto <%= scope.lookupvar('proto') %>-server +<% else -%> +proto <%= scope.lookupvar('proto') %> +<% end -%> +port <%= scope.lookupvar('port') %> +<% if scope.lookupvar('tls_server') -%> +tls-server +<% end -%> +<% if scope.lookupvar('compression') != '' -%> +<%= scope.lookupvar('compression') %> +<% end -%> +group <%= scope.lookupvar('group_to_set') %> +user <%= scope.lookupvar('user') %> +<% if scope.lookupvar('logfile') -%> +log-append <%= scope.lookupvar('logfile') %> +<% end -%> +status <%= scope.lookupvar('status_log') %> +dev <%= scope.lookupvar('dev') %> +<% if scope.lookupvar('local') != '' -%> +local <%= scope.lookupvar('local') %> +<% end -%> +<% if scope.lookupvar('ipp') -%> +ifconfig-pool-persist <%= scope.lookupvar('name') %>/vpn-ipp.txt +<% end -%> +<% if scope.lookupvar('server') != '' -%> +server <%= scope.lookupvar('server') %> +<% end -%> +<% scope.lookupvar('push').each do |item| -%> +push <%= item %> +<% end -%> diff --git a/puppet/modules/openvpn/templates/vars.erb b/puppet/modules/openvpn/templates/vars.erb new file mode 100644 index 00000000..20448b8b --- /dev/null +++ b/puppet/modules/openvpn/templates/vars.erb @@ -0,0 +1,68 @@ +# easy-rsa parameter settings + +# NOTE: If you installed from an RPM, +# don't edit this file in place in +# /usr/share/openvpn/easy-rsa -- +# instead, you should copy the whole +# easy-rsa directory to another location +# (such as /etc/openvpn) so that your +# edits will not be wiped out by a future +# OpenVPN package upgrade. + +# This variable should point to +# the top level of the easy-rsa +# tree. +export EASY_RSA="/etc/openvpn/<%= @name %>/easy-rsa" + +# +# This variable should point to +# the requested executables +# +export OPENSSL="openssl" +export PKCS11TOOL="pkcs11-tool" +export GREP="grep" + + +# This variable should point to +# the openssl.cnf file included +# with easy-rsa. +export KEY_CONFIG=`$EASY_RSA/whichopensslcnf $EASY_RSA` + +# Edit this variable to point to +# your soon-to-be-created key +# directory. +# +# WARNING: clean-all will do +# a rm -rf on this directory +# so make sure you define +# it correctly! +export KEY_DIR="$EASY_RSA/keys" + +# Issue rm -rf warning +echo NOTE: If you run ./clean-all, I will be doing a rm -rf on $KEY_DIR + +# PKCS11 fixes +export PKCS11_MODULE_PATH="dummy" +export PKCS11_PIN="dummy" + +# Increase this to 2048 if you +# are paranoid.  This will slow +# down TLS negotiation performance +# as well as the one-time DH parms +# generation process. +export KEY_SIZE=1024 + +# In how many days should the root CA key expire? +export CA_EXPIRE=3650 + +# In how many days should certificates expire? +export KEY_EXPIRE=3650 + +# These are the default values for fields +# which will be placed in the certificate. +# Don't leave any of these fields blank. +export KEY_COUNTRY="<%= @country %>" +export KEY_PROVINCE="<%= @province %>" +export KEY_CITY="<%= @city %>" +export KEY_ORG="<%= @organization %>" +export KEY_EMAIL="<%= @email %>" diff --git a/puppet/modules/openvpn/vagrant/client.pp b/puppet/modules/openvpn/vagrant/client.pp new file mode 100644 index 00000000..7ebeb1d7 --- /dev/null +++ b/puppet/modules/openvpn/vagrant/client.pp @@ -0,0 +1,5 @@ +node default { + +  package { 'openvpn': ensure => installed; } + +} diff --git a/puppet/modules/openvpn/vagrant/server.pp b/puppet/modules/openvpn/vagrant/server.pp new file mode 100644 index 00000000..a95def06 --- /dev/null +++ b/puppet/modules/openvpn/vagrant/server.pp @@ -0,0 +1,23 @@ +node default { +  openvpn::server { 'winterthur': +    country      => 'CH', +    province     => 'ZH', +    city         => 'Winterthur', +    organization => 'example.org', +    email        => 'root@example.org', +    server       => '10.200.200.0 255.255.255.0' +  } + +  openvpn::client { 'client1': +    server => 'winterthur'; +  } + +  openvpn::client_specific_config { 'client1': +    server   => 'winterthur', +    ifconfig => '10.200.200.100 255.255.255.0' +  } + +  openvpn::client { 'client2': +    server => 'winterthur'; +  } +} | 
