はじめに
ここでは ROSA 上でインターネットに公開するアプリと、内部ネットワーク向けに公開するアプリの2種類をホストする構成を作成します。
前提手順
ここでは以下の記事の順番で ROSA クラスターと周辺環境を構築している状況を前提としています。
これらの手順によって以下のような構成が作成されているはずです。
ユーザーが ROSA 上に作成したアプリケーションは、CLB を通して公開されます。この図では「Application CLB1 (*.apps.) 80/443」と書いてある CLB が全てのアプリケーションのインターネット向けのインターフェイスとして機能します。
この CLB は、デフォルトで *.apps
.[clusterドメイン]
というドメインを持っています。
ここで言う [clusterドメイン]
は、ROSA のクラスター作成時に Red Hat から提供される rosa-cluster.70d7.p1.openshiftapps.com
のようなデフォルトのドメインです。
内向きのロードバランサーの作成
ROSA では、アプリケーション用の 標準の CLB に加えて追加の CLBをデプロイする事ができます。
この追加の CLBによって以下の図の赤線のようなアクセス経路で使用できるアプリを可能にします。
追加の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 から同じドメイン名にアクセスしてみます。
(ここからの手順は「VPC1 の Private Subnet から VPC2の Public Subnet への通信の設定」でルーティングを設定してある事が前提です。)
# はじめに 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 オブジェクトにもアクセスできるようになっているため、この現象が起こります。
これを防ぐには
- 外向きの アプリケーション の CLB からのリクエストは、特定のラベル (例:access=external)を持った Route だけを処理するように Route Selector を設定する
- 外向きの アプリケーション の 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 上からへんこう可能です。
この設定は OpenShift の Web Console の向きと合わせる必要があると考えておいた方が良さそうです。
Ingress の向きを反転させる
個人的な経験上、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 を編集してラベルを付けます。
$ 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 からアクセスする必要があります。