diff options
author | kwadronaut <kwadronaut@leap.se> | 2016-07-25 00:44:22 +0200 |
---|---|---|
committer | kwadronaut <kwadronaut@leap.se> | 2016-07-25 00:44:22 +0200 |
commit | 30bc1e889dd0042132c4da21b94780c5a530b67c (patch) | |
tree | e430d45f553364c232626961df1647052166169d | |
parent | c0d2832dff7fb14e056a49b28860087b2f201619 (diff) | |
parent | 8c1aac4f23d245cda54994737c72a868d112db87 (diff) |
-rw-r--r-- | .gitignore | 53 | ||||
-rw-r--r-- | .travis.yml | 18 | ||||
-rw-r--r-- | Gemfile | 35 | ||||
-rw-r--r-- | README.md | 170 | ||||
-rw-r--r-- | Rakefile | 62 | ||||
-rw-r--r-- | lib/puppet/parser/functions/trocla.rb | 33 | ||||
-rw-r--r-- | lib/puppet/parser/functions/trocla_get.rb | 38 | ||||
-rw-r--r-- | lib/puppet/parser/functions/trocla_set.rb | 63 | ||||
-rw-r--r-- | lib/puppet/util/trocla_helper.rb | 43 | ||||
-rw-r--r-- | manifests/ca/params.pp | 11 | ||||
-rw-r--r-- | manifests/config.pp | 61 | ||||
-rw-r--r-- | manifests/master.pp | 22 | ||||
-rw-r--r-- | manifests/master/hiera.pp | 6 | ||||
-rw-r--r-- | manifests/params.pp | 6 | ||||
-rw-r--r-- | manifests/yaml.pp | 41 | ||||
-rw-r--r-- | metadata.json | 14 | ||||
-rw-r--r-- | spec/classes/ca_params_spec.rb | 8 | ||||
-rw-r--r-- | spec/classes/config_spec.rb | 114 | ||||
-rw-r--r-- | spec/classes/master_hiera_spec.rb | 11 | ||||
-rw-r--r-- | spec/classes/master_spec.rb | 52 | ||||
-rw-r--r-- | spec/classes/params_spec.pp | 8 | ||||
-rw-r--r-- | spec/classes/yaml_spec.rb | 47 | ||||
-rw-r--r-- | spec/spec_helper.rb | 34 | ||||
-rw-r--r-- | templates/troclarc.yaml.erb | 44 |
24 files changed, 745 insertions, 249 deletions
@@ -1,48 +1,5 @@ -# rcov generated -coverage - -# rdoc generated -rdoc - -# yard generated -doc -.yardoc - -# bundler -.bundle - -# jeweler generated -pkg - -# Have editor/IDE/OS specific files you need to ignore? Consider using a global gitignore: -# -# * Create a file at ~/.gitignore -# * Include files you want ignored -# * Run: git config --global core.excludesfile ~/.gitignore -# -# After doing this, these files will be ignored in all your git projects, -# saving you from having to 'pollute' every project you touch with them -# -# Not sure what to needs to be ignored for particular editors/OSes? Here's some ideas to get you started. (Remember, remove the leading # of the line) -# -# For MacOS: -# -#.DS_Store - -# For TextMate -#*.tmproj -#tmtags - -# For emacs: -#*~ -#\#* -#.\#* - -# For vim: -#*.swp - -# For redcar: -#.redcar - -# For rubinius: -#*.rbc +spec/fixtures +.librarian +.tmp +*.lock +pkg/ diff --git a/.travis.yml b/.travis.yml index 8fc17b4..1ac8b2f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,16 @@ -language: ruby rvm: - - 2.1.0 - - 2.0.0 - - 1.9.3 - 1.8.7 + - 1.9.3 + - 2.2.0 +script: 'bundle exec rake' +env: + - PUPPET_VERSION=3.8.6 + - PUPPET_VERSION=4.4.1 +matrix: + exclude: + # No support for Ruby 2.0 before Puppet 3.2 + - rvm: 2.2.0 + env: PUPPET_VERSION=3.8.6 + - rvm: 1.8.7 + env: PUPPET_VERSION=4.4.1 + @@ -1,17 +1,24 @@ -source "http://rubygems.org" -# Add dependencies required to use your gem here. -# Example: -# gem "activesupport", ">= 2.3.5" +source 'https://rubygems.org' -gem "moneta", "~> 0.7" -gem "highline" -gem "bcrypt" +if ENV.key?('PUPPET_VERSION') + puppetversion = "~> #{ENV['PUPPET_VERSION']}" +else + puppetversion = ['>= 3.8.6'] +end -# Add dependencies to develop your gem here. -# Include everything needed to run rake, tests, features, etc. -group :development do - gem "rspec", "~> 2.4" - gem "rdoc", "~> 3.8" - gem "mocha" - gem "jeweler" +if RUBY_VERSION == '1.8.7' + puppetversion = ['~> 3.8.6'] + gem 'i18n', '~> 0.6.11' + gem 'activesupport', '~> 3.2' + gem 'highline', '~> 1.6.21' + gem 'librarian-puppet', '~> 1.5.0' + gem 'rspec', '~> 3.1.0' + gem 'rake', '< 11' +else + gem 'librarian-puppet' + gem 'rake' end + +gem 'puppet', puppetversion +gem 'puppet-lint' +gem 'puppetlabs_spec_helper' @@ -1,152 +1,98 @@ # trocla -[![Build Status](https://travis-ci.org/duritong/trocla.png)](https://travis-ci.org/duritong/trocla) -Trocla provides you a simple way to create and store (random) passwords on a -central server, which can be retrieved by other applications. An example for -such an application is puppet and trocla can help you to not store any -plaintext or hashed passwords in your manifests by keeping these passwords only -on your puppetmaster. +[![Build Status](https://travis-ci.org/duritong/puppet-trocla.png)](https://travis-ci.org/duritong/puppet-trocla) -Furthermore it provides you a simple cli that helps you to modify the password -storage from the cli. +This is the puppet module to manage a trocla installation on the puppet +master. It also, provides the necessary function to query trocla from puppet. -Trocla does not only create and/or store a plain password, it is also able to -generate (and store) any kind hashed passwords based on the plain password. -As long as the plain password is preset, trocla is able to generate any kind -of hashed passwords through an easy extendible plugin system. +To get a quick start you might be interested in using the `trocla::yaml` class +on your master. This will install trocla and setup it using the default YAML +storage backend for your master. There is no need to configure anything on the +clients if you do not want to use trocla on the clients itself. -It is not necessary to store the plain password on the server, you can also -just feed trocla with the hashed password and use that in your other tools. -A common example for that is that you let puppet retrieve (and hence create) -a salted md5 password for a user. This will then store the salted md5 of -a random password AND the plain text password in trocla. Later you can -retrieve (by deleting) the plain password and send it to the user. Puppet -will still simply retrieve the hashed password that is stored in trocla, -while the plain password is not anymore stored on the server. +If you want to do your own very custom setup, you should look into the other +classes. -You can use any kind of key/value based storage supported by moneta for -trocla. By default it uses a simple yaml file. +## Compatibility -## Usage +* Version 0.2.2 of this module is for version 0.2.2 of trocla. -### create +## Functions -Assuming that we have an empty trocla storage. +### trocla - trocla create user1 plain -This will create (if not already stored in trocla) a random password and -store its plain text under key user1. The password will also be returned -by trocla. +Usage: - trocla create user2 mysql + trocla(KEY, FORMAT, [optional options]) -This will create a random password and store its plain and mysql-style hashed -sha1 password in trocla. The hashed password is returned. +This is the main function you will use. This is similar to a - trocla create user1 mysql + trocla create foo FORMAT -This will take the already stored plain text password of key user1 and generate -and store the mysql-style hashed sha1 password. +on the cli. This means, that *if* a password for this key and format +exists, it will return this one, otherwise will create one automatically +and return the generated password. So you might want to do something like: -It is possible that certain hash formats require additional options. For example -the pgsql hash requires also the user to create the md5 hash for the password. -You can pass these additional requirements as yaml-based strings to the format: + user{'foobar': + password => trocla('user_foobar','plain') + } - trocla create user1 pgsql 'username: user1' +If you want to pass down encrypted passwords, you might use: -This will create a pgsql password hash using the username user1. -### get + user{'foobar': + password => trocla('user_foobar','sha512crypt') + } -Get simply returns a stored password. It will not create a new password. +As descriped further in trocla's docs. -Assuming that we are still working with the same storage +The optional options, can be used to pass options to the format, like +overriding the default length for passwords that are being created: - trocla get user2 plain + user{'foobar': + password => trocla('user_foobar','sha512crypt','length: 32') + } -will return the plain text password of the key user2. +### trocla_get - trocla get user3 plain +Usage: -This will return nothing, as no password with this format have been stored so -far. + trocla_get(KEY, FORMAT) -### set +This will return the value of the passed key and format. If nothing is +found an error will be raised. This is interesting if you want do not +want to autogenerate a password and rather be sure that it's already +existing in trocla's database. - trocla set user3 plain +### trocla_set -This will ask you for a password and set it under the appropriate key/format. +Usage: - trocla set --password mysupersecretpassword user4 plain + trocla_set(KEY, FORMAT,PASSWORD) -This will take the password from the cli without asking you. +This will set the passed password for the key/format pair and return it +as well. This is mainly interesting if you want to migrate existing manifests +with plenty of passwords in it to trocla. - trocla set user5 mysql -p *ABC.... +## Other classes -This will store a mysql sha1 hash for the key user5, without storing any kind -of plain text password. +### trocla::config -You can also pipe in a password: +This is a class that manages a trocla configuration. You might use this +one if you do not use the default yaml setup. - echo -n foo | trocla set user6 plain -p +### trocla::master -or a file +This class manages the installation of trocla itself. It will not configure +trocla, it will just install the necessary packages. - cat some_file | trocla set user6 plain -p - trocla set user6 plain -p < some_file +## Hiera backend -### reset +Trocla can also be integrated into [Hiera](https://docs.puppetlabs.com/hiera/) by using ZeroPointEnergy's [hiera-backend](https://github.com/ZeroPointEnergy/hiera-backend-trocla). - trocla reset user1 md5crypt +Simply `include trocla::master::hiera` to make that backend available. -This will recreate the salted md5 shadow-style hash. However, it will not create -a new plain text passwords. Hence, this is mainly usefull to create new hashed -passwords based on new salts. - -If the plain password of a key is resetted, every already hashed password is -deleted as well, as the hashes wouldn't match anymore the plain text password. - -### delete - - trocla delete user1 plain - -This will delete the plain password of the key user1 and return it. - -## Attention - -If you don't feed trocla initially with a hash and/or delete the generated -plain text passwords trocla will likely create a lot of plain text passwords -and store them on your machine/server. This is by intend and is all about which -problems (mainly passwords in configuration management manifests) trocla tries -to address. - -## Installation - -Simply build and install the gem. - -## Update & Changes - -### to 0.0.8 - -1. be sure to update as well the moneta gem, trocla now uses the official moneta releases and supports current avaiable versions. -1. Options for moneta's backends have changed. For example, if you are using the yaml-backend you will likely need to change the adapter option `:path:` to `:file:` to match moneta's new API. -1. **IMPORTANT:** If you are using the yaml backend you need to migrate the current data *before* using the new trocla version! You can migrate the datastore by using the following two sed commands: `sed -i 's/^\s\{3\}/ /' /PATH/TO/trocla_data.yaml` && `sed -i '/^\s\{2\}value\:/d' /PATH/TO/trocla_data.yaml`. -1. **SECURITY:** Previous versions of trocla used quite a simple random generator. Especially in combination with the puppet `fqdn_rand` function, you likely have very predictable random passwords and I recommend you to regenerate all randomly generated passwords! Now! -1. We now support reading passwords from files, which means that you can now also easily add multi-line passwords. Have a look at the documentation above. - -## Contributing to trocla - -* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet -* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it -* Fork the project -* Start a feature/bugfix branch -* Commit and push until you are happy with your contribution -* Make sure to add tests for it. This is important so I don't break it in a future version unintentionally. -* Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it. - -## Copyright - -Copyright (c) 2011 mh. See LICENSE.txt for -further details. +## Moar +RTFC and for more information about trocla visit: https://github.com/duritong/trocla @@ -1,53 +1,23 @@ -# encoding: utf-8 - -require 'rubygems' require 'bundler' -begin - Bundler.setup(:default, :development) -rescue Bundler::BundlerError => e - $stderr.puts e.message - $stderr.puts "Run `bundle install` to install missing gems" - exit e.status_code -end -require 'rake' - +Bundler.require(:rake) -require 'jeweler' -$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), 'lib')) -require 'trocla' -Jeweler::Tasks.new do |gem| - # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options - gem.name = "trocla" - gem.homepage = "https://tech.immerda.ch/2011/12/trocla-get-hashed-passwords-out-of-puppet-manifests/" - gem.license = "GPLv3" - gem.summary = "Trocla a simple password generator and storage" - gem.description = "Trocla helps you to generate random passwords and to store them in various formats (plain, MD5, bcrypt) for later retrival." - gem.email = "mh+trocla@immerda.ch" - gem.authors = ["mh"] - gem.version = Trocla::VERSION::STRING - # dependencies defined in Gemfile -end -Jeweler::RubygemsDotOrgTasks.new +require 'puppetlabs_spec_helper/rake_tasks' +require 'puppet-lint/tasks/puppet-lint' -require 'rspec/core' -require 'rspec/core/rake_task' -RSpec::Core::RakeTask.new(:spec) do |spec| - spec.pattern = FileList['spec/**/*_spec.rb'] -end +PuppetLint.configuration.ignore_paths = ["spec/**/*.pp", "vendor/**/*.pp"] +PuppetLint.configuration.log_format = '%{path}:%{linenumber}:%{KIND}: %{message}' +PuppetLint.configuration.send("disable_class_inherits_from_params_class") -RSpec::Core::RakeTask.new(:rcov) do |spec| - spec.pattern = 'spec/**/*_spec.rb' - spec.rcov = true +# use librarian-puppet to manage fixtures instead of .fixtures.yml +# offers more possibilities like explicit version management, forge downloads,... +task :librarian_spec_prep do + sh "librarian-puppet install --path=spec/fixtures/modules/" + pwd = `pwd`.strip + unless File.directory?("#{pwd}/spec/fixtures/modules/trocla") + sh "ln -s #{pwd} #{pwd}/spec/fixtures/modules/trocla" + end end +task :spec_prep => :librarian_spec_prep -task :default => :spec -gem 'rdoc' -require 'rdoc/task' -RDoc::Task.new do |rdoc| - version = Trocla::VERSION::STRING - rdoc.rdoc_dir = 'rdoc' - rdoc.title = "trocla #{version}" - rdoc.rdoc_files.include('README*') - rdoc.rdoc_files.include('lib/**/*.rb') -end +task :default => [:spec, :lint] diff --git a/lib/puppet/parser/functions/trocla.rb b/lib/puppet/parser/functions/trocla.rb new file mode 100644 index 0000000..b1a7b61 --- /dev/null +++ b/lib/puppet/parser/functions/trocla.rb @@ -0,0 +1,33 @@ +module Puppet::Parser::Functions + newfunction(:trocla, :type => :rvalue, :doc => " +This will create or get a random password from the trocla storage. + +Usage: + + $password_user1 = trocla(key,[format='plain'[,options={}]]) + +Means: + + $password_user1 = trocla('user1') + +Create or get the plain text password for the key 'user1' + + $password_user2 = trocla('user2','mysql') + +Create or get the mysql style sha1 hashed password. + + $options_user3 = { 'username' => 'user3' } # Due to a puppet bug + # this needs to be assigned + # like that. + $password_user3 = trocla('user3','pgsql', $options_user3) + +Options can also be passed as a yaml string: + + $password_user3 = trocla('user3','pgsql', \"username: 'user3'\") + " + ) do |*args| + require File.dirname(__FILE__) + '/../../util/trocla_helper' + + Puppet::Util::TroclaHelper.trocla(:password,true,*args) + end +end diff --git a/lib/puppet/parser/functions/trocla_get.rb b/lib/puppet/parser/functions/trocla_get.rb new file mode 100644 index 0000000..fb5cd5a --- /dev/null +++ b/lib/puppet/parser/functions/trocla_get.rb @@ -0,0 +1,38 @@ +module Puppet::Parser::Functions + newfunction(:trocla_get, :type => :rvalue, :doc => " + This will only get an already stored password from the trocla storage. + +Usage: + + $password_user1 = trocla_get(key,[format='plain'[,raise_error=true]]) + +Means: + + $password_user1 = trocla('user1') + +Get the plain text password for the key 'user1' + + $password_user2 = trocla_get('user2','mysql') + +Get the mysql style sha1 hashed password. + +By default puppet will raise a parse error if the password haven't yet been +stored in trocla. This can be turned off by setting false as a third argument: + + $password_user3 = trocla_get('user2','mysql',false) + +the return value will be undef if the key & format pair is not found. +" + ) do |*args| + if args[0].is_a?(Array) + args = args[0] + end + require File.dirname(__FILE__) + '/../../util/trocla_helper' + args[1] ||= 'plain' + raise_error = args[2].nil? ? true : args[2] + if (answer=Puppet::Util::TroclaHelper.trocla(:get_password,false,[args[0],args[1]])).nil? && raise_error + raise(Puppet::ParseError, "No password for key,format #{args[0..1].flatten.inspect} found!") + end + answer.nil? ? :undef : answer + end +end diff --git a/lib/puppet/parser/functions/trocla_set.rb b/lib/puppet/parser/functions/trocla_set.rb new file mode 100644 index 0000000..06da5ae --- /dev/null +++ b/lib/puppet/parser/functions/trocla_set.rb @@ -0,0 +1,63 @@ +module Puppet::Parser::Functions + newfunction(:trocla_set, :type => :rvalue, :doc => " + This will set a password/hash in the local storage and return itself, + or hashed in another format, if the password is present in plaintext or + in that specific hash format. + + This function is mainly useful to migrate from hashes in manifests to trocla only manifests. + +Usage: + + $password_user1 = trocla_set(key,value,[format='plain',[return_format,[options={}]]]) + +Means: + + $password_user1 = trocla_set('user1','mysecret') + +Will set and return 'mysecret' as plain password. + + $password_user2 = trocla_set('user2','*AAA...','mysql') + +Will set and return the sha1 hashed mysql password for the key user2. + + $password_user3 = trocla_set('user3','mysecret','plain','sha512crypt') + +Will set 'mysecret' as plain password, but return a newly created sha512crypt hash. + + $postgres_user4 = { username => 'user4' } + $password_user4 = trocla_set('user4','mysecret','plain','pgsql',$postgres_user4) + +Will set the plain password 'mysecret' and return a pgsql md5 hash for user5. + + $password_user2 = trocla_set('user2','*AAA...','mysql','sha512crypt') + +This will likely fail, except if you add the plain password or the sha512crypt hash manually to +trocla, for example via cli. +" +) do |*args| + if args[0].is_a?(Array) + args = args[0] + end + + key = args[0] + value = args[1] + raise(Puppet::ParseError, "You need to pass at least key & value as an argument!") if key.nil? || value.nil? + + format = args[2] || 'plain' + return_format = args[3] || format + options = args[4] || {} + + configfile = File.join(File.dirname(Puppet.settings[:config]), "troclarc.yaml") + + raise(Puppet::ParseError, "Trocla config file #{configfile} not readable") unless File.exist?(configfile) + + require 'trocla' + + result = (trocla=Trocla.new(configfile)).set_password(key,format,value) + if format != return_format && (result = trocla.get_password(key,return_format)).nil? + raise(Puppet::ParseError, "Plaintext password is not present, but required to return password in format #{return_format}") if (return_format == 'plain') || trocla.get_password(key,'plain').nil? + result = trocla.password(key,return_format,options) + end + result + end +end diff --git a/lib/puppet/util/trocla_helper.rb b/lib/puppet/util/trocla_helper.rb new file mode 100644 index 0000000..ce583f5 --- /dev/null +++ b/lib/puppet/util/trocla_helper.rb @@ -0,0 +1,43 @@ +module Puppet::Util::TroclaHelper + def trocla(trocla_func,has_options,*args) + # Functions called from puppet manifests that look like this: + # lookup("foo", "bar") + # internally in puppet are invoked: func(["foo", "bar"]) + # + # where as calling from templates should work like this: + # scope.function_lookup("foo", "bar") + # + # Therefore, declare this function with args '*args' to accept any number + # of arguments and deal with puppet's special calling mechanism now: + if args[0].is_a?(Array) + args = args[0] + end + + key = args[0] || raise(Puppet::ParseError, "You need to pass at least a key as an argument!") + format = args[1] || 'plain' + options = args[2] || {} + + if options.is_a?(String) + require 'yaml' + options = YAML.load(options) + end + + has_options ? store.send(trocla_func, key, format, options) : store.send(trocla_func, key, format) + end + module_function :trocla + + private + + def store + @store ||= begin + require 'trocla' + configfile = File.join(File.dirname(Puppet.settings[:config]), "troclarc.yaml") + + raise(Puppet::ParseError, "Trocla config file #{configfile} is not readable") unless File.exist?(configfile) + + Trocla.new(configfile) + end + end + module_function :store + +end diff --git a/manifests/ca/params.pp b/manifests/ca/params.pp new file mode 100644 index 0000000..437e990 --- /dev/null +++ b/manifests/ca/params.pp @@ -0,0 +1,11 @@ +# input for a ca from trocla, so that you need only +# +# trocla('some_ca','x509',$trocla::ca::params::ca_options) +class trocla::ca::params( + $trocla_options = { + 'profiles' => ['sysdomain_nc','x509veryverylong'], + 'CN' => "automated-ca ${name} for ${::domain}", + }, +) { + $ca_options = merge($trocla_options,{ become_ca => true, render => { certonly => true }}) +} diff --git a/manifests/config.pp b/manifests/config.pp new file mode 100644 index 0000000..8c52db7 --- /dev/null +++ b/manifests/config.pp @@ -0,0 +1,61 @@ +#Installs configuration files for the trocla agent/CLI +# +#Options +# [*options*] Options for trocla. Default: empty hash. +# [*profiles*] Profiles for trocla. Default: empty hash. +# [*x509_profile_domain_constraint*] +# A profile for x509 name constraint that matches +# the own domain by default. +# This will add a profile for x509 certs with the +# option 'name_constraints' set to this array of +# domains. +# [*store*] Defines the store to be used for trocla. By default +# it's not set, meaning trocla's default (moneta) will +# be used. +# [*store_options*] This will contain a hash of the options to pass the +# trocla store configuration. +# [*encryption*] Defines the encryption method for password stored in +# the backend. By default it's not set, meaning trocla's +# default (none) will be used. +# [*encryption_options*] This will contain a hash of the options for the +# encryption. Default: empty Hash +# [*manage_dependencies*] Whether to manage the dependencies or not. +# Default *true* +class trocla::config ( + $options = {}, + $profiles = {}, + $x509_profile_domain_constraints = [$::domain], + $store = undef, + $store_options = {}, + $encryption = undef, + $encryption_options = {}, + $manage_dependencies = true, +) { + include ::trocla::params + if $manage_dependencies { + require ::trocla::master + } + + if empty($x509_profile_domain_constraints) { + $merged_profiles = $profiles + } else { + $default_profiles = { + "${trocla::params::sysdomain_profile_name}" => { + name_constraints => $x509_profile_domain_constraints + } + } + $merged_profiles = merge($default_profiles,$profiles) + } + + # Deploy default config file and link it for trocla cli lookup + file{ + "${settings::confdir}/troclarc.yaml": + content => template('trocla/troclarc.yaml.erb'), + owner => root, + group => puppet, + mode => '0640'; + '/etc/troclarc.yaml': + ensure => link, + target => "${settings::confdir}/troclarc.yaml"; + } +} diff --git a/manifests/master.pp b/manifests/master.pp new file mode 100644 index 0000000..43e203d --- /dev/null +++ b/manifests/master.pp @@ -0,0 +1,22 @@ +# Class: trocla::master +# +# This module manages the necessary things for trocla on a master. +# +class trocla::master ( + $provider = 'default', +) { + package {'trocla': + ensure => 'installed', + } + + if $provider != 'default' { + Package['trocla']{ + provider => $provider, + } + } + if $provider != 'gem' and $::osfamily == 'RedHat' { + Package['trocla']{ + name => 'rubygem-trocla' + } + } +} diff --git a/manifests/master/hiera.pp b/manifests/master/hiera.pp new file mode 100644 index 0000000..75b8bb3 --- /dev/null +++ b/manifests/master/hiera.pp @@ -0,0 +1,6 @@ +# manage trocla/hiera integration +class trocla::master::hiera { + package{'rubygem-hiera-backend-trocla': + ensure => present, + } +} diff --git a/manifests/params.pp b/manifests/params.pp new file mode 100644 index 0000000..f99aa2a --- /dev/null +++ b/manifests/params.pp @@ -0,0 +1,6 @@ +# a set of default params for various trocla usages +class trocla::params( + $sysdomain_profile_name = 'sysdomain_nc' +){ + +} diff --git a/manifests/yaml.pp b/manifests/yaml.pp new file mode 100644 index 0000000..7727219 --- /dev/null +++ b/manifests/yaml.pp @@ -0,0 +1,41 @@ +# A class for an eady start with trocla. +# This will install and configure trocla with the +# default yaml storage. +# +# [*data_file*] Where to store the passwords. +# Default: /var/lib/trocla/trocla_data.yaml +# This should be managed using the package. +class trocla::yaml( + $manage_data_dir = true, + $data_file = '/var/lib/trocla/trocla_data.yaml', +) { + + class{'trocla::config': + store => 'moneta', + store_options => { + adapter => 'YAML', + adapter_options => { + file => $data_file, + }, + }, + } + + if $manage_data_dir { + $data_dir = dirname($data_file) + file{$data_dir: + ensure => directory, + owner => puppet, + group => 0, + mode => '0600', + require => Package['trocla']; + } + } + file{ + $data_file: + ensure => file, + owner => puppet, + group => 0, + mode => '0600', + require => Package['trocla']; + } +} diff --git a/metadata.json b/metadata.json new file mode 100644 index 0000000..ae33a84 --- /dev/null +++ b/metadata.json @@ -0,0 +1,14 @@ +{ + "name": "duritong-trocla", + "version": "1.0.1", + "author": "duritong", + "summary": "This modules allows you to use trocla (https://github.com/duritong/trocla) from puppet.", + "license": "GPLv2", + "source": "https://github.com/duritong/puppet-trocla", + "project_page": "https://github.com/duritong/puppet-trocla", + "issues_url": "https://github.com/duritong/puppet-trocla/issues", + "description": "This modules allows you use trocla lookups as puppet functions.", + "dependencies": [ + {"name":"puppetlabs/stdlib","version_requirement":">= 3.2.0"} + ] +} diff --git a/spec/classes/ca_params_spec.rb b/spec/classes/ca_params_spec.rb new file mode 100644 index 0000000..5277972 --- /dev/null +++ b/spec/classes/ca_params_spec.rb @@ -0,0 +1,8 @@ +require File.expand_path(File.join(File.dirname(__FILE__),'../spec_helper')) + +describe 'trocla::ca::params', :type => 'class' do + context 'with default params' do + it { should compile.with_all_deps } + end +end + diff --git a/spec/classes/config_spec.rb b/spec/classes/config_spec.rb new file mode 100644 index 0000000..fc0a33a --- /dev/null +++ b/spec/classes/config_spec.rb @@ -0,0 +1,114 @@ +require File.expand_path(File.join(File.dirname(__FILE__),'../spec_helper')) + +describe 'trocla::config', :type => 'class' do + let(:facts){ + { + :domain => 'example.com', + } + } + context 'with default params' do + it { should contain_class('trocla::params') } + it { should contain_class('trocla::master') } + it { should contain_file('/etc/puppet/troclarc.yaml').with( + :owner => 'root', + :group => 'puppet', + :mode => '0640' + )} + it { should contain_file('/etc/puppet/troclarc.yaml').with_content("--- +profiles: + sysdomain_nc: + name_constraints: + - example.com +") } + it { should contain_file('/etc/troclarc.yaml').with( + :ensure => 'link', + :target => '/etc/puppet/troclarc.yaml' + )} + + it { should compile.with_all_deps } + end + + context 'with other params' do + let(:params) { + { + :options => { + 'length' => 24, + 'profiles' => 'mydefaultprofile', + 'random' => false, + 'expires' => 60*60*24, #1day + }, + :profiles => { + 'mydefaultprofile' => { + 'length' => 20, + }, + 'anotherprofile' => { + 'random' => true, + 'expires' => false, + }, + }, + :x509_profile_domain_constraints => ['domain1.com','domain2.com'], + :store => 'moneta', + :store_options => { + 'adapter' => 'Sequel', + 'adapter_options' => { + 'db' => 'mysql://db.server.name', + 'user' => 'trocla', + 'password' => 'secret_password', + 'database' => 'trocladb', + 'table' => 'trocla', + }, + }, + :encryption => 'ssl', + :encryption_options => { + 'private_key' => '/var/lib/puppet/ssl/private_keys/trocla.pem', + 'public_key' => '/var/lib/puppet/ssl/public_keys/trocla.pem', + }, + :manage_dependencies => false, + } + } + it { should contain_class('trocla::params') } + it { should_not contain_class('trocla::master') } + it { should contain_file('/etc/puppet/troclarc.yaml').with( + :owner => 'root', + :group => 'puppet', + :mode => '0640' + )} + it { should contain_file('/etc/puppet/troclarc.yaml').with_content("--- +encryption: :ssl +encryption_options: + :private_key: /var/lib/puppet/ssl/private_keys/trocla.pem + :public_key: /var/lib/puppet/ssl/public_keys/trocla.pem +options: + expires: 86400 + length: 24 + profiles: mydefaultprofile + random: false +profiles: + anotherprofile: + expires: false + random: true + mydefaultprofile: + length: 20 + sysdomain_nc: + name_constraints: + - domain1.com + - domain2.com +store: :moneta +store_options: + adapter: :Sequel + adapter_options: + :database: trocladb + :db: mysql://db.server.name + :password: secret_password + :table: trocla + :user: trocla +") } + it { should contain_file('/etc/troclarc.yaml').with( + :ensure => 'link', + :target => '/etc/puppet/troclarc.yaml' + )} + + it { should compile.with_all_deps } + end +end + diff --git a/spec/classes/master_hiera_spec.rb b/spec/classes/master_hiera_spec.rb new file mode 100644 index 0000000..287abaa --- /dev/null +++ b/spec/classes/master_hiera_spec.rb @@ -0,0 +1,11 @@ +require File.expand_path(File.join(File.dirname(__FILE__),'../spec_helper')) + +describe 'trocla::master::hiera', :type => 'class' do + context 'with default params' do + it { should compile.with_all_deps } + it { should contain_package('rubygem-hiera-backend-trocla').with( + :ensure => 'present' + )} + end +end + diff --git a/spec/classes/master_spec.rb b/spec/classes/master_spec.rb new file mode 100644 index 0000000..ad99c86 --- /dev/null +++ b/spec/classes/master_spec.rb @@ -0,0 +1,52 @@ +require File.expand_path(File.join(File.dirname(__FILE__),'../spec_helper')) + +describe 'trocla::master', :type => 'class' do + context 'with default params' do + context 'on RedHat' do + let(:facts) { + { + :osfamily => 'RedHat', + } + } + it { should contain_package('trocla').with( + :name => 'rubygem-trocla', + :ensure => 'installed' + )} + it { should compile.with_all_deps } + end + context 'on Debian' do + let(:facts) { + { + :osfamily => 'Debian', + } + } + it { should contain_package('trocla').with( + :ensure => 'installed' + )} + it { should compile.with_all_deps } + end + end + context 'with gem provider' do + let(:params){ + { + :provider => 'gem' + } + } + it { should contain_package('trocla').with( + :ensure => 'installed', + :provider => 'gem' + )} + + it { should compile.with_all_deps } + context 'on RedHat' do + it { should contain_package('trocla').with( + :name => 'trocla', + :ensure => 'installed', + :provider => 'gem' + )} + + it { should compile.with_all_deps } + end + end +end + diff --git a/spec/classes/params_spec.pp b/spec/classes/params_spec.pp new file mode 100644 index 0000000..4d05e1f --- /dev/null +++ b/spec/classes/params_spec.pp @@ -0,0 +1,8 @@ +require File.expand_path(File.join(File.dirname(__FILE__),'../spec_helper')) + +describe 'trocla::params', :type => 'class' do + context 'with default params' do + it { should compile.with_all_deps } + end +end + diff --git a/spec/classes/yaml_spec.rb b/spec/classes/yaml_spec.rb new file mode 100644 index 0000000..49d2cb5 --- /dev/null +++ b/spec/classes/yaml_spec.rb @@ -0,0 +1,47 @@ +require File.expand_path(File.join(File.dirname(__FILE__),'../spec_helper')) + +describe 'trocla::yaml', :type => 'class' do + let(:facts){ + { + :domain => 'example.com', + } + } + context 'with default params' do + it { should contain_class('trocla::config').with( + 'store' => 'moneta', + 'store_options' => { + 'adapter' => 'YAML', + 'adapter_options' => { + 'file' => '/var/lib/trocla/trocla_data.yaml', + } + } + )} + it { should contain_file('/etc/puppet/troclarc.yaml').with_content("--- +profiles: + sysdomain_nc: + name_constraints: + - example.com +store: :moneta +store_options: + adapter: :YAML + adapter_options: + :file: /var/lib/trocla/trocla_data.yaml +") } + it { should contain_file('/var/lib/trocla').with( + :ensure => 'directory', + :owner => 'puppet', + :group => 0, + :mode => '0600', + :require => 'Package[trocla]' + )} + it { should contain_file('/var/lib/trocla/trocla_data.yaml').with( + :ensure => 'file', + :owner => 'puppet', + :group => 0, + :mode => '0600', + :require => 'Package[trocla]' + )} + it { should compile.with_all_deps } + end +end + diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 461a858..381f972 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,29 +1,13 @@ -$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib')) -$LOAD_PATH.unshift(File.dirname(__FILE__)) -require 'rspec' -require 'mocha' -require 'trocla' +require 'puppetlabs_spec_helper/module_spec_helper' +require 'rake' -# Requires supporting files with custom matchers and macros, etc, -# in ./support/ and its subdirectories. -Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f} +fixture_path = File.expand_path(File.join(__FILE__, '..', 'fixtures')) -RSpec.configure do |config| - +RSpec.configure do |c| + c.module_path = File.join(fixture_path, 'modules') + c.manifest_dir = File.join(fixture_path, 'manifests') + c.pattern = FileList[c.pattern].exclude(/^spec\/fixtures/) end -def default_config - @default_config ||= YAML.load(File.read(File.expand_path(base_dir+'/lib/trocla/default_config.yaml'))) -end - -def test_config - return @config unless @config.nil? - @config = default_config - @config.delete('adapter_options') - @config['adapter'] = :Memory - @config -end - -def base_dir - File.dirname(__FILE__)+'/../' -end +Puppet::Util::Log.level = :warning +Puppet::Util::Log.newdestination(:console) diff --git a/templates/troclarc.yaml.erb b/templates/troclarc.yaml.erb new file mode 100644 index 0000000..5584fd8 --- /dev/null +++ b/templates/troclarc.yaml.erb @@ -0,0 +1,44 @@ +<% + # stupid but effective sorting of yaml + # forgive me for that, but puppet monkeypatches yaml heavily and breaks it constantly + # for our use case it should be sufficient, otherwise we need to + # extent it to address the new problems + def sort_pseudo_yaml(obj, indent='') + arr = obj.sort {|a,b| (a[0].is_a?(Symbol) ? a[0].to_s : a[0]) <=> (b[0].is_a?(Symbol) ? b[0].to_s : b[0]) } + out = [] + arr.each do |e| + if e[1].is_a?(Hash) + out << "#{indent}#{e[0]}:" + out << sort_pseudo_yaml(e[1],indent+' ') + elsif e[1].is_a?(Array) + out << (["#{indent}#{e[0]}:"]+e[1].collect{|e| "- #{e}" }).join("\n#{indent}") + else + out << "#{indent}#{e[0].is_a?(Symbol) ? ":#{e[0].to_s}" : e[0]}: #{e[1].is_a?(Symbol) ? ":#{e[1].to_s}" : e[1]}" + end + end + out.join("\n") + end + def sym_keys(h) + h.keys.inject({}) do |r,k| + r[k.to_sym] = h[k] + r + end + end + # transform special options so they are understood by the other libraries + so = @store_options.dup + so['adapter'] = so['adapter'].to_sym if so['adapter'] + so['adapter_options'] = sym_keys(so['adapter_options']) if so['adapter_options'] + eo = @encryption_options ? sym_keys(@encryption_options) : {} + options_hash = { + 'store' => @store.nil? ? @store : @store.to_sym, + 'store_options' => so, + 'encryption' => @encryption.nil? ? @encryption : @encryption.to_sym, + 'encryption_options' => eo, + 'options' => @options, + 'profiles' => @merged_profiles, + }.delete_if{|k,v| v.nil? || (v.is_a?(Symbol) ? v.to_s : v).empty? } + output = sort_pseudo_yaml(options_hash) +-%>--- +<% unless output.empty? -%> +<%= output %> +<% end -%> |