14729 smtp settings validation for TLS (#15029)

This commit is contained in:
Tim Lee 2023-11-21 11:48:21 -07:00 committed by GitHub
parent 4194c44131
commit 0557f10ac5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 143 additions and 26 deletions

View File

@ -61,8 +61,13 @@ jobs:
# Pre-starting dependencies here means they are ready to go when we need them.
- name: Start Infra Dependencies
# Use & to background this
run: FLEET_MYSQL_IMAGE=${{ matrix.mysql }} docker-compose -f docker-compose.yml -f docker-compose-redis-cluster.yml up -d mysql_test redis redis-cluster-1 redis-cluster-2 redis-cluster-3 redis-cluster-4 redis-cluster-5 redis-cluster-6 redis-cluster-setup minio saml_idp mailhog mailpit &
run: FLEET_MYSQL_IMAGE=${{ matrix.mysql }} docker-compose -f docker-compose.yml -f docker-compose-redis-cluster.yml up -d mysql_test redis redis-cluster-1 redis-cluster-2 redis-cluster-3 redis-cluster-4 redis-cluster-5 redis-cluster-6 redis-cluster-setup minio saml_idp mailhog mailpit smtp4dev_test &
- name: Add TLS certificate for SMTP Tests
run: |
sudo cp tools/smtp4dev/fleet.crt /usr/local/share/ca-certificates/
sudo update-ca-certificates
# It seems faster not to cache Go dependencies
- name: Install Go Dependencies
run: make deps-go

View File

@ -0,0 +1,2 @@
- Fixed: SMTP configuration was failing validation when attempting to send a test email to an SMTP server
that only supports TLS

View File

@ -76,6 +76,19 @@ services:
"--smtp-auth-allow-insecure=true"
]
# SMTP server with TLS
smtp4dev_test:
image: rnwood/smtp4dev:v3
ports:
- "8028:80"
- "1027:25"
volumes:
- ./tools/smtp4dev:/certs
environment:
- ServerOptions__TlsMode=ImplicitTls
- ServerOptions__TlsCertificate=/certs/fleet.crt
- ServerOptions__TlsCertificatePrivateKey=/certs/fleet.key
redis:
image: redis:5
ports:

View File

@ -171,7 +171,17 @@ func (m mailService) sendMail(e fleet.Email, msg []byte) error {
return nil
}
client, err := dialTimeout(smtpHost)
tlsConfig := &tls.Config{
ServerName: e.SMTPSettings.SMTPServer,
InsecureSkipVerify: !e.SMTPSettings.SMTPVerifySSLCerts,
}
var client *smtp.Client
if e.SMTPSettings.SMTPEnableTLS {
client, err = dialTimeout(smtpHost, tlsConfig)
} else {
client, err = dialTimeout(smtpHost, nil)
}
if err != nil {
return fmt.Errorf("could not dial smtp host: %w", err)
}
@ -179,11 +189,7 @@ func (m mailService) sendMail(e fleet.Email, msg []byte) error {
if e.SMTPSettings.SMTPEnableStartTLS {
if ok, _ := client.Extension("STARTTLS"); ok {
config := &tls.Config{
ServerName: e.SMTPSettings.SMTPServer,
InsecureSkipVerify: !e.SMTPSettings.SMTPVerifySSLCerts,
}
if err = client.StartTLS(config); err != nil {
if err = client.StartTLS(tlsConfig); err != nil {
return fmt.Errorf("startTLS error: %w", err)
}
}
@ -221,9 +227,11 @@ func (m mailService) sendMail(e fleet.Email, msg []byte) error {
return nil
}
const dialTimeoutDuration = 28 * time.Second
// dialTimeout sets a timeout on net.Dial to prevent email from attempting to
// send indefinitely.
func dialTimeout(addr string) (client *smtp.Client, err error) {
func dialTimeout(addr string, tlsConfig *tls.Config) (client *smtp.Client, err error) {
// Ensure that errors are always returned after at least 5s to
// eliminate (some) timing attacks (in which a malicious user tries to
// port scan using the email functionality in Fleet)
@ -235,7 +243,13 @@ func dialTimeout(addr string) (client *smtp.Client, err error) {
}
}()
conn, err := net.DialTimeout("tcp", addr, 28*time.Second)
var conn net.Conn
if tlsConfig == nil {
conn, err = net.DialTimeout("tcp", addr, dialTimeoutDuration)
} else {
conn, err = tls.DialWithDialer(&net.Dialer{Timeout: dialTimeoutDuration}, "tcp", addr, tlsConfig)
}
if err != nil {
return nil, fmt.Errorf("dialing with timeout: %w", err)
}

View File

@ -16,7 +16,7 @@ var testFunctions = [...]func(*testing.T, fleet.MailService){
testSMTPPlainAuth,
testSMTPPlainAuthInvalidCreds,
testSMTPSkipVerify,
testSMTPNoAuth,
testSMTPNoAuthWithTLS,
testMailTest,
}
@ -47,9 +47,9 @@ func testSMTPPlainAuth(t *testing.T, mailer fleet.MailService) {
SMTPAuthenticationMethod: fleet.AuthMethodNamePlain,
SMTPUserName: "mailpit-username",
SMTPPassword: "mailpit-password",
SMTPEnableTLS: true,
SMTPVerifySSLCerts: true,
SMTPEnableStartTLS: true,
SMTPEnableTLS: false,
SMTPVerifySSLCerts: false,
SMTPEnableStartTLS: false,
SMTPPort: 1026,
SMTPServer: "localhost",
SMTPSenderAddress: "test@example.com",
@ -73,9 +73,9 @@ func testSMTPPlainAuthInvalidCreds(t *testing.T, mailer fleet.MailService) {
SMTPAuthenticationMethod: fleet.AuthMethodNamePlain,
SMTPUserName: "mailpit-username",
SMTPPassword: "wrong",
SMTPEnableTLS: true,
SMTPVerifySSLCerts: true,
SMTPEnableStartTLS: true,
SMTPEnableTLS: false,
SMTPVerifySSLCerts: false,
SMTPEnableStartTLS: false,
SMTPPort: 1026,
SMTPServer: "localhost",
SMTPSenderAddress: "test@example.com",
@ -102,7 +102,7 @@ func testSMTPSkipVerify(t *testing.T, mailer fleet.MailService) {
SMTPEnableTLS: true,
SMTPVerifySSLCerts: false,
SMTPEnableStartTLS: true,
SMTPPort: 1025,
SMTPPort: 1027,
SMTPServer: "localhost",
SMTPSenderAddress: "test@example.com",
},
@ -115,7 +115,7 @@ func testSMTPSkipVerify(t *testing.T, mailer fleet.MailService) {
assert.Nil(t, err)
}
func testSMTPNoAuth(t *testing.T, mailer fleet.MailService) {
func testSMTPNoAuthWithTLS(t *testing.T, mailer fleet.MailService) {
mail := fleet.Email{
Subject: "no auth",
To: []string{"bob@foo.com"},
@ -124,7 +124,8 @@ func testSMTPNoAuth(t *testing.T, mailer fleet.MailService) {
SMTPAuthenticationType: fleet.AuthTypeNameNone,
SMTPEnableTLS: true,
SMTPVerifySSLCerts: true,
SMTPPort: 1025,
SMTPEnableStartTLS: true,
SMTPPort: 1027,
SMTPServer: "localhost",
SMTPSenderAddress: "test@example.com",
},
@ -145,11 +146,12 @@ func testMailTest(t *testing.T, mailer fleet.MailService) {
SMTPConfigured: true,
SMTPAuthenticationType: fleet.AuthTypeNameUserNamePassword,
SMTPAuthenticationMethod: fleet.AuthMethodNamePlain,
SMTPUserName: "mailpit-username",
SMTPPassword: "mailpit-password",
SMTPUserName: "foo",
SMTPPassword: "bar",
SMTPEnableTLS: true,
SMTPVerifySSLCerts: true,
SMTPPort: 1026,
SMTPEnableStartTLS: true,
SMTPPort: 1027,
SMTPServer: "localhost",
SMTPSenderAddress: "test@example.com",
},

View File

@ -59,8 +59,8 @@ func TestMailService(t *testing.T) {
SMTPAuthenticationMethod: fleet.AuthMethodNamePlain,
SMTPUserName: "mailpit-username",
SMTPPassword: "mailpit-password",
SMTPEnableTLS: true,
SMTPVerifySSLCerts: true,
SMTPEnableTLS: false,
SMTPVerifySSLCerts: false,
SMTPPort: 1026,
SMTPServer: "localhost",
SMTPSenderAddress: "foobar@example.com",
@ -103,8 +103,8 @@ func TestMailService(t *testing.T) {
"authentication_method": "authmethod_plain",
"user_name": "mailpit-username",
"password": "mailpit-password",
"enable_ssl_tls": true,
"verify_ssl_certs": true,
"enable_ssl_tls": false,
"verify_ssl_certs": false,
"port": 1026,
"server": "127.0.0.1",
"sender_address": "foobar_updated@example.com"

29
tools/smtp4dev/fleet.crt Normal file
View File

@ -0,0 +1,29 @@
-----BEGIN CERTIFICATE-----
MIIE5jCCAs6gAwIBAgIJAKq0+FAVArUhMA0GCSqGSIb3DQEBCwUAMBUxEzARBgNV
BAMMCkZsZWV0IFRlc3QwHhcNMjAxMDE1MTg1ODE5WhcNMzAwNzE1MTg1ODE5WjAV
MRMwEQYDVQQDDApGbGVldCBUZXN0MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC
CgKCAgEAttQ62lpMq48/XjQFxYg47D2fgKgTBMjDNSfCt9VpqE3xPnybmWo8VZtk
jmrFM50IhagyYSjvl9iqdrnsl3ZV8KYbWEy6849zDYF1SudmC7/pJyH7QvpKgL7V
4McM62jM905hyFy9KZTAlFiaeezWSWJre7kHsK2u5tsqS6ElatEZmF59sInixaRw
RqVxOhtDm7Sl1c5xKWM6phWoJfykspFpu5J6N2jRQXCfzBYoQWN76OohGouit/BQ
C1xvm+f7wgGZgbbfDjUoHAe9Yhd3XzZsYTgMDt/SRJRDnxFZwo8BAkY9yJm7f3dQ
AEhgJ66KbyoxITdgma1hgmeWibZY4hVymcRxB1B3RNN2at19RNy2J+brMxlG0KZk
nD77EqidrwLAlYcdeU3yLjt0vYPxT+RW7l1jiZlVi/oaykAmVfOhWnTnTwbsYs7O
UMyMyYHQECEs98ex7wrjThIBJScqhsSN1ipAxr5RgaDr+U5IR+tLhMewBy8So+nf
2YuMhLfkCgoY80ELhz5F8avts5hksB0hqnNYr+Nlwm6eXqEPZSzFJmdc1IbmWzq2
7UH1OQmBFF2qr2j/8dcM+oPNgjrEEQjtyW0S4j2PhjSEbINgcwu0AaABssLI80Vm
Gp1TjUGA92rMwIjlAtcUUB5FOKSS8vAXb1VcDWMkybh9sHj4Z0ECAwEAAaM5MDcw
NQYDVR0RBC4wLIIJbG9jYWxob3N0ghRob3N0LmRvY2tlci5pbnRlcm5hbIIJMTI3
LjAuMC4xMA0GCSqGSIb3DQEBCwUAA4ICAQBZOY++LNRTVG8XlQGVlOloEKA2WY3P
gXKJLSM7xWSxj2co0v+noyhoInHT7ysub8en59Et+vN53/0OobxNIdEKDUdqs38R
F++Oy6s/MhFHSo87F06t+W91/60ub4iFRHfev1qeNFV6Yzv9sFJ5LpXLFk+qVDb8
pPyFFE1bXjctDPjD5gUj+Y34XikVKzMb7xddWCNs34v1KCaCBW7kkfefxiZiDR6g
lCEkDzp6xaLS898oCbfFakjr4bvOgBP1IqXLIDLPMhivaxNAooHTtu/3ezp7puix
TSDkjlkStDtEFw/wjyaMcEkk51Gs1ponBbADLRxQ50AHDWk/4vy8GcIVc6CdVEOA
Zw12FN06C4Jviiiv6uCXZ6iZ+V+pjGiGmSNYF+kruUs8BfrJIB89lqxpdQ4Kx01j
AuSFvjRRvIPmvApSdKEjLcY3AYRivXsB/hASMBbjh/p1f/JzSJdxoqSvONhNQJuh
+wcdNVQhGAv3kkLn/HMHTBl2Ur+9tQaJrnR1tWl1IzwLRJIi0Soyp/q5ZjQyFglj
32xW83DZhtpQ2SI1QGy4AvWIPnGHZhMfav02KnKRhZdOMW4oekXRMrwiyXCqIazc
xXzAlCq8dHdP2Y9uvfFxVFyE+uSfkcPxX+DG/ZnpgCS27oKA/qLCybJamlqtveNs
RSjNe5qwGi0ifA==
-----END CERTIFICATE-----

52
tools/smtp4dev/fleet.key Normal file
View File

@ -0,0 +1,52 @@
-----BEGIN PRIVATE KEY-----
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
mWZen+G8ANnds10V7TmcpoaPSSoJMF0=
-----END PRIVATE KEY-----