puppetserver-ca 0.2.0 → 0.3.0

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.
@@ -0,0 +1,80 @@
1
+ module Puppetserver
2
+ module Ca
3
+ module Utils
4
+ def self.parse_without_raising(parser, args)
5
+ all, not_flags, malformed_flags, unknown_flags = [], [], [], []
6
+
7
+ begin
8
+ # OptionParser calls this block when it finds a value that doesn't
9
+ # start with one or two dashes and doesn't follow a flag that
10
+ # consumes a value.
11
+ parser.order!(args) do |not_flag|
12
+ not_flags << not_flag
13
+ all << not_flag
14
+ end
15
+ rescue OptionParser::MissingArgument => e
16
+ malformed_flags += e.args
17
+ all += e.args
18
+
19
+ retry
20
+ rescue OptionParser::ParseError => e
21
+ flag = e.args.first
22
+ unknown_flags << flag
23
+ all << flag
24
+
25
+ if does_not_contain_argument(flag) &&
26
+ args.first &&
27
+ next_arg_is_not_another_flag(args.first)
28
+
29
+ value = args.shift
30
+ unknown_flags << value
31
+ all << value
32
+ end
33
+
34
+ retry
35
+ end
36
+
37
+ return all, not_flags, malformed_flags, unknown_flags
38
+ end
39
+
40
+ def self.parse_with_errors(parser, args)
41
+ errors = []
42
+
43
+ _, non_flags, malformed_flags, unknown_flags = parse_without_raising(parser, args)
44
+
45
+ malformed_flags.each {|f| errors << " Missing argument to flag `#{f}`" }
46
+ unknown_flags.each {|f| errors << " Unknown flag or argument `#{f}`" }
47
+ non_flags.each {|f| errors << " Unknown input `#{f}`" }
48
+
49
+ errors
50
+ end
51
+
52
+ def self.handle_errors(log, errors, usage = nil)
53
+ unless errors.empty?
54
+ log.err 'Error:'
55
+ errors.each {|e| log.err e }
56
+
57
+ if usage
58
+ log.err ''
59
+ log.err usage
60
+ end
61
+
62
+ return true
63
+ else
64
+ return false
65
+ end
66
+ end
67
+
68
+ private
69
+
70
+ # eg. --flag=argument-to-flag
71
+ def self.does_not_contain_argument(flag)
72
+ !flag.include?('=')
73
+ end
74
+
75
+ def self.next_arg_is_not_another_flag(maybe_an_arg)
76
+ !maybe_an_arg.start_with?('-')
77
+ end
78
+ end
79
+ end
80
+ end
@@ -1,5 +1,5 @@
1
1
  module Puppetserver
2
2
  module Ca
3
- VERSION = "0.2.0"
3
+ VERSION = "0.3.0"
4
4
  end
5
5
  end
@@ -0,0 +1,48 @@
1
+ # A setting that represents a span of time to live, and evaluates to Numeric
2
+ # seconds to live where 0 means shortest possible time to live, a positive numeric value means time
3
+ # to live in seconds, and the symbolic entry 'unlimited' is an infinite amount of time.
4
+ #
5
+ module Puppetserver
6
+ module Settings
7
+ class TTLSetting
8
+ # How we convert from various units to seconds.
9
+ UNITMAP = {
10
+ # 365 days isn't technically a year, but is sufficient for most purposes
11
+ "y" => 365 * 24 * 60 * 60,
12
+ "d" => 24 * 60 * 60,
13
+ "h" => 60 * 60,
14
+ "m" => 60,
15
+ "s" => 1
16
+ }
17
+
18
+ # A regex describing valid formats with groups for capturing the value and units
19
+ FORMAT = /^(\d+)(y|d|h|m|s)?$/
20
+
21
+ attr_reader :errors, :munged_value
22
+
23
+ def initialize(name, setting_value)
24
+ @errors = []
25
+ @munged_value = munge(setting_value, name)
26
+ end
27
+
28
+ # Convert the value to Numeric, parsing numeric string with units if necessary.
29
+ def munge(value, name)
30
+ case
31
+ when value.is_a?(Numeric)
32
+ if value < 0
33
+ @errors << "Invalid negative 'time to live' #{value.inspect} - did you mean 'unlimited'?"
34
+ end
35
+ value
36
+
37
+ when value == 'unlimited'
38
+ Float::INFINITY
39
+
40
+ when (value.is_a?(String) and value =~ FORMAT)
41
+ $1.to_i * UNITMAP[$2 || 's']
42
+ else
43
+ @errors << "Invalid 'time to live' format '#{value.inspect}' for parameter: #{name}"
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,78 @@
1
+ require 'fileutils'
2
+ require 'etc'
3
+
4
+ module Puppetserver
5
+ module Utils
6
+ class FileUtilities
7
+
8
+ def self.instance
9
+ @instance ||= new
10
+ end
11
+
12
+ def self.write_file(*args)
13
+ instance.write_file(*args)
14
+ end
15
+
16
+ def self.ensure_dir(setting)
17
+ instance.ensure_dir(setting)
18
+ end
19
+
20
+ def self.ensure_file(location, content, mode)
21
+ if !File.exist?(location)
22
+ instance.write_file(location, content, mode)
23
+ end
24
+ end
25
+
26
+ def self.validate_file_paths(one_or_more_paths)
27
+ errors = []
28
+ Array(one_or_more_paths).each do |path|
29
+ if !File.exist?(path) || !File.readable?(path)
30
+ errors << "Could not read file '#{path}'"
31
+ end
32
+ end
33
+
34
+ errors
35
+ end
36
+
37
+ def initialize
38
+ @user, @group = find_user_and_group
39
+ end
40
+
41
+ def find_user_and_group
42
+ if !running_as_root?
43
+ return Process.euid, Process.egid
44
+ else
45
+ if pe_puppet_exists?
46
+ return 'pe-puppet', 'pe-puppet'
47
+ else
48
+ return 'puppet', 'puppet'
49
+ end
50
+ end
51
+ end
52
+
53
+ def running_as_root?
54
+ !Gem.win_platform? && Process.euid == 0
55
+ end
56
+
57
+ def pe_puppet_exists?
58
+ !!(Etc.getpwnam('pe-puppet') rescue nil)
59
+ end
60
+
61
+ def write_file(path, one_or_more_objects, mode)
62
+ File.open(path, 'w', mode) do |f|
63
+ Array(one_or_more_objects).each do |object|
64
+ f.puts object.to_s
65
+ end
66
+ end
67
+ FileUtils.chown(@user, @group, path)
68
+ end
69
+
70
+ def ensure_dir(setting)
71
+ if !File.exist?(setting)
72
+ FileUtils.mkdir_p(setting, mode: 0750)
73
+ FileUtils.chown(@user, @group, setting)
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,116 @@
1
+ require 'openssl'
2
+ require 'net/https'
3
+
4
+ module Puppetserver
5
+ module Utils
6
+ # Utilities for doing HTTPS against the CA that wraps Net::HTTP constructs
7
+ class HttpClient
8
+
9
+ HEADERS = {
10
+ 'User-Agent' => 'PuppetserverCaCli',
11
+ 'Content-Type' => 'application/json',
12
+ 'Accept' => 'application/json'
13
+ }
14
+
15
+ attr_reader :store
16
+
17
+ def initialize(localcacert, crl_usage, hostcrl)
18
+ @store = make_store(localcacert, crl_usage, hostcrl)
19
+ end
20
+
21
+ # Returns a URI-like wrapper around CA specific urls
22
+ def make_ca_url(host, port, resource_type = nil, certname = nil)
23
+ URL.new('https', host, port, 'puppet-ca', 'v1', resource_type, certname)
24
+ end
25
+
26
+ # Takes an instance URL (defined lower in the file), and creates a
27
+ # connection. The given block is passed our own Connection object.
28
+ # The Connection object should have HTTP verbs defined on it that take
29
+ # a body (and optional overrides). Returns whatever the block given returned.
30
+ def with_connection(url, &block)
31
+ request = ->(conn) { block.call(Connection.new(conn, url)) }
32
+
33
+ Net::HTTP.start(url.host, url.port,
34
+ use_ssl: true, cert_store: @store,
35
+ &request)
36
+ end
37
+
38
+ private
39
+ # Helper class that wraps a Net::HTTP connection, a HttpClient::URL
40
+ # and defines methods named after HTTP verbs that are called on the
41
+ # saved connection, returning a Result.
42
+ class Connection
43
+ def initialize(net_http_connection, url_struct)
44
+ @conn = net_http_connection
45
+ @url = url_struct
46
+ end
47
+
48
+ def get(url_overide = nil)
49
+ url = url_overide || @url
50
+
51
+ request = Net::HTTP::Get.new(url.to_uri, HEADERS)
52
+ result = @conn.request(request)
53
+
54
+ Result.new(result.code, result.body)
55
+ end
56
+
57
+ def put(body, url_override = nil)
58
+ url = url_override || @url
59
+
60
+ request = Net::HTTP::Put.new(url.to_uri, HEADERS)
61
+ request.body = body
62
+ result = @conn.request(request)
63
+
64
+ Result.new(result.code, result.body)
65
+ end
66
+
67
+ def delete(url_override = nil)
68
+ url = url_override || @url
69
+
70
+ result = @conn.request(Net::HTTP::Delete.new(url.to_uri, HEADERS))
71
+
72
+ Result.new(result.code, result.body)
73
+ end
74
+ end
75
+
76
+ # Just provide the bits of Net::HTTPResponse we care about
77
+ Result = Struct.new(:code, :body)
78
+
79
+ # Like URI, but not... maybe of suspicious value
80
+ URL = Struct.new(:protocol, :host, :port,
81
+ :endpoint, :version,
82
+ :resource_type, :resource_name) do
83
+ def full_url
84
+ protocol + '://' + host + ':' + port + '/' +
85
+ [endpoint, version, resource_type, resource_name].join('/')
86
+ end
87
+
88
+ def to_uri
89
+ URI(full_url)
90
+ end
91
+ end
92
+
93
+ def make_store(bundle, crl_usage, crls = nil)
94
+ store = OpenSSL::X509::Store.new
95
+ store.purpose = OpenSSL::X509::PURPOSE_ANY
96
+ store.add_file(bundle)
97
+
98
+ if crl_usage != :ignore
99
+
100
+ flags = OpenSSL::X509::V_FLAG_CRL_CHECK
101
+ if crl_usage == :chain
102
+ flags |= OpenSSL::X509::V_FLAG_CRL_CHECK_ALL
103
+ end
104
+
105
+ store.flags = flags
106
+ delimiter = /-----BEGIN X509 CRL-----.*?-----END X509 CRL-----/m
107
+ File.read(crls).scan(delimiter).each do |crl|
108
+ store.add_crl(OpenSSL::X509::CRL.new(crl))
109
+ end
110
+ end
111
+
112
+ store
113
+ end
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,25 @@
1
+ module Puppetserver
2
+ module Utils
3
+ class SigningDigest
4
+
5
+ attr_reader :errors, :digest
6
+
7
+ def initialize
8
+ @errors = []
9
+ if OpenSSL::Digest.const_defined?('SHA256')
10
+ @digest = OpenSSL::Digest::SHA256.new
11
+ elsif OpenSSL::Digest.const_defined?('SHA1')
12
+ @digest = OpenSSL::Digest::SHA1.new
13
+ elsif OpenSSL::Digest.const_defined?('SHA512')
14
+ @digest = OpenSSL::Digest::SHA512.new
15
+ elsif OpenSSL::Digest.const_defined?('SHA384')
16
+ @digest = OpenSSL::Digest::SHA384.new
17
+ elsif OpenSSL::Digest.const_defined?('SHA224')
18
+ @digest = OpenSSL::Digest::SHA224.new
19
+ else
20
+ @errors << "Error: No FIPS 140-2 compliant digest algorithm in OpenSSL::Digest"
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -20,6 +20,8 @@ Gem::Specification.new do |spec|
20
20
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
21
21
  spec.require_paths = ["lib"]
22
22
 
23
+ spec.add_runtime_dependency "facter", [">= 2.0.1", "< 4"]
24
+
23
25
  spec.add_development_dependency "bundler", "~> 1.16"
24
26
  spec.add_development_dependency "rake", "~> 10.0"
25
27
  spec.add_development_dependency "rspec", "~> 3.0"
metadata CHANGED
@@ -1,15 +1,35 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: puppetserver-ca
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Puppet, Inc.
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-07-27 00:00:00.000000000 Z
11
+ date: 2018-08-14 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: facter
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 2.0.1
20
+ - - "<"
21
+ - !ruby/object:Gem::Version
22
+ version: '4'
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ version: 2.0.1
30
+ - - "<"
31
+ - !ruby/object:Gem::Version
32
+ version: '4'
13
33
  - !ruby/object:Gem::Dependency
14
34
  name: bundler
15
35
  requirement: !ruby/object:Gem::Requirement
@@ -73,15 +93,27 @@ files:
73
93
  - bin/setup
74
94
  - exe/puppetserver-ca
75
95
  - lib/puppetserver/ca.rb
96
+ - lib/puppetserver/ca/clean_action.rb
76
97
  - lib/puppetserver/ca/cli.rb
77
98
  - lib/puppetserver/ca/config_utils.rb
99
+ - lib/puppetserver/ca/create_action.rb
100
+ - lib/puppetserver/ca/generate_action.rb
101
+ - lib/puppetserver/ca/host.rb
78
102
  - lib/puppetserver/ca/import_action.rb
103
+ - lib/puppetserver/ca/list_action.rb
79
104
  - lib/puppetserver/ca/logger.rb
80
105
  - lib/puppetserver/ca/puppet_config.rb
81
106
  - lib/puppetserver/ca/puppetserver_config.rb
107
+ - lib/puppetserver/ca/revoke_action.rb
108
+ - lib/puppetserver/ca/sign_action.rb
82
109
  - lib/puppetserver/ca/stub.rb
110
+ - lib/puppetserver/ca/utils.rb
83
111
  - lib/puppetserver/ca/version.rb
84
112
  - lib/puppetserver/ca/x509_loader.rb
113
+ - lib/puppetserver/settings/ttl_setting.rb
114
+ - lib/puppetserver/utils/file_utilities.rb
115
+ - lib/puppetserver/utils/http_client.rb
116
+ - lib/puppetserver/utils/signing_digest.rb
85
117
  - puppetserver-ca.gemspec
86
118
  homepage: https://github.com/puppetlabs/puppetserver-ca-cli/
87
119
  licenses: