/kube-mail

Policy-based SMTP daemon for sending emails from Kubernetes (experimental)

Primary LanguageTypeScriptApache License 2.0Apache-2.0

kube-mail -- SMTP server for Kubernetes

Build Status Docker Repository on Quay Dependabot Status


⚠️ CAUTION This is entirely experimental and may eat your cluster.


kube-mail is a policy-based SMTP server designed for running in a Kubernetes cluster. It is configurable using Kubernetes Custom Resources and allows you to define policies for outgoing emails based on Pod labels (much like NetworkPolicies).

Table of Contents

License

This project is licensed under the Apache-2.0 license.

Copyright 2023 Martin Helmich, Mittwald CM Service GmbH & Co. KG and other contributors.

Installation

The helm chart of this controller can be found under ./deploy/helm-chart/kube-mail.

Alternatively, you can use the Mittwald Kubernetes Helm Charts repository:

helm repo add mittwald https://helm.mittwald.de
helm repo update
helm install kube-mail mittwald/kube-mail --namespace kube-system

Basic architecture

When installed, kube-mail acts as an SMTP server that Pods in your cluster can use to send outgoing mails. This server works without any of the typical SMTP authentication mechanisms; instead, the kube-mail SMTP server authenticates a Pod by its IP address and then tries to find a EmailPolicy resource that matches the source Pod (by label).

If an EmailPolicy has been found for a Pod, kube-mail will forward the email that should be sent to the upstream SMTP server configured in the EmailPolicy. If no EmailPolicy matches, kube-mail will reject the email.

Within your Pod, simply use kube-mail.<namespace>.svc as SMTP server without any authentication. kube-mail will do the rest.

Custom Resources

This controller adds two Custom Resources to your Kubernetes cluster: A SMTPServer and a EmailPolicy resource, both from the kube-mail.helmich.me/v1alpha1 API group.

SMTPServer resources

An SMTPServer resource describes an SMTP server that should be used for outgoing mails. It is defined like follows:

apiVersion: kube-mail.helmich.me/v1alpha1
kind: SMTPServer
metadata:
  name: default
spec:
  server: smtp.yourserver.example
  port: 465
  tls: true
  authType: PLAIN

Concerning the individual properties:

.spec.server
The host name of your upstream SMTP server. This may be either an external service, or a cluster-internal service (for example, specified by its .svc.cluster.local address)
.spec.port
The port that the upstream SMTP server is listening on. If omitted, 587 is assumed as default.
.spec.tls
Defines if the upstream server uses TLS.
.spec.authType
The SMTP authentation type. Supported values are PLAIN, LOGIN, CRAM-MD5 and SCRAM-SHA-1.

EmailPolicy resources

An EmailPolicy defines what kube-mail should do with mails received from a certain pod. An email policy will forward the received email to one of the SMTP servers configured using the SMTPServer resources.

A forwarding email policy is defined like follows:

apiVersion: kube-mail.helmich.me/v1alpha1
kind: EmailPolicy
metadata:
  name: my-policy
spec:
  podSelector:
    matchLabels:
      app.kubernetes.io/name: my-name
  ratelimiting:
    maximum: 100
    period: hour
  sink:
    smtp:
      server:
        name: default
        namespace: default
      credentials:
        name: default-credentials
        namespace: default

Concerning the individual properties:

.spec.podSelector
This is a selector for the Pods this EmailPolicy should apply to. When a SMTP connection is opened to kube-mail, it will identity the source Pod by its Pod IP address and then test if the source Pod matches this label selector.
.spec.ratelimiting
Rate limiting may be configured per Policy. Allowed periods are "hour" and "minute". Messages are counted per policy, not per Pod.
.spec.sink
sink describes where kube-mail should deliver received emails. This may either be an SMTP server (described by a SMTPServer resource) or kube-mail's internal database.
.spec.sink.smtp.server
server is a reference to a SMTPServer resource. It may be placed in a different namespace.
.spec.sink.smtp.credentials
credentials is a reference to a Secret resource with a "username" and "password" key. It may be placed in a different namespace. NOTE: If omitted, kube-mail will attempt an unauthenticated connection to the SMTP server.

How-Tos

Forward all emails from a Pod into a mail catcher

Forwarding all outgoing emails into a mail catcher (like MailHog) is a common use case in development environments, where an application should not be allowed to actually send emails out into the world. To configure kube-mail to forward all emails into a mail catcher, proceed as follows.

  1. Make sure that kube-mail is up and running in a namespace of your choice (for this example, we'll assume that kube-mail is running in the kube-system namespace).

  2. Install the mail catcher of your choice. MailHog, for example, has a Helm chart that makes it easy to install:

    $ helm repo add codecentric https://codecentric.github.io/helm-charts
    $ helm install \
        --namespace kube-system \
        --name mailhog \
        codecentric/mailhog
    
  3. Configure an SMTPServer resource pointing to your MailHog service:

    apiVersion: kube-mail.helmich.me/v1alpha1
    kind: SMTPServer
    metadata:
      name: mailhog
      namespace: kube-system
    spec:
      server: mailhog.kubemail-system.svc.cluster.local
      port: 1025
      tls: false
  4. Configure an EmailPolicy to catch emails from a Pod and forward them to the configured SMTPServer. The policy resource needs to be in the same namespace as the Pod that's sending mails.

    apiVersion: kube-mail.helmich.me/v1alpha1
    kind: EmailPolicy
    metadata:
      name: pod-to-mailhog
      namespace: default # needs to be same namespace as Pod
    spec:
      podSelector:
        matchLabels:
          app.kubernetes.io/name: my-name
      sink:
        smtp:
          server: # server may be in a different namespace than policy
            name: mailhog
            namespace: kube-system

Sending mails from within a Pod

PHP and ssmtp

Provide ssmtp in your Pod; use the following configuration (/etc/ssmtp/ssmtp.conf):

mailhub=kube-mail.kube-system.svc.cluster.local
hostname=foo
FromLineOverride=yes

Then in the php.ini:

sendmail_path = /usr/sbin/ssmtp -t

Pending features

  • Rate limiting
  • TLS for cluster-internal communication