734fcb8cf4
Facter runs under Puppet's vendored Ruby, which reads its own bundled CA file and never the system trust store. The ENC fact now fetches over HTTPS, so every node fails certificate verification and falls back to its cached value. - set ca_file on the request to the vaultca anchor bundle - keep VERIFY_PEER on, and fall through to the existing cache path when the anchor is absent Reviewed-on: #529 Co-authored-by: unkin-agent <unkin-agent@unkin.net> Co-committed-by: unkin-agent <unkin-agent@unkin.net>
96 lines
2.7 KiB
Ruby
96 lines
2.7 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
require 'facter'
|
|
require 'yaml'
|
|
require 'net/http'
|
|
require 'openssl'
|
|
require 'uri'
|
|
require 'fileutils'
|
|
|
|
# EncapiENC module: Fetches ENC data from encapi, caches it, and provides structured facts.
|
|
module EncapiENC
|
|
CACHE_FILE = '/var/cache/puppet_enc.yaml'
|
|
CACHE_TTL = 7 * 24 * 60 * 60 # 7 days in seconds
|
|
# Facter runs under Puppet's vendored ruby, whose OpenSSL trusts only
|
|
# /opt/puppetlabs/puppet/ssl/cert.pem and never the system trust store, so the
|
|
# estate CA anchor profiles::pki::vaultca installs has to be named explicitly.
|
|
CA_BUNDLE_PATHS = [
|
|
'/etc/pki/ca-trust/source/anchors/vaultcaroot.pem',
|
|
'/usr/local/share/ca-certificates/vaultcaroot.pem'
|
|
].freeze
|
|
@enc_data = nil # In-memory cache for the ENC response
|
|
|
|
def self.read_cache
|
|
return {} unless File.exist?(CACHE_FILE)
|
|
|
|
cache_data = YAML.safe_load(File.read(CACHE_FILE)) || {}
|
|
timestamp = cache_data.fetch('timestamp', 0)
|
|
|
|
return cache_data if Time.now.to_i - timestamp < CACHE_TTL
|
|
|
|
{}
|
|
end
|
|
|
|
def self.write_cache(enc_data)
|
|
FileUtils.mkdir_p(File.dirname(CACHE_FILE))
|
|
cache_data = enc_data.merge({ 'timestamp' => Time.now.to_i })
|
|
File.write(CACHE_FILE, cache_data.to_yaml)
|
|
end
|
|
|
|
def self.ca_bundle
|
|
CA_BUNDLE_PATHS.find { |path| File.exist?(path) }
|
|
end
|
|
|
|
def self.http_client(uri)
|
|
client = Net::HTTP.new(uri.host, uri.port)
|
|
client.use_ssl = true
|
|
client.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
|
bundle = ca_bundle
|
|
client.ca_file = bundle if bundle
|
|
client
|
|
end
|
|
|
|
def self.fetch_from_encapi
|
|
uri = URI("https://encapi.k8s.syd1.au.unkin.net/cblr/svc/op/puppet/hostname/#{Facter.value(:fqdn) || Facter.value(:hostname)}")
|
|
response = http_client(uri).request(Net::HTTP::Get.new(uri))
|
|
|
|
raise "Failed to fetch ENC data. HTTP #{response.code}" unless response.is_a?(Net::HTTPSuccess)
|
|
|
|
YAML.safe_load(response.body) || {}
|
|
end
|
|
|
|
def self.retrieve_enc_data
|
|
return @enc_data if @enc_data
|
|
|
|
@enc_data = fetch_from_encapi
|
|
write_cache(@enc_data)
|
|
@enc_data
|
|
end
|
|
|
|
def self.fetch_enc_data
|
|
retrieve_enc_data
|
|
rescue StandardError => e
|
|
Facter.warn("Error retrieving encapi ENC data: #{e.message}")
|
|
@enc_data = read_cache
|
|
return @enc_data unless @enc_data.empty?
|
|
|
|
raise 'No cached ENC data available and encapi is unreachable.'
|
|
end
|
|
|
|
def self.enc_role
|
|
fetch_enc_data.fetch('classes', {}).keys.first || raise('ENC Role not found in encapi ENC response')
|
|
end
|
|
|
|
def self.enc_env
|
|
fetch_enc_data.fetch('environment', nil) || raise('ENC Environment not found in encapi ENC response')
|
|
end
|
|
end
|
|
|
|
Facter.add('enc_role') do
|
|
setcode { EncapiENC.enc_role }
|
|
end
|
|
|
|
Facter.add('enc_env') do
|
|
setcode { EncapiENC.enc_env }
|
|
end
|