Skip to content

Tunnel inbound mail to the device - #762

Open
cyberb wants to merge 7 commits into
masterfrom
mail-inbound
Open

Tunnel inbound mail to the device#762
cyberb wants to merge 7 commits into
masterfrom
mail-inbound

Conversation

@cyberb

@cyberb cyberb commented Aug 6, 2026

Copy link
Copy Markdown
Member

Device side of inbound mail. Pairs with syncloud/redirect#63, which terminates
port 25 and forwards each message down the tunnel; this is what asks for the
proxy to forward it to.

Shape

  • frpc.toml gains a type = "tcp" proxy alongside the https one, named
    <domain>-smtp to match the suffix redirect strips when it attributes
    tunnel traffic and authorises the requested port.
  • Local port 10025 is the contract with the mail app: postfix listens there
    for mail arriving through the tunnel, separately from its own port 25.

The tunnel is no longer all or nothing

Enable/Disable became Apply, which renders whichever proxies the
settings call for: web when the traffic relay is on, mail when the mail relay
is on and redirect has handed out a port, either without the other.

That combination is the point. The common case for mail is a device with a
perfectly good public address whose isp blocks 25 in both directions. It needs
a tunnel for mail alone, and previously there was no way to have one.

Where the port comes from

redirect returns it with the domain update, which already runs on both paths
that matter: the access page calls Update, the mail relay switch calls
Sync.

Update applies the tunnel a second time after the address update, because
the port only exists once redirect has answered. Without it a device turning
the mail relay on would sit without a mail proxy until some unrelated change
came along. A response that cannot be read is logged and ignored rather than
failing the update, since the address change has already happened and undoing
it over a port would be the worse outcome.

Connecting

Bringing the tunnel up now waits for every proxy it asked for rather than just
the web one, so a half connected tunnel fails loudly instead of leaving a
device that looks healthy and quietly receives no mail.

Not covered here

The mail app still needs its side: a loopback-only postfix service on 10025
with relay restrictions locked down, and rspamd. Until that lands the tunnel
forwards to a port with nothing behind it, which the relay reports as a 4xx
and senders retry, so nothing is lost, but no mail is delivered either.

redirect terminates port 25 and forwards each message down the frp tunnel, so
the device has to ask for a proxy on the port it was allocated. frpc gains a
tcp proxy alongside the https one, named <domain>-smtp to match the suffix
redirect strips when it attributes traffic and authorises the port.

The tunnel is no longer all or nothing. Enable and Disable became Apply, which
renders whichever proxies the settings call for: web when the traffic relay is
on, mail when the mail relay is on and a port has been handed out, either
without the other. That matters because the common case for mail is a device
with a public address whose isp blocks 25 outbound and inbound; it needs a
tunnel for mail alone and had no way to get one.

The port comes back on the domain update, which already runs on both paths:
the access page calls Update and the mail relay switch calls Sync. Update
applies the tunnel a second time afterwards, since the port only exists once
redirect has answered, and without that a device turning the mail relay on
would wait for some unrelated change before its mail proxy appeared. A
response that cannot be read is logged and ignored rather than failing the
update, because the address change has already happened and undoing it over a
port would be worse.

Bringing the tunnel up now waits for every proxy it asked for, not just the
web one, so a half connected tunnel is a failure rather than a device that
looks fine and quietly receives no mail.

Local port 10025 is the contract with the mail app: postfix listens there for
mail arriving through the tunnel, separately from its own port 25.
cyberb added 6 commits August 6, 2026 22:58
Every device was allocated a port on the relay so mailin had something to
dial, which meant a range with a ceiling, ports that were never reclaimed, and
the number travelling back through the domain update into the device's config.

frp routes the web proxy by name over one shared port already. tcpmux does the
same for raw tcp, matching the name in an http CONNECT that mailin sends
itself, so the mail proxy can share a port the same way. The device asks for
its tunnel by its own domain, which is also what the auth plugin already
checks for the web proxy.

That removes the smtp port from the update response, from the device config
and from this side entirely.
Inbound mail arrives at redirect because relaying traffic pointed the domain
there, so the tunnel that carries it belongs to the same switch. The mail relay
keeps its own meaning for outbound submission.

Both proxies now stand or fall together, so the template no longer has to
decide which of them to write.
The smtp proxy is a tcpmux one and the frps the ui tests run against only had
the vhost https port, so it could never register. Waiting for both proxies made
that visible instead of leaving the mail tunnel silently absent.
One was a leftover half sentence and the rest still described handing out a
port per device, which stopped being true when the tunnel moved to tcpmux.
An app registers where it accepts mail from the tunnel and platform forwards
there, so the smtp proxy exists only on devices that can actually receive.
The artifacts for 3055 have been pruned off the ci host, so there is nothing to
compare against and the step fails having compared nothing.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant