LetsEncrypt: NIC RU DNS API

Рассмотрим как выписать LetsEncrypt Wildcard сертификат используя DNS API RU Центра. Выписывать по сертификату на каждый поддоменный адрес конечно весело, но эффективнее использовать один сертификат на все поддомены.

Сразу хотелось бы стянуть с вас розовые трусики очки. Если вы задумали завести адрес cdn.test.mydomain.ru, то wildcard сертификат для третьего уровня не покроет остальные. Придется выписывать еще сертификат *.test.mydomain.ru. Так что, если вы такой же ленивец как я, то подумайте пару раз. Может целесообразнее завести адрес в духе cdn-test.mydomain.ru?

Склонируем проект acme.sh:

$ git clone https://github.com/Neilpang/acme.sh

В файл dns_nic.sh запишем следующий код (источник):

#!/usr/bin/env sh

# Author: Alexey Ashihmin
# Created 04/07/2018
# Tool for nic.ru API to finish dns-01 verifications.

dns_nic_add() {
  fulldomain=$1
  txtvalue=$2

  export domain=$(echo $fulldomain | cut -d '.' -f 2,3)

  get_token
  add_txt
}

dns_nic_rm() {
  fulldomain=$1
  txtvalue=$2
  
  _info "Remove TXT record"

  export domain=$(echo $fulldomain | cut -d '.' -f 2,3)

  get_token
  rm_txt
}

####################  Private functions below ##################################

get_token() {
  APIURL="https://api.nic.ru/oauth/token"
  DATA="grant_type=password&username=$username&password=$password&scope=(GET%7CPUT%7CPOST%7CDELETE)%3A%2Fdns-master%2Fservices%2F$services%2Fzones%2F$domain(%2F.%2B)%3F&="
  AUTH=$(echo -n "$APPNAME:$APPPASS" | base64 -w 0)
    
  _info "Try to get a token"
  export _H1="application/x-www-form-urlencoded"
  export _H2="Authorization: Basic $AUTH"
    
  token="$(_post "${DATA}" "${APIURL}" "" "POST")" 
  token=$(echo $token | sed -n 's|.*"access_token":"\([^"]*\)".*|\1|p')

  export NIC_Token=$token
  _info "Success get API token"
}

add_txt() {
  export _H1="Accept: application/xml"
  export _H2="Authorization: Bearer $NIC_Token"
  PUTURL="https://api.nic.ru/dns-master/services/$services/zones/$domain/records"
  COMMIT="https://api.nic.ru/dns-master/services/$services/zones/$domain/commit"
  
   _xml='<?xml version="1.0" encoding="UTF-8" ?>
	 <request>
	    <rr-list>
        	<rr>
		    <name>_acme-challenge</name>
		    <type>TXT</type>
			<txt>
			    <string>'"$txtvalue"'</string>
			</txt>
		</rr>
            </rr-list>
  </request>'

   _info "Adding record"         
   response="$(_post "${_xml}" "${PUTURL}" "" "PUT")"
   _info "Success added"
   _info "Reload DNS zones"
    reload="$(_post "" "${COMMIT}" "" "POST")"
   _info "Success reloaded"
}

rm_txt() {
  export _H2="Authorization: Bearer $NIC_Token"
 
  LISTURL="https://api.nic.ru/dns-master/services/$services/zones/$domain/records?token=$NIC_Token"
  COMMIT="https://api.nic.ru/dns-master/services/$services/zones/$domain/commit"

  txtlist="$(_post "" "${LISTURL}" "" "GET")"
  txtid=$(echo $txtlist | grep -oP '(?<=<rr id=).*?(?=</rr>)' | grep "$txtvalue"| cut -c2-9)
 
  DELETEURL="https://api.nic.ru/dns-master/services/$services/zones/$domain/records/$txtid"
  rmtxt="$(_post "" "${DELETEURL}" "" "DELETE")"
  reload="$(_post "" "${COMMIT}" "" "POST")"
 
  _info "Success removed TXT record"
}

Перемещаем «dns_nic.sh» в директорию «acme.sh/dnsapi/» и задаем права на исполнение «chmod +x acme.sh/dnsapi/dns_nic.sh». Далее запускаем установку:

$ mkdir -p /usr/local/letsencrypt/{conf,cert}
$ ./acme.sh --home /usr/local/letsencrypt --config-home /usr/local/letsencrypt/conf --cert-home /usr/local/letsencrypt/cert --install

Сертификаты рекомендуется обновлять каждый месяц.

Далее надо зарегистрировать приложение на сайте nic.ru после чего будет выдан логин и токен. Теперь идем в пункт «DNS хостинг» и смотрим поле «услуга». Перед запуском необходимо экспортировать переменные окружения с нужными данными:

$ export APPNAME=""  # логин зарегистрированного приложения
$ export APPPASS=""  # токен
$ export username="" # логин от аккаунта nic.ru
$ export password="" # пароль от аккаунта nic.ru
$ export services="" # идентификатор услуги

$ /usr/local/letsencrypt/acme.sh --issue --dns dns_nic -d '*.mydomain.ru' --dnssleep 500 --force

Сертификаты будут сохранены в домашней директории пользователя из под которого запускался acme.sh. В моем случае в /root/.acme.sh. Далее создадим симлинки:

ln -s /root/.acme.sh/\*.mydomain.ru/fullchain.cer /usr/local/letsencrypt/
ln -s /root/.acme.sh/\*.mydomain.ru/\*.mydomain.ru.key /usr/local/letsencrypt/

В конфигурации nginx указываем местоположение сертификата и ключа:

ssl_certificate /usr/local/letsencrypt/fullchain.cer;
ssl_certificate_key /usr/local/letsencrypt/*.mydomain.ru.key;

Вешаем задачу на cron:

#m      #h      #dom    #mon    #dow    #command
40      0       1       *       *       /usr/local/letsencrypt/acme.sh --issue -d '*.mydomain.ru' --dns dns_nic --dnssleep 500 --force > /var/log/acme.log 2>&1 && nginx -s reload

Если что-то не понятно, то более подробно можно почитать здесь.