Hyppää sisältöön

Kapsi, Let's Encrypt ja Ansible

Kapsin UKK:ssa oli varsin mainiot ohjeet siihen, miten Kapsilla voi ottaa käyttöön Let's Encrypt -palvelun varmentaman sertifikaatin.

Seurasin näitä ohjeita ja tein samalla Ansible playbookin ja roolin tiedostojen asentamista varten oikeille paikoilleen.

Ansible-projekti

Ansible-projektista tuli rakenteeltaan seuraavanlainen.

.
├── files
│   ├── account.key
│   ├── server.csr
│   └── server.key
├── host_vars
│   └── kapsi.fi.yml
├── production.ini
├── roles
│   └── acme
│       ├── files
│       │   └── acme_tiny.py
│       ├── tasks
│       │   └── main.yml
│       ├── templates
│       │   └── acme-tiny.sh.j2
│       └── vars
│           └── main.yml
└── site.yml

files-hakemistossa on Let's Encryptiä varten generoitu avain (account.key) ja sivustoa varten generoitu avain WWW-palvelimelle (server.key) sekä certificate-signing-request (server.csr). Ne voi generoida edellä mainitun ohjeen mukaisesti.

production.ini on inventaario-tiedosto, jossa määritellään koneet, joille asennuksia tehdään. Tässä määritellään kapsi.fi acme_hosts-ryhmään.

[acme_hosts]
kapsi.fi

host_vars-hakemistossa kapsi.fi.yml-tiedostossa määritellään kapsi.fi-koneelle liittyviä asetuksia, kuten tässä tapauksessa hakemisto, jonne sertifikaatin hakuun liittyvät tiedostot sijoitetaan, ja sivuston domain muuttujaan site_name. Esimerkiksi näin:

---
acme_dir: "~/acme"
site_name: "tsk.iki.fi"

site.yml on playbook, jossa määritellään, että acme_hosts-ryhmään kuuluvilla koneilla on acme-rooli.

---
- name: Setup acme-tiny for retrieving Let's Encrypt certificate.
  hosts: acme_hosts
  roles:
    - acme

Acme-rooli

Acme-roolissa on files-hakemistossa acme_tiny.py, joka on suoraan kopio acme-tiny-GIT-repositorysta.

templates-hakemistossa on shell-skripti, joka ajaa acme_tinyn sopivilla parametreilla.

#!/bin/bash
{{ acme_bin_dir }}/acme_tiny.py --account-key {{ acme_etc_dir }}/account.key --csr {{ acme_etc_dir }}/server.csr --acme-dir {{ site_www_dir }}/.well-known/acme-challenge/ > {{ site_ssl_dir }}/server.crt
wget https://letsencrypt.org/certs/lets-encrypt-x3-cross-signed.pem -O {{ site_ssl_dir }}/ca.crt

Roolin käyttämät muuttujat on määritelty vars-hakemiston main.yml-tiedostossa.

---
acme_dir: "~/acme"
acme_etc_dir: "{{ acme_dir }}/etc"
acme_bin_dir: "{{ acme_dir }}/bin"
acme_log_dir: "{{ acme_dir }}/log"
site_dir: "~/sites/{{ site_name }}"
site_ssl_dir: "{{ site_dir }}/.ssl"
site_www_dir: "{{ site_dir }}/www"

Lopuksi tasks-hakemiston main.yml-tiedostossa on varsinaiset toimenpiteet tiedostojen asentamiseksi paikoilleen.

---
- name: Assert that variables are defined.
  assert:
    that:
      - acme_dir is defined
      - site_name is defined
      - site_dir is defined

- name: Make acme directories.
  file: path={{ item }} state=directory mode=u+rwx,g-rwx,o-rwx
  with_items:
    - "{{ acme_dir }}"
    - "{{ acme_etc_dir }}"
    - "{{ acme_bin_dir }}"
    - "{{ acme_log_dir }}"

- name: Copy acme binaries.
  copy: src={{ item }} dest={{ acme_bin_dir }}/ mode=u+x
  with_items:
    - acme_tiny.py

- name: Template acme binaries.
  template: src=acme-tiny.sh.j2 dest={{ acme_bin_dir }}/acme-tiny-{{ site_name }}.sh mode=u+x

- name: Copy acme data.
  copy: src={{ item }} dest={{ acme_etc_dir }}/
  with_items:
    - account.key
    - server.csr

- name: Make ssl directory.
  file: path={{ site_ssl_dir }} state=directory mode=700

- name: Copy server.key.
  copy: src=server.key dest={{ site_ssl_dir }}/

- name: Make well-known directory.
  file:
    path: "{{ site_www_dir }}/.well-known"
    state: directory
    mode: u+rwx,g+rx,o+rx

- name: Make well-known/acme-challenge directory.
  file:
    path: "{{ site_www_dir }}/.well-known/acme-challenge"
    state: directory
    mode: u+rwx,g+rx,o+rx

- name: Setup cron job to update certificate.
  cron:
    name: "Update {{ site_name }} certificate"
    hour: 0
    minute: 0
    day: 1
    state: present
    job: "{{ acme_bin_dir }}/acme-tiny-{{ site_name }}.sh 2> {{ acme_log_dir }}/{{ site_name }}.log"

Playbookin ajo

Asennuksen saa tehtyä komennolla

ansible-playbook -i production.ini site.yml

Jonka jälkeen ansible tulostaa jotain seuraavankaltaista:

PLAY [Setup acme-tiny for retrieving Let's Encrypt certificate.] ***************

TASK [setup] *******************************************************************
ok: [kapsi.fi]

TASK [acme : Assert that variables are defined.] *******************************
ok: [kapsi.fi]

TASK [acme : Make acme directories.] *******************************************
ok: [kapsi.fi] => (item=~/acme)
ok: [kapsi.fi] => (item=~/acme/etc)
ok: [kapsi.fi] => (item=~/acme/bin)
ok: [kapsi.fi] => (item=~/acme/log)

...

Jos kaikki menee hyvin pitäisi viimeisellä rivillä lukea "failed=0".

Sertifikaattia ei vielä ole haettu, vaan tämän jälkeen pitää kerran ajaa ~/acme/bin-hakemistoon generoitu acme-tiny-tsk.iki.fi.sh-skripti. Sertifikaatin uusimista varten ajamisen tekee cron.

Huomioita

Tämä toimi minun tilanteessa. Käytössä on vain yksi sivusto. En ole pohtinut vielä, mitä kohtia pitää muuttaa, jos sivustoja olisi useita.

Roolin on tarkoitus olla uudelleenkäytettävä, eli sen pitäisi toimia sellaisenaan muillekin sivustoille, jos vain asible-projektissa määrittelee tarvittavat muuttujat oikein.

Certificate-signing-requestin (server.csr) voisi varmaankin generoida automaattisesti jonkinlaista templatea käyttäen. Sitä en kuitenkaan lähtenyt tähän vielä tuunaamaa, koska request on samanlainen joka kerralle, jos sivustoihin ei tule muutoksia.

Laittakaa kommenttia parannusehdotuksista tai vioista, niin katsotaan, saisiko tätä skriptiä täydennettyä.

Kommentit