mosquitto on kubernetesでmqttブローカーを構築

# 参考にしたページ

[ここ](https://code.sharkbaitextraordinaire.com/jacob/kube-manifests) でシェアされてるmosquittoのmanifest


# やりたいこと

* kubernetesクラスタ上にmosquittoをデプロイ
* mqttsのサービスを公開
* mosquittoのクライアント認証を有効して、クライアント証明書がないとアクセスできないようにする

# やること

## 証明書類の作成

[ここ](https://medium.com/cloudnesil/securing-mqtt-broker-on-kubernetes-5503420b84a8) の記事で公開されてるmake-keys.shが楽で便利

```make-keys.sh
#!/bin/bash

IP="example.com"
SUBJECT_CA="/C=TR/ST=Istanbul/L=Istanbul/O=example/OU=CA/CN=$IP"
SUBJECT_SERVER="/C=TR/ST=Istanbul/L=Istanbul/O=example/OU=Server/CN=$IP"
SUBJECT_CLIENT="/C=TR/ST=Istanbul/L=Istanbul/O=example/OU=Client/CN=$IP"

function generate_CA () {
   echo "$SUBJECT_CA"
   openssl req -x509 -nodes -sha256 -newkey rsa:2048 -subj "$SUBJECT_CA"  -days 365 -keyout ca.key -out ca.crt
}

function generate_server () {
   echo "$SUBJECT_SERVER"
   openssl req -nodes -sha256 -new -subj "$SUBJECT_SERVER" -keyout server.key -out server.csr
   openssl x509 -req -sha256 -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt -days 365
}

function generate_client () {
   echo "$SUBJECT_CLIENT"
   openssl req -new -nodes -sha256 -subj "$SUBJECT_CLIENT" -out client.csr -keyout client.key 
   openssl x509 -req -sha256 -in client.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out client.crt -days 365
}

##function copy_keys_to_broker () {
##   sudo cp ca.crt /etc/mosquitto/certs/
##   sudo cp server.crt /etc/mosquitto/certs/
##   sudo cp server.key /etc/mosquitto/certs/
##}

generate_CA
generate_server
generate_client
#copy_keys_to_broker
```

ファイルの冒頭で定義してるサブジェクトの中身は環境に合わせて書き換える。
certsみたいなディレクトリを作って実行するとキーペアと証明書が出力される。

```bash
$ ls -1 certs
ca.crt
ca.key
ca.srl
client.crt
client.csr
client.key
make-keys.sh
server.crt
server.csr
server.key
```

こんな感じ。

## Serviceを作成

```mosquitto.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: mosquitto
  name: mosquitto
spec:
  replicas: 1
  selector:
    matchLabels:
      app: mosquitto
  template:
    metadata:
      labels:
        app: mosquitto
    spec:
      volumes:
      - name: mosquitto-cfg
        projected:
          sources:
          - secret:
              name: mosquitto-tls
              items:
              - key: ca-certificate
                path: ca.crt
              - key: server-certificate
                path: server.crt
              - key: server-key
                path: server.key
          - configMap:
              name: mosquitto-config
              items:
              - key: mosquitto.conf
                path: mosquitto.conf
      - name: mosquitto-data
        emptyDir: {}
      containers:
      - name: mosquitto
        image: eclipse-mosquitto:latest
        imagePullPolicy: IfNotPresent
        ports:
        - name: mqtt
          containerPort: 8883
        volumeMounts:
        - mountPath: /mosquitto/config
          name: mosquitto-cfg
          readOnly: true
        - mountPath: /mosquitto/data
          name: mosquitto-data

---

apiVersion: v1
kind: Service
metadata:
  name: mosquitto
spec:
  ports:
  - name: mqtt
    protocol: TCP
    port: 8883
    targetPort: 8883
  selector:
    app: mosquitto
  type: LoadBalancer
 ```

参考もとはパスワード認証だったのでパスワード認証回りの設定を削除した。
MetalLBを使っているのでLoadBalancerで設定。NodePortとかでも大丈夫。ここは環境に合わせて。

## ConfigMapを作成

```mosquitto-config.yaml
kind: ConfigMap
apiVersion: v1
metadata:
  name: mosquitto-config
data:
  mosquitto.conf: |
    listener 8883
    # Mosquitto configuration file
    # cafile is mounted from a secret
    cafile /mosquitto/config/ca.crt
    # certfile is mounted from a secret
    certfile /mosquitto/config/server.crt
    # keyfile is mounted from a secret
    keyfile /mosquitto/config/server.key
    tls_version tlsv1.2
    require_certificate true
    use_identity_as_username true
    # Disallow weak ciphers
    ciphers DEFAULT:!aNULL:!eNULL:!LOW:!EXPORT:!SSLv2:@STRENGTH
```

ここもパスワード認証関連の設定を削除して、クライアント認証を有効にする。

## 証明書を登録

```bash
$ cd certs
$ kubectl create secret generic mosquitto-tls \
  --from-file=ca-certificate=./ca.crt \
  --from-file=server-certificate=./server.crt \
  --from-file=server-key=./server.key
```

作成した証明書類をsecretで登録。

## デプロイ

作成したmanifestを反映させる。

```bash
$ kubectl apply -f ./mosquitto-config.yaml
$ kubectl apply -f ./mosquitto.yaml
```

## 外部IPが割り当てられていることを確認

```bash
$ kubectl get services
NAME         TYPE           CLUSTER-IP     EXTERNAL-IP   PORT(S)          AGE
kubernetes   ClusterIP      10.96.0.1      <none>        443/TCP          11h
mosquitto    LoadBalancer   10.111.34.69   10.0.1.10     8883:32376/TCP   3h31m
```

## 動作確認

```bash
$ kubectl port-forward service/mosquitto 8883:8883
```

```bash
$ mosquitto_pub -d -h 127.0.0.1 -t "topicName" -p 8883 --cafile ./certs/ca.crt --cert ./certs/client.crt --key ./certs/client.key -m test
Client mosq-t0DaMzdZlBflQ3v18S sending CONNECT
Client mosq-t0DaMzdZlBflQ3v18S received CONNACK (0)
Client mosq-t0DaMzdZlBflQ3v18S sending PUBLISH (d0, q0, r0, m1, 'topicName', ... (4 bytes))
Client mosq-t0DaMzdZlBflQ3v18S sending DISCONNECT
```