ROSA上にインターネット公開/非公開の2種類のアプリを作成する

# はじめに

ここでは ROSA 上でインターネットに公開するアプリと、内部ネットワーク向けに公開するアプリの2種類をホストする構成を作成します。


# 前提手順

ここでは以下の記事の順番で ROSA クラスターと周辺環境を構築している状況を前提としています。

1) <a href="https://qiita.com/Yuhkih/private/367eccc0cfe64dfbd915" target=”_blank”>ROSA を自分が作成した VPCにインストールする (Single AZ)</a>
2) <a href="https://qiita.com/Yuhkih/private/78f39c27c29b12641b36" target=”_blank”>踏み台サーバー用のVPCを作成する</a>
3) <a href="https://qiita.com/Yuhkih/private/265eaab3f98c51b97a79" target=”_blank”>2つのVPCをTransit Gatewayで接続する</a>

これらの手順によって以下のような構成が作成されているはずです。

![ROSA Basic  (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99425/e1310912-69ad-d503-9620-79b4507acc38.png)

ユーザーが ROSA 上に作成したアプリケーションは、CLB を通して公開されます。この図では「Application CLB1 (*.apps.<cluster-domain>) 80/443」と書いてある CLB が全てのアプリケーションのインターネット向けのインターフェイスとして機能します。

この CLB は、デフォルトで *.`apps`.`[clusterドメイン]` というドメインを持っています。

ここで言う `[clusterドメイン]`は、ROSA のクラスター作成時に Red Hat から提供される `rosa-cluster.70d7.p1.openshiftapps.com` のようなデフォルトのドメインです。


# 内向きのロードバランサーの作成

ROSA では、アプリケーション用の 標準の CLB に加えて追加の CLBをデプロイする事ができます。
この追加の CLBによって以下の図の赤線のようなアクセス経路で使用できるアプリを可能にします。

![Copy of ROSA Basic 2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99425/67d07628-92b5-ce71-214c-64a81bc35def.png)


追加のCLBは以下のコマンドで追加できます。
ここでは内向き(インターネット非公開）にしたいので、--private のオプションを付けます。

```
$ rosa create  ingress --private -c rosa-cluster
```

実際にデプロイされるのは、追加の CLBだけでなく付随する Pod やリソースもデプロイされます。これは全体として Kubernetes の Ingress を構成するので、`Ingress` と呼ばれます。


作成された `Ingress` は `rosa list ingresses -c <クラスタ名>` で確認できます。

```
$ rosa list ingresses -c rosa-cluster
ID    APPLICATION ROUTER                                         PRIVATE    DEFAULT    ROUTE SELECTORS
z9l9  https://apps.rosa-cluster.70d7.p1.openshiftapps.com       no         yes
u2r0  https://apps2.rosa-cluster.70d7.p1.openshiftapps.com      yes        no
$ 
```

一方の `PRIVAE` フラグが `yes` になっています。これが追加されたインターネットに公開されてない CLB です。

これらのリストされているドメイン名の実態は CLBです。この追加の CLB を作成する事で、

外向き(インターネットに公開された) アプリケーションは、`apps`.`[clusterドメイン]` を使って公開さいれます。実際のアプリには **myapp**.`apps`.`[clusterドメイン]` のようなドメイン名になります。

内向き(VPC1 の Private Network に公開された) アプリケーションは、`apps2`.`[clusterドメイン]` を使って公開さいれます。実際のアプリには **myapp**.`apps2`.`[clusterドメイン]` のようなドメイン名になります。


# 外向けのアプリケーションをデプロイする

新しいプロジェクト `extnernal-app` を作成して、hello-openshift というサンプル Web アプリをデプロイします。

```
oc new-project external-app
oc new-app --docker-image=docker.io/openshift/hello-openshift
```

この操作で hello-openshift と言うサービスができます。

```
$ oc get svc
NAME              TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)             AGE
hello-openshift   ClusterIP   172.30.158.246   <none>        8080/TCP,8888/TCP   1h26m
$ 
```


作成されたサービスを公開します

```
oc create route edge --service=hello-openshift myapp
```

作成された route を確認します。

```
$ oc get route
NAME    HOST/PORT                                                                    PATH   SERVICES          PORT       TERMINATION   WILDCARD
myapp   myapp-external-app.apps.rosa-cluster.70d7.p1.openshiftapps.com ... 1 more          hello-openshift   8080-tcp   edge          None
$ 
```

### 外向けアプリのアクセス確認

curl でアクセスできるか確認します。

```
$ curl https://myapp-external-app.apps.rosa-cluster.70d7.p1.openshiftapps.com
Hello OpenShift! 
$ 
```

無事アクセスできました。



# 内向けのアプリケーションをデプロイする


新しいプロジェクト `internal-app` を作成して、同じ hello-openshift というサンプル Web アプリをデプロイします。

```
oc new-project internal-app
oc new-app --docker-image=docker.io/openshift/hello-openshift
```

この操作で hello-openshift と言う Service が作成されます。

```
$ oc get svc
NAME              TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)             AGE
hello-openshift   ClusterIP   172.30.63.102   <none>        8080/TCP,8888/TCP   1h11m
$ 
```

次に作成されたサービスを公開します。

が、外向きの CLB のドメインがdefault で使用されるようになっているので、`--hostname` オプションを使用して内向きの CLB のドメインを使った Host 名を明示的に指定します。

```
 $ oc create route edge --service=hello-openshift myapp  --hostname myapp-internal-app.apps2.rosa-cluster.70d7.p1.openshiftapps.com
```

`--hostname`  の指定では、内向きの CLB のドメイン `apps2.rosa-cluster.70d7.p1.openshiftapps.com` より前の名前は、自分で好きな名前を付ける事ができます。ここでは、**myapp-internal-app** としています。

作成された route を確認します。

```
$ oc get route
NAME    HOST/PORT                                                                     PATH   SERVICES          PORT       TERMINATION   WILDCARD
myapp   myapp-internal-app.apps2.rosa-cluster.70d7.p1.openshiftapps.com ... 1 more          hello-openshift   8080-tcp   edge          None
$ 
```

curl で、アクセスできるか確認します。そのままでは、アクセスできないはずです。

```
$ curl https://myapp-internal-app.apps2.rosa-cluster.70d7.p1.openshiftapps.com
curl: (7) Failed to connect to myapp-internal-app.apps2.rosa-cluster.70d7.p1.openshiftapps.com port 443: Connection timed out
$
```

ドメイン名のIPアドレスを引いてみます。

```
$ dig +short  myapp-internal-app.apps2.rosa-cluster.70d7.p1.openshiftapps.com
10.7.73.35
10.7.82.57
$ 
```

内向けのドメイン名からは RFC1918 で定義されている Private Network に予約されたレンジの IPアドレスが返ってきます。このIPアドレスは、インターネットからたどり着けません。

この IPアドレスは、内向きのCLBの IPアドレスなので VPC1内のPrivate Network からならアクセスできるはずです。

### 内向けアプリのアクセス確認

VPC1内の Private Network から同じドメイン名にアクセスしてみます。
(ここからの手順は「<a href="https://qiita.com/Yuhkih/private/265eaab3f98c51b97a79#vpc1-%E3%81%AE-private-subnet-%E3%81%8B%E3%82%89-vpc2%E3%81%AE-public-subnet-%E3%81%B8%E3%81%AE%E9%80%9A%E4%BF%A1%E3%81%AE%E8%A8%AD%E5%AE%9A"  target=”_blank”>VPC1 の Private Subnet から VPC2の Public Subnet への通信の設定</a>」でルーティングを設定してある事が前提です。)

```
# はじめに Public Network 内の Bastion にログインします。

$ export PUBLIC_BASTION=13.231.221.123
$ ssh -i BastionKeyPair.pem ec2-user@$PUBLIC_BASTION
Last login: Wed Oct 26 22:56:52 2022 from ...

       __|  __|_  )
       _|  (     /   Amazon Linux 2 AMI
      ___|\___|___|

...

[ec2-user@ip-10-8-129-192 ~]$
# 次に Private Network 内の Bastion にログインします。

[ec2-user@ip-10-8-129-192 ~]$ export PRIVATE_BASTION=10.8.63.146
[ec2-user@ip-10-8-129-192 ~]$ ssh -i BastionKeyPair.pem ec2-user@$PRIVATE_BASTION
Last login: Wed Oct 26 22:57:05 2022 from ip-10-8-129-192.ap-northeast-1.compute.internal

       __|  __|_  )
       _|  (     /   Amazon Linux 2 AMI
      ___|\___|___|

...
[ec2-user@ip-10-8-63-146 ~]$ 
```

ここから curl を実行してみます。

```
[ec2-user@ip-10-8-63-146 ~]$ curl https://myapp-internal-app.apps2.rosa-cluster.70d7.p1.openshiftapps.com
Hello OpenShift!
[ec2-user@ip-10-8-63-146 ~]$
```

Private Netowork から 無事アクセスできました。

# Security に関する考慮

curl では、-H オプションを指定する事で Host ヘッダーを指定する事ができます。

実は、今回の構成では、内部向けのアプリケーションのドメイン名を知っている場合、以下のように外向きのCLBに内向きのアプリのドメイン名を指定する事でアクセスができてしまいます。

以下は、わかりやすさのために、これまで使ってきたアプリと別のアプリを使った実験ですが、同じドメインにアクセスしていても、-H で Host ヘッダーを変更するだけで、別のCLB 経由ホストされているはずの別のアプリにアクセスに行ってしまうため結果が変わっています。

```
[ec2-user@ip-10-8-63-146 ~]$ curl https://hello-api2.apps.rosa-cluster.70d7.p1.openshiftapps.com 
{ "Message": "Hello API!" }
{ "access": "Internal" }
[ec2-user@ip-10-8-63-146 ~]$ curl https://hello-api2.apps.rosa-cluster.70d7.p1.openshiftapps.com -H Host:hello-api1.apps2.rosa-cluster.70d7.p1.openshiftapps.com
{ "Message": "Hello API!" }
{ "access": "External" }
```

外向きのCLB から HTTP リクエストを受け取った ROSA 上の Ingress Pod (router) は、リクエストに付随する HTTP Hostヘッダーと一致する値の spec.host 値を持った Route を探し、そこに記載されているサービス(Pod) にトラフィックをフォワードします。

デフォルトでは、外向きの CLB から入って来たリクエストは、ROSA上にデプロイされているどの Route オブジェクトにもアクセスできるようになっているため、この現象が起こります。

これを防ぐには

1. 外向きの アプリケーション の CLB からのリクエストは、特定のラベル (例：access=external)を持った Route だけを処理するように Route Selector を設定する
2. 外向きの アプリケーション の Route にラベル (例：access=external)を付ける

という作業が必要になります。

最初は Ingress  `ROUTE SELECTORS` の欄は空欄です。

```
$ rosa list ingress -c rosa-cluster
ID    APPLICATION ROUTER                                         PRIVATE    DEFAULT    ROUTE SELECTORS
z9l9  https://apps.rosa-cluster.70d7.p1.openshiftapps.com       no         yes
u2r0  https://apps2.rosa-cluster.70d7.p1.openshiftapps.com      yes        no
$ rosa list ingress -c rosa-cluster
```

外向きの Ingress に Route Selector の設定を行います。

```
$ rosa edit ingress z9l9 -c rosa-cluster --label-match=access=external
E: Failed to update ingress 'z9l9' on cluster 'rosa-cluster': Can't add route selectors to default ingress 'z9l9' in cluster '1vj97skq8vbe14qgks2e6vahjskeccj0'
$ 
```

が、上記のようにエラーになりました。**この Route Selector の設定作業ができるのは追加で作成した Ingress だけで default で作成されている Ingress には適用できないようです** 

そのため、セキュリティを考慮すると Ingress の向きを、現在の構成と逆向きにする必要があります。

一方でデメリットとして **OpenShift の Webコンソールは default の Ingress を利用しているこの作業によってインターネットからはアクセスできなくなります。** 

~~今回の構成では、Web Consoleからはインターネットからアクセスできなくても、CLIは引き続きインターネット経由でアクセス可能なので利便性にそれほど影響しないかもしれません。~~ → ログイン用のapi は確かにインターネットに向いているのですが、CLIでのログインができなくっていました。

```
$ oc login https://api.rosa-cluster.70d7.p1.openshiftapps.com:6443 --username cluster-admin --password 1234-HhAPB-ihneH-un5db
Unable to connect to the server: dial tcp 10.7.35.213:443: connect: connection timed out
```

Connect できないとエラーメッセージに出ている`10.7.35.213:443` は、内向きに設定した *apps* を提供するCLB のIPアドレスでした。
恐らくですが、`oc login` 実行後、OAuth サーバー (*oauth-openshift*.**apps**.[cluster domain]) にリダイレクトされたものの、OAuthサーバーが OpenShift Console と同じ内向きの **apps**ドメインで提供されているため、OAuthサーバーにアクセスできずエラーになっているのだろうと推察しています。

従って、**apps** ドメインを内向きにした時は、CLI 側の受け口 (*api*.[cluster domain]) も内向きに切り替えておくのが良さそうです。この設定は [Hybrid Cloud Console](https://console.redhat.com/) 上からへんこう可能です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99425/6e84586a-1f48-50bf-c778-ccf2fecaed79.png)
この設定は OpenShift の Web Console の向きと合わせる必要があると考えておいた方が良さそうです。

## Ingress の向きを反転させる

:::note warn
個人的な経験上、Ingress の内向き / 外向きの切り替えを何度もやると壊れるケースがあります。
裏ではCLB再作成やDNSの切り替えが行われる一方、CLIの確認ではすぐに作業が反映されてしまうので、余裕をもった作業を行うと良いと思います。エキスパートな方は、AWS Wec Console 上から Route 53 のレコードの切りかわりや、CLB の作成具合を確認するのが良いと思います。
:::

既存の状態を確認します。

```
$ rosa list ingress -c rosa-cluster
ID    APPLICATION ROUTER                                         PRIVATE    DEFAULT    ROUTE SELECTORS
z9l9  https://apps.rosa-cluster.70d7.p1.openshiftapps.com       no         yes
u2r0  https://apps2.rosa-cluster.70d7.p1.openshiftapps.com      yes        no         access=internal
$
```

default の Ingress を 内向き (private) に変更します。

```
$ rosa edit ingress -c rosa-cluster z9l9 --private  
I: Updated ingress 'z9l9' on cluster 'rosa-cluster'
$
```

追加した Ingress を一旦、削除します。

```
$ rosa delete ingress -c rosa-cluster u2r0 
? Are you sure you want to delete ingress u2r0 on cluster rosa-cluster? Yes
I: Successfully deleted ingress 'u2r0' from cluster 'rosa-cluster'
$ 
```

追加 Ingress を新規に再作成します。(前の Ingress が暫く残っているので時間を置きます。AWS Console から以前のCLBやRoute53 のエントリーが消えたのを確認すると尚、良いです）

Ingress 作成時に、Route Selector として `access=external` を指定します。

```
$ rosa create ingress --cluster=rosa-cluster --label-match=access=external
I: Ingress has been created on cluster 'rosa-cluster'.
I: To view all ingresses, run 'rosa list ingresses -c rosa-cluster'
$ 
```

できあがった状態を確認します。

```
$ rosa list ingress -c rosa-cluster
ID    APPLICATION ROUTER                                         PRIVATE    DEFAULT    ROUTE SELECTORS
z9l9  https://apps.rosa-cluster.70d7.p1.openshiftapps.com       yes        yes
t5a6  https://apps2.rosa-cluster.70d7.p1.openshiftapps.com      no         no         access=external
$ 
```

## Route Selector の動作を確認する

### 過去の Project を削除して再デプロイする

過去に作ったサンプルアプリを削除してデプロイしなおします。
project 毎、削除する事で関連付いたオブジェクトを全て消す事が可能です。

```
openshift-cluster-machine-approver                                Active
$ oc delete project external-app
project.project.openshift.io "external-app" deleted
$ oc delete project internal-app
project.project.openshift.io "internal-app" deleted
$ 
```

### 外向けのアプリケーションをデプロイする

```
$ oc new-project external-app
$ oc new-app --docker-image=docker.io/openshift/hello-openshift
```

作成されたサービスを公開するために Route を作成します。--hostname を使って外向きのドメインを指定します。(現状は default は内向きドメインなので何も指定しないと内向きになります）

```
$ oc create route edge --service=hello-openshift myapp --hostname myapp-external-app.apps2.rosa-cluster.70d7.p1.openshiftapps.com 
```

作成された route を確認します

```
$ oc get route
NAME    HOST/PORT                                                          PATH   SERVICES          PORT       TERMINATION   WILDCARD
myapp   myapp-external-app.apps2.rosa-cluster.70d7.p1.openshiftapps.com          hello-openshift   8080-tcp   edge          None
$ 
```

curl でアクセスしてみます。失敗するはずです。

```
$ curl https://myapp-external-app2.apps.rosa-cluster.70d7.p1.openshiftapps.com  
<html>
  <head>
    <meta name="viewport" content="width=device-width, initial-scale=1">
<エラーメッセージの HTML ページのソースが続く>
$ 
```

外向きの Ingress は、 `access: external`  のラベルを持った Route だけを処理するようになっています。
このラベルが Route に付いてないのが、アクセスできない理由です。

Route を編集してラベルを付けます。

```shell
$ oc edit route myapp
# Please edit the object below. Lines beginning with a '#' will be ignored,
# and an empty file will abort the edit. If an error occurs while saving this file will be
# reopened with the relevant failures.
#
apiVersion: route.openshift.io/v1
kind: Route
metadata:
  creationTimestamp: "2022-10-27T02:25:11Z"
  labels:
    access: external        # ラベルを追加
    app: hello-openshift
    app.kubernetes.io/component: hello-openshift
    app.kubernetes.io/instance: hello-openshift
  name: myapp
  namespace: external-app
  ...
$ 
```

もう一度 curl でアクセスしてみます。

```
$  curl https://myapp-external-app.apps2.rosa-cluster.70d7.p1.openshiftapps.com
Hello OpenShift!
$ 
```

今度は成功しました。

これで、外からのアクセスは、外部に公開すると明確に意図を持った(特定のラベルを貼った) Route だけにアクセスする事ができるようになっている事がわかります。

default のロードバランサーを内向きに変えた事で以下のようなアクセスパスになりました。
管理者用の Web Console も CLI も Private Subnet からアクセスする必要があります。
![Copy of ROSA Basic 2-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99425/1300f514-ef54-60be-11bb-c40293c05a31.png)