kubes_aws 0.0.0.beta → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 19d8e8171c4111b246797f27545db16dfcf6f4f8fcf97586e193b82e40e58e47
4
- data.tar.gz: 26326b10b3ce96e90468db2917930d3e4c93e5ca58dfc8cb713a3ef6a15ff99f
3
+ metadata.gz: 216f223229ec7e134e52a869f36507518c8376d768e3ae72ec5b557c7e1a31a7
4
+ data.tar.gz: 3fd50925b7906fe8c18f6b6e5c20065e895097aeaa57926093cf57ad1b445121
5
5
  SHA512:
6
- metadata.gz: 5b97552f1de27b0abfe4342e03f8206c931304dede4b4ca357b616dc7c80882c5fe04754146431d8433dcf11cf2b2606daba4b70e3a8dd8f3dbd9c373fe3a6d8
7
- data.tar.gz: 86eab18a16e46413767866558f17ff743d101fa58e4e03ab3f982194e31d2810f55cb4a61d9c908cb26bd84681f4b565a3ca2a9da14b5fc7f245813f03564f49
6
+ metadata.gz: ef86d09153f32cb9bd6ae87eb905ef4de3a07ffa86abae0ed039655ee2305bae228de3d895c7d77cc3112fa2166ffa25fa4632163bd17969034947b067d30446
7
+ data.tar.gz: 4437ae6bdc30d96afd98b0261893d52a4c195eeb135b14b8823d6024d601743f431eaefaccf787a90cb1600c6eedc405d59c8f94f958c76ef8109be39b6b6243
@@ -0,0 +1,7 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+ This project *loosely tries* to adhere to [Semantic Versioning](http://semver.org/), even before v1.0.
5
+
6
+ ## [0.1.0]
7
+ - Initial release.
data/Gemfile CHANGED
@@ -3,5 +3,5 @@ source "https://rubygems.org"
3
3
  # Specify your gem's dependencies in kubes_aws.gemspec
4
4
  gemspec
5
5
 
6
- gem "rake"
7
- gem "rspec"
6
+ gem "rake", "~> 12.0"
7
+ gem "rspec", "~> 3.0"
data/README.md CHANGED
@@ -1,10 +1,14 @@
1
- # Kubes AWS Helpers Library
1
+ # Kubes AWS
2
2
 
3
- Starter example.
3
+ [![Gem Version](https://badge.fury.io/rb/kubes_aws.png)](http://badge.fury.io/rb/kubes_aws)
4
+
5
+ [![BoltOps Badge](https://img.boltops.com/boltops/badges/boltops-badge.png)](https://www.boltops.com)
6
+
7
+ [Kubes](https://kubes.guru) Library with AWS helpers.
4
8
 
5
9
  ## Usage
6
10
 
7
- ...
11
+ For more detailed usage instructions refer to the [Kubes Helpers docs](https://kubes.guru/docs/helpers/aws/).
8
12
 
9
13
  ## Contributing
10
14
 
@@ -21,4 +21,13 @@ Gem::Specification.new do |spec|
21
21
  spec.bindir = "exe"
22
22
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
23
23
  spec.require_paths = ["lib"]
24
+
25
+ spec.add_dependency "activesupport"
26
+ spec.add_dependency "aws-sdk-eks"
27
+ spec.add_dependency "aws-sdk-iam"
28
+ spec.add_dependency "aws-sdk-secretsmanager"
29
+ spec.add_dependency "aws-sdk-ssm"
30
+ spec.add_dependency "aws_data"
31
+ spec.add_dependency "memoist"
32
+ spec.add_dependency "zeitwerk"
24
33
  end
@@ -1,6 +1,20 @@
1
1
  require "kubes_aws/version"
2
+ require "logger"
3
+
4
+ require "kubes_aws/autoloader"
5
+ KubesAws::Autoloader.setup
2
6
 
3
7
  module KubesAws
4
8
  class Error < StandardError; end
5
- # Your code goes here...
9
+
10
+ @@logger = nil
11
+ def logger
12
+ @@logger ||= Kubes.logger
13
+ end
14
+
15
+ def logger=(v)
16
+ @@logger = v
17
+ end
18
+
19
+ extend self
6
20
  end
@@ -0,0 +1,22 @@
1
+ require "zeitwerk"
2
+
3
+ module KubesAws
4
+ class Autoloader
5
+ class Inflector < Zeitwerk::Inflector
6
+ def camelize(basename, _abspath)
7
+ map = { cli: "CLI", ssm: "SSM", version: "VERSION" }
8
+ map[basename.to_sym] || super
9
+ end
10
+ end
11
+
12
+ class << self
13
+ def setup
14
+ loader = Zeitwerk::Loader.new
15
+ loader.inflector = Inflector.new
16
+ lib = File.expand_path("../", __dir__)
17
+ loader.push_dir(lib)
18
+ loader.setup
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,28 @@
1
+ require "aws-sdk-eks"
2
+ require "aws-sdk-iam"
3
+
4
+ module KubesAws
5
+ module AwsServices
6
+ extend Memoist
7
+
8
+ def eks
9
+ Aws::EKS::Client.new
10
+ end
11
+ memoize :eks
12
+
13
+ def iam
14
+ Aws::IAM::Client.new
15
+ end
16
+ memoize :iam
17
+
18
+ def secrets
19
+ Aws::SecretsManager::Client.new
20
+ end
21
+ memoize :secrets
22
+
23
+ def ssm
24
+ Aws::SSM::Client.new
25
+ end
26
+ memoize :ssm
27
+ end
28
+ end
@@ -0,0 +1,141 @@
1
+ require "active_support/core_ext/string"
2
+ require "aws_data"
3
+ require "json"
4
+
5
+ module KubesAws
6
+ class IamRole
7
+ extend Memoist
8
+ include AwsServices
9
+ include Logging
10
+ include Prebaked
11
+
12
+ # public method to keep: role_name
13
+ attr_reader :role_name
14
+ def initialize(app:, cluster:, namespace:nil, managed_policies: [], inline_policies: [], role_name: nil, ksa: nil)
15
+ @app, @cluster, @managed_policies, @inline_policies = app, cluster, managed_policies, inline_policies
16
+
17
+ # conventional names
18
+ @ksa = ksa || @app # convention: app
19
+ @namespace = namespace || "#{@app}-#{Kubes.env}" # convention: app-env
20
+ @role_name = role_name || "#{@app}-#{Kubes.env}" # convention: app-env
21
+ end
22
+
23
+ def call
24
+ create_open_id_connect_provider
25
+ create_iam_role
26
+ add_mananged_policies
27
+ add_inline_policies
28
+ end
29
+
30
+ def add_inline_policies
31
+ @inline_policies.each do |policy|
32
+ params = normalize_inline_policy(policy)
33
+ iam.put_role_policy(params)
34
+ end
35
+ end
36
+
37
+ # resp = client.put_role_policy(
38
+ # policy_document: "{\"Version\":\"2012-10-17\",\"Statement\":{\"Effect\":\"Allow\",\"Action\":\"s3:*\",\"Resource\":\"*\"}}",
39
+ # policy_name: "S3AccessPolicy",
40
+ # role_name: "S3Access",
41
+ # )
42
+ def normalize_inline_policy(policy)
43
+ prebaked = prebaked_policies[policy]
44
+ policy = prebaked if prebaked
45
+
46
+ policy_document = policy[:policy_document]
47
+ policy[:policy_document] = JSON.dump(policy_document) if policy_document.is_a?(Hash)
48
+ policy[:role_name] = @role_name
49
+ policy
50
+ end
51
+
52
+ def create_open_id_connect_provider
53
+ open_id = OpenId.new(@cluster)
54
+ open_id.create_provider
55
+ end
56
+
57
+ def create_iam_role
58
+ return if role_exist?
59
+ iam.create_role(
60
+ role_name: @role_name,
61
+ assume_role_policy_document: trust_policy,
62
+ )
63
+ logger.debug "Created IAM Role #{@role_name}"
64
+ end
65
+
66
+ def add_mananged_policies
67
+ @managed_policies.each do |policy|
68
+ policy_arn = normalize_managed_policy(policy)
69
+ iam.attach_role_policy(
70
+ role_name: @role_name,
71
+ policy_arn: policy_arn,
72
+ )
73
+ end
74
+ logger.debug "IAM Policies added to #{@role_name}"
75
+ end
76
+
77
+ # AmazonS3ReadOnlyAccess => arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess
78
+ def normalize_managed_policy(policy)
79
+ if policy.include?("arn:")
80
+ policy
81
+ else
82
+ "arn:aws:iam::aws:policy/#{policy}"
83
+ end
84
+ end
85
+
86
+ def role_exist?
87
+ iam.get_role(role_name: @role_name)
88
+ true
89
+ rescue Aws::IAM::Errors::NoSuchEntity
90
+ false
91
+ end
92
+
93
+ # public method to keep: arn
94
+ def arn
95
+ "arn:aws:iam::#{aws_account}:role/#{@role_name}"
96
+ end
97
+
98
+ # public method to keep: aws_account
99
+ def aws_account
100
+ aws.account
101
+ end
102
+
103
+ def trust_policy
104
+ issuer_host = issuer_url.sub('https://','')
105
+ provider_arn = "arn:aws:iam::#{aws_account}:oidc-provider/#{issuer_host}"
106
+ <<~JSON
107
+ {
108
+ "Version": "2012-10-17",
109
+ "Statement": [
110
+ {
111
+ "Effect": "Allow",
112
+ "Principal": {
113
+ "Federated": "#{provider_arn}"
114
+ },
115
+ "Action": "sts:AssumeRoleWithWebIdentity",
116
+ "Condition": {
117
+ "StringEquals": {
118
+ "#{issuer_host}:sub": "system:serviceaccount:#{@namespace}:#{@ksa}"
119
+ }
120
+ }
121
+ }
122
+ ]
123
+ }
124
+ JSON
125
+ end
126
+
127
+ def issuer_url
128
+ resp = eks.describe_cluster(name: @cluster)
129
+ resp.cluster.identity.oidc.issuer
130
+ end
131
+ memoize :issuer_url
132
+
133
+ def aws
134
+ AwsData.new
135
+ end
136
+ memoize :aws
137
+
138
+ # useful to store data used later
139
+ class_attribute :data, :role_arn
140
+ end
141
+ end
@@ -0,0 +1,27 @@
1
+ class KubesAws::IamRole
2
+ module Prebaked
3
+ def prebaked_policies
4
+ {
5
+ secrets_read_only: secrets_read_only
6
+ }
7
+ end
8
+
9
+ def secrets_read_only
10
+ {
11
+ policy_document: {
12
+ Version: "2012-10-17",
13
+ Statement: {
14
+ Effect: "Allow",
15
+ Action: [
16
+ "secretsmanager:Describe*",
17
+ "secretsmanager:Get*",
18
+ "secretsmanager:List*"
19
+ ],
20
+ Resource: "*"
21
+ }
22
+ },
23
+ policy_name: "SecretsReadOnly",
24
+ }
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,7 @@
1
+ module KubesAws
2
+ module Logging
3
+ def logger
4
+ KubesAws.logger
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,49 @@
1
+ require "aws-sdk-iam"
2
+ require "aws_data"
3
+ require "openssl"
4
+
5
+ module KubesAws
6
+ class OpenId
7
+ extend Memoist
8
+ include AwsServices
9
+ include Logging
10
+
11
+ def initialize(cluster)
12
+ @cluster = cluster
13
+ end
14
+
15
+ # Method is idempotent
16
+ def create_provider
17
+ fingerprint = OpenSSL::Digest::SHA1.new(cert.to_der).to_s
18
+ iam.create_open_id_connect_provider(
19
+ url: issuer_url,
20
+ thumbprint_list: [fingerprint],
21
+ client_id_list: ["sts.amazonaws.com"]
22
+ )
23
+ rescue Aws::IAM::Errors::EntityAlreadyExists => e
24
+ logger.debug "#{e.class}: #{e.message}"
25
+ logger.debug "Open ID Provider already exists"
26
+ end
27
+
28
+ def issuer_url
29
+ resp = eks.describe_cluster(name: @cluster)
30
+ resp.cluster.identity.oidc.issuer
31
+ end
32
+ memoize :issuer_url
33
+
34
+ # https://stackoverflow.com/questions/34601260/using-ruby-openssl-to-download-and-read-certificates
35
+ def cert
36
+ uri = URI(issuer_url)
37
+ ctx = OpenSSL::SSL::SSLContext.new
38
+ sock = TCPSocket.new(uri.host, 443)
39
+ ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx)
40
+ ssl.connect
41
+ ssl.peer_cert_chain.last
42
+ end
43
+ memoize :cert
44
+
45
+ def aws_region
46
+ AwsData.new.region
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,56 @@
1
+ require "aws-sdk-secretsmanager"
2
+
3
+ module KubesAws
4
+ class Secrets
5
+ include AwsServices
6
+
7
+ def initialize(upcase: false, base64: false, prefix: nil, filters: [])
8
+ @upcase, @base64, @filters = upcase, base64, filters
9
+ @prefix = ENV['AWS_SECRET_PREFIX'] || prefix # IE: prefix: demo/dev/
10
+ end
11
+
12
+ def call
13
+ items.each do |item|
14
+ next unless item.name.include?(@prefix) if @prefix
15
+
16
+ secret_value = secrets.get_secret_value(secret_id: item.name)
17
+ value = secret_value.secret_string
18
+ value = Base64.strict_encode64(value).strip if @base64
19
+
20
+ key = item.name
21
+ key = key.sub(@prefix,'') if @prefix
22
+ key = key.upcase if @upcase
23
+ self.class.data[key] = value
24
+ end
25
+ end
26
+
27
+ # Returns flattened lazy Enumerator
28
+ def items
29
+ Enumerator.new do |y|
30
+ next_token = nil
31
+ loop do
32
+ args = {max_results: PAGE_SIZE, sort_order: "asc"}
33
+ args[:next_token] = next_token if next_token
34
+ args.merge!(filters: @filters)
35
+
36
+ resp = secrets.list_secrets(args)
37
+
38
+ items = resp.secret_list
39
+ next_token = resp.next_token
40
+
41
+ y.yield(items, resp) # also provided the original resp always in case it is useful
42
+ break unless next_token
43
+ end
44
+ end.lazy.flat_map { |v| v }
45
+ end
46
+
47
+ PAGE_SIZE = 20
48
+
49
+ def data
50
+ self.class.data
51
+ end
52
+
53
+ class_attribute :data
54
+ self.data = {}
55
+ end
56
+ end
@@ -0,0 +1,59 @@
1
+ require "aws-sdk-ssm"
2
+
3
+ module KubesAws
4
+ class SSM
5
+ include AwsServices
6
+
7
+ def initialize(upcase: false, base64: false, prefix: nil, filters: [])
8
+ @upcase, @base64, @filters = upcase, base64, filters
9
+ @prefix = ENV['AWS_SSM_PREFIX'] || prefix # IE: prefix: /demo/dev/
10
+ end
11
+
12
+ def call
13
+ items.each do |item|
14
+ next unless item.name.include?(@prefix)
15
+
16
+ resp = ssm.get_parameter(name: item.name, with_decryption: true)
17
+ parameter = resp.parameter
18
+
19
+ key = parameter.name.sub(@prefix,'')
20
+ value = parameter.value
21
+ value = Base64.strict_encode64(value).strip if @base64
22
+
23
+ key = key.upcase if @upcase
24
+ self.class.data[key] = value
25
+ end
26
+ end
27
+
28
+ # Returns flattened lazy Enumerator
29
+ def items
30
+ Enumerator.new do |y|
31
+ next_token = nil
32
+ loop do
33
+ args = {max_results: PAGE_SIZE}
34
+ args[:next_token] = next_token if next_token
35
+ args.merge!(parameter_filters: @filters)
36
+
37
+ resp = ssm.get_parameters_by_path(
38
+ path: @prefix,
39
+ )
40
+
41
+ items = resp.parameters
42
+ next_token = resp.next_token
43
+
44
+ y.yield(items, resp) # also provided the original resp always in case it is useful
45
+ break unless next_token
46
+ end
47
+ end.lazy.flat_map { |v| v }
48
+ end
49
+
50
+ PAGE_SIZE = 1
51
+
52
+ def data
53
+ self.class.data
54
+ end
55
+
56
+ class_attribute :data
57
+ self.data = {}
58
+ end
59
+ end
@@ -1,3 +1,3 @@
1
1
  module KubesAws
2
- VERSION = "0.0.0.beta"
2
+ VERSION = "0.1.0"
3
3
  end
metadata CHANGED
@@ -1,15 +1,127 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kubes_aws
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.0.beta
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tung Nguyen
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-10-13 00:00:00.000000000 Z
12
- dependencies: []
11
+ date: 2020-10-28 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activesupport
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: aws-sdk-eks
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: aws-sdk-iam
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: aws-sdk-secretsmanager
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: aws-sdk-ssm
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: aws_data
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: memoist
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: zeitwerk
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :runtime
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
13
125
  description:
14
126
  email:
15
127
  - tung@boltops.com
@@ -19,12 +131,21 @@ extra_rdoc_files: []
19
131
  files:
20
132
  - ".gitignore"
21
133
  - ".rspec"
134
+ - CHANGELOG.md
22
135
  - Gemfile
23
136
  - LICENSE.txt
24
137
  - README.md
25
138
  - Rakefile
26
139
  - kubes_aws.gemspec
27
140
  - lib/kubes_aws.rb
141
+ - lib/kubes_aws/autoloader.rb
142
+ - lib/kubes_aws/aws_services.rb
143
+ - lib/kubes_aws/iam_role.rb
144
+ - lib/kubes_aws/iam_role/prebaked.rb
145
+ - lib/kubes_aws/logging.rb
146
+ - lib/kubes_aws/open_id.rb
147
+ - lib/kubes_aws/secrets.rb
148
+ - lib/kubes_aws/ssm.rb
28
149
  - lib/kubes_aws/version.rb
29
150
  homepage: https://github.com/boltops-tools/kubes_aws
30
151
  licenses:
@@ -42,9 +163,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
42
163
  version: 2.3.0
43
164
  required_rubygems_version: !ruby/object:Gem::Requirement
44
165
  requirements:
45
- - - ">"
166
+ - - ">="
46
167
  - !ruby/object:Gem::Version
47
- version: 1.3.1
168
+ version: '0'
48
169
  requirements: []
49
170
  rubygems_version: 3.1.2
50
171
  signing_key: