Skip to content

Commit c5fee90

Browse files
costimurarucmuraru
authored andcommitted
Use default AWS profile when resolving SSM secrets (#49)
1 parent 68d4aea commit c5fee90

File tree

5 files changed

+73
-18
lines changed

5 files changed

+73
-18
lines changed

src/ops/hierarchical/config_generator.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ def add_dynamic_data(self):
156156
self.merge_value(self.generated_data, remote_states)
157157

158158
def resolve_interpolations(self):
159-
resolver = InterpolationResolver(self.generated_data)
159+
resolver = InterpolationResolver()
160160
self.generated_data = resolver.resolve_interpolations(self.generated_data)
161161

162162
def validate_interpolations(self):

src/ops/hierarchical/inject_secrets.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ class SecretInjector(object):
2222
{{vault.kv2.path(ethos/k8s-ethos-config/thrash/aws/ClusterIngressTLS).field(Key)}}
2323
"""
2424

25-
def __init__(self):
26-
self.resolver = AggregatedSecretResolver()
25+
def __init__(self, default_aws_profile=None):
26+
self.resolver = AggregatedSecretResolver(default_aws_profile)
2727

2828
def is_interpolation(self, value):
2929
return value.startswith('{{') and value.endswith('}}')

src/ops/hierarchical/interpolation.py

Lines changed: 58 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,38 @@ def is_interpolation(input):
1515
return '{{' in input and '}}' in input
1616

1717

18+
class InterpolationResolver(object):
19+
20+
def resolve_interpolations(self, data):
21+
# Resolve from dictionary. Do one iteration before secret resolving, in order to resolve interpolations such as
22+
# the aws.profile
23+
# Example:
24+
# my_profile: test
25+
# aws:
26+
# profile: "{{my_profile}}"
27+
from_dict_injector = DictInterpolationResolver(data, FromDictInjector())
28+
from_dict_injector.resolve_interpolations(data)
29+
30+
# Resolve interpolations representing secrets
31+
# Example:
32+
# value1: "{{ssm.path(mysecret)}}"
33+
secrets_injector = SecretsInterpolationResolver(self.get_secret_injector(data))
34+
secrets_injector.resolve_interpolations(data)
35+
36+
# Perform another resolving, in case some secrets are used as interpolations.
37+
# Example:
38+
# value1: "{{ssm.mysecret}}"
39+
# value2: "something-{{value1}} <--- this will be resolved at this step
40+
from_dict_injector = DictInterpolationResolver(data, FromDictInjector())
41+
from_dict_injector.resolve_interpolations(data)
42+
43+
return data
44+
45+
def get_secret_injector(self, data):
46+
default_aws_profile = data['aws']['profile'] if 'aws' in data and 'profile' in data['aws'] else None
47+
return SecretInjector(default_aws_profile)
48+
49+
1850
class DictIterator():
1951

2052
def loop_all_items(self, data, process_func):
@@ -33,23 +65,39 @@ def loop_all_items(self, data, process_func):
3365
return data
3466

3567

36-
class InterpolationResolver(DictIterator):
37-
38-
def __init__(self, data, secrets_injector=SecretInjector()):
39-
self.generated_data = data
40-
self.secrets_injector = secrets_injector
41-
self.from_dict_injector = FromDictInjector()
68+
class AbstractInterpolationResolver(DictIterator):
69+
def __init__(self):
70+
pass
4271

4372
def resolve_interpolations(self, data):
4473
return self.loop_all_items(data, self.resolve_interpolation)
4574

4675
def resolve_interpolation(self, line):
4776
if not is_interpolation(line):
4877
return line
78+
return self.do_resolve_interpolation(line)
79+
80+
def do_resolve_interpolation(self, line):
81+
pass
82+
83+
84+
class DictInterpolationResolver(AbstractInterpolationResolver):
85+
def __init__(self, data, from_dict_injector):
86+
AbstractInterpolationResolver.__init__(self)
87+
self.data = data
88+
self.from_dict_injector = from_dict_injector
89+
90+
def do_resolve_interpolation(self, line):
91+
return self.from_dict_injector.resolve(line, self.data)
92+
93+
94+
class SecretsInterpolationResolver(AbstractInterpolationResolver):
95+
def __init__(self, secrets_injector):
96+
AbstractInterpolationResolver.__init__(self)
97+
self.secrets_injector = secrets_injector
4998

50-
updated_line = self.secrets_injector.inject_secret(line)
51-
updated_line = self.from_dict_injector.resolve(updated_line, self.generated_data)
52-
return updated_line
99+
def do_resolve_interpolation(self, line):
100+
return self.secrets_injector.inject_secret(line)
53101

54102

55103
class InterpolationValidator(DictIterator):
@@ -85,7 +133,7 @@ def resolve(self, line, data):
85133
continue
86134
elif isinstance(value, (int, bool)):
87135
return value
88-
else:
136+
elif not is_interpolation(value):
89137
line = line.replace(placeholder, value)
90138
return line
91139

src/ops/hierarchical/secret_resolvers.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,18 @@ def resolve(self, secret_type, secret_params):
1919

2020

2121
class SSMSecretResolver(SecretResolver):
22+
def __init__(self, default_aws_profile=None):
23+
self.default_aws_profile = default_aws_profile
24+
2225
def supports(self, secret_type):
2326
return secret_type == "ssm"
2427

2528
def resolve(self, secret_type, secret_params):
29+
aws_profile = secret_params.get("aws_profile", self.default_aws_profile)
30+
if not aws_profile:
31+
raise Exception("Could not find the aws_profile in the secret params: {}".format(secret_params))
32+
2633
path = self.get_param_or_exception("path", secret_params)
27-
aws_profile = self.get_param_or_exception("aws_profile", secret_params)
2834
region_name = secret_params.get("region_name", "us-east-1")
2935
ssm = SimpleSSM(aws_profile, region_name)
3036
return ssm.get(path)
@@ -46,13 +52,14 @@ def resolve(self, secret_type, secret_params):
4652

4753
class AggregatedSecretResolver(SecretResolver):
4854

49-
SECRET_RESOLVERS = (SSMSecretResolver(), VaultSecretResolver())
55+
def __init__(self, default_aws_profile=None):
56+
self.secret_resolvers = (SSMSecretResolver(default_aws_profile), VaultSecretResolver())
5057

5158
def supports(self, secret_type):
52-
return any([resolver.supports(secret_type) for resolver in self.SECRET_RESOLVERS])
59+
return any([resolver.supports(secret_type) for resolver in self.secret_resolvers])
5360

5461
def resolve(self, secret_type, secret_params):
55-
for resolver in self.SECRET_RESOLVERS:
62+
for resolver in self.secret_resolvers:
5663
if resolver.supports(secret_type):
5764
return resolver.resolve(secret_type, secret_params)
5865

src/ops/terraform/terraform_cmd_generator.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ def generate(self, args):
179179
else:
180180
cmd = "cd {root_dir}/{terraform_path} && " \
181181
"terraform apply " \
182-
"-refresh=true {state_out_argument} {plan_file} {variables_file}; code=$?; rm -f {plan_file}; exit $code".format(
182+
"-refresh=true {state_out_argument} {plan_file}; code=$?; rm -f {plan_file}; exit $code".format(
183183
plan_file=plan_file,
184184
root_dir=self.root_dir,
185185
state_out_argument=state_out_argument,

0 commit comments

Comments
 (0)