diff --git a/README.md b/README.md index 9f858ce12..e9ca0a18d 100644 --- a/README.md +++ b/README.md @@ -78,6 +78,8 @@ $ kubectl create ns botkube && kubectl create -f deploy-all-in-one.yaml -n botku - Check pod status in botkube namespace. Once running, send **@BotKube ping** in the Slack channel to confirm if BotKube is responding correctly. +- To deploy with TLS, download and use deploy-all-in-one-tls.yaml. Replace **ENCODED_CERTIFICATE** with your base64 encoded certificate value in the secret. + ## Add BotKube to your Mattermost team Before adding `BotKube` to your Mattermost team, please make sure you have all the pre-requisites working. @@ -92,6 +94,7 @@ Before adding `BotKube` to your Mattermost team, please make sure you have all t In the helm chart, config.yaml or deploy-all-in-one.yaml update the following fields for enabling Mattermost support and providing Mattermost config parameters. **MATTERMOST_SERVER_URL** is the URL where Mattermost is running
+**MATTERMOST_CERT** is the SSL certificate file for HTTPS connection. Place it in Helm directory and specify the path
**MATTERMOST_TOKEN** is the Token you received after installing BotKube user and creating Personal Access Token
**MATTERMOST_TEAM** is the team name where BotKube will be added
**MATTERMOST_CHANNEL** is the channel name where BotKube will be added
@@ -102,6 +105,7 @@ In the helm chart, config.yaml or deploy-all-in-one.yaml update the following fi $ helm install --name botkube --namespace botkube \ --set config.communications.mattermost.enabled=true \ --set config.communications.mattermost.url={MATTERMOST_SERVER_URL} \ +--set config.communications.mattermost.cert={MATTERMOST_CERT} \ --set config.communications.mattermost.token={MATTERMOST_TOKEN} \ --set config.communications.mattermost.team={MATTERMOST_TEAM} \ --set config.communications.mattermost.channel={MATTERMOST_CHANNEL} \ diff --git a/deploy-all-in-one-tls.yaml b/deploy-all-in-one-tls.yaml new file mode 100644 index 000000000..2910948b0 --- /dev/null +++ b/deploy-all-in-one-tls.yaml @@ -0,0 +1,253 @@ +--- +# Configmap +apiVersion: v1 +kind: ConfigMap +metadata: + name: botkube-configmap + labels: + app: botkube +data: + config.yaml: | + ## Resources you want to watch + resources: + - name: pods # Name of the resources e.g pods, deployments, ingresses, etc. (Resource name must be in plural form) + namespaces: # List of namespaces, "all" will watch all the namespaces + - all + events: # List of lifecycle events you want to receive, e.g create, update, delete OR all + - create + - delete + - name: services + namespaces: + - all + events: + - create + - delete + - name: deployments + namespaces: + - all + events: + - create + - delete + - name: ingresses + namespaces: + - all + events: + - create + - delete + - name: nodes + namespaces: + - all + events: + - create + - delete + - name: namespaces + namespaces: + - all + events: + - create + - delete + - name: persistentvolumes + namespaces: + - all + events: + - create + - delete + - name: persistentvolumeclaims + namespaces: + - all + events: + - create + - delete + - name: secrets + namespaces: + - all + events: + - create + - delete + - name: configmaps + namespaces: + - all + events: + - create + - delete + - name: daemonsets + namespaces: + - all + events: + - create + - delete + - name: jobs + namespaces: + - all + events: + - create + - delete + - name: roles + namespaces: + - all + events: + - create + - delete + - name: rolebindings + namespaces: + - all + events: + - create + - delete + - name: clusterroles + namespaces: + - all + events: + - create + - delete + - name: clusterrolebindings + namespace: + - all + events: + - create + - delete + - name: nodes + namespaces: + - all + events: + - create + - delete + + # K8S error/warning events you want to receive for the configured resources + events: + types: + #- normal + - warning + + # Check true if you want to receive recommendations + # about the best practices for the created resource + recommendations: true + + # Channels configuration + communications: + # Settings for Slack + slack: + enabled: false + channel: 'SLACK_CHANNEL' + token: 'SLACK_API_TOKEN' + + # Settings for Mattermost + mattermost: + enabled: false + url: 'MATTERMOST_SERVER_URL' # URL where Mattermost is running. e.g https://example.com:9243 + token: 'MATTERMOST_TOKEN' # Personal Access token generated by BotKube user + team: 'MATTERMOST_TEAM' # Mattermost Team to configure with BotKube + channel: 'MATTERMOST_CHANNEL' # Mattermost Channel for receiving BotKube alerts + + # Settings for ELS + elasticsearch: + enable: false + server: 'ELASTICSEARCH_ADDRESS' # e.g https://example.com:9243 + username: 'ELASTICSEARCH_USERNAME' + password: 'ELASTICSEARCH_PASSWORD' + # ELS index settings + index: + name: botkube + type: botkube-event + shards: 1 + replicas: 0 + + # Setting to support multiple clusters + settings: + # Cluster name to differentiate incoming messages + clustername: not-configured + # Set false to disable kubectl commands execution + allowkubectl: false +--- +# serviceaccount +apiVersion: v1 +kind: ServiceAccount +metadata: + name: botkube-sa + labels: + app: botkube +--- +# Source: botkube/templates/clusterrole.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: botkube-clusterrole + labels: + app: botkube +rules: + - apiGroups: ["*"] + resources: ["*"] + verbs: ["get", "watch", "list"] +--- +# clusterrolebinding +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: botkube-clusterrolebinding + labels: + app: botkube +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: botkube-clusterrole +subjects: +- kind: ServiceAccount + name: botkube-sa + namespace: botkube +--- +# Secret +apiVersion: v1 +kind: Secret +metadata: + name: botkube-secret + labels: + app: botkube +data: + ca-certificates.crt: ENCODED_CERTIFICATE +--- +# deployment +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + name: botkube + labels: + component: controller + app: botkube +spec: + replicas: 1 + selector: + matchLabels: + component: controller + app: botkube + template: + metadata: + labels: + component: controller + app: botkube + spec: + serviceAccountName: botkube-sa + containers: + - name: botkube + image: "infracloud/botkube:0.7.0" + imagePullPolicy: Always + volumeMounts: + - name: config-volume + mountPath: "/config" + - name: certs + mountPath: "/etc/ssl/certs" + env: + - name: CONFIG_PATH + value: "/config/" + # set one of the log levels- info, warn, debug, error, fatal, panic + - name: LOG_LEVEL + value: "info" + # set BotKube release version + - name: BOTKUBE_VERSION + value: 0.7.0 + volumes: + - name: config-volume + configMap: + name: botkube-configmap + - name: certs + secret: + secretName: botkube-secret diff --git a/helm/botkube/templates/deployment.yaml b/helm/botkube/templates/deployment.yaml index 38b604fc4..818a68a4f 100644 --- a/helm/botkube/templates/deployment.yaml +++ b/helm/botkube/templates/deployment.yaml @@ -31,6 +31,10 @@ spec: volumeMounts: - name: config-volume mountPath: "/config" + {{- if ne .Values.config.communications.mattermost.cert "None" }} + - name: certs + mountPath: "/etc/ssl/certs" + {{ end }} env: - name: CONFIG_PATH value: "/config/" @@ -42,3 +46,8 @@ spec: - name: config-volume configMap: name: {{ include "botkube.fullname" . }}-configmap + {{- if ne .Values.config.communications.mattermost.cert "None" }} + - name: certs + secret: + secretName: {{ include "botkube.fullname" . }}-secret + {{ end }} diff --git a/helm/botkube/templates/secret.yaml b/helm/botkube/templates/secret.yaml new file mode 100644 index 000000000..8ba14e07a --- /dev/null +++ b/helm/botkube/templates/secret.yaml @@ -0,0 +1,15 @@ +{{- if ne .Values.config.communications.mattermost.cert "None" }} + +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "botkube.fullname" . }}-secret + labels: + app.kubernetes.io/name: {{ include "botkube.name" . }} + helm.sh/chart: {{ include "botkube.chart" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} +data: + ca-certificates.crt: {{ .Files.Get (printf "%s" .Values.config.communications.mattermost.cert) | b64enc }} + +{{ end }} diff --git a/helm/botkube/values.yaml b/helm/botkube/values.yaml index 42f9497bc..241bb83a5 100644 --- a/helm/botkube/values.yaml +++ b/helm/botkube/values.yaml @@ -142,6 +142,7 @@ config: mattermost: enabled: false url: 'MATTERMOST_SERVER_URL' # URL where Mattermost is running. e.g https://example.com:9243 + cert: None # SSL Certificate file. Leave it None for http connection. e.g certs/my-cert.crt token: 'MATTERMOST_TOKEN' # Personal Access token generated by BotKube user team: 'MATTERMOST_TEAM' # Mattermost Team to configure with BotKube channel: 'MATTERMOST_CHANNEL' # Mattermost Channel for receiving BotKube alerts diff --git a/pkg/mattermost/mattermost.go b/pkg/mattermost/mattermost.go index a5940bd7e..c6153ecdc 100644 --- a/pkg/mattermost/mattermost.go +++ b/pkg/mattermost/mattermost.go @@ -2,6 +2,7 @@ package mattermost import ( "fmt" + "net/url" "strings" "github.com/infracloudio/botkube/pkg/config" @@ -15,10 +16,10 @@ var client *model.Client4 const ( // BotName stores Botkube details BotName = "botkube" - // ChannelPurpose describes the purpose of Botkube channel - ChannelPurpose = "Botkube alerts" // WebSocketProtocol stores protocol initials for web socket - WebSocketProtocol = "ws:" + WebSocketProtocol = "ws://" + // WebSocketSecureProtocol stores protocol initials for web socket + WebSocketSecureProtocol = "wss://" ) // Bot listens for user's message, execute commands and sends back the response @@ -56,32 +57,37 @@ func NewMattermostBot() *Bot { } } -// Channel structure in Mattermost -func mmChannel(channelName, teamID string) *model.Channel { - return &model.Channel{ - Name: channelName, - DisplayName: channelName, - Purpose: ChannelPurpose, - Type: model.CHANNEL_OPEN, - TeamId: teamID, - } -} - // Start establishes mattermost connection and listens for messages func (b *Bot) Start() { client = model.NewAPIv4Client(b.ServerURL) client.SetOAuthToken(b.Token) + // Check if Mattermost URL is valid + checkURL, err := url.Parse(b.ServerURL) + if err != nil { + logging.Logger.Error("The Mattermost URL entered is incorrect. URL: ", b.ServerURL, "\nError: ", err) + return + } + // Check connection to Mattermost server - err := checkServerConnection() + err = checkServerConnection() if err != nil { logging.Logger.Error("There was a problem pinging the Mattermost server URL: ", b.ServerURL, "\nError: ", err) return } // Create WebSocketClient and handle messages - webSocketURL := WebSocketProtocol + strings.SplitN(b.ServerURL, ":", 2)[1] - webSocketClient, _ := model.NewWebSocketClient4(webSocketURL, client.AuthToken) + webSocketURL := WebSocketProtocol + checkURL.Host + if checkURL.Scheme == "https" { + webSocketURL = WebSocketSecureProtocol + checkURL.Host + } + var modelError *model.AppError + webSocketClient, modelError := model.NewWebSocketClient4(webSocketURL, client.AuthToken) + if modelError != nil { + logging.Logger.Error("Error creating WebSocket for Mattermost connectivity. \nError: ", modelError) + return + } + webSocketClient.Listen() go func() { for {