Skip to content

Commit

Permalink
TLS now in production closes #47
Browse files Browse the repository at this point in the history
  • Loading branch information
geek-at committed Nov 29, 2023
1 parent bf05b15 commit f263ad8
Show file tree
Hide file tree
Showing 9 changed files with 56 additions and 190 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changelog

## V1.3.0
- Added TLS and STARTTLS support
- Various bug fixes and docs updates

## V1.2.6
- Fixed link to raw email in RSS template
- Added version string to branding part of the nav
Expand Down
33 changes: 33 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,9 @@ Just edit the `config.ini` You can use the following settings
- `PASSWORD` -> If configured, site and API can't be used without providing it via form, POST/GET variable `password` or http header `PWD` (eg: `curl -H "PWD: 123456" http://localhost:8080/json...`)
- `ALLOWED_IPS` -> Comma separated list of IPv4 or IPv6 CIDR addresses that are allowed to use the web UI or API
- `ATTACHMENTS_MAX_SIZE` -> Max size for each individual attachment of an email in Bytes
- `MAILPORT_TLS` -> If set to something higher than 0, this port will be used for TLSC (TLS on Connect). Which means plaintext auth will not be possible. Usually set to `465`. Needs `TLS_CERTIFICATE` and `TLS_PRIVATE_KEY` to work
- `TLS_CERTIFICATE` -> Path to the certificate (chain). Can be relative to the /python directory or absolute
- `TLS_PRIVATE_KEY` -> Path to the private key of the certificate. Can be relative to the /python directory or absolute

## Docker env vars
In Docker you can use the following environment variables:
Expand All @@ -83,6 +86,36 @@ In Docker you can use the following environment variables:
| PASSWORD | If configured, site and API can't be used without providing it via form, POST/GET variable `password` or http header `PWD` | yousrstrongpassword |
| ALLOWED_IPS | Comma separated list of IPv4 or IPv6 CIDR addresses that are allowed to use the web UI or API | `192.168.5.0/24,2a02:ab:cd:ef::/60,172.16.0.0/16` |
| ATTACHMENTS_MAX_SIZE | Max size for each individual attachment of an email in Bytes | `2000000` = 2MB |
| MAILPORT_TLS | If set to something higher than 0, this port will be used for TLSC (TLS on Connect). Which means plaintext auth will not be possible. Usually set to `465`. Needs `TLS_CERTIFICATE` and `TLS_PRIVATE_KEY` to work | `465` |
| TLS_CERTIFICATE | Path to the certificate (chain). Can be relative to the /python directory or absolute | `/certs/cert.pem` or `cert.pem` if it's inside the python directory |
| TLS_PRIVATE_KEY | Path to the private key of the certificate. Can be relative to the /python directory or absolute | `/certs/privkey.pem` or `key.pem` if it's inside the python directory |

## TLS
Since v1.3.0 TLS and STARTTLS are supported by OpenTrashmail.

### What you should know
Be aware there are two ways to use TLS with email

1. STARTTLS
2. TLS on Connect (TLSC)

**STARTTLS** does not require a specific port as it starts out as plaintext and then upgrades to TLS if the server advertises the "STARTTLS" command (which OpenTrashmail does automatically if the Certificate and key settings are configured). Since it's run on the default `MAILPORT` you don't need to open other ports for it to work.

**TLS on connect** is wrapping TLS around the exposed ports so it's not possible to talk to it in plaintext and therefore it needs a different port to work. Usually port 465 is used for this.

### About the certificates
For TLS to work you first need a certificate that corresponds with the hostname of the SMTP server. This can be done using Lets'encrypt and even works with wildcard certificates.

For testing environments you can create a certificate by running the following command from inside the python folder:

```bash
openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 -nodes -subj '/CN=localhost'
```

You then need to set the settings for `MAILPORT_TLS` (not needed if you only want to support STARTTLS), `TLS_CERTIFICATE` and `TLS_PRIVATE_KEY`.

### Testing TLS
The [/docs/Dev.md](/docs/Dev.md) file contains a few hints on how to debug and test TLS and TLSC connections. It uses the tool `swaks` which should be avaialable in every package manager.

# Roadmap
- [x] Mail server
Expand Down
3 changes: 3 additions & 0 deletions docker-compose-dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ services:
# - PASSWORD=123456
# - ALLOWED_IPS=192.168.0.0/16,2a02:ab:cd:ef::/60
# - ATTACHMENTS_MAX_SIZE=10000000
# - MAILPORT_TLS=465
# - TLS_CERTIFICATE=cert.pem
# - TLS_PRIVATE_KEY=key.pem

ports:
- '2525:25'
Expand Down
3 changes: 3 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ services:
# - PASSWORD=123456
# - ALLOWED_IPS=192.168.0.0/16,2a02:ab:cd:ef::/60
# - ATTACHMENTS_MAX_SIZE=10000000
# - MAILPORT_TLS=465
# - TLS_CERTIFICATE=cert.pem
# - TLS_PRIVATE_KEY=key.pem

ports:
- '2525:25'
Expand Down
2 changes: 1 addition & 1 deletion docker/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ WORKDIR /var/www/opentrashmail
VOLUME /var/www/opentrashmail/data
VOLUME /var/www/opentrashmail/logs

EXPOSE 80 25
EXPOSE 80 25 465

#CMD ["/bin/ash"]
ENTRYPOINT ["/etc/start.sh"]
4 changes: 2 additions & 2 deletions docs/Dev.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ Testing with the TLS version (non-plaintext).
Needs config options `MAILPORT_TLS`, `TLS_CERTIFICATE` and `TLS_PRIVATE_KEY` set.

```bash
echo 'Testing' | swaks --to test@example.com --from "something@example.com" --server localhost --port 465 -tlsc
echo 'Testing' | swaks --to test@example.com --from "something@example.com" --server localhost --port 2525 -tlsc
```

### Via STARTTLS
Expand All @@ -47,5 +47,5 @@ Needs config options `TLS_CERTIFICATE` and `TLS_PRIVATE_KEY` set.

Testing STARTTLS version
```bash
echo 'Testing' | swaks --to test@example.com --from "something@example.com" --server localhost -tlsc
echo 'Testing' | swaks --to test@example.com --from "something@example.com" --server localhost --port 465 -tlsc
```
179 changes: 0 additions & 179 deletions python/mailserver.py

This file was deleted.

17 changes: 10 additions & 7 deletions python/mailserver3.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,18 @@
TLS_PRIVATE_KEY = ""

class CustomHandler:
connection_type = ''
def __init__(self,conntype='Plaintext'):
self.connection_type = conntype

async def handle_DATA(self, server, session, envelope):
peer = session.peer
rcpts = []
for rcpt in envelope.rcpt_tos:
rcpts.append(rcpt)
if(server.tls_context != None):
logger.debug('Receiving message from: %s:%d (STARTTLS)' % peer)
else:
logger.debug('Receiving message from: %s:%d (Plaintext (or TLS))' % peer)

logger.debug('Receiving message from: %s (%s)', peer,self.connection_type)

logger.debug('Message addressed from: %s' % envelope.mail_from)
logger.debug('Message addressed to: %s' % str(rcpts))

Expand Down Expand Up @@ -187,16 +190,16 @@ async def run(port):
context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
context.load_cert_chain(TLS_CERTIFICATE, TLS_PRIVATE_KEY)
if MAILPORT_TLS > 0:
controller_tls = Controller(CustomHandler(), hostname='0.0.0.0', port=MAILPORT_TLS, ssl_context=context)
controller_tls = Controller(CustomHandler("TLS"), hostname='0.0.0.0', port=MAILPORT_TLS, ssl_context=context)
controller_tls.start()

controller_plaintext = Controller(CustomHandler(), hostname='0.0.0.0', port=port,tls_context=context)
controller_plaintext = Controller(CustomHandler("Plaintext or STARTTLS"), hostname='0.0.0.0', port=port,tls_context=context)
controller_plaintext.start()

logger.info("[i] Starting TLS only Mailserver on port " + str(MAILPORT_TLS))
logger.info("[i] Starting plaintext Mailserver (with STARTTLS support) on port " + str(port))
else:
controller_plaintext = Controller(CustomHandler(), hostname='0.0.0.0', port=port)
controller_plaintext = Controller(CustomHandler("Plaintext"), hostname='0.0.0.0', port=port)
controller_plaintext.start()

logger.info("[i] Starting plaintext Mailserver on port " + str(port))
Expand Down
1 change: 0 additions & 1 deletion version.txt

This file was deleted.

0 comments on commit f263ad8

Please sign in to comment.