［初心者向け］障害発生の可能性を減らす作業手順を考える

# はじめに
#### **作業の手順に不備があって、システムを利用されるお客様に影響の出る障害を発生させてしまった！**
作業を行う限りこうした作業ミスによる障害は、切っても切り離せないものだと思います。
最近では、IaCを利用した作業手順の自動化により、手順にない作業を行うというミスは減少していますが、コードの中にエラーが発生することに変わりはありません。

そこで、本記事では、**作業で起こりうるエラーを事前に想定して、そのエラーが顕在化して障害になる前に潰し込む**ことで作業手順の品質向上を考えます。
ここでは、作業ミスなどの想定外の状態を**エラー**、お客様に実際に影響が出た状態を**障害**としています。


## エラーが障害に変わる過程

以下の図に、エラーがどのようにして障害に発展するかを示します。

- エラー①が発生した時点では、エラーは顕在化しないため障害にはなりません。しかし、このエラーに気づかず作業を続けると、次の作業②でエラーが顕在化し、障害が発生します。
- 一方、エラー②のように、エラーが顕在化する作業で発生したエラーは、即座に障害となることがあります。

**顕在化しないエラーは、顕在化する前に発見・修正**することで障害を防ぐことができます。また、**エラーが顕在化する作業については特に注意を払い、慎重に手順を進め、すぐに異常に気付けるようにする**ことが重要です。万が一、障害が発生してしまった場合は、事前に用意した**コンティンジェンシープラン**（緊急対応計画）を即座に実行できるようにしておくことも大切です。



![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3929265/7aa0325a-57e6-3638-2253-2fb3616adda8.png)





## 証明書更新を例にエラーの潰し込みを考える
それでは、**作業で起こりうるエラーを事前に想定して、そのエラーが顕在化して障害になる前に潰し込む**ことを、実際の手順をもとに考えてみます。


### 想定作業内容
AWSのApplication Load Balancer (ALB) で使用するEV証明書の更新を例に、作業手順と発生しうるエラーへの対策を検討します。証明書がどのように格納・利用されているかを理解していないと、作業手順の内容が分かりにくいかもしれませんので、証明書の流れについては、[SSL/TLSで利用される証明書の流れ](#参考ssltlsで利用される証明書の流れ)で詳述しています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3929265/abe6e0cb-a1a8-3721-504c-30b9c98372e5.png)





### 作業手順（修正前）
作業者Bさんの立場で、AWSのマネジメントコンソールを利用して更新する手順を作成しました。AWS CLIでの利用でも考慮ポイントはほとんど同様です。手順の分かりやすさではなく、障害を起こさないためにはどうするかという観点でご確認ください。
　＃　terraformやcloudformationなどのIaCを利用した手順では、観点が少し変わります。


##### 1.ACMに証明書を格納
- [ ] **[画面遷移]** AWSサービスから「Certificate Manager」を開き、作業対象リージョンを確認
- [ ] **[作業]** 「インポート」を選択し、以下の証明書をインポート
  - 証明書（example.crt）
  - 秘密鍵（example.key）
  - 中間証明書（intermediate.crt）
- [ ] **[確認]**  証明書が正しくインポートされているかを確認し、リソースIDをメモ

##### 2.ALBでの証明書更新
- [ ] **[画面遷移]**AWSサービスから「EC2」→「ロードバランサー」を開き、対象の「example-ALB」を選択
- [ ] **[画面遷移]** 「リスナーとルール」→「HTTPS:443」→「証明書タブ」→「デフォルトを変更」を選択
- [ ] **[作業]** 1でメモしたリソースIDを基に、対象ALBの証明書を更新
- [ ] **[確認]** 証明書が正しく更新されたことを確認

##### 3.証明書更新後の動作確認
- [ ] **[確認]** 該当のALBに```https://example.com/```でアクセスして証明書のFQDN、有効期限が正しく更新されていることを確認

---


### どういったエラーが発生しうるか


|No|エラー埋め込み箇所|エラー内容|原因|
|-|-|-|-|
|1|ACMへの格納|秘密鍵と証明書の不一致|受領した証明書が誤っていた|
|2|ACMへの格納|中間証明書不備|受領した中間証明書が誤っていた|
|3|ACMへの格納|SANsの差分による他のFQDNのエラー|証明書のSANsに差分があった|
|4|ALBでの更新|不正な証明書選択（古い証明書など）|更新画面で誤った証明書を選択した|
|5|その他|他のALBの更新漏れ|他のALBでも証明書更新が必要だった|
|6|その他|証明書の有効期限切れ|有効期限の確認誤りで予想よりも短かった|

他に、SNI（Server Name Indication）のサポートに関連したALBのスマート証明書選択アルゴリズムで考えられるエラーがいくつかありますが、ここでは割愛しました。


---

### エラーが障害になるまで



![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3929265/05438f3a-dab6-d9b5-3eff-3eb1df55b79b.png)

エラーと記載がある箇所でエラーが発生し、青の確認箇所でそのエラーを潰し込み、潰し込めなかったエラーが赤の箇所で顕在化し障害となります。

ここで注意したいことは、２,３のように切替後の確認でも気づけていないエラーがあることです。確認が不十分で障害を検知できていないと、**障害が発生していることに気づかず、放置される可能性**があります。
また、５,６のように**作業をしないことによる障害**もあります。特に新規の要件追加ではなく、既存業務のエンハンスなどで起こりえる障害です。

---

### 各エラーの内容と対策
具体的にエラーの内容とそれに対する対策案を記載します。

#### 1.秘密鍵と証明書のアンマッチ

証明書内に公開鍵が含まれるため証明書は秘密鍵とセットで格納されます。そのため誤った証明書と秘密鍵のセットでは正常な動作を行うことができません。
##### 対策
本エラーはACMで発生しないようになっています。誤ったセットをACMに格納しようとすると、ACMがマッチしているかを確認して格納することができません。

:::note
このような仕組みを**フールプルーフ**と呼びます。人間はミスをするので可能な限りシステムでミスが入り込まないようにできることが望ましいです。
:::

:::note warn
AWSの仕様は変更になることがあります。次に記載する中間証明書も以前はACMでチェーンを確認していましたが、現在は確認する仕組みは無くなりました。
:::

#### 2.中間証明書不備
サーバ証明書は指定された認証局で発行されたものでないと、正しく検証されません。そのため誤った証明書と秘密鍵のセットでは正常な動作を行うことができません。

##### 対策
こちらも過去のACMでは、上記の発行者の証明書が入っていないとNGとなっていたはずです。現在はそこまでのチェックは行っていないようです。そのためローカル環境でチェーンの確認が必要になります。
中間証明書のインポート有無で証明書を開いてみる方法などもありますが、opensslのverifyコマンドを利用すること方法が流用しやすく良いと思います。

:::note
AWSサポート経由で機能要求ができますので、作業ミスが起こらない仕組みに気づいたら機能要求を上げてみることもよいと思います。
:::

:::note alert 
一部のPCブラウザでは、証明書内の基幹情報アクセス（AIA）に記載されたURLを参照し、中間証明書を自動で取得し補完する機能が付いています。そのため、証明書を切り替えた後に、PCブラウザで動作確認を行うと中間証明書の不備に気づけない可能性があります。
:::
 
#### 3.SANsの差分による他のFQDNのエラー
SANsは、証明書内のサブジェクトネームの別名の値です。SANsに記載のあるFQDNがこの証明書の対象のFQDNとなりますので、更新前と更新後のSANsの値は同じである必要があります。
SANsに差分があると、記載のなくなったFQDNでの通信が行えなくなります。記載のあるFQDNは利用できるため、切替後の確認について一部のFQDNのみでOKと判断すると、中間証明書と同様に障害に気づけない可能性があります。

##### 対策
ACMに格納した後にドメイン名、失効日、追加のドメイン名を確認することで、現在利用している証明書との差分を確認することができます。カラムの項目は自身でカスタマイズできるので、必要なものに限定するとよいと思います。ACMの画面を下記に示します。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3929265/6acd7bdb-ccdc-a061-cbb7-a9043eb13775.png)

また、ALBでの証明書更新時もSANsの中身を見ることができます。エラーを見逃す可能性が減るため、複数の確認を行える場合は手間だと思わずに都度確認します。
証明書の選択はフィルタすることができるので、変更する証明書のドメインでフィルタをかけ、ドメイン、有効期限、SANsを見て切替前の最終チェックを行うことができます。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3929265/775c41b4-717e-2b5a-d468-0bae33490023.png)


#### 4.不正な証明書選択（古い証明書など）
ALBでの証明書の選択を誤ると、該当のFQDNでの通信が行えなくなります。

#### 対策
3.のSANsと同様に、ALBでの切替時にフィルタをかけて更新する証明書に誤りがないかをチェックすることが有効です。

#### 5.対象のALB作業漏れ
証明書の更新は問題なく実施したが、その証明書がALB#1とALB#2の2つで利用されており、ALB#1のみの作業でALB#2の更新が漏れてしまったという状況です。更新が漏れたALB#2は、証明書の有効期限が切れる日にいきなり通信が行えなくなります。すぐに気づくことのできない恐ろしい状況です。

##### 対策
作業手順を作る前に対象を確認する対策となりそうですが、作業手順の中でも気づくことができます。

まず、ACMで対象のドメイン名を確認します。現在利用されている証明書は使用中ですかに「はい」がついています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3929265/cc030886-271a-de6c-0270-eacc009715b6.png)

この証明書IDを選択すると、証明書の内容が見れます。関連付けられたリソースがあるのでこの対象のALBを一つづつ更新していくことで漏れがなくなります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3929265/a58edbd1-437c-face-c5f3-bfe53e76095f.png)

切替が完了すると古い証明書の使用中ですか？が「いいえ」に変わり、更新漏れが無いことが確認できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3929265/f3f415cc-7e79-f072-94a0-b44668cdf9d2.png)

:::note warn 
今回は割愛しましたが、ALBでデフォルトの証明書を更新すると古い証明書が、証明書リスト（SNI）に自動で格納されます。誤ったFQDNの証明書で更新した際にエラーとならないように、そのような仕組みにしていると考えられますが、古い証明書がALBに適用されたままとなってしまうので、切替後に問題が無ければ削除することを推奨します。
:::


#### 6.証明書の有効期限切れ
2024年12月時点で、CA/Bフォーラムが制定している証明書の最長有効期限は397日であるため、約1年ごとに更新が必要となります。有効期限が切れた証明書を利用している場合、ブラウザでは警告表示や接続不可となります。

証明書更新作業において、証明書の有効期限切れを検知することが作業開始の前提になるため、作業手順外の話になりますが、有効期限切れの見逃しは重大な障害となりますので対策を記載します。

##### 対策
CloudwatchメトリクスのDaysToExpiryを利用することで、証明書の有効期限を知ることができます。適切なアラーム設定と通知設定を行い、更新漏れの無い運用とすることが可能となります。下記のURLに記載のある通り、AWSの推奨は44日間の閾値となります。こちらは、証明書更新までの準備期間と新しい証明書の発行機関などをもとに決定する必要があります。

https://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/monitoring/Best_Practice_Recommended_Alarms_AWS_Services.html#CertificateManager

:::note
ACMのメトリクスはDaysToExpiryしかありませんが、その証明書が利用されているかどうかも把握したいため、使用中ですか？の状態もメトリクスで取れるとよいと思っています。また、ACMで発行したDV証明書はDNS検証を行うことで自動更新が行えますが、使用中であることが条件にもなっています。
こちらも、サポート経由で追加機能のリクエストを行ってみました。
:::


## 結論
作業手順作成後に、障害が発生しないように下記の見直しを行うことで、ある程度の障害を未然に防ぐ手順を作ることができます。
　**1. 各作業ごとに起こりえるエラーを考える**
　**2. それぞれのエラーがどこで顕在化して障害になるかを整理する**
　**3. 障害になる前にどのようにエラーを潰すかを考え、手順を修正する**

しかし、すべてを確認するのは労力がかかるので、**「エラーが顕在化するポイントを見極めて、その前にどこまでの動作確認ができるか、できない場合はコンティンジェンシープランをどうするかを決めておく。」** といった自分なりの品質確保の考え方を持っておくのがよいのではないでしょうか。

---

### [参考] 作業手順（修正後）

##### 1. 証明書と中間証明書のチェーンを確認
- [ ] **[確認]** Linux環境で、作業で利用する証明書のチェーンが正しいことを確認
```
[ec2-user@test ~]$ ln -s intermidiates.crt "$(openssl x509 -hash -noout intermidiates.crt).0"
[ec2-user@test ~]$ openssl verify -CApath . example.crt
example.crt: OK
```

##### 2. ACMに証明書を格納
- [ ] **[画面遷移]** AWSサービスから「Certificate Manager」を開き、作業対象リージョンを確認
- [ ] **[作業]** 「インポート」を選択し、以下の証明書をインポート
  - 証明書（example.crt）
  - 秘密鍵（example.key）
  - 中間証明書（intermediate.crt）
- [ ] **[確認]**  証明書が正しくインポートされているかを確認し、リソースIDをメモ

##### 3. ACMで現在利用されている証明書を確認
- [ ] **[画面遷移]** AWSサービスから「Certificate Manager」を開き、作業対象リージョンを確認
- [ ] **[画面遷移]** ドメイン名でソートを行い対象のFQDNを確認
- [ ] **[画面遷移]** 既存の証明書が使用中ですかで「はい」、新規の証明書が「いいえ」となっていることを確認
- [ ] **[画面遷移]** 既存の証明書と追加のドメイン名が同じこと、失効日が想定通りであることを確認
- [ ] **[画面遷移]** 既存の証明書を選択して、関連付けられたリソースを確認し、arnをすべてコピー

##### 4.ALBでの証明書更新
- [ ] **[画面遷移]** AWSサービスから「EC2」→「ロードバランサー」を開き、コピーしたarnをもとに対象のALBを選択
- [ ] **[画面遷移]** 「リスナーとルール」→「HTTPS:443」→「証明書タブ」→「デフォルトを変更」を選択
- [ ] **[画面遷移]** 名前またはドメインに記載されたFQDNをコピーし、証明書のフィルターで検索
- [ ] **[画面遷移]** 現在利用している証明書のIDをメモ
- [ ] **[画面遷移]** 現在利用している証明書と更新予定の証明書を比較し、FQDN、SANsが同じであることを確認
- [ ] **[影響発生有：作業]** 更新する証明書の有効期限が想定通りであることを確認し証明書を更新
- [ ] **[確認]** 5.の動作確認を実施
- [ ] **[作業]** 証明書リストに自動保存された過去の証明書を削除
- [ ] **[確認]** 5.の動作確認を実施

　　コピーしたarn分、4. 5.を実施。

##### 5.証明書更新後の動作確認
- [ ] **[確認]** 該当のALBに```https://example.com/```でアクセスして正常に業務が行えることを確認
- [ ] **[確認]** 該当のALBに```https://example1.com/(SANsの対象すべて)```でアクセスして正常に業務が行えることを確認
- [ ] **[確認]** 下記を参考に、該当のALBに```https://example.com/```でアクセスして証明書のFQDN、有効期限が正しく更新されていることを確認

```
[ec2-user@test ~]$ curl https://example.com/ -v
略
*  subject: C=JP; ST=Tokyo; L=Minato-Ku; O=example; CN=example.com
*  start date: Dec 18 10:35:42 2024 GMT
*  expire date: Dec 18 10:35:42 2025 GMT
*  issuer: C=JP; ST=Tokyo; L=Minato-Ku; O=example; CN=example.com
略
```
##### a1.コンティンジェンシープラン（ALBの証明書更新作業後に問題があった場合に発動）

- [ ] **[画面遷移]** AWSサービスから「EC2」→「ロードバランサー」を開き、コピーしたarnをもとに対象のALBを選択
- [ ] **[画面遷移]** 「リスナーとルール」→「HTTPS:443」→「証明書タブ」→「デフォルトを変更」を選択
- [ ] **[画面遷移]** 名前またはドメインに記載されたFQDNをコピーし、証明書のフィルターで検索
- [ ] **[戻し作業]** 4.でメモした更新前の証明書IDの証明書があることを確認し証明書を更新

##### a2.コンティンジェンシープラン実施後の動作確認
- [ ] **[確認]** 該当のALBに```https://example.com/```でアクセスして正常に業務が行えることを確認
- [ ] **[確認]** 該当のALBに```https://example1.com/(SANsの対象すべて)```でアクセスして正常に業務が行えることを確認






# [参考]SSL/TLSで利用される証明書の流れ

### 1) 登場人物の紹介

SSL/TLSで利用される証明書ですが、一般的にルート証明書、中間証明書、サーバ証明書の３つの証明書と、（証明書には公開鍵が含まれるため）それぞれの証明書の対となる秘密鍵が存在します。証明書の発行・利用に関わる主要な役割は以下の通りです。


- **ルート認証局**：ルート証明書や中間証明書を発行
- **中間認証局**：サーバ証明書を発行
- **サーバ、クライアント**：各証明書を使用

少しわかりづらいですが、ルート認証局にて中間証明書、中間認証局にてサーバ証明書という形で一つ上位の認証局が証明書を発行します。また、それぞれの秘密鍵は次のように管理されます。
- **ルート証明書の秘密鍵**：ルート認証局にて管理
- **中間証明書の秘密鍵**：中間認証局にて管理
- **サーバ証明書の秘密鍵**：サーバにて管理

また、正しい認証局で証明書が発行されたことを示すものとして秘密鍵で作成した署名が必要となります。

 
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3929265/8d3e2c29-eab3-a745-8675-589ea30b90a0.png)


### 2) 証明書の配置
証明書を実際に配置した際のイメージです。この構成を覚えておくと、証明書の格納や更新の作業が具体的にイメージできるようになると思います。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3929265/757012b1-1668-35d8-403d-94aeaffb1fc3.png)



### 3) 証明書の送付と検証
クライアントがサーバにアクセスする際、証明書は以下の流れで、サーバから送付されクライアント側で正しい証明書であるか検証されます。
1. サーバが**サーバ証明書**と**中間証明書**をクライアントに送付します
2. クライアントは自分が持っている**ルート証明書**を使用し、中間証明書とサーバ証明書を順に検証します
3. この検証により、サーバ証明書が正規の認証局で発行されたものであることが確認されます


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3929265/2d4c436c-30e5-0b04-fede-335a4791a8fb.png)

:::note
検証プロセスでは、**公開鍵**と**秘密鍵**を利用したデジタル署名の仕組みが使われます。
- ルート証明書にはルート認証局の公開鍵が含まれています。この公開鍵を用いて、中間証明書の署名を検証します。  
- 同様に、中間証明書の公開鍵を使用してサーバ証明書の署名を検証します。  

このように、証明書がチェーンのように連結しているため、これを「証明書チェーン」と呼びます。
::: 

### 4) 証明書のやり取りのその後
SSL/TLS通信ではクライアントが受け取ったサーバ証明書の公開鍵を利用して、プリマスタシークレットを送付しセッションキー（共通鍵）をクライアントとサーバ間で共有します。セッションキーを利用して実際の通信は暗号化されます。
　※今回は証明書更新がメインであるため、この部分の詳細説明は割愛します。




### AWSのALBにおける証明書の配置
証明書の配置ですが、AWS環境のALBで利用する場合は以下のようになります。
証明書はALBに配置するのではなく、ACMにインポートしてALBからACMの証明書を利用するようにします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3929265/6eacaff3-fb1c-c4a1-4d67-5acdc774bc14.png)

:::note
AWSではACMという証明書の作成、保存、更新をとても便利に管理してくれる機能があり、ALBと紐づけることができます。特別な要件が無い限り証明書の管理はACMを利用するべきです。
:::