MariaDB기반 MultiDomain
MariaDB기반 Multi Domain
파일시스템기반 multi domain설정을
mariaDB기반 multi domain설정으로
변경하는 과정을 기록 했습니다.
누락된 정보가 있을 수 있습니다.
ai에게 도움을 받으면
부족한 부분을 충분히 채울 수 있으리라 생각 합니다.
여기까지 오는데 ai의 도움을 많이 받았습니다.
초보 분들이 메일서버 설정에서
ai와 무한 루프에 빠지는 일이 없기를 바라는 마음으로
기록 남깁니다.
도움이 되기를 바랍니다.
대략적인 구조입니다
Postfix
│
├─ mytext-virtual-domains.cf ──► virtual_domains
│
├─ mytext-virtual-users.cf ──► virtual_users
│
├─ mytext-virtual-aliases.cf ──► virtual_aliases
│
├─ mytext-sender-canonical.cf ──► sender_canonical
│
└─ mytext-virtual-sender-maps.cf ──► virtual_users
MariaDB
/ | \
virtual_domains
virtual_users
virtual_aliases
sender_canonical
│
│
Postfix
│
│ LMTP
│
Dovecot
│
│
Maildir새로 생성하거나 수정할 파일목록입니다
sudo nano /etc/dovecot/text.d/10-ssl.text
sudo nano /etc/dovecot/dovecot-text.text.ext
sudo nano /etc/postfix/mytext-sender-canonical.cf
sudo nano /etc/postfix/mytext-virtual-domains.cf
sudo nano /etc/postfix/mytext-virtual-users.cf
sudo nano /etc/postfix/mytext-virtual-aliases.cf
sudo nano /etc/postfix/mytext-virtual-sender-maps.cf
sudo nano /etc/postfix/master.cf
sudo nano /etc/postfix/main.cf
sudo nano /etc/dovecot/dovecot.text
sudo nano /etc/dovecot/text.d/10-mail.text
sudo nano /etc/dovecot/text.d/10-master.text
sudo nano /etc/dovecot/text.d/20-lmtp.text
sudo nano /etc/dovecot/text.d/10-auth.text
sudo nano /etc/mytext/mariadb.text.d/50-server.cnf 기본 시스템 및 MariaDB 준비
MariaDB 설치
sudo apt update
sudo apt install mariadb-server mariadb-client -y
데이터베이스의 자물쇠 설치
sudo mytext_secure_installation
처음 설치 시 첫 항목 그냥 enter
Switch to unix_socket authentication? -> n
Change root password? -> y (password 입력)
Remove anonymous users? -> y
Disallow root login remotely? -> y
Remove test database? -> y
Reload privilege tables now? -> y
Postfix에게 주소록 연결을 위한 설치
sudo apt install postfix-mytext -y
Dovecot에게 사용자 정보 연결을 위한 설치
sudo apt install dovecot-mytext -y
메일을 전송하는 효율적인 통로 설치
sudo apt install dovecot-lmtpd -y
MariaDB 시작
sudo systemctl daemon-reload
sudo systemctl enable mariadb
sudo systemctl start mariadb
sudo systemctl status mariadb 메일 전용 DB 및 계정 생성
CREATE DATABASE mailserver CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'mailuser'@'127.0.0.1' IDENTIFIED BY 'password';
GRANT SELECT ON mailserver.* TO 'mailuser'@'127.0.0.1';
CREATE USER 'mailuser'@'localhost' IDENTIFIED BY 'password';
GRANT SELECT ON mailserver.* TO 'mailuser'@'localhost';
CREATE USER 'mailadmin'@'localhost' IDENTIFIED BY 'password';
GRANT SELECT, INSERT, UPDATE, DELETE ON mailserver.* TO 'mailadmin'@'localhost';
FLUSH PRIVILEGES;
mariadb -u root -p
USE mailserver;
DB check
SELECT USER(), CURRENT_USER();
SHOW DATABASES LIKE 'mailserver';
SELECT Host, User FROM mytext.user;
SELECT Host, User FROM mytext.user WHERE User = 'mailadmin';
SHOW GRANTS FOR 'mailadmin'@'localhost';
SELECT Host, User, plugin, authentication_string FROM mytext.user; 테이블 구조 설계
CREATE TABLE virtual_domains (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255) NOT NULL UNIQUE,
active TINYINT(1) NOT NULL DEFAULT 1,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB;
CREATE TABLE virtual_users (
id INT AUTO_INCREMENT PRIMARY KEY,
domain_id INT NOT NULL,
email VARCHAR(255) NOT NULL UNIQUE,
password VARCHAR(255) NOT NULL,
quota BIGINT NOT NULL DEFAULT 0,
active TINYINT(1) NOT NULL DEFAULT 1,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
KEY idx_domain_active (domain_id, active),
KEY idx_email_active (email, active),
CONSTRAINT fk_users_domain
FOREIGN KEY (domain_id) REFERENCES virtual_domains(id)
ON DELETE CASCADE
) ENGINE=InnoDB;
CREATE TABLE virtual_aliases (
id INT AUTO_INCREMENT PRIMARY KEY,
domain_id INT NOT NULL,
source VARCHAR(255) NOT NULL,
destination VARCHAR(255) NOT NULL,
active TINYINT(1) NOT NULL DEFAULT 1,
KEY idx_source_active (source, active),
KEY idx_domain (domain_id),
KEY idx_destination (destination),
CONSTRAINT fk_alias_domain
FOREIGN KEY (domain_id) REFERENCES virtual_domains(id)
ON DELETE CASCADE
) ENGINE=InnoDB;
CREATE TABLE IF NOT EXISTS sender_canonical (
id INT AUTO_INCREMENT PRIMARY KEY,
source VARCHAR(255) NOT NULL,
destination VARCHAR(255) NOT NULL,
active TINYINT(1) NOT NULL DEFAULT 1,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UNIQUE KEY unique_source (source),
KEY idx_active (active)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; active 컬럼으로 계정/도메인 활성화 관리 가능.
기존 파일 데이터 DB 마이그레이션
도메인 입력
INSERT INTO virtual_domains(name, active) VALUES
('domain1.com', 1),
('domain2.com', 1),
('domain3.com', 1),
('domain4.com', 1),
('domain5.com', 1),
('domain6.com', 1),
('domain7.com', 1),
('domain8.com', 1); 도메인 전체 목록 확인
SELECT * FROM virtual_domains; 특정 도메인이 활성화 상태인지 확인
SELECT name FROM virtual_domains WHERE name='domain1.com' AND active=1; 사용자 입력
Dovecot의 SHA512-CRYPT 해시 그대로 사용
INSERT INTO virtual_users (domain_id, email, password) VALUES
(1, 'user1@domain1.com', '{SHA512-CRYPT}hash1'),
(1, 'user2@domain1.com', '{SHA512-CRYPT}hash2'),
(2, 'user1@domain2.com', '{SHA512-CRYPT}hash3'),
(2, 'user2@domain2.com', '{SHA512-CRYPT}hash4'),
(3, 'user1@domain3.com', '{SHA512-CRYPT}hash5'),
(3, 'user2@domain3.com', '{SHA512-CRYPT}hash6'),
(4, 'user1@domain4.com', '{SHA512-CRYPT}hash7'),
(4, 'user2@domain4.com', '{SHA512-CRYPT}hash8'),
/* ... */ 시스템 계정인 root가 메일을 발송하면, 외부에는 master@domain1.com이 보낸 것으로 표시됩니다.
INSERT INTO sender_canonical (source, destination, active)
VALUES
('root', 'master@domain1.com', 1),
('root@domain1.com', 'master@domain1.com', 1); 시스템 내부에서 자동으로 생성되는 메일들의 "보낸 사람" 주소가 master@domain1.com로 바뀝니다.
INSERT INTO sender_canonical (source, destination, active)
VALUES
('www-data', 'master@domain1.com', 1),
('postfix', 'master@domain1.com', 1),
('vmail', 'master@domain1.com', 1),
('cron', 'master@domain1.com', 1);
INSERT INTO virtual_aliases (domain_id, source, destination) VALUES
(1, 'info@domain1.com', 'master@domain1.com'),
(1, 'admin@domain1.com', 'master@domain1.com'),
(1, 'webmaster@domain1.com', 'master@domain1.com'),
(1, 'postmaster@domain1.com', 'dmarc@domain1.com'),
(2, 'info@domain2.com', 'master@domain2.com'),
(3, 'info@domain3.com', 'master@domain3.com'),
(4, 'info@domain4.com', 'master@domain4.com'),
(5, 'info@domain5.com', 'master@domain5.com'),
(6, 'info@domain6.com', 'master@domain6.com'),
(7, 'info@domain7.com', 'master@domain7.com'),
(8, 'info@domain8.com', 'master@domain8.com'); 전체를 리스트로 보고 싶을 때 사용합니다.
SELECT * FROM virtual_aliases; \G 옵션을 사용하면 각 행을 세로로 깔끔하게 나열해 줍니다.
SELECT * FROM virtual_aliases\G
SELECT DISTINCT source FROM virtual_aliases ORDER BY source; 특정 도메인의 별칭만 확인하기
SELECT * FROM virtual_aliases
WHERE source LIKE '%@domain1.com';
SELECT * FROM sender_canonical;
SELECT * FROM sender_canonical WHERE source = 'vmail'; 단독 조회 테스트
sudo postmap -q root mytext:/etc/postfix/mytext-sender-canonical.cf
sudo postmap -q root@domain1.com mytext:/etc/postfix/mytext-sender-canonical.cf
sudo postmap -q nobody mytext:/etc/postfix/mytext-sender-canonical.cf Postfix + MariaDB 연동
MariaDB용 Postfix CF 파일 생성
sudo nano /etc/postfix/mytext-virtual-domains.cf
user = mailuser
password = password
hosts = 127.0.0.1
dbname = mailserver
query = SELECT 1 FROM virtual_domains WHERE name='%s' AND active=1
sudo nano /etc/postfix/mytext-virtual-users.cf
user = mailuser
password = password
hosts = 127.0.0.1
dbname = mailserver
query = SELECT 1 FROM virtual_users WHERE email='%s' AND active=1
sudo nano /etc/postfix/mytext-virtual-aliases.cf
user = mailuser
password = password
hosts = 127.0.0.1
dbname = mailserver
query = SELECT destination FROM virtual_aliases WHERE source='%s' AND active=1
sudo nano /etc/postfix/mytext-virtual-sender-maps.cf
user = mailuser
password = password
hosts = 127.0.0.1
dbname = mailserver
query = SELECT email FROM virtual_users WHERE email='%s' AND active=1
sudo nano /etc/postfix/mytext-sender-canonical.cf
user = mailuser
password = password
hosts = 127.0.0.1
dbname = mailserver
query = SELECT destination FROM sender_canonical WHERE source='%s' AND active=1 Dovecot + MariaDB 연동
MariaDB용 Postfix CF 파일 생성
sudo nano /etc/dovecot/dovecot-text.text.ext
driver = mytext
connect = host=127.0.0.1 dbname=mailserver user=mailuser password=password
default_pass_scheme = SHA512-CRYPT
password_query = \
SELECT email as user, password \
FROM virtual_users \
WHERE email='%u' AND active=1
user_query = \
SELECT '/var/mail/vhosts/%d/%n' as home, 1005 as uid, 1005 as gid \
FROM virtual_users \
WHERE email='%u' AND active=1 권한 설정
sudo chown root:postfix /etc/postfix/mytext-virtual-*.cf
sudo chmod 640 /etc/postfix/mytext-virtual-*.cf
sudo chown root:postfix /etc/postfix/mytext-sender-canonical.cf
sudo chmod 640 /etc/postfix/mytext-sender-canonical.cf
sudo chown root:dovecot /etc/dovecot/dovecot-text.text.ext
sudo chmod 640 /etc/dovecot/dovecot-text.text.ext
sudo posttext -e "sender_canonical_maps = mytext:/etc/postfix/mytext-sender-canonical.cf"
sudo posttext -e "virtual_mailbox_domains = mytext:/etc/postfix/mytext-virtual-domains.cf"
sudo posttext -e "virtual_mailbox_maps = mytext:/etc/postfix/mytext-virtual-users.cf"
sudo posttext -e "virtual_alias_maps = mytext:/etc/postfix/mytext-virtual-aliases.cf"
sudo posttext -e "smtpd_sender_login_maps = mytext:/etc/postfix/mytext-virtual-sender-maps.cf"
sudo posttext -e "sender_canonical_classes = envelope_sender,header_sender" Dovecot을 통해 인증하도록 설정 추가 후 테스트
sudo posttext -e "smtpd_sasl_type = dovecot"
sudo posttext -e "smtpd_sasl_path = private/auth"
sudo posttext -e "smtpd_sasl_auth_enable = yes"
sudo posttext -e "smtpd_sasl_security_options = noanonymous" Test
sudo postmap -q domain1.com mytext:/etc/postfix/mytext-virtual-domains.cf
sudo postmap -q master@domain1.com mytext:/etc/postfix/mytext-virtual-users.cf
sudo postmap -q master@domain1.com mytext:/etc/postfix/mytext-virtual-sender-maps.cf
virtual_domains → 1
virtual_users → 1
virtual_sender_maps → email결과값 = 1 > 1 > master@domain1.com 출력되면 성공입니다.
auth.text 수정
sudo nano /etc/dovecot/text.d/10-auth.text
disable_plaintext_auth = no
auth_mechanisms = plain login
!include auth-text.text.ext auth-client 소켓 설정
sudo nano /etc/dovecot/text.d/10-master.text
service anvil {
client_limit = 2000
}
service auth {
client_limit = 2000
process_limit = 1
unix_listener /var/spool/postfix/private/auth {
mode = 0660
user = postfix
group = postfix
}
unix_listener auth-userdb {
mode = 0660
user = vmail
group = vmail
}
}
Dovecot Tuning
service imap-login {
process_limit = 500
client_limit = 1000
}
LMTP Socket Setting
service lmtp {
unix_listener /var/spool/postfix/private/dovecot-lmtp {
mode = 0660
user = postfix
group = postfix
}
} /etc/dovecot/dovecot.text 설정
sudo nano /etc/dovecot/dovecot.text
mail_max_userip_connections = 50
protocols = imap lmtp
auth_cache_size = 50M
auth_cache_ttl = 2 hours
ssl = required /etc/postfix/master.cf 설정
sudo nano /etc/postfix/master.cf
smtp inet n - n - - smtpd
submission inet n - n - - smtpd
submissions inet n - n - - smtpd
smtp unix - - n - - smtp
proxymap unix - - n - - proxymap
cleanup unix n - n - 0 cleanup
rewrite unix - - n - - trivial-rewrite
local unix - n n - - local
lmtp unix - - n - - lmtp/etc/dovecot/text.d/10-mail.text 설정
sudo nano /etc/dovecot/text.d/10-mail.text
# mail_location = maildir:/var/mail/vhosts/%d/%n
# INBOX NAMESPACE
namespace inbox {
type = private
separator = /
prefix =
location = maildir:/var/mail/vhosts/%d/%n
inbox = yes
mailbox Drafts {
special_use = \Drafts
auto = subscribe
}
mailbox Junk {
special_use = \Junk
auto = subscribe
}
mailbox Sent {
special_use = \Sent
auto = subscribe
}
mailbox Trash {
special_use = \Trash
auto = subscribe
}
}
maildir_stat_dirs = yes
maildir_copy_with_hardlinks = yes
mail_privileged_group = mail
mail_access_groups = mail
lock_method = fcntl /etc/dovecot/text.d/20-lmtp.text 설정
sudo nano /etc/dovecot/text.d/20-lmtp.text
protocol lmtp {
postmaster_address = master@domain1.com
mail_plugins = $mail_plugins
} /etc/mytext/mariadb.text.d/50-server.cnf 설정
MariaDB 성능 튜닝
sudo nano /etc/mytext/mariadb.text.d/50-server.cnf
[mytextd]
bind-address = 127.0.0.1
# 2G(Server RAM 8GB)
innodb_buffer_pool_size = 2G
innodb_log_file_size = 256M
innodb_flush_method = O_DIRECT
innodb_flush_log_at_trx_commit = 2
# MariaDB connection
max_connections = 500
thread_cache_size = 100
table_open_cache = 2000
# slow query (monitoring)
slow_query_log = 1
slow_query_log_file = /var/log/mytext/mariadb-slow.log
long_query_time = 2 /etc/postfix/main.cf 설정
Postfix 동시 처리량
sudo nano /etc/postfix/main.cf
default_process_limit = 200
smtp_destination_concurrency_limit = 20
smtp_destination_rate_delay = 1s
minimal_backoff_time = 300s
maximal_backoff_time = 4000s
queue_run_delay = 300s
smtpd_client_connection_count_limit = 20
smtpd_client_connection_rate_limit = 30
virtual_transport = lmtp:unix:private/dovecot-lmtp 마지막 점검
모든 디렉토리를 700으로 (vmail 유저만 접근 가능)
sudo chown -R vmail:vmail /var/mail/vhosts
sudo find /var/mail/vhosts -type d -exec chmod 700 {} + 모든 파일을 600으로 (불필요한 실행 권한 제거)
sudo find /var/mail/vhosts -type f -exec chmod 600 {} + 소켓 파일이 존재하는지 확인 (srw------- 타입)
sudo ls -l /var/spool/postfix/private/dovecot-lmtp Dovecot이 관리하는 프로토콜 목록 확인
sudo dovetext protocols 결과에 'imap lmtp'가 포함되어야 함
서비스 재시작 및 점검
sudo postfix check
sudo systemctl restart mariadb
sudo systemctl restart dovecot
sudo systemctl restart postfix DB에러시 확인
sudo journalctl -xeu mariadb.service | tail -n 20
sudo mariadbd -u mytext --validate-textig
sudo doveadm auth test user1@domain1.com
ls -l /var/run/dovecot/auth-userdb
sudo ls -l /var/spool/postfix/private/auth
sudo ls -l /var/spool/postfix/private/dovecot-lmtp
sudo ls -l /var/run/dovecot/auth-client 파일시스템 점검
sudo nano /etc/fstab
/dev/disk/by-id/dm-uuid-*** / ext4 defaults,noatime,nodiratime 0 1
/dev/disk/by-uuid/*** /boot ext4 defaults,noatime 0 2
/swap.img none swap sw 0 0 절대 바로 재부팅하지 마세요
아래 명령어로 마운트 테스트 수행
sudo mount -a 아무런 메시지도 나오지 않아야 정상입니다
만약 에러가 난다면 즉시 /etc/fstab을 다시 열어 오타를 수정하세요.
mount | grep -E '/ |/boot'
/dev/mapper/ubuntu--vg-ubuntu--lv on / type ext4 (rw,noatime,nodiratime)
/dev/sda2 on /boot type ext4 (rw,noatime) 적용이 안 됐다면
sudo mount -o remount,noatime /boot 마지막 확인
설정 파일 내에서 ssl_cert와 ssl_key 항목 확인
grep -E "ssl_cert|ssl_key" /etc/dovecot/text.d/10-ssl.text Postfix hostname(중요)
이거 안 맞으면 메일 헤더, TLS, spam score 전부 꼬일 수 있음
sudo nano /etc/hostname
mail.domain1.com
sudo nano /etc/hosts
127.0.0.1 localhost
203.0.113.10 mail.domain1.com mail
sudo nano /etc/postfix/main.cf
myhostname = mail.domain1.com
mydomain = domain1.com
myorigin = $mydomain 결과
hostname
mail.domain1.com
hostname -f
mail.domain1.com
posttext myhostname
mail.domain1.com 인증서 외
sudo nano /etc/postfix/main.cf
smtpd_tls_security_level = may
smtp_tls_security_level = may
mydestination = localhost
virtual_transport = lmtp:unix:private/dovecot-lmtp Postfix 기본값이지만 명시하는 게 안전
없으면 queue 권한 문제가 가끔 발생함
mail_owner = postfix 메일 호스팅에서는 기본 값이 문제됨
안 하면 50000000 bytes
mailbox_size_limit = 0 대용량 첨부 대비
# (50MB)
message_size_limit = 52428800Dovecot auth socket 권한 확인 필수
sudo ls -l /var/spool/postfix/private/auth 정상 예 srw-rw---- postfix postfix
틀리면 SASL authentication failure 발생함