autosign 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 3d43769ea221d92c48e517138ec7a3c1ba16ed3a
4
+ data.tar.gz: 0a8454928712134b07dfaa5aa16c4451042086ac
5
+ SHA512:
6
+ metadata.gz: fd1b62abf9b19f6806a356a693c42eb91593ca9cad9bcc79240b7df3d01d1b72bc92c6b8cb14647f92cb92a59179831aa7a90e6d379274b536c977b65a844fd1
7
+ data.tar.gz: 4d5660957738acf9edd0413efab0c1fcc029ecc23ba049d75c26807a7e68f2310d5f5e22054a3c1469ab6e8609ec796ce1956968f6445bfb6e28ff3d990eda74
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source 'https://rubygems.org'
2
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,73 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ autosign (0.0.1)
5
+ deep_merge
6
+ gli (~> 2)
7
+ iniparse (~> 1)
8
+ jwt (~> 1)
9
+ logging
10
+ require_all
11
+
12
+ GEM
13
+ remote: https://rubygems.org/
14
+ specs:
15
+ CFPropertyList (2.2.8)
16
+ aruba (0.6.2)
17
+ childprocess (>= 0.3.6)
18
+ cucumber (>= 1.1.1)
19
+ rspec-expectations (>= 2.7.0)
20
+ builder (3.2.2)
21
+ childprocess (0.5.6)
22
+ ffi (~> 1.0, >= 1.0.11)
23
+ cucumber (2.0.0)
24
+ builder (>= 2.1.2)
25
+ cucumber-core (~> 1.1.3)
26
+ diff-lcs (>= 1.1.3)
27
+ gherkin (~> 2.12)
28
+ multi_json (>= 1.7.5, < 2.0)
29
+ multi_test (>= 0.1.2)
30
+ cucumber-core (1.1.3)
31
+ gherkin (~> 2.12.0)
32
+ deep_merge (1.0.1)
33
+ diff-lcs (1.2.5)
34
+ facter (2.2.0)
35
+ CFPropertyList (~> 2.2.6)
36
+ ffi (1.9.9)
37
+ gherkin (2.12.2)
38
+ multi_json (~> 1.3)
39
+ gli (2.13.1)
40
+ hiera (1.3.4)
41
+ json_pure
42
+ iniparse (1.4.0)
43
+ json (1.8.3)
44
+ json_pure (1.8.1)
45
+ jwt (1.5.1)
46
+ little-plugger (1.1.3)
47
+ logging (2.0.0)
48
+ little-plugger (~> 1.1)
49
+ multi_json (~> 1.10)
50
+ multi_json (1.11.1)
51
+ multi_test (0.1.2)
52
+ puppet (3.7.0)
53
+ facter (> 1.6, < 3)
54
+ hiera (~> 1.0)
55
+ json_pure
56
+ rake (10.4.2)
57
+ rdoc (4.2.0)
58
+ json (~> 1.4)
59
+ require_all (1.3.2)
60
+ rspec-expectations (3.3.0)
61
+ diff-lcs (>= 1.2.0, < 2.0)
62
+ rspec-support (~> 3.3.0)
63
+ rspec-support (3.3.0)
64
+
65
+ PLATFORMS
66
+ ruby
67
+
68
+ DEPENDENCIES
69
+ aruba
70
+ autosign!
71
+ puppet
72
+ rake
73
+ rdoc
data/README.rdoc ADDED
@@ -0,0 +1,6 @@
1
+ = autosign
2
+
3
+ Describe your project here
4
+
5
+ :include:autosign.rdoc
6
+
data/Rakefile ADDED
@@ -0,0 +1,44 @@
1
+ require 'rake/clean'
2
+ require 'rubygems'
3
+ require 'rubygems/package_task'
4
+ require 'rdoc/task'
5
+ require 'cucumber'
6
+ require 'cucumber/rake/task'
7
+ Rake::RDocTask.new do |rd|
8
+ rd.main = "README.rdoc"
9
+ rd.rdoc_files.include("README.rdoc","lib/**/*.rb","bin/**/*")
10
+ rd.title = 'Your application title'
11
+ end
12
+
13
+ spec = eval(File.read('autosign.gemspec'))
14
+
15
+ Gem::PackageTask.new(spec) do |pkg|
16
+ end
17
+ CUKE_RESULTS = 'results.html'
18
+ CLEAN << CUKE_RESULTS
19
+ desc 'Run features'
20
+ Cucumber::Rake::Task.new(:features) do |t|
21
+ opts = "features --format html -o #{CUKE_RESULTS} --format progress -x"
22
+ opts += " --tags #{ENV['TAGS']}" if ENV['TAGS']
23
+ t.cucumber_opts = opts
24
+ t.fork = false
25
+ end
26
+
27
+ desc 'Run features tagged as work-in-progress (@wip)'
28
+ Cucumber::Rake::Task.new('features:wip') do |t|
29
+ tag_opts = ' --tags ~@pending'
30
+ tag_opts = ' --tags @wip'
31
+ t.cucumber_opts = "features --format html -o #{CUKE_RESULTS} --format pretty -x -s#{tag_opts}"
32
+ t.fork = false
33
+ end
34
+
35
+ task :cucumber => :features
36
+ task 'cucumber:wip' => 'features:wip'
37
+ task :wip => 'features:wip'
38
+ require 'rake/testtask'
39
+ Rake::TestTask.new do |t|
40
+ t.libs << "test"
41
+ t.test_files = FileList['test/*_test.rb']
42
+ end
43
+
44
+ task :default => [:test,:features]
data/autosign.gemspec ADDED
@@ -0,0 +1,29 @@
1
+ # Ensure we require the local version and not one we might have installed already
2
+ require File.join([File.dirname(__FILE__),'lib','autosign','version.rb'])
3
+ spec = Gem::Specification.new do |s|
4
+ s.name = 'autosign'
5
+ s.version = Autosign::VERSION
6
+ s.author = 'Your Name Here'
7
+ s.email = 'your@email.address.com'
8
+ s.homepage = 'http://your.website.com'
9
+ s.platform = Gem::Platform::RUBY
10
+ s.summary = 'A description of your project'
11
+ s.files = `git ls-files`.split("
12
+ ")
13
+ s.require_paths << 'lib'
14
+ s.has_rdoc = true
15
+ s.extra_rdoc_files = ['README.rdoc','autosign.rdoc']
16
+ s.rdoc_options << '--title' << 'autosign' << '--main' << 'README.rdoc' << '-ri'
17
+ s.bindir = 'bin'
18
+ s.executables << 'autosign'
19
+ s.add_development_dependency('rake')
20
+ s.add_development_dependency('rdoc')
21
+ s.add_development_dependency('aruba')
22
+ s.add_development_dependency('puppet')
23
+ s.add_runtime_dependency('gli','~> 2')
24
+ s.add_runtime_dependency('jwt','~> 1')
25
+ s.add_runtime_dependency('iniparse','~> 1')
26
+ s.add_runtime_dependency('logging')
27
+ s.add_runtime_dependency('deep_merge')
28
+ s.add_runtime_dependency('require_all')
29
+ end
data/autosign.rdoc ADDED
@@ -0,0 +1,5 @@
1
+ = autosign
2
+
3
+ Generate this with
4
+ autosign rdoc
5
+ After you have described your command line interface
data/bin/autosign ADDED
@@ -0,0 +1,169 @@
1
+ #!/usr/bin/env ruby
2
+ require 'gli'
3
+ require 'autosign'
4
+ require 'socket' # for determining the current hostname
5
+ include GLI::App
6
+ require 'logging'
7
+
8
+ @logger = Logging.logger['Autosign']
9
+ @logger.level = :warn
10
+
11
+ program_desc 'Easy Puppet Certificate Autosigning'
12
+
13
+ version Autosign::VERSION
14
+
15
+ subcommand_option_handling :normal
16
+ arguments :strict
17
+
18
+ desc 'Configuration file location'
19
+ arg_name 'path'
20
+ flag [:c,:config]
21
+
22
+ desc 'log file location'
23
+ arg_name 'path'
24
+ flag [:l,:logfile]
25
+
26
+ desc 'secret symmetric key'
27
+ arg_name 'secret'
28
+ flag [:s,:secret]
29
+
30
+ desc 'Enable verbose output'
31
+ switch [:v, :verbose]
32
+
33
+ desc 'Enable debug output'
34
+ switch [:d, :debug]
35
+
36
+ desc 'Quiet output - only log errors'
37
+ switch [:q, :quiet]
38
+
39
+ desc 'Generate an autosign token'
40
+ arg_name 'certname or regex the autosign token will be valid for'
41
+ command :generate do |c|
42
+ c.desc 'Generate a reusable token; default is to generate one-time tokens'
43
+ c.switch [:r, :reusable]
44
+
45
+ c.desc 'certname or regex of certnames the autosign token will be valid for'
46
+ c.arg_name 'certname'
47
+ c.flag [:n,:certname]
48
+
49
+ c.desc 'autosign token validity period'
50
+ c.default_value '7200'
51
+ c.arg_name 'seconds'
52
+ c.flag [:t,:validfor]
53
+
54
+ c.action do |global_options,options,args|
55
+ config = Autosign::Config.new({'config_file' => global_options['config']})
56
+ global_options['secret'] = config.settings['jwt_token']['secret'] if global_options['secret'].nil?
57
+ options['validfor'] = config.settings.to_hash['jwt_token']['validity'].to_s if options['validfor'] == '7200'
58
+ @logger.debug "validfor: " + options['validfor']
59
+ help_now!('no secret was defined via --secret or a config file') if global_options['secret'].nil?
60
+ help_now!('certname is required') if options['certname'].nil?
61
+
62
+ help_now!('validfor setting must be an positive integer number of seconds') if !/\A\d+\z/.match(options['validfor'].to_s)
63
+ token = Autosign::Token.new(options['certname'].to_s, options['reusable'], options['validfor'].to_i, Socket.gethostname.to_s, global_options['secret'])
64
+ @logger.info "generated token for: " + options['certname'].to_s
65
+ puts "Autosign token for: " + token.certname
66
+ puts "Valid until: " + Time.at(token.validto).to_s
67
+ puts ""
68
+ puts token.sign.to_s
69
+ puts ""
70
+ end
71
+ end
72
+
73
+ desc 'Validate a previously issued token'
74
+ arg_name 'path'
75
+ command :validate do |c|
76
+ c.desc 'display the contents of the token'
77
+
78
+ c.arg_name 'certname'
79
+ c.flag [:n,:certname]
80
+
81
+ c.action do |global_options,options,args|
82
+ config = Autosign::Config.new({'config_file' => global_options['config']})
83
+ puts config.settings.to_hash['jwt_token']
84
+ global_options['secret'] = config.settings['jwt_token']['secret'] if global_options['secret'].nil?
85
+
86
+ help_now!('no secret was defined via --secret or a config file') if global_options['secret'].nil?
87
+ help_now!('certname is required') if options['certname'].nil?
88
+ help_now!('a single token must be provided as an argument') if args.size != 1
89
+ token = Autosign::Token.validate(options['certname'].to_s, args[0], global_options['secret'])
90
+ if token == true
91
+ puts "token validated successfully"
92
+ @logger.info "token validated successfully"
93
+ else
94
+ @logger.error "Unable to validate token"
95
+ exit_now!("Unable to validate token", 1)
96
+ end
97
+ end
98
+ end
99
+
100
+
101
+ desc 'Autosign configuration'
102
+ command :config do |c|
103
+
104
+ c.desc 'Configure a puppet server for autosigning'
105
+ c.command :setup do |setup|
106
+ setup.action do |global_options,options,args|
107
+ @logger.info "setup command ran with #{global_options} #{options} #{args}"
108
+ @logger.info "generated default config file" if Autosign::Config.generate_default
109
+ end
110
+ end
111
+
112
+ c.desc 'Print autosign configuration'
113
+ c.command :print do |print|
114
+ print.action do |global_options,options,args|
115
+ @logger.debug "print command ran with #{global_options} #{options} #{args}"
116
+ config = Autosign::Config.new({'config_file' => global_options['config']})
117
+ puts config.settings.to_s
118
+ end
119
+ end
120
+
121
+ end
122
+
123
+ desc 'Install an autosign token; run this prior to running puppet for the first time on an agent'
124
+ arg_name 'token'
125
+ command :use do |c|
126
+ c.action do |global_options,options,args|
127
+ puppet_confdir = %x[puppet config print confdir].chomp
128
+ @logger.debug "use command ran with #{global_options} #{options} #{args}"
129
+ puts "put the following in #{puppet_confdir}/csr_attributes.yaml prior to running puppet agent for the first time:
130
+ custom_attributes:
131
+ challengePassword: \"#{args[0]}\""
132
+ end
133
+ end
134
+
135
+ pre do |global,command,options,args|
136
+ # Pre logic here
137
+ # Return true to proceed; false to abort and not call the
138
+ # chosen command
139
+ # Use skips_pre before a command to skip this block
140
+ # on that command only
141
+ # config = Autosign::Config.new
142
+ # @logger.level = config.settings.to_hash['general']['loglevel'].to_sym unless config.settings.to_hash['general']['loglevel'].nil?
143
+
144
+ @logger.level = :error if global['quiet']
145
+ @logger.level = :info if global['verbose']
146
+ @logger.level = :debug if global['debug']
147
+
148
+ if global['logfile'].nil?
149
+ @logger.add_appenders Logging.appenders.stdout
150
+ else
151
+ @logger.add_appenders Logging.appenders.stdout, Logging.appenders.file(global['logfile'])
152
+ end
153
+
154
+ true
155
+ end
156
+
157
+ post do |global,command,options,args|
158
+ # Post logic here
159
+ # Use skips_post before a command to skip this
160
+ # block on that command only
161
+ end
162
+
163
+ on_error do |exception|
164
+ # Error logic here
165
+ # return false to skip default error handling
166
+ true
167
+ end
168
+
169
+ exit run(ARGV)
@@ -0,0 +1,41 @@
1
+ #!/usr/bin/env ruby
2
+ require 'autosign'
3
+ require 'logging'
4
+
5
+ @logger = Logging.logger['Autosign']
6
+ @logger.level = :debug
7
+ @logger.add_appenders Logging.appenders.stdout
8
+
9
+ ### Get Inputs
10
+ unless ARGV.count == 1
11
+ @logger.error "This executable must be called with a certname as the only parameter and with an X509 CSR piped into STDIN"
12
+ exit 1
13
+ end
14
+
15
+ certname = ARGV[0]
16
+ @logger.debug "certname is " + certname
17
+
18
+ @logger.debug "reading CSR from stdin"
19
+ csr = Autosign::Decoder.decode_csr($stdin.read)
20
+ exit 1 unless csr.is_a?(Hash)
21
+
22
+ @logger.debug "CSR: " + csr.to_s
23
+ ### End Inputs
24
+
25
+ ### validate token
26
+ token_validation = Autosign::Validator.any_validator(csr[:challenge_password].to_s, certname.to_s)
27
+ ### end validation
28
+
29
+ ### Exit with correct exit status
30
+ if token_validation == true
31
+ @logger.info "token validated successfully"
32
+ exit 0
33
+ else
34
+ STDERR.puts "failed to validate token"
35
+ @logger.error "Unable to validate token"
36
+ exit 1
37
+ end
38
+ ### Done exiting
39
+
40
+ # end with an exit 1 just in case
41
+ exit 1
@@ -0,0 +1,78 @@
1
+ Feature: Generate autosign key
2
+ In order to sign puppet certificates automatically
3
+ I want to generate autosign keys programatically
4
+ So I don't have to use static strings as keys
5
+
6
+ Scenario: Generate new token
7
+ Given a pre-shared key of "secret"
8
+ And a hostname of "foo.example.com"
9
+ And a file named "autosign.conf" with:
10
+ """
11
+ [jwt_token]
12
+ validity = 7200
13
+ secret = secret
14
+ """
15
+ When I run `chmod 600 autosign.conf`
16
+ And I run `autosign --config autosign.conf generate --certname foo.example.com`
17
+ Then the output should contain "Autosign token for: foo.example.com"
18
+ And the output should contain "Valid until"
19
+ And the exit status should be 0
20
+
21
+ Scenario: Generate new reusable token
22
+ Given a pre-shared key of "secret"
23
+ And a hostname of "foo.example.com"
24
+ And a file named "autosign.conf" with:
25
+ """
26
+ [jwt_token]
27
+ secret = secret
28
+ validity = 7200
29
+ """
30
+ When I run `chmod 600 autosign.conf`
31
+ When I run `autosign --config autosign.conf generate --certname foo.example.com --reusable`
32
+ Then the output should contain "Autosign token for: foo.example.com"
33
+ And the output should contain "Valid until"
34
+ And the exit status should be 0
35
+
36
+ Scenario: Validate a token
37
+ Given a pre-shared key of "secret"
38
+ And a hostname of "foo.example.com"
39
+ And a file named "autosign.conf" with:
40
+ """
41
+ [jwt_token]
42
+ secret = secret
43
+ """
44
+ When I run `chmod 600 autosign.conf`
45
+ When I run `autosign --config autosign.conf validate --certname "foo.example.com" "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJkYXRhIjoie1wiY2VydG5hbWVcIjpcImZvby5leGFtcGxlLmNvbVwiLFwicmVxdWVzdGVyXCI6XCJEYW5pZWxzLU1hY0Jvb2stUHJvLTIubG9jYWxcIixcInJldXNhYmxlXCI6ZmFsc2UsXCJ2YWxpZGZvclwiOjI5OTk5OTk5OSxcInV1aWRcIjpcIjlkYTA0Yzc4LWQ5NjUtNDk2OC04MWNjLWVhM2RjZDllZjVjMFwifSIsImV4cCI6IjE3MzY0NjYxMzAifQ.PJwY8rIunVyWi_lw0ypFclME0jx3Vd9xJIQSyhN3VUmul3V8u4Tp9XwDgoAu9DVV0-WEG2Tfxs6F8R6Fn71Ndg"`
46
+ Then the output should contain "token validated successfully"
47
+ And the exit status should be 0
48
+
49
+ Scenario: Not validate a bad token
50
+ Given a pre-shared key of "secret"
51
+ And a hostname of "foo.example.com"
52
+ And a file named "autosign.conf" with:
53
+ """
54
+ [jwt_token]
55
+ secret = secret
56
+ """
57
+ When I run `chmod 600 autosign.conf`
58
+ When I run `autosign --config autosign.conf validate --certname "foo.example.com" "invalid_token"`
59
+ Then the exit status should be 1
60
+
61
+ Scenario: Not validate an expired token
62
+ Given a pre-shared key of "secret"
63
+ And a hostname of "foo.example.com"
64
+ And a file named "autosign.conf" with:
65
+ """
66
+ [jwt_token]
67
+ secret = secret
68
+ """
69
+ When I run `chmod 600 autosign.conf`
70
+ When I run `autosign --config autosign.conf validate --certname "foo.example.com" "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJkYXRhIjoie1wiY2VydG5hbWVcIjpcImZvby5leGFtcGxlLmNvbVwiLFwicmVxdWVzdGVyXCI6XCJEYW5pZWxzLU1hY0Jvb2stUHJvLTIubG9jYWxcIixcInJldXNhYmxlXCI6ZmFsc2UsXCJ2YWxpZGZvclwiOjEsXCJ1dWlkXCI6XCJlNjI1Y2I1Ny02NzY5LTQwMzQtODNiZS0zNzkxNmQ5YmMxMDRcIn0iLCJleHAiOiIxNDM2NDY2MzAyIn0.UXEDEbRqEWx5SdSpQjfowU56JubY5Yz2QN6cckby2es-g2P_n2lyAS6AwFeliBXyCDyVUelIT3g1QP4TdB9EEA"`
71
+ Then the exit status should be 1
72
+
73
+ Scenario: Generate a csr_attributes.yaml file
74
+ When I run `autosign use hunter2`
75
+ Then the output should contain "challengePassword: "
76
+ And the output should contain "csr_attributes.yaml"
77
+ And the output should contain "hunter2"
78
+ And the exit status should be 0