conjur-api 6.3.1.pre.848 → 6.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,140 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'json'
4
- require 'uri'
5
-
6
- module Conjur
7
- class API
8
- # Authenticator that uses AWS IAM credentials to obtain Conjur access tokens.
9
- # Signs an AWS STS GetCallerIdentity request with SigV4, then POSTs the
10
- # resulting headers as JSON to the Conjur authn-iam endpoint.
11
- #
12
- # Requires the +aws-sdk-core+ gem (not bundled with conjur-api).
13
- # Add `gem 'aws-sdk-core'` to your Gemfile to use this authenticator.
14
- class IAMAuthenticator
15
- include TokenExpiration
16
-
17
- DEFAULT_SERVICE_ID = 'prod'.freeze
18
- DEFAULT_REGION = 'us-east-1'.freeze
19
-
20
- STS_GLOBAL_URL = 'https://sts.amazonaws.com/?Action=GetCallerIdentity&Version=2011-06-15'.freeze
21
- STS_REGIONAL_URL = 'https://sts.%<region>s.amazonaws.com/?Action=GetCallerIdentity&Version=2011-06-15'.freeze
22
- STS_GLOBAL_SIGNING_REGION = 'us-east-1'.freeze
23
-
24
- attr_reader :account, :service_id, :identity, :aws_region
25
-
26
- def initialize(account, service_id, identity, aws_region = DEFAULT_REGION)
27
- @account = account
28
- @service_id = service_id
29
- @identity = identity
30
- @aws_region = aws_region
31
- update_token_born
32
- end
33
-
34
- def refresh_token
35
- Conjur::API.authenticate_iam(identity, account: account,
36
- service_id: service_id,
37
- aws_region: aws_region).tap do
38
- update_token_born
39
- end
40
- end
41
- end
42
-
43
- class << self
44
- # Create a {Conjur::API} instance authenticated via AWS IAM (authn-iam).
45
- #
46
- # AWS credentials are resolved from the default chain: environment variables
47
- # (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN), then
48
- # ~/.aws/credentials, then the EC2 instance profile.
49
- #
50
- # Requires the +aws-sdk-core+ gem.
51
- #
52
- # @param [String] identity Conjur host identity (e.g. "host/my-app/my-role")
53
- # @param [String] service_id the authn-iam service ID (default: "prod")
54
- # @param [String] account the Conjur organization account
55
- # @param [String] aws_region AWS region for STS signing. Use "global" for the
56
- # global STS endpoint (signed as us-east-1).
57
- # @param [String, nil] remote_ip optional IP address recorded in the audit log
58
- # @return [Conjur::API]
59
- def new_from_iam(identity,
60
- account: Conjur.configuration.account,
61
- service_id: IAMAuthenticator::DEFAULT_SERVICE_ID,
62
- aws_region: IAMAuthenticator::DEFAULT_REGION,
63
- remote_ip: nil)
64
- self.new.init_from_iam(identity, account: account, service_id: service_id,
65
- aws_region: aws_region, remote_ip: remote_ip)
66
- end
67
-
68
- # Authenticate via authn-iam and return a parsed Conjur access token.
69
- #
70
- # @param [String] identity Conjur host identity
71
- # @param [String] service_id the authn-iam service ID
72
- # @param [String] account the Conjur organization account
73
- # @param [String] aws_region AWS region for STS signing
74
- # @return [Hash] parsed access token
75
- def authenticate_iam(identity,
76
- account: Conjur.configuration.account,
77
- service_id: IAMAuthenticator::DEFAULT_SERVICE_ID,
78
- aws_region: IAMAuthenticator::DEFAULT_REGION)
79
- raise ArgumentError, "identity is required" if identity.nil? || identity.empty?
80
- raise ArgumentError, "service_id is required" if service_id.nil? || service_id.empty?
81
-
82
- begin
83
- require 'aws-sdk-core'
84
- rescue LoadError
85
- raise LoadError,
86
- "aws-sdk-core is required for IAM authentication. " \
87
- "Add `gem 'aws-sdk-core'` to your Gemfile."
88
- end
89
-
90
- if Conjur.log
91
- Conjur.log << "Authenticating #{identity} to account #{account} via authn-iam/#{service_id}\n"
92
- end
93
-
94
- signed_headers = build_iam_signed_headers(aws_region)
95
- JSON.parse(url_for(:authn_iam_authenticate, account, service_id, identity)
96
- .post(signed_headers.to_json, content_type: 'application/json'))
97
- end
98
-
99
- private
100
-
101
- def build_iam_signed_headers(aws_region)
102
- signing_region = aws_region == 'global' ? IAMAuthenticator::STS_GLOBAL_SIGNING_REGION : aws_region
103
- sts_url = aws_region == 'global' || aws_region == IAMAuthenticator::STS_GLOBAL_SIGNING_REGION \
104
- ? IAMAuthenticator::STS_GLOBAL_URL
105
- : format(IAMAuthenticator::STS_REGIONAL_URL, region: aws_region)
106
-
107
- provider = Aws::CredentialProviderChain.new.resolve
108
- raise ArgumentError,
109
- "No AWS credentials found. Configure an instance profile, " \
110
- "environment variables (AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY), " \
111
- "or ~/.aws/credentials." unless provider
112
-
113
- creds = provider.credentials
114
- signer = Aws::Sigv4::Signer.new(
115
- service: 'sts',
116
- region: signing_region,
117
- access_key_id: creds.access_key_id,
118
- secret_access_key: creds.secret_access_key,
119
- session_token: creds.session_token
120
- )
121
-
122
- signed = signer.sign_request(http_method: 'GET', url: sts_url, headers: {}, body: '')
123
- headers = signed.headers.dup
124
- # Conjur expects these specific keys
125
- headers['host'] = URI.parse(sts_url).host
126
- headers.compact
127
- end
128
- end
129
-
130
- def init_from_iam(identity,
131
- account: Conjur.configuration.account,
132
- service_id: IAMAuthenticator::DEFAULT_SERVICE_ID,
133
- aws_region: IAMAuthenticator::DEFAULT_REGION,
134
- remote_ip: nil)
135
- @remote_ip = remote_ip
136
- @authenticator = IAMAuthenticator.new(account, service_id, identity, aws_region)
137
- self
138
- end
139
- end
140
- end
data/secrets.yml DELETED
@@ -1,13 +0,0 @@
1
- # Summon secrets for CI. `summon -e <env> ./test.sh` resolves the !var
2
- # references below from the CI Conjur vault and exports them into the
3
- # environment for the wrapped command.
4
- #
5
- # The azure environment supplies the values the authn-azure integration
6
- # feature needs (see features/authn_azure.feature and features/README.md).
7
- # Mirrors the layout used by conjur-api-java / conjur-api-dotnet.
8
- azure:
9
- RUN_AZURE_TESTS: "true"
10
- AZURE_SUBSCRIPTION_ID: !var ci/azure/subscription-id
11
- AZURE_RESOURCE_GROUP: !var ci/azure/authn-test/resource-group
12
- USER_ASSIGNED_IDENTITY: !var ci/azure/authn-test/user-assigned-id
13
- USER_ASSIGNED_IDENTITY_CLIENT_ID: !var ci/azure/authn-test/user-assigned-id-client-id
@@ -1,91 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'spec_helper'
4
- require 'cgi'
5
- require 'conjur/api/router'
6
-
7
- describe Conjur::API, api: :dummy do
8
- let(:service_id) { 'my-azure-service' }
9
- let(:identity) { 'host/my-app/azure-host' }
10
- let(:azure_jwt) { 'eyJhbGciOiJSUzI1NiJ9.azure-payload.sig' }
11
- let(:raw_token) { { 'protected' => 'p', 'payload' => 'pl', 'signature' => 's' } }
12
-
13
- describe '.new_from_azure' do
14
- it 'returns an API instance with an AzureAuthenticator' do
15
- allow(Conjur::API).to receive(:authenticate_azure).and_return(raw_token)
16
- api_instance = Conjur::API.new_from_azure(service_id, identity, account: account)
17
- expect(api_instance).to be_a(Conjur::API)
18
- expect(api_instance.authenticator).to be_a(Conjur::API::AzureAuthenticator)
19
- end
20
- end
21
-
22
- describe '.authenticate_azure' do
23
- let(:resource) { double('resource') }
24
-
25
- before do
26
- allow(Conjur::API).to receive(:url_for)
27
- .with(:authn_azure_authenticate, account, service_id, identity)
28
- .and_return(resource)
29
- allow(resource).to receive(:post)
30
- .with("jwt=#{CGI.escape(azure_jwt)}", content_type: 'application/x-www-form-urlencoded')
31
- .and_return(raw_token.to_json)
32
- allow(Conjur::API).to receive(:fetch_azure_jwt).and_return(azure_jwt)
33
- end
34
-
35
- it 'returns a parsed token' do
36
- token = Conjur::API.authenticate_azure(service_id, identity, account: account)
37
- expect(token).to eq(raw_token)
38
- end
39
-
40
- it 'raises ArgumentError when service_id is empty' do
41
- expect { Conjur::API.authenticate_azure('', identity, account: account) }
42
- .to raise_error(ArgumentError, /service_id is required/)
43
- end
44
-
45
- it 'raises ArgumentError when identity is empty' do
46
- expect { Conjur::API.authenticate_azure(service_id, '', account: account) }
47
- .to raise_error(ArgumentError, /identity is required/)
48
- end
49
- end
50
-
51
- describe 'AzureAuthenticator#refresh_token' do
52
- subject(:authenticator) do
53
- Conjur::API::AzureAuthenticator.new(account, service_id, identity)
54
- end
55
-
56
- it 'delegates to authenticate_azure with stored credentials' do
57
- expect(Conjur::API).to receive(:authenticate_azure)
58
- .with(service_id, identity,
59
- account: account,
60
- resource_uri: Conjur::API::AzureAuthenticator::DEFAULT_RESOURCE,
61
- client_id: nil,
62
- imds_base_url: Conjur::API::AzureAuthenticator::DEFAULT_IMDS_BASE,
63
- imds_api_version: Conjur::API::AzureAuthenticator::DEFAULT_IMDS_VER)
64
- .and_return(raw_token)
65
- expect(authenticator.refresh_token).to eq(raw_token)
66
- end
67
- end
68
- end
69
-
70
- describe Conjur::API::Router do
71
- let(:account) { 'myaccount' }
72
- let(:service_id) { 'my-service' }
73
- let(:identity) { 'host/my-app/azure-host' }
74
-
75
- before do
76
- allow(Conjur.configuration).to receive(:core_url).and_return('https://conjur.example.com')
77
- allow(Conjur.configuration).to receive(:rest_client_options).and_return({})
78
- end
79
-
80
- describe '#authn_azure_authenticate' do
81
- subject { described_class.authn_azure_authenticate(account, service_id, identity) }
82
-
83
- it 'returns a RestClient::Resource' do
84
- expect(subject).to be_a(RestClient::Resource)
85
- end
86
-
87
- it 'builds the correct URL' do
88
- expect(subject.url).to end_with('/authn-azure/my-service/myaccount/host%2Fmy-app%2Fazure-host/authenticate')
89
- end
90
- end
91
- end
@@ -1,121 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'spec_helper'
4
- require 'cgi'
5
- require 'conjur/api/router'
6
-
7
- describe Conjur::API, api: :dummy do
8
- let(:identity) { 'host/data/test/gcp-apps/test-app' }
9
- let(:gcp_jwt) { 'eyJhbGciOiJSUzI1NiJ9.gcp-payload.sig' }
10
- let(:raw_token) { { 'protected' => 'p', 'payload' => 'pl', 'signature' => 's' } }
11
-
12
- describe '.new_from_gcp' do
13
- it 'returns an API instance with a GCPAuthenticator' do
14
- allow(Conjur::API).to receive(:authenticate_gcp).and_return(raw_token)
15
- api_instance = Conjur::API.new_from_gcp(identity, account: account)
16
- expect(api_instance).to be_a(Conjur::API)
17
- expect(api_instance.authenticator).to be_a(Conjur::API::GCPAuthenticator)
18
- end
19
- end
20
-
21
- describe '.authenticate_gcp' do
22
- let(:resource) { double('resource') }
23
-
24
- before do
25
- allow(Conjur::API).to receive(:url_for)
26
- .with(:authn_gcp_authenticate, account)
27
- .and_return(resource)
28
- allow(resource).to receive(:post)
29
- .with("jwt=#{CGI.escape(gcp_jwt)}", content_type: 'application/x-www-form-urlencoded')
30
- .and_return(raw_token.to_json)
31
- end
32
-
33
- context 'with a pre-supplied JWT' do
34
- it 'returns a parsed token without calling the metadata service' do
35
- expect(Conjur::API).not_to receive(:fetch_gcp_jwt)
36
- token = Conjur::API.authenticate_gcp(identity, account: account, jwt: gcp_jwt)
37
- expect(token).to eq(raw_token)
38
- end
39
- end
40
-
41
- context 'without a pre-supplied JWT' do
42
- it 'fetches from the metadata service' do
43
- allow(Conjur::API).to receive(:fetch_gcp_jwt).and_return(gcp_jwt)
44
- token = Conjur::API.authenticate_gcp(identity, account: account)
45
- expect(token).to eq(raw_token)
46
- end
47
- end
48
-
49
- it 'raises ArgumentError when identity is empty' do
50
- expect { Conjur::API.authenticate_gcp('', account: account) }
51
- .to raise_error(ArgumentError, /identity is required/)
52
- end
53
- end
54
-
55
- describe 'GCPAuthenticator#refresh_token' do
56
- context 'with a pre-supplied JWT' do
57
- subject(:authenticator) do
58
- Conjur::API::GCPAuthenticator.new(account, identity, jwt: gcp_jwt)
59
- end
60
-
61
- it 'passes the stored JWT to authenticate_gcp' do
62
- expect(Conjur::API).to receive(:authenticate_gcp)
63
- .with(identity,
64
- account: account,
65
- jwt: gcp_jwt,
66
- gcp_identity_url: Conjur::API::GCPAuthenticator::DEFAULT_METADATA_URL)
67
- .and_return(raw_token)
68
- expect(authenticator.refresh_token).to eq(raw_token)
69
- end
70
- end
71
-
72
- context 'without a pre-supplied JWT' do
73
- subject(:authenticator) do
74
- Conjur::API::GCPAuthenticator.new(account, identity)
75
- end
76
-
77
- it 'passes nil jwt to authenticate_gcp (which then calls metadata service)' do
78
- expect(Conjur::API).to receive(:authenticate_gcp)
79
- .with(identity,
80
- account: account,
81
- jwt: nil,
82
- gcp_identity_url: Conjur::API::GCPAuthenticator::DEFAULT_METADATA_URL)
83
- .and_return(raw_token)
84
- expect(authenticator.refresh_token).to eq(raw_token)
85
- end
86
- end
87
- end
88
-
89
- describe '.gcp_audience' do
90
- it 'strips a leading host/ and rebuilds the conjur audience' do
91
- expect(Conjur::API.send(:gcp_audience, account, 'host/data/app'))
92
- .to eq("conjur/#{account}/host/data/app")
93
- end
94
-
95
- it 'assumes a host identity when the host/ prefix is absent' do
96
- expect(Conjur::API.send(:gcp_audience, account, 'data/app'))
97
- .to eq("conjur/#{account}/host/data/app")
98
- end
99
- end
100
- end
101
-
102
- describe Conjur::API::Router do
103
- let(:account) { 'myaccount' }
104
-
105
- before do
106
- allow(Conjur.configuration).to receive(:core_url).and_return('https://conjur.example.com')
107
- allow(Conjur.configuration).to receive(:rest_client_options).and_return({})
108
- end
109
-
110
- describe '#authn_gcp_authenticate' do
111
- subject { described_class.authn_gcp_authenticate(account) }
112
-
113
- it 'returns a RestClient::Resource' do
114
- expect(subject).to be_a(RestClient::Resource)
115
- end
116
-
117
- it 'builds the correct URL using the serviceless authn-gcp path' do
118
- expect(subject.url).to end_with('/authn-gcp/myaccount/authenticate')
119
- end
120
- end
121
- end
@@ -1,103 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'spec_helper'
4
- require 'conjur/api/router'
5
-
6
- describe Conjur::API, api: :dummy do
7
- let(:service_id) { 'prod' }
8
- let(:identity) { 'host/my-app/my-role' }
9
- let(:raw_token) { { 'protected' => 'p', 'payload' => 'pl', 'signature' => 's' } }
10
- let(:signed_headers) do
11
- {
12
- 'Authorization' => 'AWS4-HMAC-SHA256 Credential=AKIAEXAMPLE/...',
13
- 'x-amz-date' => '20240101T000000Z',
14
- 'x-amz-content-sha256' => 'e3b0c44298fc1c149afbf4c8996fb924',
15
- 'x-amz-security-token' => 'token',
16
- 'host' => 'sts.amazonaws.com'
17
- }
18
- end
19
-
20
- describe '.new_from_iam' do
21
- it 'returns an API instance with an IAMAuthenticator' do
22
- allow(Conjur::API).to receive(:authenticate_iam).and_return(raw_token)
23
- api_instance = Conjur::API.new_from_iam(identity, account: account)
24
- expect(api_instance).to be_a(Conjur::API)
25
- expect(api_instance.authenticator).to be_a(Conjur::API::IAMAuthenticator)
26
- end
27
- end
28
-
29
- describe '.authenticate_iam' do
30
- let(:resource) { double('resource') }
31
-
32
- before do
33
- allow(Conjur::API).to receive(:require).with('aws-sdk-core')
34
- allow(Conjur::API).to receive(:url_for)
35
- .with(:authn_iam_authenticate, account, service_id, identity)
36
- .and_return(resource)
37
- allow(resource).to receive(:post)
38
- .with(signed_headers.to_json, content_type: 'application/json')
39
- .and_return(raw_token.to_json)
40
- allow(Conjur::API).to receive(:build_iam_signed_headers).and_return(signed_headers)
41
- end
42
-
43
- it 'returns a parsed token' do
44
- token = Conjur::API.authenticate_iam(identity, account: account, service_id: service_id)
45
- expect(token).to eq(raw_token)
46
- end
47
-
48
- it 'raises ArgumentError when identity is empty' do
49
- expect { Conjur::API.authenticate_iam('', account: account) }
50
- .to raise_error(ArgumentError, /identity is required/)
51
- end
52
-
53
- it 'raises ArgumentError when service_id is empty' do
54
- expect { Conjur::API.authenticate_iam(identity, account: account, service_id: '') }
55
- .to raise_error(ArgumentError, /service_id is required/)
56
- end
57
-
58
- it 'raises LoadError when aws-sdk-core is unavailable' do
59
- allow(Conjur::API).to receive(:require).with('aws-sdk-core').and_raise(LoadError)
60
- expect { Conjur::API.authenticate_iam(identity, account: account) }
61
- .to raise_error(LoadError, /aws-sdk-core/)
62
- end
63
- end
64
-
65
- describe 'IAMAuthenticator#refresh_token' do
66
- subject(:authenticator) do
67
- Conjur::API::IAMAuthenticator.new(account, service_id, identity)
68
- end
69
-
70
- it 'delegates to authenticate_iam with stored credentials' do
71
- expect(Conjur::API).to receive(:authenticate_iam)
72
- .with(identity,
73
- account: account,
74
- service_id: service_id,
75
- aws_region: Conjur::API::IAMAuthenticator::DEFAULT_REGION)
76
- .and_return(raw_token)
77
- expect(authenticator.refresh_token).to eq(raw_token)
78
- end
79
- end
80
- end
81
-
82
- describe Conjur::API::Router do
83
- let(:account) { 'myaccount' }
84
- let(:service_id) { 'prod' }
85
- let(:identity) { 'host/my-app/my-role' }
86
-
87
- before do
88
- allow(Conjur.configuration).to receive(:core_url).and_return('https://conjur.example.com')
89
- allow(Conjur.configuration).to receive(:rest_client_options).and_return({})
90
- end
91
-
92
- describe '#authn_iam_authenticate' do
93
- subject { described_class.authn_iam_authenticate(account, service_id, identity) }
94
-
95
- it 'returns a RestClient::Resource' do
96
- expect(subject).to be_a(RestClient::Resource)
97
- end
98
-
99
- it 'builds the correct URL' do
100
- expect(subject.url).to end_with('/authn-iam/prod/myaccount/host%2Fmy-app%2Fmy-role/authenticate')
101
- end
102
- end
103
- end