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

Verify container TLS, support custom CA #14019

Merged
merged 2 commits into from
Mar 1, 2017
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
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,19 @@ module ManageIQ::Providers::Kubernetes::ContainerManager::MetricsCapture::Hawkul
def hawkular_client
require 'hawkular/hawkular_client'

@hawkular_entrypoint ||= hawkular_entrypoint
@hawkular_uri ||= hawkular_uri
@hawkular_credentials ||= hawkular_credentials
@hawkular_options ||= hawkular_options

Hawkular::Metrics::Client.new(
@hawkular_entrypoint, @hawkular_credentials, @hawkular_options)
Hawkular::Metrics::Client.new(@hawkular_uri, @hawkular_credentials, @hawkular_options)
end

def hawkular_entrypoint
hawkular_endpoint = @ext_management_system.connection_configurations.hawkular.try(:endpoint)
# may be nil
def hawkular_endpoint
@ext_management_system.connection_configurations.hawkular.try(:endpoint)
end

def hawkular_uri
hawkular_endpoint_empty = hawkular_endpoint.try(:hostname).blank?
worker_class = ManageIQ::Providers::Kubernetes::ContainerManager::MetricsCollectorWorker

Expand All @@ -26,9 +29,11 @@ def hawkular_credentials
end

def hawkular_options
{ :tenant => @tenant,
:verify_ssl => @ext_management_system.verify_ssl_mode,
:http_proxy_uri => VMDB::Util.http_proxy_uri.to_s
{
:tenant => @tenant,
:http_proxy_uri => VMDB::Util.http_proxy_uri.to_s,
:verify_ssl => @ext_management_system.verify_ssl_mode(hawkular_endpoint),
:ssl_cert_store => @ext_management_system.ssl_cert_store(hawkular_endpoint),
}
end

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,8 @@ def pod_wait
health_url = pod_proxy_url(client, INSPECTOR_HEALTH_PATH)
http_options = {
:use_ssl => health_url.scheme == 'https',
:verify_mode => ext_management_system.verify_ssl_mode
:verify_mode => ext_management_system.verify_ssl_mode,
:cert_store => ext_management_system.ssl_cert_store,
}

# TODO: move this to a more appropriate place (lib)
Expand Down Expand Up @@ -281,7 +282,10 @@ def image_inspector_client
ImageInspectorClient::Client.new(
pod_proxy_url(kubeclient, ''),
'v1',
:ssl_options => kubeclient.ssl_options,
:ssl_options => {
:verify_ssl => ext_management_system.verify_ssl_mode,
:cert_store => ext_management_system.ssl_cert_store
},
:auth_options => kubeclient.auth_options,
:http_proxy_uri => kubeclient.http_proxy_uri
)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
require 'openssl'
require 'MiqContainerGroup/MiqContainerGroup'

module ManageIQ::Providers::Kubernetes::ContainerManagerMixin
Expand All @@ -18,18 +19,13 @@ def raw_api_endpoint(hostname, port, path = '')
URI::HTTPS.build(:host => hostname, :port => port.presence.try(:to_i), :path => path)
end

def verify_ssl_mode
# TODO: support real authentication using certificates
OpenSSL::SSL::VERIFY_NONE
end

def kubernetes_connect(hostname, port, options)
require 'kubeclient'

Kubeclient::Client.new(
raw_api_endpoint(hostname, port, options[:path]),
options[:version] || kubernetes_version,
:ssl_options => { :verify_ssl => verify_ssl_mode },
:ssl_options => Kubeclient::Client::DEFAULT_SSL_OPTIONS.merge(options[:ssl_options] || {}),
:auth_options => kubernetes_auth_options(options),
:http_proxy_uri => VMDB::Util.http_proxy_uri
)
Expand Down Expand Up @@ -62,13 +58,33 @@ def supports_port?
true
end

def supports_security_protocol?
true
end

def api_endpoint
self.class.raw_api_endpoint(hostname, port)
end

def verify_ssl_mode
# TODO: support real authentication using certificates
self.class.verify_ssl_mode
def verify_ssl_mode(endpoint = default_endpoint)
return OpenSSL::SSL::VERIFY_PEER if endpoint.nil? # secure by default

case endpoint.security_protocol
when nil, ''
# Previously providers didn't set security_protocol, defaulted to
# verify_ssl == 1 (VERIFY_PEER) which wasn't enforced but now is.
# However, if they explicitly set verify_ssl == 0, we'll respect that.
endpoint.verify_ssl? ? OpenSSL::SSL::VERIFY_PEER : OpenSSL::SSL::VERIFY_NONE
when 'ssl-without-validation'
OpenSSL::SSL::VERIFY_NONE
else # 'ssl-with-validation', 'ssl-with-validation-custom-ca', secure by default with unexpected values.
OpenSSL::SSL::VERIFY_PEER
end
end

def ssl_cert_store(endpoint = default_endpoint)
# Given missing (nil) endpoint, return nil meaning use system CA bundle
endpoint.try(:ssl_cert_store)
end

def connect(options = {})
Expand All @@ -78,6 +94,10 @@ def connect(options = {})
:user => options[:user] || authentication_userid(options[:auth_type]),
:pass => options[:pass] || authentication_password(options[:auth_type]),
:bearer => options[:bearer] || authentication_token(options[:auth_type] || 'bearer'),
:ssl_options => options[:ssl_options] || {
:verify_ssl => verify_ssl_mode,
:cert_store => ssl_cert_store
}
)
self.class.raw_connect(effective_options[:hostname], effective_options[:port], effective_options)
end
Expand Down Expand Up @@ -183,8 +203,13 @@ def scan_entity_create(scan_data)
scan_data[:pod_name],
scan_data[:pod_port],
scan_data[:pod_namespace])
nethttp_options = {
:use_ssl => true,
:verify_mode => verify_ssl_mode,
:cert_store => ssl_cert_store,
}
MiqContainerGroup.new(pod_proxy + SCAN_CONTENT_PATH,
verify_ssl_mode,
nethttp_options,
client.headers.stringify_keys,
scan_data[:guest_os])
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ def openshift_connect(hostname, port, options)
Kubeclient::Client.new(
raw_api_endpoint(hostname, port, '/oapi'),
options[:version] || api_version,
:ssl_options => { :verify_ssl => verify_ssl_mode },
:ssl_options => Kubeclient::Client::DEFAULT_SSL_OPTIONS.merge(options[:ssl_options] || {}),
:auth_options => kubernetes_auth_options(options),
:http_proxy_uri => VMDB::Util.http_proxy_uri
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ def create_pod(*_args)
end

def proxy_url(*_args)
'http://test.com'
'https://test.com'
end

def headers(*_args)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,49 @@
expect(described_class.raw_api_endpoint("::1", 123).to_s).to eq "https://[::1]:123"
end

context "#verify_ssl_mode" do
let(:ems) { FactoryGirl.build(:ems_kubernetes) }

it "is secure without endpoint" do
expect(ems.verify_ssl_mode(nil)).to eq(OpenSSL::SSL::VERIFY_PEER)
end

it "is secure for old providers without security_protocol" do
endpoint = Endpoint.new(:verify_ssl => nil)
expect(ems.verify_ssl_mode(endpoint)).to eq(OpenSSL::SSL::VERIFY_PEER)
# In practice both API and UI used set verify_ssl == VERIFY_PEER.
endpoint = Endpoint.new(:verify_ssl => OpenSSL::SSL::VERIFY_PEER)
expect(ems.verify_ssl_mode(endpoint)).to eq(OpenSSL::SSL::VERIFY_PEER)
end

it "respects explicit verify_ssl == 0 in absence of security_protocol" do
endpoint = Endpoint.new(:verify_ssl => OpenSSL::SSL::VERIFY_NONE)
expect(endpoint.verify_ssl?).to be_falsey
expect(ems.verify_ssl_mode(endpoint)).to eq(OpenSSL::SSL::VERIFY_NONE)
end

it "uses security_protocol when given" do
# security_protocol should win over opposite verify_ssl
endpoint = Endpoint.new(:security_protocol => 'ssl-with-validation',
:verify_ssl => OpenSSL::SSL::VERIFY_NONE)
expect(ems.verify_ssl_mode(endpoint)).to eq(OpenSSL::SSL::VERIFY_PEER)

# The UI doesn't currently use 'ssl' but it's plausible someone
# would send this via API. Generally unexpect values should mean secure.
endpoint = Endpoint.new(:security_protocol => 'ssl',
:verify_ssl => OpenSSL::SSL::VERIFY_NONE)
expect(ems.verify_ssl_mode(endpoint)).to eq(OpenSSL::SSL::VERIFY_PEER)

endpoint = Endpoint.new(:security_protocol => 'ssl-with-validation-custom-ca',
:verify_ssl => OpenSSL::SSL::VERIFY_NONE)
expect(ems.verify_ssl_mode(endpoint)).to eq(OpenSSL::SSL::VERIFY_PEER)

endpoint = Endpoint.new(:security_protocol => 'ssl-without-validation',
:verify_ssl => OpenSSL::SSL::VERIFY_PEER)
expect(ems.verify_ssl_mode(endpoint)).to eq(OpenSSL::SSL::VERIFY_NONE)
end
end

context "SmartState Analysis Methods" do
before(:each) do
EvmSpecHelper.local_miq_server(:zone => Zone.seed)
Expand Down Expand Up @@ -42,7 +85,7 @@
)

expect(entity).to be_kind_of(MiqContainerGroup)
expect(entity.verify_mode).to eq(@ems.verify_ssl_mode)
expect(entity.http_options).to include(:use_ssl => true, :verify_mode => @ems.verify_ssl_mode)
expect(entity.headers).to eq("Authorization" => "Bearer valid-token")
expect(entity.guest_os).to eq('GuestOS')
end
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.