security

My SimpliSafe doorbell lit its own fire this winter

...I'm just glad it was on the outside of the building, attached to non-flammable material :)

As part of my new studio/office buildout, I needed a 'smart' doorbell, so I could accept deliveries or see who rang, even if I was far from the door or recording.

I bought a SimpliSafe system for my location, and tied it into Home Assistant. It was easy to set up, the monthly cost was a fraction of what ADT wanted to charge, and yes, I know it's wireless-only communication can be tampered with. It's like a lock—it helps keep people honest, and is only one small part of a balanced security diet.

SimpliSafe Video Doorbell Pro

But I installed their Video Doorbell Pro a couple weeks ago, and setup was a breeze. Just get 24v doorbell wire to it, and bingo! You have a smart doorbell.

Three DDoS attacks on my personal website

Update: After posting the video yesterday, the site was hit by more low-complexity DDoS attacks, mostly just spamming one URL at a time. After I cleaned those up, the attacker finally switched to a more intelligent offense, posting actual comments to the site overnight. This morning I noticed that, and the fact the attacker found I left my edit domain un-proxied, so I switched to a different IP on DigitalOcean and shored up the Cloudflare configuration a bit more.

It was a good thing I did that, because about the same time, I got an email from DigitalOcean support saying they had to blackhole the other IP for getting 2,279,743 packets/sec of inbound traffic. Sheesh.

After cleaning up a few bits of fallout, the site should be running a bit better at this point, DDoS or no.

Rate limiting requests per IP address in Nginx

Just wanted to post this here, since I've had to do this from time to time, and always had to read through the docs and try to build my own little example of it...

If you're trying to protect an Nginx server from a ton of traffic (especially from a limited number of IP addresses hitting it with possibly DoS or DDoS-type traffic), and don't have any other protection layer in front, you can use limit_req to rate limit requests at whatever rate you choose (over a given time period) for any location on the server.

# Add this to your virtual host config file.
limit_req_zone $binary_remote_addr zone=mylimit:10m rate=10r/s;

# Later, in a `server` block:
server {
    location ~ \.php$ {
        limit_req zone=mylimit;
        ...
    } 
    ...
}

I have had to do this sometimes when I noticed a few bad IPs attacking my servers. You can adjust the rate and zone settings to your liking (the above settings limit requests to any PHP script to 10 per second over a 10 minute period).

Deadbolt impacts some ASUSTOR NASes — check your backup plan!

ASUSTOR ARM NAS with four hard drives and cover removed

A few months ago, I wrote up a post covering my backup plan. In it, I talk about the 3-2-1 backup strategy:

  • 3 copies of all your important data
  • 2 different media
  • 1 offsite

In that post, I mentioned I back everything up with two local copies (two separate NAS units), and a third offsite copy on Amazon Glacier Deep Archive.

Using an Ansible playbook with an SSH bastion / jump host

Since I've set this up a number of times, but I just realized I've never documented it on my blog, I thought I'd finally do that.

I have a set of servers that are running on a private network. That network is connected to the Internet through a single reverse proxy / 'bastion' host.

But I still want to be able to manage the servers on the private network behind the bastion from outside.

Method 1 - Inventory vars

The first way to do it with Ansible is to describe how to connect through the proxy server in Ansible's inventory. This is helpful for a project that might be run from various workstations or servers without the same SSH configuration (the configuration is stored alongside the playbook, in the inventory).

In my Ansible project, I had an inventory file like the following:

How to stop a form from blocking paste in Safari

This is a quick blog post, mostly for my own reference.

I finally got sick of a certain government website thinking that preventing pasting passwords into certain forms was some sort of security feature, so I am documenting my workaround in Safari for stupid forms written by compliance-minded folks (the same who think that expiring passwords every 30 days leads to any kind of better security).

In Safari, select Develop > Show Javascript Console (or press ⌥⌘C, that's Option + Command + 'C')1.

Paste the following into the console and press 'Enter':

var allowPaste = function(e){
  e.stopImmediatePropagation();
  return true;
};
document.addEventListener('paste', allowPaste, true);

Now you can paste to your heart's content.

1 If you don’t see the Develop menu in the menu bar, choose Safari > Preferences, click Advanced, then select “Show Develop menu in menu bar.”

Be careful, Docker might be exposing ports to the world

Recently, I noticed logs for one of my web services had strange entries that looked like a bot trying to perform scripted attacks on an application endpoint. I was surprised, because all the endpoints that were exposed over the public Internet were protected by some form of authentication, or were locked down to specific IP addresses—or so I thought.

I had re-architected the service using Docker in the past year, and in the process of doing so, I changed the way the application ran—instead of having one server per process, I ran a group of processes on one server, and routed traffic to them using DNS names (one per process) and Nginx to proxy the traffic.

In this new setup, I built a custom firewall using iptables rules (since I had to control for a number of legacy services that I have yet to route through Docker—someday it will all be in Kubernetes), installed Docker, and set up a Docker Compose file (one per server) that ran all the processes in containers, using ports like 1234, 1235, etc.

The Docker Compose port declaration for each service looked like this:

Everyone might be a cluster-admin in your Kubernetes cluster

Quite often, when I dive into someone's Kubernetes cluster to debug a problem, I realize whatever pod I'm running has way too many permissions. Often, my pod has the cluster-admin role applied to it through its default ServiceAccount.

Sometimes this role was added because someone wanted to make their CI/CD tool (e.g. Jenkins) manage Kubernetes resources in the cluster, and it was easier to apply cluster-admin to a default service account than to set all the individual RBAC privileges correctly. Other times, it was because someone found a new shiny tool and blindly installed it.

One such example I remember seeing recently is the spekt8 project; in it's installation instructions, it tells you to apply an rbac manifest:

kubectl apply -f https://raw.githubusercontent.com/spekt8/spekt8/master/fabric8-rbac.yaml

What the installation guide doesn't tell you is that this manifest grants cluster-admin privileges to every single Pod in the default namespace!

Getting AWS STS Session Tokens for MFA with AWS CLI and kubectl for EKS automatically

I've been working on some projects which require MFA for all access, including for CLI access and things like using kubectl with Amazon EKS. One super-annoying aspect of requiring MFA for CLI operations is that every day or so, you have to update your STS access token—and also for that token to work you have to update an AWS profile's Access Key ID and Secret Access Key.

I had a little bash function that would allow me to input a token code from my MFA device and it would spit out the values to put into my .aws/credentials file, but it was still tiring copying and pasting three values every single morning.

So I wrote a neat little executable Ansible playbook which does everything for me:

To use it, you can download the contents of that file to /usr/local/bin/aws-sts-token, make the file executable (chmod +x /usr/local/bin/aws-sts-token), and run the command: