Example #1
0
def cryptic(c,
            quadrant,
            encrypt="",
            decrypt="",
            cipher="PKCS1_v1_5",
            output="text"):
    """
    Handle message crypting.
    If you need to encrypt or decrypt a message specify the quadrant and action.
    For messages that need to be larger than the modulus of the key use RSA_AES,
    This cipher wont work natively with Terraform so use a data external to have
    this tool decrypt your big strings at runtime.
    """
    p_log("Task: cryptic")
    if encrypt:
        response = SecretsManager.SecretsManager({
            "key": quadrant,
            "cipher": cipher
        }).rsa_encrypt(encrypt)
    elif decrypt:
        response = SecretsManager.SecretsManager({
            "key": quadrant,
            "cipher": cipher
        }).rsa_decrypt(decrypt)

    if output == "json":
        print(json.dumps(response))
    else:
        print(response)
Example #2
0
    def read(self):
        c = False
        configs = self.get_merge_configs()
        # print(json.dumps(configs, indent=4, sort_keys=True))
        self.logger.debug("get_merge_configs: %s=%s" %
                          (self.config_file, json.dumps(configs)))
        try:
            c = ConfigManager.get(configs, self.args["attribute"])
            if c is not None:
                self.logger.info("Current Value: '%s'" % (c))
            else:
                self.logger.info("Value '%s' doesn't exists." %
                                 (self.args["attribute"]))
        except:
            self.logger.warn("Value '%s' doesn't exists." %
                             (self.args["attribute"]))

        # Emit the event to hook onto it
        ee.emit("ConfigManager.read", self.args["attribute"], c)

        if self.args["cipher"] is not None:
            c = SecretsManager.SecretsManager({
                "key": self.args["env"],
                "cipher": self.args["cipher"]
            }).secretDecoderRing(c)

        return c
Example #3
0
def pass_gen(c, length=10):
    """
    Creates a strong random password.

    """
    p_log("Task: pass_gen")
    print(SecretsManager.SecretsManager({}).passwordGenerate(length))
Example #4
0
def secrets(c, quadrant, cipher="PKCS1_v1_5"):
    """
    An interactive secret manager.
    Terraform doesn't handle secrets well, this fixes that for us.
    Using the keys we generated with the key-gen task we open an interactive
    editor with our secrets decrypted.  When we save and exit the editor our
    secrets are encrypted and stored in a our config.
    Terraform has a poorly documented function called rsadecrypt
    https://www.terraform.io/docs/configuration/interpolation.html#rsadecrypt-string-key-

    If you need to manage secrets that are larger than 245 characters
    (For a 2048bit key modulus - 11 = 245) then you should use the RSA_AES cipher
    This allows larger secrets but you can't use the built in terraform rsadecrypt

    Instead you will have to use data external like this
    Note: this will be a little slower than terraforms native rsadecrypt


    # config/${quadrant}/secrets.json
      {
        "foo": {
          "bar": {
            "API_KEY": "I got a secret, a super, super secret"
          }
        }
      }

    # projects/infrastructure/main.tf
      data "external" "secret_decrypt" {
        #Use dot notation for path to key
        program = [
          "reform",
          "config-get",
          "--quadrant",
          "${var.vpc_name}",
          "--attribute",
          "foo.bar.API_KEY",
          "--cipher",
          "RSA_AES",
          "--output",
          "json"
        ]
      }

      locals {
        API_KEY = "${data.external.secret_decrypt.result.usage}"
      }

    Now you can use local.API_KEY anywhere you need the decrypted secret and at run
    time terraform will call reform to decrypt your secret.

    """
    p_log("Task: Secrets")
    # TODO make this work with revised config format
    print(
        SecretsManager.SecretsManager(
            {"key": quadrant, "cipher": cipher}
        ).InteractiveEdit()
    )
Example #5
0
def rotate_key(c, quadrant, cipher="PKCS1_v1_5"):
    """
    Rotate our RSA Keys.
    This will move our old keys to *\*.old* and generate a new key pair.
    It then walks through our configs and re-encrypts the secrets with the new
    keys
    """
    p_log("Task: rotate_key")
    print(SecretsManager.SecretsManager({"key": quadrant, "cipher": cipher}).rekey())
Example #6
0
def get_config(c):
    """
    Fetches part of the config for use in a terraform map.
    Terraform can't handle multidimensional maps, this tool fetches a section of
    map and returns it as json.  Unlike other tasks, this tasks gets it's args
    from a json string sent to stdin.
    """
    p_log("Task: get_config")
    reform_root = settings.GetReformRoot()
    params = {}

    lines = [x.strip() for x in sys.stdin.readlines()]

    lines = list(filter(None, lines))
    if len(lines) != 0:
        params = json.loads(",".join(lines))

    c = {}

    if "cipher" in params and params["cipher"]:
        file = "%s/configs/%s/%s" % (
            reform_root,
            params["env"],
            ReformSettings.ReformSettings.reform_quadrant_secret_file,
        )
        if os.path.exists(file):
            with open(file, "r") as f:
                config = json.loads(f.read())
        else:
            p_log("Nested map not found: %s" % (file))
            exit(5)
    else:
        config = ConfigManager.ConfigManager({
            "env": params["env"]
        }).get_merge_configs()

    p_log("args: %s" % (params))

    if "swimlanes" in config:
        members = config["swimlanes"]
        # debug("Nested map found: %s"%(json.dumps(members)))
        if (params["client"] in members and params["service"]
                in members[params["client"]]["services"]):
            c = members[params["client"]]["services"][
                params["service"]]["configstore"]
    else:
        if params["client"] in config and params["service"] in config[
                params["client"]]:
            c = config[params["client"]][params["service"]]

    if "cipher" in params and params["cipher"]:
        c = SecretsManager.SecretsManager({
            "key": params["env"],
            "cipher": params["cipher"]
        }).secretDecoderRing(c)
    print(json.dumps(c))
Example #7
0
def key_gen(c, bucket, quadrant, region):
    """
    Create RSA keys for secret management.
    We will use these keys to encrypt and decrypt our secrets in terraform.
    """
    p_log("Task: key_gen")
    print(
        SecretsManager.SecretsManager(
            {"key": quadrant, "bucket": bucket, "region_name": region}
        ).generateKeyPair()
    )
Example #8
0
def key_exists(c, bucket, quadrant):
    """
    Check to see if a given key already exists
    """
    p_log("Task: key_exists")
    r = SecretsManager.SecretsManager({"bucket": bucket}).keyExists(quadrant)
    if r:
        print("Found")
        return True
    else:
        print("Not Found")
        return False
Example #9
0
    def test_rotate_key(self):
        """
    Test that we can rotate the keys in our bucket
    """
        c = MockContext()
        tasks.key_gen(c, bucket_name, quadrant, region)
        s3 = boto3.resource("s3", region)

        # Test if old key exists before
        pri_path = "%s/SecretsMaster.old" % (quadrant)
        found = False
        try:
            _obj = s3.Object(bucket_name, pri_path).load()
            found = True
        except botocore.exceptions.ClientError as e:
            found = False
        self.assertFalse(found)

        # Verify old Public key exists
        # TODO check size
        pub_path = "%s/SecretsMaster.pub.old" % (quadrant)
        found = False
        try:
            _obj = s3.Object(bucket_name, pub_path).load()
            found = True
        except botocore.exceptions.ClientError as e:
            found = False
        self.assertFalse(found)

        # Make secret before rotate
        sm = SecretsManager.SecretsManager({"key": quadrant})
        secret_file = sm.getSecretPath(quadrant)

        _secret_dict = {"test1": "hello world"}
        import copy

        copy_secret_dict = copy.deepcopy(_secret_dict)
        sm.encryptSecretFile(_secret_dict, secret_file)

        _secret_file_decrypt = sm.decryptSecretFile(secret_file)
        logging.getLogger("TestSecretsManager").debug(
            "Old: %s, New: %s" % (copy_secret_dict, _secret_file_decrypt))

        self.assertEqual(copy_secret_dict, _secret_file_decrypt)

        # Lets get a file sum
        pre_checksum = hashlib.md5(open(secret_file, "rb").read()).hexdigest()

        # Rotate the keys now
        tasks.rotate_key(c, quadrant)

        # Test if old key exists after
        pri_path = "%s/SecretsMaster.old" % (quadrant)
        found = False
        try:
            _obj = s3.Object(bucket_name, pri_path).load()
            found = True
        except botocore.exceptions.ClientError as e:
            found = False
        self.assertTrue(found)

        # Verify old Public key exists
        # TODO check size
        pub_path = "%s/SecretsMaster.pub.old" % (quadrant)
        found = False
        try:
            _obj = s3.Object(bucket_name, pub_path).load()
            found = True
        except botocore.exceptions.ClientError as e:
            found = False
        self.assertTrue(found)

        # Test if crypted strings have been updated
        post_checksum = hashlib.md5(open(secret_file, "rb").read()).hexdigest()
        self.assertNotEqual(pre_checksum, post_checksum)
Example #10
0
def preform(c, quadrant):
    """
    A simple preprocessor for terraform that processes *\*.tf.tpl* files.
    This is how we work around terraforms lack of loops and conditionals.

    This is also how we seed our dynamic reform configs for state backend and and configs we've defined.
    """
    p_log("Start: Preform")
    projects_base_path = settings.GetReformRoot()

    # TODO Open this more to include modules
    work_dir = settings.GetReformRoot()
    modules_dir = "%s/modules" % (settings.GetReformRoot())
    projects_dir = "%s/projects" % (settings.GetReformRoot())
    template_suffix = ".tpl"
    env = Environment(loader=FileSystemLoader(work_dir), trim_blocks=True)

    # Custom Jinja Filters
    def is_list(value):
        return isinstance(value, list)

    def is_dict(value):
        return isinstance(value, dict)

    env.filters["is_list"] = is_list
    env.filters["is_dict"] = is_dict
    env.filters["jsonify"] = json.dumps

    config = ConfigManager.ConfigManager({"env": quadrant}).get_merge_configs()
    secret_manager = SecretsManager.SecretsManager(
        {"key": quadrant, "cipher": "RSA_AES"}
    )
    env_secret = secret_manager.getSecretPath(quadrant)
    secrets = secret_manager.decryptSecretFile(env_secret)
    # Handle modules dir
    for directory, subdirectories, files in os.walk(modules_dir):
        for file in files:
            if file.endswith(template_suffix):
                debug("Found template file: %s" % (file))
                full_file_path = os.path.join(directory, file)
                template = env.get_template(full_file_path.replace(work_dir, ""))
                new_full_file_path = re.sub(
                    template_suffix, "", os.path.join(directory, "preform_" + file)
                )

                debug("Generating file: %s" % (new_full_file_path))
                try:
                    with open(new_full_file_path, "w+") as outfile:
                        redered_template = template.render(
                            config=config,
                            project=os.path.basename(directory),
                            quadrant=quadrant,
                            secrets=secrets,
                        )
                        debug(redered_template)
                        outfile.write(
                            "##################################################\n"
                        )
                        outfile.write(
                            "# This file auto generated by preform, do not edit!\n"
                        )
                        outfile.write("# Instead edit \n")
                        outfile.write("# %s\n" % (full_file_path))
                        outfile.write(
                            "##################################################\n"
                        )
                        outfile.write("\n\n")
                        outfile.write(redered_template)
                        outfile.write("\n\n")
                    outfile.close()
                except:
                    pass

    # Handle projects dir
    for directory, subdirectories, files in os.walk(projects_dir):
        for file in files:
            if file.endswith(template_suffix):
                debug("Found template file: %s" % (file))
                full_file_path = os.path.join(directory, file)
                template = env.get_template(full_file_path.replace(work_dir, ""))
                new_full_file_path = re.sub(
                    template_suffix, "", os.path.join(directory, "preform_" + file)
                )

                debug("Generating file: %s" % (new_full_file_path))
                with open(new_full_file_path, "w+") as outfile:
                    redered_template = template.render(
                        config=config,
                        project=os.path.basename(directory),
                        quadrant=quadrant,
                        secrets=secrets,
                    )
                    debug(redered_template)
                    outfile.write(
                        "##################################################\n"
                    )
                    outfile.write(
                        "# This file auto generated by preform, do not edit!\n"
                    )
                    outfile.write("# Instead edit \n")
                    outfile.write("# %s\n" % (full_file_path))
                    outfile.write(
                        "##################################################\n"
                    )
                    outfile.write("\n\n")
                    outfile.write(redered_template)
                    outfile.write("\n\n")
                outfile.close()

    p_log("Complete: Preform")