lambda_vault_auth 0.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/Gemfile +7 -0
- data/LICENSE +13 -0
- data/README.md +99 -0
- data/Rakefile +10 -0
- data/VERSION +1 -0
- data/lib/lambda_vault_auth.rb +107 -0
- metadata +106 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: b709606a9982fe1e6f8248acbb72436217e231f3d20bd6f808f963faf366a472
|
4
|
+
data.tar.gz: a1d035fc195ee1a0c62ea2e901687b71c954c013053f8199e7ecc73a985d6cd4
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: bd519447ef2828f5720af18996a84dd7a9365c64c20168b2220f2cfcc31d7f072df196581c9e37daa007e0ad8277a0419288b82750e28b1cbbeb15c1f7366c8b
|
7
|
+
data.tar.gz: bf6e89e32c382c60b6b216ba5d31901af1043c757ed99e1233bc2728a50061a8a87148c51043128f3f6d34fa083e8e6800a85533b413b51196d4177096a87fa0
|
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
This work is dedicated to the public domain. No rights reserved.
|
2
|
+
|
3
|
+
I, the copyright holder of this work, hereby release it into the public
|
4
|
+
domain. This applies worldwide.
|
5
|
+
|
6
|
+
I grant any entity the right to use this work for any purpose, without
|
7
|
+
any conditions, unless such conditions are required by law.
|
8
|
+
|
9
|
+
If you require a fuller legal statement, please refer to the Creative
|
10
|
+
Commons Zero license:
|
11
|
+
|
12
|
+
http://creativecommons.org/publicdomain/zero/1.0/
|
13
|
+
|
data/README.md
ADDED
@@ -0,0 +1,99 @@
|
|
1
|
+
# lambda_vault_auth
|
2
|
+
|
3
|
+
`lambda_vault_auth` provides a streamlined way to authenticate your Ruby AWS Lambda
|
4
|
+
function to [Hashicorp Vault](https://www.vaultproject.io).
|
5
|
+
|
6
|
+
## Usage Example
|
7
|
+
|
8
|
+
```ruby
|
9
|
+
require 'lambda_vault_auth'
|
10
|
+
|
11
|
+
def handler(context:, event:)
|
12
|
+
vault = LambdaVaultAuth.vault()
|
13
|
+
|
14
|
+
secrets := vault.logical.read('secrets/are/found/here')
|
15
|
+
puts "Your password is: '#{secrets.data[:password]}'"
|
16
|
+
end
|
17
|
+
```
|
18
|
+
|
19
|
+
|
20
|
+
## Setup
|
21
|
+
|
22
|
+
First, you'll need Vault up and running somewhere network-accessible to your
|
23
|
+
Lambda function. That's out of scope for this README, but please see the
|
24
|
+
[Vault documentation](https://www.vaultproject.io/docs/install/index.html)
|
25
|
+
for more.
|
26
|
+
|
27
|
+
Then you'll need to set up an AWS authentication provider. You may already have
|
28
|
+
one configured. If so, you can use that one or you can set up a new one just for
|
29
|
+
this purpose. You don't need to worry about backend credentials for this
|
30
|
+
authentication method. It works without any AWS credentials needing to be loaded
|
31
|
+
into Vault. Or if you do have credentials loaded they don't need to have access
|
32
|
+
to the AWS account your Lambda is running in.
|
33
|
+
|
34
|
+
To establish a new AWS authentication provider, run:
|
35
|
+
|
36
|
+
$ vault auth enable -path lambda -description "IAM auth for Lambdas" aws
|
37
|
+
Success! Enabled aws auth method at: lambda/
|
38
|
+
|
39
|
+
You will also need to set the `iam_server_id_header_value` if you wish to use
|
40
|
+
the extra layer of security (as described below):
|
41
|
+
|
42
|
+
$ vault write auth/lambda/config/client \
|
43
|
+
iam_server_id_header_value=vault.example.com
|
44
|
+
|
45
|
+
Next, you'll need to establish whatever Vault policies your Lambda will need.
|
46
|
+
See the [Vault Policies](https://www.vaultproject.io/docs/concepts/policies.html)
|
47
|
+
documentation for details.
|
48
|
+
|
49
|
+
Now you'll need to know the ARN of your Lambda execution role. You can create it
|
50
|
+
with the Lambda web console or by hand. Either way it should look something like:
|
51
|
+
|
52
|
+
arn:aws:iam::987654321098:role/service-role/MyLambdaRole
|
53
|
+
|
54
|
+
*IMPORTANT*: You must remove any non-essential path from the role ARN unless you
|
55
|
+
have configured your AWS auth provider with IAM permissions to look up roles. In
|
56
|
+
this example, `service-role/` is the path segment. So the principal ARN you will
|
57
|
+
be specifying to Vault in the next step will be:
|
58
|
+
|
59
|
+
arn:aws:iam::987654321098:role/MyLambdaRole
|
60
|
+
|
61
|
+
Now it's time to create the Vault authentication role. It can be named anything
|
62
|
+
you wish. In this case, we'll call it `my-vault-role` and make it periodic since
|
63
|
+
`lambda_vault_auth` will handle renewal automatically:
|
64
|
+
|
65
|
+
$ vault write auth/lambda/role/my-vault-role \
|
66
|
+
auth_type=iam \
|
67
|
+
period=14400 \
|
68
|
+
policies=list-of,vault-policies,separated-by-commas \
|
69
|
+
resolve_aws_unique_ids=false \
|
70
|
+
bound_iam_principal_arn=arn:aws:iam::987654321098:role/MyLambdaRole
|
71
|
+
|
72
|
+
Now you are ready to configure your Lambda.
|
73
|
+
|
74
|
+
## Configuration
|
75
|
+
|
76
|
+
All configuration is done with environment variables:
|
77
|
+
|
78
|
+
* `VAULT_ADDR` (Required) The URL of the Vault instance, eg `https://myvault.example.com`.
|
79
|
+
* `VAULT_AUTH_PROVIDER` (Required) The relative path of the AWS authentication provider, eg `lambda` for `auth/lambda` in the example above.
|
80
|
+
* `VAULT_AUTH_ROLE` (Required) The name of the Vault role to authenticate to, eg `my-vault-role` in the example above.
|
81
|
+
* `VAULT_AUTH_HEADER` (Optional, but recommended) The value of the `X-Vault-AWS-IAM-Server-ID` HTTP header to be included in the signed STS request this code uses to authenticate. This value is often set to the URL or DNS name of the Vault server to prevent potential replay attacks.
|
82
|
+
|
83
|
+
That should be all that is required to get up and running.
|
84
|
+
|
85
|
+
## Contributing to lambda_vault_auth
|
86
|
+
|
87
|
+
* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet.
|
88
|
+
* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it.
|
89
|
+
* Fork the project.
|
90
|
+
* Start a feature/bugfix branch.
|
91
|
+
* Commit and push until you are happy with your contribution.
|
92
|
+
* Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
|
93
|
+
* Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
|
94
|
+
|
95
|
+
|
96
|
+
## License
|
97
|
+
|
98
|
+
This software is public domain. No rights are reserved. See LICENSE for more
|
99
|
+
information.
|
data/Rakefile
ADDED
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.0
|
@@ -0,0 +1,107 @@
|
|
1
|
+
require 'aws-sdk-core'
|
2
|
+
require 'vault'
|
3
|
+
|
4
|
+
# TODO: support the advanced vault header in the signature, since it gives us better security
|
5
|
+
# VAULT_AUTH_HEADER_NAME = "X-Vault-AWS-IAM-Server-ID".freeze
|
6
|
+
|
7
|
+
# LambdaVaultAuth
|
8
|
+
class LambdaVaultAuth
|
9
|
+
# Internal class for Vault interactions
|
10
|
+
class Vaulter
|
11
|
+
attr_reader :auth_provider,
|
12
|
+
:auth_role,
|
13
|
+
:auth_token,
|
14
|
+
:client,
|
15
|
+
:expiration,
|
16
|
+
:expiration_window,
|
17
|
+
:renewal_window,
|
18
|
+
:ttl
|
19
|
+
|
20
|
+
def initialize(sts = Aws::STS::Client.new, env = ENV)
|
21
|
+
@sts = sts
|
22
|
+
@client = new_client_from_environment(env)
|
23
|
+
|
24
|
+
# TODO: Make the following configurable
|
25
|
+
# Lifecycle of each token
|
26
|
+
@expiration_window = 10 # seconds
|
27
|
+
|
28
|
+
# should be at least the length of the lambda runtime
|
29
|
+
@renewal_window = 300 # seconds
|
30
|
+
end
|
31
|
+
|
32
|
+
def expired?
|
33
|
+
expiration.nil? ? true : expiration > Time.now + expiration_window
|
34
|
+
end
|
35
|
+
|
36
|
+
def should_renew?
|
37
|
+
expiration.nil? ? true : Time.now + renewal_window > expiration
|
38
|
+
end
|
39
|
+
|
40
|
+
def renewable?
|
41
|
+
auth_token&.renewable
|
42
|
+
end
|
43
|
+
|
44
|
+
def renew!
|
45
|
+
handle_token(auth_token.renew_self(ttl))
|
46
|
+
end
|
47
|
+
|
48
|
+
def new_client_from_environment(env)
|
49
|
+
addr = env.fetch('VAULT_ADDR')
|
50
|
+
# @auth_header = env.fetch('VAULT_AUTH_HEADER')
|
51
|
+
@auth_provider = env.fetch('VAULT_AUTH_PROVIDER')
|
52
|
+
@auth_role = env.fetch('VAULT_AUTH_ROLE')
|
53
|
+
|
54
|
+
Vault::Client.new(
|
55
|
+
address: addr
|
56
|
+
)
|
57
|
+
end
|
58
|
+
|
59
|
+
def authenticate!
|
60
|
+
req = @sts.get_caller_identity.context.http_request
|
61
|
+
|
62
|
+
data = {
|
63
|
+
'iam_http_request_method': req.http_method,
|
64
|
+
'iam_request_body': Base64.strict_encode64(req.body.read),
|
65
|
+
'iam_request_headers': Base64.strict_encode64(req.headers.to_h.to_json),
|
66
|
+
'iam_request_url': Base64.strict_encode64(req.endpoint.to_s),
|
67
|
+
'role': @auth_role
|
68
|
+
}
|
69
|
+
|
70
|
+
secret = client.logical.write("auth/#{@auth_provider}/login", data)
|
71
|
+
|
72
|
+
warn secret.warnings unless secret.warnings.empty?
|
73
|
+
|
74
|
+
handle_token(secret)
|
75
|
+
|
76
|
+
# create the required data to renew/validate
|
77
|
+
# populate the token on the client and hand that to the user
|
78
|
+
end
|
79
|
+
|
80
|
+
def handle_token(secret)
|
81
|
+
@auth_token = secret.auth
|
82
|
+
@ttl = secret.lease_duration
|
83
|
+
@expiration = Time.now + ttl
|
84
|
+
@client.token = @auth_token.client_token
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
# LambdaVaultAuth.vault returns a wrapped vault which contains a few convenience accessors/helpers
|
89
|
+
# to help manage the lifecycle of a vault and access the credentials
|
90
|
+
def self.vault
|
91
|
+
@vault ||= Vaulter.new
|
92
|
+
@sts ||= Aws::STS::Client.new
|
93
|
+
|
94
|
+
return @vault.client unless @vault.expired?
|
95
|
+
|
96
|
+
if @vault.renewable? && @vault.should_renew?
|
97
|
+
@vault.renew!
|
98
|
+
return @vault.client
|
99
|
+
end
|
100
|
+
|
101
|
+
# Otherwise, authenticate
|
102
|
+
@vault.authenticate!
|
103
|
+
|
104
|
+
# return the vault client directly
|
105
|
+
@vault.client
|
106
|
+
end
|
107
|
+
end
|
metadata
ADDED
@@ -0,0 +1,106 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: lambda_vault_auth
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Ryan Taylor
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2020-05-13 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: aws-sdk-core
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '3.0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '3.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: vault
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0.13'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0.13'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: bundler
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '2.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '2.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rspec
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '3.5'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '3.5'
|
69
|
+
description: A library for authenticating a lambda function against Hashicorp Vault
|
70
|
+
via AWS as an authentication provider.
|
71
|
+
email: rtaylor@instructure.com
|
72
|
+
executables: []
|
73
|
+
extensions: []
|
74
|
+
extra_rdoc_files: []
|
75
|
+
files:
|
76
|
+
- Gemfile
|
77
|
+
- LICENSE
|
78
|
+
- README.md
|
79
|
+
- Rakefile
|
80
|
+
- VERSION
|
81
|
+
- lib/lambda_vault_auth.rb
|
82
|
+
homepage: http://github.com/instructure/lambda_vault_auth
|
83
|
+
licenses:
|
84
|
+
- CC0-1.0
|
85
|
+
metadata: {}
|
86
|
+
post_install_message:
|
87
|
+
rdoc_options: []
|
88
|
+
require_paths:
|
89
|
+
- lib
|
90
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
91
|
+
requirements:
|
92
|
+
- - ">="
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
version: '0'
|
95
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
96
|
+
requirements:
|
97
|
+
- - ">="
|
98
|
+
- !ruby/object:Gem::Version
|
99
|
+
version: '0'
|
100
|
+
requirements: []
|
101
|
+
rubygems_version: 3.1.2
|
102
|
+
signing_key:
|
103
|
+
specification_version: 4
|
104
|
+
summary: Simplify authentication between an AWS lambda function and Hashicorp Vault
|
105
|
+
via the AWS authentication provider.
|
106
|
+
test_files: []
|