GitLab Pagesを有効にする(GCPのロードバランサにワイルドカードSSL証明書を利用する)

オンプレミス(GCP)に構築したGitLabのGitLab Pagesを有効にする手順のまとめとなります。
GitLab Pagesは、仮に`https://pages.example.com`で運用する場合、

- `https://group1.pages.example.com/aaa`
- `https://group2.pages.example.com/aaa`

のようにグループ毎にサブドメインが利用されます。
その為、これを`https`で運用する為にはワイルドカード(`*.pages.example.com`)で発行したSSL証明書が必要となります。
なので、GCPのロードバランサにマネージドのワイルドカードSSL証明書を設定する手順もまとめています。

GitLabを構築する手順は下記を参照ください。

https://qiita.com/kamina/items/1113d4d8eea3fd4db3c9

# GitLab Pagesの有効化

GitLab Pagesを有効にするためにGitLabの設定を編集します。

```ruby:/etc/gitlab/gitlab.rb
pages_external_url "https://pages.example.com/"
gitlab_pages['enable'] = true
gitlab_pages['access_control'] = true

pages_nginx['enable'] = true
pages_nginx['listen_port'] = 80
pages_nginx['listen_https'] = false
pages_nginx['proxy_set_headers'] = {
  'X-Forwarded-Proto' => 'https',
}
```

GiLab Pagesに利用するドメインを設定し、有効化します。
GitLabとは違うドメインを利用する必要があり、GitLabとのサブドメイン違いはOKですが、GitLabのサブドメインはNGになります。

| GitLab             | Pages             |    |
| ------------------ | ----------------- | -- |
| example.com        | example.io        | OK |
| example.com        | pages.example.com | NG |
| gitlab.example.com | pages.example.com | OK |

https://docs.gitlab.com/ee/administration/pages/#prerequisites

設定したら、GitLabを再起動。

```shell-session
$ sudo gitlab-ctl reconfigure
```

これでGitLab Pages自体は有効になりました。

# ワイルドカードSSL証明書を利用したロードバランサを作成

次に今回の肝となるワイルドカードSSL証明書を利用したロードバランサを作成して、GitLab Pagesにアクセス可能にします。
手順としては、

1. Webコンソールから通常のHTTPSロードバランサを作成
   この段階ではWebコンソールから発行可能な単一ドメインのSSL証明書(`pages.example.com`)を利用します。
1. gcloudコマンドでワイルドカード(`*.pages.example.com`)のSSL証明書を発行
1. gcloudコマンドでロードバランサの証明書マップをワイルドカードのものに付け替え

という流れになります。
現時点(2022/10/30)では、ワイルドカードSSL証明書の発行はWebコンソールだけでは行えないので、gcloudのコマンドを利用します。
今後いつかはWebコンソールからも発行可能になるとは思いますが、現状はコマンドのみの対応です。
とはいえ、ワイルドカードのSSL証明書が手軽に扱えるだけでもありがたい。

下記は2022年2月にプレビュー版として機能が公開された際の記事です。
なので、まだかなり新しい機能になります。

https://cloud.google.com/blog/ja/products/identity-security/simplify-saas-scale-tls-certificate-management

※上記記事の中では、`gcloud beta`とβ版のコマンドとなっていますが、現状は通常の`gcloud`コマンドとして利用できます。

## ロードバランサの作成

まずは通常のHTTPSロードバランサを用意します。
流れはGitLabを構築した際と同様です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/272546/0fc11ec0-1d21-cc6b-a549-d58c8dc90ee5.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/272546/522d2721-1ab5-b798-45a1-b4c783ba9c44.png)
IPはPages用に静的なIPを払い出します。
このIPアドレスは後でDNSに登録するので、控えておいてください。
証明書は仮のものなので、何でも良いです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/272546/58c03824-1fb9-9ece-6c92-ead994d50994.png)
フロントエンドはPages用のものを新たに用意しましたが、バックエンドは宛先が同じ(GitLabと同じNginxに流せば良い)なのでGitLabのバックエンド設定をそのまま利用します。
構成のイメージとしては下記になります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/272546/de23675c-1d7c-3841-4db8-6e16ae25fe0e.png)

## Certificate ManagerでワイルドカードSSL証明書を発行

Certificate Managerを利用してワイルドカードSSL証明書を発行します。
ちなみに、公式のドキュメントは下記になります。

https://cloud.google.com/certificate-manager/docs/deploy-google-managed-dns-auth

### Cloud DNSを設定

ワイルドカードのSSL証明書を発行する為にDNS認証を行う必要がある為、Cloud DNSを作成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/272546/b30efb16-545e-101d-b5f9-4a674ac832eb.png)
Cloud DNSを有効化し、対象ドメイン(`pages.example.com`)のゾーンを追加します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/272546/572b7db4-2d6a-e743-6b44-8888bef8e38c.png)
ゾーンを作成すると、指定ドメインの`SOA` `NS`レコードは自動で作成されます。
ゾーンが作成出来たら、まずは対象ドメインのIP解決ができるようにレコードを登録しておきます。
登録するDNSレコードとしては下記。

| Host | Type | Value |
| -------------------- | ----- | ------------------ |
| pages.example.com.   | A     | 控えておいたロードバランサのIP |
| *.pages.example.com. | CNAME | pages.example.com. |

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/272546/43393160-d307-6f09-9385-12abb0cf4e1f.png)
追加するとこのようになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/272546/2f7b87c5-f3ee-7fc3-20b2-f3ecd7fc8bbe.png)
次に`NS`レコードを選択し、ネームサーバのドメイン一覧を確認します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/272546/a6628b92-cc52-9386-6081-3d82d0e988a3.png)
このネームサーバの一覧を、ルートドメイン(この場合は`example.com`)の管理元のDNSサーバに`NS`レコードとして追加します。
下記はGoogle Domainsでドメインを管理している場合の例になります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/272546/2c24c647-97d3-3e30-5e15-38036c98619c.png)
つまり、`pages.example.com`のドメイン情報はCloud DNSのネームサーバを参照してねという設定です。

これらDNSの反映を待って、`pages.example.com`や`xxx.pages.example.com`のドメインからIPアドレスが解決できるようになっていればOKです。

### DNS認証の作成

続いてCertificate Managerを使ってDNS認証などを行っていきますが、ここからは`gcloud`コマンドを利用するので、`gcloud`がインストールされていない方は下記を参照してインストールを行ってください。

https://cloud.google.com/sdk/docs/install?hl=ja

まずはDNS認証を作成します。

```shell-session
$ gcloud certificate-manager dns-authorizations create gitlab-pages --domain="pages.example.com"
API [certificatemanager.googleapis.com] not enabled on project [xxx]. Would you like to enable and retry (this will take a few minutes)? (y/N)?  y

Enabling service [certificatemanager.googleapis.com] on project [xxx]...
Operation "operations/acat.p2-xxx" finished successfully.
Create request issued for: [gitlab-pages]
Waiting for operation [projects/funlab-gitlab/locations/global/operations/operation-xxx] to complete...done.
Created dnsAuthorization [gitlab-pages].
```

下記コマンドで作成されたDNS認証を確認します。

```shell-session
$ gcloud certificate-manager dns-authorizations describe gitlab-pages
createTime: '2022-10-23T12:16:37.974019532Z'
dnsResourceRecord:
  data: xxx.13.authorize.certificatemanager.goog.
  name: _acme-challenge.pages.example.com.
  type: CNAME
domain: pages.example.com
name: projects/xxx/locations/global/dnsAuthorizations/gitlab-pages
updateTime: '2022-10-23T12:16:38.883880033Z'
```

### DNSにCNAMEレコードを追加

DNS認証の`CNAME`をCloud DNSに登録します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/272546/489dbc5a-c796-1a2d-1d11-ba65f80c40ff.png)
対象のゾーンを選択して、レコードセットを追加。
登録するのはDNS認証を確認した際の下記内容です。

```
dnsResourceRecord:
  data: xxx.13.authorize.certificatemanager.goog.
  name: _acme-challenge.pages.example.com.
  type: CNAME
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/272546/8961e769-b6e2-5cae-302a-ae59bccd5634.png)

### DNS認証を参照するマネージド証明書を作成

DNS認証の設定が完了したら、ようやくワイルドカードSSL証明書の発行になります。
対象とするドメインは`"pages.example.com,*.pages.example.com"`のようにワイルドカードなしとありの両方を指定します。

```shell-session
$ gcloud certificate-manager certificates create gitlab-pages \
  --domains="pages.example.com,*.pages.example.com" \
  --dns-authorizations="gitlab-pages"
Create request issued for: [gitlab-pages]
Waiting for operation [projects/xxx/locations/global/operations/operation-xxx] to complete...done.
Created certificate [gitlab-pages].
```

下記コマンドで証明書の状態を確認します。

```shell-session
gcloud certificate-manager certificates describe gitlab-pages
createTime: '2022-10-23T12:51:51.064352488Z'
managed:
  authorizationAttemptInfo:
  - domain: pages.example.com
    state: AUTHORIZING
  - domain: '*.pages.example.com'
    state: AUTHORIZING
  dnsAuthorizations:
  - projects/xxx/locations/global/dnsAuthorizations/gitlab-pages
  domains:
  - pages.example.com
  - '*.pages.example.com'
  state: ACTIVE
name: projects/xxx/locations/global/certificates/gitlab-pages
pemCertificate: |
  -----BEGIN CERTIFICATE-----
  ...
sanDnsnames:
- pages.example.com
- '*.pages.example.com'
updateTime: '2022-10-23T12:51:51.635821798Z'
```

stateがACTIVEになっていれば証明書の発行が完了しています。
（PROVISIONINGの場合は処理中なので、しばらくお待ちください。）

### 証明書マップの作成

証明書が発行出来たら、証明書マップ(ドメインと証明書のマッピング)を作成します。

```shell-session
$ gcloud certificate-manager maps create gitlab-pages-map
Waiting for 'operation-xxx' to complete...done.
Created certificate map [gitlab-pages-map].
```

### 証明書マップにエントリを作成

証明書マップに`pages.example.com`、`*.pages.example.com`それぞれと発行した証明書とのマッピングを追加します。

```shell-session
$ gcloud certificate-manager maps entries create gitlab-pages-map-entry1 \
  --map="gitlab-pages-map" --certificates="gitlab-pages" --hostname="pages.example.com"

$ gcloud certificate-manager maps entries create gitlab-pages-map-entry2 \
  --map="gitlab-pages-map" --certificates="gitlab-pages" --hostname="*.pages.example.com"
```

追加したマッピングを確認します。

```shell-session
$ gcloud certificate-manager maps entries describe gitlab-pages-map-entry1 --map="gitlab-pages-map"
...
state: ACTIVE
updateTime: '2022-10-23T14:26:27.073691011Z'

$ gcloud certificate-manager maps entries describe gitlab-pages-map-entry2 --map="gitlab-pages-map"
...
state: ACTIVE
updateTime: '2022-10-23T14:26:27.073691011Z'
```

### 証明書マップをターゲットプロキシにアタッチする

最後に作成した証明書マップをロードバランサに設定します。

まずはロードバランサの「ロード バランシング コンポーネントのビュー」で詳細な表示に切り替え、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/272546/bd2b0a67-bbd2-8390-4243-6d82f076bca2.png)
ターゲットプロキシを確認します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/272546/d63da2d5-cee0-bf2f-d6d7-bb8dca95c400.png)
今回の場合は。`pages-lb-target-proxy`が対象です。
このターゲットプロキシを対象に、証明書マップを設定してあげます。

```shell-session
$ gcloud compute target-https-proxies update pages-lb-target-proxy --certificate-map="gitlab-pages-map"
Updated [https://www.googleapis.com/compute/v1/projects/xxx/global/targetHttpsProxies/pages-lb-target-proxy].
```

証明書マップの設定は、下記の通りターゲットプロキシの詳細で確認できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/272546/4fea9eea-7171-f127-3965-72109bf06cd5.png)

ちなみに、この既存のロードバランサに証明書マップをアタッチするやり方は、公式のドキュメントに従ったものです。
https://cloud.google.com/certificate-manager/docs/deploy-google-managed-dns-auth#attach_the_certificate_map_to_the_target_proxy

> If there are any existing TLS (SSL) certificates attached directly to the proxy, the proxy gives preference to the certificates referenced by the certificate map over the directly attached TLS (SSL) certificates.

翻訳すると、

> プロキシに直接アタッチされた既存の TLS (SSL) 証明書がある場合、プロキシは、直接アタッチされた TLS (SSL) 証明書よりも、証明書マップによって参照される証明書を優先します。

ということらしいです。

これで設定は完了になります。
あとはGitLab PagesにPageをデプロイしてきちんと運用できているか確認してみてください。