Unable to send email to SparkPost from Google Compute Engine


(Watercolor Games) #1

I’m setting up a new Discourse instance on a friend’s Google Compute Engine VPS running Arch. Things went pretty well, except for SMTP. (Side note: although I’m using a friend’s VPS, this Discourse instance is for me, not him. He’s just providing hosting.) I already had a Discourse instance working perfectly on a DigitalOcean droplet running CentOS 7 a few days ago, but… he nuked the VPS out of rage because of community drama. So I had to move the site to a new server hosted by someone I actually know and trust.

I’m using the exact same SparkPost credentials and SMTP config that I did on the previous box, only difference being I’m using port 2525. GCE blocks 25, 465 and 587.

I’m stuck at the “Finish Installation” screen, the part where I set up my admin account, select the theme, emoji pack, etc. I’m not receiving my confirmation email. I performed the usual troubleshooting steps - checked config, it’s good. Checked SparkPost, no messages were being registered as even “rejected” - indicating a connection error between SparkPost and Discourse.

Sure enough, I look in my production.log and I’m getting

Job exception: end of file reached

directly after all attempts to send mail.

My next troubleshooting step involved writing a quick C# program that uses my SparkPost configuration settings and System.Net.Mail.SmtpClient to send a test email that confirms that the email is actually deliverable. I used the exact same config as I have in Discourse. I ran the program three times.

Once, in my development environment where I wrote the program. Second time was through Mono on Arch Linux, on the computer running the Docker container that Discourse is in. The third time was actually in the Docker Container by copying the .exe file into the /var/discourse/shared/standalone folder on the host, then running

sudo /var/discourse/launcher enter app
# in container
apt-get install mono-complete
# now I have .NET Framework
cd /shared
mono SMTPTestUtility.exe

On my Windows development environment, and on the Docker host, the program succeeded - it simply printed “Done”, waited for a keypress, and exited. As soon as it said “Done”, the test message appeared in my inbox. Both times.

However, I programmed it to print out a FULL stack trace and any and all error messages before exiting if the program experiences an error at all. Thank God I did, because when I ran the program inside the Docker container, this was its output.

root@archlinux-app:/shared# mono smtp.exe
System.Net.Mail.SmtpException: Message could not be sent. ---> System.IO.IOException: The authentication or decryption has failed. ---> System.IO.IOException: The authentication or decryption has failed. ---> System.InvalidOperationException: SSL authentication error: RemoteCertificateNotAvailable, RemoteCertificateChainErrors
  at Mono.Security.Protocol.Tls.RecordProtocol.EndReceiveRecord (IAsyncResult asyncResult) <0x41e76ea0 + 0x000ff> in <filename unknown>:0
  at Mono.Security.Protocol.Tls.SslClientStream.SafeEndReceiveRecord (IAsyncResult ar, Boolean ignoreEmpty) <0x41e76de0 + 0x0002b> in <filename unknown>:0
  at Mono.Security.Protocol.Tls.SslClientStream.NegotiateAsyncWorker (IAsyncResult result) <0x41e72000 + 0x00213> in <filename unknown>:0
  --- End of inner exception stack trace ---
  at Mono.Security.Protocol.Tls.SslClientStream.EndNegotiateHandshake (IAsyncResult result) <0x41e8e830 + 0x000bf> in <filename unknown>:0
  at Mono.Security.Protocol.Tls.SslStreamBase.AsyncHandshakeCallback (IAsyncResult asyncResult) <0x41e8e5d0 + 0x0007f> in <filename unknown>:0
  --- End of inner exception stack trace ---
  at Mono.Security.Protocol.Tls.SslStreamBase.EndRead (IAsyncResult asyncResult) <0x41e703b0 + 0x00153> in <filename unknown>:0
  at System.Net.Security.SslStream.EndAuthenticateAsClient (IAsyncResult asyncResult) <0x41e70310 + 0x0003e> in <filename unknown>:0
  at System.Net.Security.SslStream.AuthenticateAsClient (System.String targetHost, System.Security.Cryptography.X509Certificates.X509CertificateCollection clientCertificates, SslProtocols enabledSslProtocols, Boolean checkCertificateRevocation) <0x41e693f0 + 0x00055> in <filename unknown>:0
  at System.Net.Mail.SmtpClient.InitiateSecureConnection () <0x41e68f20 + 0x000df> in <filename unknown>:0
  at System.Net.Mail.SmtpClient.SendCore (System.Net.Mail.MailMessage message) <0x41e66500 + 0x0020f> in <filename unknown>:0
  at System.Net.Mail.SmtpClient.SendInternal (System.Net.Mail.MailMessage message) <0x41e5da00 + 0x00207> in <filename unknown>:0
  at System.Net.Mail.SmtpClient.Send (System.Net.Mail.MailMessage message) <0x41e5d670 + 0x00113> in <filename unknown>:0
  --- End of inner exception stack trace ---
  at System.Net.Mail.SmtpClient.Send (System.Net.Mail.MailMessage message) <0x41e5d670 + 0x001c7> in <filename unknown>:0
  at System.Net.Mail.SmtpClient.Send (System.String from, System.String to, System.String subject, System.String body) <0x41e5c1c0 + 0x00063> in <filename unknown>:0
  at SMTPTestUtility.Program.Main (System.String[] args) <0x41e32d50 + 0x000eb> in <filename unknown>:0
Done.

So obviously it’s not a Discourse configuration issue. But given this issue is preventing me from using Discourse, I thought I’d post it here. Maybe there’s something useful in that C# stacktrace that just wouldn’t show in the Discourse production.log. The most I know is it’s an SSL certificate error. Given it only happens inside the Docker container, maybe there’s some weird issue with it?

Any help would be greatly appreciated. :slight_smile:


(Michael Brown) #2

I have to say I’m impressed by the steps you’ve taken so far :slight_smile:

Can you try something simpler? Connecting with openssl should be easier and then you can see everything that is going wrong.

For example here’s an example of me connecting to my mailserver and authenticating:

○ → openssl s_client -connect mail.my.domain:587 -starttls smtp
CONNECTED(00000003)
depth=2 O = Digital Signature Trust Co., CN = DST Root CA X3
verify return:1
depth=1 C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3
verify return:1
depth=0 CN = mail.my.domain
verify return:1
---
Certificate chain
 0 s:/CN=mail.my.domain
   i:/C=US/O=Let's Encrypt/CN=Let's Encrypt Authority X3
 1 s:/C=US/O=Let's Encrypt/CN=Let's Encrypt Authority X3
   i:/O=Digital Signature Trust Co./CN=DST Root CA X3
---
Server certificate
-----BEGIN CERTIFICATE-----
«certificate»
-----END CERTIFICATE-----
subject=/CN=mail.my.domain
issuer=/C=US/O=Let's Encrypt/CN=Let's Encrypt Authority X3
---
No client certificate CA names sent
Peer signing digest: SHA512
Server Temp Key: ECDH, P-256, 256 bits
---
SSL handshake has read 3675 bytes and written 466 bytes
---
New, TLSv1/SSLv3, Cipher is ECDHE-RSA-AES256-GCM-SHA384
Server public key is 2048 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
SSL-Session:
    Protocol  : TLSv1.2
    Cipher    : ECDHE-RSA-AES256-GCM-SHA384
    Session-ID: 45936851EA88A94856253F306E38127A8A747401C3C6A930A2EE15590143793F
    Session-ID-ctx: 
    Master-Key: 92EB4033268950FB90B23B473162D66F8D8A2A6600D671000D891CFA30868FE671AE0C0B3E062F52628834288AA528E0
    Key-Arg   : None
    PSK identity: None
    PSK identity hint: None
    SRP username: None
    Start Time: 1527874911
    Timeout   : 300 (sec)
    Verify return code: 0 (ok)
---
250 SMTPUTF8
auth plain bWljaGFlbABtaWNoYWVsAGNvcnJlY3QgaG9yc2UgYmF0dGVyeSBzdGFwbGU=
235 2.7.0 Authentication successful
(rest of SMTP transaction continues)

The odd thing is that sparkpost doesn’t seem to respond in my testing past the STARTTLS negotiation on either 2525 or 587:

○ → openssl s_client -connect smtp.sparkpostmail.com:587 -starttls smtp
CONNECTED(00000003)
depth=2 C = US, O = GeoTrust Inc., CN = GeoTrust Global CA
verify return:1
depth=1 C = US, O = GeoTrust Inc., CN = RapidSSL SHA256 CA
verify return:1
depth=0 CN = *.sparkpostmail.com
verify return:1
---
Certificate chain
 0 s:/CN=*.sparkpostmail.com
   i:/C=US/O=GeoTrust Inc./CN=RapidSSL SHA256 CA
 1 s:/C=US/O=GeoTrust Inc./CN=RapidSSL SHA256 CA
   i:/C=US/O=GeoTrust Inc./CN=GeoTrust Global CA
 2 s:/C=US/O=GeoTrust Inc./CN=GeoTrust Global CA
   i:/C=US/O=Equifax/OU=Equifax Secure Certificate Authority
---
Server certificate
-----BEGIN CERTIFICATE-----
«certificate»
-----END CERTIFICATE-----
«elided»
    Verify return code: 0 (ok)
---
250 8BITMIME
auth plain bWljaGFlbABtaWNoYWVsAGNvcnJlY3QgaG9yc2UgYmF0dGVyeSBzdGFwbGU=
«read:errno=0»

To generate the auth string (replace with your credentials, naturally):

○ → python -c 'import base64; print(base64.encodestring("michael\x00michael\x00correct horse battery staple"))'
bWljaGFlbABtaWNoYWVsAGNvcnJlY3QgaG9yc2UgYmF0dGVyeSBzdGFwbGU=

I’m curious to see what results you get using openssl s_client -connect smtp.sparkpostmail.com:2525 -starttls smtp and if that points you at what’s going wrong.


(Watercolor Games) #3

Alrighty, this is what I got when I ran the command within the container. Not gonna lie, I’ve got no idea what any of this is babbling on about. :stuck_out_tongue:

root@archlinux-app:/var/www/discourse# openssl s_client -connect smtp.sparkpostmail.com:2525 -starttls smtp
CONNECTED(00000003)
depth=2 C = US, O = GeoTrust Inc., CN = GeoTrust Global CA
verify return:1
depth=1 C = US, O = GeoTrust Inc., CN = RapidSSL SHA256 CA
verify return:1
depth=0 CN = *.sparkpostmail.com
verify return:1
---
Certificate chain
 0 s:/CN=*.sparkpostmail.com
   i:/C=US/O=GeoTrust Inc./CN=RapidSSL SHA256 CA
 1 s:/C=US/O=GeoTrust Inc./CN=RapidSSL SHA256 CA
   i:/C=US/O=GeoTrust Inc./CN=GeoTrust Global CA
 2 s:/C=US/O=GeoTrust Inc./CN=GeoTrust Global CA
   i:/C=US/O=Equifax/OU=Equifax Secure Certificate Authority
---
Server certificate
-----BEGIN CERTIFICATE-----
MIIGcTCCBVmgAwIBAgIQaiTPGEDNIsuSahovj0xtojANBgkqhkiG9w0BAQsFADBC
MQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEbMBkGA1UEAxMS
UmFwaWRTU0wgU0hBMjU2IENBMB4XDTE2MTAxMjAwMDAwMFoXDTE5MTExMTIzNTk1
OVowHjEcMBoGA1UEAwwTKi5zcGFya3Bvc3RtYWlsLmNvbTCCASIwDQYJKoZIhvcN
AQEBBQADggEPADCCAQoCggEBANU6dODNQ8+kgVGIDNOxA6tYUnonR2baa14JZh3T
lWvWrhm/jm6c1YeWMuugPHZr60r15r+jCVHWmg2bHa8bdDZZV9mG3aoa1rlbtzXX
6HopgWFff2XAyQNKyEMTDmlqgg04Sj5vYLeZs6FxH72zWopRBYAn8Y5os1cxNnwr
TNzQSaw4DdlBqx3lB193NBJu/3ix55l53y+BIEibQu/JvkQ9CsMcQmHF1+BSyXKz
gBFMJsCLij5VwI58OEn0Gd7FfW5e7Vf+ih2yupmxcxF4Qz/V00Q46w8VeoNEfotM
q40Z4mlZOZpIwcUq9gBwCnvyalz1HSAOGnrUmcExhGyrpncCAwEAAaOCA4UwggOB
MDEGA1UdEQQqMCiCEyouc3Bhcmtwb3N0bWFpbC5jb22CEXNwYXJrcG9zdG1haWwu
Y29tMAkGA1UdEwQCMAAwKwYDVR0fBCQwIjAgoB6gHIYaaHR0cDovL2dwLnN5bWNi
LmNvbS9ncC5jcmwwbwYDVR0gBGgwZjBkBgZngQwBAgEwWjAqBggrBgEFBQcCARYe
aHR0cHM6Ly93d3cucmFwaWRzc2wuY29tL2xlZ2FsMCwGCCsGAQUFBwICMCAMHmh0
dHBzOi8vd3d3LnJhcGlkc3NsLmNvbS9sZWdhbDAfBgNVHSMEGDAWgBSXwidQnsLJ
7AyIMsh8reKmAU/abzAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUH
AwEGCCsGAQUFBwMCMFcGCCsGAQUFBwEBBEswSTAfBggrBgEFBQcwAYYTaHR0cDov
L2dwLnN5bWNkLmNvbTAmBggrBgEFBQcwAoYaaHR0cDovL2dwLnN5bWNiLmNvbS9n
cC5jcnQwggH4BgorBgEEAdZ5AgQCBIIB6ASCAeQB4gB2AN3rHSt6DU+mIIuBrYFo
cH4ujp0B1VyIjT0RxM227L7MAAABV7n/BzUAAAQDAEcwRQIhAJD+vSN4KEg8g/Hy
BgEJHywbVWbuVr4D/+rgH0zxnB/UAiBocGIy6WQ4q5PVRZwjMiROoM3CV5XbHwdy
JtljG9oJ1QB3AGj2mPgfZIK+OozuuSgdTPxxUV1nk9RE0QpnrLtPT/vEAAABV7n/
B0oAAAQDAEgwRgIhAPdyIepe1dNpM3Z6vug5ErkKgQ+Xn4XmgM5E5xG+2taPAiEA
iyIno/MiL3UQMCNN1ZQnytldZ/lHqDU1pgC18gkTMKIAdgDuS723dc5guuFCaR+r
4Z5mow9+X7By2IMAxHuJeqj9ywAAAVe5/wd9AAAEAwBHMEUCIQDuWE1LGKjAvmn5
PAaDZu6XjHVFgrz0FG8N8FhBo5g9wwIgALXaw7Q9JulbMzacAct+jHzxZHBzRm8K
0DzvVRN+F8kAdwC8eOHfxfY8aEZJM02hD6FfCXlpIAnAgbTz9pF/Ptm4pQAAAVe5
/wggAAAEAwBIMEYCIQD54UIVSPEkbrRr2uOvUjPU+fFEFp+rKR86uIFq1T+NSQIh
AOCgEtSJ+B1CQaKBBR2O2A0n977z/q1+RsrZg+vZwXvaMA0GCSqGSIb3DQEBCwUA
A4IBAQCnxu7Iq9HZxJfBczrhnO3QBMAfcwSbhc8Q1gpSKfcsE7SJ3pzorH2AsICm
RgYctRN9WylFG7UYzxlv11u+HLt8wvQ80jrIuKPCsP+s93iHoZzqdukae6SLhdh/
9N/UMv8p+PXpikb/xeVEkym/fC4op/tm1nzI5bTcH86BLHxKHbcUHPq0xZ/5KLn1
Lvguou19FvRGOlgd/uVw93h911nBPRxQ50vsEQwB+HVvwXZ/Iya9dLOawL9LY+Na
re16O0f621yxsTlBqRuz5Ji/E56Qmyu9tCov7p/XiI36Q+EmQ0syi5AOv1nB4yhw
zi09+YZ33hkiOMyRiyKg3Z10NoJ5
-----END CERTIFICATE-----
subject=/CN=*.sparkpostmail.com
issuer=/C=US/O=GeoTrust Inc./CN=RapidSSL SHA256 CA
---
No client certificate CA names sent
Client Certificate Types: RSA sign, DSA sign, ECDSA sign
Requested Signature Algorithms: RSA+SHA512:DSA+SHA512:ECDSA+SHA512:RSA+SHA384:DSA+SHA384:ECDSA+SHA384:RSA+SHA256:DSA+SHA256:ECDSA+SHA256:RSA+SHA224:DSA+SHA224:ECDSA+SHA224:RSA+SHA1:DSA+SHA1:ECDSA+SHA1
Shared Requested Signature Algorithms: RSA+SHA512:DSA+SHA512:ECDSA+SHA512:RSA+SHA384:DSA+SHA384:ECDSA+SHA384:RSA+SHA256:DSA+SHA256:ECDSA+SHA256:RSA+SHA224:DSA+SHA224:ECDSA+SHA224:RSA+SHA1:DSA+SHA1:ECDSA+SHA1
Peer signing digest: SHA512
Server Temp Key: ECDH, P-384, 384 bits
---
SSL handshake has read 4718 bytes and written 510 bytes
---
New, TLSv1/SSLv3, Cipher is ECDHE-RSA-AES256-GCM-SHA384
Server public key is 2048 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
SSL-Session:
    Protocol  : TLSv1.2
    Cipher    : ECDHE-RSA-AES256-GCM-SHA384
    Session-ID: 2EB7C8377EBF5C5907B89584DE61F4D7E39BE21F7D4B5F76106D847478C924B6
    Session-ID-ctx:
    Master-Key: F8C9F4777D1DDC2B2542B2FDAF2F68E325166B0A189F1D4F9B9BB268E5854C598B7980A4FB50E420F9C21B7FCE2F13F5
    Key-Arg   : None
    PSK identity: None
    PSK identity hint: None
    SRP username: None
    TLS session ticket lifetime hint: 300 (seconds)
    TLS session ticket:
    0000 - 0b 3e da c1 f4 2e 17 b3-87 38 d5 17 cb a2 f7 5b   .>.......8.....[
    0010 - 4a 17 04 98 56 a9 bf 20-14 e3 aa 36 43 ab b2 ea   J...V.. ...6C...
    0020 - 42 6d 93 43 15 48 a9 0d-08 55 55 51 b9 4c b5 d2   Bm.C.H...UUQ.L..
    0030 - 1e de d1 71 ce 70 43 9b-2e 78 1b 4c 80 c9 d6 aa   ...q.pC..x.L....
    0040 - ae a1 e7 a1 45 81 14 94-68 33 20 c2 b3 bb 74 d5   ....E...h3 ...t.
    0050 - d9 96 0e e1 27 16 e7 9b-d3 63 46 93 f7 35 ed 32   ....'....cF..5.2
    0060 - de cb 13 c1 31 7a e3 ac-01 6f a5 43 82 77 50 08   ....1z...o.C.wP.
    0070 - 83 09 55 fc 5b 75 6a 85-76 f2 39 8e f5 05 41 f8   ..U.[uj.v.9...A.
    0080 - c9 6a 5f c2 88 23 d3 73-99 23 4c c7 70 cd 14 cf   .j_..#.s.#L.p...
    0090 - 4f bd 2f 15 17 1f a0 11-a4 90 c1 c6 83 b6 24 89   O./...........$.

    Start Time: 1527876163
    Timeout   : 300 (sec)
    Verify return code: 0 (ok)
---
250 ENHANCEDSTATUSCODES
read:errno=0
root@archlinux-app:/var/www/discourse#

(I didn’t bother authenticating, wanted to see if connection was even occurring in the first place)


(Michael Brown) #4

Those are the important bits - it got the certificate and it validated.

We also need to check to see if it validates from within the container:

○ → docker run --rm -it discourse/base:2.0.20180404 bash
root@e0f355ee5e84:/# openssl s_client -verify_return_error -connect smtp.sparkpostmail.com:587 -starttls smtp
CONNECTED(00000003)
depth=2 C = US, O = GeoTrust Inc., CN = GeoTrust Global CA
verify return:1
depth=1 C = US, O = GeoTrust Inc., CN = RapidSSL SHA256 CA
verify return:1
depth=0 CN = *.sparkpostmail.com
verify return:1
---
…
    Verify return code: 0 (ok)
---
250 PIPELINING

That works for me - does it fail within your docker container?


(Watercolor Games) #5

The command output I sent in the previous post was run inside the container already. Weird.


(Watercolor Games) #6

Bump: Still unable to receive emails from my Discourse or my C# program inside the docker container. Considering shutting off my nginx reverse proxy so no one can access my Discourse until we figure this out.


(Kane York) #7

As an alternative to shutting it down entirely, you can set the disable emails setting, which will add a banner saying that emails are disabled for all visitors. You can also use the global notice for generic information about the server move.


(Watercolor Games) #8

Nahh, the issue is I can’t finish the installation. Stuck at this screen (see far left monitor).

(Side note, is that a pizza? I like pizza.)

Shutting down nginx would disable access to this page remotely while still allowing me to access it locally via SSH and links.


(Mittineague) #9

So you’re saying the Register button is non-responsive?


(Watercolor Games) #10

No, it’s responsive. But I can’t get my email verification - see the original post for details.


(Watercolor Games) #11

Update: Thinking a solution would be - well, I’m a programmer. I can write my own SMTP server to deploy into the docker container and run as a background task listening on a port not exposed to the host. Then I can configure Discourse to route all emails to this program, which then uses the SparkPost REST API and my API key to actually send the email.

Thus, hopefully, I’m bypassing the SSL errors on their SMTP server, effectively working around the problem. It’s a massive bodge but I’ve been stuck at that “Congrats, you installed Discourse!” screen for far too long and it doesn’t seem this issue will let up anytime soon so… this will be my solution. if anyone knows another way to fix the issue, please let me know. I may still end up writing this program though, as a culminating task for my Computer Science course.

If the program works, I’ll post a video on my YouTube channel showing how I coded it, how to set it up and point Discourse at it, and how to configure it to point at your SparkPost API key. Might also add Mailjet and Mailgun API support for those using said platforms who are having the same issues as me. Will also put the code on GitHub and link it here. :slight_smile:


(Michael Brown) #12

That would probably be actually pretty handy to have for environments from which you cannot get SMTP out. :+1:

You might want to consider writing a smaller program that listens on a socket for LMTP and have e.g. postfix deliver to that socket. Not sure if that’s less work or not :slight_smile:


(Watercolor Games) #13

Nah, in this case, since I’m a C# programmer, I’ve got NuGet. :stuck_out_tongue:

And NuGet has an SmtpServer library (which is also on GitHub). This library has everything you’d need to build an advanced smtp server or even a simple relay like I intend to. User authentication, mailboxes, etc. It’ll even let you blacklist domains and email addresses and prevent them from sending mail to the server or receiving mail from the server. Handy if you’re building your own web app that uses this, though Discourse already has email blacklisting built in. :slight_smile:

All I need to do is tell it to listen on the loopback IP address on some random unexposed port on the docker container, make sure that mono-complete is installed and the program is downloaded and run as a background task any time the docker container is rebuilt, then I need to configure Discourse to use this smtp server rather than SparkPost directly.

Then I can configure my smtp server to send anywhere through any type of mail transfer protocol or REST API, without Discourse needing to know. In my case I’ll write a little piece of code that runs when a request to send mail is received, that simply forwards the message to the SparkPost API and translates the API response to an SMTP response. Easy-peasy. :slight_smile:


(Watercolor Games) #14

Update: C# to the rescue.

So far I was able to get Discourse to install Mono and run a shell script in my shared directory which will eventually download, build and run a C# program that will act as the SMTP server. The program will be run as a system daemon using the mono-service command so that it runs in the background unattended.

I was able to accomplish the task by placing this in my app.yml.

## Any custom commands to run after building
run:
  - exec: echo "Beginning of custom commands"
  ## If you want to set the 'From' email address for your first registration, uncomment and change:
  ## After getting the first signup email, re-comment the line. It only needs to run once.
  - exec: rails r "SiteSetting.notification_email='noreply@community.watercolorgames.net'"
  - exec: echo "Installing the Mono runtime through apt-get..."
  - exec: apt-get update -y && apt-get install -y mono-complete
  - exec: echo "Mono's installed. Starting the SmtpRelay."
  - exec: cat /shared/SmtpRelayStart.sh | /bin/bash
  - exec: echo "Discourse should now be relaying mail through the SmtpRelay."o
  - exec: echo "End of custom commands"

It first does some discourse email stuff that’s unrelated.

Then it updates the apt-get local repositories.

Then it installs mono-complete and all dependencies. Now my docker container has a C# runtime.

Then it hands control over to a shell script that’ll start my system daemon (though right now, it just prints “dummy” to the log because I haven’t coded the daemon yet.)

One thing to note is that during the mono installation, dpkg’s gonna throw a message into the log saying it “cannot re-open stdin”. It may seem like an error, as if things failed, but, no. It’s just a warning. Discourse will still continue with Mono’s installation. It’ll take time but it’ll finish up.

To test if the app.yml did its job, you should be able to do this:

[root@archlinux discourse]# ./launcher enter app
root@archlinux-app:/var/www/discourse# mono --version
Mono JIT compiler version 4.2.1 (Debian 4.2.1.102+dfsg2-7ubuntu4)
Copyright (C) 2002-2014 Novell, Inc, Xamarin Inc and Contributors. www.mono-project.com
 TLS:           __thread
 SIGSEGV:       altstack
 Notifications: epoll
 Architecture:  amd64
 Disabled:      none
 Misc:          softdebug
 LLVM:          supported, not enabled.
 GC:            sgen
root@archlinux-app:/var/www/discourse#

and you’ll know Mono got installed and is functioning within the Discourse container. Yes, this is a hack. But it works. And THAT is the art of the bodge.


(Watercolor Games) #15

Update: This is starting to work.

I did exactly what I said I’d do with that library. Using it and my SMTP test utility, I was able to get it to send email from my development environment over the SparkPost API. Only issue is, SMTP seems to be doing some funky character escaping thing where certain characters get replaced with an = and then a hexadecimal number. Didn’t really expect the SmtpServer library not to handle that so I’ve just been blindly forwarding raw SMTP requests as text content to my email :stuck_out_tongue:

The resulting email came out looking like this:

Not too pretty and certainly won’t work for Discourse yet. :slight_smile: Maybe someone more talented can tell me what these hex codes are for? I’m thinking they’re possibly ASCII values but…I’m not sure.


(Watercolor Games) #16

Update: Woohoo. It’s sending HTML.

My relay now properly parses SMTP character escape codes =XX into UTF-8 characters for sending over SparkPost. It also now has a method that strips out HTML tags for the plain-text version of the email, and on that note, supports HTML emails.

It also sets options.transactional to true for all messages since this is intended for things like password resets, account activation, etc. I’m not sure if a Discourse community summary/digest counts as transactional, but, there’s no way for me to decipher the difference between a transactional message and a marketing/promotional one in code. Thank you, CAN-SPAM act. You just made this difficult.

Next step is to deploy it to the docker container and see how it plays out.


(Watercolor Games) #17

Update: Unfortunately, although the program is getting started as a mono-service when the docker container rebuilds, it is not listening for SMTP requests. It is SUPPOSED to listen on TCP port 456, localhost. However it does not listen on any TCP ports at all. Ugh.

Where do I even begin debugging this…


(Watercolor Games) #18

Someone please help.

Because during testing on the server I noticed a few things.

  1. My SMTP relay is listening. It’s just…not exposing itself to other programs on the container. Running nmap localhost doesn’t show an open TCP port 456 even when the server’s running.
  2. My SMTP test utility somehow can talk to the relay. But it errors out with SMTP exception 554, indicating that the server doesn’t trust the sender… which is odd because I didn’t code my relay to ever npt trust a client - because it’s not intended to face the Internet.
  3. Discourse won’t talk to the relay. At all. Not with Discourse told to talk to 127.0.0.1 OR localhost.

(Felix Freiberger) #19

Wait a second, this sounds wrong. As long as your SMTP relay isn’t running within the Discourse container, this can’t work – Docker isolates networking. You’ll need to use the correct IP and make sure your SMTP host accepts connections from Docker’s network interface.


(Watercolor Games) #20

Huh. So localhost doesn’t actually mean the Docker container…that’s handy. How exactly can I get the Docker container’s IP and bind to that?