Red Hat OpenShift on IBM Cloudで、QuarkusアプリケーションをHTTPSで公開する

# はじめに
IBM Cloud上のマネージドなOpenShift環境である [Red Hat OpenShift on IBM Cloud](https://www.ibm.com/jp-ja/cloud/openshift) では、```oc expose```コマンドにより、デプロイしたWeb・APIアプリケーションを外部(インターネット)に簡単に公開することが可能となっています。ただ、デフォルトではHTTPでの公開となっており、実際に利用するうえでは安全性に問題があります。

Red Hat OpenShift on IBM CloudではHTTPSでアプリケーションを公開する方法も用意されています。この記事では、[Quarkus](https://ja.quarkus.io/)で作成したアプリケーションを対象に、HTTPSで公開する手順について記載します。

# 経路のタイプ
[公式ドキュメントに記載されています](https://cloud.ibm.com/docs/openshift?topic=openshift-openshift_routes&locale=ja#route-types)が、OpenShiftでは4つのタイプの経路があり、当記事ではそのうちの「パススルー」と「エッジ」について試してみます。

| 経路のタイプ | 内容 |
|:-|:-|
| パススルー(passthrough)  | HTTPSの暗号化の終端をアプリケーションPodが担当。Routerは終端処理に関与せず素通しする。  |
| エッジ(edge)  | HTTPSの暗号化の終端を(OpenShift内の)Routerが担当。RouterからアプリケーションPodまでの経路は暗号化されない。  |


# 環境
- Red Hat OpenShift on IBM Cloud 4.7.12 (VPC Gen 2環境に作成)
- Quarkus 1.13.6 

# Quarkusアプリケーションの準備
公開の前提となるQuarkusアプリケーションの作成、アプリケーションのビルド、コンテナイメージのビルド、IBM Cloudのコンテナレジストリ(IBM Cloud Container Registry)へのイメージpushの手順については別記事「[IBM Cloud Code EngineにQuarkusのサンプルアプリケーションをデプロイしてみる](https://qiita.com/hisato_imanishi/items/45c83963fa44c34e0255#quarkus%E3%81%AE%E3%82%B5%E3%83%B3%E3%83%97%E3%83%AB%E3%82%A2%E3%83%97%E3%83%AA%E3%82%B1%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%81%AE%E3%82%B3%E3%83%B3%E3%83%86%E3%83%8A%E3%82%A4%E3%83%A1%E3%83%BC%E3%82%B8%E3%82%92%E4%BD%9C%E6%88%90%E3%81%97ibm-cloud-container-registry%E3%81%AB%E7%99%BB%E9%8C%B2%E3%81%99%E3%82%8B)」内の記述を参照ください。

以降は、IBM Cloud Container RegistryにQuarkusアプリケーションのイメージが登録済みであることを前提として記述します。


# パススルーでの公開手順

パススルーの場合、アプリケーションPod側で終端処理を担当する必要があるため、自己署名証明書を作成し、Quarkusアプリケーションのコンテナに含める必要があります。ここではビルド時にコンテナイメージに含めるのではなく、証明書データをSecretとして登録し、SecretをVolumeとしてマウントすることでQuarkusアプリケーションが証明書を使用できるようにします。

まず、アプリケーションをデプロイするプロジェクト(名前空間)を作成します。

```bash
oc new-project quarkus-sample
oc project quarkus-sample
```

作成したプロジェクトでIBM Cloud Container Registryからイメージをpullできるよう、defaultプロジェクトからimagePullSecret(all-icr-io)をコピーします。

```bash
oc get secret all-icr-io -n default -o yaml | sed 's/default/quarkus-sample/g' | oc create -n quarkus-sample -f -
```

自己署名証明書を作成します。cert.pem、key.pemという2つのファイルが作成されます。

```bash
openssl req -newkey rsa:2048 -new -nodes -x509 -days 3650 -keyout key.pem -out cert.pem
```

自己署名証明書のデータをSecretとして登録します。cert.pem、key.pemというファイル名と同名のキーで登録します。

```
oc create secret generic https-secret --from-file=cert.pem=./cert.pem --from-file=key.pem=./key.pem
```

QuarkusアプリケーションのコンテナイメージをOpenShiftにデプロイするための Deployment、Service、Routeのマニフェストファイルを作成します。

Deploymentでは主に以下を設定しています。

- 上記で作成したSecretをVolume化してPodにマウント
- HTTPS接続をポート8443で公開
- 「QUARKUS_HTTP_SSL_CERTIFICATE_FILE」「QUARKUS_HTTP_SSL_CERTIFICATE_KEY_FILE」という環境変数で、cert.pem、key.pemのパスを設定。これにより、Quarkusアプリケーションに自己署名証明書を渡している


「QUARKUS_HTTP_SSL_CERTIFICATE_KEY_FILE」という環境変数は、本来は「QUARKUS_HTTP_SSL_CERTIFICATE_KEY-FILE」(KEYとFILEの間がアンダーバーではなくハイフン)が正しいはずなのですが、「QUARKUS_HTTP_SSL_CERTIFICATE_KEY-FILE」を使うとなぜかQuarkusが認識しませんでした。「QUARKUS_HTTP_SSL_CERTIFICATE_KEY_FILE」を使用してQuarkusアプリケーションを起動するとタイポじゃないかという警告メッセージが出るのですが、なぜかHTTPSで正常に公開されます。

```yaml:deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: quarkus-getting-started
spec:
  selector:
    matchLabels:
      app: quarkus-getting-started
  replicas: 1
  template:
    metadata:
      labels:
        app: quarkus-getting-started
    spec:
      containers:
      - name: quarkus-getting-started
        image: <IBM Cloud Container Registry上のQuarkusアプリケーションのコンテナイメージ>
        resources:
          limits:
            memory: "256Mi"
            cpu: "500m"
        ports:
        - containerPort: 8443
        volumeMounts:
          - name: https-secret
            readOnly: true
            mountPath: /var/run/secrets/kubernetes.io/ssl
        env:
          - name: QUARKUS_HTTP_SSL_CERTIFICATE_FILE
            value: /var/run/secrets/kubernetes.io/ssl/cert.pem
          - name: QUARKUS_HTTP_SSL_CERTIFICATE_KEY_FILE
            value: /var/run/secrets/kubernetes.io/ssl/key.pem
          - name: QUARKUS_HTTP_INSECURE-REQUESTS
            value: redirect
          - name: QUARKUS_HTTP_SSL-PORT
            value: '8443'
          - name: QUARKUS_PROFILE
            value: prod
      imagePullSecrets:
          - name: all-icr-io
      volumes:
        - name: https-secret
          secret:
            secretName: https-secret
```

Serviceはポート8443を公開するように設定しています。

```yaml:service.yaml
apiVersion: v1
kind: Service
metadata:
  name: quarkus-getting-started-https
spec:
  selector:
    app: quarkus-getting-started
  ports:
    - protocol: TCP
      port: 8443
      targetPort: 8443
```

パススルーの設定はRouteに対して実施します。``termination`` で ``passthrough`` を指定しています。また、``insecureEdgeTerminationPolicy`` では、HTTPでのアクセスを受けた場合にHTTPSに自動的にリダイレクトするように ``Redirect`` を設定しています。

```yaml:route-passthrough.yaml
apiVersion: route.openshift.io/v1
kind: Route
metadata:
  labels:
    app: quarkus-getting-started
  name: quarkus-getting-started-https
spec:
  port:
    targetPort: 8443
  tls:
    termination: passthrough
    insecureEdgeTerminationPolicy: Redirect
    wildcardPolicy: None
  to:
    kind: Service
    name: quarkus-getting-started-https
    weight: 100
```

作成したマニフェストファイルをOpenShiftに反映します。

```
oc apply -f deployment.yaml
oc apply -f service.yaml
oc apply -f route-passthrough.yaml
```

作成されたRouteを確認し、Routeに設定されているホスト名にHTTPSでアクセスできることを確認します。

```
oc get route
```


# エッジでの公開手順

エッジの場合、アプリケーションPod側はHTTPSを意識することなく、自己署名証明書は不要です。RouterでTLS接続の終端処理が行われ、証明書はRed Hat OpenShift on IBM Cloud側で自動的に用意・設定されます。

プロジェクトの作成、imagePullSecretのコピーはパススルーの手順と同じです。

```bash
oc new-project quarkus-sample
oc project quarkus-sample
```

```bash
oc get secret all-icr-io -n default -o yaml | sed 's/default/quarkus-sample/g' | oc create -n quarkus-sample -f -
```

QuarkusアプリケーションのコンテナイメージをOpenShiftにデプロイするための Deployment、Service、Routeのマニフェストファイルは以下の通りです。

Deploymentでは、パススルーの場合にあった環境変数などの設定は不要です(Deploymentは終端処理に関わらないため)。

```yaml:deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: quarkus-getting-started
spec:
  selector:
    matchLabels:
      app: quarkus-getting-started
  replicas: 1
  template:
    metadata:
      labels:
        app: quarkus-getting-started
    spec:
      containers:
      - name: quarkus-getting-started
        image: <IBM Cloud Container Registry上のQuarkusアプリケーションのコンテナイメージ>
        resources:
          limits:
            memory: "256Mi"
            cpu: "500m"
        ports:
        - containerPort: 8080
      imagePullSecrets:
          - name: all-icr-io
```

```yaml:service.yaml
apiVersion: v1
kind: Service
metadata:
  name: quarkus-getting-started
spec:
  selector:
    app: quarkus-getting-started
  ports:
    - protocol: TCP
      port: 8080
      targetPort: 8080
```

エッジの設定をRouteに対して行います。``termination`` では ``edge`` を指定します。その他はパススルーの場合と同じです。

```yaml:route-edge.yaml
apiVersion: route.openshift.io/v1
kind: Route
metadata:
  labels:
    app: quarkus-getting-started
  name: quarkus-getting-started-https
spec:
  port:
    targetPort: 8080
  tls:
    termination: edge
    insecureEdgeTerminationPolicy: Redirect
    wildcardPolicy: None
  to:
    kind: Service
    name: quarkus-getting-started
    weight: 100
```

作成したマニフェストファイルをOpenShiftに反映します。

```
oc apply -f deployment.yaml
oc apply -f service.yaml
oc apply -f route-edge.yaml
```

作成されたRouteを確認し、Routeに設定されているホスト名にHTTPSでアクセスできることを確認します。

```
oc get route
```

以上です。