
메일 발송 환경을 직접 구성하기로 했을 때, 처음부터 대량 발송이나 완성된 메일 서버를 염두에 두었던 것은 아니었다. 필요했던 것은 시스템 알림이나 인증 메일처럼, 정해진 용도로 안정적으로 발송되는 no-reply 메일 하나였다.
그래서 선택된 방향은 단순했다. 외부 SMTP를 연동하지 않고, 서버 내부에서 Postfix를 사용해 메일을 보내되 최소한의 신뢰 요소만 갖추는 방식이었다.
Postfix를 메일 발송 주체로 두는 구조
기본적인 전제는 이 서버가 메일을 “받는” 역할이 아니라, “보내는” 역할만 수행한다는 점이었다. IMAP이나 POP3 설정은 제외했고, 계정 생성 대신 alias 방식으로 no-reply 주소를 처리했다.
Postfix는 Rocky Linux 기본 저장소에서 설치했다.
dnf install postfix -y systemctl enable --now postfix
이 시점에서 중요한 것은 메일이 스팸함으로 가느냐가 아니라, 우선 정상적으로 외부로 전달되는지였다. CLI에서 간단한 테스트 메일을 보내며 기본 동작을 확인했다.
no-reply 주소는 계정이 아니라 alias로 처리했다
메일 계정을 실제로 만들지 않고,
no-reply@도메인 주소를 alias로 처리하는 방식은
구조를 단순하게 유지하는 데 도움이 됐다.
Webmin 환경에서는 Mail Aliases 메뉴를 통해 no-reply 주소를 개인 메일로 포워딩하도록 설정했다. 이 방식은 발송 전용 주소를 만들기에 충분해 보였다.
DKIM 설정은 Postfix가 아니라 OpenDKIM의 역할이었다
메일이 정상적으로 도착하더라도, 헤더를 확인해보면 DKIM 서명이 없다는 점이 바로 드러났다. 이 지점에서 Postfix 자체에는 DKIM 기능이 없다는 사실을 다시 확인하게 된다.
DKIM은 OpenDKIM이라는 별도의 서비스로 처리했다.
dnf install opendkim opendkim-tools -y
Rocky Linux 9 환경에서는 CRB 저장소 활성화와 추가 라이브러리 설치가 필요했다. 이 부분을 지나지 않으면 OpenDKIM 설치 단계에서 멈추게 된다.
DKIM 키 생성과 DNS 등록 흐름
키는 도메인 단위로 생성했고, 관리 경로는 명확하게 구분했다.
mkdir -p /etc/opendkim/keys/goz.kr opendkim-genkey -b 2048 -d goz.kr -s default -D /etc/opendkim/keys/goz.kr chown -R opendkim:opendkim /etc/opendkim/keys chmod 600 /etc/opendkim/keys/goz.kr/default.private
이 과정에서 생성된 default.txt 파일의 내용은
도메인 DNS 관리자 페이지에 TXT 레코드로 등록했다.
이름은 default._domainkey 형태로 입력했고,
값에는 공개키 문자열만 한 줄로 정리해 넣었다.
처음에는 다소 낯설게 느껴졌지만,
결국 이는 메일 인증을 위한 하나의 서브도메인일 뿐이었다.
OpenDKIM과 Postfix를 연결하는 단계
키와 DNS가 준비된 이후에는 OpenDKIM이 실제로 서명을 수행할 수 있도록 설정 파일을 정리했다.
Syslog yes Canonicalization relaxed/simple Mode sv Domain goz.kr Selector default KeyFile /etc/opendkim/keys/goz.kr/default.private Socket local:/run/opendkim/opendkim.sock UserID opendkim:opendkim
소켓 디렉터리는 직접 생성해 권한을 맞췄다.
mkdir -p /run/opendkim chown opendkim:opendkim /run/opendkim chmod 755 /run/opendkim
Postfix 설정 파일에는 milter 항목을 추가해 OpenDKIM과의 연결을 명시했다.
milter_default_action = accept milter_protocol = 6 smtpd_milters = unix:/run/opendkim/opendkim.sock non_smtpd_milters = unix:/run/opendkim/opendkim.sock
정상 기동 여부는 헤더로 확인했다
서비스를 재시작한 뒤, 테스트 메일을 보내고 메일 헤더를 확인했다.
dkim=pass라는 결과가 표시되는 순간,
비로소 메일 서버가 신뢰 흐름에 편입되었다는 느낌이 들었다.
메일은 그 전에도 도착했지만,
이제는 “도착한 이유”가 설명되는 상태가 된 셈이다.
PHP에서는 mail 함수만으로 충분했다
외부 SMTP 라이브러리를 사용하지 않고, PHP의 기본 mail 함수를 그대로 사용했다. Postfix와 OpenDKIM이 이미 서버 단에서 처리하고 있었기 때문이다.
From 헤더에 표시명을 추가하면서,
받는 사람의 메일함에는
고즐 <no-reply@도메인> 형태로 표시되도록 했다.
이 구조는 대량 발송을 위한 것은 아니지만, 시스템 알림이나 인증 메일 용도로는 충분해 보인다.
귀찮아졌다는 감각의 정체
설정이 많아서라기 보다는, 메일이라는 매체가 요구하는 신뢰가 두텁다는 점이 이 작업을 귀찮게 느끼게 만드는 듯했다.
한 번 이 흐름을 따라가고 나니, 외부 SMTP를 선택하는 이유도, 직접 구성하는 선택의 의미도 조금 더 분명해진 느낌이다.