initial commit
Some checks failed
ci/woodpecker/push/lint Pipeline was successful
ci/woodpecker/push/test Pipeline failed
ci/woodpecker/push/docs unknown status
ci/woodpecker/push/notify Pipeline was successful

This commit is contained in:
Robert Kaussow 2024-09-27 20:40:51 +02:00
commit 2fd883d291
Signed by: xoxys
GPG Key ID: 4E692A2EAECC03C0
22 changed files with 445 additions and 0 deletions

11
.gitignore vendored Normal file
View File

@ -0,0 +1,11 @@
# ---> Ansible
*.retry
plugins
library
# ---> Python
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class

7
.markdownlint.yml Normal file
View File

@ -0,0 +1,7 @@
---
default: True
MD013: False
MD041: False
MD024: False
MD004:
style: dash

1
.prettierignore Normal file
View File

@ -0,0 +1 @@
LICENSE

47
.woodpecker/docs.yaml Normal file
View File

@ -0,0 +1,47 @@
---
when:
- event: [pull_request]
- event: [push, manual]
branch:
- ${CI_REPO_DEFAULT_BRANCH}
steps:
- name: generate
image: quay.io/thegeeklab/ansible-doctor
environment:
ANSIBLE_DOCTOR_EXCLUDE_FILES: "['molecule/']"
ANSIBLE_DOCTOR_RENDERER__FORCE_OVERWRITE: "true"
ANSIBLE_DOCTOR_LOGGING__LEVEL: info
ANSIBLE_DOCTOR_ROLE__NAME: ${CI_REPO_NAME}
ANSIBLE_DOCTOR_TEMPLATE__NAME: readme
- name: format
image: quay.io/thegeeklab/alpine-tools
commands:
- prettier -w README.md
- name: diff
image: quay.io/thegeeklab/alpine-tools
commands:
- git diff --color=always README.md
- name: publish
image: quay.io/thegeeklab/wp-git-action
settings:
action:
- commit
- push
author_email: ci-bot@rknet.org
author_name: ci-bot
branch: main
message: "[skip ci] automated docs update"
netrc_machine: gitea.rknet.org
netrc_password:
from_secret: gitea_token
when:
- event: [push, manual]
branch:
- ${CI_REPO_DEFAULT_BRANCH}
depends_on:
- test

30
.woodpecker/lint.yaml Normal file
View File

@ -0,0 +1,30 @@
---
when:
- event: [pull_request, tag]
- event: [push, manual]
branch:
- ${CI_REPO_DEFAULT_BRANCH}
steps:
- name: ansible-lint
image: quay.io/thegeeklab/ansible-dev-tools:1
commands:
- ansible-lint
environment:
FORCE_COLOR: "1"
- name: python-format
image: docker.io/python:3.12
commands:
- pip install -qq ruff
- ruff format --check --diff .
environment:
PY_COLORS: "1"
- name: python-lint
image: docker.io/python:3.12
commands:
- pip install -qq ruff
- ruff check .
environment:
PY_COLORS: "1"

26
.woodpecker/notify.yml Normal file
View File

@ -0,0 +1,26 @@
---
when:
- event: [tag]
- event: [push, manual]
branch:
- ${CI_REPO_DEFAULT_BRANCH}
runs_on: [success, failure]
steps:
- name: matrix
image: quay.io/thegeeklab/wp-matrix
settings:
homeserver:
from_secret: matrix_homeserver
room_id:
from_secret: matrix_room_id
user_id:
from_secret: matrix_user_id
access_token:
from_secret: matrix_access_token
when:
- status: [failure]
depends_on:
- docs

24
.woodpecker/test.yaml Normal file
View File

@ -0,0 +1,24 @@
---
when:
- event: [pull_request, tag]
- event: [push, manual]
branch:
- ${CI_REPO_DEFAULT_BRANCH}
variables:
- &molecule_base
image: quay.io/thegeeklab/ansible-dev-tools:1
group: molecule
environment:
PY_COLORS: "1"
HCLOUD_TOKEN:
from_secret: molecule_hcloud_token
steps:
- name: molecule-default
<<: *molecule_base
commands:
- molecule test -s default
depends_on:
- lint

20
.yamllint Normal file
View File

@ -0,0 +1,20 @@
---
extends: default
rules:
truthy:
allowed-values: ["True", "False"]
comments:
min-spaces-from-content: 1
comments-indentation: False
line-length: disable
braces:
min-spaces-inside: 0
max-spaces-inside: 1
brackets:
min-spaces-inside: 0
max-spaces-inside: 0
indentation: enable
octal-values:
forbid-implicit-octal: True
forbid-explicit-octal: True

21
LICENSE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2022 Robert Kaussow <mail@thegeeklab.de>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:
The above copyright notice and this permission notice (including the next
paragraph) shall be included in all copies or substantial portions of the
Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

1
README.md Normal file
View File

@ -0,0 +1 @@
# xoxys.lego

25
defaults/main.yml Normal file
View File

@ -0,0 +1,25 @@
---
lego_version: 4.18.0
lego_server: https://acme-v02.api.letsencrypt.org/directory
lego_cloudflare_email: ""
lego_cloudflare_api_key: ""
# @var lego_accounts:example: >
# lego_accounts:
# - account_email: user@example.com
# account_number: "862bf8e9-b02a-43f1-9c05-ea073e0e1c7c"
# account_key: "94ecba99-bfbd-4c5a-9fd4-790f1c061a4c"
# @end
lego_accounts: []
# @var lego_certificates:example:
# lego_certificates:
# - account_email: user@example.com
# domains:
# - example.com
# - www.example.com
# skip_create: False
# @end
lego_certificates: []

24
meta/main.yml Normal file
View File

@ -0,0 +1,24 @@
---
galaxy_info:
# @meta author:value: [Robert Kaussow](https://gitea.rknet.org/xoxys)
author: Robert Kaussow <mail@thegeeklab.de>
namespace: xoxys
role_name: lego
# @meta description: >
# [![Build Status](https://ci.rknet.org/api/badges/ansible/xoxys.lego/status.svg)](https://ci.rknet.org/repos/ansible/xoxys.lego)
# [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg?label=license)](https://gitea.rknet.org/ansible/xoxys.lego/src/branch/main/LICENSE)
#
# Deploy Lego ACME client to manage Lets Encrypt certificates.
# @end
description: Deploy Lego ACME client to manage Lets Encrypt certificates
license: MIT
min_ansible_version: "2.10"
platforms:
- name: EL
versions:
- "9"
galaxy_tags:
- ca
- lego
- corporate
dependencies: []

View File

@ -0,0 +1,22 @@
---
- name: Converge
hosts: all
vars:
lego_server: https://acme-staging-v02.api.letsencrypt.org/directory
lego_accounts:
- account_email: user@example.com
account_number: "862bf8e9-b02a-43f1-9c05-ea073e0e1c7c"
account_key: "94ecba99-bfbd-4c5a-9fd4-790f1c061a4c"
lego_certificates:
- account_email: user@example.com
domains:
- example.com
- www.example.com
skip_create: True
pre_tasks:
- name: Install requirements
ansible.builtin.package:
name: tar
state: present
roles:
- role: xoxys.lego

View File

@ -0,0 +1,17 @@
---
driver:
name: molecule_hetznercloud
dependency:
name: galaxy
options:
role-file: requirements.yml
requirements-file: requirements.yml
platforms:
- name: "rocky9-lego"
server_type: "cx22"
image: "rocky-9"
provisioner:
name: ansible
log: False
verifier:
name: testinfra

View File

@ -0,0 +1,11 @@
---
- name: Prepare
hosts: all
gather_facts: False
tasks:
- name: Bootstrap Python for Ansible
ansible.builtin.raw: |
command -v python3 python ||
((test -e /usr/bin/apt && (apt -y update && apt install -y python-minimal)) ||
echo "Warning: Python not boostrapped due to unknown platform.")
changed_when: False

View File

@ -0,0 +1,13 @@
import os
import testinfra.utils.ansible_runner
testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner(
os.environ["MOLECULE_INVENTORY_FILE"]
).get_hosts("all")
def test_lego_bin(host):
cmd = host.run("lego --version")
assert cmd.succeeded

17
pyproject.toml Normal file
View File

@ -0,0 +1,17 @@
[tool.ruff]
exclude = [".git", "__pycache__"]
line-length = 99
indent-width = 4
[tool.ruff.lint]
ignore = ["W191", "E111", "E114", "E117", "S101", "S105"]
select = ["F", "E", "I", "W", "S"]
[tool.ruff.format]
quote-style = "double"
indent-style = "space"
line-ending = "lf"
[tool.pytest.ini_options]
filterwarnings = ["ignore::FutureWarning", "ignore::DeprecationWarning"]

4
requirements.yml Normal file
View File

@ -0,0 +1,4 @@
---
collections: []
roles: []

91
tasks/main.yml Normal file
View File

@ -0,0 +1,91 @@
---
- name: Install lego
ansible.legacy.unarchive:
src: https://github.com/go-acme/lego/releases/download/v{{ lego_version }}/lego_v{{ lego_version }}_linux_amd64.tar.gz
dest: "{{ __lego_bin_dir }}"
remote_src: True
extra_opts:
- "{{ __lego_bin_name }}"
mode: "0750"
- name: Create lego base dir
ansible.builtin.file:
path: "{{ __lego_base_dir }}/bin"
state: directory
owner: root
group: root
mode: "0750"
recurse: True
- name: Create LetsEncrypt certificates directory
ansible.builtin.file:
path: "{{ __lego_base_dir }}/.lego/certificates"
state: directory
owner: root
group: root
mode: "0700"
recurse: True
- name: Create LetsEncrypt account directory
ansible.builtin.file:
path: "{{ __lego_base_dir }}/.lego/accounts/acme-v02.api.letsencrypt.org/{{ item.account_email }}/keys"
state: directory
owner: root
group: root
mode: "0700"
recurse: True
loop: "{{ lego_accounts }}"
loop_control:
label: "{{ item.account_email }}"
- name: Deploy account json
ansible.builtin.template:
dest: "{{ __lego_base_dir }}/.lego/accounts/acme-v02.api.letsencrypt.org/{{ item.account_email | mandatory }}/account.json"
group: root
owner: root
mode: "0600"
src: account.json.j2
loop: "{{ lego_accounts }}"
loop_control:
label: "{{ item.account_email }}"
- name: Deploy account key
ansible.builtin.copy:
content: "{{ item.account_key }}"
dest: "{{ __lego_base_dir }}/.lego/accounts/acme-v02.api.letsencrypt.org/{{ item.account_email | mandatory }}/keys/{{ item.account_email }}.key"
owner: root
group: root
mode: "0600"
diff: False
loop: "{{ lego_accounts }}"
loop_control:
label: "{{ item.account_email }}"
- name: Obtain certificates for domains
ansible.builtin.command: '{{ __lego_bin_file }} --email="{{ item.account_email }}" --domains {{ " --domains ".join(item.domains) }} --dns="cloudflare" run'
args:
creates: "{{ __lego_base_dir }}/.lego/certificates/{{ item.domains[0] }}.crt"
environment:
LEGO_SERVER: "{{ lego_server }}"
LEGO_PATH: "{{ __lego_base_dir }}/.lego"
CLOUDFLARE_EMAIL: "{{ lego_cloudflare_email }}"
CLOUDFLARE_API_KEY: "{{ lego_cloudflare_api_key }}"
when: not item.skip_create | bool
loop: "{{ lego_certificates }}"
loop_control:
label: "{{ item.account_email }}"
- name: Add cron scipt to renew certificates
ansible.builtin.template:
dest: "{{ __lego_base_dir }}/bin/cron_lego_renew.sh"
mode: "0755"
src: cron_lego_renew.sh.j2
- name: Add cron job to renew certificates
ansible.builtin.cron:
name: "lego-renew"
cron_file: "lego-renew"
job: "{{ __lego_base_dir }}/bin/cron_lego_renew.sh >> {{ __lego_base_dir }}/cron_lego_renew.log 2>&1"
hour: 2
minute: 5
user: root

12
templates/account.json.j2 Normal file
View File

@ -0,0 +1,12 @@
{
"email": "{{ item.account_email }}",
"registration": {
"body": {
"status": "valid",
"contact": [
"mailto:{{ item.account_email }}"
]
},
"uri": "https://acme-v02.api.letsencrypt.org/acme/acct/{{ item.account_number }}"
}
}

View File

@ -0,0 +1,15 @@
#!/bin/env bash
# run this script daily to renew any letsencrypt certs that need renewing
# renew cert if it expires within 30 days
export LEGO_SERVER="{{ lego_server }}"
export LEGO_PATH="{{ __lego_base_dir }}/.lego"
export CLOUDFLARE_EMAIL="{{ lego_cloudflare_email }}"
export CLOUDFLARE_API_KEY="{{ lego_cloudflare_api_key }}"
{% for cert in lego_certificates %}
echo "$(date) checking for cert update for {{ ', '.join(cert.domains) }}."
{{ __lego_bin_file }} --email="{{ cert.account_email }}" --domains {{ ' --domains '.join(cert.domains) }} --dns="cloudflare" renew --days 30
{% endfor %}

6
vars/main.yml Normal file
View File

@ -0,0 +1,6 @@
---
__lego_base_dir: /etc/lego
__lego_bin_dir: /usr/bin
__lego_bin_name: lego
__lego_bin_file: "{{ __lego_bin_dir }}/{{ __lego_bin_name }}"