justinlillico.com

Jan 03, 2023

Containerized Application with SSL

This drives me nuts so I more or less favour cloud builds now to get away from having to setup HTTPS. The problem is, its just so necessary.

So I am documenting here how to do this using docker compose, nginx and certbot. One resource that helped me greatly was this excellent tutorial:

https://pentacent.medium.com/nginx-and-lets-encrypt-with-docker-in-less-than-5-minutes-b4b8a60d3a71

So here is how we do it:

You are going to need somewhere to host this. I used Linode.

Once you have a cheap instance spun up (Ubuntu 20ish should do), run the following:

sudo apt update && sudo apt upgrade -y

sudo apt install docker-io docker-compose

This will get you the magical docker.

Now, create yourself a docker-compose.yml that looks like this:

version: "3"



services:

  yourapplication:

    container_name: yourapplication

    image: yourapplication/yourapplication:latest





  nginx:

    image: nginx

    volumes:

      - ./data/certbot/conf:/etc/letsencrypt

      - ./data/certbot/www:/var/www/certbot

      - ./nginx.conf:/etc/nginx/conf.d/default.conf



    command: "/bin/sh -c 'while :; do sleep 6h & wait $${!}; nginx -s reload; done & nginx -g \"daemon off;\"'"



    ports:

      - "80:80"

      - "443:443"



  certbot:

    image: certbot/certbot

    volumes:

      - ./data/certbot/conf:/etc/letsencrypt

      - ./data/certbot/www:/var/www/certbot

    entrypoint: "/bin/sh -c 'trap exit TERM; while :; do certbot renew; sleep 12h & wait $${!}; done;'"

There is a lot going on here. I am assuming you have some application you want to give SSL to. So replace yourapplication with that. Expose port 80 or whatever port you are going to tell nginx to forward requests to.

./nginx.conf:/etc/nginx/conf.d/default.conf

The above line maps a file in your current directory to a directory in the nginx container. Lets make that file!

sudo nano nginx.conf

Paste in the following:

server {

    listen 80;

    server_name YOURDOMAIN;

    location / {

        return 301 https://$host$request_uri;

    }

    location /.well-known/acme-challenge/ {

        root /var/www/certbot;

    }

}



server {

    listen                  443 ssl;

    listen                  [::]:443 ssl;

    server_name             YOURDOMAIN;



    ssl_certificate /etc/letsencrypt/live/YOURDOMAIN/fullchain.pem;

    ssl_certificate_key /etc/letsencrypt/live/YOURDOMAIN/privkey.pem;



    include /etc/letsencrypt/options-ssl-nginx.conf;

    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;



    location / {

        proxy_pass "http://YOURAPPLICATIONCONTAINERNAME:THEPORTYOUEXPOSED";

        proxy_http_version 1.1;

        proxy_set_header Upgrade $http_upgrade;

        proxy_set_header Connection "upgrade";

        proxy_set_header Host $host;

    }



    error_page   500 502 503 504  /50x.html;



}

So basically you are saying (I think)

Hey, nginx, if anyone navigates to YOURDOMAIN using http, redirect it to https. And if anyone shows up there, you can find the SSL stuff in this location to provide a cert and forward that to the docker application at http://YOURAPPLICATIONCONTAINERNAME:THEPORTYOUEXPOSED

Justin Lillico

There is a good chance I am misunderstanding this, but I had a crack!

So as the tutorial I am basing this on mentioned, there is a bit of a chicken or egg situation in that nginx needs the cert to start and the cert needs nginx to be obtained. So they have this neat little script to take care of business:

#!/bin/bash



if ! [ -x "$(command -v docker-compose)" ]; then

  echo 'Error: docker-compose is not installed.' >&2

  exit 1

fi



domains=(example.org www.example.org)

rsa_key_size=4096

data_path="./data/certbot"

email="" # Adding a valid address is strongly recommended

staging=0 # Set to 1 if you're testing your setup to avoid hitting request limits



if [ -d "$data_path" ]; then

  read -p "Existing data found for $domains. Continue and replace existing certificate? (y/N) " decision

  if [ "$decision" != "Y" ] && [ "$decision" != "y" ]; then

    exit

  fi

fi





if [ ! -e "$data_path/conf/options-ssl-nginx.conf" ] || [ ! -e "$data_path/conf/ssl-dhparams.pem" ]; then

  echo "### Downloading recommended TLS parameters ..."

  mkdir -p "$data_path/conf"

  curl -s https://raw.githubusercontent.com/certbot/certbot/master/certbot-nginx/certbot_nginx/_internal/tls_configs/options-ssl-nginx.conf > "$data_path/conf/options-ssl-nginx.conf"

  curl -s https://raw.githubusercontent.com/certbot/certbot/master/certbot/certbot/ssl-dhparams.pem > "$data_path/conf/ssl-dhparams.pem"

  echo

fi



echo "### Creating dummy certificate for $domains ..."

path="/etc/letsencrypt/live/$domains"

mkdir -p "$data_path/conf/live/$domains"

docker-compose run --rm --entrypoint "\

  openssl req -x509 -nodes -newkey rsa:$rsa_key_size -days 1\

    -keyout '$path/privkey.pem' \

    -out '$path/fullchain.pem' \

    -subj '/CN=localhost'" certbot

echo





echo "### Starting nginx ..."

docker-compose up --force-recreate -d nginx

echo



echo "### Deleting dummy certificate for $domains ..."

docker-compose run --rm --entrypoint "\

  rm -Rf /etc/letsencrypt/live/$domains && \

  rm -Rf /etc/letsencrypt/archive/$domains && \

  rm -Rf /etc/letsencrypt/renewal/$domains.conf" certbot

echo





echo "### Requesting Let's Encrypt certificate for $domains ..."

#Join $domains to -d args

domain_args=""

for domain in "${domains[@]}"; do

  domain_args="$domain_args -d $domain"

done



# Select appropriate email arg

case "$email" in

  "") email_arg="--register-unsafely-without-email" ;;

  *) email_arg="--email $email" ;;

esac



# Enable staging mode if needed

if [ $staging != "0" ]; then staging_arg="--staging"; fi



docker-compose run --rm --entrypoint "\

  certbot certonly --webroot -w /var/www/certbot \

    $staging_arg \

    $email_arg \

    $domain_args \

    --rsa-key-size $rsa_key_size \

    --agree-tos \

    --force-renewal" certbot

echo



echo "### Reloading nginx ..."

docker-compose exec nginx nginx -s reload

I know its a monster, so you can use the below command to create it on your linux box without copy and pasting:

curl -L https://raw.githubusercontent.com/wmnnd/nginx-certbot/master/init-letsencrypt.sh > init-letsencrypt.sh

In any case, pay attention to these lines:

domains=(example.org www.example.org)

email="" # Adding a valid address is strongly recommended

Add your email and a space delimited list of the domains you are verifying. You are probably only doing one and that is okay!

Now, make the file executable and run it!

chmod +x init-letsencrypt.sh

./init-letsencrypt.sh

Once it has done its thing, you should be ready to fully launch this sucker!

docker-compose up -d

Test all is well. If I have messed anything up, let me know!

Jan 01, 2023

Raspberry Pi Dashboard

I got bored and thought it might be nice to have a simple way for my partner and I to see what is coming up next in our lives.

The finished product

So I'm not going to go over the exact setup here, it is pretty easy to do with a free account at dakboard.com. I'll just tell you the commands you need to do what I did. Just get yourself a fresh Raspberry Pi and input the following commands:

sudo apt update

sudo apt upgrade

That will get the old girl up-to-date and ready to do cool stuff.

You'll need to grab your Private URL for the board you have no doubt created at dakboard by now:

Get your URL from here.

So you are gonna need to setup chromium to launch when the pi boots. To do that, first ensure the following directory path exists:

~/.config/lxsession/LXDE-pi/

Once it does, run the following:

sudo nano ~/.config/lxsession/LXDE-pi/autostart

This file will tell your raspberry pi what to do instead of its normal boot process. Inside this file, we can paste this:

@xset s off

@xset -dpms

@xset s noblank

@chromium-browser --noerrdialogs --kiosk YOUR_URL_HERE

Make sure to replace YOUR_URL_HERE with your board URL.

Let's also get the pesky cursor to go back into the fiery chasm from whence it came:

sudo sed -i -- "s/#xserver-command=X/xserver-command=X -nocursor/" /etc/lightdm/lightdm.conf

That will pretty much do the job.

Bonus Round 1: .sh script to refresh the browser

If you don't want to wait the default one hour for the dashboard to update, you can install xdotool to help.

sudo apt install xdotool

Once this is installed, make yourself a nice .sh script and put this goodness within:

export DISPLAY=":0"

WID=$(xdotool search --onlyvisible --class chromium|head -1)

xdotool windowactivate ${WID}

xdotool key ctrl+F5

That should refresh the page when you run it. If it doesn't, let me know!

Bonus Round 2: Scheduling

To go the extra mile and save the planet, you can add a cronjob for disabling the monitor on a schedule.

To do this, we will need to disable DRM VC4 V3D driver. Not 100% sure what that is, but it gets in the way of turning the monitors on and off.

sudo nano /boot/config.txt

In that file, find the line dtoverlay=vc4-kms-v3dand comment it out.

This sucka

With that bad boy disabled, the following two commands can be used to turn the display on and off:

vcgencmd display_power 0

vcgencmd display_power 1

For all of you non-nerds (I assume that will be roughly none of you) 0 if off and 1 is on.

So to do this in crontab, first open the sudo crontab:

sudo crontab -e

If it asks you some nonsense about VIM and Nano, pick Nano. VIM is for the master race and I can't do it.

Then, paste this gear in there:

0 19 * * * /usr/bin/vcgencmd display_power 0

0 7 * * * /usr/bin/vcgencmd display_power 1

This is set to have it on from 7am to 7pm. You can probably figure out what to change to get the time you want, but if you need help, crontab-guru is a great resource!

Conclusion

Oh my god, that was longer than I wanted it to be. Hope that was helpful. If not, just let me know and I will likely do nothing about it. Peace.

Jan 01, 2023

Hello world!

My name is Justin and I do things.

I am on a journey, as we all are, where I am trying to become the best version of myself. Below are some of the modes of me:

  • Father

  • Partner

  • Developer

  • Game Developer

  • Tinkerer

  • Home owner

  • Writer

  • Backyard Adventurer

  • Philosopher

I will not doubt post some things here. I have no particular schedule I am aiming to adhere to and no particular format.

I hope this information is somewhat entertaining, if not useful.

← Previous Next → Page 3 of 6