Add puppetlabs/certregen module
authorAurelien Jarno <aurelien@aurel32.net>
Sat, 30 Mar 2019 12:13:17 +0000 (13:13 +0100)
committerAurelien Jarno <aurelien@aurel32.net>
Sat, 30 Mar 2019 12:13:17 +0000 (13:13 +0100)
46 files changed:
3rdparty/Puppetfile
3rdparty/modules/certregen/CHANGELOG.md [new file with mode: 0644]
3rdparty/modules/certregen/CONTRIBUTING.md [new file with mode: 0644]
3rdparty/modules/certregen/Gemfile [new file with mode: 0644]
3rdparty/modules/certregen/LICENSE [new file with mode: 0644]
3rdparty/modules/certregen/MAINTAINERS [new file with mode: 0644]
3rdparty/modules/certregen/MAINTAINERS.md [new file with mode: 0644]
3rdparty/modules/certregen/NOTICE [new file with mode: 0644]
3rdparty/modules/certregen/README.markdown [new file with mode: 0644]
3rdparty/modules/certregen/Rakefile [new file with mode: 0644]
3rdparty/modules/certregen/appveyor.yml [new file with mode: 0644]
3rdparty/modules/certregen/checksums.json [new file with mode: 0644]
3rdparty/modules/certregen/lib/facter/has_puppet.rb [new file with mode: 0644]
3rdparty/modules/certregen/lib/facter/hostcrl.rb [new file with mode: 0644]
3rdparty/modules/certregen/lib/facter/localcacert.rb [new file with mode: 0644]
3rdparty/modules/certregen/lib/puppet/application/certregen.rb [new file with mode: 0644]
3rdparty/modules/certregen/lib/puppet/face/certregen.rb [new file with mode: 0644]
3rdparty/modules/certregen/lib/puppet/feature/chloride.rb [new file with mode: 0644]
3rdparty/modules/certregen/lib/puppet/functions/is_classified_with.rb [new file with mode: 0644]
3rdparty/modules/certregen/lib/puppet/parser/functions/is_classified_with.rb [new file with mode: 0644]
3rdparty/modules/certregen/lib/puppet_x/certregen/ca.rb [new file with mode: 0644]
3rdparty/modules/certregen/lib/puppet_x/certregen/certificate.rb [new file with mode: 0644]
3rdparty/modules/certregen/lib/puppet_x/certregen/crl.rb [new file with mode: 0644]
3rdparty/modules/certregen/lib/puppet_x/certregen/util.rb [new file with mode: 0644]
3rdparty/modules/certregen/manifests/client.pp [new file with mode: 0644]
3rdparty/modules/certregen/metadata.json [new file with mode: 0644]
3rdparty/modules/certregen/spec/acceptance/ca_spec.rb [new file with mode: 0644]
3rdparty/modules/certregen/spec/acceptance/healthcheck_spec.rb [new file with mode: 0644]
3rdparty/modules/certregen/spec/acceptance/help_spec.rb [new file with mode: 0644]
3rdparty/modules/certregen/spec/acceptance/helpers.rb [new file with mode: 0644]
3rdparty/modules/certregen/spec/acceptance/nodesets/centos-7-x64.yml [new file with mode: 0644]
3rdparty/modules/certregen/spec/acceptance/nodesets/debian-8-x64.yml [new file with mode: 0644]
3rdparty/modules/certregen/spec/acceptance/nodesets/default.yml [new file with mode: 0644]
3rdparty/modules/certregen/spec/acceptance/nodesets/docker/centos-7.yml [new file with mode: 0644]
3rdparty/modules/certregen/spec/acceptance/nodesets/docker/debian-8.yml [new file with mode: 0644]
3rdparty/modules/certregen/spec/acceptance/nodesets/docker/ubuntu-14.04.yml [new file with mode: 0644]
3rdparty/modules/certregen/spec/acceptance/workflow_regen_after_expire_spec.rb [new file with mode: 0644]
3rdparty/modules/certregen/spec/acceptance/workflow_regen_before_expire_spec.rb [new file with mode: 0644]
3rdparty/modules/certregen/spec/classes/client_spec.rb [new file with mode: 0644]
3rdparty/modules/certregen/spec/integration/puppet/face/certregen_spec.rb [new file with mode: 0644]
3rdparty/modules/certregen/spec/integration/puppet_x/certregen/ca_spec.rb [new file with mode: 0644]
3rdparty/modules/certregen/spec/integration/puppet_x/certregen/certificate_spec.rb [new file with mode: 0644]
3rdparty/modules/certregen/spec/integration/puppet_x/crl_spec.rb [new file with mode: 0644]
3rdparty/modules/certregen/spec/spec_helper.rb [new file with mode: 0644]
3rdparty/modules/certregen/spec/spec_helper_acceptance.rb [new file with mode: 0644]
3rdparty/modules/certregen/spec/spec_helper_local.rb [new file with mode: 0644]

index 53c9f9c..e896bbc 100644 (file)
@@ -9,5 +9,7 @@ mod 'puppet/rabbitmq', '8.2.0'
 
 mod 'nanliu/staging', '1.0.3'
 
+mod 'puppetlabs/certregen', '0.2.0'
+
 # OpenStack
 mod 'duritong/sysctl', '0.0.11' 
diff --git a/3rdparty/modules/certregen/CHANGELOG.md b/3rdparty/modules/certregen/CHANGELOG.md
new file mode 100644 (file)
index 0000000..ee547a1
--- /dev/null
@@ -0,0 +1,42 @@
+## Release 0.2.0
+
+#### Summary
+
+This is a feature and bugfix release.
+
+#### Features
+* (MODULES-4542) Support YAML and JSON output formats for `puppet certregen healthcheck`.
+* (MODULES-4681) Support for Puppet `strict_variables` compiler setting.
+
+#### Bugfixes
+
+* (MODULES-4523) Return expiration of new CA cert for `puppet certregen ca`; previously the expiration of the old CA certificate was returned.
+* (MODULES-4659) Remove usage of APIs deprecated in Puppet 5
+
+## Release 0.1.1
+
+#### Summary
+
+This release adds improvements based on feedback from the 0.1.0 release.
+
+#### Features
+
+* (MODULES-4273) Distribute CRL along with CA cert
+* (MODULES-4194) add unified action for CA/CRL refresh
+* (MODULES-4194) Implement CRL distribution
+* (MODULES-4163) Copy cacert to localcacert on regeneration
+
+#### Bugfixes
+
+* (MODULES-4154) Use puppet feature to test for gem
+* (MODULES-4190) Don't manage localcacert file owner/group
+* (MODULES-4191) Respect casing for cert subject, issuer, AKI extension
+* (MODULES-4193) sort healthcheck cmd output in ascending order
+* (MODULES-4192) Use versioncmp when probing for features
+* (MODULES-4194) Support Facter 1.7 confine syntax
+
+## Experimental Release 0.1.0
+
+#### Summary
+
+This is a pre-production release of the certregen module.
diff --git a/3rdparty/modules/certregen/CONTRIBUTING.md b/3rdparty/modules/certregen/CONTRIBUTING.md
new file mode 100644 (file)
index 0000000..990edba
--- /dev/null
@@ -0,0 +1,217 @@
+Checklist (and a short version for the impatient)
+=================================================
+
+  * Commits:
+
+    - Make commits of logical units.
+
+    - Check for unnecessary whitespace with "git diff --check" before
+      committing.
+
+    - Commit using Unix line endings (check the settings around "crlf" in
+      git-config(1)).
+
+    - Do not check in commented out code or unneeded files.
+
+    - The first line of the commit message should be a short
+      description (50 characters is the soft limit, excluding ticket
+      number(s)), and should skip the full stop.
+
+    - Associate the issue in the message. The first line should include
+      the issue number in the form "(#XXXX) Rest of message".
+
+    - The body should provide a meaningful commit message, which:
+
+      - uses the imperative, present tense: "change", not "changed" or
+        "changes".
+
+      - includes motivation for the change, and contrasts its
+        implementation with the previous behavior.
+
+    - Make sure that you have tests for the bug you are fixing, or
+      feature you are adding.
+
+    - Make sure the test suites passes after your commit:
+      `bundle exec rspec spec/acceptance` More information on [testing](#Testing) below
+
+    - When introducing a new feature, make sure it is properly
+      documented in the README.md
+
+  * Submission:
+
+    * Pre-requisites:
+
+      - Make sure you have a [GitHub account](https://github.com/join)
+
+      - [Create a ticket](https://tickets.puppet.com/secure/CreateIssue!default.jspa), or [watch the ticket](https://tickets.puppet.com/browse/) you are patching for.
+
+    * Preferred method:
+
+      - Fork the repository on GitHub.
+
+      - Push your changes to a topic branch in your fork of the
+        repository. (the format ticket/1234-short_description_of_change is
+        usually preferred for this project).
+
+      - Submit a pull request to the repository in the puppetlabs
+        organization.
+
+The long version
+================
+
+  1.  Make separate commits for logically separate changes.
+
+      Please break your commits down into logically consistent units
+      which include new or changed tests relevant to the rest of the
+      change.  The goal of doing this is to make the diff easier to
+      read for whoever is reviewing your code.  In general, the easier
+      your diff is to read, the more likely someone will be happy to
+      review it and get it into the code base.
+
+      If you are going to refactor a piece of code, please do so as a
+      separate commit from your feature or bug fix changes.
+
+      We also really appreciate changes that include tests to make
+      sure the bug is not re-introduced, and that the feature is not
+      accidentally broken.
+
+      Describe the technical detail of the change(s).  If your
+      description starts to get too long, that is a good sign that you
+      probably need to split up your commit into more finely grained
+      pieces.
+
+      Commits which plainly describe the things which help
+      reviewers check the patch and future developers understand the
+      code are much more likely to be merged in with a minimum of
+      bike-shedding or requested changes.  Ideally, the commit message
+      would include information, and be in a form suitable for
+      inclusion in the release notes for the version of Puppet that
+      includes them.
+
+      Please also check that you are not introducing any trailing
+      whitespace or other "whitespace errors".  You can do this by
+      running "git diff --check" on your changes before you commit.
+
+  2.  Sending your patches
+
+      To submit your changes via a GitHub pull request, we _highly_
+      recommend that you have them on a topic branch, instead of
+      directly on "master".
+      It makes things much easier to keep track of, especially if
+      you decide to work on another thing before your first change
+      is merged in.
+
+      GitHub has some pretty good
+      [general documentation](http://help.github.com/) on using
+      their site.  They also have documentation on
+      [creating pull requests](http://help.github.com/send-pull-requests/).
+
+      In general, after pushing your topic branch up to your
+      repository on GitHub, you can switch to the branch in the
+      GitHub UI and click "Pull Request" towards the top of the page
+      in order to open a pull request.
+
+
+  3.  Update the related GitHub issue.
+
+      If there is a GitHub issue associated with the change you
+      submitted, then you should update the ticket to include the
+      location of your branch, along with any other commentary you
+      may wish to make.
+
+Testing
+=======
+
+Getting Started
+---------------
+
+Our puppet modules provide [`Gemfile`](./Gemfile)s which can tell a ruby
+package manager such as [bundler](http://bundler.io/) what Ruby packages,
+or Gems, are required to build, develop, and test this software.
+
+Please make sure you have [bundler installed](http://bundler.io/#getting-started)
+on your system, then use it to install all dependencies needed for this project,
+by running
+
+```shell
+% bundle install
+Fetching gem metadata from https://rubygems.org/........
+Fetching gem metadata from https://rubygems.org/..
+Using rake (10.1.0)
+Using builder (3.2.2)
+-- 8><-- many more --><8 --
+Using rspec-system-puppet (2.2.0)
+Using serverspec (0.6.3)
+Using rspec-system-serverspec (1.0.0)
+Using bundler (1.3.5)
+Your bundle is complete!
+Use `bundle show [gemname]` to see where a bundled gem is installed.
+```
+
+NOTE some systems may require you to run this command with sudo.
+
+If you already have those gems installed, make sure they are up-to-date:
+
+```shell
+% bundle update
+```
+
+With all dependencies in place and up-to-date we can now run the tests:
+
+```shell
+% bundle exec rake spec
+```
+
+This will execute all the [rspec tests](http://rspec-puppet.com/) tests
+under [spec/defines](./spec/defines), [spec/classes](./spec/classes),
+and so on. rspec tests may have the same kind of dependencies as the
+module they are testing. While the module defines in its [Modulefile](./Modulefile),
+rspec tests define them in [.fixtures.yml](./fixtures.yml).
+
+Some puppet modules also come with [beaker](https://github.com/puppetlabs/beaker)
+tests. These tests spin up a virtual machine under
+[VirtualBox](https://www.virtualbox.org/)) with, controlling it with
+[Vagrant](http://www.vagrantup.com/) to actually simulate scripted test
+scenarios. In order to run these, you will need both of those tools
+installed on your system.
+
+You can run them by issuing the following command
+
+```shell
+% bundle exec rake spec_clean
+% bundle exec rspec spec/acceptance
+```
+
+This will now download a pre-fabricated image configured in the [default node-set](./spec/acceptance/nodesets/default.yml),
+install puppet, copy this module and install its dependencies per [spec/spec_helper_acceptance.rb](./spec/spec_helper_acceptance.rb)
+and then run all the tests under [spec/acceptance](./spec/acceptance).
+
+Writing Tests
+-------------
+
+XXX getting started writing tests.
+
+If you have commit access to the repository
+===========================================
+
+Even if you have commit access to the repository, you will still need to
+go through the process above, and have someone else review and merge
+in your changes.  The rule is that all changes must be reviewed by a
+developer on the project (that did not write the code) to ensure that
+all changes go through a code review process.
+
+Having someone other than the author of the topic branch recorded as
+performing the merge is the record that they performed the code
+review.
+
+
+Additional Resources
+====================
+
+* [Getting additional help](http://puppet.com/community/get-help)
+
+* [Writing tests](https://docs.puppet.com/guides/module_guides/bgtm.html#step-three-module-testing)
+
+* [General GitHub documentation](http://help.github.com/)
+
+* [GitHub pull request documentation](http://help.github.com/send-pull-requests/)
diff --git a/3rdparty/modules/certregen/Gemfile b/3rdparty/modules/certregen/Gemfile
new file mode 100644 (file)
index 0000000..5d86325
--- /dev/null
@@ -0,0 +1,86 @@
+#This file is generated by ModuleSync, do not edit.
+
+source ENV['GEM_SOURCE'] || "https://rubygems.org"
+
+# Determines what type of gem is requested based on place_or_version.
+def gem_type(place_or_version)
+  if place_or_version =~ /^git:/
+    :git
+  elsif place_or_version =~ /^file:/
+    :file
+  else
+    :gem
+  end
+end
+
+# Find a location or specific version for a gem. place_or_version can be a
+# version, which is most often used. It can also be git, which is specified as
+# `git://somewhere.git#branch`. You can also use a file source location, which
+# is specified as `file://some/location/on/disk`.
+def location_for(place_or_version, fake_version = nil)
+  if place_or_version =~ /^(git[:@][^#]*)#(.*)/
+    [fake_version, { :git => $1, :branch => $2, :require => false }].compact
+  elsif place_or_version =~ /^file:\/\/(.*)/
+    ['>= 0', { :path => File.expand_path($1), :require => false }]
+  else
+    [place_or_version, { :require => false }]
+  end
+end
+
+# Used for gem conditionals
+supports_windows = false
+
+group :development do
+  gem 'puppet-lint',                        :require => false
+  gem 'metadata-json-lint',                 :require => false, :platforms => 'ruby'
+  gem 'puppet_facts',                       :require => false
+  gem 'puppet-blacksmith', '>= 3.4.0',      :require => false, :platforms => 'ruby'
+  gem 'puppetlabs_spec_helper', '>= 1.2.1', :require => false
+  gem 'rspec-puppet', '>= 2.3.2',           :require => false
+  gem 'rspec-puppet-facts',                 :require => false, :platforms => 'ruby'
+  gem 'mocha', '< 1.2.0',                   :require => false
+  gem 'simplecov',                          :require => false, :platforms => 'ruby'
+  gem 'parallel_tests', '< 2.10.0',         :require => false if Gem::Version.new(RUBY_VERSION.dup) < Gem::Version.new('2.0.0')
+  gem 'parallel_tests',                     :require => false if Gem::Version.new(RUBY_VERSION.dup) >= Gem::Version.new('2.0.0')
+  gem 'rubocop', '0.41.2',                  :require => false if Gem::Version.new(RUBY_VERSION.dup) < Gem::Version.new('2.0.0')
+  gem 'rubocop',                            :require => false if Gem::Version.new(RUBY_VERSION.dup) >= Gem::Version.new('2.0.0')
+  gem 'rubocop-rspec', '~> 1.6',            :require => false if Gem::Version.new(RUBY_VERSION.dup) >= Gem::Version.new('2.3.0')
+  gem 'pry',                                :require => false
+  gem 'json_pure', '<= 2.0.1',              :require => false if Gem::Version.new(RUBY_VERSION.dup) < Gem::Version.new('2.0.0')
+  gem 'fast_gettext', '1.1.0',              :require => false if Gem::Version.new(RUBY_VERSION.dup) < Gem::Version.new('2.1.0')
+  gem 'fast_gettext',                       :require => false if Gem::Version.new(RUBY_VERSION.dup) >= Gem::Version.new('2.1.0')
+  gem 'rainbow', '< 2.2.0',                 :require => false
+end
+
+group :system_tests do
+  gem 'beaker', *location_for(ENV['BEAKER_VERSION'] || '>= 3')                  
+  gem 'beaker-pe',                                                               :require => false
+  gem 'beaker-rspec', *location_for(ENV['BEAKER_RSPEC_VERSION'])                
+  gem 'beaker-puppet_install_helper',                                            :require => false
+  gem 'beaker-module_install_helper',                                            :require => false
+  gem 'master_manipulator',                                                      :require => false
+  gem 'beaker-hostgenerator', *location_for(ENV['BEAKER_HOSTGENERATOR_VERSION'])
+  gem 'beaker-abs', *location_for(ENV['BEAKER_ABS_VERSION'] || '~> 0.1')        
+end
+
+gem 'puppet', *location_for(ENV['PUPPET_GEM_VERSION'])
+
+# Only explicitly specify Facter/Hiera if a version has been specified.
+# Otherwise it can lead to strange bundler behavior. If you are seeing weird
+# gem resolution behavior, try setting `DEBUG_RESOLVER` environment variable
+# to `1` and then run bundle install.
+gem 'facter', *location_for(ENV['FACTER_GEM_VERSION']) if ENV['FACTER_GEM_VERSION']
+gem 'hiera', *location_for(ENV['HIERA_GEM_VERSION']) if ENV['HIERA_GEM_VERSION']
+
+
+# Evaluate Gemfile.local if it exists
+if File.exists? "#{__FILE__}.local"
+  eval(File.read("#{__FILE__}.local"), binding)
+end
+
+# Evaluate ~/.gemfile if it exists
+if File.exists?(File.join(Dir.home, '.gemfile'))
+  eval(File.read(File.join(Dir.home, '.gemfile')), binding)
+end
+
+# vim:ft=ruby
diff --git a/3rdparty/modules/certregen/LICENSE b/3rdparty/modules/certregen/LICENSE
new file mode 100644 (file)
index 0000000..d645695
--- /dev/null
@@ -0,0 +1,202 @@
+
+                                 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
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   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.
diff --git a/3rdparty/modules/certregen/MAINTAINERS b/3rdparty/modules/certregen/MAINTAINERS
new file mode 100644 (file)
index 0000000..c4aeab2
--- /dev/null
@@ -0,0 +1,12 @@
+{
+  "version": 1,
+  "file_format": "This MAINTAINERS file format is described at http://pup.pt/maintainers",
+  "issues": "https://tickets.puppet.com/browse/MODULES",
+  "people": [
+    {
+      "github": "adrienthebo",
+      "email": "adrien@puppet.com",
+      "name": "Adrien Thebo"
+    }
+  ]
+}
diff --git a/3rdparty/modules/certregen/MAINTAINERS.md b/3rdparty/modules/certregen/MAINTAINERS.md
new file mode 100644 (file)
index 0000000..558cf20
--- /dev/null
@@ -0,0 +1,6 @@
+## Maintenance
+
+Maintainers:
+  - Puppet Forge Modules Team `forge-modules |at| puppet |dot| com`
+
+Tickets: https://tickets.puppet.com/browse/MODULES. Make sure to set component to `certregen`.
diff --git a/3rdparty/modules/certregen/NOTICE b/3rdparty/modules/certregen/NOTICE
new file mode 100644 (file)
index 0000000..6ee6482
--- /dev/null
@@ -0,0 +1,15 @@
+Puppet Module - puppetlabs-certregen
+
+Copyright 2017 Puppet, Inc.
+
+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.
\ No newline at end of file
diff --git a/3rdparty/modules/certregen/README.markdown b/3rdparty/modules/certregen/README.markdown
new file mode 100644 (file)
index 0000000..3e03a00
--- /dev/null
@@ -0,0 +1,300 @@
+# Certregen
+
+#### Table of Contents
+
+1. [Overview](#overview)
+2. [Module Description - What the module does and why it is useful](#module-description)
+3. [Installing](#installing)
+4. [Usage - Configuration options and additional functionality](#usage)
+5. [Reference - An under-the-hood peek at what the module is doing and how](#reference)
+6. [Limitations - OS compatibility, etc.](#limitations)
+7. [Development - Guide for contributing to the module](#development)
+
+## Overview
+
+The certregen module can painlessly refresh a certificate authority (CA) that's about to expire. It can also revive a CA that has already expired.
+
+## Module Description
+
+This module is for regenerating and redistributing Puppet CA certificates and refreshing CRLs, without invalidating certificates signed by the original CA.
+
+A Puppet deployment's CA certificate is only valid for a limited time (usually five years), after which it expires. When a CA expires, Puppet's services will no longer accept any certificates signed by that CA, and your Puppet infrastructure will immediately stop working.
+
+If your Puppet infrastructure has been in place for almost five years, you should:
+
+* Check to see if your CA is expiring soon.
+
+If your CA is expiring soon (or it's already expired and Puppet has stopped working), you should:
+
+* Generate a new CA certificate using the existing CA keypair. This will also automatically update the expiration date of the certificate revocation list (CRL).
+* Distribute the new CA cert and CRL to every node in your Puppet infrastructure.
+
+The certregen module can help you with all of these tasks.
+
+> **Note:** This module is NOT currently designed for the following tasks:
+>
+> * Re-keying and replacing a CA that has become untrustworthy (by a private key compromise or by a vulnerability like Heartbleed or the old `certdnsnames` bug).
+> * Refreshing normal node certificates that are nearing expiration.
+>
+> For the time being, you're on your own for these.
+
+## Installing
+
+If you manage your code with a Puppetfile, add the following line, replacing `<VERSION>` with the version you want to use:
+
+```
+mod 'puppetlabs-certregen', '<VERSION>'
+```
+
+To install manually, run:
+
+```
+puppet module install puppetlabs-certregen
+```
+
+## Usage
+
+The certregen module can help you with four main tasks:
+
+* Check whether any certificates (including the CA) are expired or nearing their expiration date.
+* Refresh and redistribute a CA that hasn't yet expired, in a working Puppet deployment.
+* Refresh and redistribute a CRL that hasn't yet expired, in a working Puppet deployment.
+* Revive and redistribute an expired CA and CRL, in a Puppet deployment that has stopped working.
+
+### Check for nearly-expired (or expired) certificates
+
+The `healthcheck` action can show you which certificates are expiring soon, as well as any that have already expired. The most important certificate is your CA cert --- if it is almost expired, you must refresh it soon.
+
+1. **On your Puppet CA server,** run `sudo puppet certregen healthcheck`.
+
+   This finds any certificates with less than 10% of their lifetime remaining (plus any that have already expired), and lists their certname, fingerprint, remaining lifetime, and expiration date. (If no certs are near expiration, the output is blank.)
+
+   ```
+   [root@pe-201621-master vagrant]# puppet certregen healthcheck
+   "foo.com" (SHA256) 07:2F:61:B4:FB:B3:05:75:9D:45:D1:A8:B1:69:0F:D0:EB:C9:27:03:4E:F8:DD:4A:59:AE:DF:EF:8E:11:74:69
+     Status: expiring
+     Expiration date: 2016-11-02 19:52:59 UTC
+     Expires in: 4 minutes, 19 seconds
+   ```
+
+2. Search the `healthcheck` output for a cert named `"ca"`, which is your CA. **If "ca" is expiring or expired, you must refresh it or revive it as soon as possible.** See the tasks below for more details.
+
+### Refresh a CA that's expiring soon
+
+[ca_ttl]: https://docs.puppet.com/puppet/latest/reference/configuration.html#cattl
+[main manifest]: https://docs.puppet.com/puppet/latest/reference/dirs_manifest.html
+
+To refresh an expiring CA, you must:
+
+* Regenerate the CA certificate with `puppet certregen ca`.
+* Distribute the new CA with the `certregen::client` class.
+
+Before you begin, check to make sure your CA really needs to be refreshed (see above).
+
+1. **On your Puppet CA server,** run `sudo puppet certregen ca`.
+
+   **This will result in an error,** which is normal: since this action replaces your CA cert, it has an extra layer of protection. The error should look something like this:
+
+   ```
+   Error: The serial number of the CA certificate to rotate must be provided. If you are sure that you want to rotate the CA certificate, rerun this command with --ca_serial 03
+   ```
+
+2. In the error message, find and remember the `--ca_serial <NUMBER>` option.
+3. Run `sudo puppet certregen ca --ca_serial <NUMBER>`, using the number from the error message.
+
+   By default, this gives the new CA a lifetime of five years. If you wish to set a non-default lifetime, you can add `--ca_ttl <DURATION>`. See [the docs on the ca_ttl setting][ca_ttl] for details.
+
+   When you regenerate the CA certificate, the CRL will be refreshed at the same time with a new expiration of five years as well.
+
+   At this point:
+
+   * The CA certificate _on your CA server_ has been replaced with a new one. The new CA uses the same keypair, subject, issuer, and subject key identifier as the old one; in practical terms, this means it is a seamless replacement for the old CA.
+   * The CRL _on your CA server_ has been updated with a new expiration date, but is otherwise unchanged.
+   * Your Puppet nodes are still using the old CA and CRL.
+4. **In your [main manifest][] directory,** add a new manifest file called `ca.pp`. In that file, add this line:
+
+   ``` puppet
+   include certregen::client
+   ```
+
+   > **Note:** You must do this in **every** active environment, so that **every** node receives this class in its catalog. You must also ensure that the certregen module is installed in every active environment.
+   >
+   > If you have a prohibitively large number of environments... contact us, because we're still developing this module and soliciting feedback on it.
+5. If your deployment has multiple compile masters, make sure each one completes a Puppet run against the CA server.
+6. Ensure that Puppet runs at least once on every other node in your deployment.
+7. On any Puppet infrastructure nodes (Puppet Server, PuppetDB, PE console), restart all Puppet-related services. The exact services to restart will depend on your version of PE or open source Puppet.
+
+At this point, the new CA and CRL is fully distributed and you're good for another five years.
+
+### Revive a CA that's already expired
+
+[ssldir]: https://docs.puppet.com/puppet/latest/reference/dirs_ssldir.html
+
+If your CA has expired, Puppet has already stopped working, and recovering will take some extra work. You must:
+
+* Regenerate the CA certificate with `puppet certregen ca`.
+* Distribute the new CA to Linux nodes with `puppet certregen redistribute`.
+* Manually distribute the new CA to Windows and non-Linux \*nix nodes.
+
+Before you begin:
+
+* Check to make sure your CA has really expired (see above).
+* If you wish to automatically distribute the new CA cert, ensure that:
+    * The Puppet CA server is also a PuppetDB server.
+    * Your Linux nodes have a user account which can log in with an SSH key and run `sudo` commands without a password.
+
+1. **On your Puppet CA server,** run `sudo puppet certregen ca`.
+
+   **This will result in an error,** which is normal: since this action replaces your CA cert, it has an extra layer of protection. The error should look something like this:
+
+   ```
+   Error: The serial number of the CA certificate to rotate must be provided. If you are sure that you want to rotate the CA certificate, rerun this command with --ca_serial 03
+   ```
+
+2. In the error message, find and remember the `--ca_serial <NUMBER>` option.
+3. Run `sudo puppet certregen ca --ca_serial <NUMBER>`, using the number from the error message.
+
+   By default, this gives the new CA a lifetime of five years. If you wish to set a non-default lifetime, you can add `--ca_ttl <DURATION>`. See [the docs on the ca_ttl setting][ca_ttl] for details.
+
+   When you regenerate the CA certificate, the CRL will be refreshed at the same time with a new expiration of five years as well.
+
+   At this point:
+
+   * The CA certificate _on your CA server_ has been replaced with a new one. The new CA uses the same keypair, subject, issuer, and subject key identifier as the old one; in practical terms, this means it is a seamless replacement for the old CA.
+   * The CRL _on your CA server_ has been updated with a new expiration date, but is otherwise unchanged.
+   * Your Puppet nodes still have the old CA, and Puppet is still non-functional.
+
+4. Use `puppet certregen redistribute` to automatically distribute the CA certificate and CRL to as many Linux nodes as possible. If you don't meet the prerequisites for this (see above), move on to the next step and manually copy the new CA cert to all nodes.
+
+   1. Ensure that the CA server has an SSH private key that can log into your affected nodes. If this key is not usually present on this server, copy it over temporarily.
+   2. Run `/opt/puppetlabs/puppet/bin/gem install chloride`. This SSH helper gem is required by the `certregen redistribute` action.
+   3. Run `puppet certregen redistribute --username <USER> --ssh_key_file <PATH TO KEY>`, replacing the placeholders with your SSH key and username.
+
+   This extracts a list of node hostnames from PuppetDB's database, then copy the new CA cert to each node using SSH. When finished, it lists which nodes were successful and which ones failed.
+
+   ```
+   [root@pe-201640-master vagrant]# puppet certregen redistribute --username vagrant --ssh_key_file /vagrant/id_rsa
+
+   {
+     "succeeded": [
+       "pe-201640-agent0.puppetdebug.vlan",
+       "pe-201640-agent1.puppetdebug.vlan",
+       "pe-201640-agent3.puppetdebug.vlan"
+       "pe-201640-agent2.puppetdebug.vlan",
+       "pe-201640-agent4.puppetdebug.vlan",
+       "pe-201640-master.puppetdebug.vlan"
+     ],
+     "failed": [
+     ]
+   }
+   ```
+5. Manually copy the new CA certificate to any non-Linux nodes and any nodes where automatic distribution failed.
+
+   * The source file is on your CA server, at `/etc/puppetlabs/puppet/ssl/ca/ca_crt.pem`.
+   * Copy it to `<SSLDIR>/certs/ca.pem` on every node. The default ssldir is `/etc/puppetlabs/puppet/ssl` on \*nix, and `%PROGRAMDATA%\PuppetLabs\puppet\etc` on Windows. See [the ssldir docs][ssldir] for the full details.
+   * If you have any other services that use Puppet's PKI (like MCollective, for example), you must update the CA cert for them as well.
+
+6. Manually copy the updated CRL to any non-Linux nodes and any nodes where automatic distribution failed.
+
+   * The source file is on your CA server, at `/etc/puppetlabs/puppet/ssl/ca/ca_crl.pem`.
+   * Copy it to `<SSLDIR>/crl.pem` on every node. The default ssldir is `/etc/puppetlabs/puppet/ssl` on \*nix, and `%PROGRAMDATA%\PuppetLabs\puppet\etc` on Windows. See [the ssldir docs][ssldir] for the full details.
+   * If you have any other services that use Puppet's PKI (like MCollective, for example) and uses the Puppet CRL, you must update the CRL for them as well.
+
+7. On any Puppet infrastructure nodes (Puppet Server, PuppetDB, PE console), restart all Puppet-related services. The exact services to restart will depend on your version of PE or open source Puppet.
+
+At this point, the new CA and CRL is fully distributed and you're good for another five years.
+
+## Reference
+
+### Concepts
+
+Puppet's security is based on a PKI using X.509 certificates. If you're only partially familiar with these concepts, [we've written an introductory primer](https://docs.puppet.com/background/ssl/) about them.
+
+The certregen module's `puppet certregen ca` action creates a new self-signed CA cert using the same keypair as the prior self-signed CA. The new CA has the same:
+
+* Keypair.
+* Subject.
+* Issuer.
+* X509v3 Subject Key Identifier (the fingerprint of the public key).
+
+The new CA has a different:
+
+* Authority Key Identifier (just the serial number, since it's self-signed).
+* Validity period (the point of the whole exercise).
+* Signature (since we changed the serial number and validity period).
+
+Since Puppet's services (and other services that use Puppet's PKI) validate certs by trusting a self-signed CA and comparing its public key to the signatures and Authority Key Identifiers of the certs it has issued, it's possible to issue a new self-signed CA based on a prior keypair without invalidating any certs issued by the old CA. Once you've done that, it's just a matter of delivering the new CA cert to every participant in the PKI.
+
+### Faces
+
+#### puppet certregen ca
+
+Regenerate the CA certificate with the subject of the old CA certificate and updated notBefore and notAfter dates, and update the CRL with a nextUpdate field of 5 years from the present date.
+
+This command combines the behaviors of the `puppet certregean cacert` and `puppet certregen crl` commands and is the preferred method of regenerating your CA.
+
+#### puppet certregen healthcheck
+
+Check all signed certificates (including the CA certificate) for certificates that are expired or nearing expiry.
+
+**Examples**:
+
+~~~
+[root@pe-201621-master vagrant]# puppet certregen healthcheck
+"foo.com" (SHA256) 07:2F:61:B4:FB:B3:05:75:9D:45:D1:A8:B1:69:0F:D0:EB:C9:27:03:4E:F8:DD:4A:59:AE:DF:EF:8E:11:74:69
+  Status: expiring
+  Expiration date: 2016-11-02 19:52:59 UTC
+  Expires in: 4 minutes, 19 seconds
+~~~
+
+#### puppet certregen cacert
+
+Regenerate the CA certificate with the subject of the old CA certificate and updated notBefore and notAfter dates.
+
+#### puppet certregen crl
+
+Update the CRL with a nextUpdate field of 5 years from the present date.
+
+CRLs don't usually expire, since their expiration date is normally updated whenever a certificate is revoked. But in rare cases, like when no certificates have been revoked in five years, they can expire and cause problems.
+
+#### puppet certregen redistribute
+
+Copy a regenerated Puppet CA certificate to all active nodes in PuppetDB in case the CA cert has already expired. This command must be run on the CA server and requires that the PostgreSQL server that PuppetDB uses is also located on the current node.
+
+~~~
+[root@pe-201640-master vagrant]# puppet certregen redistribute --username vagrant --ssh_key_file /vagrant/id_rsa
+
+{
+  "succeeded": [
+    "pe-201640-agent0.puppetdebug.vlan",
+    "pe-201640-agent1.puppetdebug.vlan",
+    "pe-201640-agent3.puppetdebug.vlan"
+    "pe-201640-agent2.puppetdebug.vlan",
+    "pe-201640-agent4.puppetdebug.vlan",
+    "pe-201640-master.puppetdebug.vlan"
+  ],
+  "failed": [
+  ]
+}
+~~~
+
+This subcommand depends on the `chloride` gem, which is not included with this Puppet face. In order to use this subcommand you must manually install it with the `gem` command provided via the Puppet agent installer.
+
+
+### Classes
+
+#### Public Classes
+
+  * `certregen::client`: Rotate the CA certificate and CRL on Puppet agents.
+
+## Limitations
+
+The certregen module is designed to rotate an expiring CA certificate and reuse the CA key pair. Because the keypair is reused this module is unsuitable for rotating a CA certificate when the CA private key has been compromised.
+
+## Development
+
+Puppet Labs modules on the Puppet Forge are open projects, and community contributions are essential for keeping them great. We can’t access the huge number of platforms and myriad of hardware, software, and deployment configurations that Puppet is intended to serve.
+
+We want to keep it as easy as possible to contribute changes so that our modules work in your environment. There are a few guidelines that we need contributors to follow so that we can have a chance of keeping on top of things.
+
+For more information, see our [module contribution guide.](https://docs.puppetlabs.com/forge/contributing.html)
diff --git a/3rdparty/modules/certregen/Rakefile b/3rdparty/modules/certregen/Rakefile
new file mode 100644 (file)
index 0000000..f0adad3
--- /dev/null
@@ -0,0 +1,39 @@
+require 'puppetlabs_spec_helper/rake_tasks'
+require 'puppet-lint/tasks/puppet-lint'
+require 'puppet_blacksmith/rake_tasks' if Bundler.rubygems.find_name('puppet-blacksmith').any?
+
+PuppetLint.configuration.fail_on_warnings = true
+PuppetLint.configuration.send('relative')
+MetadataJsonLint.options.strict_dependencies = false
+MetadataJsonLint.options.fail_on_warnings = false
+
+desc 'Generate pooler nodesets'
+task :gen_nodeset do
+  require 'beaker-hostgenerator'
+  require 'securerandom'
+  require 'fileutils'
+
+  agent_target = ENV['TEST_TARGET']
+  if ! agent_target
+    STDERR.puts 'TEST_TARGET environment variable is not set'
+    STDERR.puts 'setting to default value of "redhat-64default."'
+    agent_target = 'redhat-64default.'
+  end
+
+  master_target = ENV['MASTER_TEST_TARGET']
+  if ! master_target
+    STDERR.puts 'MASTER_TEST_TARGET environment variable is not set'
+    STDERR.puts 'setting to default value of "redhat7-64mdcl"'
+    master_target = 'redhat7-64mdcl'
+  end
+
+  targets = "#{master_target}-#{agent_target}"
+  cli = BeakerHostGenerator::CLI.new([targets])
+  nodeset_dir = "tmp/nodesets"
+  nodeset = "#{nodeset_dir}/#{targets}-#{SecureRandom.uuid}.yaml"
+  FileUtils.mkdir_p(nodeset_dir)
+  File.open(nodeset, 'w') do |fh|
+    fh.print(cli.execute)
+  end
+  puts nodeset
+end
diff --git a/3rdparty/modules/certregen/appveyor.yml b/3rdparty/modules/certregen/appveyor.yml
new file mode 100644 (file)
index 0000000..c87ed7c
--- /dev/null
@@ -0,0 +1,40 @@
+version: 1.1.x.{build}
+skip_commits:
+  message: /^\(?doc\)?.*/
+clone_depth: 10
+init:
+- SET
+- 'mkdir C:\ProgramData\PuppetLabs\code && exit 0'
+- 'mkdir C:\ProgramData\PuppetLabs\facter && exit 0'
+- 'mkdir C:\ProgramData\PuppetLabs\hiera && exit 0'
+- 'mkdir C:\ProgramData\PuppetLabs\puppet\var && exit 0'
+environment:
+  matrix:
+  - PUPPET_GEM_VERSION: ~> 4.0
+    RUBY_VER: 21
+  - PUPPET_GEM_VERSION: ~> 4.0
+    RUBY_VER: 21-x64
+  - PUPPET_GEM_VERSION: ~> 4.0
+    RUBY_VER: 23
+  - PUPPET_GEM_VERSION: ~> 4.0
+    RUBY_VER: 23-x64
+  - PUPPET_GEM_VERSION: 4.2.3
+    RUBY_VER: 21-x64
+matrix:
+  fast_finish: true
+install:
+- SET PATH=C:\Ruby%RUBY_VER%\bin;%PATH%
+- bundle install --jobs 4 --retry 2 --without system_tests
+- type Gemfile.lock
+build: off
+test_script:
+- bundle exec puppet -V
+- ruby -v
+- bundle exec rake spec SPEC_OPTS='--format documentation'
+notifications:
+- provider: Email
+  to:
+  - nobody@nowhere.com
+  on_build_success: false
+  on_build_failure: false
+  on_build_status_changed: false
diff --git a/3rdparty/modules/certregen/checksums.json b/3rdparty/modules/certregen/checksums.json
new file mode 100644 (file)
index 0000000..441dc6e
--- /dev/null
@@ -0,0 +1,46 @@
+{
+  "CHANGELOG.md": "04a989e835468882cb5b251f3a4d8c8f",
+  "CONTRIBUTING.md": "77d0440d7cd4206497f99065c60bed46",
+  "Gemfile": "064893aff1d6bf890eee483af3a79c9e",
+  "LICENSE": "3b83ef96387f14655fc854ddc3c6bd57",
+  "MAINTAINERS": "51bbdbf1dc456295732f1f0dd76aedcb",
+  "MAINTAINERS.md": "b726ae453c434aeed7b0fc82c2fc7a65",
+  "NOTICE": "14ffb9e62d0db875691259c5e2cb4bcc",
+  "README.markdown": "497e4b153b314883444dea035daf82f2",
+  "Rakefile": "fef62450237321833e4ef87ec82b3a77",
+  "appveyor.yml": "7e0ff52cc2abe3fa4f2d8d978bf18ad7",
+  "lib/facter/has_puppet.rb": "bb66c9aca57a13700e912da8af9ba912",
+  "lib/facter/hostcrl.rb": "bf23cb4ad6855286b6dbf74d9ed63ec7",
+  "lib/facter/localcacert.rb": "6bbb10ef4a73a93848acc9e05f25fd8b",
+  "lib/puppet/application/certregen.rb": "656ee8523951160c53eb45ceba6bbf35",
+  "lib/puppet/face/certregen.rb": "b3b8c2b62b2803e56bceecfd71f5ed74",
+  "lib/puppet/feature/chloride.rb": "34a8d7d63f1052bc6d970e1e779dbffa",
+  "lib/puppet/functions/is_classified_with.rb": "e112225f2fc18ca5eb8fc82dc4aec0ea",
+  "lib/puppet/parser/functions/is_classified_with.rb": "dc3218f92da12cd7946f706db07f5279",
+  "lib/puppet_x/certregen/ca.rb": "467e66222b8e32a220bc2396a1edc786",
+  "lib/puppet_x/certregen/certificate.rb": "940a185a9b8a1c6ef871f3a48f6b52e9",
+  "lib/puppet_x/certregen/crl.rb": "79061588b6bdffe15e5af0f533b4d20c",
+  "lib/puppet_x/certregen/util.rb": "f76dcd4502ddb6fac7599e570ec01aa8",
+  "manifests/client.pp": "fbf805ce207507ae289e6663eb32c0e0",
+  "metadata.json": "dca9ed5e5ecafc9834b128eb101c40dd",
+  "spec/acceptance/ca_spec.rb": "cfa03e05b7587592bb490b0fc7acac13",
+  "spec/acceptance/healthcheck_spec.rb": "ce2f493a8c16040ece8db094377656df",
+  "spec/acceptance/help_spec.rb": "15713d283a2974f2d7b7883ba6673b5e",
+  "spec/acceptance/helpers.rb": "9ecc202c0995af3fc98b4c9f4e8ea9d1",
+  "spec/acceptance/nodesets/centos-7-x64.yml": "a713f3abd3657f0ae2878829badd23cd",
+  "spec/acceptance/nodesets/debian-8-x64.yml": "d2d2977900989f30086ad251a14a1f39",
+  "spec/acceptance/nodesets/default.yml": "b42da5a1ea0c964567ba7495574b8808",
+  "spec/acceptance/nodesets/docker/centos-7.yml": "8a3892807bdd62306ae4774f41ba11ae",
+  "spec/acceptance/nodesets/docker/debian-8.yml": "ac8e871d1068c96de5e85a89daaec6df",
+  "spec/acceptance/nodesets/docker/ubuntu-14.04.yml": "dc42ee922a96908d85b8f0f08203ce58",
+  "spec/acceptance/workflow_regen_after_expire_spec.rb": "d3656286496a3afb2a881ea228008f2f",
+  "spec/acceptance/workflow_regen_before_expire_spec.rb": "dffe5d2056deba34c6e2fee9b89d4961",
+  "spec/classes/client_spec.rb": "2d373c8a8d5d76f88dad167e8dd6dc8a",
+  "spec/integration/puppet/face/certregen_spec.rb": "243c371ef91cbc871334c075ac45c8d1",
+  "spec/integration/puppet_x/certregen/ca_spec.rb": "1fc058082e4d8a2617038a59652c4993",
+  "spec/integration/puppet_x/certregen/certificate_spec.rb": "bb4931fe495e9bf0f2509b0eb02a9762",
+  "spec/integration/puppet_x/crl_spec.rb": "a2cac0bf040f37969755548708b06b01",
+  "spec/spec_helper.rb": "680042bbf76628be53cc1354710facce",
+  "spec/spec_helper_acceptance.rb": "facfa9e274f0809fe6d8db9eb544dc14",
+  "spec/spec_helper_local.rb": "ea8230b2baf7376b7159c2949fb7b9c5"
+}
\ No newline at end of file
diff --git a/3rdparty/modules/certregen/lib/facter/has_puppet.rb b/3rdparty/modules/certregen/lib/facter/has_puppet.rb
new file mode 100644 (file)
index 0000000..05f2e80
--- /dev/null
@@ -0,0 +1,10 @@
+Facter.add(:has_puppet) do
+  setcode do
+    begin
+      require 'puppet'
+      true
+    rescue LoadError
+      false
+    end
+  end
+end
diff --git a/3rdparty/modules/certregen/lib/facter/hostcrl.rb b/3rdparty/modules/certregen/lib/facter/hostcrl.rb
new file mode 100644 (file)
index 0000000..1d69a66
--- /dev/null
@@ -0,0 +1,4 @@
+Facter.add(:hostcrl) do
+  confine :has_puppet => true
+  setcode { Puppet[:hostcrl] }
+end
diff --git a/3rdparty/modules/certregen/lib/facter/localcacert.rb b/3rdparty/modules/certregen/lib/facter/localcacert.rb
new file mode 100644 (file)
index 0000000..278ca8b
--- /dev/null
@@ -0,0 +1,4 @@
+Facter.add(:localcacert) do
+  confine :has_puppet => true
+  setcode { Puppet[:localcacert] }
+end
diff --git a/3rdparty/modules/certregen/lib/puppet/application/certregen.rb b/3rdparty/modules/certregen/lib/puppet/application/certregen.rb
new file mode 100644 (file)
index 0000000..73d6ca2
--- /dev/null
@@ -0,0 +1,4 @@
+require 'puppet/application/face_base'
+
+class Puppet::Application::Certregen < Puppet::Application::FaceBase
+end
diff --git a/3rdparty/modules/certregen/lib/puppet/face/certregen.rb b/3rdparty/modules/certregen/lib/puppet/face/certregen.rb
new file mode 100644 (file)
index 0000000..24c4b30
--- /dev/null
@@ -0,0 +1,205 @@
+require 'puppet/face'
+require 'puppet_x/certregen/ca'
+require 'puppet_x/certregen/certificate'
+require 'puppet_x/certregen/crl'
+require 'puppet/feature/chloride'
+
+Puppet::Face.define(:certregen, '0.1.0') do
+  copyright "Puppet", 2016
+  summary "Regenerate the Puppet CA and client certificates"
+
+  description <<-EOT
+    This subcommand provides tools for monitoring the health of the Puppet CA, regenerating
+    expiring CA certificates, and remediation for expired CA certificates.
+  EOT
+
+  action(:ca) do
+    summary "Refresh the Puppet CA certificate and CRL"
+
+    option('--ca_serial SERIAL') do
+      summary 'The serial number (in hexadecimal) of the CA to rotate.'
+    end
+
+    when_invoked do |opts|
+      cert = Puppet::Face[:certregen, :current].cacert(:ca_serial => opts[:ca_serial])
+      crl = Puppet::Face[:certregen, :current].crl()
+      [cert, crl]
+    end
+
+    when_rendering :console do |(cert, crl)|
+      "CA expiration is now #{cert.content.not_after}\n" + \
+      "CRL next update is now #{crl.content.next_update}"
+    end
+  end
+
+  action(:cacert) do
+    summary "Regenerate the Puppet CA certificate"
+
+    description <<-EOT
+      This subcommand generates a new CA certificate that can replace the existing CA certificate.
+      The new CA certificate uses the same subject as the current CA certificate and reuses the
+      key pair associated with the current CA certificate, so all certificates signed by the old
+      CA certificate will remain valid.
+    EOT
+
+    option('--ca_serial SERIAL') do
+      summary 'The serial number (in hexadecimal) of the CA to rotate.'
+    end
+
+    when_invoked do |opts|
+      ca = PuppetX::Certregen::CA.setup
+
+      current_ca_serial = ca.host.certificate.content.serial.to_s(16)
+      if opts[:ca_serial].nil?
+        raise "The serial number of the CA certificate to rotate must be provided. If you " \
+          "are sure that you want to rotate the CA certificate, rerun this command with " \
+          "--ca_serial #{current_ca_serial}"
+      elsif opts[:ca_serial] != current_ca_serial
+        raise "The serial number of the current CA certificate (#{current_ca_serial}) "\
+          "does not match the serial number given on the command line (#{opts[:ca_serial]}). "\
+          "If you are sure that you want to rotate the CA certificate, rerun this command with "\
+          "--ca_serial #{current_ca_serial}"
+      end
+
+      PuppetX::Certregen::CA.backup
+      PuppetX::Certregen::CA.regenerate(ca)
+      Puppet::SSL::Certificate.indirection.find(Puppet::SSL::CA_NAME)
+    end
+
+    when_rendering(:console) do |cert|
+      "CA expiration is now #{cert.content.not_after}"
+    end
+  end
+
+  action(:crl) do
+    summary 'Update the lastUpdate and nextUpdate field for the CA CRL'
+
+    when_invoked do |opts|
+      ca = PuppetX::Certregen::CA.setup
+      PuppetX::Certregen::CRL.refresh(ca)
+    end
+
+    when_rendering(:console) do |crl|
+      "CRL next update is now #{crl.content.next_update}"
+    end
+  end
+
+  action(:healthcheck) do
+    summary "Check for expiring certificates"
+
+    description <<-EOT
+      This subcommand checks for certificates that are nearing or past expiration.
+    EOT
+
+    option('--all') do
+      summary "Report certificate expiry for all nodes, including nodes that aren't near expiration."
+    end
+
+    when_invoked do |opts|
+      ca = PuppetX::Certregen::CA.setup
+
+      certs = Puppet::SSL::Certificate.indirection.search('*').select do |cert|
+        opts[:all] || PuppetX::Certregen::Certificate.expiring?(cert)
+      end
+
+      cacert = ca.host.certificate
+      certs << cacert if (opts[:all] || PuppetX::Certregen::Certificate.expiring?(cacert))
+
+      certs.sort { |a, b| a.content.not_after <=> b.content.not_after }
+    end
+
+    when_rendering :console do |certs|
+      if certs.empty?
+        "No certificates are approaching expiration."
+      else
+        certs.map do |cert|
+          str = "#{cert.name.inspect} #{cert.digest.to_s}\n"
+          expiry = PuppetX::Certregen::Certificate.expiry(cert)
+          str << "Status: #{expiry[:status]}\n"
+          str << "Expiration date: #{expiry[:expiration_date]}\n"
+          if expiry[:expires_in]
+            str << "Expires in: #{expiry[:expires_in]}\n"
+          end
+          str
+        end
+      end
+    end
+
+    when_rendering :pson do |certs|
+      certs.map do |cert|
+        {
+          :name => cert.name,
+          :digest => cert.digest.to_s,
+          :expiry => PuppetX::Certregen::Certificate.expiry(cert)
+        }
+      end
+    end
+
+    when_rendering :yaml do |certs|
+      certs.map do |cert|
+        {
+          :name => cert.name,
+          :digest => cert.digest.to_s,
+          :expiry => PuppetX::Certregen::Certificate.expiry(cert)
+        }
+      end
+    end
+  end
+
+  action(:redistribute) do
+    summary "Redistribute the regenerated CA certificate and CRL to nodes in PuppetDB"
+
+    description <<-EOT
+      Redistribute the regenerated CA certificate and CRL to active nodes in PuppetDB. This command is
+      only necessary if the CA certificate is expired and a new CA certificate needs to be manually
+      distributed via SSH.
+
+      This subcommand depends on the `chloride` gem, which is not included with this Puppet face.
+
+      Distributing the CA certificate via SSH requires either a private ssh key (given by the
+      `--ssh_key_file` flag) or entering the password when prompted. If password auth is used,
+      the `highline` gem should be installed so that the entered password is not echoed to the
+      terminal.
+    EOT
+
+    option('--username USER') do
+      summary "The username to use when logging into the remote machine"
+    end
+
+    option('--ssh_key_file FILE') do
+      summary "The SSH key file to use for authentication"
+      default_to { "~/.ssh/id_rsa" }
+    end
+
+    when_invoked do |opts|
+      unless Puppet.features.chloride?
+        raise "Unable to distribute CA certificate: the chloride gem is not available."
+      end
+
+      config = {}
+
+      config.merge!(username: opts[:username]) if opts[:username]
+      config.merge!(ssh_key_file: File.expand_path(opts[:ssh_key_file])) if opts[:ssh_key_file]
+
+      ca = PuppetX::Certregen::CA.setup
+      cacert = ca.host.certificate
+      if PuppetX::Certregen::Certificate.expiring?(cacert)
+        Puppet.err "Refusing to distribute CA certificate: certificate is pending expiration."
+        exit 1
+      end
+
+      rv = {succeeded: [], failed: []}
+      PuppetX::Certregen::CA.certnames.each do |certname|
+        begin
+          PuppetX::Certregen::CA.distribute(certname, config)
+          rv[:succeeded] << certname
+        rescue => e
+          Puppet.log_exception(e)
+          rv[:failed] << certname
+        end
+      end
+
+      rv
+    end
+  end
+end
diff --git a/3rdparty/modules/certregen/lib/puppet/feature/chloride.rb b/3rdparty/modules/certregen/lib/puppet/feature/chloride.rb
new file mode 100644 (file)
index 0000000..ea777cb
--- /dev/null
@@ -0,0 +1,3 @@
+require 'puppet/util/feature'
+
+Puppet.features.add(:chloride, libs: 'chloride')
diff --git a/3rdparty/modules/certregen/lib/puppet/functions/is_classified_with.rb b/3rdparty/modules/certregen/lib/puppet/functions/is_classified_with.rb
new file mode 100644 (file)
index 0000000..0f6d54b
--- /dev/null
@@ -0,0 +1,9 @@
+Puppet::Functions.create_function(:is_classified_with) do
+  dispatch :is_classified_with do
+    param 'String', :str
+  end
+
+  def is_classified_with(str)
+    closure_scope.find_global_scope.compiler.node.classes.keys.include?(str.to_s)
+  end
+end
diff --git a/3rdparty/modules/certregen/lib/puppet/parser/functions/is_classified_with.rb b/3rdparty/modules/certregen/lib/puppet/parser/functions/is_classified_with.rb
new file mode 100644 (file)
index 0000000..1e17887
--- /dev/null
@@ -0,0 +1,4 @@
+Puppet::Parser::Functions::newfunction(:is_classified_with, :arity => 1,
+                                       :type => :rvalue) do |(str)|
+  compiler.node.classes.keys.include?(str)
+end
diff --git a/3rdparty/modules/certregen/lib/puppet_x/certregen/ca.rb b/3rdparty/modules/certregen/lib/puppet_x/certregen/ca.rb
new file mode 100644 (file)
index 0000000..c9e7457
--- /dev/null
@@ -0,0 +1,137 @@
+require 'securerandom'
+require 'shellwords'
+
+require 'puppet'
+require 'puppet/util/execution'
+require 'puppet/util/package'
+
+require 'puppet/feature/chloride'
+
+module PuppetX
+  module Certregen
+    module CA
+      module_function
+
+      def setup
+        Puppet::SSL::Host.ca_location = :only
+        Puppet.settings.preferred_run_mode = "master"
+
+        if !Puppet::SSL::CertificateAuthority.ca?
+          raise "Unable to set up CA: this node is not a CA server."
+        end
+
+        if Puppet::SSL::Certificate.indirection.find('ca').nil?
+          raise "Unable to set up CA: the CA certificate is not present."
+        end
+
+        Puppet::SSL::CertificateAuthority.instance
+      end
+
+      def backup
+        cacert_backup_path = File.join(Puppet[:cadir], "ca_crt.#{Time.now.to_i}.pem")
+        Puppet.notice("Backing up current CA certificate to #{cacert_backup_path}")
+        FileUtils.cp(Puppet[:cacert], cacert_backup_path)
+      end
+
+      # Generate an updated CA certificate with the same subject as the existing CA certificate
+      # and synchronize the new CA certificate with the local CA certificate.
+      def regenerate(ca, cert = Puppet::SSL::Certificate.indirection.find("ca"))
+        Puppet[:ca_name] = cert.content.subject.to_a[0][1]
+
+        request = Puppet::SSL::CertificateRequest.new(Puppet::SSL::Host::CA_NAME)
+        request.generate(ca.host.key)
+        PuppetX::Certregen::CA.sign(ca, Puppet::SSL::CA_NAME,
+                                    {allow_dns_alt_names: false, self_signing_csr: request})
+        FileUtils.cp(Puppet[:cacert], Puppet[:localcacert])
+      end
+
+      # Copy the current CA certificate and CRL to the given host.
+      #
+      # @note Only Linux systems are supported and requires that the localcacert/hostcrl setting on the
+      #   given host is the default path.
+      #
+      # @param [String] hostname The host to copy the CA cert to
+      # @param [Hash] config the Chloride host config
+      # @return [void]
+      def distribute(hostname, config)
+        host = Chloride::Host.new(hostname, config)
+        host.ssh_connect
+
+        Puppet.debug("SSH status for #{hostname}: #{host.ssh_status}")
+
+        log_events = lambda do |event|
+          event.data[:messages].each do |data|
+            Puppet.info "[#{data.severity}:#{data.hostname}]: #{data.message.inspect}"
+          end
+        end
+
+        distribute_cacert(host, log_events)
+        distribute_crl(host, log_events)
+      end
+
+      def distribute_cacert(host, blk)
+        src = Puppet[:cacert]
+        dst ='/etc/puppetlabs/puppet/ssl/certs/ca.pem' # @todo: query node for localcacert
+        distribute_file(host, src, dst, blk)
+      end
+
+      def distribute_crl(host, blk)
+        src = Puppet[:cacrl]
+        dst ='/etc/puppetlabs/puppet/ssl/crl.pem' # @todo: query node for hostcrl
+        distribute_file(host, src, dst, blk)
+      end
+
+      def distribute_file(host, src, dst, blk)
+        tmp = "#{File.basename(src)}.tmp.#{SecureRandom.uuid}"
+
+        copy_action = Chloride::Action::FileCopy.new(to_host: host, from: src, to: tmp)
+        copy_action.go(&blk)
+        if copy_action.success?
+          Puppet.info "Copied #{src} to #{host.hostname}:#{tmp}"
+        else
+          raise "Failed to copy #{src} to #{host.hostname}:#{tmp}: #{copy_action.status}"
+        end
+
+        move_action = Chloride::Action::Execute.new(host: host, cmd: "cp #{tmp} #{dst}", sudo: true)
+        move_action.go(&blk)
+
+        if move_action.success?
+          Puppet.info "Updated #{host.hostname}:#{dst}"
+        else
+          raise "Failed to copy #{tmp} to #{host.hostname}:#{dst}"
+        end
+
+      end
+
+
+      # Enumerate Puppet nodes without relying on PuppetDB
+      #
+      # If the Puppet CA certificate has expired we cannot rely on PuppetDB working
+      # or being able to connect to Postgres via the network. In order to access
+      # this information while the CA is in a degraded state we perform the query
+      # directly via a local psql call.
+      def certnames
+        psql = '/opt/puppetlabs/server/bin/psql -d pe-puppetdb --pset format=unaligned --pset t=on -c %s'
+        query = 'SELECT certname FROM certnames WHERE deactivated IS NULL AND expired IS NULL;'
+        cmd = psql % Shellwords.escape(query)
+        Puppet::Util::Execution.execute(cmd,
+                                        uid: 'pe-postgres',
+                                        gid: 'pe-postgres').split("\n")
+
+      end
+
+      # Abstract API changes for CA cert signing
+      #
+      # @param ca [Puppet::SSL::CertificateAuthority]
+      # @param hostname [String]
+      # @param options [Hash<Symbol, Object>]
+      def sign(ca, hostname, options)
+        if Puppet::Util::Package.versioncmp(Puppet::PUPPETVERSION, "4.6.0") != -1
+          ca.sign(hostname, options)
+        else
+          ca.sign(hostname, options[:allow_dns_alt_names], options[:self_signing_csr])
+        end
+      end
+    end
+  end
+end
diff --git a/3rdparty/modules/certregen/lib/puppet_x/certregen/certificate.rb b/3rdparty/modules/certregen/lib/puppet_x/certregen/certificate.rb
new file mode 100644 (file)
index 0000000..56ad970
--- /dev/null
@@ -0,0 +1,42 @@
+require 'puppet_x/certregen/util'
+
+module PuppetX
+  module Certregen
+    module Certificate
+      module_function
+
+      # @param cert [Puppet::SSL::Certificate]
+      # @return [Hash<Symbol, String>]
+      def expiry(cert)
+        if cert.content.not_after < Time.now
+          status = :expired
+        elsif expiring?(cert)
+          status = :expiring
+        else
+          status = :ok
+        end
+
+        data = {
+          :status => status,
+          :expiration_date => cert.content.not_after
+        }
+
+        if status != :expired
+          data[:expires_in] = PuppetX::Certregen::Util.duration(cert.content.not_after - Time.now)
+        end
+
+        data
+      end
+
+      # Is this certificate expiring or expired?
+      #
+      # @param cert [Puppet::SSL::Certificate]
+      # @param percent [Integer]
+      def expiring?(cert, percent = 10)
+        remaining = cert.content.not_after - Time.now
+        lifetime = cert.content.not_after - (cert.content.not_before + 86400)
+        remaining / lifetime < (percent / 100.0)
+      end
+    end
+  end
+end
diff --git a/3rdparty/modules/certregen/lib/puppet_x/certregen/crl.rb b/3rdparty/modules/certregen/lib/puppet_x/certregen/crl.rb
new file mode 100644 (file)
index 0000000..e82f929
--- /dev/null
@@ -0,0 +1,56 @@
+require 'fileutils'
+require 'openssl'
+
+module PuppetX
+  module Certregen
+    # @api private
+    # @see {Puppet::SSL::CertificateRevocationList}
+    module CRL
+      module_function
+
+      FIVE_YEARS = 5 * 365*24*60*60
+
+      def refresh(ca)
+        crl = ca.crl
+        crl_content = crl.content
+        update_to_next_crl_number(crl_content)
+        update_valid_time_range_to_start_at(crl_content, Time.now)
+        sign_with(crl_content, ca.host.key.content)
+        Puppet::SSL::CertificateRevocationList.indirection.save(crl)
+        FileUtils.cp(Puppet[:cacrl], Puppet[:hostcrl])
+        Puppet::SSL::CertificateRevocationList.indirection.find("ca")
+      end
+
+      # @api private
+      def update_valid_time_range_to_start_at(crl_content, time)
+        # The CRL is not valid if the time of checking == the time of last_update.
+        # So to have it valid right now we need to say that it was updated one second ago.
+        crl_content.last_update = time - 1
+        crl_content.next_update = time + FIVE_YEARS
+      end
+
+      # @api private
+      def update_to_next_crl_number(crl_content)
+        crl_content.extensions = with_next_crl_number_from(crl_content, crl_content.extensions)
+      end
+
+      # @api private
+      def with_next_crl_number_from(crl_content, existing_extensions)
+        existing_crl_num = existing_extensions.find { |e| e.oid == 'crlNumber' }
+        new_crl_num = existing_crl_num ? existing_crl_num.value.to_i + 1 : 0
+        extensions_without_crl_num = existing_extensions.reject { |e| e.oid == 'crlNumber' }
+        extensions_without_crl_num + [crl_number_of(new_crl_num)]
+      end
+
+      # @api private
+      def crl_number_of(number)
+        OpenSSL::X509::Extension.new('crlNumber', OpenSSL::ASN1::Integer(number))
+      end
+
+      # @api private
+      def sign_with(crl_content, cakey)
+        crl_content.sign(cakey, OpenSSL::Digest::SHA1.new)
+      end
+    end
+  end
+end
diff --git a/3rdparty/modules/certregen/lib/puppet_x/certregen/util.rb b/3rdparty/modules/certregen/lib/puppet_x/certregen/util.rb
new file mode 100644 (file)
index 0000000..e21298a
--- /dev/null
@@ -0,0 +1,27 @@
+module PuppetX
+  module Certregen
+    module Util
+      module_function
+
+      def duration(epoch)
+        seconds = epoch.to_i
+        minutes = (epoch / 60).to_i; seconds %= 60 if minutes > 0
+        hours = (minutes / 60).to_i; minutes %= 60 if hours > 0
+        days = (hours / 24).to_i;    hours %= 24 if days > 0
+        years = (days / 365).to_i;   days %= 365 if years > 0
+
+        list = []
+        list << "#{years} #{pluralize('year', years)}" if years > 0
+        list << "#{days} #{pluralize('day', days)}" if days > 0
+        list << "#{hours} #{pluralize('hour', hours)}" if hours > 0
+        list << "#{minutes} #{pluralize('minute', minutes)}" if minutes > 0
+        list << "#{seconds} #{pluralize('second', seconds)}" if seconds > 0
+        list.join(", ")
+      end
+
+      def pluralize(str, count)
+        count == 1 ? str : str + 's'
+      end
+    end
+  end
+end
diff --git a/3rdparty/modules/certregen/manifests/client.pp b/3rdparty/modules/certregen/manifests/client.pp
new file mode 100644 (file)
index 0000000..54eb153
--- /dev/null
@@ -0,0 +1,28 @@
+# Distribute the current Puppet CA certificate to client systems.
+#
+# To ensure the portability of this code and minimize dependencies, this class uses the `file`
+# function to distribute the CA certificate instead of having end nodes directly fetch the
+# certificate themselves. This means that Puppet installations using a master of master/CA server
+# and compile nodes will need to run Puppet on the compile masters before the CA cert can be
+# distributed to the agents.
+class certregen::client(
+  $manage_crl = true
+) {
+  file { $::localcacert:
+    ensure  => present,
+    content => file($settings::cacert, $settings::localcacert, '/dev/null'),
+    mode    => '0644',
+  }
+
+  $pe_build = getvar('::pe_build')
+  $crl_managed_by_pe = ($pe_build and versioncmp($pe_build, '3.7.0') >= 0) and is_classified_with('puppet_enterprise::profile::master')
+  $needs_crl = $manage_crl and !defined(File[$::hostcrl]) and !$crl_managed_by_pe
+
+  if $needs_crl {
+    file { $::hostcrl:
+      ensure  => present,
+      content => file($settings::cacrl, $settings::hostcrl, '/dev/null'),
+      mode    => '0644',
+    }
+  }
+}
diff --git a/3rdparty/modules/certregen/metadata.json b/3rdparty/modules/certregen/metadata.json
new file mode 100644 (file)
index 0000000..c20c524
--- /dev/null
@@ -0,0 +1,76 @@
+{
+  "name": "puppetlabs-certregen",
+  "version": "0.2.0",
+  "author": "puppetlabs",
+  "summary": "Puppet module providing the certregen face for regenerating CA certificates",
+  "license": "Apache-2.0",
+  "source": "https://github.com/puppetlabs/puppetlabs-certregen",
+  "project_page": "https://github.com/puppetlabs/puppetlabs-certregen",
+  "issues_url": "https://tickets.puppetlabs.com/browse/MODULES",
+  "dependencies": [
+    {"name":"puppetlabs/stdlib"}
+  ],
+  "data_provider": null,
+  "operatingsystem_support": [
+    {
+      "operatingsystem": "RedHat",
+      "operatingsystemrelease": [
+        "5",
+        "6",
+        "7"
+      ]
+    },
+    {
+      "operatingsystem": "CentOS",
+      "operatingsystemrelease": [
+        "5",
+        "6",
+        "7"
+      ]
+    },
+    {
+      "operatingsystem": "OracleLinux",
+      "operatingsystemrelease": [
+        "6",
+        "7"
+      ]
+    },
+    {
+      "operatingsystem": "Scientific",
+      "operatingsystemrelease": [
+        "5",
+        "6",
+        "7"
+      ]
+    },
+    {
+      "operatingsystem": "Debian",
+      "operatingsystemrelease": [
+        "6",
+        "7",
+        "8"
+      ]
+    },
+    {
+      "operatingsystem": "SLES",
+      "operatingsystemrelease": [
+        "11 SP1"
+      ]
+    },
+    {
+      "operatingsystem": "Ubuntu",
+      "operatingsystemrelease": [
+        "10.04",
+        "12.04",
+        "14.04",
+        "16.04"
+      ]
+    }
+  ],
+  "requirements": [
+    {
+      "name": "puppet",
+      "version_requirement": ">= 3.0.0 < 5.0.0"
+    }
+  ]
+}
diff --git a/3rdparty/modules/certregen/spec/acceptance/ca_spec.rb b/3rdparty/modules/certregen/spec/acceptance/ca_spec.rb
new file mode 100644 (file)
index 0000000..c9df863
--- /dev/null
@@ -0,0 +1,60 @@
+require 'spec_helper_acceptance'
+
+describe "puppet certregen ca" do
+  if hosts_with_role(hosts, 'master').length>0 then
+    context 'regen ca on master' do
+
+      context 'C99811 - without --ca_serial' do
+        it 'should provide ca serial id via stderr' do
+          on(master, puppet("certregen ca"), :acceptable_exit_codes => 1) do |result|
+            expect(result.stderr).to match(/rerun this command with --ca_serial ([0-9a-fA-F]+)/)
+          end
+        end
+      end
+
+      context "C99815 - 'puppet certregen ca --ca_serial'" do
+        before(:all) do
+          serial = get_ca_serial_id_on(master)
+          today = get_time_on(master)
+          @future = today + 5*YEAR
+          @regen_result = on(master, "puppet certregen ca --ca_serial #{serial}")
+        end
+        it 'should output the updated CA expiration date' do
+          expect(@regen_result.stdout).to match( /CA expiration is now #{@future.utc.strftime('%Y-%m-%d')}/ )
+        end
+        it 'should update CA cert enddate' do
+          enddate = get_ca_enddate_time_on(master)
+          expect(enddate - @future).to be < 10.0
+        end
+      end
+
+      context 'C99816 - invalid ca_serial id' do
+        it 'should yield an error' do
+          on(master, puppet("certregen ca --ca_serial FD"), :acceptable_exit_codes => 1) do |result|
+            expect(result.stderr).to match(/The serial number of the current CA certificate .* does not match the serial number given on the command line \(FD\)/)
+            expect(result.stderr).to match(/rerun this command with --ca_serial ([0-9a-fA-F]+)/)
+          end
+        end
+      end
+
+      context "C99817 - 'puppet certregen ca --ca_serial --ca_ttl 1d'" do
+        before(:all) do
+          today = get_time_on(master)
+          @tomorrow = today + 1*DAY
+
+          serial = get_ca_serial_id_on(master)
+          @regen_result = on(master, "puppet certregen ca --ca_serial #{serial} --ca_ttl 1d")
+        end
+
+        it 'should output the updated CA expiration date' do
+          expect(@regen_result.stdout).to match( /CA expiration is now #{@tomorrow.utc.strftime('%Y-%m-%d')}/ )
+        end
+        it 'should update CA cert enddate' do
+          enddate = get_ca_enddate_time_on(master)
+          expect(enddate - @tomorrow).to be < 10.0
+        end
+      end
+
+    end
+  end
+end
diff --git a/3rdparty/modules/certregen/spec/acceptance/healthcheck_spec.rb b/3rdparty/modules/certregen/spec/acceptance/healthcheck_spec.rb
new file mode 100644 (file)
index 0000000..387810d
--- /dev/null
@@ -0,0 +1,135 @@
+require 'spec_helper_acceptance'
+require 'yaml'
+require 'json'
+
+describe "puppet certregen healthcheck" do
+  if hosts_with_role(hosts, 'master').length>0 then
+
+    context 'C99803 - cert with more than 10 percent of life' do
+      before(:all) do
+        serial = get_ca_serial_id_on(master)
+        on(master, "puppet certregen ca --ca_serial #{serial}")
+      end
+      it 'should not produce a health warning' do
+        on(master, "puppet certregen healthcheck") do |result|
+          expect(result.stderr).to be_empty
+          expect(result.stdout).to match(/No certificates are approaching expiration/)
+        end
+      end
+    end
+
+    context 'C99804 - cert with less than 10 percent of life' do
+      before(:all) do
+        serial = get_ca_serial_id_on(master)
+        # patch puppet to defeat copywrite date check when generating historical CA
+        patch_puppet_date_check_on(master)
+        @today = get_time_on(master)
+        # set back the clock in order to create a CA that will be approaching its EOL
+        past = @today - (5*YEAR - 20*DAY)
+        on(master, "date #{past.strftime('%m%d%H%M%Y')}")
+        # create old CA
+        on(master, "puppet certregen ca --ca_serial #{serial}")
+        # update to current time
+        on(master, "date #{@today.strftime('%m%d%H%M%Y')}")
+        # revert patch to defeat copywrite date check
+        patch_puppet_date_check_on(master, 'reverse')
+      end
+
+      it 'system should have current date' do
+        today = get_time_on(master)
+        expect(today.utc.strftime('%Y-%m-%d')).to eq @today.utc.strftime('%Y-%m-%d')
+      end
+
+      it 'should warn about pending expiration' do
+        enddate = get_ca_enddate_time_on(master)
+        on(master, "puppet certregen healthcheck") do |result|
+          expect(result.stdout).to match(/Status:\s+expiring/)
+          expect(result.stdout).to match(/Expiration date:\s+#{enddate.utc.strftime('%Y-%m-%d')}/)
+        end
+      end
+
+    end
+
+    context 'C99805 - expired cert' do
+      before(:all) do
+        serial = get_ca_serial_id_on(master)
+        on(master, "puppet certregen ca --ca_serial #{serial} --ca_ttl 1s")
+        sleep 2
+      end
+      it 'should produce a health warning' do
+        on(master, "puppet certregen healthcheck") do |result|
+          expect(result.stdout.gsub("\n", " ")).to match(/ca.*Status: expired/)
+        end
+      end
+    end
+
+    context '--all flag' do
+
+      context 'C99806 --all' do
+        before(:all) do
+          on(master, puppet("cert list --all")) do |result|
+            @certs = result.stdout.scan(/\) ([A-F0-9:]+) /)
+          end
+          @result = on(master, "puppet certregen healthcheck --all")
+        end
+        it 'should contain expiration data for ca cert' do
+          expect(@result.stdout).to match(/"ca".*\n\s*Status:\s*[Ee]xpir/)
+        end
+        it 'should contain expiration data for all node certs' do
+          @certs.each do |cert|
+            expect(@result.stdout).to include cert[0]
+          end
+        end
+      end
+
+      context '--render-as flag' do
+
+        context 'C99808 - --render-as yaml' do
+          before(:all) do
+            on(master, puppet("cert list --all")) do |result|
+              @certs = result.stdout.scan(/\) ([A-F0-9:]+) /)
+            end
+            @result = on(master, "puppet certregen healthcheck --all --render-as yaml")
+            @yaml = YAML.load(@result.stdout)
+          end
+          it 'should return valid yaml' do
+            expect(YAML.parse(@result.stdout)).to be_instance_of(Psych::Nodes::Document)
+          end
+          it 'should contain expiration data for ca cert' do
+            ca = @yaml.find { |record| record[:name] == 'ca' }
+            expect(ca).not_to be nil
+            expect(ca[:expiry][:status]).to eq(:expired)
+          end
+          it 'should contain expiration data for all node certs' do
+            @certs.each do |cert|
+              expect(@yaml.find { |record| record[:digest] =~ /#{cert[0]}/ }).not_to be nil
+            end
+          end
+        end
+
+        context 'C99809 - --render-as json prints valid json containing expiration data' do
+          before(:all) do
+            on(master, puppet("cert list --all")) do |result|
+              @certs = result.stdout.scan(/\) ([A-F0-9:]+) /)
+            end
+            @json = JSON.parse(on(master, "puppet certregen healthcheck --all --render-as json").stdout)
+          end
+          it 'should return valid json' do
+            expect(@json).not_to be nil
+          end
+          it 'should contain expiration data for ca cert' do
+            ca = @json.find { |record| record['name'] == 'ca' }
+            expect(ca).not_to be nil
+          end
+          it 'should contain expiration data for all node certs' do
+            @certs.each do |cert|
+              expect(@json.find { |record| record['digest'] =~ /#{cert[0]}/ }).not_to be nil
+            end
+          end
+        end
+
+      end
+    end
+
+  end
+end
diff --git a/3rdparty/modules/certregen/spec/acceptance/help_spec.rb b/3rdparty/modules/certregen/spec/acceptance/help_spec.rb
new file mode 100644 (file)
index 0000000..7d1e83d
--- /dev/null
@@ -0,0 +1,39 @@
+require 'spec_helper_acceptance'
+
+describe "pupper help certregen" do
+  # NOTE: MODULES-4733 certregen is not currently compatible with ruby < 1.9
+  ruby_ver = 0
+  on(default, 'ruby --version') do |result|
+    m = /\d+\.\d+\.\d+/.match(result.stdout)
+    ruby_ver = m[0] if m
+  end
+  unless version_is_less(ruby_ver, '1.9') then
+    describe "C98923 - Verify that 'puppet certregen --help' prints help text" do
+      # NOTE: `--help` only works on puppet version 4+
+      if version_is_less( '3.9.9', on(default, puppet('--version')).stdout)
+        describe command("puppet certregen --help") do
+          its(:stdout) { should match( /.*USAGE: puppet certregen <action>.*/ ) }
+          its(:stdout) { should match( /.*See 'puppet man certregen' or 'man puppet-certregen' for full help.*/ ) }
+        end
+      end
+    end
+    describe "C99812 - Verify that 'puppet help certregen' prints help text" do
+        describe command("puppet help certregen") do
+          its(:stdout) { should match( /.*USAGE: puppet certregen <action>.*/ ) }
+          its(:stdout) { should match( /.*See 'puppet man certregen' or 'man puppet-certregen' for full help.*/ ) }
+        end
+    end
+    describe "C99813 - Verify that 'puppet help certregen healthcheck' prints help text for healthcheck subcommand" do
+        describe command("puppet help certregen healthcheck") do
+          its(:stdout) { should match( /.*USAGE: puppet certregen healthcheck .*/ ) }
+          its(:stdout) { should match( /.*See 'puppet man certregen' or 'man puppet-certregen' for full help.*/ ) }
+        end
+    end
+    describe "C99814 - Verify that 'puppet help certregen ca' prints help text for ca subcommand" do
+        describe command("puppet help certregen ca") do
+          its(:stdout) { should match( /.*USAGE: puppet certregen ca .*/ ) }
+          its(:stdout) { should match( /.*See 'puppet man certregen' or 'man puppet-certregen' for full help.*/ ) }
+        end
+    end
+  end
+end
diff --git a/3rdparty/modules/certregen/spec/acceptance/helpers.rb b/3rdparty/modules/certregen/spec/acceptance/helpers.rb
new file mode 100644 (file)
index 0000000..dba7d81
--- /dev/null
@@ -0,0 +1,83 @@
+require 'openssl'
+
+# Time constants in seconds
+HOUR =  60 * 60
+DAY  =  24 * HOUR
+YEAR = 365 * DAY
+
+# Retrieve CA Certificate from the given host
+#
+# @param  [Host]           host   single Beaker::Host
+#
+# @return [OpenSSL::X509::Certificate]  Certificate object
+def get_ca_cert_on(host)
+  if host[:roles].include? 'master' then
+    dir = on(host, puppet('config', 'print', 'cadir')).stdout.chomp
+    ca_path = "#{dir}/ca_crt.pem"
+  else
+    dir = on(host, puppet('config', 'print', 'certdir')).stdout.chomp
+    ca_path = "#{dir}/ca.pem"
+  end
+  on(host, "cat #{ca_path}") do |result|
+    cert = OpenSSL::X509::Certificate.new(result.stdout)
+    return cert
+  end
+end
+
+# Execute `date` command on host with optional arguments
+# and get back a Ruby Time object
+#
+# @param [Host]            host   single Beaker::Host to run the command on
+# @param [Array<String>]   args   Array of arguments to be appended to the
+#                                 `date` command
+# @return [Time]    Ruby Time object
+def get_time_on(host, args = [])
+  arg_string = args.join(' ')
+  date = on(host, "date #{arg_string}").stdout.chomp
+  return Time.parse(date)
+end
+
+# Retrieve the CA enddate on a given host as a Ruby time object
+#
+# @param [Host]            host   single Beaker::Host to get CA enddate from
+#
+# @return [Time]    Ruby Time object, or nil if error
+def get_ca_enddate_time_on(host)
+  cert = get_ca_cert_on(host)
+  return cert.not_after if cert
+  return nil
+end
+
+# Retrieve the current ca_serial value for `puppet certgen ca` on a given host
+#
+# @param [Host]            host   single Beaker::Host to get ca_serial from
+#
+# @return [String]    ca_serial in hexadecimal, or nil if error
+def get_ca_serial_id_on(host)
+  cert = get_ca_cert_on(host)
+  return cert.serial.to_s(16) if cert
+  return nil
+end
+
+# Patch puppet to get around the date check validation.
+#
+# This method is used to patch puppet in order to prevent it from failing to
+# create a CA if the system clock is turned back in time by years. The same
+# method is used to reverse the patch with the `reverse` parameter.
+#
+# @param [Host]            host     single Beaker::Host to run the command on
+# @param [String]          reverse  causes the patch to be reversed
+def patch_puppet_date_check_on(host, reverse=nil)
+  reverse = '--reverse' if reverse
+  apply_manifest_on(host, 'package { "patch": ensure => present}')
+  interface_documentation_file = "/opt/puppetlabs/puppet/lib/ruby/vendor_ruby/puppet/interface/documentation.rb"
+  patch =<<EOF
+305c305
+<           raise ArgumentError, "copyright with a year \#{fault} is very strange; did you accidentally add or subtract two years?"
+---
+>           #raise ArgumentError, "copyright with a year \#{fault} is very strange; did you accidentally add or subtract two years?"
+EOF
+  patch_file        = host.tmpfile('iface_doc_patch')
+  create_remote_file(host, patch_file, patch)
+  on(host, "patch #{reverse} #{interface_documentation_file} < #{patch_file}", :acceptable_exit_codes => [0,1])
+end
diff --git a/3rdparty/modules/certregen/spec/acceptance/nodesets/centos-7-x64.yml b/3rdparty/modules/certregen/spec/acceptance/nodesets/centos-7-x64.yml
new file mode 100644 (file)
index 0000000..5eebdef
--- /dev/null
@@ -0,0 +1,10 @@
+HOSTS:
+  centos-7-x64:
+    roles:
+      - agent
+      - default
+    platform: el-7-x86_64
+    hypervisor: vagrant
+    box: puppetlabs/centos-7.2-64-nocm
+CONFIG:
+  type: foss
diff --git a/3rdparty/modules/certregen/spec/acceptance/nodesets/debian-8-x64.yml b/3rdparty/modules/certregen/spec/acceptance/nodesets/debian-8-x64.yml
new file mode 100644 (file)
index 0000000..fef6e63
--- /dev/null
@@ -0,0 +1,10 @@
+HOSTS:
+  debian-8-x64:
+    roles:
+      - agent
+      - default
+    platform: debian-8-amd64
+    hypervisor: vagrant
+    box: puppetlabs/debian-8.2-64-nocm
+CONFIG:
+  type: foss
diff --git a/3rdparty/modules/certregen/spec/acceptance/nodesets/default.yml b/3rdparty/modules/certregen/spec/acceptance/nodesets/default.yml
new file mode 100644 (file)
index 0000000..dba339c
--- /dev/null
@@ -0,0 +1,10 @@
+HOSTS:
+  ubuntu-1404-x64:
+    roles:
+      - agent
+      - default
+    platform: ubuntu-14.04-amd64
+    hypervisor: vagrant
+    box: puppetlabs/ubuntu-14.04-64-nocm
+CONFIG:
+  type: foss
diff --git a/3rdparty/modules/certregen/spec/acceptance/nodesets/docker/centos-7.yml b/3rdparty/modules/certregen/spec/acceptance/nodesets/docker/centos-7.yml
new file mode 100644 (file)
index 0000000..a3333aa
--- /dev/null
@@ -0,0 +1,12 @@
+HOSTS:
+  centos-7-x64:
+    platform: el-7-x86_64
+    hypervisor: docker
+    image: centos:7
+    docker_preserve_image: true
+    docker_cmd: '["/usr/sbin/init"]'
+    # install various tools required to get the image up to usable levels
+    docker_image_commands:
+      - 'yum install -y crontabs tar wget openssl sysvinit-tools iproute which initscripts'
+CONFIG:
+  trace_limit: 200
diff --git a/3rdparty/modules/certregen/spec/acceptance/nodesets/docker/debian-8.yml b/3rdparty/modules/certregen/spec/acceptance/nodesets/docker/debian-8.yml
new file mode 100644 (file)
index 0000000..df5c319
--- /dev/null
@@ -0,0 +1,11 @@
+HOSTS:
+  debian-8-x64:
+    platform: debian-8-amd64
+    hypervisor: docker
+    image: debian:8
+    docker_preserve_image: true
+    docker_cmd: '["/sbin/init"]'
+    docker_image_commands:
+      - 'apt-get update && apt-get install -y net-tools wget locales strace lsof && echo "en_US.UTF-8 UTF-8" > /etc/locale.gen && locale-gen'
+CONFIG:
+  trace_limit: 200
diff --git a/3rdparty/modules/certregen/spec/acceptance/nodesets/docker/ubuntu-14.04.yml b/3rdparty/modules/certregen/spec/acceptance/nodesets/docker/ubuntu-14.04.yml
new file mode 100644 (file)
index 0000000..b1efa58
--- /dev/null
@@ -0,0 +1,12 @@
+HOSTS:
+  ubuntu-1404-x64:
+    platform: ubuntu-14.04-amd64
+    hypervisor: docker
+    image: ubuntu:14.04
+    docker_preserve_image: true
+    docker_cmd: '["/sbin/init"]'
+    docker_image_commands:
+      # ensure that upstart is booting correctly in the container
+      - 'rm /usr/sbin/policy-rc.d && rm /sbin/initctl && dpkg-divert --rename --remove /sbin/initctl && apt-get update && apt-get install -y net-tools wget && locale-gen en_US.UTF-8'
+CONFIG:
+  trace_limit: 200
diff --git a/3rdparty/modules/certregen/spec/acceptance/workflow_regen_after_expire_spec.rb b/3rdparty/modules/certregen/spec/acceptance/workflow_regen_after_expire_spec.rb
new file mode 100644 (file)
index 0000000..3ae0a9e
--- /dev/null
@@ -0,0 +1,105 @@
+require 'spec_helper_acceptance'
+require 'json'
+
+# https://forge.puppet.com/puppetlabs/certregen#revive-a-ca-thats-already-expired
+describe "C99821 - workflow - regen CA after it expires" do
+  if find_install_type == 'pe' then
+    # This workflow only works with a master to manage the CA
+    # This workflow only works with a puppetdb instance to query hostnames from
+    context 'create CA to be expired and update agents' do
+      before(:all) do
+        ttl = 60
+        serial = get_ca_serial_id_on(master)
+        on(master, puppet("certregen ca --ca_serial #{serial} --ca_ttl #{ttl}s"))
+        start = Time.now
+        agents.each do |agent|
+          on(agent, puppet('agent -t'), :acceptable_exit_codes => [0,2])
+        end
+        finish = Time.now
+        elapsed_time = (finish - start).to_i
+        sleep (ttl - elapsed_time) if elapsed_time < ttl
+        sleep 1
+      end
+
+      it 'should warn that ca is expired' do
+        on(master, puppet("certregen healthcheck")) do |result|
+          expect(result.stdout).to match(/Status:\s+expired/)
+        end
+      end
+
+      context 'regenerate CA' do
+        before(:all) do
+          serial = get_ca_serial_id_on(master)
+          on(master, puppet("certregen ca --ca_serial #{serial}"))
+        end
+
+        it 'should update CA cert enddate' do
+          enddate = get_ca_enddate_time_on(master)
+          future = get_time_on(master, ['-d', "'5 years'"])
+          expect(future - enddate).to be <= (48*HOUR)
+        end
+
+        context 'automatically distribute new ca to linux hosts' do
+          before(:all) do
+            # distribute ssh key for root to agents
+            on(master, "ssh-keygen -t rsa -f $HOME/.ssh/id_rsa -P ''")
+            on(master, "cat $HOME/.ssh/id_rsa.pub") do |result|
+              key_array = result.stdout.split(' ')
+              fail_test('could not get ssh key from master') unless key_array.size > 1
+              @public_key = key_array[1]
+            end
+            agents.each do |agent|
+              unless agent['platform'] =~ /windows/
+                args = ['ensure=present',
+                        "user='root'",
+                        "type='rsa'",
+                        "key='#{@public_key}'",
+                       ]
+                on(agent, puppet_resource('ssh_authorized_key', master.hostname, args))
+                on(master, "ssh -o StrictHostKeyChecking=no #{agent.hostname} ls")
+              end
+            end
+            on(master, "/opt/puppetlabs/puppet/bin/gem install chloride")
+            result = on(master, puppet("certregen redistribute"))
+            @report = JSON.parse(result.stdout)
+          end
+
+          after(:all) do
+            on(master, "rm -f $HOME/.ssh/id_rsa $HOME/.ssh/id_rsa.pub", :acceptable_exit_codes => [0,1])
+            agents.each do |agent|
+              on(agent, puppet_resource('ssh_authorized_key', master.hostname, ['ensure=absent', "user='root'"]), :acceptable_exit_codes => [0,1])
+            end
+          end
+
+          it 'should emit a report in valid json' do
+            expect(@report).not_to be nil
+          end
+          it 'should emit a report with a succeeded key' do
+            expect(@report['succeeded']).not_to be nil
+          end
+          it 'should emit a report with a failed key' do
+            expect(@report['failed']).not_to be nil
+          end
+          it 'should report success on all linux agents' do
+            agents.each do |agent|
+              if agent['platform'] =~ /debian|ubuntu|cumulus|huaweios|el-|centos|fedora|redhat|oracle|scientific|eos|archlinux|sles/
+                expect(@report['succeeded']).to include agent.hostname
+              end
+            end
+          end
+          it 'should update CA cert on all linux agents' do
+            master_enddate = get_ca_enddate_time_on(master)
+            agents.each do |agent|
+              if agent['platform'] =~ /debian|ubuntu|cumulus|huaweios|el-|centos|fedora|redhat|oracle|scientific|eos|archlinux|sles/
+                on(agent, puppet('agent -t'), :acceptable_exit_codes => [0,2])
+                enddate = get_ca_enddate_time_on(agent)
+                expect(enddate).to eq master_enddate
+              end
+            end
+          end
+        end
+
+      end
+    end
+  end
+end
diff --git a/3rdparty/modules/certregen/spec/acceptance/workflow_regen_before_expire_spec.rb b/3rdparty/modules/certregen/spec/acceptance/workflow_regen_before_expire_spec.rb
new file mode 100644 (file)
index 0000000..bad7a84
--- /dev/null
@@ -0,0 +1,77 @@
+require 'spec_helper_acceptance'
+
+# https://forge.puppet.com/puppetlabs/certregen#refresh-a-ca-thats-expiring-soon
+describe "C99818 - workflow - regen CA before it expires" do
+  if hosts_with_role(hosts, 'master').length>0 then
+    # This workflow only works with a master to manage the CA
+    context 'setting CA to expire soon' do
+      before(:all) do
+        serial = get_ca_serial_id_on(master)
+
+        # patch puppet to defeat copywrite date check when generating historical CA
+        patch_puppet_date_check_on(master)
+
+        # determine current time on master
+        @today = get_time_on(master)
+
+        # set back the clock in order to create a CA that will be approaching its EOL
+        past = @today - (5*YEAR - 20*DAY)
+        on(master, "date #{past.strftime('%m%d%H%M%Y')}")
+        # create old CA
+        on(master, puppet(" certregen ca --ca_serial #{serial}"))
+        # update to current time
+        on(master, "date #{@today.strftime('%m%d%H%M%Y')}")
+      end
+
+      it 'should have current date' do
+        today = get_time_on(master)
+        expect(today.utc.strftime('%Y-%m-%d')).to eq @today.utc.strftime('%Y-%m-%d')
+      end
+
+      it 'should warn about pending expiration' do
+        enddate = get_ca_enddate_time_on(master)
+        on(master, puppet("certregen healthcheck")) do |result|
+          expect(result.stdout).to match(/Status:\s+expiring/)
+          expect(result.stdout).to match(/Expiration date:\s+#{enddate.utc.strftime('%Y-%m-%d')}/)
+        end
+      end
+
+      context 'restoring previously patched puppet' do
+        before(:all) do
+          # revert patch to defeat copywrite date check
+          patch_puppet_date_check_on(master, 'reverse')
+        end
+
+        context 'regenerating CA prior to expiration' do
+          before(:all) do
+            serial = get_ca_serial_id_on(master)
+            on(master, puppet("certregen ca --ca_serial #{serial}"))
+          end
+          # validate time stamp
+          it 'should update CA cert enddate' do
+            enddate = get_ca_enddate_time_on(master)
+            future = get_time_on(master, ['-d', "'5 years'"])
+            expect(future - enddate).to be <= (48*HOUR)
+          end
+
+          context 'distribute new ca to linux hosts that have been classified with `certregen::client`' do
+            before(:all) do
+              create_remote_file(master, '/etc/puppetlabs/code/environments/production/manifests/ca.pp', 'include certregen::client')
+              on(master, 'chmod 755 /etc/puppetlabs/code/environments/production/manifests/ca.pp')
+              on(master, puppet('agent -t'), :acceptable_exit_codes => [0,2])
+            end
+            it 'should update CA cert on all linux agents' do
+              master_enddate = get_ca_enddate_time_on(master)
+              agents.each do |agent|
+                on(agent, puppet('agent -t'), :acceptable_exit_codes => [0,2])
+                enddate = get_ca_enddate_time_on(agent)
+                expect(enddate).to eq master_enddate
+              end
+            end
+          end
+
+        end
+      end
+    end
+  end
+end
diff --git a/3rdparty/modules/certregen/spec/classes/client_spec.rb b/3rdparty/modules/certregen/spec/classes/client_spec.rb
new file mode 100644 (file)
index 0000000..843c3b1
--- /dev/null
@@ -0,0 +1,81 @@
+require 'spec_helper'
+
+RSpec.shared_examples "managing the CRL on the client" do |setting|
+  describe "when manage_crl is false" do
+    let(:params) {{'manage_crl' => false}}
+
+    it "doesn't manage the hostcrl on the client" do
+      should_not contain_file(client_hostcrl)
+    end
+  end
+
+  describe "when manage_crl is true" do
+    let(:params) {{'manage_crl' => true}}
+
+    it "manages the hostcrl on the client from the server '#{setting}' setting" do
+      should contain_file(client_hostcrl).with(
+        'ensure'  => 'present',
+        'content' => Puppet.settings.setting(setting).open(&:read),
+        'mode'    => '0644',
+      )
+    end
+  end
+end
+
+RSpec.describe 'certregen::client' do
+  include_context "Initialize CA"
+
+  let(:client_localcacert) { tmpfilename('ca.pem') }
+  let(:client_hostcrl) { tmpfilename('crl.pem') }
+
+  let(:facts) do
+    {
+      'localcacert' => client_localcacert,
+      'hostcrl'     => client_hostcrl,
+      'pe_build'    => '2016.4.0',
+    }
+  end
+
+  before do
+    Puppet.settings.setting(:localcacert).open('w') { |f| f.write("local CA cert") }
+    Puppet.settings.setting(:hostcrl).open('w') { |f| f.write("local CRL") }
+  end
+
+  describe 'when the compile master has CA ssl files' do
+    before do
+      Puppet.settings.setting(:cacert).open('w') { |f| f.write("CA cert") }
+      Puppet.settings.setting(:cacrl).open('w') { |f| f.write("CA CRL") }
+    end
+
+    describe "managing the localcacert on the client" do
+      it do
+        should contain_file(client_localcacert).with(
+          'ensure'  => 'present',
+          'content' => Puppet.settings.setting(:cacert).open(&:read),
+          'mode'    => '0644',
+        )
+      end
+    end
+
+    it_behaves_like "managing the CRL on the client", :cacrl
+  end
+
+  describe "when the compile master only has agent SSL files" do
+    before do
+      FileUtils.rm(Puppet[:cacert])
+      FileUtils.rm(Puppet[:cacrl])
+    end
+
+    describe "managing the localcacert on the client" do
+      it 'manages the client CA cert from the `localcacert` setting' do
+        should contain_file(client_localcacert).with(
+          'ensure'  => 'present',
+          'content' => Puppet.settings.setting(:localcacert).open(&:read),
+          'mode'    => '0644',
+        )
+      end
+    end
+
+    it_behaves_like "managing the CRL on the client", :hostcrl
+  end
+end
diff --git a/3rdparty/modules/certregen/spec/integration/puppet/face/certregen_spec.rb b/3rdparty/modules/certregen/spec/integration/puppet/face/certregen_spec.rb
new file mode 100644 (file)
index 0000000..342aa5a
--- /dev/null
@@ -0,0 +1,77 @@
+require 'spec_helper'
+require 'puppet/face/certregen'
+
+describe Puppet::Face[:certregen, :current] do
+  before(:each) do
+    allow(Puppet::SSL::CertificateAuthority).to receive(:instance) { Puppet::SSL::CertificateAuthority.new }
+  end
+
+  include_context "Initialize CA"
+
+  describe "ca action" do
+    it "invokes the cacert and crl actions" do
+      expect(described_class).to receive(:cacert).with(ca_serial: "01")
+      expect(described_class).to receive(:crl)
+      described_class.ca(ca_serial: "01")
+    end
+  end
+
+  describe "cacert action" do
+    it "raises an error when the ca_serial option is not provided" do
+      expect {
+        described_class.ca
+      }.to raise_error(RuntimeError, /The serial number of the CA certificate to rotate must be provided/)
+    end
+
+    it "raises an error when the ca_serial option is not provided" do
+      expect {
+        described_class.ca(ca_serial: "02")
+      }.to raise_error(RuntimeError, /The serial number of the current CA certificate \(01\) does not match the serial number/)
+    end
+
+    it "backs up the old CA cert and regenerates a new CA cert" do
+      old_cacert_serial = Puppet::SSL::CertificateAuthority.new.host.certificate.content.serial
+      described_class.ca(ca_serial: "01")
+      new_cacert_serial = Puppet::SSL::CertificateAuthority.new.host.certificate.content.serial
+      expect(old_cacert_serial).to_not eq(new_cacert_serial)
+    end
+
+    it "returns the new CA certificate" do
+      returned_cacert = described_class.ca(ca_serial: "01").first
+      new_cacert = Puppet::SSL::CertificateAuthority.new.host.certificate.content
+      expect(returned_cacert.content.serial).to eq new_cacert.serial
+      expect(returned_cacert.content.not_after).to eq new_cacert.not_after
+    end
+  end
+
+  describe 'healthcheck action' do
+    let(:not_before) { Time.now - (60 * 60 * 24 * 365 * 4) }
+    let(:not_after) { Time.now + (60 * 60 * 24 * 30) }
+    it 'warns about expiring CA certificates' do
+      ca = Puppet::SSL::CertificateAuthority.new
+      cert = backdate_certificate(ca, ca.host.certificate, not_before, not_after)
+      Puppet::SSL::Certificate.indirection.save(cert)
+
+      allow(PuppetX::Certregen::CA).to receive(:setup).and_return Puppet::SSL::CertificateAuthority.new
+      healthchecked = described_class.healthcheck
+      expect(healthchecked.size).to eq(1)
+      expect(healthchecked.first.digest.to_s).to eq(cert.digest.to_s)
+    end
+
+    it 'warns about expiring client certificates' do
+      cert = make_certificate("expiring", not_before, not_after)
+      Puppet::SSL::Certificate.indirection.save(cert)
+
+      healthchecked = described_class.healthcheck
+      expect(healthchecked.size).to eq(1)
+      expect(healthchecked.first.digest.to_s).to eq(cert.digest.to_s)
+    end
+
+    it 'orders certificates from shortest expiry to longest expiry' do
+      Puppet::SSL::Certificate.indirection.save(make_certificate("first", not_before, not_after))
+      Puppet::SSL::Certificate.indirection.save(make_certificate("last", not_before + 1, not_after + 1))
+
+      expect(described_class.healthcheck.map(&:name)).to eq %w[first last]
+    end
+  end
+end
diff --git a/3rdparty/modules/certregen/spec/integration/puppet_x/certregen/ca_spec.rb b/3rdparty/modules/certregen/spec/integration/puppet_x/certregen/ca_spec.rb
new file mode 100644 (file)
index 0000000..bb77a7d
--- /dev/null
@@ -0,0 +1,88 @@
+require 'spec_helper'
+require 'puppet_x/certregen/ca'
+
+RSpec.describe PuppetX::Certregen::CA do
+
+  include_context "Initialize CA"
+
+  describe "#setup" do
+    it "errors out when the node is not a CA" do
+      Puppet[:ca] = false
+      expect {
+        described_class.setup
+      }.to raise_error(RuntimeError, "Unable to set up CA: this node is not a CA server.")
+    end
+
+    it "errors out when the node does not have a signed CA certificate" do
+      FileUtils.rm(Puppet[:cacert])
+      expect {
+        described_class.setup
+      }.to raise_error(RuntimeError, "Unable to set up CA: the CA certificate is not present.")
+    end
+  end
+
+  describe '#sign' do
+    let(:ca) { double('ca') }
+
+    it 'uses the positional argument form when the Puppet version predates 4.6.0' do
+      stub_const('Puppet::PUPPETVERSION', '4.5.0')
+      expect(ca).to receive(:sign).with('hello', false, true)
+      described_class.sign(ca, 'hello', allow_dns_alt_names: false, self_signing_csr: true)
+    end
+
+    it 'uses the hash argument form when the Puppet version is 4.6.0 or greater' do
+      stub_const('Puppet::PUPPETVERSION', '4.8.0')
+      expect(ca).to receive(:sign).with('hello', allow_dns_alt_names: false, self_signing_csr: false)
+      described_class.sign(ca, 'hello', allow_dns_alt_names: false, self_signing_csr: false)
+    end
+  end
+
+  describe '#backup_cacert' do
+    it 'backs up the CA cert based on the current timestamp' do
+      now = Time.now
+      expect(Time).to receive(:now).at_least(:once).and_return now
+      described_class.backup
+      backup = File.join(Puppet[:cadir], "ca_crt.#{Time.now.to_i}.pem")
+      expect(File.read(backup)).to eq(File.read(Puppet[:cacert]))
+    end
+  end
+
+  describe '#regenerate_cacert' do
+    it 'generates a certificate with a different serial number' do
+      old_serial = Puppet::SSL::CertificateAuthority.new.host.certificate.content.serial
+      described_class.regenerate(Puppet::SSL::CertificateAuthority.new)
+      new_serial = Puppet::SSL::Certificate.indirection.find("ca").content.serial
+      expect(old_serial).to_not eq new_serial
+    end
+
+    before do
+      Puppet[:ca_name] = 'bar'
+      described_class.regenerate(Puppet::SSL::CertificateAuthority.new)
+    end
+
+    it 'copies the old subject CN to the new certificate' do
+      new_cacert = Puppet::SSL::Certificate.indirection.find("ca")
+      expect(new_cacert.content.subject.to_a[0][1]).to eq 'Puppet CA: foo'
+    end
+
+    it "matches the issuer field with the old CA and new CA" do
+      new_cacert = Puppet::SSL::Certificate.indirection.find("ca")
+      expect(new_cacert.content.issuer.to_a[0][1]).to eq 'Puppet CA: foo'
+    end
+
+    it "matches the Authority Key Identifier field with the old CA and new CA" do
+      new_cacert = Puppet::SSL::Certificate.indirection.find("ca")
+      aki = new_cacert.content.extensions.find { |ext| ext.oid == 'authorityKeyIdentifier' }
+      expect(aki.value).to match(/Puppet CA: foo/)
+    end
+
+    it 'copies the cacert to the localcacert' do
+      described_class.regenerate(Puppet::SSL::CertificateAuthority.new)
+      cacert = Puppet::SSL::Certificate.from_instance(
+                                       OpenSSL::X509::Certificate.new(File.read(Puppet[:cacert])))
+      localcacert = Puppet::SSL::Certificate.from_instance(
+                                       OpenSSL::X509::Certificate.new(File.read(Puppet[:localcacert])))
+      expect(cacert.content.serial).to eq localcacert.content.serial
+    end
+  end
+end
diff --git a/3rdparty/modules/certregen/spec/integration/puppet_x/certregen/certificate_spec.rb b/3rdparty/modules/certregen/spec/integration/puppet_x/certregen/certificate_spec.rb
new file mode 100644 (file)
index 0000000..e60a11b
--- /dev/null
@@ -0,0 +1,92 @@
+require 'spec_helper'
+require 'puppet_x/certregen/certificate'
+
+RSpec.describe PuppetX::Certregen::Certificate do
+  include_context "Initialize CA"
+
+  let(:ok_certificate) do
+    Puppet::SSL::CertificateAuthority.new.generate("ok")
+  end
+
+  let(:expired_certificate) do
+    one_year = 60 * 60 * 24 * 365
+    not_before = Time.now - one_year * 6
+    not_after = Time.now - one_year
+    make_certificate("expired", not_before, not_after)
+  end
+
+  let(:expiring_certificate) do
+    not_before = Time.now - (60 * 60 * 24 * 365 * 4)
+    not_after = Time.now + (60 * 60 * 24 * 30)
+    make_certificate("expiring", not_before, not_after)
+  end
+
+  let(:short_lived_certificate) do
+    not_before = Time.now - 86400
+    not_after = Time.now + (60 * 5)
+    make_certificate("expiring", not_before, not_after)
+  end
+
+  describe "#expiring?" do
+    it "is false for nodes outside of the expiration window" do
+      expect(described_class.expiring?(ok_certificate)).to eq(false)
+    end
+
+    it "is true for newly generated short lived certificates" do
+      expect(described_class.expiring?(short_lived_certificate)).to eq(false)
+    end
+
+    it "is true for expired nodes" do
+      expect(described_class.expiring?(expired_certificate)).to eq(true)
+    end
+
+    it "is true for nodes within the expiration window" do
+      expect(described_class.expiring?(expiring_certificate)).to eq(true)
+    end
+  end
+
+  describe '#expiry' do
+    describe "with an expired cert" do
+      subject { described_class.expiry(expired_certificate) }
+      it "has a status of expired" do
+        expect(subject[:status]).to eq :expired
+      end
+
+      it "includes the not after date" do
+        expect(subject[:expiration_date]).to eq expired_certificate.content.not_after
+      end
+    end
+
+    describe "with an expiring cert" do
+      subject { described_class.expiry(expiring_certificate) }
+
+      it "has a status of expiring" do
+        expect(subject[:status]).to eq :expiring
+      end
+
+      it "includes the not after date" do
+        expect(subject[:expiration_date]).to eq expiring_certificate.content.not_after
+      end
+
+      it "includes the time till expiration" do
+        expect(subject[:expires_in]).to match(/29 days, 23 hours, 59 minutes/)
+      end
+    end
+
+    describe "with an ok cert" do
+      subject { described_class.expiry(ok_certificate) }
+
+      it "has a status of ok" do
+        expect(subject[:status]).to eq :ok
+      end
+
+      it "includes the not after date" do
+        expect(subject[:expiration_date]).to eq ok_certificate.content.not_after
+      end
+
+      it "includes the time till expiration" do
+        expect(subject[:expires_in]).to match(/4 years, 364 days, 23 hours, 59 minutes/)
+      end
+    end
+  end
+end
diff --git a/3rdparty/modules/certregen/spec/integration/puppet_x/crl_spec.rb b/3rdparty/modules/certregen/spec/integration/puppet_x/crl_spec.rb
new file mode 100644 (file)
index 0000000..3d50cfc
--- /dev/null
@@ -0,0 +1,54 @@
+require 'spec_helper'
+require 'puppet_x/certregen/crl'
+
+RSpec.describe PuppetX::Certregen::CRL do
+  include_context "Initialize CA"
+
+  describe '.refresh' do
+    def normalize_time(t)
+      t.utc.round
+    end
+
+    let(:stub_time) { normalize_time(Time.now + 60 * 60 * 24 * 365) }
+    let(:oldcrl) { @oldcrl }
+
+    before do
+      @oldcrl = Puppet::SSL::CertificateRevocationList.indirection.find("ca")
+      allow(Time).to receive(:now).and_return stub_time
+      described_class.refresh(Puppet::SSL::CertificateAuthority.new)
+    end
+
+    subject { Puppet::SSL::CertificateRevocationList.indirection.find('ca') }
+
+    it 'updates the lastUpdate field' do
+      last_update = normalize_time(subject.content.last_update.utc)
+      expect(last_update).to eq normalize_time(stub_time - 1)
+    end
+
+    it 'updates the nextUpdate field' do
+      next_update = normalize_time(subject.content.next_update.utc)
+      expect(next_update).to eq normalize_time(stub_time + described_class::FIVE_YEARS)
+    end
+
+    def crl_number(crl)
+      crl.content.extensions.find { |ext| ext.oid == 'crlNumber' }.value
+    end
+
+    it "increments the CRL number" do
+      newcrl = Puppet::SSL::CertificateRevocationList.from_instance(
+        OpenSSL::X509::CRL.new(File.read(Puppet[:cacrl])), 'ca')
+
+      old_crl_number = crl_number(oldcrl).to_i
+      new_crl_number = crl_number(newcrl).to_i
+      expect(new_crl_number).to eq old_crl_number + 1
+    end
+
+    it 'copies the cacrl to the hostcrl' do
+      cacrl = Puppet::SSL::CertificateRevocationList.from_instance(
+                               OpenSSL::X509::CRL.new(File.read(Puppet[:cacrl])), 'ca')
+      hostcrl = Puppet::SSL::CertificateRevocationList.from_instance(
+                               OpenSSL::X509::CRL.new(File.read(Puppet[:hostcrl])), 'ca')
+      expect(crl_number(cacrl)).to eq crl_number(hostcrl)
+    end
+  end
+end
diff --git a/3rdparty/modules/certregen/spec/spec_helper.rb b/3rdparty/modules/certregen/spec/spec_helper.rb
new file mode 100644 (file)
index 0000000..9ae37b1
--- /dev/null
@@ -0,0 +1,16 @@
+#This file is generated by ModuleSync, do not edit.
+require 'puppetlabs_spec_helper/module_spec_helper'
+
+if Puppet.version.to_f >= 4.5
+  RSpec.configure do |c|
+    c.before :each do
+      Puppet.settings[:strict] = :error
+    end
+  end
+end
+
+# put local configuration and setup into spec_helper_local
+begin
+  require 'spec_helper_local'
+rescue LoadError
+end
diff --git a/3rdparty/modules/certregen/spec/spec_helper_acceptance.rb b/3rdparty/modules/certregen/spec/spec_helper_acceptance.rb
new file mode 100644 (file)
index 0000000..18c4fe4
--- /dev/null
@@ -0,0 +1,17 @@
+require 'beaker-rspec'
+require 'beaker/puppet_install_helper'
+require 'beaker/module_install_helper'
+require 'acceptance/helpers'
+
+run_puppet_install_helper
+install_ca_certs unless ENV['PUPPET_INSTALL_TYPE'] =~ /pe/i
+hosts.each do |host|
+  install_module_on(host)
+end
+install_module_dependencies_on(hosts)
+
+RSpec.configure do |c|
+  # Readable test descriptions
+  c.formatter = :documentation
+end
+
diff --git a/3rdparty/modules/certregen/spec/spec_helper_local.rb b/3rdparty/modules/certregen/spec/spec_helper_local.rb
new file mode 100644 (file)
index 0000000..3dfb8aa
--- /dev/null
@@ -0,0 +1,52 @@
+RSpec.configure do |c|
+  c.include PuppetlabsSpec::Files
+  c.mock_with :rspec
+
+  c.before(:each) do
+    # Suppress cert fingerprint logging
+    allow_any_instance_of(Puppet::SSL::CertificateAuthority).to receive(:puts)
+
+    # remove the stub that causes puppet to believe it is
+    # always being run as root.
+    # See https://github.com/puppetlabs/puppetlabs_spec_helper/blob/master/lib/puppetlabs_spec_helper/module_spec_helper.rb#L29
+    Puppet.features.unstub(:root?)
+
+    Puppet[:vardir] = tmpdir('var')
+    Puppet[:confdir] = tmpdir('conf')
+  end
+
+  def backdate_certificate(ca, cert, not_before, not_after)
+    cert.content.not_before = not_before
+    cert.content.not_after = not_after
+    signer = Puppet::SSL::CertificateSigner.new
+    signer.sign(cert.content, ca.host.key.content)
+    cert
+  end
+
+  def make_certificate(name, not_before, not_after)
+    ca = Puppet::SSL::CertificateAuthority.new
+    cert = ca.generate(name)
+    backdate_certificate(ca, cert, not_before, not_after)
+  end
+end
+
+RSpec.shared_context "Initialize CA" do
+  # PKI generation is done by initializing a CertificateAuthority object, which has the effect of
+  # applying the settings catalog, generating a RSA keypair, and generating a CA certificate.
+  # Since we're regenerating the CA state between each test we need to create a new
+  # CertificateAuthority object instead of using CertificateAuthority.instance, since that will
+  # memoize a single instance and will not generate the ca folder structure and PKI files.
+  def generate_pki
+    Puppet::SSL::CertificateAuthority.new
+  end
+
+  before(:each) do
+    Puppet::SSL::Host.ca_location = :only
+    Puppet.settings.preferred_run_mode = "master"
+
+    Puppet[:ca] = true
+    Puppet[:ca_name] = 'Puppet CA: foo'
+
+    generate_pki
+  end
+end