From 9e23e5a7c9c29ef990655cae893a8d47915533a0 Mon Sep 17 00:00:00 2001 From: svkalinin Date: Wed, 6 Jul 2022 12:36:21 +0300 Subject: [PATCH] Added GO-realisation email check service --- check_email_delivery_go/.gitignore | 3 + check_email_delivery_go/Dockerfile | 20 + check_email_delivery_go/README.md | 131 ++ check_email_delivery_go/cronjobs | 5 + check_email_delivery_go/docker-compose.yml | 9 + check_email_delivery_go/email-check.go | 585 +++++++ check_email_delivery_go/go.mod | 9 + check_email_delivery_go/go.sum | 14 + .../Template_Email_Delivery_Check.xml | 1407 +++++++++++++++++ 9 files changed, 2183 insertions(+) create mode 100644 check_email_delivery_go/.gitignore create mode 100644 check_email_delivery_go/Dockerfile create mode 100644 check_email_delivery_go/README.md create mode 100644 check_email_delivery_go/cronjobs create mode 100644 check_email_delivery_go/docker-compose.yml create mode 100644 check_email_delivery_go/email-check.go create mode 100644 check_email_delivery_go/go.mod create mode 100644 check_email_delivery_go/go.sum create mode 100644 check_email_delivery_go/zabbix_templates/Template_Email_Delivery_Check.xml diff --git a/check_email_delivery_go/.gitignore b/check_email_delivery_go/.gitignore new file mode 100644 index 0000000..c47a8d5 --- /dev/null +++ b/check_email_delivery_go/.gitignore @@ -0,0 +1,3 @@ +.env +main +email-check diff --git a/check_email_delivery_go/Dockerfile b/check_email_delivery_go/Dockerfile new file mode 100644 index 0000000..e239b98 --- /dev/null +++ b/check_email_delivery_go/Dockerfile @@ -0,0 +1,20 @@ +FROM golang:alpine AS build +RUN apk --no-cache add gcc g++ make git + +WORKDIR /go/src/app + +COPY . . +RUN go get ./... +RUN GOOS=linux go build -ldflags="-s -w" -o ./bin/email-check ./email-check.go +FROM alpine:3.9 +RUN apk add tzdata +#RUN apk --no-cache add ca-certificates +WORKDIR /usr/bin +COPY --from=build /go/src/app/bin /go/bin + +COPY cronjobs /etc/crontabs/root + +# start crond with log level 8 in foreground, output to stderr +CMD ["crond", "-f", "-d", "8"] + +#ENTRYPOINT /go/bin/email-check \ No newline at end of file diff --git a/check_email_delivery_go/README.md b/check_email_delivery_go/README.md new file mode 100644 index 0000000..8f2b264 --- /dev/null +++ b/check_email_delivery_go/README.md @@ -0,0 +1,131 @@ +# Email Monitoring + +"Сервис" проверки отправки и получения почты с возможностью мониторинга в Zabbix. Также можно просто отправлять любые сообщения с целью автоматизации чего-нибудь. + +Пароли от учетных записей почты сервис может брать из Hashicorp Vault. + +Программу можно запускать как в консоли так и в docker-контейнере. + +# Режимы работы + + +# использование + +Для работы используются переменные окружения (либо соответствующие им ключи (опции) командной строки), при указании параметров с командной строки , переменные окружения будут проигнорированы: + +- IMAP_SERVER=imap.google.com +- IMAP_PORT=143 +- IMAP_USER=some@gmail.com +- IMAP_PASSWORD=12345 +- MAIL_FROM=some@localmaildomain.ru +- MAIL_MESSAGE="Message body" +- MAIL_SUBJECT="Test email subject" +- MAIL_TO=""some@gmail.com +- SMTP_SERVER=smtp.localmaildomain.ru +- SMTP_PORT=25 +- SMTP_USER=some@localmaildomain.ru +- SMTP_PASSWORD=123456 +- VAULT_ADDRESS=https://you.vault.address +- VAULT_TOKEN="You vault token" +- ZABBIX_SERVER=https://zabbix2 +- ZABBIX_HOST=mail.localmaildomain.ru +- ZABBIX_USERNAME=zabbix_user +- ZABBIX_PASSWORD=somepass + +Для отправки сообщения следует указать опцию "-send" и задать все остальные параметры (адреса, пользователей и так далее), для проверки получения сообщения надо указать "-recieve". Если указать обе опции то будет протестирована отправка и получение одного сообщения. +Если Тема (mail-subject) и Сообщение (mail-message) не указаны то будет сгенерирован уникальный код, который будет вставлен в сообщение. + +При указании адреса vault и токена с командной строки или через переменные окружения, пароли будут взяты оттуда. В качестве ключа используются значения SMTP_USER (-smtp-user), IMAP_USER (-imap-user), ZABBIX_USER (-zabbix-api-user). Если секреты для почтовых адресов и пароли от заббикса (API) хрянятся в разных разделах, то путь к хранилищу VAULT можно построить указав отдельные пути при помощи опций "-vault-email-secret-path" и "-vault-zabbix-secret-path". Т.е.: + +Если указать значения параметров: +``` + -vault-address="https://vault:8200/v1/secret/data/" + -vault-email-secret-path="email" + -vault-zabbix-secret-path="zabbix/helpers" +``` +или + +``` + -vault-address="https://vault:8200" + -vault-email-secret-path="/v1/secret/data/email" + -vault-zabbix-secret-path="/v1/secret/data/zabbix/helpers" +``` + +То, получим полный путь "https://vault:8200/v1/secret/data/email" или https://vault:8200/v1/secret/data/zabbix/helpers". + +Для отправки результатов проверки в zabbix можно использовать как Zabbix API (опция "-zabbix-api") так и zabbix-sender (опция "-zabbix-sender"). При указании одного из этих вариантов требуется также указывать параметры работы с заббикс (адрес сервера, узел, пользователя -zabbix-api-user и пароль -zabbix-api-password (в случае АПИ)). Но предварительно следует настроить узел в заббикс и подключить к нему приложенный шаблон (шаблон настроен на использование zabbix-sender). + +## Пример команды + +Отправка сообщения через локальный SMTP сервер с авторизацией (пароли будут взяты из хрфнилища секретов Vault), и проверка получения этого сообщения в учетной записи gmail по imap: + +``` +email-check -mail-from "some@localmaildomain.ru" -mail-to "some@gmail.com" \ +-smtp-user="some@localmaildomain.ru" -smtp-server="smtp.localmaildomain.ru" -smtp-port="25" \ +-imap-port="993" -vault-address="https://vault:8200/v1/secret/data/email" \ +-imap-user="some@gmail.com" -imap-server="imap.google.com" -send -recieve -delay 20 +``` + +Опция "-delay" устанавливает задержку между отправкой и получением (чтением) сообщения и по умолчанию равна 10 секунд. Это необходимо для того, чтобы сообщение успело пройти все фильтры и проверки почтовой системы. + +# Краткая помощь по программе + +Опции командной строки: + +``` + -delay int + Pause is required between operations (default 10) + -direction string + Direction of email checking. Must be an: 'local', 'incoming', 'outgoing'. (default "local") + -imap-password string + IMAP user password (IMAP_PASSWORD) + -imap-port string + IMAP server port (IMAP_PORT) (default "993") + -imap-server string + IMAP server address (IMAP_SERVER) + -imap-user string + IMAP user (IMAP_USER) + -mail-from string + Mail sender address 'From' (MAIL_FROM) + -mail-message string + Mail message body (MAIL_MESSAGE) + -mail-subject string + Mail message subject (MAIL_SUBJECT) + -mail-to string + Mail receiver address 'From' (MAIL_TO) + -receive + receive message is ON + -send + Sending message is ON + -smtp-password string + SMTP user password (SMTP_PASSWORD) + -smtp-port string + SMTP server port (SMTP_PORT) (default "25") + -smtp-server string + SMTP server address (SMTP_SERVER) + -smtp-user string + SMTP user (SMTP_USER) + -use-zabbix-sender + Send metrics or discovery data into zabbix via zabbix-sender + -vault-address string + Hashicorp (c) vault address (VAULT_ADDRESS) + -vault-email-secret-path string + Path to Vault email secret, where 'full Vault path' = 'vault-address' + 'vault-email-secret-path' + -vault-token string + Vault access token (VAULT_TOKEN) + -vault-zabbix-secret-path string + Path to Vault zabbix secret, where 'full Vault path' = 'vault-address' + 'vault-zabbix-secret-path' + -zabbix-api + Send metrics or discovery data into zabbix via Zabbix API (not implemented yet) + -zabbix-api-password string + Zabbix server API password + -zabbix-api-user string + Zabbix server API user + -zabbix-host string + Zabbix monitoring host name + -zabbix-port int + Zabbix server port (default 10051) + -zabbix-server string + Zabbix server address + +``` \ No newline at end of file diff --git a/check_email_delivery_go/cronjobs b/check_email_delivery_go/cronjobs new file mode 100644 index 0000000..4119426 --- /dev/null +++ b/check_email_delivery_go/cronjobs @@ -0,0 +1,5 @@ +*/10 * * * * /go/bin/email-check -mail-to "${EXT_RECEIVER_EMAIL}" -mail-from "${SENDER_EMAIL}" -imap-user="${EXT_RECEIVER_EMAIL}" -imap-server="${EXT_IMAP_SERVER}" -smtp-user="${SENDER_EMAIL}" -smtp-server="${MAIL_SERVER}" -send -receive -delay 10 -direction outgoing -use-zabbix-sender -zabbix-host="${MAIL_SERVER}" -vault-email-secret-path="/v1/secret-inf/data/email" >> /var/log/email-check-out.log 2>&1 + +*/10 * * * * /go/bin/email-check -mail-to "${RECEIVER_EMAIL}" -mail-from "${EXT_SENDER_EMAIL}" -imap-user="${RECEIVER_EMAIL}" -imap-server="${MAIL_SERVER}" -smtp-user="${EXT_SENDER_EMAIL}" -smtp-server="${EXT_SMTP_SERVER}" -send -receive -delay 10 -direction incoming -use-zabbix-sender -zabbix-host="${MAIL_SERVER}" -vault-email-secret-path="/v1/secret-inf/data/email" >> /var/log/email-check-in.log 2>&1 + +*/10 * * * * /go/bin/email-check -mail-to "${RECEIVER_EMAIL}" -mail-from "${SENDER_EMAIL}" -imap-user="${RECEIVER_EMAIL}" -imap-server="${MAIL_SERVER}" -smtp-user="${SENDER_EMAIL}" -smtp-server="${MAIL_SERVER}" -send -receive -direction local -use-zabbix-sender -zabbix-host="${MAIL_SERVER}" -vault-email-secret-path="/v1/secret-inf/data/email" >> /var/log/email-check-local.log 2>&1 \ No newline at end of file diff --git a/check_email_delivery_go/docker-compose.yml b/check_email_delivery_go/docker-compose.yml new file mode 100644 index 0000000..f12411b --- /dev/null +++ b/check_email_delivery_go/docker-compose.yml @@ -0,0 +1,9 @@ +version: '3' + +services: + check_email: + image: check-email-delivery:latest + env_file: .env + restart: always + build: + context: . diff --git a/check_email_delivery_go/email-check.go b/check_email_delivery_go/email-check.go new file mode 100644 index 0000000..987aa79 --- /dev/null +++ b/check_email_delivery_go/email-check.go @@ -0,0 +1,585 @@ +// ------------------------------------------------------ +// Email sender and receiver for checking mail transport +// Author: Sergey Kalinin, 2022 +//------------------------------------------------------- + +package main + +import ( + "crypto/tls" + "encoding/json" + "errors" + "flag" + "fmt" + "log" + "net" + "net/http" + "net/smtp" + "os" + "strconv" + "time" + + "github.com/adubkov/go-zabbix" + "github.com/emersion/go-imap" + "github.com/emersion/go-imap/client" + "github.com/google/uuid" +) + +type SmtpSession struct { + smtpUser string + smtpPassword string + smtpServer string + smtpPort string +} + +type ImapSession struct { + imapPassword string + imapServer string + imapUser string + imapPort string +} + +type ZabbixSession struct { + zabbixUser string + zabbixPassword string + zabbixServer string + zabbixPort int + zabbixHost string +} + +type MailMessage struct { + mailFrom string + mailTo string + mailSubject string + mailMessage string + messageUUID string +} + +type loginAuth struct { + username, password string +} + +func LoginAuth(username, password string) smtp.Auth { + return &loginAuth{username, password} +} + +func (a *loginAuth) Start(server *smtp.ServerInfo) (string, []byte, error) { + return "LOGIN", []byte(a.username), nil +} + +func (a *loginAuth) Next(fromServer []byte, more bool) ([]byte, error) { + if more { + switch string(fromServer) { + case "Username:": + return []byte(a.username), nil + case "Password:": + return []byte(a.password), nil + default: + return nil, errors.New("Unknown from server") + } + } + return nil, nil +} + +func sendEmail(smtpSession *SmtpSession, message *MailMessage) bool { + + // Receiver email address. + to := []string{ + message.mailTo, + } + + serverName := smtpSession.smtpServer + ":" + smtpSession.smtpPort + // fmt.Println(serverName, smtpSession.smtpUser, smtpSession.smtpPassword) + + conn, err := net.Dial("tcp", serverName) + if err != nil { + log.Println(err) + } + + c, err := smtp.NewClient(conn, smtpSession.smtpServer) + if err != nil { + log.Println(err) + } + + tlsconfig := &tls.Config{ + ServerName: smtpSession.smtpServer, + } + + if err = c.StartTLS(tlsconfig); err != nil { + log.Println(err) + } + + auth := LoginAuth(smtpSession.smtpUser, smtpSession.smtpPassword) + + if err = c.Auth(auth); err != nil { + log.Println(err) + } + + msg := []byte("From: " + message.mailFrom + "\r\n" + + "To: " + message.mailTo + "\r\n" + + "Subject: " + message.mailSubject + "\r\n\r\n" + + message.mailMessage + "\r\n") + + // Sending email. + err = smtp.SendMail(serverName, auth, message.mailFrom, to, msg) + + if err != nil { + fmt.Println(err) + return false + } + // fmt.Println("Email Sent!") + + if err != nil { + log.Fatal(err) + return false + } else { + log.Println("Email", message.messageUUID, "from", message.mailFrom, "sent successfully via", smtpSession.smtpServer) + return true + } +} + +var httpClient = &http.Client{ + Timeout: 10 * time.Second, +} + +func genMessageUID() string { + id := uuid.New().String() + return id +} + +func getVaultData(vaultAddr string, vaultToken string, vaultSecretName string) string { + + customTransport := &(*http.DefaultTransport.(*http.Transport)) + customTransport.TLSClientConfig = &tls.Config{InsecureSkipVerify: true} + + client := &http.Client{Transport: customTransport} + // client := &http.Client{} + + req, _ := http.NewRequest("GET", vaultAddr, nil) + req.Header.Add("Accept", "application/json") + req.Header.Add("X-Vault-Token", vaultToken) + resp, err := client.Do(req) + + if err != nil { + log.Println("Errored when sending request to the Vault server") + } + + var result map[string]interface{} + json.NewDecoder(resp.Body).Decode(&result) + secret := result["data"].(map[string]interface{})["data"].(map[string]interface{})[vaultSecretName] + + return fmt.Sprint(secret) +} + +func zabbixSender(zabbixSession *ZabbixSession, zabbixItem string, zabbixItemValue string) bool { + var ( + metrics []*zabbix.Metric + err error + ) + + metrics = append(metrics, zabbix.NewMetric(zabbixSession.zabbixHost, zabbixItem, zabbixItemValue, time.Now().Unix())) + // metrics = append(metrics, zabbix.NewMetric(zabbixSession.zabbixHost, "status", "OK")) + + // Create instance of Packet class + packet := zabbix.NewPacket(metrics) + // fmt.Println(zabbix_host, metric_name, metric_value, metrics) + // Send packet to zabbix + z := zabbix.NewSender(zabbixSession.zabbixServer, zabbixSession.zabbixPort) + res, err := z.Send(packet) + log.Println(zabbixSession.zabbixServer, zabbixSession.zabbixPort, zabbixSession.zabbixHost, zabbixItem, zabbixItemValue, metrics, zabbixSession) + log.Println("Result", string(res)) + if err != nil { + log.Println("Sending to zabbix should have failed:", err) + } + + return true +} + +func receiveEmail(imapSession *ImapSession, message *MailMessage) bool { + + serverName := imapSession.imapServer + ":" + imapSession.imapPort + + log.Println("Connecting to server", serverName, "...") + + // Connect to server + c, err := client.DialTLS(serverName, nil) + if err != nil { + log.Fatal(err) + } + log.Println("Connected") + log.Println("Trying to logged in to imap with username", imapSession.imapUser, "...") + // Don't forget to logout + defer c.Logout() + + // Login + if err := c.Login(imapSession.imapUser, imapSession.imapPassword); err != nil { + log.Fatal(err) + return false + } + log.Println(imapSession.imapUser, "logged in") + + // Select INBOX + _, err = c.Select("INBOX", false) + if err != nil { + log.Fatal(err) + } + log.Println("Select the INBOX:") + + // Get the last 4 messages + // from := uint32(1) + // to := mbox.Messages + // if mbox.Messages > 10 { + // // We're using unsigned integers here, only subtract if the result is > 0 + // from = mbox.Messages - 10 + // } + // seqset := new(imap.SeqSet) + // seqset.AddRange(from, to) + + // messages := make(chan *imap.Message, 10) + // done := make(chan error, 1) + + // go func() { + // done <- c.Fetch(seqset, []imap.FetchItem{imap.FetchEnvelope, imap.FetchUid}, messages) + // }() + // // var uid uint32 + // log.Println("Search message with subject", message.mailSubject, "in last messages:") + // for msg := range messages { + // log.Println(msg.Uid) + // if msg.Envelope.Subject == message.mailSubject { + // log.Println("Message was found:", msg.Envelope.Subject, msg.Envelope.MessageId, msg.Uid) + // // uid = msg.Uid + // // item := imap.FormatFlagsOp(imap.AddFlags, true) + // // flags := []interface{}{imap.DeletedFlag} + // // if err := c.Store(seqset, item, flags, nil); err != nil { + // // log.Fatal(err) + // // } else { + // // log.Println("Message with UID", mbox.Messages, "was marked for deletion") + // // } + + // } + // } + // if err := <-done; err != nil { + // log.Fatal(err) + // return false + // } + + // ----------------------- + // We will delete the last message + // if mbox.Messages == 0 { + // log.Fatal("No message in mailbox") + // } + // seqset.Clear() + // seqSet := new(imap.SeqSet) + + // seqSet.AddNum(uid) + + // // First mark the message as deleted + // item := imap.FormatFlagsOp(imap.AddFlags, true) + // flags := []interface{}{imap.DeletedFlag} + // if err := c.Store(seqSet, item, flags, nil); err != nil { + // log.Fatal(err) + // } else { + // log.Println("Last", mbox.Messages, "messages was marked for deletion") + // } + + // Then delete it + // if err := c.Expunge(nil); err != nil { + // log.Fatal(err) + // } + + // log.Println("Last messages has been deleted") + + log.Println("Search message with subject", message.mailSubject, "in last messages...") + + criteria := imap.NewSearchCriteria() + + criteria.Text = []string{message.mailSubject} + ids, err := c.Search(criteria) + if err != nil { + log.Fatal(err) + } + log.Println("Total messages :", ids) + + if len(ids) > 0 { + seqset := new(imap.SeqSet) + seqset.AddNum(ids...) + + messages := make(chan *imap.Message, 10) + done := make(chan error, 1) + go func() { + done <- c.Fetch(seqset, []imap.FetchItem{imap.FetchEnvelope}, messages) + }() + + // log.Println("Unseen messages:") + for msg := range messages { + log.Println("Message was found:", msg.Envelope.Subject, msg.Envelope.MessageId, msg.Uid) + // log.Println("* " + msg.Envelope.Subject) + } + + if err := <-done; err != nil { + log.Fatal(err) + return false + } + item := imap.FormatFlagsOp(imap.AddFlags, true) + flags := []interface{}{imap.DeletedFlag} + if err := c.Store(seqset, item, flags, nil); err != nil { + log.Fatal(err) + return false + } else { + log.Println("The message with subject", message.mailSubject, "was marked for deletion") + } + } + + // Then delete it + log.Println("Delete marked messages...") + if err := c.Expunge(nil); err != nil { + log.Fatal(err) + } else { + log.Println("All marked messages was deleted") + } + + log.Println("Done!") + return true +} + +func main() { + var ( + vaultAddress string + vaultToken string + vaultEmailSecretPath string + vaultZabbixSecretPath string + vaultPath string + message MailMessage + smtpSession SmtpSession + imapSession ImapSession + zabbixSession ZabbixSession + sendMessage bool + receiveMessage bool + operationDelay int + useZabbixSender bool + zabbixApi bool + err error + msgSendResult bool + msgReceiveResult bool + direction string + ) + + // Genarate uniq ID + message.messageUUID = genMessageUID() + + // ---------------------------------------------------------- + // Read command line options and and settins the variables + + flag.StringVar(&message.mailFrom, "mail-from", "", "Mail sender address 'From' (MAIL_FROM)") + flag.StringVar(&message.mailTo, "mail-to", "", "Mail receiver address 'From' (MAIL_TO)") + flag.StringVar(&message.mailSubject, "mail-subject", "", "Mail message subject (MAIL_SUBJECT)") + flag.StringVar(&message.mailMessage, "mail-message", "", "Mail message body (MAIL_MESSAGE)") + flag.StringVar(&smtpSession.smtpServer, "smtp-server", "", "SMTP server address (SMTP_SERVER)") + flag.StringVar(&smtpSession.smtpPort, "smtp-port", "587", "SMTP server port (SMTP_PORT)") + flag.StringVar(&smtpSession.smtpUser, "smtp-user", "", "SMTP user (SMTP_USER)") + flag.StringVar(&smtpSession.smtpPassword, "smtp-password", "", "SMTP user password (SMTP_PASSWORD)") + flag.StringVar(&imapSession.imapServer, "imap-server", "", "IMAP server address (IMAP_SERVER)") + flag.StringVar(&imapSession.imapPort, "imap-port", "993", "IMAP server port (IMAP_PORT)") + flag.StringVar(&imapSession.imapUser, "imap-user", "", "IMAP user (IMAP_USER)") + flag.StringVar(&imapSession.imapPassword, "imap-password", "", "IMAP user password (IMAP_PASSWORD)") + flag.StringVar(&vaultAddress, "vault-address", "", "Hashicorp (c) vault address (VAULT_ADDRESS)") + flag.StringVar(&vaultToken, "vault-token", "", "Vault access token (VAULT_TOKEN)") + flag.StringVar(&vaultEmailSecretPath, "vault-email-secret-path", "", "Path to Vault email secret, where 'full Vault path' = 'vault-address' + 'vault-email-secret-path'") + flag.StringVar(&vaultZabbixSecretPath, "vault-zabbix-secret-path", "", "Path to Vault zabbix secret, where 'full Vault path' = 'vault-address' + 'vault-zabbix-secret-path'") + flag.BoolVar(&sendMessage, "send", false, "Sending message is ON") + flag.BoolVar(&receiveMessage, "receive", false, "receive message is ON") + flag.IntVar(&operationDelay, "delay", 5, "Pause is required between operations") + flag.BoolVar(&useZabbixSender, "use-zabbix-sender", false, "Send metrics or discovery data into zabbix via zabbix-sender") + flag.BoolVar(&zabbixApi, "zabbix-api", false, "Send metrics or discovery data into zabbix via Zabbix API (not implemented yet)") + flag.StringVar(&zabbixSession.zabbixServer, "zabbix-server", "", "Zabbix server address (ZABBIX_SERVER)") + flag.IntVar(&zabbixSession.zabbixPort, "zabbix-port", 10051, "Zabbix server port (ZABBIX_PORT)") + flag.StringVar(&zabbixSession.zabbixUser, "zabbix-api-user", "", "Zabbix server API user (ZABBIX_USER)") + flag.StringVar(&zabbixSession.zabbixPassword, "zabbix-api-password", "", "Zabbix server API password (ZABBIX_PASSWORD)") + flag.StringVar(&zabbixSession.zabbixHost, "zabbix-host", "", "Zabbix monitoring host name") + flag.StringVar(&direction, "direction", "local", "Direction of email checking. Must be an: 'local', 'incoming', 'outgoing'.") + + flag.Parse() + + if os.Getenv("VAULT_ADDRESS") != "" { + vaultAddress = os.Getenv("VAULT_ADDRESS") + } + + if vaultAddress != "" && vaultToken == "" && os.Getenv("VAULT_TOKEN") == "" { + fmt.Println("If You setting VAULT_ADDRES, You must sure environment variables `VAULT_TOKEN`, or used with '-vault-token' argument") + os.Exit(1) + } else if vaultToken == "" && os.Getenv("VAULT_TOKEN") != "" { + vaultToken = os.Getenv("VAULT_TOKEN") + } + + if message.mailFrom == "" && os.Getenv("MAIL_FROM") == "" { + fmt.Println("Make sure environment variables `MAIL_FROM`, or used with '-mail-from' argument") + os.Exit(1) + } else if message.mailFrom == "" && os.Getenv("MAIL_FROM") != "" { + message.mailFrom = os.Getenv("MAIL_FROM") + } + + if message.mailTo == "" && os.Getenv("MAIL_TO") == "" { + fmt.Println("Make sure environment variables `MAIL_TO`, or used with '-mail-to' argument") + os.Exit(1) + } else if message.mailTo == "" && os.Getenv("MAIL_TO") != "" { + message.mailTo = os.Getenv("MAIL_TO") + } + + if message.mailSubject == "" && os.Getenv("MAIL_SUBJECT") == "" { + message.mailSubject = message.messageUUID + // fmt.Println("Make sure environment variables `MAIL_SUBJECT`, or used with '-mail-subject' argument") + // os.Exit(1) + } else if message.mailSubject == "" && os.Getenv("MAIL_SUBJECT") != "" { + message.mailFrom = os.Getenv("MAIL_SUBJECT") + } + + if message.mailMessage == "" && os.Getenv("MAIL_MESSAGE") == "" { + message.mailMessage = message.messageUUID + // fmt.Println("Make sure environment variables `MAIL_MESSAGE`, or used with '-mail-message' argument") + // os.Exit(1) + } else if message.mailMessage == "" && os.Getenv("MAIL_MESSAGE") != "" { + message.mailMessage = os.Getenv("MAIL_MESSAGE") + } + + if smtpSession.smtpUser == "" && os.Getenv("SMTP_USER") == "" { + fmt.Println("Make sure environment variables `SMTP_USER`, or used with '-smtp-user' argument") + os.Exit(1) + } else if smtpSession.smtpUser == "" && os.Getenv("SMTP_USER") != "" { + smtpSession.smtpServer = os.Getenv("SMTP_USER") + } + + if smtpSession.smtpPassword == "" && os.Getenv("SMTP_PASSWORD") == "" && vaultAddress == "" { + fmt.Println("Make sure environment variables `SMTP_PASSWORD`, or used with '-smtp-password' argument") + os.Exit(1) + } else if smtpSession.smtpPassword == "" && os.Getenv("SMTP_PASSWORD") != "" { + smtpSession.smtpPassword = os.Getenv("SMTP_PASSWORD") + } else if vaultAddress != "" && smtpSession.smtpUser != "" && smtpSession.smtpPassword == "" { + vaultPath = vaultAddress + vaultEmailSecretPath + smtpSession.smtpPassword = getVaultData(vaultPath, vaultToken, smtpSession.smtpUser) + // fmt.Println(smtpUser, smtpPassword) + } + + if smtpSession.smtpServer == "" && os.Getenv("SMTP_SERVER") == "" { + fmt.Println("Make sure environment variables `SMTP_SERVER`, or used with '-smtp-server' argument") + os.Exit(1) + } else if smtpSession.smtpServer == "" && os.Getenv("SMTP_SERVER") != "" { + smtpSession.smtpServer = os.Getenv("SMTP_SERVER") + } + + if imapSession.imapUser == "" && os.Getenv("IMAP_USER") == "" { + fmt.Println("Make sure environment variables `IMAP_USER`, or used with '-imap-user' argument") + os.Exit(1) + } else if imapSession.imapUser == "" && os.Getenv("IMAP_USER") != "" { + imapSession.imapServer = os.Getenv("IMAP_USER") + } + + if imapSession.imapPassword == "" && os.Getenv("IMAP_PASSWORD") == "" && vaultAddress == "" { + fmt.Println("Make sure environment variables `IMAP_PASSWORD`, or used with '-imap-password' argument") + os.Exit(1) + } else if imapSession.imapUser == "" && os.Getenv("IMAP_PASSWORD") != "" { + imapSession.imapPassword = os.Getenv("IMAP_PASSWORD") + } else if vaultAddress != "" && imapSession.imapUser != "" && imapSession.imapPassword == "" { + // secret := append("") + vaultPath = vaultAddress + vaultEmailSecretPath + imapSession.imapPassword = getVaultData(vaultPath, vaultToken, imapSession.imapUser) + // fmt.Println(imapUser, imapPassword) + } + + if imapSession.imapServer == "" && os.Getenv("IMAP_SERVER") == "" { + fmt.Println("Make sure environment variables `IMAP_SERVER`, or used with '-imap-server' argument") + os.Exit(1) + } else if imapSession.imapServer == "" && os.Getenv("IMAP_SERVER") != "" { + imapSession.imapServer = os.Getenv("IMAP_SERVER") + } + + if zabbixApi || useZabbixSender { + if zabbixSession.zabbixServer == "" && os.Getenv("ZABBIX_SERVER") == "" { + fmt.Println("Make sure environment variables `ZABBIX_SERVER`, or used with '-zabbix-server' argument") + os.Exit(1) + } else if zabbixSession.zabbixServer == "" && os.Getenv("ZABBIX_SERVER") != "" { + zabbixSession.zabbixServer = os.Getenv("ZABBIX_SERVER") + } + + if os.Getenv("ZABBIX_PORT") != "" { + fmt.Println("Make sure environment variables `ZABBIX_PORT`, or used with '-zabbix-port' argument") + os.Exit(1) + if zabbixSession.zabbixPort, err = strconv.Atoi(os.Getenv("ZABBIX_PORT")); err != nil { + log.Println(zabbixSession.zabbixPort, "Zabbix port value error") + } + } + + if zabbixSession.zabbixHost == "" && os.Getenv("ZABBIX_HOST") == "" { + fmt.Println("Make sure environment variables `ZABBIX_HOST`, or used with '-zabbix-host' argument") + os.Exit(1) + } else if zabbixSession.zabbixHost == "" && os.Getenv("ZABBIX_HOST") != "" { + zabbixSession.zabbixHost = os.Getenv("ZABBIX_HOST") + } + } + + if zabbixApi { + if zabbixSession.zabbixUser == "" && os.Getenv("ZABBIX_USER") == "" { + fmt.Println("Make sure environment variables `ZABBIX_USER`, or used with '-zabbix-api-user' argument") + os.Exit(1) + } else if zabbixSession.zabbixUser == "" && os.Getenv("ZABBIX_USER") != "" { + zabbixSession.zabbixUser = os.Getenv("ZABBIX_USER") + } + + if zabbixSession.zabbixPassword == "" && os.Getenv("ZABBIX_PASSWORD") == "" { + fmt.Println("Make sure environment variables `ZABBIX_PASSWORD`, or used with '-zabbix-api-password' argument") + os.Exit(1) + } else if vaultAddress != "" && zabbixSession.zabbixUser != "" && zabbixSession.zabbixPassword == "" { + // secret := append("") + vaultPath = vaultAddress + vaultZabbixSecretPath + zabbixSession.zabbixPassword = getVaultData(vaultPath, vaultToken, zabbixSession.zabbixUser) + // fmt.Println(imapUser, imapPassword) + } else if zabbixSession.zabbixPassword == "" && os.Getenv("ZABBIX_PASSWORD") != "" { + zabbixSession.zabbixUser = os.Getenv("ZABBIX_USER") + } + } + + fmt.Println(useZabbixSender) + // ---------------------------------------------------------- + // Run the operations + // Send message + if sendMessage { + log.Println("Send email message with UUID:", message.messageUUID) + msgSendResult = sendEmail(&smtpSession, &message) + // Send result into zabbix + if useZabbixSender { + if msgSendResult { + zabbixSender(&zabbixSession, `email.smtp_send.`+direction+`.status`, "OK") + } else { + zabbixSender(&zabbixSession, `email.smtp_send.`+direction+`.status`, "CRITICAL") + } + } + } + + // When using both "send" and "receive", + // a 10-second pause between operations is required + if sendMessage && receiveMessage { + time.Sleep(time.Duration(operationDelay) * time.Second) + } + + // Receive message + if receiveMessage { + log.Println("Read email message with UUID:", message.messageUUID, "via", imapSession.imapServer) + msgReceiveResult = receiveEmail(&imapSession, &message) + // Send result into zabbix + if useZabbixSender { + if msgReceiveResult { + zabbixSender(&zabbixSession, `email.imap_receive.`+direction+`.status`, "OK") + } else { + zabbixSender(&zabbixSession, `email.imap_receive.`+direction+`.status`, "CRITICAL") + } + } + } + + // Send result into zabbix + if useZabbixSender { + if msgSendResult && msgReceiveResult { + zabbixSender(&zabbixSession, `email.delivery.`+direction+`.status`, "OK") + } else { + zabbixSender(&zabbixSession, `email.delivery.`+direction+`.status`, "CRITICAL") + } + } +} diff --git a/check_email_delivery_go/go.mod b/check_email_delivery_go/go.mod new file mode 100644 index 0000000..22c42a2 --- /dev/null +++ b/check_email_delivery_go/go.mod @@ -0,0 +1,9 @@ +module email-check.go + +go 1.15 + +require ( + github.com/adubkov/go-zabbix v0.0.0-20170118040903-3c6a95ec4fdc // indirect + github.com/emersion/go-imap v1.2.1 // indirect + github.com/google/uuid v1.3.0 // indirect +) diff --git a/check_email_delivery_go/go.sum b/check_email_delivery_go/go.sum new file mode 100644 index 0000000..7a13425 --- /dev/null +++ b/check_email_delivery_go/go.sum @@ -0,0 +1,14 @@ +github.com/adubkov/go-zabbix v0.0.0-20170118040903-3c6a95ec4fdc h1:gqqI4ZPa7uwK+gX9Zgk2AweAh+2dX0FpETcXTsA2TrE= +github.com/adubkov/go-zabbix v0.0.0-20170118040903-3c6a95ec4fdc/go.mod h1:ihDXRSVen590YHlXIrv00CcmRrL6pUho/Iwm3ZmM8n8= +github.com/emersion/go-imap v1.2.1 h1:+s9ZjMEjOB8NzZMVTM3cCenz2JrQIGGo5j1df19WjTA= +github.com/emersion/go-imap v1.2.1/go.mod h1:Qlx1FSx2FTxjnjWpIlVNEuX+ylerZQNFE5NsmKFSejY= +github.com/emersion/go-message v0.15.0/go.mod h1:wQUEfE+38+7EW8p8aZ96ptg6bAb1iwdgej19uXASlE4= +github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21 h1:OJyUGMJTzHTd1XQp98QTaHernxMYzRaOasRir9hUlFQ= +github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21/go.mod h1:iL2twTeMvZnrg54ZoPDNfJaJaqy0xIQFuBdrLsmspwQ= +github.com/emersion/go-textwrapper v0.0.0-20200911093747-65d896831594/go.mod h1:aqO8z8wPrjkscevZJFVE1wXJrLpC5LtJG7fqLOsPb2U= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= diff --git a/check_email_delivery_go/zabbix_templates/Template_Email_Delivery_Check.xml b/check_email_delivery_go/zabbix_templates/Template_Email_Delivery_Check.xml new file mode 100644 index 0000000..a335877 --- /dev/null +++ b/check_email_delivery_go/zabbix_templates/Template_Email_Delivery_Check.xml @@ -0,0 +1,1407 @@ + + + 4.0 + 2020-09-02T11:28:06Z + + + Mail + + + + + + + + {Template_Email_Delivery_Check:email.delivery.incoming.status.diff()}=1 + 0 + + Mail: {HOSTNAME} Incoming Email Delivery failed + 0 + + + 0 + 2 + + 0 + 0 + + + + + {Template_Email_Delivery_Check:email.delivery.incoming.status.nodata(20m)}=1 + 0 + + Mail: {HOSTNAME} Incoming Email Delivery No Data + 0 + + + 0 + 2 + + 0 + 0 + + + + + {Template_Email_Delivery_Check:email.delivery.local.status.diff()}=1 + 0 + + Mail: {HOSTNAME} Local Email Delivery failed + 0 + + + 0 + 2 + + 0 + 0 + + + + + {Template_Email_Delivery_Check:email.delivery.local.status.nodata(20m)}=1 + 0 + + Mail: {HOSTNAME} Local Email Delivery No Data + 0 + + + 0 + 2 + + 0 + 0 + + + + + {Template_Email_Delivery_Check:email.delivery.outgoing.status.diff()}=1 + 0 + + Mail: {HOSTNAME} Outgoing Email Delivery failed + 0 + + + 0 + 2 + + 0 + 0 + + + + + {Template_Email_Delivery_Check:email.delivery.outgoing.status.nodata(20m)}=1 + 0 + + Mail: {HOSTNAME} Outgoing Email Delivery No Data + 0 + + + 0 + 2 + + 0 + 0 + + + + +