simonrob / email-oauth2-proxy

An IMAP/POP/SMTP proxy that transparently adds OAuth 2.0 authentication for email clients that don't support this method.
Apache License 2.0
797 stars 86 forks source link

"Refused connection" when using versions 2023-03-09 or 2023-05-18 in Windows #179

Closed bwbug closed 1 year ago

bwbug commented 1 year ago

Hello, I'm back, and I'm still unable to run the emailproxy without a console window. As explained in #134, I would like to run the proxy in a way that does make available the systray icon, but does not require a console window. Since #134 was closed, I updated my installation of eamil-oauth2-proxy, and recompiled using the --noconsole option, as follows:

.\pyinstaller --noconsole --onefile emailproxy.py --hidden-import timeago.locales.en

Running the executable no longer causes a crash, and I do see the systray icon but no console window (as desired).

However, when attempting to send email via SMTP, my email client logs the following error:

25908    16: 9.48 Open 127.0.0.1:1587
25908     8: 9.50 Dialog: "Could not connect to "localhost"\r\n"
25908     8: 9.50 Dialog: "\r\n"
25908     8: 9.50 Dialog: "Cause: connection refused (10061)"

Running the proxy in debug mode, the log file only shows the following:

2023-06-26 22:40:46,268: Initialising Email OAuth 2.0 Proxy (version 2023-05-18) from config file C:\Program Installation Files\Email-OAuth2-Proxy_portable\dist\emailproxy.config
2023-06-26 22:40:46,276: Starting POP server at localhost:1995 (unsecured) proxying outlook.office365.com:995 (SSL/TLS)
2023-06-26 22:40:46,291: Starting SMTP server at localhost:1587 (unsecured) proxying smtp.office365.com:587 (STARTTLS)
2023-06-26 22:40:46,291: Initialised Email OAuth 2.0 Proxy - listening for authentication requests. Connect your email client to begin
2023-06-26 22:41:34,735: Stopping Email OAuth 2.0 Proxy

I have checked that an previously compiled executable (which does have a console window) still runs properly and allows the email client to connect to port 1587 for sending by SMTP.

Please advise.

simonrob commented 1 year ago

You've imported timeago.locales.en rather than timeago.locales.en_short, which could be the reason?

Also, you're building an essentially identical version to the official release – it might be worth just using that?

bwbug commented 1 year ago

I had seen the pre-compiled releases for Windows, but I assumed it wasn't using the --noconsole option, which is why I built my own executable.

Unfortunately, using the official release results in the same behavior ("connection refused"), as does my own build after switching the imported file to timeago.locales.en_short.

I also tried re-building this without the --noconsole option, and still get the same error. (Thus, I should probably change the title of this Issue).

bwbug commented 1 year ago

I've tested version 2022-12-14 (which works), version 2023-02-08 (which works), version 2023-03-09 (which does not work), and version 2023-05-18 (which does not work). Thus I've modified the topic title accordingly.

The March 9 and May 18 versions both result in a "Connection Refused" error. This occurs whether I have built the executable myself using PyInstaller or not (i.e., using the pre-built Windows binary). Even running pythonw emailproxy.py does not work.

Furthermore, I should clarify that the error message thrown by the email client ("Could not connect to "localhost". Cause: connection refused (10061)") is the same error message that occurs when the emailproxy.exe is not running at all.


Another issue (which is probably unrelated, but I mention it for completeness) was found in version 2023-03-09 only: When selecting "Quit Email OAuth 2.0 Proxy" from the systray icon menu, the app is not closed (nothing happens). This problems seems to occur only with the pre-compiled release for Windows, not when building my own executable from the .py file.

bwbug commented 1 year ago

Since this is some sort of connection issue, tried replacing localhost by ::1 in my email client, but this results in the following error (possibly because the email client may not support IPv6):

Resolving address for "::1"
Error getting network address for "::1"
Cause: host not found (11001)

Using 127.0.0.1 in place of localhost also does not work.


Additional testing:

In a CMD shell, I typed telnet localhost 1587 (to test the SMTP connection), which returned the following string:

220 MN2PR10CA0019.outlook.office365.com Microsoft ESMTP MAIL Service ready at Wed, 28 Jun 2023 02:07:34 +0000

I was then able to connect to the proxy (triggering a prompt to authenticate), as follows:

EHLO localhost
250-BL1PR13CA0260.outlook.office365.com Hello [72.94.160.40]
250-SIZE 157286400
250-PIPELINING
250-DSN
250-ENHANCEDSTATUSCODES
250-AUTH PLAIN LOGIN
250-8BITMIME
250-BINARYMIME
250-CHUNKING
250 SMTPUTF8
AUTH LOGIN
334 VXNlcm5hbWU6
bXl1c2VybmFtZUBteWRvbWFpbi5jb20=
334 UGFzc3dvcmQ6
ZHVtbXlwYXNzd29yZA==

So, at this point, I don't understand why my email client is unable to even establish a connection (especially since it works fine in versions of emailproxy prior to 2023-03-09).

simonrob commented 1 year ago

Thanks for following up with this extra detail. The changes since this version have been minor, and there is nothing to do with connection setup, so I have to assume this is something to do with PyInstaller or another dependency. So, please can you just clarify this bit:

Even running pythonw emailproxy.py does not work.

To be extra clear, is it the case that the versions in question (2023-03-09 onwards) do not work even when PyInstaller is not involved at all and you are just downloading and running the script normally? That is very odd, given that telnet connections work.

Please could you provide details about the client you are using, and also the output of python -m pip list.

mtlg commented 1 year ago

I believe I have the same problem. Up to 2023-02-08 everything is fine, starting with 2023-03-09 I get connection errors.

I have boiled it down to socket_family=socket.AF_UNSPEC vs. socket_family=socket.AF_INET:

--- email-oauth2-proxy-2023-05-18/emailproxy.py      2023-05-18 21:59:31.000000000 +0200
+++ emailproxy.py       2023-06-28 12:55:56.167192118 +0200
@@ -1513,7 +1513,7 @@
         self.create_socket()
         self.connect(self.server_address)

-    def create_socket(self, socket_family=socket.AF_UNSPEC, socket_type=socket.SOCK_STREAM):
+    def create_socket(self, socket_family=socket.AF_INET, socket_type=socket.SOCK_STREAM):
         # connect to whichever resolved IPv4 or IPv6 address is returned first by the system
         for a in socket.getaddrinfo(self.server_address[0], self.server_address[1], socket_family, socket.SOCK_STREAM):
             super().create_socket(a[0], socket.SOCK_STREAM)
@@ -1922,7 +1922,7 @@
         self.bind(self.local_address)
         self.listen(5)

-    def create_socket(self, socket_family=socket.AF_UNSPEC, socket_type=socket.SOCK_STREAM):
+    def create_socket(self, socket_family=socket.AF_INET, socket_type=socket.SOCK_STREAM):
         # listen using both IPv4 and IPv6 where possible (python 3.8 and later)
         socket_family = socket.AF_INET6 if socket_family == socket.AF_UNSPEC else socket_family
         if socket_family != socket.AF_INET:

I use the proxy for:

Regardless, I still do not see why setting socket_family=socket.AF_UNSPEC would require the use of IPv6 addressing.

simonrob commented 1 year ago

Thanks for this – I misunderstood the previous update, so yes, the changes since 2022-02-08 do indeed include modifications to the connection setup. The new default behaviour after this is to try IPv6 first. But (at least in my own testing), the intent is that a failure to set up the IPv6 socket would fall back to IPv4. Clearly on Windows this isn't always happening.

I don't have access to a Windows environment to check myself right now, but please could you confirm:

One potential resolution here is to change the proxy's default local_address to ::, but I'm very cautious about adverse effects (for example, on systems without dual-stack support, or those supporting IPv4 only).

It would be useful to hear from both of you @bwbug and @mtlg about whether adding local_address = :: for these servers resolves things for the proxy versions and clients concerned.

bwbug commented 1 year ago

It would be useful to hear from both of you @bwbug and @mtlg about whether adding local_address = :: for these servers resolves things for the proxy versions and clients concerned.

I'm afraid I will need explicit instructions about where in the emailproxy.config file to add the local_address = :: line. Please advise. Update: Nevermind, I found the instructions. I have posted my test results in a separate comment below.

To be extra clear, is it the case that the versions in question (2023-03-09 onwards) do not work even when PyInstaller is not involved at all and you are just downloading and running the script normally?

Yes, I've tested this with both version 2023-03-09 and 2023-05-18 — executing pythonw emailproxy.py brings up the systray icon, but the email client is unable to connect.

Please could you provide details about the client you are using, and also the output of python -m pip list.

The client I'm using is Qualcomm Eudora 7.1.0.9 (released in 2006, thus pre-dating the adoption of IPv6).

Output of py -m pip list:

Package                   Version
------------------------- ---------
altgraph                  0.17.3
bottle                    0.12.23
cffi                      1.15.1
clr-loader                0.2.5
cryptography              39.0.0
future                    0.18.3
pefile                    2022.5.30
Pillow                    9.4.0
pip                       23.1.2
prompt-toolkit            3.0.36
proxy-tools               0.1.0
pycparser                 2.21
pyinstaller               5.13.0
pyinstaller-hooks-contrib 2022.15
pystray                   0.19.4
pythonnet                 3.0.1
pywebview                 4.2.2
pywin32-ctypes            0.2.1
setuptools                65.5.0
six                       1.16.0
timeago                   1.0.16
typing_extensions         4.6.3
wcwidth                   0.2.6
bwbug commented 1 year ago

It would be useful to hear from both of you @bwbug and @mtlg about whether adding local_address = :: for these servers resolves things for the proxy versions and clients concerned.

I am please to report that after adding local_address = :: to the [SMTP-1587] section of the emailproxy.config file, my Eudora client is now able to connect to the proxy. I tested this with the 2023-05-18 version, and confirmed that it works either running the pre-compiled binary release for Windows, or by executing pythonw emailproxy.py.

One unexpected behavior was that after making the change to the .config file, Windows Defender Firewall requested that I approve permissions for pythonw.exe or emailconfig.exe (depending on which method I was using to run the proxy). Approving authorizes inbound access to all local and remote ports by TCP and UDP. I imagine that this could create somewhat of a security risk, so I would appreciate if you could clarify the minimum requirements for inbound traffic through the firewall.

By the way, I did test whether the changes to the Firewall settings were responsible for fixing the original connection problem, but this does not appear to be the case. I left the new Firewall rules in place, but reverted the emailproxy.config file (removing the references to local_address = ::); in this case, the email client again was refused a connection to the proxy server.

mtlg commented 1 year ago
  1. IPv4: local_address = 127.0.0.1

    • Works if clients point to localhost (i.e., 127.0.0.1)
    • Does not work if clients point to localhost6 (i.e., ::1)
  2. IPv6: local_address = ::1

    • Does not work if clients point to localhost (i.e., 127.0.0.1)
    • Works if clients point to localhost6 (i.e., ::1)
  3. Dual-stack: local_address = ::

    • Works if clients point to localhost (i.e., 127.0.0.1)
    • Works if clients point to localhost6 (i.e., ::1)

Looks coherent, but I'm not sure it was intentional to require users to add a local_address = :: setting for all servers (probably should be the new default) :-)

Regarding which of the two diffs has effect, it's the second, on line 1925:

In the tests above I left AF_UNSPEC on line 1516 (it has no effect anyway).

BTW, I do all the testing on Fedora 38 installed on the real HW (not VM, containers, ...).

simonrob commented 1 year ago

Thank you both for following up – this is really helpful.

It does seem like switching the proxy's default local_address value to :: would resolve this, and also in most cases avoid the need to investigate other issues that have been reported for related reasons (e.g., #152, #162, #178).

I will test this in various scenarios, and assuming all goes well, plan to integrate this change in the next release.

bwbug commented 1 year ago

@simonrob

I would appreciate if you could address my query above about the requirement to open the firewall:

Approving authorizes inbound access to all local and remote ports by TCP and UDP. I imagine that this could create somewhat of a security risk, so I would appreciate if you could clarify the minimum requirements for inbound traffic through the firewall.

simonrob commented 1 year ago

I'm not familiar with the Windows firewall, but assuming it is running on the same host as your client, there is no reason to open any remote ports. If the firewall blocks local connections, then you'd only need to allow the ports it is actively using (i.e., those listed in the configuration file).

bwbug commented 1 year ago

@simonrob Does it use UDP or TCP or both?

simonrob commented 1 year ago

IMAP, POP and SMTP all use TCP.

bwbug commented 1 year ago

Thank you for the guidance. So I customized the inbound rule (on the Windows Defender Firewall running on the same computer as my email client & emailproxy,exe) so that it only allowed TCP connections to local port 1587 from remote ports 1587, with the scope restricted to local IP address of 127.0.0.1 and a remote IP address of 127.0.0.1. I confirmed that my email client could still send email via the proxy, so that's good (I wasn't sure if the IP addresses would have to be specified as IPv6 or not).

However, then I disabled this firewall rule for testing purposes, and found (to my surprise) that I could still send email using the proxy!? Any insights? Also, I don't recall previously being prompted by the firewall to allow access for the proxy (when I was testing versions earlier than 2023-03-09) — this only happened after I was able to get 2023-03-09/2023-05-18 working by adding the local_address = :: line to the configuration.

simonrob commented 1 year ago

I can't really offer any firewall advice, but remember that the proxy's own ports are all for local connections, and its only external connections are to the actual IMAP/POP/SMTP servers you are using. You may not need any special rules here.

bwbug commented 1 year ago

I understand, I was just confused why the emailproxy seems to be inconsistent as to when it triggers involvement of the firewall.