Как получить доступ к выходным данным плагина в serverless.yml Serverless Framework?

Контекст: для достижения полной «инфраструктуры как кода» я хочу систематизировать процесс запроса SSL-сертификата с использованием certbot, проверки домена с использованием записей DNS TXT, загрузки сертификата в Amazon Certificate Manager (ACM) и, наконец, прикрепление сертификата ACM ARN к моему дистрибутиву Cloudfront. Все это должно быть сделано через бессерверную структуру.

Я видел 2 возможных варианта, чтобы сделать эту работу.

Вариант 1: использование асинхронных переменных файла JavaScript

т.е. в serverless.yml я бы определил такие записи, как:

custom:

  domains:
    prod: tommedema.tk

  ssl:
    prod:
      dnsTxtRoot: ${{file(scripts/request-cert.js):cert.dnsTxtRoot}}
      dnsTxtWww: ${{file(scripts/request-cert.js):cert.dnsTxtWww}}
      certArn: ${{file(scripts/request-cert.js):cert.certArn}}

Где ресурсы затем будут использовать эти переменные следующим образом:

- Type: TXT
  Name: _acme-challenge.www.${{self:custom.domains.${{self:provider.stage}}, ''}}
  TTL: '86400'
  ResourceRecords:
    - ${{self:custom.ssl.${{self:provider.stage}}.dnsTxtWww}}

Где scripts/request-cert.js будет выглядеть так:

module.exports.cert = () => {
  console.log('running async logic')

  // TODO: run certbot, get DNS records, upload to ACM

  return Promise.resolve({
    dnsTxtRoot: '"LnaKMkgqlIkXXXXXXXX-7PkKvqb_wqwVnC4q0"',
    dnsTxtWww: '"c43VS-XXXXXXXXXWVBRPCXXcA"',
    certArn: 'arn:aws:acm:us-east-1:XXXX95:certificate/XXXXXX'
  })
}

Проблема здесь в том, что невозможно отправить параметры в request-cert.js или чтобы этот скрипт знал о параметрах плагина serverless или options (поскольку это не плагин, а простой скрипт без контекста). Это означает, что сценарий не может знать об этапе, домене и т. д., для которых предназначено развертывание, и поэтому в нем отсутствуют необходимые переменные для запроса сертификата.

Так что вариант 1 кажется невозможным.

Вариант 2: создайте подключаемый модуль

Конечно, я могу создать плагин, в котором будут все необходимые переменные, потому что он может получить доступ к объектам serverless и options. Теперь проблема в том, что мне нужно будет получить доступ к выводу плагина внутри serverless.yml, и пока я не видел, как это можно сделать. т.е. Я хотел бы иметь возможность сделать что-то вроде этого:

custom:

  domains:
    prod: tommedema.tk

  ssl:
    prod:
      dnsTxtRoot: ${{myPlugin:cert.dnsTxtRoot}}
      dnsTxtWww: ${{myPlugin:cert.dnsTxtWww}}
      certArn: ${{myPlugin:cert.certArn}}

Но это не кажется возможным. Это правильно?

Если это также невозможно, как я могу достичь своей цели, чтобы программно (т. е. следуя принципам инфраструктуры как кода) развернуть свои службы с помощью пользовательских сертификатов SSL без каких-либо ручных действий? т.е.

  1. запросить сертификат у certbot
  2. получать txt-записи DNS для проверки от certbot
  3. прикрепить записи DNS txt к наборам записей route53
  4. развернуть записи DNS и проверить сертификат
  5. скачайте сертификат с certbot и загрузите его в ACM
  6. получить сертификат ARN от ACM
  7. ссылка на сертификат ARN из дистрибутива cloudfront внутри шаблона cloudformation
  8. повторное развертывание с прикрепленным сертификатом ARN

comment
Теперь я также рассмотрел третий вариант, который заключается в использовании пользовательских облачных ресурсов, поддерживаемых лямбдой. Проблема в этом случае заключается в том, что невозможно установить зависимости или прикрепить двоичные файлы к этим лямбдам.   -  person Tom    schedule 17.11.2017


Ответы (2)


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

Когда я столкнулся с этой проблемой, я создал Lambda, чтобы обновить сертификат SSL и установить его. (для этого нужен большой тайм-аут, но это нормально - его не нужно запускать часто). Ключ, который ему нужен, может быть задан как безопасные переменные среды.

Затем я использую serverless-warmup-plugin, чтобы настроить ежедневный триггер, чтобы проверить, нужно ли обновлять сертификат. Плагин также можно настроить для разогрева соответствующих лямбда-выражений при развертывании, что позволяет мне проверять истекшие или отсутствующие сертификаты SSL при каждом развертывании.

Может быть, вы могли бы сделать что-то подобное.

person Trent Bartlem    schedule 13.11.2017
comment
но как вы динамически определяете, для каких сред необходимо запрашивать и устанавливать сертификат, и как вы динамически создаете наборы записей route53 на основе проверки DNS, возвращаемой certbot? - person Tom; 14.11.2017
comment
Через переменные среды и с помощью AWS SDK. - person Trent Bartlem; 15.11.2017
comment
Ясно спасибо. Но такой подход мне кажется неправильным. Вы обогащаете ресурсы, которые являются частью стека облачного формирования за пределами облачного формирования, т. е. они обречены на рассинхронизацию. Если бы вы, например, sls remove, стек cloudformation будет удален, но ваши записи route53 и т. д. не будут. Кажется, это нарушает инфраструктуру как принципы кода. - person Tom; 15.11.2017
comment
Для дальнейшего уточнения: ресурсы, которые вы создаете (любые, включая наборы записей route53), должны не просто создаваться с помощью кода, но и деконструироваться как таковые. Если вы этого не сделаете, это будет иметь много последствий. Например, было бы сложно реплицировать и очистить экземпляр вашей архитектуры в процессах CI. Вы не сможете выполнить sls deploy, npm test, а затем sls remove, так как ваши ручные вызовы API десинхронизируют часть ваших ресурсов из вашего стека. - person Tom; 15.11.2017
comment
Я не сторонник декларативной конфигурации. Кроме того, я могу добавить хук sls remove, чтобы указать моему стеку удалить себя, если это необходимо. - person Trent Bartlem; 15.11.2017
comment
Это может быть немного не по теме, но потребуется ли замена набора записей? Я не могу представить изменение какой-либо части без полного повторного развертывания, поэтому внешнее обновление не должно нарушать развертывание/удаление sls. - person apathyman; 15.11.2017
comment
Это больше похоже на установку нового сертификата, а затем на него. - person Trent Bartlem; 16.11.2017

Использование пошаговых функций было бы лучшим выбором для этого конкретного случая использования. Поскольку у вас есть 3 отдельных шага, это будут три отдельные лямбда-функции, между которыми функции шагов могут передавать входные/выходные данные, а также время ожидания и повторные попытки.

person Robo    schedule 18.11.2017