Introducing SMTP2Graph
In a recent customer project, I was looking for a way to let a legacy application send emails via Office 365 or more specifically, the MS Graph API.
The customer's Office 365 environment did not allow using SMTP connections anymore, so sending emails via the Graph API seemed the only possible solution. I wasn't keen on writing my own SMTP proxy. Searching the internet didn't help a lot, so I asked the AI.
I didn't expect a good answer, but was surprised when the SMTP2Graph project was mentioned, as I haven't heard of the project so far. What is SMTP2Graph? It's a versatile proxy that will run an SMTP server that relays messages over Office 365 using the Microsoft Graph API.
As a bonus, you can run it with Docker easily:
docker run -p 587:587 -v ./smtp2graph:/data smtp2graph/smtp2graph:latest
The configuration is done in a YAML file:
mode: full
send:
appReg:
tenant: <TENANT_ID>
id: <SECRET_ID>
secret: <SECRET_KEY>
It even allows for configuring SMTP authentication credentials:
receive:
requireAuth: true
users:
- username: users
password: P@ssword!
You can see the complete example configuration here.
Since the project already ships a Docker image, we were able to use it directly in a Nomad jobfile:
job "smtp2graph" {
datacenters = ["dc1"]
type = "service"
group "smtp2graph-group" {
count = 1
network {
port "smtp" {
static = 25
}
}
volume "smtp2graph-data" {
type = "host"
source = "smtp2graph"
access_mode = "single-node-writer"
attachment_mode = "file-system"
}
task "smtp2graph-task" {
driver = "docker"
template {
data = <<EOH
mode: full
send:
appReg:
tenant: {{ with nomadVar "nomad/jobs/smtp2graph" }}{{ .TENANT_ID }}{{ end }}
id: {{ with nomadVar "nomad/jobs/smtp2graph" }}{{ .SECRET_ID }}{{ end }}
secret: {{ with nomadVar "nomad/jobs/smtp2graph" }}{{ .SECRET }}{{ end }}
receive:
port: 25
requireAuth: true
allowInsecureAuth: false
users:
- username: {{ with nomadVar "nomad/jobs/smtp2graph" }}{{ .SMTP_USERNAME }}{{ end }}
password: {{ with nomadVar "nomad/jobs/smtp2graph" }}{{ .SMTP_PASS }}{{ end }}
EOH
destination = "local/config.yml"
}
config {
image = "smtp2graph/smtp2graph:v1.1.4"
ports = [ "smtp" ]
volumes = [ "local/config.yml:/data/config.yml" ]
}
volume_mount {
volume = "smtp2graph-data"
destination = "/data"
read_only = false
}
service {
name = "smtp2graph"
provider = "nomad"
port = "smtp"
}
}
}
}
The only downside: The application does not log to stdout, it logs into a file in the /data/logs directory.