diff --git a/REFERENCE.md b/REFERENCE.md
index 7aef694b..cb85d76e 100644
--- a/REFERENCE.md
+++ b/REFERENCE.md
@@ -86,6 +86,7 @@
* [`ssl_clean`](#ssl_clean): Clean an agent's certificate
* [`submit_csr`](#submit_csr): Submit a certificate signing request
* [`transform_classification_groups`](#transform_classification_groups): Transform the user groups from a source backup to a list of groups on the target server
+* [`validate_rbac_token`](#validate_rbac_token): Check an RBAC token stored in a file is valid
* [`wait_until_service_ready`](#wait_until_service_ready): Return when the orchestrator service is healthy, or timeout after 15 seconds
### Plans
@@ -1572,6 +1573,20 @@ Data type: `String`
Location of target node group yaml file and where to create the transformed file
+### `validate_rbac_token`
+
+Check an RBAC token stored in a file is valid
+
+**Supports noop?** false
+
+#### Parameters
+
+##### `token_file`
+
+Data type: `Optional[String]`
+
+The path to the token file to use
+
### `wait_until_service_ready`
Return when the orchestrator service is healthy, or timeout after 15 seconds
diff --git a/plans/upgrade.pp b/plans/upgrade.pp
index 5d061e0d..d15f7e44 100644
--- a/plans/upgrade.pp
+++ b/plans/upgrade.pp
@@ -67,6 +67,8 @@
'upgrade-replica-compilers',
'finalize']] $begin_at_step = undef,
) {
+ out::message('# Validating inputs')
+
# Ensure input valid for a supported architecture
$arch = peadm::assert_supported_architecture(
$primary_host,
@@ -97,6 +99,11 @@
$replica_postgresql_target,
])
+ # Validate the RBAC token used to upgrade compilers if compilers are present
+ if $compiler_targets and $compiler_targets.size > 0 {
+ run_task('peadm::validate_rbac_token', $primary_target, token_file => $token_file)
+ }
+
out::message('# Gathering information')
# lint:ignore:strict_indent
diff --git a/plans/util/retrieve_and_upload.pp b/plans/util/retrieve_and_upload.pp
index 2bb40168..51002d28 100644
--- a/plans/util/retrieve_and_upload.pp
+++ b/plans/util/retrieve_and_upload.pp
@@ -29,39 +29,39 @@
|-HEREDOC
# lint:endignore
-$operating_system = run_task('peadm::os_identification', 'local://localhost')
-$os_string =$operating_system.first.value['_output']
+ $operating_system = run_task('peadm::os_identification', 'local://localhost')
+ $os_string =$operating_system.first.value['_output']
-if 'windows' in $os_string {
- $exists = run_command("[System.IO.File]::Exists('${local_path}')", 'local://localhost')
- if $exists.first['stdout'].chomp == 'false' {
- run_task('peadm::download', 'local://localhost',
- source => $source,
- path => $local_path,
- )
- }
+ if 'windows' in $os_string {
+ $exists = run_command("[System.IO.File]::Exists('${local_path}')", 'local://localhost')
+ if $exists.first['stdout'].chomp == 'false' {
+ run_task('peadm::download', 'local://localhost',
+ source => $source,
+ path => $local_path,
+ )
+ }
- $result_size = run_task('peadm::filesize', 'local://localhost',
- path => $local_path,
- )
- $local_size = $result_size.first.value['_output']
-} else {
- $exists = without_default_logging() || {
- run_command("test -e '${local_path}'", 'local://localhost',
- _catch_errors => true,
- ).ok()
- }
- unless $exists {
- run_task('peadm::download', 'local://localhost',
- source => $source,
- path => $local_path,
+ $result_size = run_task('peadm::filesize', 'local://localhost',
+ path => $local_path,
)
- }
+ $local_size = $result_size.first.value['_output']
+ } else {
+ $exists = without_default_logging() || {
+ run_command("test -e '${local_path}'", 'local://localhost',
+ _catch_errors => true,
+ ).ok()
+ }
+ unless $exists {
+ run_task('peadm::download', 'local://localhost',
+ source => $source,
+ path => $local_path,
+ )
+ }
- $local_size = run_task('peadm::filesize', 'local://localhost',
- path => $local_path,
- ).first['size']
-}
+ $local_size = run_task('peadm::filesize', 'local://localhost',
+ path => $local_path,
+ ).first['size']
+ }
$targets_needing_file = run_task('peadm::filesize', $nodes,
path => $upload_path,
diff --git a/tasks/puppet_infra_upgrade.rb b/tasks/puppet_infra_upgrade.rb
index daad14b8..11fea276 100755
--- a/tasks/puppet_infra_upgrade.rb
+++ b/tasks/puppet_infra_upgrade.rb
@@ -73,7 +73,8 @@ def wait_until_connected(nodes:, token_file:, timeout: 120)
loop do
response = https.request(request)
unless response.is_a? Net::HTTPSuccess
- raise "Unexpected result from orchestrator: #{response.class}\n#{response}"
+ body = JSON.parse(response.body)
+ raise "Unexpected result from orchestrator: #{response.code}#{body.kind}\n#{body.msg}"
end
inventory = JSON.parse(response.body)
break if inventory['items'].all? { |item| item['connected'] }
diff --git a/tasks/validate_rbac_token.json b/tasks/validate_rbac_token.json
new file mode 100644
index 00000000..6c526643
--- /dev/null
+++ b/tasks/validate_rbac_token.json
@@ -0,0 +1,13 @@
+{
+ "description": "Check an RBAC token stored in a file is valid",
+ "parameters": {
+ "token_file": {
+ "type": "Optional[String]",
+ "description": "The path to the token file to use"
+ }
+ },
+ "input_method": "stdin",
+ "implementations": [
+ {"name": "validate_rbac_token.rb"}
+ ]
+}
diff --git a/tasks/validate_rbac_token.rb b/tasks/validate_rbac_token.rb
new file mode 100755
index 00000000..eed23384
--- /dev/null
+++ b/tasks/validate_rbac_token.rb
@@ -0,0 +1,78 @@
+#!/opt/puppetlabs/puppet/bin/ruby
+# frozen_string_literal: true
+
+require 'uri'
+require 'net/https'
+require 'json'
+require 'etc'
+require 'puppet'
+
+# Class to check an rbac token is valid
+class ValidateRbacToken
+ def initialize(params)
+ @token_file = params['token_file']
+ end
+
+ def execute!
+ token_file = @token_file || File.join(Etc.getpwuid.dir, '.puppetlabs', 'token')
+
+ uri = URI("https://#{Puppet.settings[:certname]}:4433/rbac-api/v2/auth/token/authenticate")
+ https = https_object(uri: uri)
+ request = request_object(token_file: token_file)
+
+ resp = https.request(request)
+
+ if resp.code == '200'
+ puts 'RBAC token is valid'
+ exit 0
+ else
+ body = JSON.parse(resp.body)
+ case resp.code
+ when '401', '403'
+ puts "#{resp.code} #{body['kind']}: " \
+ "Check your API token at #{token_file}.\n" \
+ "Please refresh your token or provide an alternate file.\n" \
+ "See https://www.puppet.com/docs/pe/latest/rbac_token_auth_intro for more details.\n"
+ else
+ puts "Error validating token: #{resp.code} #{body['kind']}"
+ puts body['msg']
+ end
+
+ exit 1
+ end
+ end
+
+ def request_object(token_file:)
+ token = File.read(token_file)
+ body = {
+ 'token' => token.chomp,
+ 'update_last_activity?' => false,
+ }.to_json
+
+ request = Net::HTTP::Post.new('/rbac-api/v2/auth/token/authenticate')
+ request['Content-Type'] = 'application/json'
+ request.body = body
+
+ request
+ end
+
+ def https_object(uri:)
+ https = Net::HTTP.new(uri.host, uri.port)
+ https.use_ssl = true
+ https.cert = OpenSSL::X509::Certificate.new(File.read(Puppet.settings[:hostcert]))
+ https.key = OpenSSL::PKey::RSA.new(File.read(Puppet.settings[:hostprivkey]))
+ https.verify_mode = OpenSSL::SSL::VERIFY_PEER
+ https.ca_file = Puppet.settings[:localcacert]
+
+ https
+ end
+end
+
+# Run the task unless an environment flag has been set, signaling not to. The
+# environment flag is used to disable auto-execution and enable Ruby unit
+# testing of this task.
+unless ENV['RSPEC_UNIT_TEST_MODE']
+ Puppet.initialize_settings
+ validate = ValidateRbacToken.new(JSON.parse(STDIN.read))
+ validate.execute!
+end