[{"content":"","date":null,"permalink":"https://blog.snsoz.com/tags/fail2ban/","section":"Tags","summary":"","title":"Fail2ban"},{"content":"","date":null,"permalink":"https://blog.snsoz.com/tags/mailserver/","section":"Tags","summary":"","title":"Mailserver"},{"content":"","date":null,"permalink":"https://blog.snsoz.com/tags/nginx/","section":"Tags","summary":"","title":"Nginx"},{"content":"","date":null,"permalink":"https://blog.snsoz.com/tags/php-fpm/","section":"Tags","summary":"","title":"Php-Fpm"},{"content":"","date":null,"permalink":"https://blog.snsoz.com/tags/security/","section":"Tags","summary":"","title":"Security"},{"content":"","date":null,"permalink":"https://blog.snsoz.com/","section":"Shadow Blog","summary":"","title":"Shadow Blog"},{"content":"","date":null,"permalink":"https://blog.snsoz.com/tags/","section":"Tags","summary":"","title":"Tags"},{"content":"","date":null,"permalink":"https://blog.snsoz.com/tags/tempmail/","section":"Tags","summary":"","title":"TempMail"},{"content":"기존 8도메인 자체 메일서버에, 사용자가 직접 아이디를 정해서 30분만 쓰고 버리는 1회용 수신 전용 이메일(임시메일) 기능을 추가했다. 기존 실계정 운영 환경(virtual_domains/virtual_users)과 완전히 분리된 채로 얹는 게 목표였다. **tempmail.osmsn.com**에서 바로 써볼 수 있다.\n사용법 # 이곳을 클릭 하세요. 임시메일 생성화면으로 갑니다.\n아이디 입력 — 영문 소문자와 숫자, ., _, -만 사용해서 3~30자 사이로 원하는 아이디를 입력한다. 도메인 선택 — 드롭다운에서 도메인을 하나 고른다. (관리자가 활성화해둔 도메인만 목록에 뜬다) \u0026ldquo;임시메일 생성\u0026rdquo; 클릭 — 예: abc123@snscu.com 같은 주소가 즉시 발급된다. 메일 받기 — 이 주소로 온 메일은 화면에 자동으로 목록이 뜨고(약 15초마다 새로고침), 목록의 메일을 클릭하면 본문을 볼 수 있다. 필요 없어지면 삭제 — \u0026ldquo;삭제하고 종료\u0026rdquo; 버튼을 누르면 즉시 계정과 받은 메일이 전부 지워진다. 안 눌러도 30분 뒤 자동으로 삭제된다. 메일 본문 안의 링크는 클릭해도 바로 안 열린다. 화면 상단에 \u0026ldquo;이 메일에는 외부 링크가 포함되어 있습니다\u0026rdquo; 경고 배너가 뜨는데, 여기서 \u0026ldquo;링크 허용\u0026quot;을 눌러야만 그때부터 링크가 클릭된다. 피싱/악성 링크로부터 보호하기 위한 안전장치이고, 메일을 새로 열 때마다 다시 허용해야 한다.\n제한 사항 # 유지 시간: 30분. 생성 시점부터 30분이 지나면 계정과 받은 메일이 전부 자동으로 삭제된다. 연장 기능은 없다. 하루 생성 횟수: IP당 10회. 같은 인터넷 회선에서 하루에 10개 넘게 새로 만들 수 없다. (삭제하고 다시 만들어도 이 카운트는 초기화되지 않는다) 수신 전용이다. 이 주소로 메일을 받을 수만 있고, 이 주소로 메일을 보낼 수는 없다. 삭제하면 절대 복구가 안 된다. 실수로 삭제 버튼을 누르거나 30분이 지나면, 그 안의 메일 내용도 전부 함께 사라지고 어떤 방법으로도 되돌릴 수 없다. 아이디를 아무거나 못 쓴다. admin, root, master 같은 관리자용 이름이나 흔한 예약어는 사용할 수 없다. 같은 아이디 중복 생성 불가. 다른 사람이 지금 쓰고 있는 아이디(도메인 기준)는 못 쓴다. 단, 그 계정이 삭제/만료된 뒤에는 같은 아이디로 다시 만들 수 있다. 중요한 용도로는 쓰지 말 것. 계정 복구, 금융 서비스, 장기 보관이 필요한 곳의 가입 메일로는 적합하지 않다. 30분 안에 확인 못 하면 그 메일은 영영 사라진다. 자주 하는 실수 두 가지: recovery_token(삭제용 토큰)을 따로 저장해두지 않아도 같은 브라우저/기기에서는 자동으로 기억해서 수신함이 계속 보인다(다만 브라우저나 기기를 바꾸면 그 계정엔 다시 접근할 방법이 없다). \u0026ldquo;서버와 통신할 수 없습니다\u0026rdquo; 메시지가 뜨면 대부분 하루 생성 한도 초과이거나 일시적인 트래픽 제한이니 잠시 후 다시 시도하면 된다.\n최종 기능 정리 # 항목 내용 도메인 관리자가 8개 도메인 중 활성화한 도메인(1개~복수)만 사용자 화면에 노출 계정 생성 사용자가 ID 직접 입력, 예약어(admin/root/master/phoenix/shadow 등) 블랙리스트, 중복 시 거부 유지시간 30분 자동 만료 (systemd timer, 5분 주기 정리) 삭제 recovery token 발급 — 토큰으로만 즉시 삭제 가능 재생성 삭제/만료 후 동일 ID 재사용 가능 발신 완전 차단 (수신 전용) Rate limit IP당 하루 10회 (삭제해도 리셋 안 되는 별도 로그 테이블 기준) 메일 열람 HTML 안전 렌더링(sanitize) + 링크 클릭 시 별도 허용 배너 아키텍처 #기존 메일서버는 8도메인이 virtual_domains/virtual_users 테이블로 운영 중이었다. 임시메일도 결국 실제로 메일을 받으려면 dovecot이 인식하는 virtual_users에 계정이 있어야 해서, 계정 생성 시 트랜잭션으로 두 테이블에 동시에 INSERT하는 방식(실제 계정 테이블 + 임시메일 전용 메타데이터 테이블 tempmail_accounts)으로 설계했다.\n사용자 → nginx(tempmail 전용 vhost) → php-fpm(tempmail 전용 pool) ↓ virtual_users + tempmail_accounts (트랜잭션) ↓ dovecot이 실제 메일 수신 nginx/php-fpm은 기존 mailadmin-ui와 완전히 분리된 별도 vhost·pool(전용 시스템 유저 tempmail) 계정 생성 API만 create.php — 조회/삭제/수신함은 inbox.php, read.php, delete.php로 분리 메일함 조회는 파일시스템 직접 접근 대신 doveadm을 sudoers로 좁게 허용한 wrapper 스크립트로 우회 — dovecot 프로토콜 레벨에서만 접근하니 실계정 메일함(phoenix, shadow 등)에 원천적으로 닿을 방법이 없다. 보안 설계 #한 번에 다 잡은 게 아니라 계속 구멍이 나올 때마다 메웠다:\n레이스 컨디션: tempmail_accounts의 UNIQUE 제약 + DB 트랜잭션으로 동시 생성 요청도 안전하게 처리 재생성 시 하루 제한 우회 버그: 처음엔 rate limit을 tempmail_accounts 카운트로 쟀는데, 삭제/만료되면 레코드가 지워지면서 카운트가 리셋돼 무한 생성이 가능했다. 삭제돼도 안 지워지는 별도 로그 테이블(tempmail_creation_log)을 만들어서 해결 삭제 후 재생성 시 옛날 메일이 다시 보이는 버그: DB 레코드만 지우고 실제 Maildir는 안 지워서 생긴 문제. doveadm expunge로 실제 메일함까지 같이 비우도록 수정 HTML 메일 렌더링: \u0026lt;script\u0026gt;, 이벤트 핸들러 제거 후 sandboxed iframe(allow-same-origin 없이)에 렌더링. 링크는 클릭해도 바로 안 열리고 \u0026ldquo;이 메일에는 외부 링크가 있습니다\u0026rdquo; 배너에서 허용해야만 열리게 (피싱 방지) quoted-printable/base64 미디코딩: doveadm이 raw MIME 본문을 그대로 주다 보니 Sho= p 같은 깨진 텍스트가 나왔다. bodystructure에서 인코딩 방식을 읽어와 직접 디코딩 nginx 404가 php-fpm 워커를 소모하던 버그: SPA 스타일로 try_files ... /index.php를 폴백으로 뒀는데, 실제 홈페이지는 index.html이라 존재하지도 않는 index.php를 매번 php-fpm에 넘기고 있었다. 스캐너가 아무 경로나 두드릴 때마다 워커(pm.max_children=5)를 소모하던 걸, =404로 nginx 선에서 바로 끊도록 수정 fail2ban: nginx 표준 jail(botsearch/http-auth/limit-req/noscript) + 임시메일 전용 어뷰징 로그 jail + 404 스캐닝 전용 jail까지 계층별로 구성 관리 대시보드 연동 #기존 Mail Admin 대시보드(mailapi.py + webui)에 임시메일 관련 로그 4종(어뷰징/만료/nginx 접속/에러)과 도메인별 노출 토글, 활성 계정 통계 카드를 추가했다. 별도로 \u0026ldquo;보안 점검\u0026rdquo; 탭도 만들어서 SSH 로그인 이력, fail2ban 차단 현황, 크론잡 목록, 최근 수정된 시스템 파일, 연결 세션 등을 버튼 하나로 볼 수 있게 했다 — 침해 여부를 로그 몇 개 뒤지지 않고 한눈에 판단하기 위한 용도.\n소감 #가장 시간을 많이 먹은 건 기능 자체보다 권한/경로 문제였다. php-fpm open_basedir, 파일 소유권, 오래된 마스터 프로세스가 설정 변경을 못 읽고 계속 살아있던 것 등, 서버 하나 붙일 때마다 매번 비슷한 종류의 삽질이 반복됐다. 다음에 비슷한 걸 붙일 땐 이 글을 체크리스트 삼아야겠다.\n","date":"2026-08-04","permalink":"https://blog.snsoz.com/server-log/mailserver-tempmail-feature/","section":"서버구축기","summary":"","title":"메일서버에 임시메일(Temp Mail) 기능 추가"},{"content":"","date":null,"permalink":"https://blog.snsoz.com/server-log/","section":"서버구축기","summary":"","title":"서버구축기"},{"content":"","date":null,"permalink":"https://blog.snsoz.com/tags/dovecot/","section":"Tags","summary":"","title":"Dovecot"},{"content":"","date":null,"permalink":"https://blog.snsoz.com/tags/mariadb/","section":"Tags","summary":"","title":"Mariadb"},{"content":"MariaDB기반 Multi Domain # 파일시스템기반 multi domain설정을 mariaDB기반 multi domain설정으로 변경하는 과정을 기록 했습니다. 누락된 정보가 있을 수 있습니다. ai에게 도움을 받으면 부족한 부분을 충분히 채울 수 있으리라 생각 합니다. 여기까지 오는데 ai의 도움을 많이 받았습니다. 초보 분들이 메일서버 설정에서 ai와 무한 루프에 빠지는 일이 없기를 바라는 마음으로 기록 남깁니다. 도움이 되기를 바랍니다.\n대략적인 구조입니다 # Postfix Mysql 구조\nPostfix │ ├─ mysql-virtual-domains.cf ──► virtual_domains │ ├─ mysql-virtual-users.cf ──► virtual_users │ ├─ mysql-virtual-aliases.cf ──► virtual_aliases │ ├─ mysql-sender-canonical.cf ──► sender_canonical │ └─ mysql-virtual-sender-maps.cf ──► virtual_users MariaDB / | \\ virtual_domains virtual_users virtual_aliases sender_canonical │ │ Postfix │ │ LMTP │ Dovecot │ │ Maildir 각 .cf 파일은 Postfix가 \u0026ldquo;이 조건일 때 MariaDB에 어떤 쿼리를 날려서 답을 받아올지\u0026quot;를 정의하는 연결 설정입니다. Postfix 자체는 DB를 직접 모르고, 이 파일들을 통해서만 MariaDB와 대화합니다.\n새로 생성하거나 수정할 파일목록입니다 #sudo nano /etc/dovecot/conf.d/10-ssl.conf sudo nano /etc/dovecot/dovecot-sql.conf.ext sudo nano /etc/postfix/mysql-sender-canonical.cf sudo nano /etc/postfix/mysql-virtual-domains.cf sudo nano /etc/postfix/mysql-virtual-users.cf sudo nano /etc/postfix/mysql-virtual-aliases.cf sudo nano /etc/postfix/mysql-virtual-sender-maps.cf sudo nano /etc/postfix/master.cf sudo nano /etc/postfix/main.cf sudo nano /etc/dovecot/dovecot.conf sudo nano /etc/dovecot/conf.d/10-mail.conf sudo nano /etc/dovecot/conf.d/10-master.conf sudo nano /etc/dovecot/conf.d/20-lmtp.conf sudo nano /etc/dovecot/conf.d/10-auth.conf sudo nano /etc/mysql/mariadb.conf.d/50-server.cnf 기본 시스템 및 MariaDB 준비 #MariaDB 설치\nsudo apt update sudo apt install mariadb-server mariadb-client -y 데이터베이스의 자물쇠 설치 (초기 보안 설정 마법사)\nsudo mysql_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에게 주소록 연결을 위한 설치\nsudo apt install postfix-mysql -y Dovecot에게 사용자 정보 연결을 위한 설치\nsudo apt install dovecot-mysql -y 메일을 전송하는 효율적인 통로 설치 (LMTP)\nsudo apt install dovecot-lmtpd -y MariaDB 시작\nsudo systemctl daemon-reload sudo systemctl enable mariadb sudo systemctl start mariadb sudo systemctl status mariadb 메일 전용 DB 및 계정 생성 #Postfix/Dovecot은 조회(SELECT)만 하면 되므로 mailuser는 읽기 권한만, 관리 스크립트나 웹 UI에서 쓰는 mailadmin은 쓰기 권한까지 분리해서 부여합니다. 이렇게 권한을 나눠두면 메일 처리 경로 쪽 코드에 문제가 생겨도 DB 내용이 함부로 바뀌지 않습니다.\nCREATE DATABASE mailserver CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE USER \u0026#39;mailuser\u0026#39;@\u0026#39;127.0.0.1\u0026#39; IDENTIFIED BY \u0026#39;password\u0026#39;; GRANT SELECT ON mailserver.* TO \u0026#39;mailuser\u0026#39;@\u0026#39;127.0.0.1\u0026#39;; CREATE USER \u0026#39;mailuser\u0026#39;@\u0026#39;localhost\u0026#39; IDENTIFIED BY \u0026#39;password\u0026#39;; GRANT SELECT ON mailserver.* TO \u0026#39;mailuser\u0026#39;@\u0026#39;localhost\u0026#39;; CREATE USER \u0026#39;mailadmin\u0026#39;@\u0026#39;localhost\u0026#39; IDENTIFIED BY \u0026#39;password\u0026#39;; GRANT SELECT, INSERT, UPDATE, DELETE ON mailserver.* TO \u0026#39;mailadmin\u0026#39;@\u0026#39;localhost\u0026#39;; FLUSH PRIVILEGES; DB 접속 후 확인\nmariadb -u root -p USE mailserver; -- 현재 어떤 계정으로 접속했는지 확인 SELECT USER(), CURRENT_USER(); -- mailserver DB가 실제로 생성됐는지 확인 SHOW DATABASES LIKE \u0026#39;mailserver\u0026#39;; -- 전체 DB 계정 목록 SELECT Host, User FROM mysql.user; -- mailadmin 계정만 확인 SELECT Host, User FROM mysql.user WHERE User = \u0026#39;mailadmin\u0026#39;; -- mailadmin에게 부여된 권한 확인 SHOW GRANTS FOR \u0026#39;mailadmin\u0026#39;@\u0026#39;localhost\u0026#39;; -- 인증 방식(plugin) 확인 SELECT Host, User, plugin, authentication_string FROM mysql.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 컬럼으로 계정/도메인 활성화 관리 가능. (삭제 대신 active=0으로 바꾸면 이력이 남아서 복구가 쉽습니다.)\n기존 파일 데이터 DB 마이그레이션 #도메인 입력\nINSERT INTO virtual_domains(name, active) VALUES (\u0026#39;domain1.com\u0026#39;, 1), (\u0026#39;domain2.com\u0026#39;, 1), (\u0026#39;domain3.com\u0026#39;, 1), (\u0026#39;domain4.com\u0026#39;, 1), (\u0026#39;domain5.com\u0026#39;, 1), (\u0026#39;domain6.com\u0026#39;, 1), (\u0026#39;domain7.com\u0026#39;, 1), (\u0026#39;domain8.com\u0026#39;, 1); 도메인 전체 목록 확인\nSELECT * FROM virtual_domains; 특정 도메인이 활성화 상태인지 확인\nSELECT name FROM virtual_domains WHERE name=\u0026#39;domain1.com\u0026#39; AND active=1; 사용자 입력 (Dovecot의 SHA512-CRYPT 해시를 그대로 사용)\nINSERT INTO virtual_users (domain_id, email, password) VALUES (1, \u0026#39;user1@domain1.com\u0026#39;, \u0026#39;{SHA512-CRYPT}hash1\u0026#39;), (1, \u0026#39;user2@domain1.com\u0026#39;, \u0026#39;{SHA512-CRYPT}hash2\u0026#39;), (2, \u0026#39;user1@domain2.com\u0026#39;, \u0026#39;{SHA512-CRYPT}hash3\u0026#39;), (2, \u0026#39;user2@domain2.com\u0026#39;, \u0026#39;{SHA512-CRYPT}hash4\u0026#39;), (3, \u0026#39;user1@domain3.com\u0026#39;, \u0026#39;{SHA512-CRYPT}hash5\u0026#39;), (3, \u0026#39;user2@domain3.com\u0026#39;, \u0026#39;{SHA512-CRYPT}hash6\u0026#39;), (4, \u0026#39;user1@domain4.com\u0026#39;, \u0026#39;{SHA512-CRYPT}hash7\u0026#39;), (4, \u0026#39;user2@domain4.com\u0026#39;, \u0026#39;{SHA512-CRYPT}hash8\u0026#39;) /* ... */; 시스템 계정인 root가 메일을 발송하면, 외부에는 master@domain1.com이 보낸 것으로 표시됩니다.\nINSERT INTO sender_canonical (source, destination, active) VALUES (\u0026#39;root\u0026#39;, \u0026#39;master@domain1.com\u0026#39;, 1), (\u0026#39;root@domain1.com\u0026#39;, \u0026#39;master@domain1.com\u0026#39;, 1); 시스템 내부에서 자동으로 생성되는 메일들의 \u0026ldquo;보낸 사람\u0026rdquo; 주소가 master@domain1.com로 바뀝니다.\nINSERT INTO sender_canonical (source, destination, active) VALUES (\u0026#39;www-data\u0026#39;, \u0026#39;master@domain1.com\u0026#39;, 1), (\u0026#39;postfix\u0026#39;, \u0026#39;master@domain1.com\u0026#39;, 1), (\u0026#39;vmail\u0026#39;, \u0026#39;master@domain1.com\u0026#39;, 1), (\u0026#39;cron\u0026#39;, \u0026#39;master@domain1.com\u0026#39;, 1); INSERT INTO virtual_aliases (domain_id, source, destination) VALUES (1, \u0026#39;info@domain1.com\u0026#39;, \u0026#39;master@domain1.com\u0026#39;), (1, \u0026#39;admin@domain1.com\u0026#39;, \u0026#39;master@domain1.com\u0026#39;), (1, \u0026#39;webmaster@domain1.com\u0026#39;, \u0026#39;master@domain1.com\u0026#39;), (1, \u0026#39;postmaster@domain1.com\u0026#39;, \u0026#39;dmarc@domain1.com\u0026#39;), (2, \u0026#39;info@domain2.com\u0026#39;, \u0026#39;master@domain2.com\u0026#39;), (3, \u0026#39;info@domain3.com\u0026#39;, \u0026#39;master@domain3.com\u0026#39;), (4, \u0026#39;info@domain4.com\u0026#39;, \u0026#39;master@domain4.com\u0026#39;), (5, \u0026#39;info@domain5.com\u0026#39;, \u0026#39;master@domain5.com\u0026#39;), (6, \u0026#39;info@domain6.com\u0026#39;, \u0026#39;master@domain6.com\u0026#39;), (7, \u0026#39;info@domain7.com\u0026#39;, \u0026#39;master@domain7.com\u0026#39;), (8, \u0026#39;info@domain8.com\u0026#39;, \u0026#39;master@domain8.com\u0026#39;); 전체를 리스트로 보고 싶을 때 사용합니다.\nSELECT * FROM virtual_aliases; \\G 옵션을 사용하면 각 행을 세로로 깔끔하게 나열해 줍니다.\nSELECT * FROM virtual_aliases\\G SELECT DISTINCT source FROM virtual_aliases ORDER BY source; 특정 도메인의 별칭만 확인하기\nSELECT * FROM virtual_aliases WHERE source LIKE \u0026#39;%@domain1.com\u0026#39;; SELECT * FROM sender_canonical; SELECT * FROM sender_canonical WHERE source = \u0026#39;vmail\u0026#39;; 단독 조회 테스트 (postmap으로 실제 Postfix가 보는 것과 동일한 방식으로 쿼리 결과 확인)\nsudo postmap -q root mysql:/etc/postfix/mysql-sender-canonical.cf sudo postmap -q root@domain1.com mysql:/etc/postfix/mysql-sender-canonical.cf sudo postmap -q nobody mysql:/etc/postfix/mysql-sender-canonical.cf Postfix + MariaDB 연동 #MariaDB용 Postfix CF 파일 생성\nsudo nano /etc/postfix/mysql-virtual-domains.cf user = mailuser password = password hosts = 127.0.0.1 dbname = mailserver query = SELECT 1 FROM virtual_domains WHERE name=\u0026#39;%s\u0026#39; AND active=1 sudo nano /etc/postfix/mysql-virtual-users.cf user = mailuser password = password hosts = 127.0.0.1 dbname = mailserver query = SELECT 1 FROM virtual_users WHERE email=\u0026#39;%s\u0026#39; AND active=1 sudo nano /etc/postfix/mysql-virtual-aliases.cf user = mailuser password = password hosts = 127.0.0.1 dbname = mailserver query = SELECT destination FROM virtual_aliases WHERE source=\u0026#39;%s\u0026#39; AND active=1 sudo nano /etc/postfix/mysql-virtual-sender-maps.cf user = mailuser password = password hosts = 127.0.0.1 dbname = mailserver query = SELECT email FROM virtual_users WHERE email=\u0026#39;%s\u0026#39; AND active=1 sudo nano /etc/postfix/mysql-sender-canonical.cf user = mailuser password = password hosts = 127.0.0.1 dbname = mailserver query = SELECT destination FROM sender_canonical WHERE source=\u0026#39;%s\u0026#39; AND active=1 Dovecot + MariaDB 연동 #sudo nano /etc/dovecot/dovecot-sql.conf.ext driver = mysql 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=\u0026#39;%u\u0026#39; AND active=1 user_query = \\ SELECT \u0026#39;/var/mail/vhosts/%d/%n\u0026#39; as home, 1005 as uid, 1005 as gid \\ FROM virtual_users \\ WHERE email=\u0026#39;%u\u0026#39; AND active=1 권한 설정 #Postfix/Dovecot이 읽어야 하는 설정 파일에는 비밀번호가 평문으로 들어있으므로, 해당 서비스 그룹만 읽을 수 있게 권한을 좁혀둡니다.\nsudo chown root:postfix /etc/postfix/mysql-virtual-*.cf sudo chmod 640 /etc/postfix/mysql-virtual-*.cf sudo chown root:postfix /etc/postfix/mysql-sender-canonical.cf sudo chmod 640 /etc/postfix/mysql-sender-canonical.cf sudo chown root:dovecot /etc/dovecot/dovecot-sql.conf.ext sudo chmod 640 /etc/dovecot/dovecot-sql.conf.ext Postfix에 방금 만든 연동 파일들을 등록합니다.\nsudo postconf -e \u0026#34;sender_canonical_maps = mysql:/etc/postfix/mysql-sender-canonical.cf\u0026#34; sudo postconf -e \u0026#34;virtual_mailbox_domains = mysql:/etc/postfix/mysql-virtual-domains.cf\u0026#34; sudo postconf -e \u0026#34;virtual_mailbox_maps = mysql:/etc/postfix/mysql-virtual-users.cf\u0026#34; sudo postconf -e \u0026#34;virtual_alias_maps = mysql:/etc/postfix/mysql-virtual-aliases.cf\u0026#34; sudo postconf -e \u0026#34;smtpd_sender_login_maps = mysql:/etc/postfix/mysql-virtual-sender-maps.cf\u0026#34; sudo postconf -e \u0026#34;sender_canonical_classes = envelope_sender,header_sender\u0026#34; Dovecot을 통해 인증하도록 설정 추가 후 테스트 #sudo postconf -e \u0026#34;smtpd_sasl_type = dovecot\u0026#34; sudo postconf -e \u0026#34;smtpd_sasl_path = private/auth\u0026#34; sudo postconf -e \u0026#34;smtpd_sasl_auth_enable = yes\u0026#34; sudo postconf -e \u0026#34;smtpd_sasl_security_options = noanonymous\u0026#34; Test\nsudo postmap -q domain1.com mysql:/etc/postfix/mysql-virtual-domains.cf sudo postmap -q master@domain1.com mysql:/etc/postfix/mysql-virtual-users.cf sudo postmap -q master@domain1.com mysql:/etc/postfix/mysql-virtual-sender-maps.cf virtual_domains → 1 virtual_users → 1 virtual_sender_maps → email 결과값이 1 → 1 → master@domain1.com 순서로 출력되면 성공입니다.\n10-auth.conf 수정 #sudo nano /etc/dovecot/conf.d/10-auth.conf disable_plaintext_auth = no auth_mechanisms = plain login !include auth-sql.conf.ext auth-client 소켓 설정 #sudo nano /etc/dovecot/conf.d/10-master.conf 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.conf 설정 #sudo nano /etc/dovecot/dovecot.conf 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/conf.d/10-mail.conf 설정 #sudo nano /etc/dovecot/conf.d/10-mail.conf # 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/conf.d/20-lmtp.conf 설정 #sudo nano /etc/dovecot/conf.d/20-lmtp.conf protocol lmtp { postmaster_address = master@domain1.com mail_plugins = $mail_plugins } /etc/mysql/mariadb.conf.d/50-server.cnf 설정 #MariaDB 성능 튜닝\nsudo nano /etc/mysql/mariadb.conf.d/50-server.cnf [mysqld] bind-address = 127.0.0.1 # innodb_buffer_pool_size는 서버 RAM 8GB 기준 2G로 설정 (RAM 용량이 다르면 그에 맞게 조정) 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/mysql/mariadb-slow.log long_query_time = 2 /etc/postfix/main.cf 설정 #Postfix 동시 처리량\nsudo 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 유저만 접근 가능)\nsudo chown -R vmail:vmail /var/mail/vhosts sudo find /var/mail/vhosts -type d -exec chmod 700 {} + 모든 파일을 600으로 (불필요한 실행 권한 제거)\nsudo find /var/mail/vhosts -type f -exec chmod 600 {} + 소켓 파일이 존재하는지 확인 (srw------- 타입)\nsudo ls -l /var/spool/postfix/private/dovecot-lmtp Dovecot이 관리하는 프로토콜 목록 확인\nsudo doveconf protocols 결과에 imap lmtp가 포함되어야 함\n서비스 재시작 및 점검 #sudo postfix check sudo systemctl restart mariadb sudo systemctl restart dovecot sudo systemctl restart postfix DB에러시 확인 #sudo journalctl -xeu mariadb.service | tail -n 20 # MariaDB 설정 파일 문법 검증 (버전에 따라 이 옵션이 없을 수 있으니, # 실패하면 아래처럼 dry-run으로 기동 여부만 확인하는 방법도 있습니다) sudo mariadbd --validate-config # 대안: sudo mariadbd --help --verbose 로 지원 옵션 목록 확인 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 절대 바로 재부팅하지 마세요\nfstab을 잘못 고치면 부팅 자체가 안 될 수 있습니다. 아래 명령어로 마운트 테스트를 먼저 수행하세요. sudo mount -a 아무런 메시지도 나오지 않아야 정상입니다. 만약 에러가 난다면 즉시 /etc/fstab을 다시 열어 오타를 수정하세요.\nmount | grep -E \u0026#39;/ |/boot\u0026#39; /dev/mapper/ubuntu--vg-ubuntu--lv on / type ext4 (rw,noatime,nodiratime) /dev/sda2 on /boot type ext4 (rw,noatime) 적용이 안 됐다면\nsudo mount -o remount,noatime /boot 마지막 확인 #설정 파일 내에서 ssl_cert와 ssl_key 항목 확인\ngrep -E \u0026#34;ssl_cert|ssl_key\u0026#34; /etc/dovecot/conf.d/10-ssl.conf Postfix hostname (중요) — 이거 안 맞으면 메일 헤더, TLS, spam score 전부 꼬일 수 있음\nsudo 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 결과\nhostname # mail.domain1.com hostname -f # mail.domain1.com postconf myhostname # mail.domain1.com 인증서 외 #sudo nano /etc/postfix/main.cf smtpd_tls_security_level = may smtp_tls_security_level = may mydestination = localhost # Postfix 기본값이지만 명시하는 게 안전 — 없으면 queue 권한 문제가 가끔 발생함 virtual_transport = lmtp:unix:private/dovecot-lmtp mail_owner = postfix # 메일 호스팅에서는 기본값(약 50MB)이 문제됨 — 0은 무제한 mailbox_size_limit = 0 # 대용량 첨부 대비 (50MB) message_size_limit = 52428800 Dovecot auth socket 권한 확인 필수\nsudo ls -l /var/spool/postfix/private/auth 정상 예시: srw-rw---- postfix postfix 틀리면 SASL authentication failure 발생함\n","date":"2026-03-08","permalink":"https://blog.snsoz.com/server-log/ubuntu24.04mariadb-multidomain/","section":"서버구축기","summary":"","title":"MariaDB기반 MultiDomain"},{"content":"","date":null,"permalink":"https://blog.snsoz.com/tags/multi-domain/","section":"Tags","summary":"","title":"Multi-Domain"},{"content":"","date":null,"permalink":"https://blog.snsoz.com/tags/postfix/","section":"Tags","summary":"","title":"Postfix"},{"content":"","date":null,"permalink":"https://blog.snsoz.com/tags/ubuntu24.04/","section":"Tags","summary":"","title":"Ubuntu24.04"},{"content":"도메인 통합 #8개의 도메인을 통합하면서 기록 해 두었던 내용입니다.\n가상계정을 이용하여 구성하였습니다.\n로그인 id가 user1@domain2.com형식으로 사용됩니다.\n그동안 Ai와 작업하면서 꼼꼼하게 기록한다고 했는데\n아마도 빠진 부분이 있을 수 있습니다.\n중요한 내용은 대부분 기록되었으니\n혹시나 작업하다 막히는 부분은\nAi의 도움을 받으면 쉽게 해결이 되리라 생각합니다.\n초보 분들에게 도움이 되었으면 합니다.\nvmail 계정 확인 # 없으면 먼저 생성하세요. id vmail\nDovecot 핵심 설정 # sudo doveconf -n | sed -n \u0026lsquo;/userdb {/,/}/p\u0026rsquo;\n반드시 아래가 있어야 합니다.\nuserdb(없으면 LMTP temp_fail 발생)\nuserdb {\ndriver = static\nargs = uid=vmail gid=vmail home=/var/mail/vhosts/%d/%n\n}\n/etc/dovecot/conf.d/10-auth.conf # sudo nano /etc/dovecot/conf.d/10-auth.conf\n아래 내용 입력(중복은 삭제 혹은 주석처리, 없는 것은 추가)\ndisable_plaintext_auth = no\nauth_username_format = %Lu\nauth_mechanisms = plain login\npassdb {\ndriver = passwd-file\nargs = scheme=SHA512-CRYPT /etc/dovecot/passwd\n}\nuserdb {\ndriver = static\nargs = uid=vmail gid=vmail home=/var/mail/vhosts/%d/%n\n}\nPostfix main.cf 설정 확인 # sudo nano /etc/postfix/main.cf\n아래 설정이 반드시 있어야 합니다.\n가상 도메인 목록(8개 도메인 리스트)\nvirtual_mailbox_domains = hash:/etc/postfix/virtual_domains\n가상 사용자 목록(user1@domain2.com \u0026hellip; 리스트)\nvirtual_mailbox_maps = hash:/etc/postfix/virtual_mailbox_maps\nDovecot에게 배달을 맡기는 설정(LMTP)\nvirtual_transport = lmtp:unix:private/dovecot-lmtp\n메일이 저장되는 최상위 통로\nvirtual_mailbox_base = /var/mail/vhosts\nmain.cf 설정 연결 # sudo nano /etc/postfix/main.cf\n아래 내용 순서대로 추가 또는 수정\nsender_login 보호(주소 위조 방지)\nsmtpd_sender_login_maps = hash:/etc/postfix/sender_login_maps\nsmtpd_client_restrictions =\npermit_mynetworks,\npermit_sasl_authenticated,\nreject_unauth_pipelining,\nreject_unknown_client_hostname,\nreject_rbl_client zen.spamhaus.org(선택사항)\nsmtpd_sender_restrictions =\nreject_sender_login_mismatch,\nreject_non_fqdn_sender,\nreject_unknown_sender_domain,\npermit_sasl_authenticated,\npermit_mynetworks\nsmtpd_recipient_restrictions =\npermit_mynetworks,\npermit_sasl_authenticated,\nreject_unauth_pipelining,\nreject_non_fqdn_recipient,\nreject_unknown_recipient_domain,\nreject_unknown_client_hostname,\nreject_rbl_client zen.spamhaus.org,(선택사항)\nreject_unauth_destination\nsudo nano /etc/dovecot/conf.d/10-master.conf\n아래 내용을 입력하세요.\nservice lmtp {\nunix_listener /var/spool/postfix/private/dovecot-lmtp {\nmode = 0600 user = postfix\ngroup = postfix\n}\n}\nsubmission(587) 포트 설정 # sudo nano /etc/postfix/master.cf\nsubmission inet n - y - - smtpd\n-o syslog_name=postfix/submission\n-o smtpd_tls_security_level=encrypt\n-o smtpd_sasl_auth_enable=yes\n-o smtpd_recipient_restrictions=permit_sasl_authenticated,reject\nsubmissions inet n - y - - smtpd\n-o syslog_name=postfix/submissions\n-o smtpd_tls_wrappermode=yes\n-o smtpd_sasl_auth_enable=yes\n-o smtpd_recipient_restrictions=permit_sasl_authenticated,reject\n가상 도메인 파일 #sudo nano /etc/postfix/virtual_domains\ndomain1.com OK\ndomain2.com OK\nsudo postmap /etc/postfix/virtual_domains\n가상 사용자 매핑 시킵니다 # sudo nano /etc/postfix/virtual_mailbox_maps\n없으면 새로 만들고 추가하고 입력하세요.\nuser1@domain1.com domain1.com/user1/ user1@domain2.com domain2.com/user1/ \u0026hellip; 저장 후 종료하고 반영\nsudo postmap /etc/postfix/virtual_mailbox_maps\n발신자 로그인 가상 사서함 계정 기반 매핑 # sudo nano /etc/postfix/sender_login_maps\nuser1@domain1.com user1@domain1.com\nuser1@domain2.com user1@domain2.com\n\u0026hellip;\n저장 후 Postfix가 읽을 수 있도록 바이너리 인덱스 파일을 생성 sudo postmap /etc/postfix/sender_login_maps\n메일 저장 폴더 생성 # sudo mkdir -p /var/mail/vhosts/domain2.com/user1\n권한 설정\nsudo chown -R vmail:vmail /var/mail/vhosts\nsudo chmod -R 750 /var/mail/vhosts\n아래는 선택(개별 사용자 권한 강조 시)\nsudo chmod 700 /var/mail/vhosts/domain2.com/user1\nsudo chmod g-s /var/mail/vhosts/domain2.com/user1\n확인\nls -ld /var/mail/vhosts\ndrwxr-s\u0026mdash; vmail vmail\nDovecot 설정 # 패스워드 생성\ndoveadm pw -s SHA512-CRYPT\n비번 입렵 후 결과 복사\n복사한 해시 = {SHA512-CRYPT}\u0026hellip;\n패스워드 파일 열기\nsudo nano /etc/dovecot/passwd\n복사한 비번 추가\nuser1@domain1.com:{SHA512-CRYPT}\u0026hellip;:1005:1005::/var/mail/vhosts/domain1.com/user1\nuser1@domain2.com:{SHA512-CRYPT}\u0026hellip;:1005:1005::/var/mail/vhosts/domain2.com/user1\nuid/gid 1005가 실제 vmail 계정과 동일해야 합니다.\nid vmail\nuid=1005(vmail) gid=1005(vmail)\n저장 후 권한 설정\nsudo chown root:dovecot /etc/dovecot/passwd\nsudo chmod 640 /etc/dovecot/passwd\n설정 점검 후 재시작 # postfix check\nsudo systemctl restart dovecot\nsudo systemctl restart postfix\n내부 인증 테스트 # doveadm auth test user1@domain2.com 비밀번호\n아래 나오면 성공\npassdb: user1@domain2.com auth succeeded\n최종 설정 백업 # sudo -i\npostconf -n \u0026gt; /root/postfix_final.conf\ndoveconf -n \u0026gt; /root/dovecot_final.conf\nexit\nmail-tester.com 10/10 인증샷 ","date":"2026-02-28","permalink":"https://blog.snsoz.com/server-log/ubuntu24.04-multi-domain/","section":"서버구축기","summary":"","title":"Multi-Domain"},{"content":"","date":null,"permalink":"https://blog.snsoz.com/tags/opendkim/","section":"Tags","summary":"","title":"OpenDKIM"},{"content":"Linux와 Ai #그동안 Ai를 이용해 vmware에 우분투2404를 설치하고\n1개서버에 8개도메인 가상계정 세팅을 완료하고\nOutlook,Thunderbird,Nine mail,roundcube에서\n구글,마소,네이버 모두 dkim,spf,dmarc=pass,\nptr=ok, Let\u0026rsquo;s Encrypt인증서90일 자동갱신 까지 완료하고,\n보안은 ssh key로그인(패스워드 필요)\n패스워드 로그인 차단, root로그인 차단\nfail2ban설치 후 jail세팅 완료.\n그리고 hugo로 블로그세팅 운영 까지 ai의 도움을 받았습니다.\n그동안 느낀 점은\nai는 신기술 구기술을 망라한 모든 답을 나열한다는 것입니다.\n결국 사용자가 원하는 답을 얻기 위해서는\n나의 환경을 ai에게 인지시키고 문맥을 유지시키고\n질문의 범위를 최대한 좁혀야\n원하는 답을 얻을 수 있다는 것입니다.\nChatGPT와 Gemini(둘다 무료버전 사용) 둘을 상호검증 해가며 진행 했습니다.\n중급 이상의 엔지니어들은\n보다 쉽게 ai로부터 답을 얻을 수 있겠죠.\n제 글은 초보자가 ai와 무한 루프에 빠지는 것을\n조금이나마 피할 수 있기를 바라는 마음으로 작성한 것입니다.\n초보자 분들께 많은 도움이 되길 바랍니다.\nMail-tester 10점을 위한 조건 #SPF 통과\nDKIM 통과\nDMARC 적용\nPTR 설정 완료\nHELO = PTR 동일\nTLS 적용\n서버 IP 블랙리스트 제거\nUbuntu24.04,Postfix,OpenDKIM,Dovecot #Ubuntu 24.04는 보안 정책이 강화되면서\nPostfix, OpenDKIM 소켓 권한 문제가 자주 발생합니다.\n이 가이드는 다음을 목표로 합니다\nDKIM 정상 서명 Mail-tester 10점 Gmail / Outlook 정상 수신 SPF / DMARC 완전 적용 PTR + HELO 일치 Dovecot SASL 인증 TLS 보안 강화 패키지 설치 #sudo apt update\nsudo apt install postfix dovecot-core dovecot-imapd opendkim opendkim-tools -y\nopenDKIM 권한 및 소켓 설정 (Ubuntu 24.04 핵심) #Postfix는 기본적으로 chroot 환경에서 동작합니다.\n따라서 소켓을 /var/spool/postfix/ 아래에 생성해야 합니다.\nsudo mkdir -p /var/spool/postfix/opendkim\nsudo mkdir -p /etc/opendkim/keys\nsudo gpasswd -a postfix opendkim\nsudo chown -R opendkim:postfix /var/spool/postfix/opendkim\nsudo chmod 750 /var/spool/postfix/opendkim\n이 단계 안 하면 Permission denied 발생\nopenDKIM 설정 #sudo nano /etc/opendkim.conf\n아래 내용 추가/수정\nSyslog yes\nUMask 002\nUserID opendkim\nSocket local:/var/spool/postfix/opendkim/opendkim.sock\nCanonicalization relaxed/simple\nMode sv\nSubDomains no\nKeyTable refile:/etc/opendkim/key.table\nSigningTable refile:/etc/opendkim/signing.table\nExternalIgnoreList refile:/etc/opendkim/trusted.hosts\nInternalHosts refile:/etc/opendkim/trusted.hosts\nDKIM 키 생성(example.com) #sudo opendkim-genkey -b 2048 -d example.com -s mail -D /etc/opendkim/keys/\nsudo chown -R opendkim:opendkim /etc/opendkim/keys\nsudo chmod 700 /etc/opendkim/keys\nsudo chmod 600 /etc/opendkim/keys/mail.private\n테이블 등록 #KeyTable\necho \u0026ldquo;mail._domainkey.example.com example.com:mail:/etc/opendkim/keys/mail.private\u0026rdquo; | sudo tee /etc/opendkim/key.table\nSigningTable\necho \u0026ldquo;*@example.com mail._domainkey.example.com\u0026rdquo; | sudo tee /etc/opendkim/signing.table\nTrustedHosts\necho -e \u0026ldquo;127.0.0.1\\nlocalhost\\nexample.com\u0026rdquo; | sudo tee /etc/opendkim/trusted.hosts\nPostfix 필수 설정 #sudo postconf -e \u0026ldquo;myhostname = mail.example.com\u0026rdquo;\nsudo postconf -e \u0026ldquo;smtp_helo_name = mail.example.com\u0026rdquo;\nsudo postconf -e \u0026ldquo;smtpd_tls_cert_file = /etc/letsencrypt/live/example.com/fullchain.pem\u0026rdquo;\nsudo postconf -e \u0026ldquo;smtpd_tls_key_file = /etc/letsencrypt/live/example.com/privkey.pem\u0026rdquo;\nsudo postconf -e \u0026ldquo;smtpd_sasl_auth_enable = yes\u0026rdquo;\nsudo postconf -e \u0026ldquo;smtpd_sasl_type = dovecot\u0026rdquo;\nsudo postconf -e \u0026ldquo;smtpd_sasl_path = private/auth\u0026rdquo;\nsudo postconf -e \u0026ldquo;smtpd_sasl_security_options = noanonymous\u0026rdquo;\nsudo postconf -e \u0026ldquo;smtpd_recipient_restrictions = permit_mynetworks,permit_sasl_authenticated,reject_unauth_destination\u0026rdquo;\nsudo postconf -e \u0026ldquo;disable_vrfy_command = yes\u0026rdquo;\nsudo postconf -e \u0026ldquo;smtpd_helo_required = yes\u0026rdquo;\nsudo postconf -e \u0026ldquo;milter_protocol = 6\u0026rdquo;\nsudo postconf -e \u0026ldquo;milter_default_action = accept\u0026rdquo;\nsudo postconf -e \u0026ldquo;smtpd_milters = unix:opendkim/opendkim.sock\u0026rdquo;\nsudo postconf -e \u0026ldquo;non_smtpd_milters = unix:opendkim/opendkim.sock\u0026rdquo;\nDovecot SASL 소켓 설정 #파일 수정\nsudo nano /etc/dovecot/conf.d/10-master.conf\n아래 부분 확인/수정\nservice auth {\nunix_listener /var/spool/postfix/private/auth {\nmode = 0660m\nuser = postfix\ngroup = postfix\n}\n}\n재시작\nsudo systemctl restart dovecot\nSPF / DKIM / DMARC DNS 설정 (10점 필수) #SPF (TXT)\nv=spf1 mx ip4:서버IP -all\nDKIM\nsudo cat /etc/opendkim/keys/mail.txt\nmail.txt 파일에 생성된 공개키를\n\u0026ldquo;v= 부터 \u0026ldquo;끝 까지 복사해서 메모장에 옮긴 후\n줄 바꿈을 끄고 p=뒤에 있는 \u0026ldquo;와 공백을 제거하고\nend키로 끝으로 이동 후\ndelete연타(복붙하는 중에 혹시 모르게 삽입되는 노이즈 문자 제거)\nhome키로 맨 앞으로 이동 후\nbackspace연타(복붙하는 중에 혹시 모르게 삽입되는 노이즈 문자 제거)후에\nDNS TXT 레코드에 그대로 추가 합니다.\nmail._domainkey.example.com\n값 = 공개키\nDMARC (TXT 레코드)추가\n_dmarc.example.com\n값 = v=DMARC1; p=quarantine; adkim=s; aspf=s; rua=mailto:postmaster@example.com;\np=none 쓰면 10점 거의 안 나옵니다.\nPTR (Reverse DNS) 설정 #PTR은 고정IP인 경우에만 해당 됩니다.\n유동IP인 경우 A 레코드만 세팅하시면 됩니다.\nDNS 관리자 페이지에서\n정방향\nmail.example.com → 서버IP(A 레코드)\n역방향\n서버IP → mail.example.com(PTR-고정아이피 임대업체에 요청)\n( Gmail,Outlook 매우 중요하게 봄 )\n서비스 재시작\nsudo systemctl restart opendkim\nsudo systemctl restart postfix\n검증 방법 #DKIM 확인\nsudo opendkim-testkey -d example.com -s mail -vvv\nkey OK 확인\nDNS 확인\ndig +short TXT mail._domainkey.example.com\n로그 실시간 확인 #journalctl -u opendkim -f\njournalctl -u postfix -f\nUbuntu 24.04에서 가장 많이 발생하는 오류 #Permission denied (DKIM)\n원인은\npostfix가 opendkim 그룹에 없음\n소켓 경로를 절대경로로 설정\n해결\nsudo gpasswd -a postfix opendkim\nSocket local:/var/spool/postfix/opendkim/opendkim.sock\n","date":"2026-02-27","permalink":"https://blog.snsoz.com/server-log/ubuntu24.04-postfix-opendkim-dovecot/","section":"서버구축기","summary":"","title":"Ubuntu24.04에서 Postfix,OpenDKIM,Dovecot 연동"},{"content":"","date":null,"permalink":"https://blog.snsoz.com/tags/%EC%9A%B0%EB%B6%84%ED%88%AC24.04/","section":"Tags","summary":"","title":"우분투24.04"},{"content":"","date":null,"permalink":"https://blog.snsoz.com/tags/roundcube/","section":"Tags","summary":"","title":"Roundcube"},{"content":"","date":null,"permalink":"https://blog.snsoz.com/tags/sendmail/","section":"Tags","summary":"","title":"Sendmail"},{"content":"2024년 메일서버 도전기 #postfix, dovecot, certbot 명령어 위주로 실습한 기록입니다.\n만약 ai로 리눅스 메일서버에 도전하는 분이 계시다면\nai에 질문 할 때 많은 참고가 될 것입니다.\npostfix는 훨씬 쉽습니다.\nsendmail로 시도하면 대충 메일서버의 흐름이 파악 됩니다.\n그 다음에 postfix로 갈아타시면 될 겁니다.\n처음 시도하시는 분께 도움이 되길 바랍니다.\n시스템 업데이트 및 Sendmail 설치 # sudo apt-get update\nsudo apt -y install sendmail\n호스트네임 설정 # sudo nano /etc/hostname\n(내용: mail.example.com)\nHosts 파일에 공인 IP 매핑 # sudo nano /etc/hosts\n[공인아이피] mail.example.com 추가\n로컬 호스트 네임 추가 후 재부팅 # sudo nano /etc/mail/local-host-names\nmail.example.com 추가\nsudo reboot\nDovecot 설치 #Dovecot(POP3) 설치\nsudo apt -y install dovecot-pop3d\nSendmail 설정 수정 # sudo nano /etc/mail/sendmail.cf\n98행: Cwexample.com 기록\n269행: 외부 접속 허용을 위해 \u0026lsquo;addr=127.0.0.1\u0026rsquo; 부분 삭제\n270행: DAEMON_OPTIONS(`Name=MSA, Port=587, M=E) 587포트 사용\n설정 적용 및 서비스 활성화 # sudo makemap hash /etc/mail/access \u0026lt; /etc/mail/access\nsudo systemctl enable \u0026ndash;now sendmail\nsudo systemctl enable \u0026ndash;now dovecot\nDovecot 기본 설정 수정 # sudo nano /etc/dovecot/dovecot.conf\nlisten, base_dir 주석(#) 제거\ndisable_plaintext_auth = no 추가 (인증 허용)\nDovecot 메일함 권한 설정 # sudo nano /etc/dovecot/conf.d/10-mail.conf\nmail_access_groups = mail 수정\nlock_method = fcntl 수정\n웹메일(Roundcube) 및 LAMP 스택 설치 #APM 패키지 및 IMAP 설치\nsudo apt -y install lamp-server^ dovecot-imapd roundcube\nRoundcube 아파치 설정 활성화 # sudo nano /etc/apache2/conf-enabled/roundcube.conf\nAlias /roundcube 주석 제거\nRoundcube 설정 수정 # sudo nano /etc/roundcube/config.inc.php\ndefault_host = \u0026rsquo;example.com\u0026rsquo;\nsmtp_port = 587\nsmtp_user 값에서 %u 삭제\n서비스 재시작 및 시간대 설정 # sudo systemctl restart apache2 mysql\nsudo timedatectl set-timezone Asia/Seoul\nSSL 인증서 설치(Certbot) # sudo apt-get install software-properties-common\nsudo add-apt-repository universe\nsudo apt-get update\nsudo apt-get install certbot python3-certbot-apache\nSSL 발급 및 자동 설정 # sudo certbot \u0026ndash;apache\n인증서 관리 명령어 # sudo certbot renew \u0026ndash;dry-run # 갱신 테스트\nsudo certbot renew # 실제 갱신\nsudo certbot certificates # 인증서 상태 확인\n","date":"2026-02-25","permalink":"https://blog.snsoz.com/server-log/2024-ubuntu-mailserver-setup/","section":"서버구축기","summary":"","title":"ubuntu24.04 mailserver setup"},{"content":"방명록에 자유롭게 글을 남겨주세요!\n","date":null,"permalink":"https://blog.snsoz.com/guestbook/","section":"방명록","summary":"","title":"방명록"},{"content":"","date":null,"permalink":"https://blog.snsoz.com/tags/%EB%A9%94%EC%9D%BC%EC%84%9C%EB%B2%84/","section":"Tags","summary":"","title":"메일서버"},{"content":"24년 ubuntu2404가 출시되고\n그해 말쯤 메일서버에 처음 도전을 했습니다.\n일단 vmware를 설치를 하고 ubuntu2404를 설치했습니다.\n그리고 유튜브에서 몇 년 된 영상들 뒤지고 검색 해 가면서\nsendmail, dovecot, apache2, roundcube를 설치해서\n겨우 메일을 송수신 할 수 있게 되었습니다.\ncertbot으로 tls인증서를 발급(90일 자동갱신)받아 사용했습니다.\n후이즈에서 DNS레코드 관리를 했고\na레코드와 txt레코드에 spf만 설정한 채로 운영했습니다.\n그렇게 5개 서버를 세팅해서 했죠.\noutlook,naver에 메일 송수신이 되기는 하더라구요.\n유동아이피라 아이피가 바뀔때마다 DNS레코드를 수정해야 했습니다.\n뭐 적용이 거의 실시간으로 되니 큰 불편은 없었는데\n문제는 유동아이피 대부분이 스팸아이피로 등록 되어있다는 겁니다.\n메일을 보내도 IP가 대부분 차단되어있다는 거죠.\nnaver는 메일 보내면 하루정도 안에 풀어줍니다.\nspamhouse는 자동화 되어 있어서 대부분 하루안에 풀어지죠.\n하지만 마이크로소프트는 무조건 거부합니다.\n끈질기게 4~5번정도 해제요청메일을 보내면 결국 풀어주기는 합니다.\n이렇게 그냥 취미삼아 서버 5개를 운영했습니다.\n24시간 켜 놓으면 전기세가 많이 나와서 잠자는 시간엔 꺼 놓았죠.\n그렇게 2년정도 필수 업데이트만 하면서 운영 했습니다.\n그러던 중 ai를 경험하게 되었죠. 첨엔 관심도 없었습니다.\n그러다가 어느날 심심해서 ai에게\nubuntu mailserver에 관해서 몇가지 질문을 해봤습니다.\n와\u0026hellip; 좀더 깊이 들어가도 되겠는데? 하는 생각이 들기 시작했죠.\n최종 목표는 내가 소유한 8개 도메인을 1개 서버에 통합시키고\n제대로 된 메일서버를 구축하는 거였습니다.\n일주일 정도 ai를 이용해 씨름을 한 끝에\npostfix,dovecot을 기번으로 imap,smtp를 이용해\noutlook, thunderbird, nine mail, roundcube에서\n구글,마소,네이버 모두 dkim=pass, spf=pass, dmarc=pass를 성공했습니다.\ntls인증서도 각 도메인 별로 발급받았고요.\n8개도메인 통합에 성공하고 mail-tester에서 8.9점 까지 나오더군요.\n“ssh key로그인(패스워드사용.), password로그인 차단,root로그인 차단, fail2ban설치”\n서버방어도 어느정도 자리를 잡게 되었고\n여기까지 오니 mail-tester에서 10을 받아보고싶은 욕심이 생기더군요.\n노트북으로 서버를 옮겨서 24시간 운영을 하고\nptr(Reverse Domain)이 마지막 남은 과제가 되었습니다.\n취미로 라고 하기에는 좀 과한 상황이었지만\n술한두번 안먹지 하는 마음으로 고정아이피를 신청 했습니다.\nptr레코드 등록이 되고 mail-tester에서\n10점 만점이 찍힌 것을 본 순간 정말 흐뭇한 기분이 들었습니다.\n혹시 메일서버를 운영해 보고 싶은 누군가와\n이 기분을 공유하고 싶은 생각이 들더군요.\n그래서 통합하고 남은 서버 4개중에 한개를 블로그 전용서버로 사용하기 위해서\nhugo와 nginx를 설치하고 github댓글 시스템을 연결해서\n블로그 서버도 운영하게 되었습니다.\n","date":"2026-02-20","permalink":"https://blog.snsoz.com/server-log/first-post/","section":"서버구축기","summary":"","title":"복붙으로 메일서버 구축하기"}]