LetsEncrypt: NIC RU DNS API

Выписывать по сертификату на каждый поддоменный адрес конечно весело, но эффективнее использовать один сертификат на все поддомены.

Сразу хотелось бы стянуть с вас розовые очки. Если вы задумали завести адрес 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

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

UPD 06.01.20

Вторая часть статьи вот тут.