Lightweight SMTP and POP3 mail proxy to Microsoft 365
Project description
SMTP & POP3 Proxy for Microsoft 365 Shared Mailboxes
Why This Project?
Many legacy systems, embedded devices, and applications:
- โ Do not support OAuth2 for sending or receiving email
- โ Cannot access Microsoft 365 endpoints directly due to network restrictions
- โ Require SMTP/POP3 and basic username/password authentication
This proxy provides a secure bridge between those tools and Microsoft 365:
- ๐ก๏ธ Messages are relayed through a single authenticated Microsoft 365 account
- ๐ Only authorized clients (defined in config) can use the proxy
- ๐ Useful when you can't expose your own mail servers in SPF records
- ๐ฅ Enables retrieval of mail from shared mailboxes via POP3
- ๐ธ With a single low-cost Exchange Online Kiosk license + free shared mailboxes, you can build a distributed, multi-sender notification system โ without a full SMTP infrastructure
Ideal for:
- Network monitoring software
- Firewalls, routers, printers, IP cameras
- IoT and industrial equipment
- Legacy on-prem systems
- Environments with no internet access or firewall egress rules
This tool helps consolidate notifications from many devices and subsystems under one tenant, while still keeping SPF/DKIM/DMARC compliant and not exposing internal systems.
โจ New Features (v2)
โจ Features
- โ Full async SMTP/POP3 proxy
- โ Microsoft Graph API integration (sendMail, get messages)
- โ Shared mailbox support
- โ OAuth2 Device Flow authentication
- โ Encrypted token storage
- โ Built-in mail queue (for offline mode)
- โ Automatic resend when connection restored
- โ Linux and Windows support
- โ Drop-in SMTP/POP3 replacement
๐ Quick Start
pip install m365proxy
# First-time login to Microsoft 365 via Device Flow
m365proxy --login
# Start the proxy (SMTP + POP3)
m365proxy run
๐ฆ Offline Mode & Queue
When Microsoft 365 is unreachable:
- โ๏ธ Messages are queued to disk
- ๐ Automatically retried in background (every 5 minutes)
- ๐ง No data loss โ even if the device is offline for hours
You can inspect the queue at:
ls ~/.m365proxy/queue/
๐ก Architecture
Legacy Device (SMTP) โโโโโโ
โโ> SMTP Proxy โโ> Graph API โโ> Exchange Mailbox
App / Printer / Camera โโโ
POP3 Client <โโโโโโโโโโโโโโ POP3 Proxy <โโโ Graph API (Shared Mail)
๐ Installation
To install the proxy as a Python package:
pip install m365proxy
You can then run it via:
m365proxy
Or:
python -m m365proxy
Usage
python -m m365proxy [options]
python -m m365proxy [-config CONFIG] [command]
Simple SMTP and POP3 mail proxy to Microsoft 365 mailbox over HTTPS (using Microsoft Graph API).
Options
| Option | Description |
|---|---|
-h, --help |
Show help message and exit |
-config CONFIG |
Path to configuration file (default: <home_folder>/.m365proxy/config.json) |
-token TOKEN |
Path to token file (default: <config_folder>/tokens.enc) |
-log-file PATH |
Log file path (default: <config_folder>/m365.log) |
-log-level LEVEL |
Logging level for file. One of DEBUG, INFO, WARNING, ERROR (default: INFO) |
-bind ADDRESS |
Bind address for services (default: 127.0.0.1) |
-smtp-port PORT |
SMTP listening port (default: 10025) |
-pop3-port PORT |
POP3 listening port (optional, default: None) |
-https-proxy URL |
HTTPS proxy URL (e.g. http://proxy.local:3128) |
-no-ssl |
Disable SSL/TLS for SMTP and POP3 |
-debug |
Enable verbose output (CLI only) |
-quiet |
Suppress all output except errors (CLI only) |
Commands
| Command | Description |
|---|---|
| (none) | Start SMTP/POP3 proxy server |
init-config |
Create minimal default config.json |
configure |
Run interactive configuration |
login |
Start Microsoft device code login flow |
check-token |
Check current token validity |
show-token |
Show contents of decrypted token |
check-config |
Show effective configuration |
test |
Send a test email |
hash |
Hash password for use in config.json |
๐
hashrequires an argument: the plain password to be hashed.
Examples
# Start the proxy with default config
python -m m365proxy
# Generate default config file
python -m m365proxy init-config
# Configure interactively
python -m m365proxy configure
# Login via Microsoft device code flow
python -m m365proxy login
# Start the proxy on custom SMTP port and bind to all interfaces
python -m m365proxy -smtp-port 2525 -bind 0.0.0.0
# Run with custom config and token paths in quiet mode
python -m m365proxy -config ./myconfig.json -token /tmp/mytoken.enc -quiet
# Run with detailed logging
python -m m365proxy -config /tmp/myconfig.json -token /tmp/mytoken.enc -log-file /tmp/m365.log -log-level DEBUG
๐ณ Docker
Hereโs how to run m365proxy in a container:
Dockerfile:
FROM python:3.11-slim
WORKDIR /app
# Install the proxy from PyPI
RUN pip install m365proxy
# Copy configuration files
COPY config.json .
COPY tokens.enc .
# Expose required ports
EXPOSE 10025 10110
CMD ["m365proxy"]
Run example:
docker run -d \
-v $(pwd)/config.json:/app/config.json \
-v $(pwd)/tokens.enc:/app/tokens.enc \
-p 10025:10025 -p 10110:10110 \
m365proxy
๐ You can also override config or token paths with
-configand-tokenarguments.
๐ช Windows Task Scheduler
To auto-start the proxy on Windows:
- Open Task Scheduler
- Choose Create Basic Task
- Trigger: At startup or Log on
- Action: Start a program
- Set
Program/script:to:pythonw
SetAdd arguments:to:-m m365proxy -quiet
Or, if using the bundledEXE, point directly tom365proxy.exe.
๐ง systemd (Linux Autostart)
To run the proxy as a background service on Linux, create a systemd unit file:
/etc/systemd/system/m365proxy.service
[Unit]
Description=Microsoft 365 Mail Proxy
After=network.target
[Service]
ExecStart=/usr/bin/python3 -m m365proxy -quiet
WorkingDirectory=/opt/m365proxy
Restart=always
User=nobody
Environment=PYTHONUNBUFFERED=1
[Install]
WantedBy=multi-user.target
Then enable and start:
sudo systemctl daemon-reload
sudo systemctl enable m365proxy
sudo systemctl start m365proxy
Sample Configuration (config.json)
{
"user": "licensed@example.com",
"client_id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"tenant_id": "yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy",
"allowed_domains": ["example.com"],
"mailboxes": [
{
"username": "shared1@example.com",
"password": "hashed-secret1"
},
{
"username": "shared2@example.com",
"password": "hashed-secret2"
}
],
"bind": "127.0.0.1",
"smtp_port": 10025,
"pop3_port": 10110,
"https_proxy": {
"url": "http://proxy.local:3128",
"user": "proxyuser",
"password": "proxypass"
},
"logging": {
"level": "INFO"
}
}
๐ All passwords hashed and stored locally.
Azure AD / Entra ID Setup
To use Microsoft Graph API, you must register your application in Microsoft Entra ID (Azure Active Directory).
1. Register Your App
- Go to https://entra.microsoft.com
- Navigate to Azure Active Directory โ App registrations โ New registration
- Set a name like
smtp-proxy, leave redirect URI empty (device code flow doesn't require it)
2. Save Credentials
- Copy the Client ID and Tenant ID into your
config.json
3. Configure Permissions
- Go to API permissions โ Add a permission โ Microsoft Graph โ Delegated
- Add:
Mail.Send(required, for outgoing mail)Mail.Send.Shared(required, for outgoing mail)Mail.ReadWrite(optional, for incoming mail)Mail.ReadWrite.Shared(optional, for incoming mail)offline_access(required, for refresh tokens)
- Grant admin consent for your tenant
4. Enable Public Client Flow
- Go to Authentication โ Enable public client (mobile & desktop)
- Allow device code flow
5. Authorize the Proxy
- Run the proxy with
login - You will be prompted to sign in once in a browser
- After that, tokens will be refreshed automatically
Shared Mailbox Configuration
To send or receive mail on behalf of shared addresses:
- Go to Microsoft 365 Admin Center โ Shared mailboxes โ Create
- Assign Send As or Send on Behalf rights to your user in the config
Grant Send As Rights (PowerShell):
Add-RecipientPermission -Identity shared@domain.com -Trustee user@domain.com -AccessRights SendAs
Grant Send on Behalf:
Set-Mailbox shared@domain.com -GrantSendOnBehalfTo user@domain.com
๐ Send As is preferred for compatibility.
Shared mailboxes do not require licenses and can be used for routing, monitoring, and distribution identities.
Additional Permissions for POP3 Access
To read or delete messages from shared mailboxes using the POP3 proxy, you must add the following delegated Microsoft Graph API permissions to your app:
Mail.ReadWriteMail.ReadWrite.Shared
These permissions allow the proxy to fetch and delete messages on behalf of the user or shared mailbox.
๐ก๏ธ Without
Mail.ReadWritethe proxy will not be able to mark or delete messages after downloading, which may result in repeated deliveries.
โ Admin consent is required for these permissions.
Disabling POP3 (Optional)
If your use case does not require access to incoming mail, you can disable POP3 entirely:
{
"pop3_port": null
}
๐ This prevents the proxy from exposing any POP3 service.
Notes
- All outgoing messages use the address specified in
MAIL FROM:. - The proxy preserves full message structure: subject, HTML, attachments.
- Attachments are limited to 80MB by default (adjustable).
- All allowed users are declared in
mailboxes[]. - SMTP/POP3 clients must authenticate using one of the defined
username/passwordcombinations. - POP3 supports UIDL to avoid downloading duplicates.
License
MIT โ Author: sh0rch
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file m365proxy-1.2.0.tar.gz.
File metadata
- Download URL: m365proxy-1.2.0.tar.gz
- Upload date:
- Size: 36.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.12.10
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0c812160afde7881938f83443dbf7e74d967dd482cb18ca1e1a953ab2cad9bcf
|
|
| MD5 |
ee2984ded1e4c76c67fdbe27f5358e68
|
|
| BLAKE2b-256 |
f22fc8057a954e6886a1a8d4e6188e21d077d918e4b26fa73c64b29f6003e78c
|
File details
Details for the file m365proxy-1.2.0-py3-none-any.whl.
File metadata
- Download URL: m365proxy-1.2.0-py3-none-any.whl
- Upload date:
- Size: 38.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.12.10
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1a6d5dc901863f8637ad648b3f9027904b802ca18548681fdd269e0becd2d98f
|
|
| MD5 |
2b7a5f126a30c9cc8dc78478c14726a5
|
|
| BLAKE2b-256 |
299e25e7b5e8f91e03b0810da859a2edbcc377269074e70471de57be59b8b109
|