From 552246b5d3321724bd668d603aea13c79a757ead Mon Sep 17 00:00:00 2001 From: Bryan Frimin Date: Fri, 5 Jun 2026 07:22:55 +0200 Subject: [PATCH] Add SMTP_HELLO_NAME to let operators set the EHLO/HELO identity Google Workspace SMTP relay (smtp-relay.gmail.com) rejects generic EHLO identifiers such as "localhost", which is Go net/smtp's default. Operators can now set SMTP_HELLO_NAME to a valid hostname so that c.Hello() is called before StartTLS, satisfying strict relay policies. Fixes https://github.com/getprobo/probo/issues/1284 Signed-off-by: Bryan Frimin --- contrib/helm/charts/probo/templates/deployment.yaml | 4 ++++ contrib/helm/charts/probo/values-production.yaml.example | 3 +++ contrib/helm/charts/probo/values.yaml | 2 ++ pkg/bootstrap/builder.go | 1 + pkg/bootstrap/builder_test.go | 1 + pkg/mailer/mailer.go | 7 +++++++ pkg/probod/probod.go | 1 + pkg/probodconfig/mailer_config.go | 1 + 8 files changed, 20 insertions(+) diff --git a/contrib/helm/charts/probo/templates/deployment.yaml b/contrib/helm/charts/probo/templates/deployment.yaml index b1ed6f468..57310f03e 100644 --- a/contrib/helm/charts/probo/templates/deployment.yaml +++ b/contrib/helm/charts/probo/templates/deployment.yaml @@ -225,6 +225,10 @@ spec: {{- end }} - name: SMTP_TLS_REQUIRED value: {{ .Values.probo.mailer.smtp.tlsRequired | quote }} + {{- if .Values.probo.mailer.smtp.helloName }} + - name: SMTP_HELLO_NAME + value: {{ .Values.probo.mailer.smtp.helloName | quote }} + {{- end }} # OpenAI Integration {{- if .Values.probo.openai.apiKey }} - name: OPENAI_API_KEY diff --git a/contrib/helm/charts/probo/values-production.yaml.example b/contrib/helm/charts/probo/values-production.yaml.example index ce60edc88..909806a55 100644 --- a/contrib/helm/charts/probo/values-production.yaml.example +++ b/contrib/helm/charts/probo/values-production.yaml.example @@ -157,6 +157,9 @@ probo: user: "apikey" password: "CHANGE_ME_SMTP_PASSWORD" tlsRequired: true + # Set to your server's hostname when the relay requires a specific EHLO/HELO + # identity (e.g. Google Workspace smtp-relay.gmail.com rejects "localhost"). + # helloName: "mail.example.com" # OpenAI integration for AI features (optional) openai: diff --git a/contrib/helm/charts/probo/values.yaml b/contrib/helm/charts/probo/values.yaml index ead9d261e..94dcede64 100644 --- a/contrib/helm/charts/probo/values.yaml +++ b/contrib/helm/charts/probo/values.yaml @@ -251,11 +251,13 @@ probo: user: "" password: "" tlsRequired: true + helloName: "" # smtp: # addr: "localhost:1025" # user: "" # password: "" # tlsRequired: false +# helloName: "" # OpenAI integration (optional) openai: diff --git a/pkg/bootstrap/builder.go b/pkg/bootstrap/builder.go index 42a729e38..5b9e56cea 100644 --- a/pkg/bootstrap/builder.go +++ b/pkg/bootstrap/builder.go @@ -166,6 +166,7 @@ func (b *Builder) Build() (*probodconfig.FullConfig, error) { User: b.getEnv("SMTP_USER"), Password: b.getEnv("SMTP_PASSWORD"), TLSRequired: b.getEnvBoolOrDefault("SMTP_TLS_REQUIRED", false), + HelloName: b.getEnv("SMTP_HELLO_NAME"), }, }, Slack: probodconfig.SlackConfig{ diff --git a/pkg/bootstrap/builder_test.go b/pkg/bootstrap/builder_test.go index 352948942..2b47caa78 100644 --- a/pkg/bootstrap/builder_test.go +++ b/pkg/bootstrap/builder_test.go @@ -192,6 +192,7 @@ func TestBuilder_Build_Defaults(t *testing.T) { assert.Equal(t, "no-reply@notification.getprobo.com", cfg.Probod.Notifications.Mailer.SenderEmail) assert.Equal(t, "localhost:1025", cfg.Probod.Notifications.Mailer.SMTP.Addr) assert.False(t, cfg.Probod.Notifications.Mailer.SMTP.TLSRequired) + assert.Empty(t, cfg.Probod.Notifications.Mailer.SMTP.HelloName) assert.Equal(t, 60, cfg.Probod.Notifications.Mailer.MailerInterval) assert.Equal(t, 60, cfg.Probod.Notifications.Slack.SenderInterval) assert.Empty(t, cfg.Probod.Notifications.Slack.SigningSecret) diff --git a/pkg/mailer/mailer.go b/pkg/mailer/mailer.go index 6633bbb6d..0d0471bab 100644 --- a/pkg/mailer/mailer.go +++ b/pkg/mailer/mailer.go @@ -49,6 +49,7 @@ type ( User string Password string TLSRequired bool + HelloName string } SendingWorkerOption func(*sendingHandler) @@ -319,6 +320,12 @@ func (h *sendingHandler) sendMail(ctx context.Context, to []string, msg []byte) defer func() { _ = c.Quit() }() + if h.smtp.HelloName != "" { + if err := c.Hello(h.smtp.HelloName); err != nil { + return fmt.Errorf("SMTP EHLO error: %w", err) + } + } + if h.smtp.TLSRequired { if err := c.StartTLS(&tls.Config{ServerName: host}); err != nil { return fmt.Errorf("TLS negotiation error: %w", err) diff --git a/pkg/probod/probod.go b/pkg/probod/probod.go index ad90f24a4..978289707 100644 --- a/pkg/probod/probod.go +++ b/pkg/probod/probod.go @@ -654,6 +654,7 @@ func (impl *Implm) Run( User: impl.cfg.Notifications.Mailer.SMTP.User, Password: impl.cfg.Notifications.Mailer.SMTP.Password, TLSRequired: impl.cfg.Notifications.Mailer.SMTP.TLSRequired, + HelloName: impl.cfg.Notifications.Mailer.SMTP.HelloName, }, l.Named("sending-worker"), []mailer.SendingWorkerOption{ diff --git a/pkg/probodconfig/mailer_config.go b/pkg/probodconfig/mailer_config.go index 7b7829a47..b836bb24e 100644 --- a/pkg/probodconfig/mailer_config.go +++ b/pkg/probodconfig/mailer_config.go @@ -26,4 +26,5 @@ type SMTPConfig struct { User string `json:"user"` Password string `json:"password"` TLSRequired bool `json:"tls-required"` + HelloName string `json:"hello-name"` }