puppetserver-ca 0.3.1 → 0.4.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.
Files changed (34) hide show
  1. checksums.yaml +4 -4
  2. data/lib/puppetserver/ca/action/clean.rb +102 -0
  3. data/lib/puppetserver/ca/action/create.rb +161 -0
  4. data/lib/puppetserver/ca/action/generate.rb +313 -0
  5. data/lib/puppetserver/ca/action/import.rb +132 -0
  6. data/lib/puppetserver/ca/action/list.rb +132 -0
  7. data/lib/puppetserver/ca/action/revoke.rb +101 -0
  8. data/lib/puppetserver/ca/action/sign.rb +126 -0
  9. data/lib/puppetserver/ca/certificate_authority.rb +224 -0
  10. data/lib/puppetserver/ca/cli.rb +17 -16
  11. data/lib/puppetserver/ca/config/puppet.rb +242 -0
  12. data/lib/puppetserver/ca/config/puppetserver.rb +85 -0
  13. data/lib/puppetserver/ca/utils/cli_parsing.rb +82 -0
  14. data/lib/puppetserver/ca/utils/config.rb +13 -0
  15. data/lib/puppetserver/ca/utils/file_system.rb +90 -0
  16. data/lib/puppetserver/ca/utils/http_client.rb +129 -0
  17. data/lib/puppetserver/ca/utils/signing_digest.rb +27 -0
  18. data/lib/puppetserver/ca/version.rb +1 -1
  19. metadata +17 -17
  20. data/lib/puppetserver/ca/clean_action.rb +0 -157
  21. data/lib/puppetserver/ca/config_utils.rb +0 -11
  22. data/lib/puppetserver/ca/create_action.rb +0 -265
  23. data/lib/puppetserver/ca/generate_action.rb +0 -227
  24. data/lib/puppetserver/ca/import_action.rb +0 -153
  25. data/lib/puppetserver/ca/list_action.rb +0 -153
  26. data/lib/puppetserver/ca/puppet_config.rb +0 -197
  27. data/lib/puppetserver/ca/puppetserver_config.rb +0 -83
  28. data/lib/puppetserver/ca/revoke_action.rb +0 -136
  29. data/lib/puppetserver/ca/sign_action.rb +0 -190
  30. data/lib/puppetserver/ca/utils.rb +0 -80
  31. data/lib/puppetserver/settings/ttl_setting.rb +0 -48
  32. data/lib/puppetserver/utils/file_utilities.rb +0 -78
  33. data/lib/puppetserver/utils/http_client.rb +0 -129
  34. data/lib/puppetserver/utils/signing_digest.rb +0 -25
@@ -0,0 +1,85 @@
1
+ require 'hocon'
2
+ require 'puppetserver/ca/utils/config'
3
+
4
+ module Puppetserver
5
+ module Ca
6
+ module Config
7
+ # Provides an interface for querying Puppetserver settings w/o loading
8
+ # Puppetserver or any TK config service. Uses the ruby-hocon gem for parsing.
9
+ class PuppetServer
10
+
11
+ include Puppetserver::Ca::Utils::Config
12
+
13
+ def self.parse(config_path = nil)
14
+ instance = new(config_path)
15
+ instance.load
16
+
17
+ return instance
18
+ end
19
+
20
+ attr_reader :errors, :settings
21
+
22
+ def initialize(supplied_config_path = nil)
23
+ @using_default_location = !supplied_config_path
24
+ @config_path = supplied_config_path || "/etc/puppetlabs/puppetserver/conf.d/ca.conf"
25
+
26
+ @settings = nil
27
+ @errors = []
28
+ end
29
+
30
+ # Populate this config object with the CA-related settings
31
+ def load
32
+ if explicitly_given_config_file_or_default_config_exists?
33
+ begin
34
+ results = Hocon.load(@config_path)
35
+ rescue Hocon::ConfigError => e
36
+ errors << e.message
37
+ end
38
+ end
39
+
40
+ overrides = results || {}
41
+ @settings = supply_defaults(overrides).freeze
42
+ end
43
+
44
+ private
45
+
46
+ # Return the correct confdir. We check for being root on *nix,
47
+ # else the user path. We do not include a check for running
48
+ # as Adminstrator since non-development scenarios for Puppet Server
49
+ # on Windows are unsupported.
50
+ # Note that Puppet Server runs as the [pe-]puppet user but to
51
+ # start/stop it you must be root.
52
+ def user_specific_ca_dir
53
+ if running_as_root?
54
+ '/etc/puppetlabs/puppetserver/ca'
55
+ else
56
+ "#{ENV['HOME']}/.puppetlabs/etc/puppetserver/ca"
57
+ end
58
+ end
59
+
60
+ # Supply defaults for any CA settings not present in the config file
61
+ # @param [Hash] overrides setting names and values loaded from the config file,
62
+ # for overriding the defaults
63
+ # @return [Hash] CA-related settings
64
+ def supply_defaults(overrides = {})
65
+ ca_settings = overrides['certificate-authority'] || {}
66
+ settings = {}
67
+
68
+ cadir = settings[:cadir] = ca_settings.fetch('cadir', user_specific_ca_dir)
69
+
70
+ settings[:cacert] = ca_settings.fetch('cacert', "#{cadir}/ca_crt.pem")
71
+ settings[:cakey] = ca_settings.fetch('cakey', "#{cadir}/ca_key.pem")
72
+ settings[:cacrl] = ca_settings.fetch('cacrl', "#{cadir}/ca_crl.pem")
73
+ settings[:serial] = ca_settings.fetch('serial', "#{cadir}/serial")
74
+ settings[:cert_inventory] = ca_settings.fetch('cert-inventory', "#{cadir}/inventory.txt")
75
+
76
+ return settings
77
+ end
78
+
79
+ def explicitly_given_config_file_or_default_config_exists?
80
+ !@using_default_location || File.exist?(@config_path)
81
+ end
82
+ end
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,82 @@
1
+ module Puppetserver
2
+ module Ca
3
+ module Utils
4
+ module CliParsing
5
+ def self.parse_without_raising(parser, args)
6
+ all, not_flags, malformed_flags, unknown_flags = [], [], [], []
7
+
8
+ begin
9
+ # OptionParser calls this block when it finds a value that doesn't
10
+ # start with one or two dashes and doesn't follow a flag that
11
+ # consumes a value.
12
+ parser.order!(args) do |not_flag|
13
+ not_flags << not_flag
14
+ all << not_flag
15
+ end
16
+ rescue OptionParser::MissingArgument => e
17
+ malformed_flags += e.args
18
+ all += e.args
19
+
20
+ retry
21
+ rescue OptionParser::ParseError => e
22
+ flag = e.args.first
23
+ unknown_flags << flag
24
+ all << flag
25
+
26
+ if does_not_contain_argument(flag) &&
27
+ args.first &&
28
+ next_arg_is_not_another_flag(args.first)
29
+
30
+ value = args.shift
31
+ unknown_flags << value
32
+ all << value
33
+ end
34
+
35
+ retry
36
+ end
37
+
38
+ return all, not_flags, malformed_flags, unknown_flags
39
+ end
40
+
41
+ def self.parse_with_errors(parser, args)
42
+ errors = []
43
+
44
+ _, non_flags, malformed_flags, unknown_flags = parse_without_raising(parser, args)
45
+
46
+ malformed_flags.each {|f| errors << " Missing argument to flag `#{f}`" }
47
+ unknown_flags.each {|f| errors << " Unknown flag or argument `#{f}`" }
48
+ non_flags.each {|f| errors << " Unknown input `#{f}`" }
49
+
50
+ errors
51
+ end
52
+
53
+ def self.handle_errors(log, errors, usage = nil)
54
+ unless errors.empty?
55
+ log.err 'Error:'
56
+ errors.each {|e| log.err e }
57
+
58
+ if usage
59
+ log.err ''
60
+ log.err usage
61
+ end
62
+
63
+ return true
64
+ else
65
+ return false
66
+ end
67
+ end
68
+
69
+ private
70
+
71
+ # eg. --flag=argument-to-flag
72
+ def self.does_not_contain_argument(flag)
73
+ !flag.include?('=')
74
+ end
75
+
76
+ def self.next_arg_is_not_another_flag(maybe_an_arg)
77
+ !maybe_an_arg.start_with?('-')
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,13 @@
1
+ module Puppetserver
2
+ module Ca
3
+ module Utils
4
+ module Config
5
+
6
+ def running_as_root?
7
+ !Gem.win_platform? && Process::UID.eid == 0
8
+ end
9
+
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,90 @@
1
+ require 'fileutils'
2
+ require 'etc'
3
+
4
+ module Puppetserver
5
+ module Ca
6
+ module Utils
7
+ class FileSystem
8
+
9
+ def self.instance
10
+ @instance ||= new
11
+ end
12
+
13
+ def self.write_file(*args)
14
+ instance.write_file(*args)
15
+ end
16
+
17
+ def self.ensure_dir(setting)
18
+ instance.ensure_dir(setting)
19
+ end
20
+
21
+ def self.ensure_file(location, content, mode)
22
+ if !File.exist?(location)
23
+ instance.write_file(location, content, mode)
24
+ end
25
+ end
26
+
27
+ def self.validate_file_paths(one_or_more_paths)
28
+ errors = []
29
+ Array(one_or_more_paths).each do |path|
30
+ if !File.exist?(path) || !File.readable?(path)
31
+ errors << "Could not read file '#{path}'"
32
+ end
33
+ end
34
+
35
+ errors
36
+ end
37
+
38
+ def self.check_for_existing_files(one_or_more_paths)
39
+ errors = []
40
+ Array(one_or_more_paths).each do |path|
41
+ if File.exist?(path)
42
+ errors << "Existing file at '#{path}'"
43
+ end
44
+ end
45
+ errors
46
+ end
47
+
48
+ def initialize
49
+ @user, @group = find_user_and_group
50
+ end
51
+
52
+ def find_user_and_group
53
+ if !running_as_root?
54
+ return Process.euid, Process.egid
55
+ else
56
+ if pe_puppet_exists?
57
+ return 'pe-puppet', 'pe-puppet'
58
+ else
59
+ return 'puppet', 'puppet'
60
+ end
61
+ end
62
+ end
63
+
64
+ def running_as_root?
65
+ !Gem.win_platform? && Process.euid == 0
66
+ end
67
+
68
+ def pe_puppet_exists?
69
+ !!(Etc.getpwnam('pe-puppet') rescue nil)
70
+ end
71
+
72
+ def write_file(path, one_or_more_objects, mode)
73
+ File.open(path, 'w', mode) do |f|
74
+ Array(one_or_more_objects).each do |object|
75
+ f.puts object.to_s
76
+ end
77
+ end
78
+ FileUtils.chown(@user, @group, path)
79
+ end
80
+
81
+ def ensure_dir(setting)
82
+ if !File.exist?(setting)
83
+ FileUtils.mkdir_p(setting, mode: 0750)
84
+ FileUtils.chown(@user, @group, setting)
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,129 @@
1
+ require 'openssl'
2
+ require 'net/https'
3
+
4
+ module Puppetserver
5
+ module Ca
6
+ module Utils
7
+ # Utilities for doing HTTPS against the CA that wraps Net::HTTP constructs
8
+ class HttpClient
9
+
10
+ DEFAULT_HEADERS = {
11
+ 'User-Agent' => 'PuppetserverCaCli',
12
+ 'Content-Type' => 'application/json',
13
+ 'Accept' => 'application/json'
14
+ }
15
+
16
+ attr_reader :store
17
+
18
+ def initialize(settings)
19
+ @store = make_store(settings[:localcacert],
20
+ settings[:certificate_revocation],
21
+ settings[:hostcrl])
22
+ @cert = load_cert(settings[:hostcert])
23
+ @key = load_key(settings[:hostprivkey])
24
+ end
25
+
26
+ def load_cert(cert_path)
27
+ OpenSSL::X509::Certificate.new(File.read(cert_path))
28
+ end
29
+
30
+ def load_key(key_path)
31
+ OpenSSL::PKey.read(File.read(key_path))
32
+ end
33
+
34
+ # Takes an instance URL (defined lower in the file), and creates a
35
+ # connection. The given block is passed our own Connection object.
36
+ # The Connection object should have HTTP verbs defined on it that take
37
+ # a body (and optional overrides). Returns whatever the block given returned.
38
+ def with_connection(url, &block)
39
+ request = ->(conn) { block.call(Connection.new(conn, url)) }
40
+
41
+ Net::HTTP.start(url.host, url.port,
42
+ use_ssl: true, cert_store: @store,
43
+ cert: @cert, key: @key,
44
+ &request)
45
+ end
46
+
47
+ private
48
+ # Helper class that wraps a Net::HTTP connection, a HttpClient::URL
49
+ # and defines methods named after HTTP verbs that are called on the
50
+ # saved connection, returning a Result.
51
+ class Connection
52
+ def initialize(net_http_connection, url_struct)
53
+ @conn = net_http_connection
54
+ @url = url_struct
55
+ end
56
+
57
+ def get(url_overide = nil, headers = {})
58
+ url = url_overide || @url
59
+ headers = DEFAULT_HEADERS.merge(headers)
60
+
61
+ request = Net::HTTP::Get.new(url.to_uri, headers)
62
+ result = @conn.request(request)
63
+
64
+ Result.new(result.code, result.body)
65
+ end
66
+
67
+ def put(body, url_override = nil, headers = {})
68
+ url = url_override || @url
69
+ headers = DEFAULT_HEADERS.merge(headers)
70
+
71
+ request = Net::HTTP::Put.new(url.to_uri, headers)
72
+ request.body = body
73
+ result = @conn.request(request)
74
+
75
+ Result.new(result.code, result.body)
76
+ end
77
+
78
+ def delete(url_override = nil, headers = {})
79
+ url = url_override || @url
80
+ headers = DEFAULT_HEADERS.merge(headers)
81
+
82
+ result = @conn.request(Net::HTTP::Delete.new(url.to_uri, headers))
83
+
84
+ Result.new(result.code, result.body)
85
+ end
86
+ end
87
+
88
+ # Just provide the bits of Net::HTTPResponse we care about
89
+ Result = Struct.new(:code, :body)
90
+
91
+ # Like URI, but not... maybe of suspicious value
92
+ URL = Struct.new(:protocol, :host, :port,
93
+ :endpoint, :version,
94
+ :resource_type, :resource_name) do
95
+ def full_url
96
+ protocol + '://' + host + ':' + port + '/' +
97
+ [endpoint, version, resource_type, resource_name].join('/')
98
+ end
99
+
100
+ def to_uri
101
+ URI(full_url)
102
+ end
103
+ end
104
+
105
+ def make_store(bundle, crl_usage, crls = nil)
106
+ store = OpenSSL::X509::Store.new
107
+ store.purpose = OpenSSL::X509::PURPOSE_ANY
108
+ store.add_file(bundle)
109
+
110
+ if crl_usage != :ignore
111
+
112
+ flags = OpenSSL::X509::V_FLAG_CRL_CHECK
113
+ if crl_usage == :chain
114
+ flags |= OpenSSL::X509::V_FLAG_CRL_CHECK_ALL
115
+ end
116
+
117
+ store.flags = flags
118
+ delimiter = /-----BEGIN X509 CRL-----.*?-----END X509 CRL-----/m
119
+ File.read(crls).scan(delimiter).each do |crl|
120
+ store.add_crl(OpenSSL::X509::CRL.new(crl))
121
+ end
122
+ end
123
+
124
+ store
125
+ end
126
+ end
127
+ end
128
+ end
129
+ end
@@ -0,0 +1,27 @@
1
+ module Puppetserver
2
+ module Ca
3
+ module Utils
4
+ class SigningDigest
5
+
6
+ attr_reader :errors, :digest
7
+
8
+ def initialize
9
+ @errors = []
10
+ if OpenSSL::Digest.const_defined?('SHA256')
11
+ @digest = OpenSSL::Digest::SHA256.new
12
+ elsif OpenSSL::Digest.const_defined?('SHA1')
13
+ @digest = OpenSSL::Digest::SHA1.new
14
+ elsif OpenSSL::Digest.const_defined?('SHA512')
15
+ @digest = OpenSSL::Digest::SHA512.new
16
+ elsif OpenSSL::Digest.const_defined?('SHA384')
17
+ @digest = OpenSSL::Digest::SHA384.new
18
+ elsif OpenSSL::Digest.const_defined?('SHA224')
19
+ @digest = OpenSSL::Digest::SHA224.new
20
+ else
21
+ @errors << "Error: No FIPS 140-2 compliant digest algorithm in OpenSSL::Digest"
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -1,5 +1,5 @@
1
1
  module Puppetserver
2
2
  module Ca
3
- VERSION = "0.3.1"
3
+ VERSION = "0.4.0"
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: puppetserver-ca
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.1
4
+ version: 0.4.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-08-15 00:00:00.000000000 Z
11
+ date: 2018-08-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: facter
@@ -93,27 +93,27 @@ files:
93
93
  - bin/setup
94
94
  - exe/puppetserver-ca
95
95
  - lib/puppetserver/ca.rb
96
- - lib/puppetserver/ca/clean_action.rb
96
+ - lib/puppetserver/ca/action/clean.rb
97
+ - lib/puppetserver/ca/action/create.rb
98
+ - lib/puppetserver/ca/action/generate.rb
99
+ - lib/puppetserver/ca/action/import.rb
100
+ - lib/puppetserver/ca/action/list.rb
101
+ - lib/puppetserver/ca/action/revoke.rb
102
+ - lib/puppetserver/ca/action/sign.rb
103
+ - lib/puppetserver/ca/certificate_authority.rb
97
104
  - lib/puppetserver/ca/cli.rb
98
- - lib/puppetserver/ca/config_utils.rb
99
- - lib/puppetserver/ca/create_action.rb
100
- - lib/puppetserver/ca/generate_action.rb
105
+ - lib/puppetserver/ca/config/puppet.rb
106
+ - lib/puppetserver/ca/config/puppetserver.rb
101
107
  - lib/puppetserver/ca/host.rb
102
- - lib/puppetserver/ca/import_action.rb
103
- - lib/puppetserver/ca/list_action.rb
104
108
  - lib/puppetserver/ca/logger.rb
105
- - lib/puppetserver/ca/puppet_config.rb
106
- - lib/puppetserver/ca/puppetserver_config.rb
107
- - lib/puppetserver/ca/revoke_action.rb
108
- - lib/puppetserver/ca/sign_action.rb
109
109
  - lib/puppetserver/ca/stub.rb
110
- - lib/puppetserver/ca/utils.rb
110
+ - lib/puppetserver/ca/utils/cli_parsing.rb
111
+ - lib/puppetserver/ca/utils/config.rb
112
+ - lib/puppetserver/ca/utils/file_system.rb
113
+ - lib/puppetserver/ca/utils/http_client.rb
114
+ - lib/puppetserver/ca/utils/signing_digest.rb
111
115
  - lib/puppetserver/ca/version.rb
112
116
  - 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
117
117
  - puppetserver-ca.gemspec
118
118
  homepage: https://github.com/puppetlabs/puppetserver-ca-cli/
119
119
  licenses: