Skip to content

Commit 63fc086

Browse files
(PE-39411) Add descriptive error during infrastructure upgrade when rbac token is invalid (#514)
* (PE-39411) Rewrite infra upgrade error to be more useful. Add validate_rbac_token task to check if a given token file is valid * (PE-39411) Validate RBAC token on upgrade if required for compiler upgrades * (maint) Fix lint issues * (maint) Regenerate references.md * Update tasks/validate_rbac_token.rb Co-authored-by: Neil Anderson <[email protected]> --------- Co-authored-by: Neil Anderson <[email protected]>
1 parent 315d4ea commit 63fc086

6 files changed

+144
-30
lines changed

Diff for: REFERENCE.md

+15
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@
8888
* [`ssl_clean`](#ssl_clean): Clean an agent's certificate
8989
* [`submit_csr`](#submit_csr): Submit a certificate signing request
9090
* [`transform_classification_groups`](#transform_classification_groups): Transform the user groups from a source backup to a list of groups on the target server
91+
* [`validate_rbac_token`](#validate_rbac_token): Check an RBAC token stored in a file is valid
9192
* [`wait_until_service_ready`](#wait_until_service_ready): Return when the orchestrator service is healthy, or timeout after 15 seconds
9293

9394
### Plans
@@ -1587,6 +1588,20 @@ Data type: `String`
15871588

15881589
Location of target node group yaml file and where to create the transformed file
15891590

1591+
### <a name="validate_rbac_token"></a>`validate_rbac_token`
1592+
1593+
Check an RBAC token stored in a file is valid
1594+
1595+
**Supports noop?** false
1596+
1597+
#### Parameters
1598+
1599+
##### `token_file`
1600+
1601+
Data type: `Optional[String]`
1602+
1603+
The path to the token file to use
1604+
15901605
### <a name="wait_until_service_ready"></a>`wait_until_service_ready`
15911606

15921607
Return when the orchestrator service is healthy, or timeout after 15 seconds

Diff for: plans/upgrade.pp

+7
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@
6262

6363
Optional[Peadm::UpgradeSteps] $begin_at_step = undef,
6464
) {
65+
out::message('# Validating inputs')
66+
6567
# Ensure input valid for a supported architecture
6668
$arch = peadm::assert_supported_architecture(
6769
$primary_host,
@@ -92,6 +94,11 @@
9294
$replica_postgresql_target,
9395
])
9496

97+
# Validate the RBAC token used to upgrade compilers if compilers are present
98+
if $compiler_targets and $compiler_targets.size > 0 {
99+
run_task('peadm::validate_rbac_token', $primary_target, token_file => $token_file)
100+
}
101+
95102
out::message('# Gathering information')
96103

97104
# lint:ignore:strict_indent

Diff for: plans/util/retrieve_and_upload.pp

+29-29
Original file line numberDiff line numberDiff line change
@@ -29,39 +29,39 @@
2929
|-HEREDOC
3030
# lint:endignore
3131

32-
$operating_system = run_task('peadm::os_identification', 'local://localhost')
33-
$os_string =$operating_system.first.value['_output']
32+
$operating_system = run_task('peadm::os_identification', 'local://localhost')
33+
$os_string =$operating_system.first.value['_output']
3434

35-
if 'windows' in $os_string {
36-
$exists = run_command("[System.IO.File]::Exists('${local_path}')", 'local://localhost')
37-
if $exists.first['stdout'].chomp == 'false' {
38-
run_task('peadm::download', 'local://localhost',
39-
source => $source,
40-
path => $local_path,
41-
)
42-
}
35+
if 'windows' in $os_string {
36+
$exists = run_command("[System.IO.File]::Exists('${local_path}')", 'local://localhost')
37+
if $exists.first['stdout'].chomp == 'false' {
38+
run_task('peadm::download', 'local://localhost',
39+
source => $source,
40+
path => $local_path,
41+
)
42+
}
4343

44-
$result_size = run_task('peadm::filesize', 'local://localhost',
45-
path => $local_path,
46-
)
47-
$local_size = $result_size.first.value['_output']
48-
} else {
49-
$exists = without_default_logging() || {
50-
run_command("test -e '${local_path}'", 'local://localhost',
51-
_catch_errors => true,
52-
).ok()
53-
}
54-
unless $exists {
55-
run_task('peadm::download', 'local://localhost',
56-
source => $source,
57-
path => $local_path,
44+
$result_size = run_task('peadm::filesize', 'local://localhost',
45+
path => $local_path,
5846
)
59-
}
47+
$local_size = $result_size.first.value['_output']
48+
} else {
49+
$exists = without_default_logging() || {
50+
run_command("test -e '${local_path}'", 'local://localhost',
51+
_catch_errors => true,
52+
).ok()
53+
}
54+
unless $exists {
55+
run_task('peadm::download', 'local://localhost',
56+
source => $source,
57+
path => $local_path,
58+
)
59+
}
6060

61-
$local_size = run_task('peadm::filesize', 'local://localhost',
62-
path => $local_path,
63-
).first['size']
64-
}
61+
$local_size = run_task('peadm::filesize', 'local://localhost',
62+
path => $local_path,
63+
).first['size']
64+
}
6565

6666
$targets_needing_file = run_task('peadm::filesize', $nodes,
6767
path => $upload_path,

Diff for: tasks/puppet_infra_upgrade.rb

+2-1
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,8 @@ def wait_until_connected(nodes:, token_file:, timeout: 120)
7373
loop do
7474
response = https.request(request)
7575
unless response.is_a? Net::HTTPSuccess
76-
raise "Unexpected result from orchestrator: #{response.class}\n#{response}"
76+
body = JSON.parse(response.body)
77+
raise "Unexpected result from orchestrator: #{response.code}#{body.kind}\n#{body.msg}"
7778
end
7879
inventory = JSON.parse(response.body)
7980
break if inventory['items'].all? { |item| item['connected'] }

Diff for: tasks/validate_rbac_token.json

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"description": "Check an RBAC token stored in a file is valid",
3+
"parameters": {
4+
"token_file": {
5+
"type": "Optional[String]",
6+
"description": "The path to the token file to use"
7+
}
8+
},
9+
"input_method": "stdin",
10+
"implementations": [
11+
{"name": "validate_rbac_token.rb"}
12+
]
13+
}

Diff for: tasks/validate_rbac_token.rb

+78
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
#!/opt/puppetlabs/puppet/bin/ruby
2+
# frozen_string_literal: true
3+
4+
require 'uri'
5+
require 'net/https'
6+
require 'json'
7+
require 'etc'
8+
require 'puppet'
9+
10+
# Class to check an rbac token is valid
11+
class ValidateRbacToken
12+
def initialize(params)
13+
@token_file = params['token_file']
14+
end
15+
16+
def execute!
17+
token_file = @token_file || File.join(Etc.getpwuid.dir, '.puppetlabs', 'token')
18+
19+
uri = URI("https://#{Puppet.settings[:certname]}:4433/rbac-api/v2/auth/token/authenticate")
20+
https = https_object(uri: uri)
21+
request = request_object(token_file: token_file)
22+
23+
resp = https.request(request)
24+
25+
if resp.code == '200'
26+
puts 'RBAC token is valid'
27+
exit 0
28+
else
29+
body = JSON.parse(resp.body)
30+
case resp.code
31+
when '401', '403'
32+
puts "#{resp.code} #{body['kind']}: " \
33+
"Check your API token at #{token_file}.\n" \
34+
"Please refresh your token or provide an alternate file.\n" \
35+
"See https://www.puppet.com/docs/pe/latest/rbac_token_auth_intro for more details.\n"
36+
else
37+
puts "Error validating token: #{resp.code} #{body['kind']}"
38+
puts body['msg']
39+
end
40+
41+
exit 1
42+
end
43+
end
44+
45+
def request_object(token_file:)
46+
token = File.read(token_file)
47+
body = {
48+
'token' => token.chomp,
49+
'update_last_activity?' => false,
50+
}.to_json
51+
52+
request = Net::HTTP::Post.new('/rbac-api/v2/auth/token/authenticate')
53+
request['Content-Type'] = 'application/json'
54+
request.body = body
55+
56+
request
57+
end
58+
59+
def https_object(uri:)
60+
https = Net::HTTP.new(uri.host, uri.port)
61+
https.use_ssl = true
62+
https.cert = OpenSSL::X509::Certificate.new(File.read(Puppet.settings[:hostcert]))
63+
https.key = OpenSSL::PKey::RSA.new(File.read(Puppet.settings[:hostprivkey]))
64+
https.verify_mode = OpenSSL::SSL::VERIFY_PEER
65+
https.ca_file = Puppet.settings[:localcacert]
66+
67+
https
68+
end
69+
end
70+
71+
# Run the task unless an environment flag has been set, signaling not to. The
72+
# environment flag is used to disable auto-execution and enable Ruby unit
73+
# testing of this task.
74+
unless ENV['RSPEC_UNIT_TEST_MODE']
75+
Puppet.initialize_settings
76+
validate = ValidateRbacToken.new(JSON.parse(STDIN.read))
77+
validate.execute!
78+
end

0 commit comments

Comments
 (0)