def test_source_valid(self): # write the vault password to the .pwd file pwd_path = "/tmp/.pwd" # make sure file is not present if os.path.exists(pwd_path): os.remove(pwd_path) # write the pwd to the file with open(pwd_path, "w") as text_file: text_file.write("open_sesame") secrets_vaults = [{ "type": "keepass", "name": "keepass-1", "configs": { "vault-path": "yac/tests/vaults/vectors/test_vault.kdbx", "vault-pwd-path": pwd_path } }] vaults = SecretVaults(secrets_vaults) # get the results keepass vault and assert that it is available vault = vaults.get_vault('keepass-1') self.assertTrue(vault)
def test_json_file_source(self): secrets_vaults = [ { "type": "file", "name": "file-1", "configs": { "vault-path": "yac/tests/vault/vectors/file_vault.json" } } ] vaults = SecretVaults(secrets_vaults) vaults.initialize({}) # get the results keepass vault and assert that it is available vault = vaults.get_vault('file-1') self.assertTrue(vault)
def test_source_valid(self): secrets_vaults = [ { "type": "s3", "name": "s3", "configs": { "bucket": "gitlab-secure", "vault-path": "secrets/s3_vault.json" } } ] vaults = SecretVaults(secrets_vaults) # get the resulting s3 vault and assert that it is ready vault = vaults.get_vault('s3') self.assertTrue(vault.is_ready())
def test_source_invalid(self): # vault-pwd is an invalid config setting for keepass secrets_vaults = [{ "type": "keepass", "name": "keepass-1", "configs": { "vault-path": "yac/tests/vaults/vectors/test_vault.kdbx", "vault-pwd": "/path/does/not/matter/for/this/test" } }] # test that these invalid vault configs don't throw a validation error vaults = SecretVaults(secrets_vaults) self.assertTrue(vaults)
def test_secrets(self): my_secrets = { "param-key-1": { "comment": "branch 1, child 1, entry 1", "source": "keepass", "lookup": { "path": "Branch 1/B1-C1/B1-C1-E1", "field": "password" } }, "param-key-2": { "comment": "branch 2, child 2, entry 1", "source": "keepass", "lookup": { "path": "Branch 2/B2-C2/B2-C2-E1", "field": "password" } } } secrets_vaults = [{ "type": "keepass", "name": "keepass", "configs": { "vault-path": "yac/tests/vault/vectors/test_vault.kdbx", "vault-pwd-path": TestCase.pwd_path } }] params = Params({}) vaults = SecretVaults(secrets_vaults) secrets = Secrets(my_secrets) secrets.load(params, vaults) print(secrets.get_errors()) both_loaded = (params.get("param-key-1") == 'b1-c1-e1-secret' and params.get("param-key-2") == 'b2-c2-e1-secret') self.assertTrue(both_loaded)
def test_file_contents(self): secrets_vaults = [{ "type": "b64", "name": "my-file", "configs": { "vault-path": "yac/tests/vault/vectors/b64_vault.yaml" } }] vaults = SecretVaults(secrets_vaults) vaults.initialize({}) self.assertTrue(vaults.get("my-file", "secret1") == "secret1 value") self.assertTrue(vaults.get("my-file", "secret2.prod") == "prod value") self.assertTrue(vaults.get("my-file", "secret3[1]") == "val2")
def test_schema_good(self): serialized_obj = [{ "type": "s3", "name": "main", "configs": { "comment": "vault is in our nonprod v2 account", "bucket": "gitlab-secrets-dots", "vault-path": "gitlab-secrets.json", "format": "json" } }, { "type": "file", "name": "local", "configs": { "comment": "vault is in a local file", "vault-path": "/opt/gitlab/etc/secrets.yaml", "format": "yaml" } }, { "type": "keepass", "name": "keepass", "configs": { "comment": "vault is in a local keepass vault", "vault-path": "/opt/etc/vault.kdbx", "vault-pwd-path": "/tmp/.pwd" } }] # test that no schema validation errors are raised validation_success = True try: secrets = SecretVaults(serialized_obj) except ValidationError as e: validation_success = False print("validation failed") self.assertTrue(validation_success == True)
def __init__(self, serialized_service, service_path, alias="", params_path="", kvps=""): # first validate. this should throw an exception if # required fields aren't present validate(serialized_service, "yac/schema/service.json") self.path = service_path self.kvps_str = kvps self.params_file_path = params_path self.description = Description(search('Description', serialized_service,{}), alias) self.vaults = SecretVaults(search('"Vaults"', serialized_service,[])) # a service can references other services that it includes self.includes = search("includes", serialized_service,{}) # initialize stack params (key/value pairs and maps), including static params specified # in the serialized service, params from an external file, # params specified in a key-value pair string (kvps), self.params = Params(search('"Params"', serialized_service, {})) # initialize the dictionary that will hold all params (statics+secrets+inputs) self.all_params = {} inputs_cacher = InputsCacher(search('InputsCache', serialized_service,{})) self.inputs = Inputs(search('Inputs', serialized_service,{}), inputs_cacher) self.secrets = Secrets(search('"Secrets"', serialized_service,{})) # inialize the stack associated with this service self.stack = Stack(search('Stack', serialized_service, {})) self.tasks = Tasks(search('Tasks', serialized_service,{})) # initialize the tests associated with this service self.tests = IntegrationTests(search('"IntegrationTests"', serialized_service,{})) # initialize the artifacts associate with this service self.artifacts = Artifacts(search('Artifacts', serialized_service,[])) # initialize the credentialer associated with this service self.credentialers = Credentialers(search("Credentialers",serialized_service,[])) # initialize the pipeline associated with this service self.pipeline = Pipeline(search('Pipeline', serialized_service,{})) # load the objects from each included service self.load_includes() # save a copy of the full serialized version of the # service to support the serialize() method self.serialized_service = serialized_service
class Service(): def __init__(self, serialized_service, service_path, alias="", params_path="", kvps=""): # first validate. this should throw an exception if # required fields aren't present validate(serialized_service, "yac/schema/service.json") self.path = service_path self.kvps_str = kvps self.params_file_path = params_path self.description = Description(search('Description', serialized_service,{}), alias) self.vaults = SecretVaults(search('"Vaults"', serialized_service,[])) # a service can references other services that it includes self.includes = search("includes", serialized_service,{}) # initialize stack params (key/value pairs and maps), including static params specified # in the serialized service, params from an external file, # params specified in a key-value pair string (kvps), self.params = Params(search('"Params"', serialized_service, {})) # initialize the dictionary that will hold all params (statics+secrets+inputs) self.all_params = {} inputs_cacher = InputsCacher(search('InputsCache', serialized_service,{})) self.inputs = Inputs(search('Inputs', serialized_service,{}), inputs_cacher) self.secrets = Secrets(search('"Secrets"', serialized_service,{})) # inialize the stack associated with this service self.stack = Stack(search('Stack', serialized_service, {})) self.tasks = Tasks(search('Tasks', serialized_service,{})) # initialize the tests associated with this service self.tests = IntegrationTests(search('"IntegrationTests"', serialized_service,{})) # initialize the artifacts associate with this service self.artifacts = Artifacts(search('Artifacts', serialized_service,[])) # initialize the credentialer associated with this service self.credentialers = Credentialers(search("Credentialers",serialized_service,[])) # initialize the pipeline associated with this service self.pipeline = Pipeline(search('Pipeline', serialized_service,{})) # load the objects from each included service self.load_includes() # save a copy of the full serialized version of the # service to support the serialize() method self.serialized_service = serialized_service # add mergeable fields from another service into this service def add(self, service): if service.params: self.params.add(service.params) if service.secrets: self.secrets.add(service.secrets) if service.vaults: self.vaults.add(service.vaults) if service.stack.impl: # there can be only one stack if self.stack.impl: self.stack.add(service.stack) else: self.stack = service.stack if service.tasks: self.tasks.add(service.tasks) if service.inputs: self.inputs.add(service.inputs) if service.tests: self.tests.add(service.tests) if service.artifacts: self.artifacts.add(service.artifacts) if service.credentialers: self.credentialers.add(service.credentialers) if service.pipeline and service.pipeline.get_stages(): # there can be only one pipeline per service self.pipeline = service.pipeline def add_params_via_kvps(self,kvp_str): # load key-value pairs via a kvp string formatted as: # <key1>:<value1>,<key2>:<val2>,etc self.params.load_kvps(kvp_str) def load_includes(self): # load objects from each included service # for each included service specified for service_key in self.includes: sub_service_path = self.includes[service_key]["value"] # load the included service ... this_sub, err = get_service(sub_service_path, servicefile_path=self.path) # add to this service if not err: print("including '%s' service ..."%(service_key)) self.add(this_sub) else: print("included service '%s' could not be loaded from %s"%(service_key, sub_service_path)) print("error: %s"%err) print("exiting ...") exit(0) exit(0) def get_meta_params(self): # get meta data about this service service_metadata = Params({}) service_metadata.set("service-default-alias",self.description.default_alias, "service default alias") service_metadata.set("service-alias",self.description.alias, "service alias") service_metadata.set("service-name",self.description.name, "service name") service_metadata.set("servicefile-path",self.path, "path to the servicefile") # add service summary and repo service_metadata.set('service-summary',self.description.summary, "service summary") service_metadata.set('service-repo',self.description.repo, "repo containing this service") # add the command that was run against this service service_metadata.set("yac-command",sys.argv[0], 'the yac command being run') return service_metadata def get_params(self): # add params describing the service itself self.params.add(self.get_meta_params()) # load any params from yac-supported env variables self.params.load_from_env_variables() # load kvps (typically used for injecting inputs in pipelines or overriding # an invidual param setpoint) self.params.load_kvps(self.kvps_str) # load params from file self.params.load_from_file(self.params_file_path) return self.params def get_all_params(self, context="", dry_run_bool=False, credentialer_names=[]): # Take a copy of params self.all_params = self.get_params() # process inputs and load results into params self.inputs.load(self.all_params) # load secrets into params self.secrets.load(self.all_params, self.vaults) return self.all_params def get_description(self): return self.description def get_artifacts(self): return self.artifacts def get_stack(self): return self.stack def get_tests(self): return self.tests def get_tasks(self): return self.tasks def get_vaults(self): return self.vaults def get_deployer(self): return self.deployer() def get_inputs(self): return self.inputs def get_secrets(self): return self.secrets def get_pipeline(self): return self.pipeline def get_credentialers(self): return self.credentialers def get_serialized_pipeline(self): return self.serialized_pipeline def deploy_boot_files(self, dry_run_bool=False): self.boot_files.deploy(self.params, dry_run_bool) def serialize(self): return self.serialized_service def __str__(self): ret = ("description:\n %s\n"%self.description + "params:\n %s\n"%self.params + "secrets:\n %s\n"%self.secrets + "stack:\n %s\n"%self.stack + "vaults: \n %s\n")%self.vaults return ret