puppetserver-ca 0.3.1 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
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: