Скрипт для автоматизации массового получения и обновления сертификатов. Базируется на библиотеке #LeGo.
- Скопировать файлы
app.acme.lego.conf
, app.acme.lego.example.com.conf
и app.acme.lego.sh
в директорию /root/apps/acme/
. - Указать бит выполнения для
*.sh
скриптов: chmod +x /root/apps/acme/*.sh
. - Скопировать файл
app_acme_lego
в директорию /etc/cron.d/
. - Настроить параметры скрипта в файлах
app.acme.lego.conf
и app.acme.lego.example.com.conf
. - При заказе первого сертификата для домена, необходимо выполнить следующую команду:
1
| ~/apps/acme/app.acme.lego.sh 'example.com' 'run'
|
Скрипт состоит из 4-х компонентов:
- Файл с основными настройками.
- Файл с настройками домена.
- Приложение.
- Задача для CRON.
Файл в общими параметрами скрипта. Этот файл подключается первым и содержит в себе общие параметры для всех доменов. Общие параметры можно переопределять файлом конфигурации домена.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
| ACME_SERVER='https://acme-staging-v02.api.letsencrypt.org/directory'
# ACME_SERVER='https://acme-v02.api.letsencrypt.org/directory'
ACME_EMAIL='mail@example.com'
ACME_PATH='/etc/ssl/acme'
ACME_METHOD='http'
ACME_HTTP_PORT=':80'
ACME_HTTP_PROXY_HEADER='Host'
ACME_TLS_PORT=':443'
ACME_KEY_TYPE='ec256'
ACME_PFX_PASSWORD='pa$$word'
ACME_PFX_FORMAT='RC2'
ACME_CRT_TIMEOUT='30'
ACME_DAYS='30'
ACME_SERVICES=(
'angie.service'
'apache2.service'
'nginx.service'
'postfix@-.service'
'prosody.service'
)
ACME_DNS_PROVIDER='cloudflare'
ACME_DNS_RESOLVERS=(
'8.8.8.8'
'8.8.4.4'
)
export CLOUDFLARE_API_EMAIL='mail@example.com'
export CLOUDFLARE_API_KEY=''
export CLOUDFLARE_DNS_API_TOKEN=''
export CLOUDFLARE_ZONE_API_TOKEN=''
|
ACME_SERVER
- сервер Let’s Encrypt для заказа сертификата. По умолчанию указан тестовый сервер. Может принимать следующие значения:https://acme-staging-v02.api.letsencrypt.org/directory
- сервер для тестирования получения проверочных сертификатов. Используется для проверки работы скрипта.https://acme-v02.api.letsencrypt.org/directory
- сервер для получения настоящих сертификатов.
ACME_EMAIL
- адрес электронной почты для регистрации и восстановления сертификата Let’s Encrypt.ACME_PATH
- путь к хранилищу сертификатов. По умолчанию: /etc/ssl/acme
.ACME_METHOD
- метод получения/обновления сертификата. По умолчанию http
. Может принимать следующие значения:dns
- получения сертификата методом DNS-01.http
- получение сертификата методом HTTP-01.tls
- получения сертификата методом TLS-ALPN-01.
ACME_HTTP_PORT
- порт, который будет слушать LeGo при получении сертификата методом HTTP-01. По умолчанию: :80
. Может принимать следующие значения:interface:port
- слушать на конкретном интерфейсе и номере порта.:port
- слушать на всех интерфейсах и конкретном порту.
ACME_HTTP_PROXY_HEADER
- проверка по указанному заголовку, если LeGo находится за обратным proxy-сервером.ACME_TLS_PORT
- порт, который будет слушать LeGo при получении сертификата методом TLS-ALPN-01. По умолчанию: :443
. Может принимать следующие значения:interface:port
- слушать на конкретном интерфейсе и номере порта.:port
- слушать на всех интерфейсах и конкретном порту.
ACME_KEY_TYPE
- тип приватного ключа сертификата. По умолчанию: ec256
. Может принимать следующие значения:rsa2048
- тип RSA-2048.rsa3072
- тип RSA-3072.rsa4096
- тип RSA-4096.rsa8192
- тип RSA-8192.ec256
- тип EC-256.ec384
- тип EC-384.
ACME_PFX_PASSWORD
- пароль для контейнера PFX.ACME_PFX_FORMAT
- формат контейнера PFX. По умолчанию: RC2
. Может принимать следующие значения:RC2
- формат RC2.DES
- формат DES.SHA256
- формат SHA256.
ACME_CRT_TIMEOUT
- время ожидания сертификата в секундах. По умолчанию: 30
секунд. Используется только при получении нового сертификата.ACME_DAYS
- количество дней, оставшихся у сертификата для его продления.ACME_SERVICES
- сервисы, которые необходимо перезапустить после обновления сертификата. Каждый из сервисов проверяется на наличие в системе.ACME_DNS_PROVIDER
- выбор DNS-провайдера для получения сертификата методом DNS-01.ACME_DNS_RESOLVERS
- Адреса DNS-резолверов. По умолчанию: 8.8.8.8
и 8.8.4.4
. Может принимать следующие значения:host:port
- название хоста и номер порта.
Файл с индивидуальной конфигурацией для каждого домена. Название файла должно строиться по шаблону app.acme.lego.[domain.com].conf
. Этот файл подключается после файла с общими параметрами и позволяет переопределять их.
1
2
3
4
5
| ACME_DOMAINS=(
'example.com'
'mail.example.com'
)
ACME_HTTP_WEBROOT='/var/www/html'
|
ACME_DOMAINS
- список доменов для получения сертификата.ACME_HTTP_WEBROOT
- WEB-директория сервера для интеграции директории идентификации .well-known/acme-challenge
. Если параметр пустой, LeGo поднимает свой внутренний сервер для получения сертификата.
Тело скрипта. Скрипт подключает файлы с основными параметрами и параметрами домена.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
| #!/usr/bin/env -S bash -eu
# -------------------------------------------------------------------------------------------------------------------- #
# ACME: CERTIFICATE
# Script for generating a certificate using Lego.
# -------------------------------------------------------------------------------------------------------------------- #
# @package Bash
# @author Kai Kimera
# @license MIT
# @version 0.1.0
# @link https://lib.onl/ru/2024/11/1f5924fa-7a3d-574e-a15d-8a41208fe10d/
# -------------------------------------------------------------------------------------------------------------------- #
(( EUID != 0 )) && { echo >&2 'This script should be run as root!'; exit 1; }
# Sources.
SRC_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd -P )"; readonly SRC_DIR # Script directory.
SRC_NAME="$( basename "$( readlink -f "${BASH_SOURCE[0]}" )" )"; readonly SRC_NAME # Script name.
SRC_DOMAIN="${1:?}"; readonly SRC_DOMAIN # Domain name.
# shellcheck source=/dev/null
. "${SRC_DIR}/${SRC_NAME%.*}.conf" # Loading main configuration file.
# shellcheck source=/dev/null
. "${SRC_DIR}/${SRC_NAME%.*}.${SRC_DOMAIN}.conf" # Loading domain configuration file.
# Environment variables.
export LEGO_PATH="${SRC_DIR:?}"
export LEGO_SERVER="${ACME_SERVER:?}"
export LEGO_PFX_PASSWORD="${ACME_PFX_PASSWORD:?}"
export LEGO_PFX_FORMAT="${ACME_PFX_FORMAT:?}"
# Parameters.
ACME_CMD="${2:?}"; readonly ACME_CMD; [[ ! "${ACME_CMD}" =~ ^(run|renew)$ ]] && exit 1
ACME_EMAIL="${ACME_EMAIL:?}"; readonly ACME_EMAIL
ACME_PATH="${ACME_PATH:?}"; readonly ACME_PATH
ACME_METHOD="${ACME_METHOD:?}"; readonly ACME_METHOD
ACME_HTTP_PORT="${ACME_HTTP_PORT:?}"; readonly ACME_HTTP_PORT
ACME_HTTP_PROXY_HEADER="${ACME_HTTP_PROXY_HEADER:?}"; readonly ACME_HTTP_PROXY_HEADER
ACME_HTTP_WEBROOT="${ACME_HTTP_WEBROOT?}"; readonly ACME_HTTP_WEBROOT
ACME_TLS_PORT="${ACME_TLS_PORT:?}"; readonly ACME_TLS_PORT
ACME_KEY_TYPE="${ACME_KEY_TYPE:?}"; readonly ACME_KEY_TYPE
ACME_CRT_TIMEOUT="${ACME_CRT_TIMEOUT:?}"; readonly ACME_CRT_TIMEOUT
ACME_DAYS="${ACME_DAYS:?}"; readonly ACME_DAYS
ACME_SERVICES=("${ACME_SERVICES[@]:?}"); readonly ACME_SERVICES
ACME_DOMAINS=("${ACME_DOMAINS[@]:?}"); readonly ACME_DOMAINS
ACME_DNS_PROVIDER="${ACME_DNS_PROVIDER:?}"; readonly ACME_DNS_PROVIDER
ACME_DNS_RESOLVERS=("${ACME_DNS_RESOLVERS[@]:?}"); readonly ACME_DNS_RESOLVERS
# -------------------------------------------------------------------------------------------------------------------- #
# INITIALIZATION
# -------------------------------------------------------------------------------------------------------------------- #
run() { lego; }
# -------------------------------------------------------------------------------------------------------------------- #
# LEGO
# Let's Encrypt client and ACME library written in Go.
# -------------------------------------------------------------------------------------------------------------------- #
lego() {
local cmd; cmd=("${ACME_CMD}")
case "${ACME_CMD}" in
'renew')
cmd+=('--days' "${ACME_DAYS}")
;;
esac
local opts
opts=(
'--accept-tos'
'--key-type' "${ACME_KEY_TYPE}"
'--email' "${ACME_EMAIL}"
'--cert.timeout' "${ACME_CRT_TIMEOUT}"
'--pem'
'--pfx'
)
case "${ACME_METHOD}" in
'dns')
opts+=('--dns' "${ACME_DNS_PROVIDER}")
for i in "${ACME_DNS_RESOLVERS[@]}"; do opts+=('--dns.resolvers' "${i}"); done
;;
'http')
opts+=('--http' '--http.port' "${ACME_HTTP_PORT}" '--http.proxy-header' "${ACME_HTTP_PROXY_HEADER}")
[[ -n "${ACME_HTTP_WEBROOT}" ]] && opts+=('--http.webroot' "${ACME_HTTP_WEBROOT}")
;;
'tls')
opts+=('--tls' '--tls.port' "${ACME_TLS_PORT}")
;;
*)
echo 'ACME_METHOD is not supported!'; exit 1
;;
esac
for i in "${ACME_DOMAINS[@]}"; do opts+=('--domains' "${i}"); done
if "${SRC_DIR}/lego" "${opts[@]}" "${cmd[@]}"; then
[[ ! -d "${ACME_PATH}" ]] && mkdir -p "${ACME_PATH}"
cp -f "${LEGO_PATH}/certificates/"{*.crt,*.key,*.pem,*.pfx} "${ACME_PATH}" \
&& chmod 644 "${ACME_PATH}/"{*.crt,*.key,*.pem,*.pfx}
for s in "${ACME_SERVICES[@]}"; do _if_service "${s}" && systemctl reload "${s}"; done
fi
}
# -------------------------------------------------------------------------------------------------------------------- #
# ------------------------------------------------< COMMON FUNCTIONS >------------------------------------------------ #
# -------------------------------------------------------------------------------------------------------------------- #
# Checking service availability.
_if_service() {
local s; s="${1}"
if systemctl list-units --full -all | grep -Fq "${s}"; then return 0; fi
return 1
}
# -------------------------------------------------------------------------------------------------------------------- #
# -------------------------------------------------< RUNNING SCRIPT >------------------------------------------------- #
# -------------------------------------------------------------------------------------------------------------------- #
run && exit 0 || exit 1
|
Файл задачи размещается в директории /etc/cron.d/
. Задача запускает скрипт каждый день для проверки состояния сертификатов.
1
2
3
4
5
| SHELL=/bin/bash
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin
HOME=/root
0 0 * * * root "${HOME}/apps/acme/app.acme.lego.sh" 'example.com' 'renew' > /dev/null 2>&1
|
example.com
- название домена для загрузки файла с его параметрами.renew
- команда LeGo для проверки и обновления сертификатов.