Главная
Главная Руководства WWW › Получаем SSL сертификаты от Let's Encrypt при помощи acme.sh.



Автор:

Статья опубликована: 2019-02-13 11:58:29
Последние правки: 2023-09-21 17:33:04

Let's Encrypt - центр сертификации от некоммерческой организации ISRG, существующий при поддержке EFF и многих компаний, взявшей на себя миссию дать людям бесплатные SSL/TLS сертификаты для сайтов и серверов. Хотя сам LE в качестве клиента рекомендует использовать certbot, могут возникнуть сложные случаи, где certbot, увы, бесполезен. В этом случае нам поможет acme.sh.


  • Исходные данные
    $ uname -sr
    FreeBSD 11.2-RELEASE-p5
    $ pkg info | grep acme.sh
    acme.sh-2.7.9_1                ACME protocol client written in shell
    
    Тип получаемого сертификата: wildcard. Это означает, что проверка владения доменом определяется только через DNS;
    DNS-сервера: размещены у регистратора r01.ru. Изменения зон на DNS-серверах этого регистратора производятся в течение 40 минут.


  • acme.sh - установка
    # cd /usr/ports/security/acme.sh
    # make install clean
    
    Настройки в процессе установки:
    [ ] BINDTOOLS   Depend on bind-tools for nsupdate
    [x] DOCS        Build and/or install documentation
    [ ] STANDALONE  Standalone mode requires SOCAT
    ─────────────────────────────────── HTTP ─────────────────────────────────
    (*) CURL        Depend on cURL for HTTP(S) queries
    ( ) WGET        Depend on Wget for HTTP(S) queries
    


  • Выпуск сертификата вручную
    Для тех, кто забыл, напоминаю: зона расположена у регистратора r01.ru, у которого данные на dns серверах обновляются в течение 40 минут.
    # acme.sh --issue -d mysite.com -d *.mysite.com --dns dns_show --dnssleep 2500 --force
    
    --dnssleep 2500 - задержка в ~41 минуту
    --force - добавлять, если выполняете строку из-под root или при помощи sudo
    В процессе нас попросят добавить в зону mysite.com две текстовые записи _acme-challenge. Добавляем, как и просят, ДВЕ записи, с разным содержимым.
    Ждем 41 минуту.
    Затем:
    # acme.sh --renew -d mysite.com -d *.mysite.com
    
    Мы делали операции от root, поэтому сертификаты ищем в /root/.acme.sh


  • DNS API hook для r01.ru
    У меня все же дошли руки написать hook для автоматического добавления записей на dns сервера регистратора r01.ru через их SOAP API. acme.sh предполагает использование интерпретатора sh, я с SOAP умею работать только через perl, поэтому получилось то, что получилось. Я знаю, что нет предела совершенству, в том числе и в вылизывании кода, но мой вполне себе работает (а больше от него ничего не надо).
    Итак.

    Файл /root/.acme.sh/dns_r01.sh:
    #!/usr/bin/env sh
    
    ########################################################################
    # r01.ru API hook script for acme.sh
    #
    # Author: Skull
    # Site: http://www.site-motor.ru/
    
    #-- dns_r01_add() - Add TXT record --------------------------------------
    # Usage: dns_r01_add _acme-challenge.subdomain.domain.com "XyZ123..."
    
    dns_r01_add() {
      _full_domain=$1
      _txt_value=$2
      _info "Using r01.ru DNS API hook"
      /root/.acme.sh/dnsapi/r01-add.pl $1 $2
    }
    
    #-- dns_r01_rm() - Remove TXT record ------------------------------------
    # Usage: dns_r01_rm _acme-challenge.subdomain.domain.com "XyZ123..."
    
    dns_r01_rm() {
      _full_domain=$1
      _txt_value=$2
      _info "Cleaning up after use r01.ru DNS API hook"
      /root/.acme.sh/dnsapi/r01-rm.pl $1
    }
    
    Как видите, я из sh-скрипта тупо передаю все в perl-скрипт.

    Файл /root/.acme.sh/dnsapi/r01-add.pl:
    #!/usr/bin/env perl
    use strict;
    use warnings;
    use SOAP::Lite;
    use HTTP::Cookies;
    $|=1;
    
    my $user = '';
    my $pass = '';
    
    my $domain = $ARGV[0];
    $domain =~ s/^_acme-challenge\.//;
    my $record = $ARGV[1];
    
    
    print "DOMAIN:\t$domain\n";
    print "DNS:\t_acme-challenge=$record\n";
    
    
    my $soap = SOAP::Lite->new(
        user_agent => 'RegbaseSoapInterfaceClient',
        default_ns => 'urn:RegbaseSoapInterface'
    );
    $soap->proxy(
        'https://partner.r01.ru:1443/partner_api.khtml',
        cookie_jar => ( HTTP::Cookies->new(
            autosave => 1,
            ignore_discard => 1
        ))
    );
    
    
    # логин
    my $login = $soap->logIn($user,$pass);
    die 'logIn() ERROR ==> '.$login->faultstring if ($login->fault);
    print 'logIn() ',$login->result->{status}{code},': ',$login->result->{status}{name},"\n";
    $soap->set_cookie('SOAPClient',$login->result->{status}{message});
    
    
    # добавляем запись _acme-challenge
    my $params =
        SOAP::Data->value(
            SOAP::Data->name('domain' => uc($domain)),
            SOAP::Data->name('type_record' => 'TXT'),
            SOAP::Data->name('params' => \SOAP::Data->value(
                SOAP::Data->name('owner' => '_acme-challenge'),
                SOAP::Data->name('data' => $record),
            ))
    );
    my $ret = $soap->addNewRrRecord($params);
    die 'addNewRrRecord() ERROR ==> '.$ret->faultstring if ($ret->fault);
    print 'addNewRrRecord() ',$ret->result->{status}{code},': ',$ret->result->{status}{name},"\n";
    
    exit;
    
    $user, $pass - здесь надо задать логин и пароль для API r01.ru

    Файл /root/.acme.sh/dnsapi/r01-rm.pl:
    #!/usr/bin/env perl
    use strict;
    use warnings;
    use SOAP::Lite;
    use HTTP::Cookies;
    $|=1;
    
    my $user = '';
    my $pass = '';
    
    my $domain = $ARGV[0];
    $domain =~ s/^_acme-challenge\.//;
    print "DOMAIN:\t$domain\n";
    
    
    my $soap = SOAP::Lite->new(
        user_agent => 'RegbaseSoapInterfaceClient',
        default_ns => 'urn:RegbaseSoapInterface'
    );
    $soap->proxy(
        'https://partner.r01.ru:1443/partner_api.khtml',
        cookie_jar => ( HTTP::Cookies->new(
            autosave => 1,
            ignore_discard => 1
        ))
    );
    
    
    # логин в систему
    my $login = $soap->logIn($user,$pass);
    die 'logIn() ERROR ==> '.$login->faultstring if ($login->fault);
    print 'logIn() ',$login->result->{status}{code},': ',$login->result->{status}{name},"\n";
    $soap->set_cookie('SOAPClient',$login->result->{status}{message});
    
    
    # читаем записи домена
    my $records = $soap->getRrRecords(uc($domain));
    die 'getRrRecords() ERROR ==> '.$records->faultstring if ($records->fault);
    print 'getRrRecords() ',$records->result->{status}{code},': ',$records->result->{status}{name},"\n";
    
    for my $el (@{$records->result->{data}}){
        if($el->{owner} eq '_acme-challenge'){
            # удаляем запись
            my $ret = $soap->deleteRrRecord($el->{id});
            die 'deleteRrRecord() '.$el->{id}.' ERROR ==> '.$ret->faultstring if ($ret->fault);
            print 'deleteRrRecord() ',$ret->result->{status}{code},': ',$ret->result->{status}{name},"\n";
        }
    }
    
    # выход из системы
    my $logout = $soap->call('logOut');
    
    exit;
    
    $user, $pass - здесь надо задать логин и пароль для API r01.ru

    Не забываем все файлы сделать исполняемыми (chmod +x)


  • Выпуск сертификата в автоматическом режиме
    # acme.sh --issue -d mysite.com -d *.mysite.com --dns dns_r01 --dnssleep 2500
    
    В результате записи _acme-challenge будут автоматически добавлены в dns, но в процессе придется сидеть и ждать 2500 секунд. После чего записи в dns также автоматически будут удалены и вы станете счастливым обладателем wildcard-сертификата.


  • Автоматический перевыпуск сертификатов
    Для перезапуска после обновления, например, nginx, необходимо сделать следующее:
    - открываем файл /root/.acme.sh/mysite.com/mysite.com.conf;
    - ищем там строчку Le_RenewHook='' и меняем её (или добавляем новую):
    Le_RenewHook='/usr/local/etc/rc.d/nginx restart'
    
    Для root в cron добавляем:
    34 1 * * * /usr/local/sbin/acme.sh --cron > /dev/null
    
    34 - минута запуска, поменяйте на другое число от 0 до 59
    1 - час запуска, поменяйте на другое число от 0 до 23


  • nginx - пути к сертификатам для добавления в блок server
    ssl_certificate /root/.acme.sh/mysite.com/fullchain.cer;
    ssl_certificate_key /root/.acme.sh/mysite.com/mysite.com.key;
    


  • Посмотреть данные сертификата
    # openssl x509 -in cert.pem  -noout -text
    


  • Ошибки (куда ж без них)

    Create new order error. Le_OrderFinalize not found. {"type":"urn:ietf:params:acme:error:unauthorized","status":401,"detail":"A requested identifier is not permitted [site-motor.ru]"}
    Для нового домена попросили сделать новую учетную запись, после чего при попытке получить сертификат выскочила ошибка. Лечим сменой сервера сертификации:
    # acme.sh --debug --log --set-default-ca --server letsencrypt
    




Связанные странички:
Asterisk 18. FreeBSD. Модем Huawei E1550.