SMTPD.CONF(5)
π¦ SMTPD.CONF(5) π¦
File Formats Manual, bestie. We're decoding the config for the mail daemon, okay? π
π NAME π
smtpd.conf β SMTP daemon configuration file
β¨ DESCRIPTION β¨
smtpd.conf is the configuration file for the mail daemon smtpd(8).
When mail arrives, each RCPT TO: command generates a mail envelope. If an envelope matches any of a pre-designated set of criteria (using the match directive), the message is accepted for delivery. A copy of the message, as well as its associated envelopes, is saved in the mail queue and later dispatched according to an associated set of actions (using the action directive). If an envelope does not match any options, it is rejected. The match rules are evaluated sequentially, with the first match winning. periodt.
The format of the configuration file is fairly flexible. The current line can be extended over multiple lines using a backslash (\). Comments can be put anywhere in the file using a hash mark (#) and extend to the end of the current line. Care should be taken when commenting out multi-line text: the comment is effective until the end of the entire block. Arguments not beginning with a letter, digit, or underscore, as well as reserved words (such as listen, match, and port), must be quoted. Arguments containing whitespace should be surrounded by double quotes ("). π
A macro is defined with a command of the form name=value. The macro name can contain letters, digits, and underscores and cannot be a reserved word. Within unquoted arguments, the string $name is later expanded to value.
For example:
lan_addr = "192.168.0.1"
listen on $lan_addr
listen on $lan_addr tls auth
The syntax of smtpd.conf is described below. π
πΈ action name method [options] πΈ
When the queue runner processes an envelope from the mail queue, it carries out the action name, selected by the match ... action directive when the message was received. The action directive provides configuration data for delivery attempts. Required lookups are performed at the time of each delivery attempt. Consequently, changing an action directive or the files it references and restarting the smtpd(8) daemon causes the changes to take effect for subsequent delivery attempts for the respective dispatcher name, even for messages that were already stuck in the queue prior to the configuration changes. No cap, the queue remembers. π
π The delivery method parameter may be one of the following: π
expand-onlyβ Only accept the message if a delivery method was specified in analiasesor.forwardfile.forward-onlyβ Only accept the message if the recipient results in a remote address after the processing ofaliasesor forward file.lmtp destination [rcpt-to]β Deliver the message to an LMTP server at destination. The location may be expressed ashost:portor as a UNIX socket. Optionally,rcpt-tomight be specified to use the recipient email address (after expansion) instead of the local user in the LMTP session asRCPT TO.maildir [pathname] [junk]β Deliver the message to the maildir in pathname if specified, or by default to~/Maildir. The pathname may contain format specifiers that are expanded before use (see FORMAT SPECIFIERS). If thejunkargument is provided, the message will be moved to theJunkfolder if it contains a positiveX-Spamheader. This folder will be created under pathname if it does not yet exist.mboxβ Deliver the message to the user's mbox withmail.local(8).mda commandβ Delegate the delivery to a command that receives the message on its standard input (see MDA COMMANDS). The command may contain format specifiers that are expanded before use (see FORMAT SPECIFIERS).relayβ Relay the message to another SMTP server.
π The local delivery methods support additional options: π
alias <table>β Use the mapping table foraliases(5)expansion.ttl n{s|m|h|d}β Specify how long a message may remain in the queue.user usernameβ Specify the username for performing the delivery, to be looked up withgetpwnam(3). This is used for virtual hosting where a single username is in charge of handling delivery for all virtual users. This option is not usable with themboxdelivery method. Only the delivery user's.forwardfile will be processed.userbase <table>β Use the mapping table for user lookups instead of thegetpwnam(3)function. Theuserbasedoes not apply to theuseroption.virtual <table>β Use the mapping table for virtual expansion. The aliasing table format is described intable(5).wrapper nameβ Use the wrapper specified inmda wrapper.
π The relay delivery methods also support additional options: π
backupβ Operate as a backup mail exchanger delivering messages to any mail exchanger with higher priority.backup mx nameβ Operate as a backup mail exchanger delivering messages to any mail exchanger with higher priority than mail exchanger identified as name.ca canameβ For secure connections, use the certificate authority associated with caname (declared in acadirective) to validate the server's identity.helo helonameβ Advertise heloname as the hostname to other mail exchangers during the HELO phase.helo-src <table>β Use the mapping table to look up a hostname matching the source address, to advertise during the HELO phase.domain <domains>β Do not perform MX lookups but look up destination domain in domains and use matching relay url as relay host.host relay-urlβ Do not perform MX lookups but relay messages to the relay host described by relay-url. The format for relay-url is[proto://[label@]]host[:port]. The following protocols are available:smtpβ Normal SMTP session with opportunistic STARTTLS (the default).smtp+tlsβ Normal SMTP session with mandatory STARTTLS.smtp+notlsβ Plain text SMTP session without TLS.lmtpβ LMTP session. port is required.smtpsβ SMTP session with forced TLS on connection. The default port is465. Unless noted, port defaults to25. The label corresponds to an entry in a credentials table, as documented intable(5). It is used with thesmtp+tlsandsmtpsprotocols for authentication. Server certificates for those protocols are verified by default.
pki pkinameβ For secure connections, use the certificate associated with pkiname (declared in apkidirective) to prove the client's identity to the remote mail server.srsβ When relaying a mail resulting from a forward, use the Sender Rewriting Scheme to rewrite sender address.tls [no-verify]β Require TLS to be used when relaying, using mandatory STARTTLS by default. When used with a smarthost, the protocol must not besmtp+notls://. Ifno-verifyis specified, do not require a valid certificate.protocols protostrβ Define the protocol versions to be used for TLS sessions. Refer to thetls_config_parse_protocols(3)manpage for the format of protostr.ciphers cipherstrβ Define the list of ciphers that may be used for TLS sessions. Refer to thetls_config_set_ciphers(3)manpage for the format of cipherstr.auth < table >β use the mappingtableto connect torelay-urlwith credentials. This one only works if you also have thehostoption set, periodt. The credential table format? It's all intable(5), bestie. πmail-from mailaddrβ setsmailaddras the MAIL FROM address in the SMTP transaction. Like, that's the sender identity, no cap. π§src sourceaddr | < sourceaddr >β use the string or list tablesourceaddrfor the source IP address. Super handy on machines with multiple interfaces. If the list has more than one address, all of them get used, and traffic is routed as efficiently as possible. Efficient queen. π
admd authservidβ sets the Administrative Management Domain this mail server belongs to. Theauthservidgets forwarded to filters, which use it to identify or mark authentication-results headers. If you omit it, it defaults to the server name. Slay. β¨bounce warn-interval delay [, delay ...]β sends warning messages to the envelope sender when temporary delivery failures keep a message in the queue for longer thandelay. Eachdelayis a positive decimal integer plus a unit:s,m,h, ord. You can specify at most fourdelayparameters. The default is"bounce warn-interval 4h", which sends a single warning after four hours. So like, don't ghost the sender. πca caname cert cafileβ associates the Certificate Authority (CA) certificate filecafilewith thecaentrycaname. You can reference thatcaentry inlisten onandaction ... relayrules. Secure vibes only. πfilter chain-name chain {filter-name [, ...]}β registers a chain of filters namedchain-name, made up of the filters listed infilter-name. Filters in a chain run in the order they're declared, for each phase they're registered for. You can use a filter chain in place of a filter for any directive except filter chains themselves. So meta. πfilter filter-name phase phase-name match conditions decisionβ registers a filter calledfilter-name. Adecisionabout what to do with the mail happens at phasephase-namewhen matchingconditions. Phases, matching conditions, and decisions are all described in MAIL FILTERING, below. Go read that. πfilter filter-name proc proc-nameβ registers a"proc"filter namedfilter-name, backed by theproc-nameprocess. Like, it's a whole process behind the scenes. πfilter filter-name proc-exec commandβ registers and executes a"proc"filter namedfilter-namefromcommand, conformant with thesmtpd-filters(7)API. Ifcommandstarts with a slash, it's executed with an absolute path; otherwise it runs from/usr/libexec/opensmtpd. Path logic, but make it make sense. πinclude "pathname"β replaces this directive with the content of the additional configuration file at the absolutepathname. Like a config import, bestie. π₯listen on interface [family] [options]β listens on theinterfacefor incoming connections, using the same syntax asifconfig(8). Theinterfaceparameter can also be an interface group, an IP address, or a domain name. You can optionally restrict listening to a specific addressfamily, eitherinet4orinet6. Theoptionsare as follows:auth [< authtable >]β supports SMTPAUTH: clients can only start SMTP transactions after successful authentication. Users are authenticated against either their own normal login credentials or a credentials tableauthtable, whose format is described intable(5). Auth wall, but make it chic. πauth-optional [< authtable >]β supports SMTPAUTH optionally: clients don't have to authenticate, but they can. This lets alisten ondirective both accept incoming mail from untrusted senders and permit outgoing mail from authenticated users (usingmatch auth). Use it when you can't listen on a separate port (usually the submission port, 587) for users to authenticate. Flexible queen. πββοΈca canameβ for secure connections, use the CA certificate associated withcaname(declared in acadirective) as the CA certificate when verifying client certificates. Trust issues, but the secure kind. πfilter nameβ applies filternameon connections handled by this listener. Filter it, bestie. π§hostname hostnameβ useshostnamein the greeting banner instead of the default server name. Customize that intro. β¨hostnames < names >β overrides the server name for specific addresses. Thenamestable maps IP addresses to hostnames. If the address where the connection arrives is in the mapping, the associated hostname is used. IP-to-name glow-up. πmask-srcβ omits thefrompart when prependingReceivedheaders. Like, hide the source a little. πno-dsnβ disables the DSN (Delivery Status Notification) extension. No delivery receipts, periodt. π«pki pkinameβ for secure connections, use the certificate associated withpkiname(declared in apkidirective) to prove a mail server's identity. You can use this option multiple times to provide alternate certificates for SNI. Cert flex. πport [port]β listens on the givenportinstead of the default port 25. Port control, no cap. πproxy-v2β supports the PROXYv2 protocol, appropriately rewriting the source address received from proxy. Proxy glow-up. πͺreceived-authβ inReceivedheaders, reports whether the session was authenticated and by which local user. Receipts on the receipts. π§Ύsenders < users >[masquerade]β looks up the authenticated user in theusersmapping table to find the email addresses that user is allowed to submit mail as. Plus, if themasqueradeoption is provided, the From header is rewritten to match the sender provided in the SMTP session. Identity switch, but make it authorized. πsmtps [verify]β supports SMTPS, by default on port 465. With theverifyoption, clients must also provide a valid certificate to establish an SMTP session. Secure socket, but make it verify. π
Mutually exclusive with tls β pick one, bestie. π
tag tagβ clients connecting to the listener are tagged with the giventag. Tagged, you're it. π·οΈtlsβ supports STARTTLS, by default on port 25. TLS but make it start. β¨
Mutually exclusive with smtps β same energy. π
tls-require [verify]β liketls, but forces clients to establish a secure connection before they're allowed to start an SMTP transaction. With theverifyoption, clients must also provide a valid certificate to establish an SMTP session. No secure connection, no entry. π«protocols protostrβ defines the protocol versions to be used for TLS sessions. Refer to thetls_config_parse_protocols(3)manpage for the format ofprotostr. Protocol picker, iykyk. π‘ciphers cipherstrβ defines the list of ciphers that may be used for TLS sessions. Refer to thetls_config_set_ciphers(3)manpage for the format ofcipherstr. Cipher suite, but make it curated. π
listen on socket [options]β listens for incoming SMTP connections on the Unix domain socket/run/smtpd.sock. This happens by default, even if the directive is absent. Socket slay. π Theoptionsare as follows:filter nameβ applies filternameon connections handled by this listener. Same filter energy. π§mask-srcβ omits thefrompart when prependingReceivedheaders. Hide the source. πno-dsnβ disables the DSN (Delivery Status Notification) extension. No delivery receipts. π«tag tagβ clients connecting to the listener are tagged with the giventag. Tagged. π·οΈ
matchoptionsactionname β bestie, this is the glow-up clause β¨ if even ONE mail envelope matches the options of a singlematch actiondirective, then boom: we accept the incoming message, drop a copy into every matching envelope, and atomically save those envelopes to the mail spool so the respective dispatcher name can deal with them later. And that dispatcher? It has to be declared beforehand with theactiondirective. No declaring it after, that's not the vibe. π
The matching options below are all supported, and every single one of them can be negated with !:
[ !] for anyβ session may address literally any destination. Anything. Anywhere. Periodt. π[ !] for localβ session may address any local domain. This is the default, so you can just... omit it. It's the chill one. π«Ά[ !] for domain [regex]/domain|<domain>β session may address the string or list table domain. Theregexkeyword is required if domain is a regular expression or a table of regular expressions. Don't forget it, she will not work otherwise. π[ !] for rcpt-to [regex]/recipient|<recipient>β session may address the string or list table recipient. Theregexkeyword is required if recipient is a regular expression or a table of regular expressions.[ !] from anyβ session may originate from any source. Feral. Unhinged. We love that for her. π[ !] from authβ session may originate from any authenticated user, no matter the source IP address. IP who? Never heard of her. π¦[ !] from auth [regex]/user|<user>β session may originate from authenticated user or user list user, no matter the source IP address. Theregexkeyword is required if user is a regular expression or a table of regular expressions.[ !] from localβ session may only originate from a local IP address, or from the local enqueuer. This is the default, and may be omitted. Homebody energy, honestly. π[ !] from mail-from [regex]/sender|<sender>β session may originate from sender or sender list sender, no matter the source IP address. Theregexkeyword is required if sender is a regular expression or a table of regular expressions.[ !] from rdnsβ session may only originate from an IP address that resolves to a reverse DNS. If she can't reverse-lookup, she can't come in. Bye. π[ !] from rdns [regex]/hostname|<hostname>β session may only originate from an IP address that resolves to a reverse DNS matching string or list string hostname. Theregexkeyword is required if hostname is a regular expression or a table of regular expressions.[ !] from socketβ session may only originate from the local enqueuer. Local only, no long-distance relationships. π΅[ !] from src [regex]/address|<address>β session may only originate from string or list table address, which can be a specific address or a subnet expressed in CIDR-notation. Theregexkeyword is required if address is a regular expression or a table of regular expressions.
Plus, bestie, the following transaction options can also be matched:
[ !] authβ matches transactions which have been authenticated. Verified and valid, she ate. β[ !] auth [regex]/username|<username>β matches transactions which have been authenticated for user or user list username. Theregexkeyword is required if username is a regular expression or a table of regular expressions.[ !] helo [regex]/helo-name|<helo-name>β session'sHELO/EHLOshould match the string or list table helo-name. Theregexkeyword is required if helo-name is a regular expression or a table of regular expressions.[ !] mail-from [regex]/sender|<sender>β transaction'sMAIL FROMshould match the string or list table sender. Theregexkeyword is required if sender is a regular expression or a table of regular expressions.[ !] rcpt-to [regex]/recipient|<recipient>β transaction'sRCPT TOshould match the string or list table recipient. Theregexkeyword is required if recipient is a regular expression or a table of regular expressions.[ !] tag [regex] tagβ matches transactions tagged with the given tag. Theregexkeyword is required if tag is a regular expression.[ !] tlsβ transaction should take place in a TLS channel. Secure the bag, fr fr. πβ¨
matchoptionsrejectβ reject the incoming message during the SMTP dialogue. Rude but efficient. The same options are supported as for thematch actiondirective β same menu, different ending. π«mda wrappername command β associate command with the mail delivery agent wrapper named name. When a local delivery specifies a wrapper, the command associated with that wrapper gets executed instead. The command may contain format specifiers (go peek at FORMAT SPECIFIERS). πmta max-deferrednumber β when delivery to a given host is suspended because of temporary failures, cache at most number envelopes for that host, so they can be delivered as soon as another delivery succeeds to that host. The default is100. Hoarding, but make it strategic. ποΈpkipkinamecertcertfile β associate certificate file certfile with pki entry pkiname. The pki entry defines a keypair configuration that can be referenced in listener rules and relay actions.
A certificate chain may be created by appending one or many certificates β including a Certificate Authority certificate β to certfile. The creation of certificates is documented in starttls(8). π
pkipkinamekeykeyfile β associate the key located in keyfile with pki entry pkiname. The other half of the power couple. ππpkipkinamedheparams β specify the DHE parameters to use for DHE cipher suites with pki entry pkiname. Valid parameter values arenone,legacy, andauto. Forlegacy, a fixed key length of 1024 bits is used, whereas forauto, the key length is determined automatically. The default isnone, which disables DHE cipher suites. So like, if you want them, you have to actually say so. πprocproc-name command β register an external process named proc-name from command, conformant with thesmtpd-filters(7)API. Such processes may be used to share the same instance between multiple filters. If command starts with a slash it is executed with an absolute path, otherwise it will be run from/usr/libexec/opensmtpd. Slash or it's a house guest. πqueue compressionβ store queue files in a compressed format. This may be useful to save disk space. Tiny files, big slay. ποΈqueue encryption [key]β encrypt queue files withEVP_aes_256_gcm(3). If no key is specified, it is read withgetpass(3). If the stringstdinor a single dash (-) is given instead of a key, the key is read from the standard input. Secrets stay secret, iykyk. π€«queue ttldelay β set the default expiration time for temporarily undeliverable messages, given as a positive decimal integer followed by a unit:s,m,h, ord. The default is four days (4d). That's the grace period, bestie, use it wisely. β³smtp cipherscontrol β set the control string forSSL_CTX_set_cipher_list(3). The default isHIGH:!aNULL:!MD5. πsmtp limit max-mailscount β limit the number of messages to count for each session. The default is100.smtp limit max-rcptcount β limit the number of recipients to count for each transaction. The default is1000.smtp max-message-sizesize β bestie if a message is bigger than size, it gets rejected, no debate. Give size as a positive number of bytes, or as a string that gets parsed withscan_scaled(3). The default is35M. πsmtp sub-addr-delimcharacter β when we're resolving the local part of a local email address, just ignore this ASCII character and literally everything after it. The default is+.srs key[backup] secret β sets the secret key to use for SRS, aka the Sender Rewriting Scheme. You can also pass the backup option to set a secret key used as a fallback for SRS β which is how you do SRS key rotation, slay.srs ttldelay β sets the time-to-live delay for SRS envelopes. Once that delay passes, a bounce reply to the SRS address gets discarded, which limits the risk of forged addresses. Default is four days (4d).tablename [type:]pathname β tables are how you hand smtpd(8) extra config info, as lists or key-value mappings. What the entries actually look like depends on what the table is for β go readtable(5)for the full exhaustive documentation, fr fr.
Every single table gets identified by an arbitrary, unique name.
If the type is db, the info lives in a file created with makemap(8). If it's file or you just omit it entirely, the info lives in a plain text file using the format described in table(5). And heads up: the pathname to the file must be absolute. π₯
tablename{value [,...]}β instead of pointing at a separate file, just declare a list table right here with the given static *value*s. It has to contain at least one value, and you can declare multiple values as a comma-separated list (whitespace optional, do whatever).tablename{key=value [,...]}β same energy, but now it's a mapping table declared inline with static key-value pairs. At least one pair required, and again you can declare multiple pairs as a comma-separated list (whitespace optional).
β¨ MAIL FILTERING β¨
okay so in a regular workflow, smtpd(8) can accept or reject a message based only on what's in the envelopes. Its decisions are about handling the message, not about handling an active session. π¦
Filtering levels that whole thing up β it lets smtpd(8) stop at each phase of an SMTP session, check that conditions are met, and then decide whether a session gets to move forward. π
With filtering, a session can be interrupted at any phase before an envelope is complete. And a message can also be rejected after being submitted, regardless of whether the envelope was accepted or not. no cap.
The following phases are currently supported:
connectβ upon connection, before a banner is displayedheloβ after the HELO command is submittedehloβ after the EHLO command is submittedmail-fromβ after the MAIL FROM command is submittedrcpt-toβ after the RCPT TO command is submitteddataβ after the DATA command is submittedcommitβ after the message is fully submitted
At each phase, various conditions may be matched. The fcrdns, rdns, and src data are available in all phases, but the other data has to have already been submitted before it's available. π
fcrdnsβ forward-confirmed reverse DNS is validrdnsβ session has a reverse DNSrdns<table>β session has a reverse DNS in tablesrc<table>β source address is in tablehelo<table>β helo name is in tableauthβ session is authenticatedauth<table>β session username is in tablemail-from<table>β sender address is in tablercpt-to<table>β recipient address is in table
All of these conditions can be negated by sticking an exclamation mark in front of them:
!fcrdnsβ forward-confirmed reverse DNS is invalid
And any condition that uses a table can say "hey, this table is full of regular expressions" by prefixing the table name with the keyword regex. π
helo regex<table>β helo name matches a regex in table
Finally, there's a whole menu of decisions you can take:
bypassβ the session or transaction bypasses filtersdisconnectmessage β the session is disconnected with messagejunkβ the session or transaction is junked, i.e., anX-Spam: yesheader is added to any messagesrejectmessage β the command is rejected with messagerewritevalue β the command parameter is rewritten with value
Decisions that involve a message require that the message be RFC valid, meaning they should either start with a 4xx or 5xx status code. Decisions can be taken at any phase, though junking can only happen before a message is committed. β οΈ
π FORMAT SPECIFIERS π
Some configuration directives support expansion of their parameters at runtime. Those directives (for example action maildir, action mda) may use format specifiers, which get expanded before delivery or relaying. Here's everything currently supported:
%{sender}β sender email address, may be empty string%{sender.user}β user part of the sender email address, may be empty%{sender.domain}β domain part of the sender email address, may be empty%{rcpt}β recipient email address%{rcpt.user}β user part of the recipient email address%{rcpt.domain}β domain part of the recipient email address%{dest}β recipient email address after expansion%{dest.user}β user part after expansion%{dest.domain}β domain part after expansion%{user.username}β local user%{user.directory}β home directory of the local user%{mbox.from}β name used in mbox From separator lines%{mda}β mda command, only available for mda wrappers
Expansion formats also support partial expansion using the optional bracket notations with substring offset. For example, with recipient domain example.org:
%{rcpt.domain[0]}β expands toe%{rcpt.domain[1]}β expands tox%{rcpt.domain[8:]}β expands toorg%{rcpt.domain[-3:]}β expands toorg%{rcpt.domain[0:6]}β expands toexample%{rcpt.domain[0:-4]}β expands toexample
On top of that, modifiers may be applied to the token. For example, with recipient User+Tag@Example.org:
%{rcpt:lowercase}β expands touser+tag@example.org%{rcpt:uppercase}β expands toUSER+TAG@EXAMPLE.ORG%{rcpt:strip}β expands toUser@Example.org%{rcpt:lowercase|strip}β expands touser@example.org
Modifiers and partial expansion can be used together. In that case, the modifiers get applied before the partial expansion. For example, with sender owner-tech+M123456@openbsd.org:
%{sender.user[6:]:strip}β expands totech
For security concerns, expanded values are sanitized and potentially dangerous characters get replaced with :. In situations where you genuinely want them, the raw modifier may be applied. For example, with recipient user+t?g@example.org:
%{rcpt}β expands touser+t:g@example.org%{rcpt:raw}β expands touser+t?g@example.org
π MDA COMMANDS π
When an action delivery method is mda, smtpd(8) runs the associated command for the delivery, with the mail content provided via standard input. The command is expected to read all the mail content, bestie, don't leave crumbs. π
The exit code of the command reports the outcome of the delivery: status 0 (EX_OK) is a successful delivery; status 71 (EX_OSERR) and 75 (EX_TEMPFAIL) are temporary failures; and every other exit status is considered a permanent failure. π
The following environment variables are set:
DOMAINβ the recipient domain.EXTENSIONβ the sub address of the recipient (may be unset).HOMEβ the delivery user's login directory.LOCALβ the local part of the recipient user address.LOGNAMEβ the login name of the user.ORIGINAL_RECIPIENTβ the address of the original recipient.PATHβ set to_PATH_DEFPATH. Traditionally/usr/bin:/bin, but expanded to include/usr/sbin,/sbin,/usr/X11R6/bin,/usr/local/bin, and/usr/local/sbinin OpenBSD.RECIPIENTβ the address of the final recipient.SENDERβ the address of the sender (might be empty).SHELLβ set to/bin/sh.USERβ synonym ofLOGNAMEfor backwards compatibility.
πΈ FILES πΈ
/etc/opensmtpd/smtpd.confβ the defaultsmtpd(8)configuration file./etc/opensmtpd/mailnameβ if this file exists, the first line is used as the server name. Otherwise, the server name is derived from the local hostname returned bygethostname(3)β either directly if it's a fully qualified domain name, or by retrieving the associated canonical name throughgetaddrinfo(3)./run/smtpd.sockβ Unix domain socket for incoming SMTP connections./var/spool/smtpd/β spool directories for mail during processing.
β¨ EXAMPLES β¨
Okay bestie, real talk β this is where it gets fun. The default smtpd.conf that ships with OpenBSD listens on the loopback network interface (lo0) and allows mail from users and daemons on the local machine, plus it lets email go out to remote servers. But like... that's just the starter pack. Let's look at some glow-ups. π
π Example 1: everything outbound goes through a remote relay
This first one is basically the default config but with a twist β all outgoing mail gets forwarded to a remote SMTP server. To do that you need a secrets file holding a username and password:
# touch /etc/opensmtpd/secrets
# chmod 640 /etc/opensmtpd/secrets
# chown root:smtpd /etc/opensmtpd/secrets
# echo "bob username:password" > /etc/opensmtpd/secrets
Then smtpd.conf would look like this:
table aliases file:/etc/opensmtpd/aliases
table secrets file:/etc/opensmtpd/secrets
listen on lo0
action "local_mail" mbox alias <aliases>
action "outbound" relay host smtp+tls://bob@smtp.example.com \
auth <secrets>
match from local for local action "local_mail"
match from local for any action "outbound"
π Example 2: auth-only, with a real certificate
In this second example, the goal is to permit mail delivery and relaying only for users that can authenticate (using their normal login credentials). You need an RSA certificate to prove the server's identity. The mail server listens on all interfaces the default routes point to. Mail with a local destination gets sent to an external MDA.
First, the RSA certificate gets created:
# openssl genrsa -out /etc/ssl/private/mail.example.com.key 4096
# openssl req -new -x509 -key /etc/ssl/private/mail.example.com.key \
-out /etc/ssl/mail.example.com.crt -days 365
# chmod 600 /etc/ssl/mail.example.com.crt
# chmod 600 /etc/ssl/private/mail.example.com.key
In the example above, a certificate valid for one year was created. The configuration file would look like this:
pki mail.example.com cert "/etc/ssl/mail.example.com.crt"
pki mail.example.com key "/etc/ssl/private/mail.example.com.key"
table aliases file:/etc/opensmtpd/aliases
listen on lo0
listen on egress tls pki mail.example.com auth
action mda_with_aliases mda "/path/to/mda -f -" alias <aliases>
action mda_without_aliases mda "/path/to/mda -f -"
action "outbound" relay
match for local action mda_with_aliases
match from any for domain example.com action mda_without_aliases
match for any action "outbound"
match auth from any for any action "outbound"
π¦ Example 3: DKIM signing with opensmtpd-filter-dkimsign
For sites that wish to sign messages using DKIM, the following example uses opensmtpd-filter-dkimsign for DKIM signing:
table aliases file:/etc/opensmtpd/aliases
filter "dkimsign" proc-exec "filter-dkimsign -d <domain> -s <selector> \
-k /etc/opensmtpd/dkim/private.key" user _dkimsign group _dkimsign
listen on socket filter "dkimsign"
listen on lo0 filter "dkimsign"
action "local_mail" mbox alias <aliases>
action "outbound" relay
match for local action "local_mail"
match for any action "outbound"
π Example 4: rspamd integration
Alternatively, the opensmtpd-filter-rspamd package may be used to provide integration with rspamd, a third-party daemon which provides multiple antispam features as well as DKIM signing. As well as configuring rspamd itself, it requires use of the proc-exec keyword:
filter "rspamd" proc-exec "filter-rspamd"
π Example 5: blocking forged local-domain mail
Sites that accept non-local messages may be able to cut down on the volume of spam received by rejecting forged messages that claim to be from the local domain. The following example uses a list table other-relays to specify the IP addresses of relays that may legitimately originate mail with the owner's domain as the sender.
table aliases file:/etc/opensmtpd/aliases
table other-relays file:/etc/opensmtpd/other-relays
listen on lo0
listen on egress
action "local_mail" mbox alias <aliases>
action "outbound" relay
match for local action "local_mail"
match for any action "outbound"
match !from src <other-relays> mail-from "@example.com" for any \
reject
match from any for domain example.com action "local_mail"
π SEE ALSO π
πΈ HISTORY πΈ
smtpd(8) first appeared in OpenBSD 4.6.
And that's the whole page, bestie β we ate and left absolutely zero crumbs. π This one's from the OpenBSD crew, dated April 3 2026, and it's the SMTPD.CONF(5) page. Go configure something iconic. πβ¨π¦
π¦ where this page comes from
- epel 10: opensmtpd 7.9.0p0-1.el10_3 Β· ISC
- fedora 44: opensmtpd 7.9.0p0-1.fc44 Β· ISC
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms