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.
- checksums.yaml +4 -4
- data/lib/puppetserver/ca/action/clean.rb +102 -0
- data/lib/puppetserver/ca/action/create.rb +161 -0
- data/lib/puppetserver/ca/action/generate.rb +313 -0
- data/lib/puppetserver/ca/action/import.rb +132 -0
- data/lib/puppetserver/ca/action/list.rb +132 -0
- data/lib/puppetserver/ca/action/revoke.rb +101 -0
- data/lib/puppetserver/ca/action/sign.rb +126 -0
- data/lib/puppetserver/ca/certificate_authority.rb +224 -0
- data/lib/puppetserver/ca/cli.rb +17 -16
- data/lib/puppetserver/ca/config/puppet.rb +242 -0
- data/lib/puppetserver/ca/config/puppetserver.rb +85 -0
- data/lib/puppetserver/ca/utils/cli_parsing.rb +82 -0
- data/lib/puppetserver/ca/utils/config.rb +13 -0
- data/lib/puppetserver/ca/utils/file_system.rb +90 -0
- data/lib/puppetserver/ca/utils/http_client.rb +129 -0
- data/lib/puppetserver/ca/utils/signing_digest.rb +27 -0
- data/lib/puppetserver/ca/version.rb +1 -1
- metadata +17 -17
- data/lib/puppetserver/ca/clean_action.rb +0 -157
- data/lib/puppetserver/ca/config_utils.rb +0 -11
- data/lib/puppetserver/ca/create_action.rb +0 -265
- data/lib/puppetserver/ca/generate_action.rb +0 -227
- data/lib/puppetserver/ca/import_action.rb +0 -153
- data/lib/puppetserver/ca/list_action.rb +0 -153
- data/lib/puppetserver/ca/puppet_config.rb +0 -197
- data/lib/puppetserver/ca/puppetserver_config.rb +0 -83
- data/lib/puppetserver/ca/revoke_action.rb +0 -136
- data/lib/puppetserver/ca/sign_action.rb +0 -190
- data/lib/puppetserver/ca/utils.rb +0 -80
- data/lib/puppetserver/settings/ttl_setting.rb +0 -48
- data/lib/puppetserver/utils/file_utilities.rb +0 -78
- data/lib/puppetserver/utils/http_client.rb +0 -129
- 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,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
|
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.
|
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-
|
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/
|
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/
|
99
|
-
- lib/puppetserver/ca/
|
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:
|