Ubuntu18.04でAnsibleを使用してLet'sEncrypt証明書を取得する方法

提供:Dev Guides
移動先:案内検索

著者は、 Electronic Frontier Foundation を選択して、 Write forDOnationsプログラムの一環として寄付を受け取りました。

序章

最新のインフラストラクチャ管理は、自動化されたプロセスとツールを使用して行うのが最適です。 標準のCertbotクライアントを使用してLet'sEncrypt 証明書を取得するのは迅速かつ簡単ですが、通常、サーバーを試運転するときに手動で実行する必要があるタスクです。 これは個々のサーバーのセットアップで管理できますが、より大きなフリートを展開する場合は面倒になる可能性があります。

Ansible などの構成管理ツールを使用して証明書を取得すると、このタスクが完全に自動化され、再現可能になります。 サーバーを再構築または更新する必要がある場合は、手動で手順を再度実行する必要はなく、 AnsiblePlaybookを実行するだけで済みます。

このチュートリアルでは、Ansibleハンドブックを作成して、AnsibleホストマシンのLet'sEncrypt証明書を自動的に取得します。

前提条件

このチュートリアルを完了するには、次のものが必要です。

  • Ubuntu 18.04を使用した初期サーバーセットアップに従ってセットアップされた2つのUbuntu18.04サーバー(sudo非rootユーザーを含む)。

最初のサーバーはAnsibleサーバーとして使用されます。このチュートリアルでは、このサーバーを Ansibleserverと呼びます。 これは、Ansibleが実行されてコマンドをホストマシンに送信する場所です。 または、ローカルマシンまたはAnsibleインベントリがAnsibleサーバーとして構成されている他のマシンを使用することもできます。

Ansibleサーバーでは、次のものが必要です。

2番目のサーバーはAnsibleホストとして使用されます。このチュートリアルでは、このサーバーをホストマシンと呼びます。 これは、構成して証明書を発行するマシンです。 このマシンは、証明書発行検証ファイルを提供するためにWebサーバーも実行します。

ホストマシンでは、次のものが必要です。

  • TLS証明書を取得する資格があるドメイン名。必要なDNSレコードは、Ansibleホストマシンを指すように構成されています。 この特定の例では、プレイブックはyour-domainおよびwww.your-domainに有効な証明書を取得しますが、必要に応じて他のドメインまたはサブドメインに合わせて調整できます。
  • ポート80(HTTP)を介してインターネットからアクセスできるWebサーバー。たとえば、 Ubuntu 18.04にApacheWebサーバーをインストールする方法の手順1、2、および3に従います。 これは、Nginxサーバー、またはその他の適切なWebサーバーソフトウェアの場合もあります。

これらの準備ができたら、root以外のユーザーとして Ansibleサーバーにログインし、開始します。

ステップ1— Let'sEncryptAnsibleモジュールの設定を構成する

Ansibleにはletsencryptという名前の組み込みモジュールがあり、ACME(自動証明書管理環境)プロトコルを使用して有効なTLS証明書を取得できます。

この最初のステップでは、ホスト変数構成ファイルを追加して、モジュールを使用するために必要な構成変数を定義します。

注: letsencryptモジュールは、Ansible2.6以降acme_certificateに名前が変更されました。 letsencryptの名前はacme_certificateのエイリアスになりましたので、引き続き機能しますが、プレイブックの将来性を確保するために、代わりにacme_certificateを使用することをお勧めします。 ansible --versionを使用してAnsibleのバージョンを確認できます。 このチュートリアルの執筆時点では、Ubuntu18.04Aptリポジトリはまだacme_certificateをサポートしていません。


まず、Ansibleサーバーhost_varsAnsibleディレクトリを作成します。

sudo mkdir /etc/ansible/host_vars

次に、/etc/ansible/host_varsディレクトリに、 Ansiblehostマシンの名前で新しいファイルを作成します。 この例では、ホストの名前としてhost1を使用します。

sudo nano /etc/ansible/host_vars/host1

次の設定例には、検証方法とサーバーアドレス、証明書の有効期限のリマインダーを受信するための電子メールアドレス、Let'sEncryptのキーと証明書が保存されるディレクトリなどの開始に必要なすべてのものが含まれています。

サンプル構成をファイルにコピーします。

/ etc / ansible / host_vars / host1

---
acme_challenge_type: http-01
acme_directory: https://acme-v02.api.letsencrypt.org/directory
acme_version: 2
acme_email: certificate-reminders@your-domain
letsencrypt_dir: /etc/letsencrypt
letsencrypt_keys_dir: /etc/letsencrypt/keys
letsencrypt_csrs_dir: /etc/letsencrypt/csrs
letsencrypt_certs_dir: /etc/letsencrypt/certs
letsencrypt_account_key: /etc/letsencrypt/account/account.key
domain_name: your-domain

終了したら、ファイルを保存して閉じます。

必要に応じて、ドメイン名とメールアドレスを調整します。 任意のメールアドレスを使用できます。your-domainのメールアドレスである必要はありません。

定義されたディレクトリ/ファイルパスの一部は、実際にはまだサーバーに存在しない可能性があります。 これで結構です; プレイブックの最初の部分は、これらのディレクトリを作成し、関連する権限を割り当てることです。

必要な構成変数をAnsibleインベントリファイルに追加しました。 次に、証明書を取得するためのプレイブックの作成を開始します。

ステップ2—Let'sEncryptのディレクトリとアカウントキーを作成する

このステップでは、必要なLet's Encryptディレクトリを作成し、適切なアクセス許可を割り当て、Let'sEncryptアカウントキーを生成するために使用するAnsibleタスクを記述します。

まず、 Ansibleサーバーの選択した新しいディレクトリ(/home/user/ansible-playbooksなど)にletsencrypt-issue.ymlという名前の新しいプレイブックを作成します。

cd ~
mkdir ansible-playbooks
cd ansible-playbooks
nano letsencrypt-issue.yml

Ansibleタスクの作成を開始する前に、ホストと関連する設定を指定する必要があります。 前提条件チュートリアルでホストをどのように参照したかに応じて、以下を調整します。 次に、ファイルの先頭に以下を追加します。

letsencrypt-issue.yml

---
- hosts: "host1"
  tasks:

これで、必要なタスクの作成を開始できます。最初のタスクは、Let'sEncryptファイルの保存に必要なファイルシステムディレクトリを作成することです。 前のコンテンツの後に、次のAnsibleタスクをファイルに追加します。

letsencrypt-issue.yml

...
  - name: "Create required directories in /etc/letsencrypt"
    file:
      path: "/etc/letsencrypt/{{ item }}"
      state: directory
      owner: root
      group: root
      mode: u=rwx,g=x,o=x
    with_items:
    - account
    - certs
    - csrs
    - keys

このAnsibleタスクは、/etc/letsencryptaccountcertscsrs、およびkeysディレクトリを作成します。取得した証明書が保存されます。

ディレクトリの所有者をrootに設定し、アクセス許可u=rwx,g=x,o=xを適用して、rootのみがディレクトリへの読み取りおよび書き込みアクセス権を持つようにします。 ディレクトリには秘密鍵、証明書署名要求(CSR)、および署名付き証明書が含まれるため、これをお勧めします。これらは機密情報として保持する必要があります。

次に、Let'sEncryptアカウントキーを作成する必要があります。 これを使用して、Let'sEncryptサービスに対して自分自身を識別します。

プレイブックに次のタスクを追加します。

letsencrypt-issue.yml

...
  - name: "Generate a Let's Encrypt account key"
    shell: "if [ ! -f {{ letsencrypt_account_key }} ]; then openssl genrsa 4096 | sudo tee {{ letsencrypt_account_key }}; fi"

証明書を更新するたびにアカウントキーを再作成する必要はないため、既存のキーif [ ! -f テンプレート:Letsencrypt account key ];のチェックを追加して、上書きされないようにします。

次のステップではletsencrypt-issue.ymlで作業を続行するため、このファイルはまだ閉じないでください。

Let's Encrypt証明書を取得する準備をするために、プレイブックを作成し、初期構成とタスクを設定しました。 次に、秘密鍵とCSR生成のためのタスクをさらに追加します。

ステップ3—秘密鍵と証明書署名要求を生成する

このステップでは、必要な秘密鍵と証明書署名要求を生成するためのプレイブックタスクを記述します。

このセクションの最初のタスクでは、証明書に必要な秘密鍵を生成します。 ステップ2で書き始めたプレイブックの最後に以下を追加します。

letsencrypt-issue.yml

...
  - name: "Generate Let's Encrypt private key"
    shell: "openssl genrsa 4096 | sudo tee /etc/letsencrypt/keys/{{ domain_name }}.key"

同じドメイン上のサブドメインはすべて、サブジェクト代替名(SAN)を使用して同じ証明書に追加されるため、現時点では1つの秘密鍵を生成するだけで済みます。

次のタスクを使用して、取得する証明書の証明書署名要求(CSR)を生成します。 これは、各証明書を検証して発行するためにLet'sEncryptに送信されます。

プレイブックの最後に以下を追加します。

letsencrypt-issue.yml

...
  - name: "Generate Let's Encrypt CSR"
    shell: "openssl req -new -sha256 -key /etc/letsencrypt/keys/{{ domain_name }}.key -subj \"/CN={{ domain_name }}\" -reqexts SAN -config <(cat /etc/ssl/openssl.cnf <(printf \"\n[SAN]\nsubjectAltName=DNS:{{ domain_name }},DNS:www.{{ domain_name }}\")) | sudo tee /etc/letsencrypt/csrs/{{ domain_name }}.csr"
    args:
      executable: /bin/bash

このタスクは、ドメインのCSRを生成し、wwwサブドメインをSANとして証明書に追加します。

次のステップではletsencrypt-issue.ymlで作業を続行するため、このファイルはまだ閉じないでください。

証明書の秘密鍵とCSRを生成するためのAnsibleタスクを作成しました。 次に、検証と発行のプロセスを開始するタスクに取り組みます。

ステップ4—ACME検証プロセスを開始する

このステップでは、ステップ3で文書化されたタスクから出力されたファイルを使用して、Let'sEncryptに証明書署名要求を送信するタスクを記述します。 これにより、いくつかのchallengeファイルが返されます。これらのファイルは、証明書を要求しているドメイン名とサブドメインの所有権を証明するためにWebサーバーで提供する必要があります。

次のタスクは、your-domainのCSRを送信します。 プレイブックの最後に追加します。

letsencrypt-issue.yml

...
  - name: "Begin Let's Encrypt challenges"
    letsencrypt:
      acme_directory: "{{ acme_directory }}"
      acme_version: "{{ acme_version }}"
      account_key_src: "{{ letsencrypt_account_key }}"
      account_email: "{{ acme_email }}"
      terms_agreed: 1
      challenge: "{{ acme_challenge_type }}"
      csr: "{{ letsencrypt_csrs_dir }}/{{ domain_name }}.csr"
      dest: "{{ letsencrypt_certs_dir }}/{{ domain_name }}.crt"
      fullchain_dest: "{{ letsencrypt_certs_dir }}/fullchain_{{ domain_name }}.crt"
      remaining_days: 91
    register: acme_challenge_your_domain

このタスクでは、ステップ1で構成した変数を幅広く使用します。 次のステップで使用するACMEチャレンジファイルを含む変数を登録します。 your-domainを含むように変数の名前を手動で調整する必要がありますが、変数名にドットを使用できないため、すべての.文字を_に置き換えます。 。 たとえば、example.comの変数はacme_challenge_example_comになります。

次のステップではletsencrypt-issue.ymlで作業を続行するため、このファイルはまだ閉じないでください。

CSRをLet'sEncryptに送信するタスクを作成しました。 次に、証明書検証プロセスを完了するためのACMEチャレンジファイルを実装するタスクを追加します。

ステップ5—ACMEチャレンジファイルの実装

このステップでは、ACMEチャレンジファイルを読み取って実装するためのAnsibleタスクを記述します。 これらのファイルは、要求されたドメインとサブドメインの証明書を取得する資格があることを証明します。

ACMEチャレンジファイルは、証明書を要求しているドメインまたはサブドメインの/.well-known/acme-challenge/パスで、ポート80をリッスンしているWebサーバーで提供する必要があります。 たとえば、www.your-domainの証明書要求を検証するには、ACMEチャレンジファイルにインターネット経由でhttp://www.your-domain/.well-known/acme-challengeのパスでアクセスできる必要があります。

必要な宛先でこれらのファイルを提供する方法は、現在のWebサーバーの設定によって大幅に異なります。 ただし、このガイドでは、/var/www/htmlディレクトリからファイルを提供するように構成されたWebサーバー(前提条件のチュートリアルに従って)があることを前提としています。 したがって、独自のWebサーバー設定と互換性を持たせるために、それに応じてタスクを調整する必要がある場合があります。

まず、プレイブックの最後にファイルを提供するために必要な.well-known/acme-challenge/ディレクトリ構造を作成する次のタスクを追加します。

letsencrypt-issue.yml

...
  - name: "Create .well-known/acme-challenge directory"
    file:
      path: /var/www/html/.well-known/acme-challenge
      state: directory
      owner: root
      group: root
      mode: u=rwx,g=rx,o=rx

/var/www/html以外のディレクトリを使用してWebサーバーでファイルを提供している場合は、それに応じてパスを調整してください。

次に、次のタスクを使用して、手順4でacme_challenge_your-domain変数に保存されたACMEチャレンジファイルを実装します。

letsencrypt-issue.yml

...
  - name: "Implement http-01 challenge files"
    copy:
      content: "{{ acme_challenge_your_domain['challenge_data'][item]['http-01']['resource_value'] }}"
      dest: "/var/www/html/{{ acme_challenge_your_domain['challenge_data'][item]['http-01']['resource'] }}"
      owner: root
      group: root
      mode: u=rw,g=r,o=r
    with_items:
    - "{{ domain_name }}"
    - "www.{{ domain_name }}"

タスクのacme_challenge_your_domain変数名を手動で調整して、ACMEチャレンジ変数の名前に設定する必要があることに注意してください。この名前はacme_challenge_の後にドメイン名が続きますが、すべて[X195X ]文字が_に置き換えられました。 このAnsibleタスクは、ACME検証ファイルを変数からWebサーバーの.well-known/acme-challengeパスにコピーします。 これにより、Let's Encryptがそれらを取得して、ドメインの所有権と証明書を取得する資格を確認できるようになります。

次のステップではletsencrypt-issue.ymlで作業を続行するため、このファイルはまだ閉じないでください。

ACME検証ディレクトリとファイルを作成するために必要なAnsibleタスクを作成しました。 次に、ACME検証プロセスを完了し、署名された証明書を取得します。

ステップ6—証明書を取得する

このステップでは、Let's Encryptをトリガーして、送信したACMEチャレンジファイルを検証するタスクを記述します。これにより、署名された証明書を取得できます。

次のタスクは、手順5で実装したACMEチャレンジファイルを検証し、署名された証明書を指定されたパスに保存します。 プレイブックの最後に追加します。

letsencrypt-issue.yml

...
  - name: "Complete Let's Encrypt challenges"
    letsencrypt:
      acme_directory: "{{ acme_directory }}"
      acme_version: "{{ acme_version }}"
      account_key_src: "{{ letsencrypt_account_key }}"
      account_email: "{{ acme_email }}"
      challenge: "{{ acme_challenge_type }}"
      csr: "{{ letsencrypt_csrs_dir }}/{{ domain_name }}.csr"
      dest: "{{ letsencrypt_certs_dir }}/{{ domain_name }}.crt"
      chain_dest: "{{ letsencrypt_certs_dir }}/chain_{{ domain_name }}.crt"
      fullchain_dest: "{{ letsencrypt_certs_dir }}/fullchain_{{ domain_name }}"
      data: "{{ acme_challenge_your_domain }}"

手順4と同様に、このタスクでは、手順1で構成した変数を使用します。 タスクが完了すると、署名された証明書が指定されたパスに保存され、アプリケーションまたはサービスでの使用を開始できるようになります。

手順5と同様に、タスクのdata値を手動で調整して、ACMEチャレンジ変数の名前に設定する必要があることに注意してください。

以下は、追加した各タスクを示す完全なプレイブックです。

letsencrypt-issue.yml

- hosts: "host1"
  tasks:

  - name: "Create required directories in /etc/letsencrypt"
    file:
      path: "/etc/letsencrypt/{{ item }}"
      state: directory
      owner: root
      group: root
      mode: u=rwx,g=x,o=x
    with_items:
    - account
    - certs
    - csrs
    - keys

  - name: "Generate a Let's Encrypt account key"
    shell: "if [ ! -f {{ letsencrypt_account_key }} ]; then openssl genrsa 4096 | sudo tee {{ letsencrypt_account_key }}; fi"

  - name: "Generate Let's Encrypt private key"
    shell: "openssl genrsa 4096 | sudo tee /etc/letsencrypt/keys/{{ domain_name }}.key"

  - name: "Generate Let's Encrypt CSR"
    shell: "openssl req -new -sha256 -key /etc/letsencrypt/keys/{{ domain_name }}.key -subj \"/CN={{ domain_name }}\" -reqexts SAN -config <(cat /etc/ssl/openssl.cnf <(printf \"\n[SAN]\nsubjectAltName=DNS:{{ domain_name }},DNS:www.{{ domain_name }}\")) | sudo tee /etc/letsencrypt/csrs/{{ domain_name }}.csr"
    args:
      executable: /bin/bash

  - name: "Begin Let's Encrypt challenges"
    letsencrypt:
      acme_directory: "{{ acme_directory }}"
      acme_version: "{{ acme_version }}"
      account_key_src: "{{ letsencrypt_account_key }}"
      account_email: "{{ acme_email }}"
      terms_agreed: 1
      challenge: "{{ acme_challenge_type }}"
      csr: "{{ letsencrypt_csrs_dir }}/{{ domain_name }}.csr"
      dest: "{{ letsencrypt_certs_dir }}/{{ domain_name }}.crt"
      fullchain_dest: "{{ letsencrypt_certs_dir }}/fullchain_{{ domain_name }}.crt"
      remaining_days: 91
    register: acme_challenge_your_domain

  - name: "Create .well-known/acme-challenge directory"
    file:
      path: /var/www/html/.well-known/acme-challenge
      state: directory
      owner: root
      group: root
      mode: u=rwx,g=rx,o=rx

  - name: "Implement http-01 challenge files"
    copy:
      content: "{{ acme_challenge_your_domain['challenge_data'][item]['http-01']['resource_value'] }}"
      dest: "/var/www/html/{{ acme_challenge_your_domain['challenge_data'][item]['http-01']['resource'] }}"
      owner: root
      group: root
      mode: u=rw,g=r,o=r
    with_items:
    - "{{ domain_name }}"
    - "www.{{ domain_name }}"

  - name: "Complete Let's Encrypt challenges"
    letsencrypt:
      acme_directory: "{{ acme_directory }}"
      acme_version: "{{ acme_version }}"
      account_key_src: "{{ letsencrypt_account_key }}"
      account_email: "{{ acme_email }}"
      challenge: "{{ acme_challenge_type }}"
      csr: "{{ letsencrypt_csrs_dir }}/{{ domain_name }}.csr"
      dest: "{{ letsencrypt_certs_dir }}/{{ domain_name }}.crt"
      chain_dest: "{{ letsencrypt_certs_dir }}/chain_{{ domain_name }}.crt"
      fullchain_dest: "{{ letsencrypt_certs_dir }}/fullchain_{{ domain_name }}"
      data: "{{ acme_challenge_your_domain }}"

終了したら、ファイルを保存して閉じます。

ACMEチャレンジを完了し、署名された証明書を取得するタスクを追加しました。 次に、すべてのアクションを実行するために、Ansibleホストマシンに対してプレイブックを実行します。

ステップ7—ハンドブックを実行する

プレイブックと必要なすべてのタスクを作成したので、Ansibleホストマシンに対してプレイブックを実行して証明書を発行できます。

Ansibleサーバーから、ansible-playbookコマンドを使用してプレイブックを実行できます。

ansible-playbook letsencrypt-issue.yml

これにより、一度に1つのタスクでプレイブックが実行されます。 次のような出力が表示されます。

OutputPLAY [host1] **********************************************************************************

TASK [Gathering Facts] ************************************************************************
ok: [host1]

TASK [Create required directories in /etc/letsencrypt] ****************************************
changed: [host1] => (item=account)
changed: [host1] => (item=certs)
changed: [host1] => (item=csrs)
changed: [host1] => (item=keys)

TASK [Generate a Let's Encrypt account key] ***************************************************
changed: [host1]

TASK [Generate Let's Encrypt private key] *****************************************************
changed: [host1]

TASK [Generate Let's Encrypt CSR] *************************************************************
changed: [host1]

TASK [Begin Let's Encrypt challenges] *********************************************************
changed: [host1]

TASK [Create .well-known/acme-challenge directory] ********************************************
changed: [host1]

TASK [Implement http-01 challenge files] ******************************************************
changed: [host1] => (item=your-domain)
changed: [host1] => (item=www.your-domain)

TASK [Complete Let's Encrypt challenges] ******************************************************
changed: [host1]

PLAY RECAP ************************************************************************************
host1                      : ok=9    changed=8    unreachable=0    failed=0

プレイブックの実行中にエラーが発生した場合は、レビュー用に出力されます。

プレイブックが終了すると、有効なLet'sEncrypt証明書がホストマシン/etc/letsencrypt/certsディレクトリに保存されます。 次に、これを/etc/letsencrypt/keysの秘密鍵と一緒に使用して、Webサーバーやメールサーバーなどへの接続を保護できます。

Let's Encrypt証明書は、デフォルトで90日間有効です。 手順1で指定したアドレスに更新のリマインダーがメールで届きます。 証明書を更新するには、プレイブックを再度実行できます。 新しい証明書を適切に採用するには、証明書を手動でインストールするか、特定のディレクトリに移動するか、サービスを再起動する必要がある場合があるため、証明書を使用するサービスが新しい証明書を取得していることを再確認してください。

このステップでは、有効なLet'sEncrypt証明書を発行したプレイブックを実行しました。

結論

この記事では、有効なLet'sEncrypt証明書を要求して取得するためのAnsibleプレイブックを作成しました。

次のステップとして、新しいプレイブックを使用して、多数のサーバーの証明書を発行することを検討できます。 証明書を一元的に発行してWebサーバーに配布できる中央のACME検証サーバーを作成することもできます。

最後に、ACME仕様とLet's Encryptプロジェクトについて詳しく知りたい場合は、次のリンクを確認してください。

他の関連するAnsibleチュートリアルもご覧ください。