Tanzu Mission Controlの監査ログをsyslogに転送する

Tanzu Mission Control（以下TMC）は各種操作ログを監査ログとして残しているが、外部に転送する手段を直接的には持っていない。
ただ、APIを利用して取得することが出来るので、今回はAPIを使って取得してsyslogに転送できるか検証する。

## syslogサーバの用意
検証用にsyslogサーバをKubernetes上のPodとして用意する。
以前書いた[Tanzu Kubernete Grid上にTanzu PackageでFluent Bitをインストール＆動作確認をする](https://qiita.com/ipppppei/items/fa90e7efddf61ae63594)という記事の中で立て方をまとめてるので、こちらをなぞって起動する。

```sh
$ kubectl get all -n rsyslog
NAME          READY   STATUS    RESTARTS   AGE
pod/rsyslog   1/1     Running   0          2m38s

NAME              TYPE           CLUSTER-IP     EXTERNAL-IP      PORT(S)         AGE
service/rsyslog   LoadBalancer   100.65.22.38   10.151.199.248   514:30334/TCP   2m39s
```
動作確認もしておく。
```sh
$ docker run --log-driver syslog --log-opt syslog-address=tcp://$(kubectl get svc -o jsonpath={.status.loadBalancer.ingress[0].ip} rsyslog -n rsyslog):514 alpine echo hello world
Unable to find image 'alpine:latest' locally
latest: Pulling from library/alpine
f56be85fc22e: Pull complete
Digest: sha256:124c7d2707904eea7431fffe91522a01e5a861a624ee31d03372cc1d138a3126
Status: Downloaded newer image for alpine:latest
hello world
```
```sh
$ kubectl exec -it rsyslog -n rsyslog -- cat /var/log/remote.log
2023-04-12T05:33:24+00:00 100.96.2.1 f3908a049a8e[893]: hello world
```
## 検証
サンプルが[こちら](https://github.com/vmwarecode/Tanzu-Mission-Control-Event-Stream-Service-Call)にあるため、これをそのまま利用する。

### 手動によるログ取得
TMCのTokenを取得する。取得方法は
[Tanzu Mission ControlのCLIで既存Kubernetesクラスタの登録と新規クラスタ作成を試す](https://qiita.com/ipppppei/items/14d2302a89b263a97266#cli%E3%81%AE%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB%E3%81%A8%E5%88%9D%E6%9C%9F%E8%A8%AD%E5%AE%9A)という記事を参照。
tmcコマンドを利用している人は`~/.vmware-cna-saas/contexts/<コンテキスト名>`というファイルの中の`refresh_token`にも格納されているので、忘れている場合はそちらを参照するとよい。
取得したトークンと、TMCのURLを以下でセットする。
```
export TMC_URL="<org>.tmc.cloud.vmware.com"
export CSP_TOKEN="<取得したトークン>"
```
続いて設定した環境変数ベースで、VMware CloudからTMCへのアクセストークンを取得し、それを元にcurlで実際にログを取得してみる。
※追記：
引用元のAPIではBodyに`refresh_token`を使っており、当初は合わせてそちらを使っていたが、[APIリファレンス](https://console.cloud.vmware.com/csp/gateway/authn/api/swagger-ui.html#/Authentication/getAccessTokenByApiRefreshTokenUsingPOST)を見ると`refresh_token`がdeprecatedになっており、`api_token`が推奨されていたので`api_token`を使うように変更した。
```
export ACCESS_TOKEN=$(curl -s -X POST "https://console.cloud.vmware.com/csp/gateway/am/api/auth/api-tokens/authorize" \
-H "Content-Type: application/x-www-form-urlencoded" \
--data-urlencode "api_token=$CSP_TOKEN" | jq -r '.access_token')

curl -s -H "Authorization: Bearer $ACCESS_TOKEN" \
https://${TMC_URL}/v1alpha1/events/stream
```
curlを実行すると、待ち受け状態となる。この状態でTMCにアクセスすると、端末に監査ログが表示される。
```
{"result":{"event":{"id":"32fb2b44-411f-4688-a621-c74210e0a091","time":"2023-04-12T05:49:42.161211600Z","type":"com.vmware.tmc.audit","source":"api-gateway", ...
```
なお、Tokenの寿命が短いようで、少し放置するとアクセスできなくなる。
```sh
curl -s -H "Authorization: Bearer $ACCESS_TOKEN" \
> https://${TMC_URL}/v1alpha1/events/stream
{"error":{"grpcCode":16,"httpCode":401,"message":"No valid authentication credentials","httpStatus":"Unauthorized"}}
```
なので、定期的にTokenを更新する必要がありそうだ。

### 自動化
スクリプトを書いて転送してもよいが、面倒なのでfluent-bitに転送してもらう。
なお、ここではfluent-bitの構築は終わっているものとする。構築方法が必要な場合、TKG1.6.0なら[こちら](https://qiita.com/ipppppei/items/fa90e7efddf61ae63594)、TKG2.1.0なら[こちら](https://qiita.com/ipppppei/items/f4ff27aa55ee30a4823e)にメモを残しているので必要に応じて利用するとよい。

先程の手順をManifest化する。最初にSecretを作成する。
```sh
kubectl create secret generic tmc-log-forwarder --from-literal=TMC_URL=$TMC_URL --from-literal=CSP_TOKEN=$CSP_TOKEN -o yaml --dry-run=client > tmc-log-forwarder-secret.yaml
```
ACCESS_TOKENは有効期限が短いため、Secret化せずに定期的に取得する。
次にDeploymentとConfigMapを作成する。centosイメージにjqをインストールしてwhile文でcurlを実行し続けている。
```yaml
cat <<'EOF' > ./tmc-log-forwarder.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: tmc-forwarder-script
data:
  tmc-forwarder.sh: |
    #!/bin/bash
    curl -OL https://github.com/stedolan/jq/releases/download/jq-1.6/jq-linux64
    install jq-linux64 /usr/local/bin/jq
    while true; do
      export ACCESS_TOKEN=$(curl -s -X POST "https://console.cloud.vmware.com/csp/gateway/am/api/auth/api-tokens/authorize" -H "Content-Type: application/x-www-form-urlencoded" --data-urlencode "api_token=$CSP_TOKEN" | jq -r '.access_token')
      curl -s -H "Authorization: Bearer $ACCESS_TOKEN" https://${TMC_URL}/v1alpha1/events/stream
    done
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: tmc-log-forwarder
spec:
  selector:
    matchLabels:
      app: tmc-log-forwarder
  replicas: 1
  template:
    metadata:
      labels:
        app: tmc-log-forwarder
    spec:
      containers:
      - command:
        - /bin/bash
        - /mnt/tmc-forwarder.sh
        image: centos
        name: centos
        env:
        - name: TMC_URL
          valueFrom:
            secretKeyRef:
              name: tmc-log-forwarder
              key: TMC_URL
        - name: CSP_TOKEN
          valueFrom:
            secretKeyRef:
              name: tmc-log-forwarder
              key: CSP_TOKEN
        volumeMounts:
        - name: script
          mountPath: /mnt
      volumes:
      - name: script
        configMap:
          name: tmc-forwarder-script
          items:
            - key: tmc-forwarder.sh
              path: tmc-forwarder.sh
EOF
```
作成したManifestをapplyする。
```sh
kubectl apply -f ./
```
以下のような感じでログが取れている。
```sh
$ kubectl logs deployment/tmc-log-forwarder
：（省略）
{"result":{"event":{"id":"3a638df2-2eb2-450a-946d-c540463da5c1","time":"2023-04-12T09:55:44.840866835Z","type":"com.vmware.tmc.cluster.health", ...
```
syslog側も確認する。
```sh
$ kubectl exec -n rsyslog -it rsyslog -- grep event /var/log/remote.log
：（省略）
2023-04-12T10:51:11.113970Z - - {"result":{"event":{"id":"d751334a-3c34-45e8-99b4-939fa9a967da","time":"2023-04-12T10:50:46.045240491Z","type":"com.vmware.tmc.cluster.health", ...
```
TMCの監査ログが転送できているようだ。