Module: Fluent::Plugin::PublicLoggingSetup

Included in:
OCILoggingOutput
Defined in:
lib/fluent/plugin/logging_setup.rb

Overview

Setup code for OCI Logging

Constant Summary collapse

RETRIES =
3
USER_SIGNER_TYPE =
'user'
AUTO_SIGNER_TYPE =
'auto'
LINUX_OCI_CONFIG_DIR =
Fluent::Plugin::PublicLoggingUtils.determine_linux_config_path
WINDOWS_OCI_CONFIG_DIR =
Fluent::Plugin::PublicLoggingUtils.determine_windows_config_path
USER_CONFIG_PROFILE_NAME =
Fluent::Plugin::PublicLoggingUtils.determine_config_profile_name(LINUX_OCI_CONFIG_DIR, WINDOWS_OCI_CONFIG_DIR)
PUBLIC_RESOURCE_PRINCIPAL_ENV_FILE =
'/etc/resource_principal_env'
R1_CA_PATH =
'/etc/pki/tls/certs/ca-bundle.crt'
PUBLIC_DEFAULT_LINUX_CA_PATH =
'/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem'
PUBLIC_DEFAULT_WINDOWS_CA_PATH =
'C:\\oracle_unified_agent\\unified-monitoring-agent\\embedded\\ssl\\certs\\cacert.pem'
PUBLIC_DEFAULT_UBUNTU_CA_PATH =
'/etc/ssl/certs/ca-certificates.crt'
PUBLIC_DEFAULT_DEBIAN_CA_PATH =
PUBLIC_DEFAULT_UBUNTU_CA_PATH

Instance Method Summary collapse

Instance Method Details

#add_rp_env_overrideObject



260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
# File 'lib/fluent/plugin/logging_setup.rb', line 260

def add_rp_env_override
  env_file = ENV['LOCAL_TEST_ENV_FILE'] || PUBLIC_RESOURCE_PRINCIPAL_ENV_FILE

  resource_principal_env = {}

  raise 'resource principal env file does not exist' unless File.exist? env_file

  file = File.readlines(env_file)
  file.each do |env|
    a = env.split('=')
    resource_principal_env[a[0]] = a[1].chomp
  end

  logger.info("resource principal env is setup with #{resource_principal_env}")
  ENV.update resource_principal_env
end

#create_instance_principal_signerObject



277
278
279
280
281
282
283
284
285
286
287
# File 'lib/fluent/plugin/logging_setup.rb', line 277

def create_instance_principal_signer
  endpoint = @federation_endpoint_override || get_federation_endpoint(@region)

  unless endpoint.nil?
    logger.info "create instance principal with  federation_endpoint = #{endpoint}, cert_bundle #{@ca_file}"
  end

  ::OCI::Auth::Signers::InstancePrincipalsSecurityTokenSigner.new(
    federation_endpoint: endpoint, federation_client_cert_bundle: @ca_file
  )
end

#create_resource_principal_signerObject



251
252
253
254
255
256
257
258
# File 'lib/fluent/plugin/logging_setup.rb', line 251

def create_resource_principal_signer
  logger.info 'creating resource principal'
  add_rp_env_override
  OCI::Auth::Signers.resource_principals_signer
rescue StandardError => e
  logger.info("fail to create resource principal with error #{e}")
  nil
end

#get_federation_endpoint(region) ⇒ String

Calculate federation endpoints based on metadata and optional inputs

Parameters:

  • region (String)

    the region identifier

Returns:

  • (String)

    the federation endpoint that will be used



42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
# File 'lib/fluent/plugin/logging_setup.rb', line 42

def get_federation_endpoint(region)
  if region == 'r1'
    endpoint = ENV['FEDERATION_ENDPOINT'] || 'https://auth.r1.oracleiaas.com/v1/x509'
  else
    if @realmDomainComponent.nil?
      logger.warn('realm domain is null, fall back to OCI Regions')
      @realmDomainComponent = OCI::Regions.get_second_level_domain(region) if @realmDomainComponent.nil?
    end

    endpoint = ENV['FEDERATION_ENDPOINT'] || "https://auth.#{region}.#{@realmDomainComponent}/v1/x509"
  end

  logger.info("endpoint is #{endpoint} in region #{region}")
  endpoint
end

#get_host_info_for_non_oci_instance(oci_config) ⇒ Object



188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
# File 'lib/fluent/plugin/logging_setup.rb', line 188

def get_host_info_for_non_oci_instance(oci_config)
  # set needed properties
  @region = oci_config.region
  # for non-OCI instances we can't get the display_name or hostname from IMDS and the fallback is the ip address
  # of the machine
  begin
    @hostname = Socket.gethostname
  rescue StandardError
    ip = Socket.ip_address_list.detect { |intf| intf.ipv4_private? }
    @hostname = ip ? ip.ip_address : 'UNKNOWN'
  end

  # No metadata service support for non-OCI instances
  @realmDomainComponent = OCI::Regions.get_second_level_domain(@region)

  logger.info("in non oci instance, region is #{@region}, "\
              " hostname is #{@hostname}, realm is #{@realmDomainComponent}")
end

#get_host_info_for_oci_instanceObject



207
208
209
210
211
212
213
214
215
216
217
# File 'lib/fluent/plugin/logging_setup.rb', line 207

def get_host_info_for_oci_instance
  md = @metadata_override || get_instance_md_with_retry

  @region = md['canonicalRegionName'] == 'us-seattle-1' ? 'r1' : md['canonicalRegionName']
  @hostname = md.key?('displayName') ? md['displayName'] : ''
  @realmDomainComponent = md.fetch('regionInfo', {}).fetch(
    'realmDomainComponent', OCI::Regions.get_second_level_domain(@region)
  )
  logger.info("in oci instance, region is #{@region},  hostname is"\
    " #{@hostname}, realm is #{@realmDomainComponent}")
end

#get_instance_mdObject



64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
# File 'lib/fluent/plugin/logging_setup.rb', line 64

def get_instance_md
  # v2 of IMDS requires an authorization header
  md = get_instance_md_with_url('http://169.254.169.254/opc/v2/instance/')
  if md.nil?
    logger.info('IMDS v2 is not available, use v1')
    md = get_instance_md_with_url('http://169.254.169.254/opc/v1/instance/')
  end

  if !md.nil?
    logger.info "Successfully fetch instance metadata for hosts in overlay #{md}"
    return md
  else
    raise StandardError, 'Failure fetching instance metadata, possible '\
      'reason is network issue or host is not OCI instance'
  end
end

#get_instance_md_with_retry(retries = RETRIES) ⇒ Object



58
59
60
61
62
# File 'lib/fluent/plugin/logging_setup.rb', line 58

def get_instance_md_with_retry(retries = RETRIES)
  Retriable.retriable(tries: retries, on: StandardError, timeout: 12) do
    return get_instance_md
  end
end

#get_instance_md_with_url(uri_link) ⇒ Object



81
82
83
84
85
86
87
88
89
90
91
92
93
# File 'lib/fluent/plugin/logging_setup.rb', line 81

def get_instance_md_with_url(uri_link)
  uri = URI.parse(uri_link)
  http = ::Net::HTTP.new(uri.host, uri.port)
  http.open_timeout = 2 # in seconds
  http.read_timeout = 2 # in seconds
  request = ::Net::HTTP::Get.new(uri.request_uri)
  request.add_field('Authorization', 'Bearer Oracle') if uri_link.include?('v2')
  resp = http.request(request)
  JSON.parse(resp.body)
rescue StandardError
  logger.warn("failed to get instance metadata with link #{uri_link}")
  nil
end

#get_logging_endpoint(region, logging_endpoint_override: nil) ⇒ String

Calculate logging endpoint from environment or metadata. Preference is given to the environment variable ‘LOGGING_FRONTEND’.

Parameters:

  • region (String)

    the region identifier

Returns:

  • (String)

    The logging endpoint that will be used.



102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
# File 'lib/fluent/plugin/logging_setup.rb', line 102

def get_logging_endpoint(region, logging_endpoint_override: nil)
  unless logging_endpoint_override.nil?
    logger.info "using logging endpoint override #{logging_endpoint_override} for testing"
    return logging_endpoint_override
  end

  if region == 'r1'
    endpoint = ENV['LOGGING_FRONTEND'] || "https://ingestion.logging.#{region}.oci.oracleiaas.com"
  else
    if @realmDomainComponent.nil?
      logger.warn('realm domain is null, fall back to OCI Regions')
      @realmDomainComponent = OCI::Regions.get_second_level_domain(region) if @realmDomainComponent.nil?
    end

    endpoint = ENV['LOGGING_FRONTEND'] || "https://ingestion.logging.#{region}.oci.#{@realmDomainComponent}"
  end

  logger.info("endpoint is #{endpoint} in region #{region}")
  endpoint
end

#get_signer(oci_config, signer_type) ⇒ OCI::Signer

Configure the signer for the logging client call

Parameters:

  • signer_type (String)

    the type of signer that should be returned

Returns:

  • (OCI::Signer)

    a signer that is representative of the signer type



157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
# File 'lib/fluent/plugin/logging_setup.rb', line 157

def get_signer(oci_config, signer_type)
  case signer_type
  when USER_SIGNER_TYPE
    get_host_info_for_non_oci_instance(oci_config)
    set_default_ca_file
    OCI::Signer.new(
      oci_config.user,
      oci_config.fingerprint,
      oci_config.tenancy,
      oci_config.key_file,
      pass_phrase: oci_config.pass_phrase
    )
  when AUTO_SIGNER_TYPE
    logger.info 'signer type is "auto", creating signer based on system setup'
    get_host_info_for_oci_instance
    set_default_ca_file
    signer = create_resource_principal_signer
    if signer.nil?
      logger.info('resource principal is not setup, use instance principal instead')
      signer = create_instance_principal_signer
    else
      logger.info('use resource principal')
    end

    signer
  else
    raise StandardError, "Principal type #{signer_type} not supported, "\
      "use 'instance', 'resource' or 'user' instead"
  end
end

#get_signer_type(principal_override: nil, config_dir: nil) ⇒ Object



123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
# File 'lib/fluent/plugin/logging_setup.rb', line 123

def get_signer_type(principal_override: nil, config_dir: nil)
  config_dir ||= OS.windows? ?  WINDOWS_OCI_CONFIG_DIR : LINUX_OCI_CONFIG_DIR

  if (File.file?(config_dir) && principal_override != AUTO_SIGNER_TYPE) || principal_override == USER_SIGNER_TYPE
    begin
      logger.info("using #{USER_SIGNER_TYPE} signer type with config dir #{config_dir}")
      oci_config = OCI::ConfigFileLoader.load_config(
        config_file_location: config_dir, profile_name: USER_CONFIG_PROFILE_NAME
      )
      signer_type = USER_SIGNER_TYPE
    rescue StandardError => e
      if e.full_message.include?('Profile not found in the given config file.')
        logger.warn("Profile #{USER_CONFIG_PROFILE_NAME} not configured "\
          "in user api-key cli using other principal instead: #{e}")
        signer_type = AUTO_SIGNER_TYPE
        oci_config = OCI::Config.new
      else
        raise "User api-keys not setup correctly: #{e}"
      end
    end
  else # if user api-keys is not setup in the expected format we expect instance principal
    logger.info("using #{AUTO_SIGNER_TYPE} signer type")
    signer_type = AUTO_SIGNER_TYPE
    oci_config = OCI::Config.new
  end
  [oci_config, signer_type]
end

#loggerObject



32
33
34
# File 'lib/fluent/plugin/logging_setup.rb', line 32

def logger
  @log ||= OS.windows? ? Logger.new(WINDOWS_UPLOADER_OUTPUT_LOG) : Logger.new($stdout)
end

#set_default_ca_fileObject

Since r1 overlay has a different default make sure to update this



222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
# File 'lib/fluent/plugin/logging_setup.rb', line 222

def set_default_ca_file
  @ca_file = PUBLIC_DEFAULT_LINUX_CA_PATH if @ca_file.nil?
  if OS.windows?
    @ca_file = @ca_file == PUBLIC_DEFAULT_LINUX_CA_PATH ? PUBLIC_DEFAULT_WINDOWS_CA_PATH : @ca_file
  elsif OS.ubuntu?
    @ca_file = @ca_file == PUBLIC_DEFAULT_LINUX_CA_PATH ? PUBLIC_DEFAULT_UBUNTU_CA_PATH : @ca_file
  elsif OS.debian?
    @ca_file = @ca_file == PUBLIC_DEFAULT_LINUX_CA_PATH ? PUBLIC_DEFAULT_DEBIAN_CA_PATH : @ca_file
  else
    @ca_file = @region == 'r1' && @ca_file == PUBLIC_DEFAULT_LINUX_CA_PATH ? R1_CA_PATH : @ca_file
  end

  if @ca_file.nil?
    msg = 'ca_file is not specified'
    logger.error msg
    raise StandardError, msg
  end

  # verify the ssl bundle actually exists
  unless File.file?(@ca_file)
    msg = "Does not exist or cannot open ca file: #{@ca_file}"
    logger.error msg
    raise StandardError, msg
  end

  # setting the cert_bundle_path
  logger.info "using cert_bundle_path #{@ca_file}"
end