Commit 0763c82f authored by Changzheng Liu's avatar Changzheng Liu Committed by Dylan Griffith

Send AWS credentials via environment variables to indexer

parent f5489998
---
title: Send AWS credentials via environment variables to indexer
merge_request: 36917
author:
type: changed
...@@ -3,6 +3,8 @@ ...@@ -3,6 +3,8 @@
module Gitlab module Gitlab
module Elastic module Elastic
module Client module Client
extend Gitlab::Utils::StrongMemoize
# Takes a hash as returned by `ApplicationSetting#elasticsearch_config`, # Takes a hash as returned by `ApplicationSetting#elasticsearch_config`,
# and configures itself based on those parameters # and configures itself based on those parameters
def self.build(config) def self.build(config)
...@@ -32,13 +34,18 @@ module Gitlab ...@@ -32,13 +34,18 @@ module Gitlab
return static_credentials if static_credentials&.set? return static_credentials if static_credentials&.set?
# When static credentials are not configured, Aws::CredentialProviderChain API # When static credentials are not configured, use Aws::CredentialProviderChain API
# will be used to retrieve credentials. It will check AWS access credential environment aws_credential_provider if aws_credential_provider&.set?
end
def self.aws_credential_provider
# Aws::CredentialProviderChain API will check AWS access credential environment
# variables, AWS credential profile, ECS credential service and EC2 credential service. # variables, AWS credential profile, ECS credential service and EC2 credential service.
# Please see aws-sdk-core/lib/aws-sdk-core/credential_provider_chain.rb for details of # Please see aws-sdk-core/lib/aws-sdk-core/credential_provider_chain.rb for details of
# the possible providers and order of the providers. # the possible providers and order of the providers.
instance_credentials = Aws::CredentialProviderChain.new.resolve strong_memoize(:instance_credentials) do
instance_credentials if instance_credentials&.set? Aws::CredentialProviderChain.new.resolve
end
end end
end end
end end
......
...@@ -109,11 +109,26 @@ module Gitlab ...@@ -109,11 +109,26 @@ module Gitlab
'SSL_CERT_DIR' => OpenSSL::X509::DEFAULT_CERT_DIR 'SSL_CERT_DIR' => OpenSSL::X509::DEFAULT_CERT_DIR
} }
# Set AWS environment variables for IAM role authentication if present
vars = build_aws_credentials_env(vars)
# Users can override default SSL certificate path via SSL_CERT_FILE SSL_CERT_DIR # Users can override default SSL certificate path via SSL_CERT_FILE SSL_CERT_DIR
# AWS_CONTAINER_CREDENTIALS_RELATIVE_URI is used in AWS ECS to get credentials when making AWS API calls vars.merge(ENV.slice('SSL_CERT_FILE', 'SSL_CERT_DIR'))
%w(SSL_CERT_FILE SSL_CERT_DIR AWS_CONTAINER_CREDENTIALS_RELATIVE_URI).each_with_object(vars) do |key, hash| end
hash[key] = ENV[key] if ENV.key?(key)
end def build_aws_credentials_env(vars)
# AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN need to be set as
# environment variable in case of using IAM role based authentication in AWS
# The credentials are buffered to prevent from hitting rate limit. They will be
# refreshed when expired
credentials = Gitlab::Elastic::Client.aws_credential_provider&.credentials
return vars unless credentials&.set?
vars.merge(
'AWS_ACCESS_KEY_ID' => credentials.access_key_id,
'AWS_SECRET_ACCESS_KEY' => credentials.secret_access_key,
'AWS_SESSION_TOKEN' => credentials.session_token
)
end end
def last_commit def last_commit
......
...@@ -78,7 +78,6 @@ RSpec.describe Gitlab::Elastic::Client do ...@@ -78,7 +78,6 @@ RSpec.describe Gitlab::Elastic::Client do
aws_region: 'us-east-1' aws_region: 'us-east-1'
} }
end end
let(:credentials) { double(:aws_credentials, set?: true) }
before do before do
allow_next_instance_of(Aws::CredentialProviderChain) do |instance| allow_next_instance_of(Aws::CredentialProviderChain) do |instance|
...@@ -86,25 +85,63 @@ RSpec.describe Gitlab::Elastic::Client do ...@@ -86,25 +85,63 @@ RSpec.describe Gitlab::Elastic::Client do
end end
end end
it 'returns credentials from Aws::CredentialProviderChain' do after do
expect(creds).to eq credentials described_class.clear_memoization(:instance_credentials)
end end
context 'when Aws::CredentialProviderChain returns unset credentials' do context 'when aws sdk provides credentials' do
let(:credentials) { double(:aws_credentials, set?: false) } let(:credentials) { double(:aws_credentials, set?: true) }
it 'returns nil' do it 'return the credentials' do
expect(creds).to be_nil expect(creds).to eq(credentials)
end end
end end
context 'when Aws::CredentialProviderChain returns nil' do context 'when aws sdk does not provide credentials' do
let(:credentials) { nil } let(:credentials) { nil }
it 'return the credentials' do
expect(creds).to eq(nil)
end
end
context 'when Aws::CredentialProviderChain returns unset credentials' do
let(:credentials) { double(:aws_credentials, set?: false) }
it 'returns nil' do it 'returns nil' do
expect(creds).to be_nil expect(creds).to eq(nil)
end end
end end
end end
end end
describe '.aws_credential_provider' do
let(:creds) { described_class.aws_credential_provider }
before do
allow_next_instance_of(Aws::CredentialProviderChain) do |instance|
allow(instance).to receive(:resolve).and_return(credentials)
end
end
after do
described_class.clear_memoization(:instance_credentials)
end
context 'when Aws::CredentialProviderChain returns set credentials' do
let(:credentials) { double(:aws_credentials) }
it 'returns credentials' do
expect(creds).to eq(credentials)
end
end
context 'when Aws::CredentialProviderChain returns nil' do
let(:credentials) { nil }
it 'returns nil' do
expect(creds).to eq(nil)
end
end
end
end end
...@@ -304,8 +304,10 @@ RSpec.describe Gitlab::Elastic::Indexer do ...@@ -304,8 +304,10 @@ RSpec.describe Gitlab::Elastic::Indexer do
let(:cert_dir) { '/fake/cert/dir' } let(:cert_dir) { '/fake/cert/dir' }
before do before do
stub_env('SSL_CERT_FILE', cert_file) allow(ENV).to receive(:slice).with('SSL_CERT_FILE', 'SSL_CERT_DIR').and_return({
stub_env('SSL_CERT_DIR', cert_dir) 'SSL_CERT_FILE' => cert_file,
'SSL_CERT_DIR' => cert_dir
})
end end
context 'when building env vars for child process' do context 'when building env vars for child process' do
...@@ -317,19 +319,38 @@ RSpec.describe Gitlab::Elastic::Indexer do ...@@ -317,19 +319,38 @@ RSpec.describe Gitlab::Elastic::Indexer do
end end
end end
context 'when AWS_CONTAINER_CREDENTIALS_RELATIVE_URI is set' do context 'when no aws credentials available' do
let(:aws_cred_relative_uri) { '/ecs/relative/cred/uri'} subject { envvars }
before do before do
stub_env('AWS_CONTAINER_CREDENTIALS_RELATIVE_URI', aws_cred_relative_uri) allow(Gitlab::Elastic::Client).to receive(:aws_credential_provider).and_return(nil)
end end
context 'when building env vars for child process' do it 'credentials env vars will not be included' do
subject { envvars } expect(subject).not_to include('AWS_ACCESS_KEY_ID')
expect(subject).not_to include('AWS_SECRET_ACCESS_KEY')
expect(subject).not_to include('AWS_SESSION_TOKEN')
end
end
it 'AWS_CONTAINER_CREDENTIALS_RELATIVE_URI env vars will be included' do context 'when aws credentials are available' do
expect(subject).to include('AWS_CONTAINER_CREDENTIALS_RELATIVE_URI' => aws_cred_relative_uri) let(:access_key_id) { '012' }
end let(:secret_access_key) { 'secret' }
let(:session_token) { 'token' }
let(:credentials) { Aws::Credentials.new(access_key_id, secret_access_key, session_token) }
subject { envvars }
before do
allow(Gitlab::Elastic::Client).to receive(:aws_credential_provider).and_return(credentials)
end
it 'credentials env vars will be included' do
expect(subject).to include({
'AWS_ACCESS_KEY_ID' => access_key_id,
'AWS_SECRET_ACCESS_KEY' => secret_access_key,
'AWS_SESSION_TOKEN' => session_token
})
end end
end end
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment