From 86c5d03a9fdd693f113163a477ed5bbd8a136878 Mon Sep 17 00:00:00 2001 From: Christopher Holmes Date: Tue, 25 May 2021 02:33:34 +0100 Subject: [PATCH] feat: add a SSL verify mode option for the OTLP exporter (#768) * feat: add a verify mode option for the OTLP exporter This change covers adding configuration to the OTLP Exporter to support exporting to Collectors that have certificates that can't be verified by OpenSSL. This approach is copied from the similar change for the Jaeger CollectorExporter: https://github.com/open-telemetry/opentelemetry-ruby/pull/743 * add documentation on OTLP exporter configuration * Fix typo in README Co-authored-by: Ariel Valentin Co-authored-by: Ariel Valentin Co-authored-by: Francis Bogsanyi --- exporter/otlp/README.md | 16 +++++++++ .../opentelemetry/exporter/otlp/exporter.rb | 14 +++++++- .../exporter/otlp/exporter_test.rb | 33 +++++++++++++++++++ 3 files changed, 62 insertions(+), 1 deletion(-) diff --git a/exporter/otlp/README.md b/exporter/otlp/README.md index 0567823d8..974a9955b 100644 --- a/exporter/otlp/README.md +++ b/exporter/otlp/README.md @@ -65,6 +65,22 @@ end For additional examples, see the [examples on github][examples-github]. +## How can I configure the OTLP exporter? + +The collector exporter can be configured explicitly in code, as shown above, or via environment variables. The configuration parameters, environment variables, and defaults are shown below. + +| Parameter | Environment variable | Default | +| ------------------- | -------------------------------------------- | ----------------------------------- | +| `endpoint:` | `OTEL_EXPORTER_OTLP_ENDPOINT` | `"http://localhost:4317/v1/traces"` | +| `certificate_file: `| `OTEL_EXPORTER_OTLP_CERTIFICATE` | | +| `headers:` | `OTEL_EXPORTER_OTLP_HEADERS` | | +| `compression:` | `OTEL_EXPORTER_OTLP_COMPRESSION` | | +| `timeout:` | `OTEL_EXPORTER_OTLP_TIMEOUT` | `10` | +| `ssl_verify_mode:` | `OTEL_RUBY_EXPORTER_OTLP_SSL_VERIFY_PEER` or | `OpenSSL::SSL:VERIFY_PEER` | +| | `OTEL_RUBY_EXPORTER_OTLP_SSL_VERIFY_NONE` | | + +`ssl_verify_mode:` parameter values should be flags for server certificate verification: `OpenSSL::SSL:VERIFY_PEER` and `OpenSSL::SSL:VERIFY_NONE` are acceptable. These values can also be set using the appropriately named environment variables as shown where `VERIFY_PEER` will take precedence over `VERIFY_NONE`. Please see [the Net::HTTP docs](https://ruby-doc.org/stdlib-2.5.1/libdoc/net/http/rdoc/Net/HTTP.html#verify_mode) for more information about these flags. + ## How can I get involved? The `opentelemetry-exporter-otlp` gem source is [on github][repo-github], along with related gems including `opentelemetry-sdk`. diff --git a/exporter/otlp/lib/opentelemetry/exporter/otlp/exporter.rb b/exporter/otlp/lib/opentelemetry/exporter/otlp/exporter.rb index 168ce5cd8..6a03afdb1 100644 --- a/exporter/otlp/lib/opentelemetry/exporter/otlp/exporter.rb +++ b/exporter/otlp/lib/opentelemetry/exporter/otlp/exporter.rb @@ -31,8 +31,19 @@ class Exporter # rubocop:disable Metrics/ClassLength WRITE_TIMEOUT_SUPPORTED = Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('2.6') private_constant(:KEEP_ALIVE_TIMEOUT, :RETRY_COUNT, :WRITE_TIMEOUT_SUPPORTED) - def initialize(endpoint: config_opt('OTEL_EXPORTER_OTLP_TRACES_ENDPOINT', 'OTEL_EXPORTER_OTLP_ENDPOINT', default: 'https://localhost:4317/v1/traces'), # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity + def self.ssl_verify_mode + if ENV.key?('OTEL_RUBY_EXPORTER_OTLP_SSL_VERIFY_PEER') + OpenSSL::SSL::VERIFY_PEER + elsif ENV.key?('OTEL_RUBY_EXPORTER_OTLP_SSL_VERIFY_NONE') + OpenSSL::SSL::VERIFY_NONE + else + OpenSSL::SSL::VERIFY_PEER + end + end + + def initialize(endpoint: config_opt('OTEL_EXPORTER_OTLP_TRACES_ENDPOINT', 'OTEL_EXPORTER_OTLP_ENDPOINT', default: 'https://localhost:4317/v1/traces'), # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/MethodLength certificate_file: config_opt('OTEL_EXPORTER_OTLP_TRACES_CERTIFICATE', 'OTEL_EXPORTER_OTLP_CERTIFICATE'), + ssl_verify_mode: Exporter.ssl_verify_mode, headers: config_opt('OTEL_EXPORTER_OTLP_TRACES_HEADERS', 'OTEL_EXPORTER_OTLP_HEADERS'), compression: config_opt('OTEL_EXPORTER_OTLP_TRACES_COMPRESSION', 'OTEL_EXPORTER_OTLP_COMPRESSION'), timeout: config_opt('OTEL_EXPORTER_OTLP_TRACES_TIMEOUT', 'OTEL_EXPORTER_OTLP_TIMEOUT', default: 10), @@ -49,6 +60,7 @@ def initialize(endpoint: config_opt('OTEL_EXPORTER_OTLP_TRACES_ENDPOINT', 'OTEL_ @http = Net::HTTP.new(@uri.host, @uri.port) @http.use_ssl = @uri.scheme == 'https' + @http.verify_mode = ssl_verify_mode @http.ca_file = certificate_file unless certificate_file.nil? @http.keep_alive_timeout = KEEP_ALIVE_TIMEOUT diff --git a/exporter/otlp/test/opentelemetry/exporter/otlp/exporter_test.rb b/exporter/otlp/test/opentelemetry/exporter/otlp/exporter_test.rb index 83dae63ff..7d760a2af 100644 --- a/exporter/otlp/test/opentelemetry/exporter/otlp/exporter_test.rb +++ b/exporter/otlp/test/opentelemetry/exporter/otlp/exporter_test.rb @@ -22,6 +22,7 @@ _(http.ca_file).must_be_nil _(http.use_ssl?).must_equal true _(http.address).must_equal 'localhost' + _(http.verify_mode).must_equal OpenSSL::SSL::VERIFY_PEER _(http.port).must_equal 4317 end @@ -57,6 +58,7 @@ 'OTEL_EXPORTER_OTLP_CERTIFICATE' => '/foo/bar', 'OTEL_EXPORTER_OTLP_HEADERS' => 'a=b,c=d', 'OTEL_EXPORTER_OTLP_COMPRESSION' => 'gzip', + 'OTEL_RUBY_EXPORTER_OTLP_SSL_VERIFY_NONE' => 'true', 'OTEL_EXPORTER_OTLP_TIMEOUT' => '11') do OpenTelemetry::Exporter::OTLP::Exporter.new end @@ -68,6 +70,7 @@ _(http.ca_file).must_equal '/foo/bar' _(http.use_ssl?).must_equal false _(http.address).must_equal 'localhost' + _(http.verify_mode).must_equal OpenSSL::SSL::VERIFY_NONE _(http.port).must_equal 1234 end @@ -76,11 +79,13 @@ 'OTEL_EXPORTER_OTLP_CERTIFICATE' => '/foo/bar', 'OTEL_EXPORTER_OTLP_HEADERS' => 'a:b,c:d', 'OTEL_EXPORTER_OTLP_COMPRESSION' => 'flate', + 'OTEL_RUBY_EXPORTER_OTLP_SSL_VERIFY_PEER' => 'true', 'OTEL_EXPORTER_OTLP_TIMEOUT' => '11') do OpenTelemetry::Exporter::OTLP::Exporter.new(endpoint: 'http://localhost:4321', certificate_file: '/baz', headers: { 'x' => 'y' }, compression: 'gzip', + ssl_verify_mode: OpenSSL::SSL::VERIFY_NONE, timeout: 12) end _(exp.instance_variable_get(:@headers)).must_equal('x' => 'y') @@ -90,11 +95,39 @@ http = exp.instance_variable_get(:@http) _(http.ca_file).must_equal '/baz' _(http.use_ssl?).must_equal false + _(http.verify_mode).must_equal OpenSSL::SSL::VERIFY_NONE _(http.address).must_equal 'localhost' _(http.port).must_equal 4321 end end + describe 'ssl_verify_mode:' do + it 'can be set to VERIFY_NONE by an envvar' do + exp = with_env('OTEL_RUBY_EXPORTER_OTLP_SSL_VERIFY_NONE' => 'true') do + OpenTelemetry::Exporter::OTLP::Exporter.new + end + http = exp.instance_variable_get(:@http) + _(http.verify_mode).must_equal OpenSSL::SSL::VERIFY_NONE + end + + it 'can be set to VERIFY_PEER by an envvar' do + exp = with_env('OTEL_RUBY_EXPORTER_OTLP_SSL_VERIFY_PEER' => 'true') do + OpenTelemetry::Exporter::OTLP::Exporter.new + end + http = exp.instance_variable_get(:@http) + _(http.verify_mode).must_equal OpenSSL::SSL::VERIFY_PEER + end + + it 'VERIFY_PEER will override VERIFY_NONE' do + exp = with_env('OTEL_RUBY_EXPORTER_OTLP_SSL_VERIFY_NONE' => 'true', + 'OTEL_RUBY_EXPORTER_OTLP_SSL_VERIFY_PEER' => 'true') do + OpenTelemetry::Exporter::OTLP::Exporter.new + end + http = exp.instance_variable_get(:@http) + _(http.verify_mode).must_equal OpenSSL::SSL::VERIFY_PEER + end + end + describe '#export' do let(:exporter) { OpenTelemetry::Exporter::OTLP::Exporter.new }