Skip to content

메일 서버 구축

  • Postfix와 Dovecot를 활용한 메일 서버 구축
    • SMTP - Postfix
    • IMAP - Dovecot
    • LMTP를 사용하여 로컬 메일 전송
    • MySQL을 사용하여 SASL auth login user credential 저장
    • bind9를 사용하여 DNS 설정

Configuration

DNS 서버 설정

  • 로컬 도메인 설정을 위해 bind를 사용하여 DNS 서버 구축
  • 설치

    $ dnf install bind -y
    ...
    $ systemctl enable --now named
    
  • 설정

    /etc/named.conf
    -         listen-on port 53 { 127.0.0.1; };
    +         listen-on port 53 { any; };
    ...
    -         allow-query     { localhost; };
    +         allow-query     { any; };
    
    /etc/named.rfc1912.zones
    + zone "linux.cju" IN {
    +         type master;
    +         file "cju.zone";
    + };
    
    + zone "67.0.168.192.in-addr.arpa" IN {
    +         type master;
    +         file "cju.rev";
    + };
    
    + /var/named/cju.zone
    $TTL 1D
    @       IN SOA  linux.cju. hostmaster.linux.cju. (
                                            2024121201      ; serial
                                            1D      ; refresh
                                            1H      ; retry
                                            1W      ; expire
                                            3H )    ; minimum
            NS      ns.linux.cju.
            A       192.168.0.67
            MX 10   mail.linux.cju.
    ns      A       192.168.0.67
    mail    A       192.168.0.67
    www     A       192.168.0.67
    
    + /var/named/cju.rev
    $TTL 1D
    @       IN SOA  ns.linux.cju. hostmaster.linux.cju. (
                                            0       ; serial
                                            1D      ; refresh
                                            1H      ; retry
                                            1W      ; expire
                                            3H )    ; minimum
            NS      ns.linux.cju.
            PTR     linux.cju.
            PTR     ns.linux.cju.
            PTR     mail.linux.cju.
            PTR     www.linux.cju.
    

MySQL 설정

  • 사용자 정보 저장
  • 설치

    $ dnf install mysql mysql-server
    ...
    $ systemctl enable --now mysqld
    ...
    $ mysql
    ...
    mysql> 
    
  • 설정

    • MySQL

      ALTER USER 'root'@'localhost' IDENTIFIED BY 'root';
      create user 'mailrl'@'%' identified by 'mailrl';
      grant all privileges on *.* to 'mailrl'@'%' with grant option;
      create database myapp;
      use myapp;
      create table if not exists users (
          username varchar(64) primary key,
          password varchar(106) not null
      );
      create table if not exists aliases (
          source varchar(64) not null,
          destination varchar(64) not null
      );
      
    • bash

      $ doveadm pw -s SHA512-CRYPT -p cju
      {SHA512-CRYPT}$6$0xA0c83AfYxE.5mX$jsJrZ3.zKNmmDWBls7t7Qn/drAHyrapqEGKqWqeCaonCelTIymU8BxZCiZNEPg2FWcnup9AjO/4nGVaqpC.aR.
      $ doveadm pw -s SHA512-CRYPT -p test
      {SHA512-CRYPT}$6$C2NsWemSpft6OVPJ$Yfl8H6nS5InTX.LeLe59Zr9u8vWF6dFc6UGcqswdOgPyz/5FxCFGCxQXdTGwMoqWYMYfWoFxueAyusguulPsk0
      
    • MySQL

      insert into users(username,password) values('cju','$6$0xA0c83AfYxE.5mX$jsJrZ3.zKNmmDWBls7t7Qn/drAHyrapqEGKqWqeCaonCelTIymU8BxZCiZNEPg2FWcnup9AjO/4nGVaqpC.aR.');
      insert into users(username,password) values('test','$6$C2NsWemSpft6OVPJ$Yfl8H6nS5InTX.LeLe59Zr9u8vWF6dFc6UGcqswdOgPyz/5FxCFGCxQXdTGwMoqWYMYfWoFxueAyusguulPsk0');
      

Postfix 설정

  • 설치

    $ dnf install postfix postfix-mysql -y
    ...
    $ systemctl enable --now postfix
    
  • 설정

    /etc/postfix/main.cf
    - #myhostname = host.domain.tld
    + myhostname = mail.linux.cju
    
    - #mydomain = domain.tld
    + mydomain = linux.cju
    
    - #myorigin = $mydomain
    + myorigin = $mydomain
    
    - inet_interfaces = localhost
    + inet_interfaces = all
    
    - mydestination = $myhostname, localhost.$mydomain, localhost
    + mydestination =
    
    + biff = no
    + append_dot_mydomain = no
    + smtpd_sasl_type = dovecot
    + smtpd_sasl_path = private/auth
    + smtpd_sasl_auth_enable = yes
    + smtpd_sasl_local_domain = $myhostname
    + virtual_transport = lmtp:unix:private/dovecot-lmtp
    + virtual_mailbox_domains = $mydomain
    + virtual_mailbox_maps = mysql:/etc/postfix/mysql-mailbox.cf
    + virtual_alias_maps = mysql:/etc/postfix/mysql-alias.cf
    
    + /etc/postfix/mysql-mailbox.cf
    user = mailrl
    password = mailrl
    hosts = localhost
    dbname = myapp
    query = SELECT 1 from users where CONCAT(username, '@', 'linux.cju')='%s';
    
    + /etc/postfix/mysql-alias.cf
    user = mailrl
    password = mailrl
    hosts = localhost
    dbname = myapp
    query = SELECT destination FROM aliases WHERE source='%s'
    
    /etc/postfix/master.cf
    - #submission inet n       -       n       -       -       smtpd
    - #  -o syslog_name=postfix/submission
    - #  -o smtpd_tls_security_level=encrypt
    - #  -o smtpd_sasl_auth_enable=yes
    - #  -o smtpd_tls_auth_only=yes
    - #  -o smtpd_reject_unlisted_recipient=no
    + submission inet n       -       n       -       -       smtpd
    +   -o syslog_name=postfix/submission
    +   -o smtpd_tls_security_level=encrypt
    +   -o smtpd_sasl_auth_enable=yes
    +   -o smtpd_tls_auth_only=yes
    +   -o smtpd_reject_unlisted_recipient=no
    
    - #  -o smtpd_recipient_restrictions=
    - #  -o smtpd_relay_restrictions=permit_sasl_authenticated,reject
    - #  -o milter_macro_daemon_name=ORIGINATING
    - #smtps     inet  n       -       n       -       -       smtpd
    - #  -o syslog_name=postfix/smtps
    - #  -o smtpd_tls_wrappermode=yes
    - #  -o smtpd_sasl_auth_enable=yes
    - #  -o smtpd_reject_unlisted_recipient=no
    +   -o smtpd_recipient_restrictions=
    +   -o smtpd_relay_restrictions=permit_sasl_authenticated,reject
    +   -o milter_macro_daemon_name=ORIGINATING
    + smtps     inet  n       -       n       -       -       smtpd
    +   -o syslog_name=postfix/smtps
    +   -o smtpd_tls_wrappermode=yes
    +   -o smtpd_sasl_auth_enable=yes
    +   -o smtpd_reject_unlisted_recipient=no
    
    - #  -o smtpd_recipient_restrictions=
    - #  -o smtpd_relay_restrictions=permit_sasl_authenticated,reject
    - #  -o milter_macro_daemon_name=ORIGINATING
    +   -o smtpd_recipient_restrictions=
    +   -o smtpd_relay_restrictions=permit_sasl_authenticated,reject
    +   -o milter_macro_daemon_name=ORIGINATING
    
  • 적용

    $ postfix check # check config error
    $ postfix reload
    postfix/postfix-script: refreshing the Postfix mail system
    

Dovecot 설정

  • 설치

    $ dnf install dovecot dovecot-mysql -y
    ...
    $ systemctl enable --now dovecot
    
  • 설정

    • shell
    $ mkdir -p /var/mail/linux.cju
    
    /etc/dovecot/dovecot.conf
    - #protocols = imap pop3 lmtp submission
    + protocols = imap pop3 lmtp
    
    - #listen = *, ::
    + listen = *, ::
    
    /etc/dovecot/conf.d/10-mail.conf
    - #mail_location =
    + mail_location = maildir:/var/mail/%d/%n
    
    - #mail_privileged_group =
    + mail_privileged_group = mail
    
    - first_valid_uid = 1000
    - #last_valid_uid = 0
    + first_valid_uid = 8 // $ id mail
    + last_valid_uid = 8
    
    + /etc/dovecot/dovecot-sql.conf.ext
    driver = mysql
    connect = host=127.0.0.1 dbname=myapp user=mailrl password=mailrl
    default_pass_scheme = SHA512-CRYPT
    user_query = SELECT CONCAT('/var/mail/linux.cju/',username) AS home, 'mail' AS uid, 'mail' AS gid FROM users WHERE username='%n'
    password_query = SELECT password FROM users WHERE username='%n';
    
    /etc/dovecot/conf.d/10-auth.conf
    - #disable_plaintext_auth = yes
    + disable_plaintext_auth = yes
    
    - auth_mechanisms = plain
    + auth_mechanisms = plain login
    
    - !include auth-system.conf.ext
    - #!include auth-sql.conf.ext
    + #!include auth-system.conf.ext
    + !include auth-sql.conf.ext
    
    /etc/dovecot/conf.d/10-master.conf
    service lmtp {
    -   unix_listener lmtp {
    -     #mode = 0666
    +   unix_listener /var/spool/postfix/private/dovecot-lmtp {
    +     mode = 0666
    +     user = postfix
    +     group = postfix
      }
    
    service auth {
      unix_listener auth-userdb {
    -    #mode = 0666
    -    #user =
    -    #group =
    +    mode = 0666
    +    user = mail
    +    group = mail
      }
    
      # Postfix smtp-auth
    -  #unix_listener /var/spool/postfix/private/auth {
    -  #  mode = 0666
    -  #}
    +  unix_listener /var/spool/postfix/private/auth {
    +    mode = 0660
    +    user = postfix
    +    group = postfix
    +  }
    
    service auth-worker {
    
    -  #user = root
    +  user = mail
    }
    
  • 적용

    $ doveconf 1>/dev/null # check config error
    $ doveadm reload
    

Test

  • SMTP/SMTPS

    # SMTP
    $ telnet mail.linux.cju 25
    # SMTPS
    $ openssl s_client --connect mail.linux.cju:465
    ...    
    220 mail.linux.cju ESMTP Postfix
    
    > ehlo linux.cju
    ...
    250-AUTH PLAIN LOGIN
    ...
    
    > auth login
    334 VXNlcm5hbWU6
    
    > Y2p1QGxpbnV4LmNqdQ== # echo 'cju@linux.cju' | base64
    334 UGFzc3dvcmQ6
    
    > Y2p1 # echo 'cju' | base64
    235 2.7.0 Authentication successful
    
    > mail from:<cju@linux.cju>
    250 2.1.0 Ok
    
    > rcpt to:<test@linux.cju>
    250 2.1.5 Ok
    
    > data
    354 End data with <CR><LF>.<CR><LF>
    
    > subject:Hello test!
    > This is cju!
    > thx..
    > .
    250 2.0.0 Ok: queued as 3D748B0539
    
    > quit
    221 2.0.0 Bye
    
    Image

    smtp_test

  • IMAP/IMAPS

    # IMAP
    $ telnet mail.linux.cju 143
    # IMAPS
    $ openssl s_client --connect mail.linux.cju:993
    * OK [CAPABILITY IMAP4rev1 SASL-IR LOGIN-REFERRALS ID ENABLE IDLE LITERAL+ AUTH=PLAIN AUTH=LOGIN] Dovecot ready.
    
    > a1 login test@linux.cju test
    a1 OK [CAPABILITY IMAP4rev1 SASL-IR LOGIN-REFERRALS ID ENABLE IDLE SORT SORT=DISPLAY THREAD=REFERENCES THREAD=REFS THREAD=ORDEREDSUBJECT MULTIAPPEND URL-PARTIAL CATENATE UNSELECT CHILDREN NAMESPACE UIDPLUS LIST-EXTENDED I18NLEVEL=1 CONDSTORE QRESYNC ESEARCH ESORT SEARCHRES WITHIN CONTEXT=SEARCH LIST-STATUS BINARY MOVE SNIPPET=FUZZY PREVIEW=FUZZY PREVIEW STATUS=SIZE SAVEDATE LITERAL+ NOTIFY SPECIAL-USE] Logged in
    
    > a2 list "" "*"
    * LIST (\HasNoChildren) "." INBOX
    a2 OK List completed (0.001 + 0.000 secs).
    
    > a3 examine INBOX
    * FLAGS (\Answered \Flagged \Deleted \Seen \Draft)
    * OK [PERMANENTFLAGS ()] Read-only mailbox.
    * 3 EXISTS
    * 3 RECENT
    * OK [UNSEEN 1] First unseen.
    * OK [UIDVALIDITY 1734058789] UIDs valid
    * OK [UIDNEXT 2] Predicted next UID
    a3 OK [READ-ONLY] Examine completed (0.001 + 0.000 secs).
    
    > a4 fetch 1 body[]
    * 1 FETCH (BODY[] {598}
    Return-Path: <cju@linux.cju>
    Delivered-To: test@linux.cju
    Received: from mail.linux.cju
            by mail.linux.cju with LMTP
            id cketB5ejW2ckKwAAOJi4uQ
            (envelope-from <cju@linux.cju>)
            for <test@linux.cju>; Fri, 13 Dec 2024 12:01:43 +0900
    Received: from linux.cju (mail.linux.cju [IPv6:fe80::5054:ff:feb4:5be9])
            by mail.linux.cju (Postfix) with ESMTPSA id 51F32AE9C3
            for <test@linux.cju>; Fri, 13 Dec 2024 12:01:09 +0900 (JST)
    subject:Hello test!
    Message-Id: <20241213030117.51F32AE9C3@mail.linux.cju>
    Date: Fri, 13 Dec 2024 12:01:09 +0900 (JST)
    From: cju@linux.cju
    
    This is cju!
    thx..
    )
    a4 OK Fetch completed (0.001 + 0.000 secs).
    
    > a5 logout
    * BYE Logging out
    a5 OK Logout completed (0.001 + 0.000 secs).
    
    Image

    imap_test

References