mirror of
https://github.com/mjl-/mox.git
synced 2025-07-12 17:04:39 +03:00
improve http request handling for internal services and multiple domains
per listener, you could enable the admin/account/webmail/webapi handlers. but that would serve those services on their configured paths (/admin/, /, /webmail/, /webapi/) on all domains mox would be webserving, including any non-mail domains. so your www.example/admin/ would be serving the admin web interface, with no way to disabled that. with this change, the admin interface is only served on requests to (based on Host header): - ip addresses - the listener host name (explicitly configured in the listener, with fallback to global hostname) - "localhost" (for ssh tunnel/forwarding scenario's) the account/webmail/webapi interfaces are served on the same domains as the admin interface, and additionally: - the client settings domains, as optionally configured in each Domain in domains.conf. typically "mail.<yourdomain>". this means the internal services are no longer served on other domains configured in the webserver, e.g. www.example.org/admin/ will not be handled specially. the order of evaluation of routes/services is also changed: before this change, the internal handlers would always be evaluated first. with this change, only the system handlers for MTA-STS/autoconfig/ACME-validation will be evaluated first. then the webserver handlers. and finally the internal services (admin/account/webmail/webapi). this allows an admin to configure overrides for some of the domains (per hostname-matching rules explained above) that would normally serve these services. webserver handlers can now be configured that pass the request to an internal service: in addition to the existing static/redirect/forward config options, there is now an "internal" config option, naming the service (admin/account/webmail/webapi) for handling the request. this allows enabling the internal services on custom domains. for issue #160 by TragicLifeHu, thanks for reporting!
This commit is contained in:
@ -63,6 +63,16 @@ var (
|
||||
|
||||
var ErrConfig = errors.New("config error")
|
||||
|
||||
// Set by packages webadmin, webaccount, webmail, webapisrv to prevent cyclic dependencies.
|
||||
var NewWebadminHandler = func(basePath string, isForwarded bool) http.Handler { return nopHandler }
|
||||
var NewWebaccountHandler = func(basePath string, isForwarded bool) http.Handler { return nopHandler }
|
||||
var NewWebmailHandler = func(maxMsgSize int64, basePath string, isForwarded bool, accountPath string) http.Handler {
|
||||
return nopHandler
|
||||
}
|
||||
var NewWebapiHandler = func(maxMsgSize int64, basePath string, isForwarded bool) http.Handler { return nopHandler }
|
||||
|
||||
var nopHandler = http.HandlerFunc(nil)
|
||||
|
||||
// Config as used in the code, a processed version of what is in the config file.
|
||||
//
|
||||
// Use methods to lookup a domain/account/address in the dynamic configuration.
|
||||
@ -262,6 +272,13 @@ func (c *Config) Routes(accountName string, domain dns.Domain) (accountRoutes, d
|
||||
return
|
||||
}
|
||||
|
||||
func (c *Config) IsClientSettingsDomain(d dns.Domain) (is bool) {
|
||||
c.withDynamicLock(func() {
|
||||
_, is = c.Dynamic.ClientSettingDomains[d]
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
func (c *Config) allowACMEHosts(log mlog.Log, checkACMEHosts bool) {
|
||||
for _, l := range c.Static.Listeners {
|
||||
if l.TLS == nil || l.TLS.ACME == "" {
|
||||
@ -1124,6 +1141,7 @@ func prepareDynamicConfig(ctx context.Context, log mlog.Log, dynamicPath string,
|
||||
checkRoutes("global routes", c.Routes)
|
||||
|
||||
// Validate domains.
|
||||
c.ClientSettingDomains = map[dns.Domain]struct{}{}
|
||||
for d, domain := range c.Domains {
|
||||
dnsdomain, err := dns.ParseDomain(d)
|
||||
if err != nil {
|
||||
@ -1140,6 +1158,7 @@ func prepareDynamicConfig(ctx context.Context, log mlog.Log, dynamicPath string,
|
||||
addErrorf("bad client settings domain %q: %s", domain.ClientSettingsDomain, err)
|
||||
}
|
||||
domain.ClientSettingsDNSDomain = csd
|
||||
c.ClientSettingDomains[csd] = struct{}{}
|
||||
}
|
||||
|
||||
for _, sign := range domain.DKIM.Sign {
|
||||
@ -1814,6 +1833,29 @@ func prepareDynamicConfig(ctx context.Context, log mlog.Log, dynamicPath string,
|
||||
}
|
||||
}
|
||||
}
|
||||
if wh.WebInternal != nil {
|
||||
n++
|
||||
wi := wh.WebInternal
|
||||
if !strings.HasPrefix(wi.BasePath, "/") || !strings.HasSuffix(wi.BasePath, "/") {
|
||||
addErrorf("webinternal %s %s: base path %q must start and end with /", wh.Domain, wh.PathRegexp, wi.BasePath)
|
||||
}
|
||||
// todo: we could make maxMsgSize and accountPath configurable
|
||||
const isForwarded = false
|
||||
switch wi.Service {
|
||||
case "admin":
|
||||
wi.Handler = NewWebadminHandler(wi.BasePath, isForwarded)
|
||||
case "account":
|
||||
wi.Handler = NewWebaccountHandler(wi.BasePath, isForwarded)
|
||||
case "webmail":
|
||||
accountPath := ""
|
||||
wi.Handler = NewWebmailHandler(config.DefaultMaxMsgSize, wi.BasePath, isForwarded, accountPath)
|
||||
case "webapi":
|
||||
wi.Handler = NewWebapiHandler(config.DefaultMaxMsgSize, wi.BasePath, isForwarded)
|
||||
default:
|
||||
addErrorf("webinternal %s %s: unknown service %q", wh.Domain, wh.PathRegexp, wi.Service)
|
||||
}
|
||||
wi.Handler = SafeHeaders(http.StripPrefix(wi.BasePath[:len(wi.BasePath)-1], wi.Handler))
|
||||
}
|
||||
if n != 1 {
|
||||
addErrorf("webhandler %s %s: must have exactly one handler, not %d", wh.Domain, wh.PathRegexp, n)
|
||||
}
|
||||
|
Reference in New Issue
Block a user