Laden...

SMTP + Anhänge in Container (Alpine)

Erstellt von Campy vor 3 Jahren Letzter Beitrag vor 3 Jahren 524 Views
C
Campy Themenstarter:in
439 Beiträge seit 2008
vor 3 Jahren
SMTP + Anhänge in Container (Alpine)

Hallo zusammen,

ich habe folgenden Code der in einem Docker Container (mcr.microsoft.com/dotnet/runtime:5.0-alpine3.12-amd64) zum Versenden von E-Mails ausgeführt wird:
(Ich weiß, der Code ist noch nicht optimal aber habe ihn in ein Testprojekt kopiert.


var message = new MimeMessage();
                message.From.Add(new MailboxAddress(email.SenderName.Length > 0 ? email.SenderName : Parameters["Sendername"],
                    email.SenderEmail.Length > 0 ? email.SenderEmail : Parameters["Sender"]));

                var recipients = new List<string>();
                var ccRecipients = new List<string>();
                var bccRecipients = new List<string>();

                if (!string.IsNullOrEmpty(email.Recipients))
                    recipients = new List<string>(email.Recipients.Split(";".ToCharArray()));

                if (!string.IsNullOrEmpty(email.RecipientsCC))
                    ccRecipients = new List<string>(email.RecipientsCC.Split(";".ToCharArray()));

                if (!string.IsNullOrEmpty(email.RecipientsBCC))
                    bccRecipients = new List<string>(email.RecipientsBCC.Split(";".ToCharArray()));

                if (bccRecipients != null)
                {
                    foreach (string bccRecipient in bccRecipients)
                        if (!string.IsNullOrEmpty(bccRecipient))
                            message.Bcc.Add(MailboxAddress.Parse(bccRecipient));
                }

                if (ccRecipients != null)
                {
                    foreach (string ccRecipient in ccRecipients)
                        if (!string.IsNullOrEmpty(ccRecipient))
                            message.Cc.Add(MailboxAddress.Parse(ccRecipient));
                }

                if (recipients != null)
                {
                    foreach (string Recipient in recipients)
                        if (!string.IsNullOrEmpty(Recipient))
                            message.Cc.Add(MailboxAddress.Parse(Recipient));
                }

                message.Subject = email.Subject;

                var body = new TextPart("plain")
                {
                    Text = email.Body
                };

                var multipart = new Multipart("mixed");
                multipart.Add(body);
                
                if (email.Attachment1 != null)
                {
                    var attachment1 = new MimePart("application", "pdf")
                    {
                        Content = new MimeContent(new MemoryStream(email.Attachment1), ContentEncoding.Default),
                        ContentDisposition = new ContentDisposition(ContentDisposition.Attachment),
                        ContentTransferEncoding = ContentEncoding.Base64,
                        FileName = email.Attachment1Name
                    };
                    multipart.Add(attachment1);
                }

                // now set the multipart/mixed as the message body
                message.Body = multipart;

                try
                {
                    using (var client = new SmtpClient())
                    {
                        client.Connect(server, 25, false);

                        // Note: only needed if the SMTP server requires authentication
                        if (!string.IsNullOrEmpty(username) && !string.IsNullOrEmpty(password))
                            client.Authenticate(username, password);

                        client.Send(message);
                        client.Disconnect(true);
                    }

                    _logger.LogDebug("Die E-Mail {mailid} wurde erfolgreich versendet", email.Id);
                    email.Success = true;
                }
                catch(Exception ex)
                {
                    _logger.LogError(ex, "Fehler beim Versenden der E-Mail {mailid}", email.Id);
                }
                finally
                {
                    email.RetryCount = email.RetryCount + 1;
                    _logger.LogDebug("Neuer RetryCount für E-Mail {maild} beträgt {retrycount}", email.Id, email.RetryCount);
                    email.LastTry = DateTime.Now;

                    if (emailProxy.Put(email))
                        _logger.LogDebug("Die E-Mail wurde erfolgreich aktualisiert");
                    else
                        _logger.LogError("Fehler beim Speichern der Änderungen der E-Mail");
                }

Das ganze funktioniert nun im Docker Container, solange Anhang1 nicht zwei Bilder enthält (Dateigröße gute 50kb)..
Starte ich die Anwendung unter Windows, gleicher SMTP Server und gleiche Datenbank, funktioniert es sofort.

Hat jemand eine Idee? Sollte ich mal ein Ubuntu Image probieren?

Vielen Dank!

A programmer is just a tool, which converts coffeine into code! 🙂

C
Campy Themenstarter:in
439 Beiträge seit 2008
vor 3 Jahren

Habs nun mit einem aktuellen Ubuntu getestet - funktioniert ebenso nicht.

Fehlermeldung:
System.IO.IOException: Connection timed out
---> System.Net.Sockets.SocketException (110): Connection timed out
at System.Net.Sockets.Socket.Receive(Byte[] buffer, Int32 offset, Int32 size, SocketFlags socketFlags)
at MailKit.Net.NetworkStream.Read(Byte[] buffer, Int32 offset, Int32 count)
--- End of inner exception stack trace ---
at MailKit.Net.NetworkStream.Read(Byte[] buffer, Int32 offset, Int32 count)
at MailKit.Net.Smtp.SmtpStream.ReadAheadAsync(Boolean doAsync, CancellationToken cancellationToken)
at MailKit.Net.Smtp.SmtpStream.ReadResponseAsync(Boolean doAsync, CancellationToken cancellationToken)
at MailKit.Net.Smtp.SmtpStream.ReadResponse(CancellationToken cancellationToken)
at MailKit.Net.Smtp.SmtpClient.DataAsync(FormatOptions options, MimeMessage message, Int64 size, Boolean doAsync, CancellationToken cancellationToken, ITransferProgress progress)
at MailKit.Net.Smtp.SmtpClient.SendAsync(FormatOptions options, MimeMessage message, MailboxAddress sender, IList`1 recipients, Boolean doAsync, CancellationToken cancellationToken, ITransferProgress progress)
at MailKit.Net.Smtp.SmtpClient.Send(FormatOptions options, MimeMessage message, CancellationToken cancellationToken, ITransferProgress progress)

Der Logoutput vom Postfix Relay sieht so aus:

Fehlermeldung:
Mar 18 08:21:00 postfix/smtpd[814]: connect from unknown[172.16.3.4]
Mar 18 08:21:00 postfix/smtpd[814]: B76D31A0A3F: client=unknown[172.16.3.4]
Mar 18 08:21:00 postfix/cleanup[815]: B76D31A0A3F: message-id=<ZH2CU9WQ8DU4.1MGZG2DZJZLO1@demodaten>
Mar 18 08:23:00 postfix/smtpd[630]: timeout after DATA (65713 bytes) from unknown[172.16.3.4]
Mar 18 08:23:00 postfix/smtpd[630]: disconnect from unknown[172.16.3.4] ehlo=1 mail=1 rcpt=1 data=0/1 commands=3/4

A programmer is just a tool, which converts coffeine into code! 🙂

D
261 Beiträge seit 2015
vor 3 Jahren

Wenn man Probleme mit einer Library hat einfach mal in den Github Issues der Library suchen.
Auf die schnelle hab ich das hier gefunden: https://github.com/jstedfast/MailKit/issues/1157

C
Campy Themenstarter:in
439 Beiträge seit 2008
vor 3 Jahren

Ja da hast du Recht - ich habs sogar schon mit System.Net.Mail probiert gehabt (einfach mal einen Schritt zurück) aber auch da kam der gleiche Fehler 😠

A programmer is just a tool, which converts coffeine into code! 🙂

D
261 Beiträge seit 2015
vor 3 Jahren

Der Autor der Library vermutet "probably a .NET Core runtime bug on Linux". Wenn das tatäschlich so ist, dann bringen dir leider auch andere Libraries nichts, sofern diese auf der gleichen Basis aufsetzen.

C
Campy Themenstarter:in
439 Beiträge seit 2008
vor 3 Jahren

Der Autor der Library vermutet "probably a .NET Core runtime bug on Linux". Wenn das tatäschlich so ist, dann bringen dir leider auch andere Libraries nichts, sofern diese auf der gleichen Basis aufsetzen.

Was halt komisch ist und bleibt, das Problem besteht überhaupt nicht, wenn keine Anhänge angehängt wurden, und auch nur bei einem Teil von Anhängen..

A programmer is just a tool, which converts coffeine into code! 🙂

16.827 Beiträge seit 2008
vor 3 Jahren

Campy, naja; die Fehlermeldung zeigt einen Socket-Fehler beim Ressounrcezugriff.
Wenn Du keine Attachments verlinkst, wird eben das Zeug auch nicht ausgeführt; ergo ist der Bug wohl egal, wenn Du keine Attachments hast.

Kannst Du mal bisschen mehr Infos geben wie zB: wo liegen die Ressourcen? Worin unterscheiden sich Ressourcen, die funktionieren und die nicht funktionieren?
Ansonsten einfach mal MailKit Source ziehen und selbst debuggen; das is ja das schöne an Open Source.

C
Campy Themenstarter:in
439 Beiträge seit 2008
vor 3 Jahren

Campy, naja; die Fehlermeldung zeigt einen Socket-Fehler beim Ressounrcezugriff.
Wenn Du keine Attachments verlinkst, wird eben das Zeug auch nicht ausgeführt; ergo ist der Bug wohl egal, wenn Du keine Attachments hast.

Ja das stimmt, jedoch funktioniert ein Teil (alle sind PDFs die vom gleichen Generator generiert wurden).

Kannst Du mal bisschen mehr Infos geben wie zB: wo liegen die Ressourcen? Worin unterscheiden sich Ressourcen, die funktionieren und die nicht funktionieren?
Ansonsten einfach mal MailKit Source ziehen und selbst debuggen; das is ja das schöne an Open Source.

Die E-Mails liegen in einer Datenbank (postgresql) als bytea (byte[]) und werden per WebApi Dienst abgeholt


        private byte[] _Attachment1;
        public virtual byte[] Attachment1
        {
            get { return _Attachment1; }
            set { _Attachment1 = value; NotifyPropertyChanged("Attachment1"); }
        }

So wird es an Mailkit übergeben:


 var body = new TextPart("plain")
                {
                    Text = email.Body
                };

                var multipart = new Multipart("mixed");
                multipart.Add(body);
                
                if (email.Attachment1 != null)
                {
                    var attachment1 = new MimePart("application", "pdf")
                    {
                        Content = new MimeContent(new MemoryStream(email.Attachment1), ContentEncoding.Default),
                        ContentDisposition = new ContentDisposition(ContentDisposition.Attachment),
                        ContentTransferEncoding = ContentEncoding.Base64,
                        FileName = email.Attachment1Name
                    };
                    multipart.Add(attachment1);
                }

                // now set the multipart/mixed as the message body
                message.Body = multipart;

Hast du einen Tipp wie ich es am besten Debuggen könnte, da der Fehler nur im Docker Container besteht obwohl alle Rahmenparameter sonst die Gleichen sind.
Führe ich die Anwendung bei mir unter Windows aus funktioniert es sofort problemlos..

Danke im Voraus!

A programmer is just a tool, which converts coffeine into code! 🙂

16.827 Beiträge seit 2008
vor 3 Jahren

Hast Du Dir überhaupt mal den GitHub Issue durchgelesen, der für den Fehler verantwortlich sein könnte?
Da steht ja klar, dass es eine Race Condition / Safety des Threads ist.

Das heisst, dass der Fehler auftreten kann, aber nicht muss. Ergo kann das bei einem File auftreten; muss aber nicht.
Es kann bei File A auftreten, aber bei B nicht. Oder bei beidem. Oder bei keinem.

WebApi Dienst

Web Api ist ein Produkt. Was du meinst ist eine Http Api.
Aber das ist nur Wording.

Du kannst in MailKit auch Attachments direkt als Byte übergeben, ohne explizit ein MemoryStream verwenden zu müssen.
Aber da unbekannt ist, wer für den Socket-Error verantwortlich ist heisst das nicht, dass es dann geht..

da der Fehler nur im Docker Container besteht

Vorhin hast noch gesagt es gibt den Error auch unter Ubuntu; und den Fehler gibts offenbar auf alle Linux-Varianten.
Dann verwende doch einfach Docker lokal zum Debuggen oder die WSL. Wo ist das Problem?
Debug Your .NET Core Apps in WSL 2 with Visual Studio | .NET Blog

C
Campy Themenstarter:in
439 Beiträge seit 2008
vor 3 Jahren

Danke Abt, ich konnte das Projekt auch erfolgreich und ohne Probleme unter WSL debuggen.
Da es auch dort ohne Probleme lief, haben wir uns weiter auf die Suche gemacht =>

Es lag wohl am Zusammenspiel von Docker Port-Mappings des Relay Containers und der OpnSense Firewall die eingesetzt wird.
Eigentlich befinden sich die SMTP-Container und der Relay Container im gleichen Netzwerk - durch das PortMapping hat das Docker anscheinend anders geroutet und so lag die OpnSense dazwischen. Komisch und fragwürdig bleibt nur, wieso dies erst beim überschreiten der MTU Size geblockt hat.

A programmer is just a tool, which converts coffeine into code! 🙂