Sep 01, 2016

Self service AWS IAM policy

A common practice for me when a new member joins the team or when someone forgets his/ her AWS account password is to change the account password myself, send the new password over an insecure channel (email, Slack) but force the account to change the password on first login. Also, I prefer to have users manage their own keys to AWS themselves. But without the correct IAM policy users aren't able to perform either action. Here's an IAM to allow both:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "iam:GetAccountPasswordPolicy",
                "iam:ListAccount*",
                "iam:GetAccountSummary",
                "iam:GetAccountPasswordPolicy",
                "iam:ListUsers"
            ],
            "Resource": "*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "iam:ChangePassword",
                "iam:*LoginProfile",
                "iam:*AccessKey*",
                "iam:*SSHPublicKey*"
            ],
            "Resource": "arn:aws:iam::<INSERT AWS ACCOUNT ID HERE>:user/${aws:username}"
        }
    ]
}

If you want a little script with the AWS CLI, here's one for you:

tempfile=$(mktemp)
accountid="$(aws ec2 describe-security-groups \
    --group-names 'Default' \
    --query 'SecurityGroups[0].OwnerId' \
    --output text)"
curl https://www.shore.co.il/blog/static/policy.json | sed "s/<INSERT AWS ACCOUNT ID HERE>/$accountid/" > $tempfile
aws iam create-policy \
    --policy-name change-own-password \
    --policy-document file://$tempfile
rm $tempfile

Aug 16, 2016

Ad-hoc serving of git repositories

On some occasion you want to serve your git repo from your local copy (perhaps your git repository is quite large and your internet connection is slow or your build process would benefit from pulling from an intermediary without authentication). Here are 2 ways to serve your git repository without any configuration or software installation. Both ways serve a single repository without authentication or encryption but read-only (no push).

Using the git protocol

The git executable is itself a git server using the native git protocol. Inside the root of the repository run the following command

git daemon --reuseaddr --verbose  --base-path=. --export-all ./.git

And on the client you can clone by running

git clone git://servername/ reponame

Using the HTTP protocol

This way serves the repo over HTTP using Python 2's SimpleHTTPServer. Run the following in the rot of the git repo

git update-server-info
cd .git
python -m SimpleHTTPServer

And on the client clone by running

git clone http://servername:8000/ reponame

Final words

I've added both ways as git aliases in my rcfiles repo.

Jul 05, 2016

SSH security

Over the years I'd heard many people share their best practices regarding securing SSH access and have been asked by friends and colleagues how to secure their servers. So here are my opinions and practices regarding SSH security. The main point I try to get across is balance. Balance between security and functionality.

Practices I avoid

First, changing the listening port. The upside is that a high random port is scanned less often and the various script kiddies sometimes fails to notice it, thus reducing the noise in the logs. This however is no real security measure as any capable attacker will quickly spot the daemon listening on a different and all benefits will be lost. The downside is that by not using the default port you need to configure all clients accordingly. So, no substantial wins and minor loss. I pass on this idea.

The second most common is allowing access only from the office IP or a few select IP addresses. The security benefit is high but the risk is also high. I view SSH access to servers as critical and limiting access puts you in risk of locking yourself out in case of trouble/ emergency. Not having management access to your servers is more dangerous to your business than allowing whomever to try (not succeed) to access your servers. Therefore I prefer to limit the access in different ways.

Practices I employ

Most importantly, allow only key-based authentication. This works without relying on 3rd party services and is extremely secure when done properly. The most important issue keeping your private key private. Personally I keep my home directory on a separate partition that is encrypted with LUKS so the keys are encrypted when in rest. If you don't have encrypted storage for your keys, password encrypt the keys (consult the ssh-keygen manual for instructions).

Another measure of security is limiting the number of authentication attempts any single IP can perform in a given time. This is achieved by 2 actions. By ensuring that MaxAuthTries in your sshd config is not set too high (the default is 6 which is damn reasonable) and by limiting the number of TCP connections to port 22 any IP can initiate. With UFW on Linux this is done by running ufw limit ssh and for OpenBSD or FreeBSD I'd refer you to Peter Hansteen's great PF tutorial.

These 2 steps will create a barrier to entry that no brute-force attack will be able to overcome in anyone's lifetime. This is what I do on all of my servers and it has served me well. However, although the commonly used OpenSSH is extensively used, researched and tested, bugs still happen. Keeping current with updates is vital.

Also, some other good practices are disabling root login, SSH protocol version 1 is deprecated, insecure and must be turned off (is off by default, but I felt it's worth mentioning). To further simplify things, here is a short Ansible playbook that covers the actions mentioned above for Debian based systems.

---
- hosts: all
  handlers:
  - name: Restart SSH
    service:
      name: ssh
      state: restarted

  tasks:
  - name: APT install and update
    with_items:
    - openssh-server
    - ufw
    apt:
      name: '{{ item }}'
      state: latest
      update_cache: yes
      cache_valid_time: 3600

  - name: Configure SSHd
    with_dict:
      MaxAuthTries: 10
      PasswordAuthentication: no
      PermitRootLogin: no
      Protocol: 2
    lineinfile:
      dest: /etc/ssh/sshd_config
      regexp: '{{ item.key }}'
      line: '{{ item.key }} {{ item.value }}'
      state: present
    notify:
    - Restart SSH

  - name: Enable UFW
    ufw:
      state: enabled

  - name: Rate limit SSH
    ufw:
      rule: limit
      port: ssh
      protocol: tcp