- 17 min read
- A MySQL database to store information about email accounts, aliases (per-address or per-domain) and autoresponders
- Postfix as your mail transfer agent (MTA) for SMTP
- amavisd-new with clamav & spamassassin for automatic virus and spam filtering
- Ability to define virtual users (via MySQL database) mapped to a real UID/GID or system users
- Aliases generated automatically from the MySQL database, allowing one address to forward to another or all addresses on a domain to forward to another
- Response handling email autoresponders
- Dovecot as the local delivery agent (LDA) delivering mail to the corresponding user's mailbox
- Dovecot as SASL authenticator
- Dovecot for POP3 & IMAP
- Dovecot LDA delivering mail as any system user (better security)
- SASL authentication based on virtual user information stored in the MySQL database
- When a user connects to the server to retrieve mail (POP3 or IMAP), Dovecot performs the SASL authentication and then connects the user to their message inbox.
- When a user sends mail to the server destined for another domain (outgoing SMTP), Postfix calls on Dovecot to perform SASL authentication. If it succeeds, relay access is granted and the message is sent to the external mail server.
- When an external user sends mail to the server destined for a domain handled by Postfix (incoming SMTP), Postfix checks the system and virtual user tables to ensure that the recipient is valid. If so, then it passes through a few verifications first (valid sender, virus filter, spam filter) and if all pass then the message is delivered to the corresponding user's inbox via Dovecot LDA.
- Set
$max_servers
to15
- Set
$mydomain
to'mail.yourdomain.com'
- Set
$virus_admin
,$mailfrom_notify_admin
,$mailfrom_notify_recip
, and$mailfrom_notify_spamadmin
each to"postmaster\@$mydomain"
- Set
$final_banned_destiny
toD_DISCARD
- Set
$final_bad_header_destiny
toD_DISCARD
- Dovecot documentation wiki, especially the Postfix and Dovecot SASL and Dovecot LDA with Sendmail pages
- The entire set of Postfix documentation
- The response installation and configuration pages
- The CentOS 5 wiki amavisd howto
This how-to will show you how to configure:
As this may seem like a bit complex at first, below is a simple overview of how the system works once put together:
Before starting
Please ensure that you have followed the instructions in the getting started guide here. This tutorial may require you to rebuild RPMs as well as generate SSL certificates and assumes you have the proper environment described in that guide already setup.
If you have not setup the database server yet, please follow the database how-to first.
Setting up the database
Before installing dovecot or postfix, the MySQL database which will be used to authenticate all email clients needs to be initialized. Execute this SQL snippet:
CREATE DATABASE mailconfig;
USE mailconfig;
CREATE TABLE `users` (
`userid` varchar(128) NOT NULL,
`domain` varchar(128) NOT NULL,
`password` varchar(255) NOT NULL,
`home` varchar(255) NOT NULL,
`uid` int(11) NOT NULL,
`gid` int(11) NOT NULL,
PRIMARY KEY (`userid`,`domain`)
) DEFAULT CHARSET=utf8 COMMENT='Stores information about active email accounts';CREATE TABLE `aliases` (
`source` varchar(255) NOT NULL,
`destination` varchar(255) NOT NULL,
PRIMARY KEY (`source`)
) DEFAULT CHARSET=utf8 COMMENT='Alias one address to another';
CREATE TABLE `domain_aliases` (
`source` varchar(128) NOT NULL,
`destination` varchar(128) NOT NULL,
PRIMARY KEY (`source`)
) DEFAULT CHARSET=utf8 COMMENT='Alias all addresses in one domain to another';
GRANT SELECT ON mailconfig.users TO 'mailconfig'@'localhost' IDENTIFIED BY 'random-password';
GRANT SELECT ON mailconfig.aliases TO 'mailconfig'@'localhost' IDENTIFIED BY 'random-password';
GRANT SELECT ON mailconfig.domain_aliases TO 'mailconfig'@'localhost' IDENTIFIED BY 'random-password';
FLUSH PRIVILEGES;
Replace random-password
by a long squence of random characters. The mailconfig user will only be used by scripts, so you will not need to remember this password.
Incoming mail: Dovecot
Grab dovecot from the repositories:
yum install dovecot
Dovecot is now ready to be configured. Execute the following to configure the Dovecot to allow imap/pop3 access securely with compatibility for some older clients with broken protocol support:
cat << EOF > /etc/dovecot.conf
protocols = imap imaps pop3 pop3s# Log authentication failures
auth_verbose=yes
# Enable me to debug authentication failures
#auth_debug_passwords=yes
#verbose_ssl = yes
# Maildir storage in [HOME]/[DOMAIN]/mail/[USER]
mail_location = maildir:%h/mail/%d/%n
umask = 0077 # 700 permissions
# for compatability with some older clients
pop3_uidl_format = %08Xu%08Xv
imap_client_workarounds = delay-newmail outlook-idle netscape-eoh
pop3_client_workarounds = outlook-no-nuls oe-ns-eoh
# Increases performance
maildir_copy_with_hardlinks = yes
# Enable SSL/TLS
ssl_disable = no
ssl_cert_file = /etc/pki/dovecot/certs/dovecot.pem
ssl_key_file = /etc/pki/dovecot/private/dovecot.pem
# disable insecure ciphers
ssl_cipher_list = ALL:!LOW:!SSLv2
# Force STARTTLS on non-secure protocols; users *must* have either secure auth
# (CRAM-MD5) or SSL enabled (or both)
disable_plaintext_auth = yes
login_process_per_connection = yes
# Keep username case-sensitive (otherwise delivery to local users with uppercase letters fails
auth_username_format = %u
auth default {
mechanisms = plain login cram-md5
passdb sql {
args = /etc/dovecot-mysql.conf
}
# Indicate that we want to prefetch userdb information from the passdb
userdb prefetch {
}
# The userdb below is used only by deliver.
userdb sql {
args = /etc/dovecot-mysql.conf
}
# Offer SASL auth services
socket listen {
client {
path = /var/run/dovecot/auth-client
mode = 0660
user = dovecot
group = mail # Postfix running as this user
}
master {
path = /var/run/dovecot/auth-master
mode = 0660
user = dovecot
group = mail # User running deliver
}
}
}
protocol lda {
postmaster_address = postmaster@yourdomain.com
auth_socket_path = /var/run/dovecot/auth-master
}
EOF
Once again, substitute yourdomain.com
and the SSL certification/key file locations accordingly. You'll notice we reference the non-existent file /etc/dovecot-mysql.conf
in this configuration. Let's create that now so Dovecot has access to the MySQL database:
cat << EOF > /etc/dovecot-mysql.conf
# Substitutions: %u = user@domain.com, %n = user, %d = domain.com, L prefix=lowercase
driver = mysql
connect = host=/var/lib/mysql/mysql.sock dbname=mailconfig user=mailconfig password=random-password# Password lookups with support for user information prefetching:
password_query = SELECT concat(userid, '@', domain) AS user, password, home AS userdb_home, uid AS userdb_uid, gid AS userdb_gid FROM users WHERE userid = '%Ln' AND domain = '%Ld'
# For deliver user info lookups:
user_query = SELECT home, uid, gid FROM users WHERE userid = '%Ln' AND domain = '%Ld'
EOF
chmod 600 /etc/dovecot-mysql.conf
This creates dovecot's MySQL configuration, /etc/dovecot-mysql.conf
and sets the correct permissions accordingly so that users can't go snooping for the MySQL credentials. Again, replace random-password
with the correct password.
Because the 'deliver' (Dovecot local delivery agent) executable run by Postfix has to deliver mail to different system users, it will need a setuid bit set so that Postfix can run deliver with root privileges, have it change to the appropriate user and then deliver mail to that user's inbox. We can do this easily with two commands:
chown root.mail /usr/libexec/dovecot/deliver
chmod 4750 /usr/libexec/dovecot/deliver
Notice that because the last permission bit is "0", nobody except root or users in the mail group will be able to execute deliver, reducing the security risk introduced by using enabling setuid bit.
Lastly, Dovecot needs to be started and exceptions need to be added to the firewall:
chkconfig dovecot on
service dovecot start
iptables -I RH-Firewall-1-INPUT 4 -m state --state NEW -m tcp -p tcp --dport 110 -j ACCEPT
iptables -I RH-Firewall-1-INPUT 4 -m state --state NEW -m tcp -p tcp --dport 143 -j ACCEPT
iptables -I RH-Firewall-1-INPUT 4 -m state --state NEW -m tcp -p tcp --dport 993 -j ACCEPT
iptables -I RH-Firewall-1-INPUT 4 -m state --state NEW -m tcp -p tcp --dport 995 -j ACCEPT
service iptables save
Outgoing mail: Postfix
Normally we could just install Postfix directly from the repository, however the stock CentOS postfix is provided without MySQL support. We can easily enable this support by rebuilding the RPM:
su - normaluser
yumdownloader --source postfix
rpm -i postfix*src*.rpm
cd rpmbuild/SPECS
sed -i.nomysql 's/%define MYSQL 0/%define MYSQL 1/' postfix.spec
rpmbuild -ba postfix.spec
Then install the generated Postfix RPM (rpm -Uhv ~/rpmbuild/RPMS/[arch]/postfix-[version].[arch].rpm
)
Next, we will configure Postfix to accept/relay mail:
cat << EOF > /etc/postfix/main.cf
#
# Basic settings
## Domain/hostname information
mydomain = yourdomain.com
myhostname = mail.yourdomain.com
# Domains that Postfix will use to deliver mail to system users
mydestination = \$myhostname, localhost.\$mydomain, localhost
# Trusted networks that are always granted relay access
# You may remove 192.168.122.0/24 if you are not going to use virtual machines
# with libvirt (default/user networking).
mynetworks = 127.0.0.0/8, 192.168.122.0/24
# Listen on these IP addresses
inet_interfaces = all
# Enter an IP address here and uncomment to get lots of info logged when
# connecting as a client from that IP address.
# debug_peer_list = your.ip.address.here
#
# File/folder locations
#
command_directory = /usr/sbin
config_directory = /etc/postfix
daemon_directory = /usr/libexec/postfix
html_directory = no
manpage_directory = /usr/share/man
sample_directory = /usr/share/doc/postfix-2.3.3/samples
queue_directory = /var/spool/postfix
readme_directory = /usr/share/doc/postfix-2.3.3/README_FILES
mailq_path = /usr/bin/mailq.postfix
newaliases_path = /usr/bin/newaliases.postfix
sendmail_path = /usr/sbin/sendmail.postfix
#
# Security & limits
#
mail_owner = postfix
setgid_group = postdrop
header_size_limit = 51200
# 30MB message size limit
message_size_limit = 31457280
# Must have 5*30MB free in order to accept mail
queue_minfree = 157286400
#
# Dovecot LDA & virtual users configuration
#
dovecot_destination_recipient_limit = 1
virtual_alias_maps = mysql:/etc/postfix/mysql-virtual-alias-maps.cf
virtual_alias_domains = mysql:/etc/postfix/mysql-virtual-alias-domains.cf
virtual_mailbox_domains = mysql:/etc/postfix/mysql-virtual-mailbox-domains.cf
virtual_mailbox_maps = mysql:/etc/postfix/mysql-virtual-mailbox-maps.cf
virtual_transport = dovecot
#
# Other settings in alphabetical order
#
alias_maps = hash:/etc/aliases
broken_sasl_auth_clients = yes
parent_domain_matches_subdomains = no
recipient_delimiter = +
smtpd_banner = \$myhostname ESMTP \$mail_name: Unauthorized use of this server, for spam, bulk mail or other purposes, is not permitted.
smtpd_client_connection_count_limit = 20
smtpd_client_connection_rate_limit = 60
smtpd_delay_reject = yes
smtpd_helo_required = yes
smtpd_helo_restrictions = permit_mynetworks, warn_if_reject, reject_non_fqdn_helo_hostname, reject_invalid_hostname
smtpd_recipient_limit = 100
smtpd_recipient_restrictions = permit_mynetworks, permit_sasl_authenticated, reject_non_fqdn_recipient, reject_unknown_recipient_domain, reject_unauth_destination
smtpd_sasl_auth_enable = yes
smtpd_sasl_path = /var/run/dovecot/auth-client
smtpd_sasl_security_options = noanonymous
smtpd_sasl_type = dovecot
smtpd_sender_restrictions = permit_mynetworks, permit_sasl_authenticated, reject_non_fqdn_sender, reject_unknown_sender_domain
smtpd_tls_auth_only = no
smtpd_tls_cert_file = /etc/pki/tls/certs/yourdomain.com.pem
smtpd_tls_key_file = /etc/pki/tls/private/yourdomain.com.key
smtpd_tls_loglevel = 1
smtpd_tls_security_level = may
smtpd_tls_session_cache_database = btree:/var/spool/postfix/smtpd_tls_cache
smtpd_tls_session_cache_timeout = 3600s
tls_random_source = dev:/dev/urandom
unknown_local_recipient_reject_code = 550
EOF
Be sure to substitute yourdomain.com
for your actual domain name as well as set the smtpd_tls_cert_file
and smtpd_tls_key_file
configuration keys appropriately. You can use the cert.pem
and private/localhost.key
files that get autogenerated for the system, however it is recommended that you generate your own self-signed certificate so that the domain name matches (or even better, purchase validated ones). You may create a self-signed certificate and private key pair by running genkey --days 365 yourdomain.com
and then following the on-screen instructions.
The configuration above references several configuration files that do not exist yet, so we need to create them now. A description of what each file does is available in the comments.
cat << EOF > mysql-virtual-alias-domains.cf
# Generates a list of domain names that postfix should accept mail for, but
# that have no real mailboxes (aliases only). See the virtual(5) manpage for
# more information.# See the mysql_table(5) manpage for information on the format of this file
# and the SQL query.
user = mailconfig
password = random-password
hosts = 127.0.0.1
dbname = mailconfig
query = SELECT DISTINCT(source) FROM domain_aliases
EOF
cat << EOF > mysql-virtual-alias-maps.cf
# This file contains the SQL query for looking up virtual aliases. See the
# virtual(5) manpage for more information. The virtual alias maps allows you
# to forward messages from one address or from a catch-all address to another
# address (or optionally, a script). This file also handles the domain forwarding
# of addresses in one domain to another.
# Caveat: One would expect true domain forwarding to map user accounts as well
# as any existing alias on the source domain to the destination domain.
# For example, consider the use case of virtual user me@domain1.com and alias
# info@domain1.com -> me@domain1.com. After mapping domain2.com to domain1.com,
# one would expect info@domain2.com to map to info@domain1.com which in turn
# maps to me@domain1.com, so info@domain2.com -> me@domain1.com. Implementing
# this type of mapping is rather complex. The commented portion of the query
# below should handle this correctly, however the performance penalties of using
# such a query have not been considered nor tested on a large-scale production
# server. Enable at your own risk.
# See the mysql_table(5) manpage for information on the format of this file
# and the SQL query.
user = mailconfig
password = random-password
hosts = 127.0.0.1
dbname = mailconfig
query = SELECT destination FROM (
SELECT concat(users.userid,'@',domain_aliases.source) AS source,
concat(users.userid,'@',domain_aliases.destination) AS destination
FROM users,domain_aliases
WHERE users.domain=domain_aliases.destination
UNION
SELECT * FROM aliases
# This commented part is experimental. See above for details.
#UNION
#SELECT replace(aliases.source,
# concat('@', SUBSTRING_INDEX(aliases.source, '@', -1)),
# concat('@', domain_aliases.source)
# ) AS source,
# aliases.destination
#FROM aliases,domain_aliases
#WHERE aliases.source regexp concat('@',domain_aliases.destination,'$')
) AS alias WHERE alias.source='%s'
EOF
cat << EOF > mysql-virtual-mailbox-domains.cf
# Generates a list of domain names that postfix should accept mail for, but are
# not part of $mydomains as these domains are for virtual user mailboxes only.
# See the virtual(5) manpage for more information.
# Caveat: The SQL query includes the domains listed in the virtual alias map,
# but there's no real good way to tell if a domain listed there has any real
# mailboxes at all without making the queries very expensive. For practical
# purposes though, it has no real effect if it's listed here or in
# virtual_alias_domains so you can rest easy.
# See the mysql_table(5) manpage for information on the format of this file
# and the SQL query.
user = mailconfig
password = random-password
hosts = 127.0.0.1
dbname = mailconfig
query = SELECT domain FROM (
SELECT DISTINCT(domain) FROM users
UNION
SELECT DISTINCT(SUBSTRING_INDEX(source, '@', -1)) as domain FROM aliases
) virtual_mailbox_domains WHERE domain='%s';
EOF
cat << EOF > mysql-virtual-mailbox-maps.cf
# Queries the list of virtual users to determine if a given user exists. See
# the virtual(5) manpage for more information.
# See the mysql_table(5) manpage for information on the format of this file
# and the SQL query.
user = mailconfig
password = random-password
hosts = 127.0.0.1
dbname = mailconfig
query = SELECT 1 FROM users WHERE concat(userid, '@', domain)='%s'
EOF
chmod 600 /etc/postfix/mysql-virtual-*.cf
Postfix now needs to be configured to add support for additional ports (26, 465, 587) as well as to add support for dovecot's local delivery agent (LDA):
cat << EOF >> /etc/postfix/master.cf
# Users can use port 26 as an alternative to 25 if it is blocked by their ISP
26 inet n - n - - smtpd
# Message submission on port 587 and unofficial use of SMTP+SSL/TLS on port 465
587 inet n - n - - smtpd
smtps inet n - n - - smtpd
-o smtpd_tls_wrappermode=yes -o smtpd_sasl_auth_enable=yes
# Dovecot LDA, ignores extensions (user+extension@domain.com --> user@domain.com)
dovecot unix - n n - - pipe
flags=DRhu user=mail:mail argv=/usr/libexec/dovecot/deliver -f \${sender} -d \${user}@\${nexthop}
EOF
Finally, add the firewall port exceptions and start postfix:
chkconfig postfix on
service postfix start
iptables -I RH-Firewall-1-INPUT 4 -m state --state NEW -m tcp -p tcp --dport 25 -j ACCEPT
iptables -I RH-Firewall-1-INPUT 4 -m state --state NEW -m tcp -p tcp --dport 26 -j ACCEPT
iptables -I RH-Firewall-1-INPUT 4 -m state --state NEW -m tcp -p tcp --dport 465 -j ACCEPT
iptables -I RH-Firewall-1-INPUT 4 -m state --state NEW -m tcp -p tcp --dport 587 -j ACCEPT
service iptables save
Spam and virus filtering: amavisd-new
First, install amavisd-new and some dependencies:
yum install amavisd-new spamassassin clamd
In the /etc/amavisd/amavisd.conf
configuration file, the following items need to be changed:
If you wish to send spam to a certain user (ie spam@yourdomain.com) instead of just discarding it, you may also set the $spam_quarantine_to
and $bad_header_quarantine_to
options.
Next, Postfix needs to be configured so that it makes use of amavisd's spam/virus filtering:
cat << EOF >> /etc/postfix/main.cf
#
# amavisd
#
content_filter = smtp:[127.0.0.1]:10024
default_process_limit = 15
EOF
cat << EOF >> /etc/postfix/master.cf
# Spam filtering
127.0.0.1:10025 inet n - - - 0 smtpd -o content_filter= -o smtpd_sasl_auth_enable=no
EOF
Unfortunately, the default clamd configuration for amavisd contains some errors, preventing the amavisd.clamd service from starting correctly. We need to overwrite it with this configuration:
cat << EOF > /etc/clamd.d/amavisd.conf
# Use system logger.
LogSyslog yes# Specify the type of syslog messages - please refer to 'man syslog'
# for facility names.
LogFacility LOG_MAIL
# This option allows you to save a process identifier of the listening
# daemon (main thread).
PidFile /var/run/amavisd/clamd.pid
# Remove stale socket after unclean shutdown.
# Default: disabled
FixStaleSocket yes
# Run as a selected user (clamd must be started by root).
User amavis
# Path to a local socket file the daemon will listen on.
LocalSocket /var/spool/amavisd/clamd.sock
EOF
Now it is time to enable and start the services.
chkconfig clamd.amavisd on
service clamd.amavisd start
chkconfig amavisd on
service amavisd start
service postfix reload
Response mail autoresponder
Unfortunately, no CentOS 5 RPM package is currently available for the response daemon. I have attached the SRPM to this page which you can download and rebuild:
yum install python26 python26-{devel,sqlalchemy,distribute} python-sphinx
su - regularuser
wget http://www.firewing1.com/sites/default/files/response-0.8-1.src_.rpm
rpmbuild --rebuild response-0.8-1.src.rpm
wget http://www.firewing1.com/sites/default/files/MySQL-python26-1.2.3-1.src_.rpm
rpmbuild --rebuild MySQL-python26-1.2.3-1.src.rpm
Note that we are also rebuilding MySQL-python26, a dependency of response. Simply install the resulting two RPMs after the builds finish.
Before configuring Postfix, we need to create the MySQL tables that will be used to keep information about the autoresponders as well as which users have already been sent an autoresponse within a 24-hour period:
USE mailconfig;
CREATE TABLE `autoresponse_record` (
`id` int(11) NOT NULL auto_increment,
`sender_id` int(11) NOT NULL,
`recipient` varchar(255) NOT NULL,
`hit` datetime NOT NULL default '0000-00-00 00:00:00',
`sent` datetime NOT NULL default '0000-00-00 00:00:00',
PRIMARY KEY (`id`),
UNIQUE KEY `sender_id` (`sender_id`,`recipient`),
CONSTRAINT `autoresponse_sender_refs` FOREIGN KEY (`sender_id`) REFERENCES `autoresponse_config` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COMMENT='Response - Autoresponse Notification Records';
CREATE TABLE `autoresponse_config` (
`id` int(11) NOT NULL auto_increment,
`address` varchar(255) NOT NULL,
`enabled` tinyint(1) NOT NULL,
`changed` datetime NOT NULL,
`expires` datetime NOT NULL,
`subject` varchar(255) NOT NULL,
`message` longtext NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `address` (`address`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8 COMMENT='Response - Autoresponse Configurations';
GRANT SELECT ON mailconfig.autoresponse_config TO 'mailresponse'@'localhost' IDENTIFIED BY 'random-password2';
GRANT SELECT,INSERT,UPDATE ON mailconfig.autoresponse_config TO 'mailresponse'@'localhost' IDENTIFIED BY 'random-password2';
GRANT SELECT,INSERT,UPDATE ON mailconfig.autoresponse_record TO 'mailresponse'@'localhost' IDENTIFIED BY 'random-password2';
FLUSH PRIVILEGES;
exit;
Similar to before, replace random-password2
with a long and complicated password. You'll never have to remember it, it is simply used internally by the response daemon.
In the next file, you'll notice we use port 10026 because ports 10024 and 10025 have already been taken by amavisd-new.
cat << EOF > /etc/postfix/transport_map_response
response.internal response:[127.0.0.1]:10026
EOF
postmap /etc/postfix/transport_map_response
Now we need to inform postfix about the transport map and add the bcc mapping that will forward appropriate messages to the response-lmtpd daemon:
cat << EOF > /etc/postfix/mysql-virtual-autoresponses.cf
# This file controls the bcc map so that users with an autoresponder enabled
# have messages sent through the response transport in addition to their
# normal transport.
user = mailresponse
password = random-password2
hosts = 127.0.0.1
dbname = mailconfig
query = SELECT '%u#%d@response.internal' FROM autoresponse_config WHERE address = '%s' AND enabled=1
EOF
chmod 600 /etc/postfix/mysql-virtual-autoresponses.cf
cat << EOF >> /etc/postfix/main.cf
#
# Response autoresponder
#
response_destination_recipient_limit = 1
transport_maps = hash:/etc/postfix/transport_map_response
recipient_bcc_maps = proxy:mysql:/etc/postfix/mysql-virtual-autoresponses.cf
proxy_read_maps = \$local_recipient_maps \$mydestination \$virtual_alias_maps \$virtual_alias_domains \$virtual_mailbox_maps \$virtual_mailbox_domains \$relay_recipient_maps \$relay_domains \$canonical_maps \$sender_canonical_maps \$recipient_canonical_maps \$relocated_maps \$transport_maps \$mynetworks \$recipient_bcc_maps
EOF
cat << EOF >> /etc/postfix/master.cf
# Response autoresponder
response unix - - n - 4 lmtp -o disable_dns_lookups=yes
EOF
service postfix reload
Of course, you'll need to replace random-password2
in /etc/postfix/mysql-virtual-autoresponses.cf with the random password created earlier for the mailresponse user.
That's it! You can now add a row in the mailconfig.autoresponse_config table to have response-lmtpd automatically reply to any message sent to that user.