From 7b6afb4d4d76423a83e0dad71ace1218e6cb94e9 Mon Sep 17 00:00:00 2001 From: Thomas Maier Date: Tue, 30 Jul 2019 09:51:20 +0200 Subject: [PATCH] Introduces Dockerfile for deployment and a Docker Compose stack (Prosody server + test clients) for development --- Dockerfile | 17 +++++ README.md | 7 ++ dev/xmpp-dev-stack/docker-compose.yml | 26 +++++++ dev/xmpp-dev-stack/prosody/Dockerfile | 6 ++ dev/xmpp-dev-stack/prosody/dev-server.sh | 26 +++++++ dev/xmpp-dev-stack/recipient/.gitignore | 2 + dev/xmpp-dev-stack/recipient/Dockerfile | 9 +++ dev/xmpp-dev-stack/recipient/go.mod | 9 +++ dev/xmpp-dev-stack/recipient/main.go | 87 ++++++++++++++++++++++++ go.mod | 2 + 10 files changed, 191 insertions(+) create mode 100644 Dockerfile create mode 100644 dev/xmpp-dev-stack/docker-compose.yml create mode 100644 dev/xmpp-dev-stack/prosody/Dockerfile create mode 100755 dev/xmpp-dev-stack/prosody/dev-server.sh create mode 100644 dev/xmpp-dev-stack/recipient/.gitignore create mode 100644 dev/xmpp-dev-stack/recipient/Dockerfile create mode 100644 dev/xmpp-dev-stack/recipient/go.mod create mode 100644 dev/xmpp-dev-stack/recipient/main.go diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..7619d77 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,17 @@ +FROM golang:1.13-alpine3.10 as builder +MAINTAINER Thomas Maier +RUN apk add --no-cache git +COPY . /build +WORKDIR /build +RUN GOOS=linux GOARCH=amd64 go build + +FROM alpine:3.10 +RUN apk add --no-cache ca-certificates +COPY --from=builder /build/xmpp-webhook /xmpp-webhook +RUN adduser -D -g '' xmpp-webhook +USER xmpp-webhook +ENV XMPP_ID="" \ + XMPP_PASS="" \ + XMPP_RECEIVERS="" +EXPOSE 4321 +CMD ["/xmpp-webhook"] diff --git a/README.md b/README.md index edd8189..468d29e 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,13 @@ curl -X POST -d @grafana-webhook-alert-example.json localhost:4321/grafana ``` - After parsing the body in the appropriate `parserFunc`, the notification is then distributed to the configured receivers. +## Run with Docker +### Build it +- Build image: `docker build -t xmpp-webhook .` +- Run: `docker run -e "XMPP_ID=alerts@example.org" -e "XMPP_PASS=xxx" -e "XMPP_RECEIVERS=receiver1@example.org,receiver2@example.org" -p 4321:4321 -d --name xmpp-webhook xmpp-webhook` +### Use prebuilt image from Docker Hub +- Run: `docker run -e "XMPP_ID=alerts@example.org" -e "XMPP_PASS=xxx" -e "XMPP_RECEIVERS=receiver1@example.org,receiver2@example.org" -p 4321:4321 -d --name xmpp-webhook opthomasprime/xmpp-webhook:latest` + ## Installation IMPORTANT NOTE: For the sake of simplicity, `xmpp-webhook` is not reconnecting to the XMPP server after a connection-loss. If you use the provided `xmpp-webhook.service` - Systemd will manage the reconnect by restarting the service. diff --git a/dev/xmpp-dev-stack/docker-compose.yml b/dev/xmpp-dev-stack/docker-compose.yml new file mode 100644 index 0000000..b87d289 --- /dev/null +++ b/dev/xmpp-dev-stack/docker-compose.yml @@ -0,0 +1,26 @@ +version: "3.7" +services: + prosody: + build: + context: ./prosody + network_mode: host + recipient-a: + build: + context: ./recipient + network_mode: host + depends_on: + - prosody + restart: always + environment: + - XMPP_ID=recipient-a@localhost + - XMPP_PASS=insecure + recipient-b: + build: + context: ./recipient + network_mode: host + depends_on: + - prosody + restart: always + environment: + - XMPP_ID=recipient-b@localhost + - XMPP_PASS=insecure diff --git a/dev/xmpp-dev-stack/prosody/Dockerfile b/dev/xmpp-dev-stack/prosody/Dockerfile new file mode 100644 index 0000000..73f376c --- /dev/null +++ b/dev/xmpp-dev-stack/prosody/Dockerfile @@ -0,0 +1,6 @@ +FROM unclev/prosody-docker-extended:0.11 + +ADD ./dev-server.sh /dev-server.sh + +ENTRYPOINT ["/dev-server.sh"] +CMD ["prosody"] diff --git a/dev/xmpp-dev-stack/prosody/dev-server.sh b/dev/xmpp-dev-stack/prosody/dev-server.sh new file mode 100755 index 0000000..6dd10b9 --- /dev/null +++ b/dev/xmpp-dev-stack/prosody/dev-server.sh @@ -0,0 +1,26 @@ +#!/usr/bin/env bash + +# applies https://modules.prosody.im/mod_auth_any.html and https://modules.prosody.im/mod_roster_allinall.html for testing + +PROSODY_CONF=/etc/prosody/prosody.cfg.lua +AUTH_ANY='authentication = "any"' +MODULES='modules_enabled = { "auth_any", "roster_allinall" }' + +function changes_missing { + if grep -q "$AUTH_ANY" $PROSODY_CONF; then + return 1 + else + return 0 + fi +} + +function apply_changes { + echo "$AUTH_ANY" >> $PROSODY_CONF + echo "$MODULES" >> $PROSODY_CONF +} + +if changes_missing; then + apply_changes +fi + +/usr/bin/entrypoint.sh "$@" diff --git a/dev/xmpp-dev-stack/recipient/.gitignore b/dev/xmpp-dev-stack/recipient/.gitignore new file mode 100644 index 0000000..e37d41b --- /dev/null +++ b/dev/xmpp-dev-stack/recipient/.gitignore @@ -0,0 +1,2 @@ +go.sum +recipient diff --git a/dev/xmpp-dev-stack/recipient/Dockerfile b/dev/xmpp-dev-stack/recipient/Dockerfile new file mode 100644 index 0000000..70b113e --- /dev/null +++ b/dev/xmpp-dev-stack/recipient/Dockerfile @@ -0,0 +1,9 @@ +FROM golang:1.13-alpine3.10 as builder +RUN apk add --no-cache git +COPY . /build +WORKDIR /build +RUN go build + +FROM alpine:3.10 +COPY --from=builder /build/recipient /recipient +CMD /recipient diff --git a/dev/xmpp-dev-stack/recipient/go.mod b/dev/xmpp-dev-stack/recipient/go.mod new file mode 100644 index 0000000..c1c3fe7 --- /dev/null +++ b/dev/xmpp-dev-stack/recipient/go.mod @@ -0,0 +1,9 @@ +module github.com/opthomas-prime/xmpp-webhook/dev/xmpp-dev-stack/recipient + +go 1.13 + +require ( + mellium.im/sasl v0.2.1 + mellium.im/xmlstream v0.14.0 + mellium.im/xmpp v0.14.0 +) diff --git a/dev/xmpp-dev-stack/recipient/main.go b/dev/xmpp-dev-stack/recipient/main.go new file mode 100644 index 0000000..2a5f1f9 --- /dev/null +++ b/dev/xmpp-dev-stack/recipient/main.go @@ -0,0 +1,87 @@ +package main + +import ( + "context" + "crypto/tls" + "encoding/xml" + "fmt" + "io" + "log" + "mellium.im/sasl" + "mellium.im/xmlstream" + "mellium.im/xmpp" + "mellium.im/xmpp/dial" + "mellium.im/xmpp/jid" + "mellium.im/xmpp/stanza" + "os" +) + +func panicOnErr(err error) { + if err != nil { + panic(err) + } +} + +type MessageBody struct { + stanza.Message + Body string `xml:"body"` +} + +func main() { + xi := os.Getenv("XMPP_ID") + xp := os.Getenv("XMPP_PASS") + + if xi == "" || xp == "" { + log.Fatal("XMPP_ID, XMPP_PASS not set") + } + + address, err := jid.Parse(xi) + panicOnErr(err) + + var dialer = dial.Dialer{NoTLS: true} + conn, err := dialer.Dial(context.TODO(), "tcp", address) + panicOnErr(err) + + tlsConfig := tls.Config{InsecureSkipVerify: true} + + session, err := xmpp.NegotiateSession( + context.TODO(), + address.Domain(), + address, + conn, + false, + xmpp.NewNegotiator(xmpp.StreamConfig{Features: []xmpp.StreamFeature{ + xmpp.BindResource(), + xmpp.StartTLS(true, &tlsConfig), + xmpp.SASL("", xp, sasl.ScramSha1Plus, sasl.ScramSha1, sasl.Plain), + }}), + ) + panicOnErr(err) + + fmt.Println("connected") + + err = session.Send(context.TODO(), stanza.WrapPresence(address, stanza.AvailablePresence, nil)) + panicOnErr(err) + + err = session.Serve(xmpp.HandlerFunc(func(t xmlstream.TokenReadEncoder, start *xml.StartElement) error { + d := xml.NewTokenDecoder(t) + if start.Name.Local != "message" { + return nil + } + + msg := MessageBody{} + err = d.DecodeElement(&msg, start) + if err != nil && err != io.EOF { + return nil + } + + if msg.Body == "" || msg.Type != stanza.ChatMessage { + return nil + } + + fmt.Printf("%s: %s\n", msg.From, msg.Body) + + return nil + })) + panicOnErr(err) +} diff --git a/go.mod b/go.mod index 7ad0d49..3e69833 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,5 @@ module github.com/opthomas-prime/xmpp-webhook require github.com/emgee/go-xmpp v0.0.0-20170414153234-efce8dbb9711 + +go 1.13