Checking for Lint in your CloudFormation Templates

    If you are like me, you want your templates to pass muster and therefore usually pass them through some type of verification tool beyond the human eye, which is a poor verification tool by the way. Especially without caffiene.

    As much as I love hunting config files for missing comas and quotation marks, or an out of alignment issue. I would rather spend the time thinking about the content of the template, rather the formatting.

    VS Code Extension

    So have I ventured to find a few solutions to make the hunt easier.

    Install cfn-lint

    Installing cfn-lint is usually a few keystrokes away using your favourite package manager.

    Linux

    If you are on a Debian-based Linux

    apt install cfn-lint
    

    or if you run a Red Hat based system

    yum install cfn-lint
    

    or Arch Linux (umm, okay, I can see it)

    sudo pacman -S python-cfn-lint
    

    or Gentoo Linux (what the hell? why?)

    ebuild dev-python/cfn-python-lint
    

    macOS

    brew install cfn-lint
    

    FreeBSD

    pkg install cfn-lint
    

    This should get cfn-lint installed to your system. Verify.

    $ cfn-lint -v
    cfn-lint 0.48.2
    

    Command line

    Using this script you can check your CloudFormation templates until the cows come home. But, that is going to get old real quick. And you will want to either add an extension to your editor or at the very least add the pre-commit hooks to prevent commits with templates that don’t pass.

    Issues can be output in different formats. There are parseable, json, junit, and pretty formats. For example, if you just want parse output in a script or command.

    $ cfn-lint --format parseable template.yml -f parseable
    template.yml:115:3:115:15:W3011:Both UpdateReplacePolicy and DeletionPolicy are needed to protect Resources/ConfigBucket from deletion
    

    Or if you want JSON output

    $ cfn-lint -t template.yml -f json
    [
        {
            "Filename": "templates.yml",
            "Level": "Warning",
            "Location": {
                "End": {
                    "ColumnNumber": 15,
                    "LineNumber": 115
                },
                "Path": [
                    "Resources",
                    "ConfigBucket"
                ],
                "Start": {
                    "ColumnNumber": 3,
                    "LineNumber": 115
                }
            },
            "Message": "Both UpdateReplacePolicy and DeletionPolicy are needed to protect Resources/ConfigBucket from deletion",
            "Rule": {
                "Description": "Both UpdateReplacePolicy and DeletionPolicy are needed to protect resources from deletion",
                "Id": "W3011",
                "ShortDescription": "Check resources with UpdateReplacePolicy/DeletionPolicy have both",
                "Source": "https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-attribute-deletionpolicy.html"
            }
        }
    ]
    

    Visual Studio Code & Codium

    For the people that like to do things away from the command line, CloudFormation Linter is an extension that can add CodeLens details about template issues to VSC.

    I couldn’t find it in the Codium marketplace, so I had to download the VISX file and install manually. VS Code should be a straight click Install of the extension from the Microsoft Visual Studio Code marketplace.

    Git Pre Commit Hooks

    Install

    brew install pre-commit
    

    Or from Python

    pip install pre-commit
    

    Repository Config

    You can setup git repository pre-commit hooks to run cfn-lint against the file(s) in the commit. Of course, this requires that pre-commit be installed to the system.

    # .pre-commit-config.yaml
    repos:
    # cfn-python-lint
    - repo: https://github.com/aws-cloudformation/cfn-python-lint
      rev: v0.48.2
      hooks:
        - id: cfn-python-lint
          files: templates/.*\.(json|yml|yaml)$
    

    The files entry uses regexp to math path and file names.

    When you commit the pre-hook will run the templates through cfn-lint, preventing any templates that don’t pass cfn-lint.

    $ git commit -m "Extremely nice commit message!"
    AWS CloudFormation Linter................................................Passed
    

    Happy Linting!

    Filed in: AWS, CloudFormation, DevOps
    Reading Time: 3 minute(s)

    Amazon EC2 SSH Public Keys via IAM Service

    There will come a day when we can provision users and provide the necessary authorization to required enterprise resources with easy, but until that day comes there will be hacks like this one.

    This is very much Proof of Concept (PoC), I have not tested this beyond my network, and there would be a lot of angles to consider with any type of deployment, security (sanitization and additional logic), IAM SSH Public Keys were designed for CodeCommit, and other unknowns.

    My first attempt was to create a PAM module that would authenticate SSH users via aws-sdk-cpp libraries, accessing the API using instance profile / EC2 IAM roles, allowing EC2 instances to obtain credentials to access the information. This seemed like a good route, but trying to hack a PAM module together that would be portable enough for distribution would be a challenge, static versions of aws-sdk-cpp, etc. I will revisit this in the future, but for now, I will move on.

    On my second attempt, I create a Bash script that would produce the correct output for SSH AuthorizedKeysCommand directive using SSH public keys from AWS IAM service via awscli. As the EC2 instance can utilize the instance profile role via aws command. This didn’t come without headaches, permission issues, etc.

    Check sshd DEBUG logs if having issues with execution.

    chmod 0744 /usr/local/bin/aws-iam-authorized-keys
    chown root:root /usr/local/bin/aws-iam-authorized-keys
    

    Once I was finally able to get sshd to execute aws-iam-authorized-keys (my aptly named script), it was able to query for the username’s public SSH keys in IAM and output them in the proper format.

    For the EC2 instance to access the IAM to list and retrieve keys, it will require an instance profile, that allows ListSSHPublicKeys and GetSSHPublicKey actions, similar to the following IAM role policy I have been using. You can read more in the user guide under IAM Roles for Amazon EC2:

    {
        "Version": "2012-10-17",
        "Statement": [
            {
                "Effect": "Allow",
                "Action": [
                    "iam:GetSSHPublicKey",
                    "iam:ListSSHPublicKeys"
                ],
                "Resource": [
                    "*"
                ]
            }
        ]
    }
    

    The two /etc/ssh/sshd_config directives

    AuthorizedKeysCommand /usr/local/bin/aws-iam-authorized-keys
    AuthorizedKeysCommandUser nobody
    

    The daemon sshd will pass the username to the script or application that you supply to AuthorizedKeysCommand as the first argument, and will execute it as the user-defined in AuthorizedKeysCommandUser.

    In this scenario, I have a user in IAM with the name jonathan, SSH public key, SSH private key on my notebook (passphrase protected), local EC2 Linux user jonathan. This will allow my notebook SSH client to authenticate, after I provide the passphrase of course.

    jonathan$ ssh [email protected]
    Enter passphrase for key '/Users/jonathan/.ssh/id_rsa': 
    Last login: Thu Jan 26 03:44:03 2017 from
    
     __| __|_ )
     _| ( / Amazon Linux AMI
     ___|\___|___|
    
    https://aws.amazon.com/amazon-linux-ami/2016.09-release-notes/
    
    [jonathan@ip-X-Y-Z-114 ~] $ id uid=501(jonathan) gid=501(jonathan) groups=501(jonathan)
    [jonathan@ip-X-Y-Z-114 ~] $ ls -la .ssh/ total 8 drwxr-xr-x 2 jonathan jonathan 4096 Jan 26 02:12 . drwx------ 3 jonathan jonathan 4096 Jan 26 03:26 .. 
    [jonathan@ip-X-Y-Z-114 ~]
    

    Eureka! We have made a successful authentication. I have increased the log level to DEBUG using the sshd_config directive LogLevel. With this, we can see what sshd is doing.

    Filed in: AWS, EC2, IAM
    Reading Time: 3 minute(s)