# Kubernetesにおけるサービスの公開

{{#include ../../banners/hacktricks-training.md}}

Kubernetesには、**内部**エンドポイントと**外部**エンドポイントの両方がアクセスできるようにサービスを公開する**さまざまな方法**があります。このKubernetesの設定は非常に重要であり、管理者が**攻撃者にアクセスしてはいけないサービスへのアクセスを与える**可能性があります。

### 自動列挙

K8sがサービスを公開する方法を列挙する前に、名前空間、サービス、イングレスをリストできる場合、次のコマンドを使用して公開されているすべてのものを見つけることができることを知っておいてください:
```bash
kubectl get namespace -o custom-columns='NAME:.metadata.name' | grep -v NAME | while IFS='' read -r ns; do
echo "Namespace: $ns"
kubectl get service -n "$ns"
kubectl get ingress -n "$ns"
echo "=============================================="
echo ""
echo ""
done | grep -v "ClusterIP"
# Remove the last '| grep -v "ClusterIP"' to see also type ClusterIP
```
### ClusterIP

**ClusterIP** サービスは、**デフォルト** の Kubernetes **サービス** です。これは、クラスター内の他のアプリがアクセスできる **クラスター内のサービス** を提供します。**外部アクセス** はありません。

しかし、これは Kubernetes プロキシを使用してアクセスできます：
```bash
kubectl proxy --port=8080
```
今、次のスキームを使用してKubernetes APIを介してサービスにアクセスできます：

`http://localhost:8080/api/v1/proxy/namespaces/<NAMESPACE>/services/<SERVICE-NAME>:<PORT-NAME>/`

例えば、次のURLを使用できます：

`http://localhost:8080/api/v1/proxy/namespaces/default/services/my-internal-service:http/`

このサービスにアクセスするために：
```yaml
apiVersion: v1
kind: Service
metadata:
name: my-internal-service
spec:
selector:
app: my-app
type: ClusterIP
ports:
- name: http
port: 80
targetPort: 80
protocol: TCP
```
_この方法では、**認証されたユーザー**として `kubectl` を実行する必要があります。_

ClusterIPの一覧を表示します:
```bash
kubectl get services --all-namespaces -o=custom-columns='NAMESPACE:.metadata.namespace,NAME:.metadata.name,TYPE:.spec.type,CLUSTER-IP:.spec.clusterIP,PORT(S):.spec.ports[*].port,TARGETPORT(S):.spec.ports[*].targetPort,SELECTOR:.spec.selector' | grep ClusterIP
```
### NodePort

**NodePort**が利用されると、すべてのノード（仮想マシンを表す）で指定されたポートが利用可能になります。この特定のポートに向けられた**トラフィック**は、体系的に**サービスにルーティング**されます。通常、この方法は欠点があるため推奨されません。

すべてのNodePortをリストします:
```bash
kubectl get services --all-namespaces -o=custom-columns='NAMESPACE:.metadata.namespace,NAME:.metadata.name,TYPE:.spec.type,CLUSTER-IP:.spec.clusterIP,PORT(S):.spec.ports[*].port,NODEPORT(S):.spec.ports[*].nodePort,TARGETPORT(S):.spec.ports[*].targetPort,SELECTOR:.spec.selector' | grep NodePort
```
NodePort仕様の例：
```yaml
apiVersion: v1
kind: Service
metadata:
name: my-nodeport-service
spec:
selector:
app: my-app
type: NodePort
ports:
- name: http
port: 80
targetPort: 80
nodePort: 30036
protocol: TCP
```
もしyamlで**nodePort**を**指定しない**場合（開かれるポートです）、**30000–32767の範囲内のポートが使用されます**。

### LoadBalancer <a href="#id-0d96" id="id-0d96"></a>

**クラウドプロバイダーのロードバランサーを使用して**サービスを外部に公開します。GKEでは、[Network Load Balancer](https://cloud.google.com/compute/docs/load-balancing/network/)が起動され、サービスへのすべてのトラフィックを転送する単一のIPアドレスが提供されます。AWSでは、ロードバランサーが起動されます。

公開されたサービスごとにロードバランサーの料金が発生し、高額になる可能性があります。

すべてのロードバランサーをリストします：
```bash
kubectl get services --all-namespaces -o=custom-columns='NAMESPACE:.metadata.namespace,NAME:.metadata.name,TYPE:.spec.type,CLUSTER-IP:.spec.clusterIP,EXTERNAL-IP:.status.loadBalancer.ingress[*],PORT(S):.spec.ports[*].port,NODEPORT(S):.spec.ports[*].nodePort,TARGETPORT(S):.spec.ports[*].targetPort,SELECTOR:.spec.selector' | grep LoadBalancer
```
### External IPs <a href="#external-ips" id="external-ips"></a>

> [!TIP]
> 外部IPは、Load Balancerタイプのサービスによって公開され、一般的に外部Cloud Provider Load Balancerが使用されるときに使用されます。
>
> それらを見つけるには、`EXTERNAL-IP`フィールドに値があるロードバランサーを確認してください。

クラスターに入るトラフィックが**外部IP**（**宛先IP**）で、サービスポートである場合、**サービスエンドポイントの1つにルーティングされます**。`externalIPs`はKubernetesによって管理されず、クラスター管理者の責任です。

サービス仕様では、`externalIPs`は任意の`ServiceTypes`と共に指定できます。以下の例では、"`my-service`"はクライアントによって"`80.11.12.10:80`"（`externalIP:port`）でアクセスできます。
```yaml
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
selector:
app: MyApp
ports:
- name: http
protocol: TCP
port: 80
targetPort: 9376
externalIPs:
- 80.11.12.10
```
### ExternalName

[**From the docs:**](https://kubernetes.io/docs/concepts/services-networking/service/#externalname) ExternalNameタイプのサービスは、**サービスをDNS名にマッピングします**。`my-service`や`cassandra`のような典型的なセレクタにはマッピングしません。これらのサービスは`spec.externalName`パラメータで指定します。

このサービス定義は、例えば、`prod`ネームスペースの`my-service`サービスを`my.database.example.com`にマッピングします：
```yaml
apiVersion: v1
kind: Service
metadata:
name: my-service
namespace: prod
spec:
type: ExternalName
externalName: my.database.example.com
```
`my-service.prod.svc.cluster.local`というホストを調べると、クラスターDNSサービスは値`my.database.example.com`を持つ`CNAME`レコードを返します。`my-service`へのアクセスは他のサービスと同様に機能しますが、重要な違いは**リダイレクションがDNSレベルで行われる**ということです。

すべてのExternalNamesをリストします:
```bash
kubectl get services --all-namespaces | grep ExternalName
```
### Ingress

上記のすべての例とは異なり、**Ingressはサービスの一種ではありません**。代わりに、**複数のサービスの前に位置し、「スマートルーター」として機能します**、またはクラスターへのエントリーポイントです。

Ingressを使用すると、さまざまなことができます。また、**異なる機能を持つ多くのタイプのIngressコントローラーがあります**。

デフォルトのGKE Ingressコントローラーは、[HTTP(S) Load Balancer](https://cloud.google.com/compute/docs/load-balancing/http/)を自動的に作成します。これにより、パスベースおよびサブドメインベースのルーティングをバックエンドサービスに対して行うことができます。たとえば、foo.yourdomain.comのすべてをfooサービスに送信し、yourdomain.com/bar/パスの下のすべてをbarサービスに送信できます。

GKE上の[L7 HTTP Load Balancer](https://cloud.google.com/compute/docs/load-balancing/http/)を使用したIngressオブジェクトのYAMLは次のようになります:
```yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: my-ingress
spec:
backend:
serviceName: other
servicePort: 8080
rules:
- host: foo.mydomain.com
http:
paths:
- backend:
serviceName: foo
servicePort: 8080
- host: mydomain.com
http:
paths:
- path: /bar/*
backend:
serviceName: bar
servicePort: 8080
```
すべてのイングレスをリストします:
```bash
kubectl get ingresses --all-namespaces -o=custom-columns='NAMESPACE:.metadata.namespace,NAME:.metadata.name,RULES:spec.rules[*],STATUS:status'
```
この場合、各情報を一つずつ取得して、より良く読む方が良いです：
```bash
kubectl get ingresses --all-namespaces -o=yaml
```
### 参考文献

- [https://medium.com/google-cloud/kubernetes-nodeport-vs-loadbalancer-vs-ingress-when-should-i-use-what-922f010849e0](https://medium.com/google-cloud/kubernetes-nodeport-vs-loadbalancer-vs-ingress-when-should-i-use-what-922f010849e0)
- [https://kubernetes.io/docs/concepts/services-networking/service/](https://kubernetes.io/docs/concepts/services-networking/service/)

{{#include ../../banners/hacktricks-training.md}}
