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.
- 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:
|