mox

package
v0.0.13 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 6, 2024 License: MIT Imports: 65 Imported by: 13

Documentation

Overview

Package mox provides functions dealing with global state, such as the current configuration, and convenience functions.

Index

Constants

This section is empty.

Variables

View Source
var (
	ConfigStaticPath  string
	ConfigDynamicPath string
	Conf              = Config{Log: map[string]slog.Level{"": slog.LevelError}}
)

Config paths are set early in program startup. They will point to files in the same directory.

View Source
var (
	ErrDomainNotFound  = errors.New("domain not found")
	ErrAddressNotFound = errors.New("address not found")
)
View Source
var Connections = &connections{
	conns:  map[net.Conn]connKind{},
	gauges: map[connKind]prometheus.GaugeFunc{},
	active: map[connKind]int64{},
}

Connections holds all active protocol sockets (smtp, imap). They will be given an immediate read/write deadline shortly after initiating mox shutdown, after which the connections get 1 more second for error handling before actual shutdown.

This context should be used as parent by most operations. It is canceled 1 second after graceful shutdown was initiated with the cancelation of the Shutdown context. This should abort active operations.

Operations typically have context timeouts, 30s for single i/o like DNS queries, and 1 minute for operations with more back and forth. These are set through a context.WithTimeout based on this context, so those contexts are still canceled when shutting down.

HTTP servers don't get graceful shutdown, their connections are just aborted. todo: should shut down http connections as well, and shut down the listener and/or return 503 for new requests.

View Source
var ContextCancel func()
View Source
var ErrConfig = errors.New("config error")
View Source
var ErrRequest = errors.New("bad request")
View Source
var FilesImmediate bool

For privileged file descriptor operations (listen and opening privileged files), perform them immediately, regardless of running as root or other user, in case ForkExecUnprivileged is not used.

View Source
var LicensesFsys fs.FS
View Source
var LimiterFailedAuth *ratelimit.Limiter
View Source
var NewWebaccountHandler = func(basePath string, isForwarded bool) http.Handler { return nopHandler }
View Source
var NewWebadminHandler = func(basePath string, isForwarded bool) http.Handler { return nopHandler }

Set by packages webadmin, webaccount, webmail, webapisrv to prevent cyclic dependencies.

View Source
var NewWebapiHandler = func(maxMsgSize int64, basePath string, isForwarded bool) http.Handler { return nopHandler }
View Source
var NewWebmailHandler = func(maxMsgSize int64, basePath string, isForwarded bool, accountPath string) http.Handler {
	return nopHandler
}
View Source
var Pedantic bool

Pedantic enables stricter parsing.

View Source
var Shutdown context.Context

Shutdown is canceled when a graceful shutdown is initiated. SMTP, IMAP, periodic processes should check this before starting a new operation. If this context is canaceled, the operation should not be started, and new connections/commands should receive a message that the service is currently not available.

View Source
var ShutdownCancel func()

Functions

func AcceptsGzip added in v0.0.9

func AcceptsGzip(r *http.Request) bool

AcceptsGzip returns whether the client accepts gzipped responses.

func AccountAdd

func AccountAdd(ctx context.Context, account, address string) (rerr error)

AccountAdd adds an account and an initial address and reloads the configuration.

The new account does not have a password, so cannot yet log in. Email can be delivered.

Catchall addresses are not supported for AccountAdd. Add separately with AddressAdd.

func AccountRemove

func AccountRemove(ctx context.Context, account string) (rerr error)

AccountRemove removes an account and reloads the configuration.

func AccountSave added in v0.0.11

func AccountSave(ctx context.Context, account string, xmodify func(acc *config.Account)) (rerr error)

AccountSave updates the configuration of an account. Function xmodify is called with a shallow copy of the current configuration of the account. It must not change referencing fields (e.g. existing slice/map/pointer), they may still be in use, and the change may be rolled back. Referencing values must be copied and replaced by the modify. The function may raise a panic for error handling.

func AddressAdd

func AddressAdd(ctx context.Context, address, account string) (rerr error)

AddressAdd adds an email address to an account and reloads the configuration. If address starts with an @ it is treated as a catchall address for the domain.

func AddressRemove

func AddressRemove(ctx context.Context, address string) (rerr error)

AddressRemove removes an email address and reloads the configuration. Address can be a catchall address for the domain of the form "@<domain>".

If the address is member of an alias, remove it from from the alias, unless it is the last member.

func AliasAdd added in v0.0.11

func AliasAdd(ctx context.Context, addr smtp.Address, alias config.Alias) error

func AliasAddressesAdd added in v0.0.11

func AliasAddressesAdd(ctx context.Context, addr smtp.Address, addresses []string) error

func AliasAddressesRemove added in v0.0.11

func AliasAddressesRemove(ctx context.Context, addr smtp.Address, addresses []string) error

func AliasRemove added in v0.0.11

func AliasRemove(ctx context.Context, addr smtp.Address) error

func AliasUpdate added in v0.0.11

func AliasUpdate(ctx context.Context, addr smtp.Address, alias config.Alias) error

func AllowMsgFrom added in v0.0.11

func AllowMsgFrom(accountName string, msgFrom smtp.Address) bool

AllowMsgFrom returns whether account is allowed to submit messages with address as message From header, based on configured addresses and membership of aliases that allow using its address.

func CanonicalLocalpart

func CanonicalLocalpart(localpart smtp.Localpart, d config.Domain) smtp.Localpart

CanonicalLocalpart returns the canonical localpart, removing optional catchall separator, and optionally lower-casing the string.

func Cid

func Cid() int64

Cid returns a new unique id to be used for connections/sessions/requests.

func CidFromCtx added in v0.0.2

func CidFromCtx(ctx context.Context) int64

CidFromCtx returns the cid in the context, or 0.

func CleanupPassedFiles added in v0.0.4

func CleanupPassedFiles()

CleanupPassedFiles closes the listening socket file descriptors and files passed in by the parent process. To be called by the unprivileged child after listeners have been recreated (they dup the file descriptor), and by the privileged process after starting its child.

func ConfigDirPath

func ConfigDirPath(f string) string

ConfigDirPath returns the path to "f". Either f itself when absolute, or interpreted relative to the directory of the current config file.

func ConfigSave added in v0.0.11

func ConfigSave(ctx context.Context, xmodify func(config *config.Dynamic)) (rerr error)

ConfigSave calls xmodify with a shallow copy of the dynamic config. xmodify can modify the config, but must clone all referencing data it changes. xmodify may employ panic-based error handling. After xmodify returns, the modified config is verified, saved and takes effect.

func CryptoRandInt

func CryptoRandInt() int64

CryptoRandInt returns a cryptographically random number.

func DKIMAdd added in v0.0.11

func DKIMAdd(ctx context.Context, domain, selector dns.Domain, algorithm, hash string, headerRelaxed, bodyRelaxed, seal bool, headers []string, lifetime time.Duration) (rerr error)

DKIMAdd adds a DKIM selector for a domain, generating a key and writing it to disk.

func DKIMRemove added in v0.0.11

func DKIMRemove(ctx context.Context, domain, selector dns.Domain) (rerr error)

DKIMRemove removes the selector from the domain, moving the key file out of the way.

func DKIMSelectors added in v0.0.9

func DKIMSelectors(dkimConf config.DKIM) []dkim.Selector

DKIMSelectors returns the selectors to use for signing.

func DKIMSign added in v0.0.9

func DKIMSign(ctx context.Context, log mlog.Log, from smtp.Path, smtputf8 bool, data []byte) (string, error)

DKIMSign looks up the domain for "from", and uses its DKIM configuration to generate DKIM-Signature headers, for inclusion in a message. The DKIM-Signatur headers, are returned. If no domain was found an empty string and nil error is returned.

func DataDirPath

func DataDirPath(f string) string

DataDirPath returns to the path to "f". Either f itself when absolute, or interpreted relative to the data directory from the currently active configuration.

func DomainAdd

func DomainAdd(ctx context.Context, domain dns.Domain, accountName string, localpart smtp.Localpart) (rerr error)

DomainAdd adds the domain to the domains config, rewriting domains.conf and marking it loaded.

accountName is used for DMARC/TLS report and potentially for the postmaster address. If the account does not exist, it is created with localpart. Localpart must be set only if the account does not yet exist.

func DomainRecords

func DomainRecords(domConf config.Domain, domain dns.Domain, hasDNSSEC bool, certIssuerDomainName, acmeAccountURI string) ([]string, error)

DomainRecords returns text lines describing DNS records required for configuring a domain.

If certIssuerDomainName is set, CAA records to limit TLS certificate issuance to that caID will be suggested. If acmeAccountURI is also set, CAA records also restricting issuance to that account ID will be suggested.

func DomainRemove

func DomainRemove(ctx context.Context, domain dns.Domain) (rerr error)

DomainRemove removes domain from the config, rewriting domains.conf.

No accounts are removed, also not when they still reference this domain.

func DomainSPFIPs added in v0.0.12

func DomainSPFIPs() (ips []net.IP)

DomainSPFIPs returns IPs to include in SPF records for domains. It includes the IPs on listeners that have SMTP enabled, and includes IPs configured for SOCKS transports.

func DomainSave added in v0.0.11

func DomainSave(ctx context.Context, domainName string, xmodify func(config *config.Domain) error) (rerr error)

DomainSave calls xmodify with a shallow copy of the domain config. xmodify can modify the config, but must clone all referencing data it changes. xmodify may employ panic-based error handling. After xmodify returns, the modified config is verified, saved and takes effect.

func FallbackMtime added in v0.0.9

func FallbackMtime(log mlog.Log) time.Time

FallbackMtime returns a time to use for the Last-Modified header in case we cannot find a file, e.g. when used in production.

func FillExample added in v0.0.11

func FillExample(seen []reflect.Type, rv reflect.Value) reflect.Value

FillExample returns a modified value with nil/empty maps/slices/pointers values replaced with non-empty versions, for more helpful examples of types. Useful for documenting JSON representations of types.

func FillNil added in v0.0.11

func FillNil(rv reflect.Value) (nv reflect.Value, changed bool)

FillNil returns a modified value with nil maps/slices replaced with empty maps/slices.

func ForkExecUnprivileged added in v0.0.2

func ForkExecUnprivileged()

Fork and exec as unprivileged user.

We don't use just setuid because it is hard to guarantee that no other privileged go worker processes have been started before we get here. E.g. init functions in packages can start goroutines.

func IPs

func IPs(ctx context.Context, receiveOnly bool) ([]net.IP, error)

IPs returns ip addresses we may be listening/receiving mail on or connecting/sending from to the outside.

func LastKnown

func LastKnown() (current, lastknown updates.Version, mtime time.Time, rerr error)

LastKnown returns the last known version that has been mentioned in an update email, or the current application.

func LicensesWrite added in v0.0.12

func LicensesWrite(dst io.Writer) error

LicensesWrite writes the licenses to dst.

func LimitersInit

func LimitersInit()

LimitesrsInit initializes the failed auth rate limiter.

func Listen added in v0.0.2

func Listen(network, addr string) (net.Listener, error)

Listen returns a newly created network listener when starting as root, and otherwise (not root) returns a network listener from a file descriptor that was passed by the parent root process.

func LoadConfig

func LoadConfig(ctx context.Context, log mlog.Log, doLoadTLSKeyCerts, checkACMEHosts bool) []error

LoadConfig attempts to parse and load a config, returning any errors encountered.

func LocalserveNeedsError added in v0.0.11

func LocalserveNeedsError(lp smtp.Localpart) (code int, timeout bool)

func LookupAddress added in v0.0.11

func LookupAddress(localpart smtp.Localpart, domain dns.Domain, allowPostmaster, allowAlias bool) (accountName string, alias *config.Alias, canonicalAddress string, dest config.Destination, rerr error)

FindAccount looks up the account for localpart and domain.

Can return ErrDomainNotFound and ErrAddressNotFound.

func MakeAccountConfig

func MakeAccountConfig(addr smtp.Address) config.Account

MakeAccountConfig returns a new account configuration for an email address.

func MakeDKIMEd25519Key

func MakeDKIMEd25519Key(selector, domain dns.Domain) ([]byte, error)

MakeDKIMEd25519Key returns a PEM buffer containing an ed25519 key for use with DKIM. selector and domain can be empty. If not, they are used in the note.

func MakeDKIMRSAKey

func MakeDKIMRSAKey(selector, domain dns.Domain) ([]byte, error)

MakeDKIMRSAKey returns a PEM buffer containing an rsa key for use with DKIM. selector and domain can be empty. If not, they are used in the note.

func MakeDomainConfig

func MakeDomainConfig(ctx context.Context, domain, hostname dns.Domain, accountName string, withMTASTS bool) (config.Domain, []string, error)

MakeDomainConfig makes a new config for a domain, creating DKIM keys, using accountName for DMARC and TLS reports.

func MessageIDGen

func MessageIDGen(smtputf8 bool) string

MessageIDGen returns a generated unique random Message-Id value, excluding <>.

func MustLoadConfig

func MustLoadConfig(doLoadTLSKeyCerts, checkACMEHosts bool)

MustLoadConfig loads the config, quitting on errors.

func Network

func Network(ip string) string

Network returns tcp4 or tcp6, depending on the ip. This network can be passed to Listen instead of "tcp", which may start listening on both ipv4 and ipv6 for addresses 0.0.0.0 and ::, which can lead to errors about the port already being in use. For invalid IPs, "tcp" is returned.

func NewPseudoRand added in v0.0.8

func NewPseudoRand() *rand

NewPseudoRand returns a new PRNG seeded with random bytes from crypto/rand. Its functions can be called concurrently.

func OpenPrivileged added in v0.0.4

func OpenPrivileged(path string) (*os.File, error)

Open a privileged file, such as a TLS private key. When running as root (during startup), the file is opened and the file descriptor is stored. These file descriptors are passed to the unprivileged process. When in the unprivileged processed, we lookup a passed file descriptor. The same calls should be made in the privileged and unprivileged process.

func ParseDynamicConfig

func ParseDynamicConfig(ctx context.Context, log mlog.Log, dynamicPath string, static config.Static) (c config.Dynamic, mtime time.Time, accDests map[string]AccountDestination, aliases map[string]config.Alias, errs []error)

PrepareDynamicConfig parses the dynamic config file given a static file.

func PrepareStaticConfig

func PrepareStaticConfig(ctx context.Context, log mlog.Log, configFile string, conf *Config, checkOnly, doLoadTLSKeyCerts bool) (errs []error)

PrepareStaticConfig parses the static config file and prepares data structures for starting mox. If checkOnly is set no substantial changes are made, like creating an ACME registration.

func ReceivedID

func ReceivedID(cid int64) string

ReceivedID returns an ID for use in a message Received header.

The ID is based on the cid. The cid itself is a counter and would leak the number of connections in received headers. Instead they are obfuscated by encrypting them with AES with a per-install key and random buffer. This allows recovery of the cid based on the id. See subcommand cid.

func ReceivedIDInit

func ReceivedIDInit(key, rand []byte) error

ReceivedIDInit sets an AES key (must be 16 bytes) and random buffer (must be 8 bytes) for use by ReceivedID.

func ReceivedToCid

func ReceivedToCid(s string) (cid int64, err error)

ReceivedToCid returns the cid given a ReceivedID.

func RestorePassedFiles added in v0.0.4

func RestorePassedFiles()

RestorePassedFiles reads addresses from $MOX_SOCKETS and paths from $MOX_FILES and prepares an os.File for each file descriptor, which are used by later calls of Listen or opening files.

func SafeHeaders added in v0.0.12

func SafeHeaders(fn http.Handler) http.Handler

Set some http headers that should prevent potential abuse. Better safe than sorry.

func SetConfig

func SetConfig(c *Config)

SetConfig sets a new config. Not to be used during normal operation.

func SetPedantic added in v0.0.9

func SetPedantic(p bool)

Set pedantic in all packages.

func Sleep

func Sleep(ctx context.Context, d time.Duration)

Sleep for d, but return as soon as ctx is done.

Used for a few places where sleep is used to push back on clients, but where shutting down should abort the sleep.

func StoreLastKnown

func StoreLastKnown(v updates.Version) error

StoreLastKnown stores the the last known version. Future update checks compare against it, or the currently running version, whichever is newer.

func TLSReceivedComment added in v0.0.9

func TLSReceivedComment(log mlog.Log, cs tls.ConnectionState) []string

TLSReceivedComment returns a comment about TLS of the connection for use in a Receive header.

func TXTStrings

func TXTStrings(s string) string

TXTStrings returns a TXT record value as one or more quoted strings, each max 100 characters. In case of multiple strings, a multi-line record is returned.

Types

type AccountDestination

type AccountDestination struct {
	Catchall    bool           // If catchall destination for its domain.
	Localpart   smtp.Localpart // In original casing as written in config file.
	Account     string
	Destination config.Destination
}

type ClientConfig

type ClientConfig struct {
	IMAP       ProtocolConfig
	Submission ProtocolConfig
}

func ClientConfigDomain

func ClientConfigDomain(d dns.Domain) (rconfig ClientConfig, rerr error)

ClientConfigDomain returns a single IMAP and Submission client configuration for a domain.

type ClientConfigs added in v0.0.7

type ClientConfigs struct {
	Entries []ClientConfigsEntry
}

ClientConfigs holds the client configuration for IMAP/Submission for a domain.

func ClientConfigsDomain added in v0.0.7

func ClientConfigsDomain(d dns.Domain) (ClientConfigs, error)

ClientConfigsDomain returns the client configs for IMAP/Submission for a domain.

type ClientConfigsEntry added in v0.0.7

type ClientConfigsEntry struct {
	Protocol string
	Host     dns.Domain
	Port     int
	Listener string
	Note     string
}

type Config

type Config struct {
	Static config.Static // Does not change during the lifetime of a running instance.

	Log map[string]slog.Level

	Dynamic config.Dynamic // Can only be accessed directly by tests. Use methods on Config for locked access.

	DynamicLastCheck time.Time // For use by quickstart only to skip checks.
	// contains filtered or unexported fields
}

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.

func ParseConfig

func ParseConfig(ctx context.Context, log mlog.Log, p string, checkOnly, doLoadTLSKeyCerts, checkACMEHosts bool) (c *Config, errs []error)

ParseConfig parses the static config at path p. If checkOnly is true, no changes are made, such as registering ACME identities. If doLoadTLSKeyCerts is true, the TLS KeyCerts configuration is loaded and checked. This is used during the quickstart in the case the user is going to provide their own certificates. If checkACMEHosts is true, the hosts allowed for acme are compared with the explicitly configured ips we are listening on.

func (*Config) Account

func (c *Config) Account(name string) (acc config.Account, ok bool)

func (*Config) AccountDestination

func (c *Config) AccountDestination(addr string) (accDest AccountDestination, alias *config.Alias, ok bool)

func (*Config) Accounts

func (c *Config) Accounts() (l []string)

func (*Config) Domain

func (c *Config) Domain(d dns.Domain) (dom config.Domain, ok bool)

func (*Config) DomainLocalparts

func (c *Config) DomainLocalparts(d dns.Domain) (map[string]string, map[string]config.Alias)

DomainLocalparts returns a mapping of encoded localparts to account names for a domain, and encoded localparts to aliases. An empty localpart is a catchall destination for a domain.

func (*Config) Domains

func (c *Config) Domains() (l []string)

func (*Config) DynamicConfig added in v0.0.11

func (c *Config) DynamicConfig() (config config.Dynamic)

DynamicConfig returns a shallow copy of the dynamic config. Must not be modified.

func (*Config) IsClientSettingsDomain added in v0.0.12

func (c *Config) IsClientSettingsDomain(d dns.Domain) (is bool)

func (*Config) LogLevelRemove

func (c *Config) LogLevelRemove(log mlog.Log, pkg string)

LogLevelRemove removes a configured log level for a package.

func (*Config) LogLevelSet

func (c *Config) LogLevelSet(log mlog.Log, pkg string, level slog.Level)

LogLevelSet sets a new log level for pkg. An empty pkg sets the default log value that is used if no explicit log level is configured for a package. This change is ephemeral, no config file is changed.

func (*Config) LogLevels

func (c *Config) LogLevels() map[string]slog.Level

LogLevels returns a copy of the current log levels.

func (*Config) Routes added in v0.0.5

func (c *Config) Routes(accountName string, domain dns.Domain) (accountRoutes, domainRoutes, globalRoutes []config.Route)

type ProtocolConfig added in v0.0.7

type ProtocolConfig struct {
	Host    dns.Domain
	Port    int
	TLSMode TLSMode
}

type TLSMode added in v0.0.7

type TLSMode uint8
const (
	TLSModeImmediate TLSMode = 0
	TLSModeSTARTTLS  TLSMode = 1
	TLSModeNone      TLSMode = 2
)

type WebappFile added in v0.0.9

type WebappFile struct {
	HTML, JS         []byte // Embedded html/js data.
	HTMLPath, JSPath string // Paths to load html/js from during development.

	sync.Mutex
	// contains filtered or unexported fields
}

WebappFile serves a merged HTML and JS webapp as a single compressed, cacheable file. It merges the JS into the HTML at first load, caches a gzipped version that is generated on first need, and responds with a Last-Modified header.

func (*WebappFile) Serve added in v0.0.9

func (a *WebappFile) Serve(ctx context.Context, log mlog.Log, w http.ResponseWriter, r *http.Request)

Serve serves a combined file, with headers for caching and possibly gzipped.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL