Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(PE-39411) Add descriptive error during infrastructure upgrade when rbac token is invalid #514

Merged
merged 5 commits into from
Oct 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions REFERENCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -1572,6 +1573,20 @@ Data type: `String`

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

### <a name="validate_rbac_token"></a>`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

### <a name="wait_until_service_ready"></a>`wait_until_service_ready`

Return when the orchestrator service is healthy, or timeout after 15 seconds
Expand Down
7 changes: 7 additions & 0 deletions plans/upgrade.pp
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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
Expand Down
58 changes: 29 additions & 29 deletions plans/util/retrieve_and_upload.pp
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
3 changes: 2 additions & 1 deletion tasks/puppet_infra_upgrade.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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'] }
Expand Down
13 changes: 13 additions & 0 deletions tasks/validate_rbac_token.json
Original file line number Diff line number Diff line change
@@ -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"}
]
}
78 changes: 78 additions & 0 deletions tasks/validate_rbac_token.rb
Original file line number Diff line number Diff line change
@@ -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
Loading