puppetserver-ca 1.11.7 → 2.0.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/README.md +5 -15
- data/lib/puppetserver/ca/action/clean.rb +3 -3
- data/lib/puppetserver/ca/action/enable.rb +1 -1
- data/lib/puppetserver/ca/action/generate.rb +8 -27
- data/lib/puppetserver/ca/action/import.rb +15 -12
- data/lib/puppetserver/ca/action/list.rb +20 -87
- data/lib/puppetserver/ca/action/migrate.rb +1 -9
- data/lib/puppetserver/ca/action/revoke.rb +3 -3
- data/lib/puppetserver/ca/action/setup.rb +16 -13
- data/lib/puppetserver/ca/action/sign.rb +1 -1
- data/lib/puppetserver/ca/certificate_authority.rb +9 -9
- data/lib/puppetserver/ca/cli.rb +7 -14
- data/lib/puppetserver/ca/config/puppet.rb +45 -40
- data/lib/puppetserver/ca/host.rb +2 -2
- data/lib/puppetserver/ca/local_certificate_authority.rb +9 -9
- data/lib/puppetserver/ca/logger.rb +1 -9
- data/lib/puppetserver/ca/utils/config.rb +17 -0
- data/lib/puppetserver/ca/utils/file_system.rb +5 -0
- data/lib/puppetserver/ca/utils/http_client.rb +10 -22
- data/lib/puppetserver/ca/version.rb +1 -1
- metadata +3 -4
- data/lib/puppetserver/ca/action/prune.rb +0 -137
data/lib/puppetserver/ca/cli.rb
CHANGED
@@ -8,7 +8,6 @@ require 'puppetserver/ca/action/list'
|
|
8
8
|
require 'puppetserver/ca/action/revoke'
|
9
9
|
require 'puppetserver/ca/action/setup'
|
10
10
|
require 'puppetserver/ca/action/sign'
|
11
|
-
require 'puppetserver/ca/action/prune'
|
12
11
|
require 'puppetserver/ca/action/migrate'
|
13
12
|
require 'puppetserver/ca/errors'
|
14
13
|
require 'puppetserver/ca/logger'
|
@@ -26,12 +25,11 @@ Manage the Private Key Infrastructure for
|
|
26
25
|
Puppet Server's built-in Certificate Authority
|
27
26
|
BANNER
|
28
27
|
|
29
|
-
|
28
|
+
INIT_ACTIONS = {
|
30
29
|
'import' => Action::Import,
|
31
30
|
'setup' => Action::Setup,
|
32
|
-
'enable'
|
33
|
-
'migrate'
|
34
|
-
'prune' => Action::Prune
|
31
|
+
'enable' => Action::Enable,
|
32
|
+
'migrate' => Action::Migrate,
|
35
33
|
}
|
36
34
|
|
37
35
|
MAINT_ACTIONS = {
|
@@ -42,15 +40,15 @@ BANNER
|
|
42
40
|
'sign' => Action::Sign
|
43
41
|
}
|
44
42
|
|
45
|
-
VALID_ACTIONS =
|
43
|
+
VALID_ACTIONS = INIT_ACTIONS.merge(MAINT_ACTIONS).sort.to_h
|
46
44
|
|
47
45
|
ACTION_LIST = "\nAvailable Actions:\n\n" +
|
48
46
|
" Certificate Actions (requires a running Puppet Server):\n\n" +
|
49
47
|
MAINT_ACTIONS.map do |action, cls|
|
50
48
|
" #{action}\t#{cls::SUMMARY}"
|
51
49
|
end.join("\n") + "\n\n" +
|
52
|
-
"
|
53
|
-
|
50
|
+
" Initialization Actions (requires Puppet Server to be stopped):\n\n" +
|
51
|
+
INIT_ACTIONS.map do |action, cls|
|
54
52
|
" #{action}\t#{cls::SUMMARY}"
|
55
53
|
end.join("\n")
|
56
54
|
|
@@ -66,10 +64,8 @@ BANNER
|
|
66
64
|
|
67
65
|
|
68
66
|
def self.run(cli_args = ARGV, out = STDOUT, err = STDERR)
|
67
|
+
logger = Puppetserver::Ca::Logger.new(:info, out, err)
|
69
68
|
parser, general_options, unparsed = parse_general_inputs(cli_args)
|
70
|
-
level = general_options.delete('verbose') ? :debug : :info
|
71
|
-
|
72
|
-
logger = Puppetserver::Ca::Logger.new(level, out, err)
|
73
69
|
|
74
70
|
if general_options['version']
|
75
71
|
logger.inform Puppetserver::Ca::VERSION
|
@@ -125,9 +121,6 @@ BANNER
|
|
125
121
|
opts.on('--version', 'Display the version') do |v|
|
126
122
|
parsed['version'] = true
|
127
123
|
end
|
128
|
-
opts.on('--verbose', 'Display low-level information') do |verbose|
|
129
|
-
parsed['verbose'] = true
|
130
|
-
end
|
131
124
|
|
132
125
|
opts.separator ACTION_OPTIONS
|
133
126
|
opts.separator "\nSee `puppetserver ca <action> --help` for detailed info"
|
@@ -23,9 +23,9 @@ module Puppetserver
|
|
23
23
|
# A regex describing valid formats with groups for capturing the value and units
|
24
24
|
TTL_FORMAT = /^(\d+)(y|d|h|m|s)?$/
|
25
25
|
|
26
|
-
def self.parse(config_path)
|
26
|
+
def self.parse(config_path, logger)
|
27
27
|
instance = new(config_path)
|
28
|
-
instance.load
|
28
|
+
instance.load({}, logger)
|
29
29
|
|
30
30
|
return instance
|
31
31
|
end
|
@@ -34,7 +34,7 @@ module Puppetserver
|
|
34
34
|
|
35
35
|
def initialize(supplied_config_path = nil)
|
36
36
|
@using_default_location = !supplied_config_path
|
37
|
-
@config_path = supplied_config_path ||
|
37
|
+
@config_path = supplied_config_path || user_specific_puppet_config
|
38
38
|
|
39
39
|
@settings = nil
|
40
40
|
@errors = []
|
@@ -46,55 +46,45 @@ module Puppetserver
|
|
46
46
|
# on Windows are unsupported.
|
47
47
|
# Note that Puppet Server runs as the [pe-]puppet user but to
|
48
48
|
# start/stop it you must be root.
|
49
|
-
def
|
50
|
-
@
|
51
|
-
if Puppetserver::Ca::Utils::Config.running_as_root?
|
52
|
-
'/etc/puppetlabs/puppet'
|
53
|
-
else
|
54
|
-
"#{ENV['HOME']}/.puppetlabs/etc/puppet"
|
55
|
-
end
|
49
|
+
def user_specific_puppet_confdir
|
50
|
+
@user_specific_puppet_confdir ||= Puppetserver::Ca::Utils::Config.puppet_confdir
|
56
51
|
end
|
57
52
|
|
58
|
-
def
|
59
|
-
|
53
|
+
def user_specific_puppet_config
|
54
|
+
user_specific_puppet_confdir + '/puppet.conf'
|
60
55
|
end
|
61
56
|
|
62
|
-
def load(cli_overrides = {})
|
57
|
+
def load(cli_overrides = {}, logger)
|
63
58
|
if explicitly_given_config_file_or_default_config_exists?
|
64
59
|
results = parse_text(File.read(@config_path))
|
65
60
|
end
|
66
61
|
|
67
62
|
results ||= {}
|
68
63
|
results[:main] ||= {}
|
69
|
-
# The [master] config section is deprecated
|
70
|
-
# We now favor [server], but support both for backwards compatibility
|
71
64
|
results[:master] ||= {}
|
72
|
-
results[:server] ||= {}
|
73
65
|
results[:agent] ||= {}
|
74
66
|
|
75
|
-
overrides = results[:agent].merge(results[:main]).merge(results[:master])
|
67
|
+
overrides = results[:agent].merge(results[:main]).merge(results[:master])
|
76
68
|
overrides.merge!(cli_overrides)
|
77
|
-
if overrides[:masterport]
|
78
|
-
overrides[:serverport] ||= overrides.delete(:masterport)
|
79
|
-
end
|
80
69
|
|
81
|
-
@settings = resolve_settings(overrides).freeze
|
70
|
+
@settings = resolve_settings(overrides, logger).freeze
|
82
71
|
end
|
83
72
|
|
84
73
|
def default_certname
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
74
|
+
@certname ||=
|
75
|
+
hostname = Facter.value(:hostname)
|
76
|
+
domain = Facter.value(:domain)
|
77
|
+
if domain and domain != ''
|
78
|
+
fqdn = [hostname, domain].join('.')
|
79
|
+
else
|
80
|
+
fqdn = hostname
|
81
|
+
end
|
82
|
+
fqdn.chomp('.')
|
93
83
|
end
|
94
84
|
|
95
85
|
# Resolve settings from default values, with any overrides for the
|
96
86
|
# specific settings or their dependent settings (ssldir, cadir) taken into account.
|
97
|
-
def resolve_settings(overrides = {})
|
87
|
+
def resolve_settings(overrides = {}, logger)
|
98
88
|
unresolved_setting = /\$[a-z_]+/
|
99
89
|
|
100
90
|
# Returning the key for unknown keys (rather than nil) is required to
|
@@ -106,12 +96,12 @@ module Puppetserver
|
|
106
96
|
# These need to be evaluated before we can construct their dependent
|
107
97
|
# defaults below
|
108
98
|
base_defaults = [
|
109
|
-
[:confdir,
|
99
|
+
[:confdir, user_specific_puppet_confdir],
|
110
100
|
[:ssldir,'$confdir/ssl'],
|
111
101
|
[:certdir, '$ssldir/certs'],
|
112
102
|
[:certname, default_certname],
|
113
103
|
[:server, 'puppet'],
|
114
|
-
[:
|
104
|
+
[:masterport, '8140'],
|
115
105
|
[:privatekeydir, '$ssldir/private_keys'],
|
116
106
|
[:publickeydir, '$ssldir/public_keys'],
|
117
107
|
]
|
@@ -129,7 +119,7 @@ module Puppetserver
|
|
129
119
|
:serial => '$cadir/serial',
|
130
120
|
:cert_inventory => '$cadir/inventory.txt',
|
131
121
|
:ca_server => '$server',
|
132
|
-
:ca_port => '$
|
122
|
+
:ca_port => '$masterport',
|
133
123
|
:localcacert => '$certdir/ca.pem',
|
134
124
|
:hostcrl => '$ssldir/crl.pem',
|
135
125
|
:hostcert => '$certdir/$certname.pem',
|
@@ -153,9 +143,12 @@ module Puppetserver
|
|
153
143
|
end
|
154
144
|
|
155
145
|
cadir = find_cadir(overrides.fetch(:cadir, false),
|
156
|
-
settings[:confdir]
|
146
|
+
settings[:confdir],
|
147
|
+
settings[:ssldir],
|
148
|
+
logger)
|
157
149
|
settings[:cadir] = substitutions['$cadir'] = cadir
|
158
150
|
|
151
|
+
|
159
152
|
dependent_defaults.each do |setting_name, default_value|
|
160
153
|
setting_value = overrides.fetch(setting_name, default_value)
|
161
154
|
settings[setting_name] = setting_value
|
@@ -218,17 +211,29 @@ module Puppetserver
|
|
218
211
|
|
219
212
|
private
|
220
213
|
|
221
|
-
|
214
|
+
|
215
|
+
def find_cadir(configured_cadir, confdir, ssldir, logger)
|
216
|
+
warning = 'The cadir is currently configured to be inside the ' +
|
217
|
+
'%{ssldir} directory. This config setting and the directory ' +
|
218
|
+
'location will not be used in a future version of puppet. ' +
|
219
|
+
'Please run the puppetserver ca tool to migrate out from the ' +
|
220
|
+
'puppet confdir to the /etc/puppetlabs/puppetserver/ca directory. ' +
|
221
|
+
'Use `puppetserver ca migrate --help` for more info.'
|
222
|
+
|
222
223
|
if configured_cadir
|
224
|
+
if configured_cadir.start_with?(ssldir)
|
225
|
+
logger.warn(warning % {ssldir: ssldir})
|
226
|
+
end
|
223
227
|
configured_cadir
|
228
|
+
|
224
229
|
else
|
225
230
|
old_cadir = Puppetserver::Ca::Utils::Config.old_default_cadir(confdir)
|
226
231
|
new_cadir = Puppetserver::Ca::Utils::Config.new_default_cadir(confdir)
|
227
|
-
|
228
|
-
|
229
|
-
new_cadir
|
230
|
-
else
|
232
|
+
if File.exist?(old_cadir) && !File.symlink?(old_cadir)
|
233
|
+
logger.warn(warning % {ssldir: ssldir})
|
231
234
|
old_cadir
|
235
|
+
else
|
236
|
+
new_cadir
|
232
237
|
end
|
233
238
|
end
|
234
239
|
end
|
@@ -279,7 +284,7 @@ module Puppetserver
|
|
279
284
|
end
|
280
285
|
|
281
286
|
if settings.dig(:server_list, 0, 1) &&
|
282
|
-
settings[:ca_port] == '$
|
287
|
+
settings[:ca_port] == '$masterport'
|
283
288
|
|
284
289
|
settings[:ca_port] = settings.dig(:server_list, 0, 1)
|
285
290
|
end
|
data/lib/puppetserver/ca/host.rb
CHANGED
@@ -58,10 +58,10 @@ module Puppetserver
|
|
58
58
|
@errors = []
|
59
59
|
end
|
60
60
|
|
61
|
-
# If both the private and public keys exist for a
|
61
|
+
# If both the private and public keys exist for a master then we want
|
62
62
|
# to honor them here, if only one key exists we want to surface an error,
|
63
63
|
# and if neither exist we generate a new key. This logic is necessary for
|
64
|
-
# proper bootstrapping for certain
|
64
|
+
# proper bootstrapping for certain master workflows.
|
65
65
|
def create_private_key(keylength, private_path = '', public_path = '')
|
66
66
|
if File.exists?(private_path) && File.exists?(public_path)
|
67
67
|
return OpenSSL::PKey.read(File.read(private_path))
|
@@ -20,7 +20,7 @@ module Puppetserver
|
|
20
20
|
|
21
21
|
CLI_AUTH_EXT_OID = "1.3.6.1.4.1.34380.1.3.39"
|
22
22
|
|
23
|
-
|
23
|
+
MASTER_EXTENSIONS = [
|
24
24
|
["basicConstraints", "CA:FALSE", true],
|
25
25
|
["nsComment", "Puppet Server Internal Certificate", false],
|
26
26
|
["authorityKeyIdentifier", "keyid:always", false],
|
@@ -132,23 +132,23 @@ module Puppetserver
|
|
132
132
|
time.strftime('%Y-%m-%dT%H:%M:%S%Z')
|
133
133
|
end
|
134
134
|
|
135
|
-
def
|
136
|
-
|
137
|
-
|
135
|
+
def create_master_cert
|
136
|
+
master_cert = nil
|
137
|
+
master_key = @host.create_private_key(@settings[:keylength],
|
138
138
|
@settings[:hostprivkey],
|
139
139
|
@settings[:hostpubkey])
|
140
|
-
if
|
141
|
-
|
140
|
+
if master_key
|
141
|
+
master_csr = @host.create_csr(name: @settings[:certname], key: master_key)
|
142
142
|
if @settings[:subject_alt_names].empty?
|
143
143
|
alt_names = "DNS:puppet, DNS:#{@settings[:certname]}"
|
144
144
|
else
|
145
145
|
alt_names = @settings[:subject_alt_names]
|
146
146
|
end
|
147
147
|
|
148
|
-
|
148
|
+
master_cert = sign_authorized_cert(master_csr, alt_names)
|
149
149
|
end
|
150
150
|
|
151
|
-
return
|
151
|
+
return master_key, master_cert
|
152
152
|
end
|
153
153
|
|
154
154
|
def sign_authorized_cert(csr, alt_names = '')
|
@@ -176,7 +176,7 @@ module Puppetserver
|
|
176
176
|
end
|
177
177
|
|
178
178
|
def add_authorized_extensions(cert, ef)
|
179
|
-
|
179
|
+
MASTER_EXTENSIONS.each do |ext|
|
180
180
|
extension = ef.create_extension(*ext)
|
181
181
|
cert.add_extension(extension)
|
182
182
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'puppetserver/ca/utils/file_system'
|
2
|
+
|
1
3
|
module Puppetserver
|
2
4
|
module Ca
|
3
5
|
module Utils
|
@@ -31,6 +33,10 @@ module Puppetserver
|
|
31
33
|
File.join(File.dirname(puppet_confdir), 'puppetserver')
|
32
34
|
end
|
33
35
|
|
36
|
+
def self.default_ssldir(confdir = puppet_confdir)
|
37
|
+
File.join(confdir, 'ssl')
|
38
|
+
end
|
39
|
+
|
34
40
|
def self.old_default_cadir(confdir = puppet_confdir)
|
35
41
|
File.join(confdir, 'ssl', 'ca')
|
36
42
|
end
|
@@ -38,6 +44,17 @@ module Puppetserver
|
|
38
44
|
def self.new_default_cadir(confdir = puppet_confdir)
|
39
45
|
File.join(puppetserver_confdir(confdir), 'ca')
|
40
46
|
end
|
47
|
+
|
48
|
+
def self.symlink_to_old_cadir(current_cadir, puppet_confdir)
|
49
|
+
old_cadir = old_default_cadir(puppet_confdir)
|
50
|
+
new_cadir = new_default_cadir(puppet_confdir)
|
51
|
+
return if current_cadir != new_cadir
|
52
|
+
# This is only run on setup/import, so there should be no files in the
|
53
|
+
# old cadir, so it should be safe to forcibly remove it (which we need
|
54
|
+
# to do in order to create a symlink).
|
55
|
+
Puppetserver::Ca::Utils::FileSystem.forcibly_symlink(new_cadir, old_cadir)
|
56
|
+
end
|
57
|
+
|
41
58
|
end
|
42
59
|
end
|
43
60
|
end
|
@@ -1,6 +1,5 @@
|
|
1
1
|
require 'net/https'
|
2
2
|
require 'openssl'
|
3
|
-
require 'uri'
|
4
3
|
|
5
4
|
require 'puppetserver/ca/errors'
|
6
5
|
|
@@ -20,8 +19,7 @@ module Puppetserver
|
|
20
19
|
|
21
20
|
# Not all connections require a client cert to be present.
|
22
21
|
# For example, when querying the status endpoint.
|
23
|
-
def initialize(
|
24
|
-
@logger = logger
|
22
|
+
def initialize(settings, with_client_cert: true)
|
25
23
|
@store = make_store(settings[:localcacert],
|
26
24
|
settings[:certificate_revocation],
|
27
25
|
settings[:hostcrl])
|
@@ -52,7 +50,7 @@ module Puppetserver
|
|
52
50
|
# The Connection object should have HTTP verbs defined on it that take
|
53
51
|
# a body (and optional overrides). Returns whatever the block given returned.
|
54
52
|
def with_connection(url, &block)
|
55
|
-
request = ->(conn) { block.call(Connection.new(conn, url
|
53
|
+
request = ->(conn) { block.call(Connection.new(conn, url)) }
|
56
54
|
|
57
55
|
begin
|
58
56
|
Net::HTTP.start(url.host, url.port,
|
@@ -87,30 +85,25 @@ module Puppetserver
|
|
87
85
|
# and defines methods named after HTTP verbs that are called on the
|
88
86
|
# saved connection, returning a Result.
|
89
87
|
class Connection
|
90
|
-
def initialize(net_http_connection, url_struct
|
88
|
+
def initialize(net_http_connection, url_struct)
|
91
89
|
@conn = net_http_connection
|
92
90
|
@url = url_struct
|
93
|
-
@logger = logger
|
94
91
|
end
|
95
92
|
|
96
93
|
def get(url_overide = nil, headers = {})
|
97
94
|
url = url_overide || @url
|
98
95
|
headers = DEFAULT_HEADERS.merge(headers)
|
99
96
|
|
100
|
-
@logger.debug("Making a GET request at #{url.full_url}")
|
101
|
-
|
102
97
|
request = Net::HTTP::Get.new(url.to_uri, headers)
|
103
98
|
result = @conn.request(request)
|
104
|
-
Result.new(result.code, result.body)
|
105
99
|
|
100
|
+
Result.new(result.code, result.body)
|
106
101
|
end
|
107
102
|
|
108
103
|
def put(body, url_override = nil, headers = {})
|
109
104
|
url = url_override || @url
|
110
105
|
headers = DEFAULT_HEADERS.merge(headers)
|
111
106
|
|
112
|
-
@logger.debug("Making a PUT request at #{url.full_url}")
|
113
|
-
|
114
107
|
request = Net::HTTP::Put.new(url.to_uri, headers)
|
115
108
|
request.body = body
|
116
109
|
result = @conn.request(request)
|
@@ -122,8 +115,6 @@ module Puppetserver
|
|
122
115
|
url = url_override || @url
|
123
116
|
headers = DEFAULT_HEADERS.merge(headers)
|
124
117
|
|
125
|
-
@logger.debug("Making a DELETE request at #{url.full_url}")
|
126
|
-
|
127
118
|
result = @conn.request(Net::HTTP::Delete.new(url.to_uri, headers))
|
128
119
|
|
129
120
|
Result.new(result.code, result.body)
|
@@ -136,13 +127,10 @@ module Puppetserver
|
|
136
127
|
# Like URI, but not... maybe of suspicious value
|
137
128
|
URL = Struct.new(:protocol, :host, :port,
|
138
129
|
:endpoint, :version,
|
139
|
-
:resource_type, :resource_name
|
130
|
+
:resource_type, :resource_name) do
|
140
131
|
def full_url
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
url = url + "?" + URI.encode_www_form(query) unless query.nil? || query.empty?
|
145
|
-
return url
|
132
|
+
protocol + '://' + host + ':' + port + '/' +
|
133
|
+
[endpoint, version, resource_type, resource_name].join('/')
|
146
134
|
end
|
147
135
|
|
148
136
|
def to_uri
|
@@ -178,15 +166,15 @@ module Puppetserver
|
|
178
166
|
def self.check_server_online(settings, logger)
|
179
167
|
status_url = URL.new('https', settings[:ca_server], settings[:ca_port], 'status', 'v1', 'simple', 'ca')
|
180
168
|
begin
|
181
|
-
# Generating certs offline is necessary if the
|
169
|
+
# Generating certs offline is necessary if the master cert has been destroyed
|
182
170
|
# or compromised. Since querying the status endpoint does not require a client cert, and
|
183
171
|
# we commonly won't have one, don't require one for creating the connection.
|
184
172
|
# Additionally, we want to ensure the server is stopped before migrating the CA dir to
|
185
173
|
# avoid issues with writing to the CA dir and moving it.
|
186
|
-
self.new(
|
174
|
+
self.new(settings, with_client_cert: false).with_connection(status_url) do |conn|
|
187
175
|
result = conn.get
|
188
176
|
if result.body == "running"
|
189
|
-
logger.err "
|
177
|
+
logger.err "CA service is running. Please stop it before attempting to run this command."
|
190
178
|
true
|
191
179
|
else
|
192
180
|
false
|
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:
|
4
|
+
version: 2.0.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:
|
11
|
+
date: 2020-11-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: facter
|
@@ -100,7 +100,6 @@ files:
|
|
100
100
|
- lib/puppetserver/ca/action/import.rb
|
101
101
|
- lib/puppetserver/ca/action/list.rb
|
102
102
|
- lib/puppetserver/ca/action/migrate.rb
|
103
|
-
- lib/puppetserver/ca/action/prune.rb
|
104
103
|
- lib/puppetserver/ca/action/revoke.rb
|
105
104
|
- lib/puppetserver/ca/action/setup.rb
|
106
105
|
- lib/puppetserver/ca/action/sign.rb
|
@@ -140,7 +139,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
140
139
|
- !ruby/object:Gem::Version
|
141
140
|
version: '0'
|
142
141
|
requirements: []
|
143
|
-
rubygems_version: 3.0.
|
142
|
+
rubygems_version: 3.0.8
|
144
143
|
signing_key:
|
145
144
|
specification_version: 4
|
146
145
|
summary: A simple CLI tool for interacting with Puppet Server's Certificate Authority
|
@@ -1,137 +0,0 @@
|
|
1
|
-
require 'optparse'
|
2
|
-
require 'openssl'
|
3
|
-
require 'set'
|
4
|
-
require 'puppetserver/ca/errors'
|
5
|
-
require 'puppetserver/ca/utils/cli_parsing'
|
6
|
-
require 'puppetserver/ca/utils/file_system'
|
7
|
-
require 'puppetserver/ca/utils/config'
|
8
|
-
require 'puppetserver/ca/x509_loader'
|
9
|
-
|
10
|
-
module Puppetserver
|
11
|
-
module Ca
|
12
|
-
module Action
|
13
|
-
class Prune
|
14
|
-
include Puppetserver::Ca::Utils
|
15
|
-
|
16
|
-
SUMMARY = "Prune the local CRL on disk to remove any duplicated certificates"
|
17
|
-
BANNER = <<-BANNER
|
18
|
-
Usage:
|
19
|
-
puppetserver ca prune [--help]
|
20
|
-
puppetserver ca prune [--config]
|
21
|
-
|
22
|
-
Description:
|
23
|
-
Prune the list of revoked certificates of any duplication within it. This command
|
24
|
-
will only prune the CRL issued by Puppet's CA cert.
|
25
|
-
|
26
|
-
Options:
|
27
|
-
BANNER
|
28
|
-
|
29
|
-
def initialize(logger)
|
30
|
-
@logger = logger
|
31
|
-
end
|
32
|
-
|
33
|
-
def run(inputs)
|
34
|
-
config_path = inputs['config']
|
35
|
-
exit_code = 0
|
36
|
-
|
37
|
-
# Validate the config path.
|
38
|
-
if config_path
|
39
|
-
errors = FileSystem.validate_file_paths(config_path)
|
40
|
-
return 1 if Errors.handle_with_usage(@logger, errors)
|
41
|
-
end
|
42
|
-
|
43
|
-
# Validate puppet config setting.
|
44
|
-
puppet = Config::Puppet.new(config_path)
|
45
|
-
puppet.load(logger: @logger)
|
46
|
-
return 1 if Errors.handle_with_usage(@logger, puppet.errors)
|
47
|
-
|
48
|
-
# Validate that we are offline
|
49
|
-
return 1 if HttpClient.check_server_online(puppet.settings, @logger)
|
50
|
-
|
51
|
-
# Getting the CRL(s)
|
52
|
-
loader = X509Loader.new(puppet.settings[:cacert], puppet.settings[:cakey], puppet.settings[:cacrl])
|
53
|
-
|
54
|
-
verified_crls = loader.crls.select { |crl| crl.verify(loader.key) }
|
55
|
-
|
56
|
-
if verified_crls.length == 1
|
57
|
-
puppet_crl = verified_crls.first
|
58
|
-
@logger.inform("Total number of certificates found in Puppet's CRL is: #{puppet_crl.revoked.length}.")
|
59
|
-
number_of_removed_duplicates = prune_CRL(puppet_crl)
|
60
|
-
|
61
|
-
if number_of_removed_duplicates > 0
|
62
|
-
update_pruned_CRL(puppet_crl, loader.key)
|
63
|
-
FileSystem.write_file(puppet.settings[:cacrl], loader.crls, 0644)
|
64
|
-
@logger.inform("Removed #{number_of_removed_duplicates} duplicated certs from Puppet's CRL.")
|
65
|
-
else
|
66
|
-
@logger.inform("No duplicate revocations found in the CRL.")
|
67
|
-
end
|
68
|
-
else
|
69
|
-
@logger.err("Could not identify Puppet's CRL. Aborting prune action.")
|
70
|
-
exit_code = 1
|
71
|
-
end
|
72
|
-
|
73
|
-
return exit_code
|
74
|
-
end
|
75
|
-
|
76
|
-
def prune_CRL(crl)
|
77
|
-
number_of_removed_duplicates = 0
|
78
|
-
|
79
|
-
existed_serial_number = Set.new()
|
80
|
-
revoked_list = crl.revoked
|
81
|
-
@logger.debug("Pruning duplicate entries in CRL for issuer " \
|
82
|
-
"#{crl.issuer.to_s(OpenSSL::X509::Name::RFC2253)}") if @logger.debug?
|
83
|
-
|
84
|
-
revoked_list.delete_if do |revoked|
|
85
|
-
if existed_serial_number.add?(revoked.serial)
|
86
|
-
false
|
87
|
-
else
|
88
|
-
number_of_removed_duplicates += 1
|
89
|
-
@logger.debug("Removing duplicate of #{revoked.serial}, " \
|
90
|
-
"revoked on #{revoked.time}\n") if @logger.debug?
|
91
|
-
true
|
92
|
-
end
|
93
|
-
end
|
94
|
-
crl.revoked=(revoked_list)
|
95
|
-
|
96
|
-
return number_of_removed_duplicates
|
97
|
-
end
|
98
|
-
|
99
|
-
def update_pruned_CRL(crl, pkey)
|
100
|
-
number_ext, other_ext = crl.extensions.partition{ |ext| ext.oid == "crlNumber" }
|
101
|
-
number_ext.each do |crl_number|
|
102
|
-
updated_crl_number = OpenSSL::BN.new(crl_number.value) + OpenSSL::BN.new(1)
|
103
|
-
crl_number.value=(OpenSSL::ASN1::Integer(updated_crl_number))
|
104
|
-
end
|
105
|
-
crl.extensions=(number_ext + other_ext)
|
106
|
-
crl.sign(pkey, OpenSSL::Digest::SHA256.new)
|
107
|
-
end
|
108
|
-
|
109
|
-
def self.parser(parsed = {})
|
110
|
-
OptionParser.new do |opts|
|
111
|
-
opts.banner = BANNER
|
112
|
-
opts.on('--help', 'Display this command-specific help output') do |help|
|
113
|
-
parsed['help'] = true
|
114
|
-
end
|
115
|
-
opts.on('--config CONF', 'Path to the puppet.conf file on disk') do |conf|
|
116
|
-
parsed['config'] = conf
|
117
|
-
end
|
118
|
-
end
|
119
|
-
end
|
120
|
-
|
121
|
-
def parse(args)
|
122
|
-
results = {}
|
123
|
-
parser = self.class.parser(results)
|
124
|
-
errors = CliParsing.parse_with_errors(parser, args)
|
125
|
-
errors_were_handled = Errors.handle_with_usage(@logger, errors, parser.help)
|
126
|
-
|
127
|
-
if errors_were_handled
|
128
|
-
exit_code = 1
|
129
|
-
else
|
130
|
-
exit_code = nil
|
131
|
-
end
|
132
|
-
return results, exit_code
|
133
|
-
end
|
134
|
-
end
|
135
|
-
end
|
136
|
-
end
|
137
|
-
end
|