작업실
서버 분류

도커로 메일서버 구축하기

컨텐츠 정보

본문


도커 메일서버란?


도커에서 메일서버를 직접 운영할 수 있게 해줍니다.


SMTP, ESMTP, POP3, IMAP 방식을 사용할 수 있습니다.


자신의 도메인 계정으로 메일을 보내고 받을 수 있습니다.


DNS 설정으로 구글, 네이버, 다음, 카카오, 프로톤 메일 등에서 스팸처리 당하지 않게 합니다.


그누보드, 라이믹스, 워드프레스와 연동하여 메일을 보낼 수 있습니다.


아웃룩, 구글 지메일과 연동하여 메일을 읽고 보낼 수 있습니다.



전제조건


RAM 최소 1GB 이상 (SWAP 2GB 이상 추천)


그리고 반드시 25(SMTP), 993(IMAP), 465(ESMTP)포트가 열릴 수 있어야 합니다. POP3가 필요하다면 995번 포트도 포함


구글 클라우드의 경우 25번 포트를 열 수 없기 때문에 도커 메일서버를 사용할 수 없습니다.


KT회선으로 직접 운영하거나, 오라클 클라우드를 사용한다면 25번 포트가 열려있으므로 바로 사용 가능합니다.


하지만 오라클 클라우드의 경우 리버스 DNS를 유료사용자에게만 제공하므로 무료 사용자는 힘들다고 보면 됩니다.


25번 포트가 막혀있을 땐 SendGrid api를 이용하여 relay로 쓸 수 있다고 합니다. 


(링크 : https://medium.com/minds-in-the-cloud/spin-off-an-email-server-with-containers-using-docker-compose-on-google-cloud-platform-debian-9-aa0fe8bf3d88 )


mailhog나 mailcatcher를 이용해도 될 것 같은데, 연구 중입니다.



방화벽 오픈


우선 포트부터 오픈해줍니다.


ufw를 사용하고 있다면



sudo ufw allow 25/tcp
sudo ufw allow 465/tcp
sudo ufw allow 993/tcp


위와 같이 25번, 465번, 993번 포트를 열어줍니다. POP3를 원한다면 995번도 같은 방식으로 추가해주세요.



iptables를 사용하고 있다면



sudo iptables -A INPUT -p tcp -m tcp --dport 25 -j ACCEPT
sudo iptables -A INPUT -p tcp -m tcp --dport 465 -j ACCEPT
sudo iptables -A INPUT -p tcp -m tcp --dport 993 -j ACCEPT
sudo netfilter-persistent save
sudo netfilter-persistent reload


위와 같이 25번, 143번 포트를 열어줍니다. POP3를 원한다면 995번도 같은 방식으로 추가해주세요.



firewall을 사용하고 있다면



sudo firewall-cmd --permanent --add-port=25/tcp
sudo firewall-cmd --permanent --add-port=465/tcp
sudo firewall-cmd --permanent --add-port=993/tcp
sudo firewall-cmd --reload


위와 같이 25번, 143번 포트를 열어줍니다. POP3를 원한다면 995번도 같은 방식으로 추가해주세요.


또한 각 호스팅 및 클라우드 서비스 패널에서 해당 포트를 모두 열어주세요.



적용예시


이 가이드를 바로 적용할 수 있는 글은 다음과 같습니다.


Docker compose로 라이믹스 편하게 설치하는 방법


https://www.wsgvet.com/ubuntu/125



Docker compose로 그누보드 편하게 설치하는 방법


https://www.wsgvet.com/ubuntu/126



Docker compose로 워드프레스 편하게 설치하는 방법


https://www.wsgvet.com/ubuntu/153



Docker compose로 멀티 사이트 운영하기


https://www.wsgvet.com/ubuntu/123 



여기서부터는 제가 올려드린 예시를 통해 설치했다고 가정하고 시작합니다.


기존에 도커를 쓰지 않는 상황에서는 바로 적용하기 어렵습니다.


하지만 해당 소스 및 설명을 이해할 수 있다면 누구나 적용할 수 있습니다.



SSL 인증서 설치


IMAP이나 POP3를 이용하려면 인증서 발급이 필수입니다.


certbot을 이용할 것입니다.


와일드카드 인증서를 가지고 있다면 마지막 설정 부분만 보시면 됩니다.


그 이외 부분은 https://github.com/tomav/docker-mailserver/wiki/Configure-SSL 링크를 참조하세요.



먼저 도메인의 DNS 설정에서 mail.example.com을 자신의 서버IP를 가리키게 해주세요.


이하 example.com을 모두 자신의 도메인으로 바꿔주세요.


이제 도커 환경으로 돌아옵니다.


./nginx/conf.d/mail.conf 파일을 만들어서



server {
        listen 80;
        listen [::]:80;


        server_name mail.example.com;


        # Useful for Let's Encrypt
        location ~ /.well-known/acme-challenge/ {
                allow all;
                root /usr/share/nginx/html;
        }
}


위 내용을 넣습니다.



sudo docker-compose up -d --force-recreate --no-deps nginx


위 명령어로 Nginx 설정을 적용해줍니다.


docker-compose.yml 에서



  certbot:
    image: certbot/certbot:latest
    container_name: certbot
    volumes:
      - ./ssl/:/etc/letsencrypt/
      - /usr/share/nginx/html:/usr/share/nginx/html
    #command: certonly -n --webroot -w /usr/share/nginx/html -d example.com -d www.example.com -d port.example.com -d pma.example.com
    command: certonly --dry-run -n --webroot -w /usr/share/nginx/html -d mail.example.com


위와 같이 certbot 부분에 기존 command를 주석처리하고, mail.example.com 생성 테스트를 위한 command를 넣습니다.


저장합니다.


그리고 



sudo docker-compose up certbot


위 명령어로 certbot을 실행합니다.



# sudo docker-compose up certbot
Recreating certbot ... done
Attaching to certbot
certbot         | Saving debug log to /var/log/letsencrypt/letsencrypt.log
certbot         | Plugins selected: Authenticator webroot, Installer None
certbot         | Obtaining a new certificate
certbot         | Performing the following challenges:
certbot         | http-01 challenge for mail.example.com
certbot         | Using the webroot path /usr/share/nginx/html for all unmatched domains.
certbot         | Waiting for verification...
certbot         | Cleaning up challenges
certbot         | IMPORTANT NOTES:
certbot         |  - The dry run was successful.
certbot         |  - Your account credentials have been saved in your Certbot
certbot         |    configuration directory at /etc/letsencrypt. You should make a
certbot         |    secure backup of this folder now. This configuration directory will
certbot         |    also contain certificates and private keys obtained by Certbot so
certbot         |    making regular backups of this folder is ideal.
certbot exited with code 0


위와 같이 dry run 테스트가 성공했습니다.


이제 다시 docker-compose.yml 파일을 열어서



  certbot:
    image: certbot/certbot:latest
    container_name: certbot
    volumes:
      - ./ssl/:/etc/letsencrypt/
      - /usr/share/nginx/html:/usr/share/nginx/html
    #command: certonly -n --webroot -w /usr/share/nginx/html -d example.com -d www.example.com -d port.example.com -d pma.example.com
    command: certonly -n --webroot -w /usr/share/nginx/html -d mail.example.com


위와 같이 --dry-run을 삭제해줍니다.



sudo docker-compose up certbot


다시 위 명령어로 인증서 발급을 시도합니다.



# sudo docker-compose up certbot
Recreating certbot ... done
Attaching to certbot
certbot         | Saving debug log to /var/log/letsencrypt/letsencrypt.log
certbot         | Plugins selected: Authenticator webroot, Installer None
certbot         | Obtaining a new certificate
certbot         | Performing the following challenges:
certbot         | http-01 challenge for mail.example.com
certbot         | Using the webroot path /usr/share/nginx/html for all unmatched domains.
certbot         | Waiting for verification...
certbot         | Cleaning up challenges
certbot         | IMPORTANT NOTES:
certbot         |  - Congratulations! Your certificate and chain have been saved at:
certbot         |    /etc/letsencrypt/live/mail.example.com/fullchain.pem
certbot         |    Your key file has been saved at:
certbot         |    /etc/letsencrypt/live/mail.example.com/privkey.pem
certbot         |    Your cert will expire on 2020-12-22. To obtain a new or tweaked
certbot         |    version of this certificate in the future, simply run certbot
certbot         |    again. To non-interactively renew *all* of your certificates, run
certbot         |    "certbot renew"
certbot         |  - If you like Certbot, please consider supporting our work by:
certbot         | 
certbot         |    Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
certbot         |    Donating to EFF:                    https://eff.org/donate-le
certbot         | 
certbot exited with code 0


위와 같이 발급이 잘 된 것을 볼 수 있습니다.



이제 다시 docker-compose.yml 파일을 열어서



  certbot:
    image: certbot/certbot:latest
    container_name: certbot
    volumes:
      - ./ssl/:/etc/letsencrypt/
      - /usr/share/nginx/html:/usr/share/nginx/html
    command: certonly -n --webroot -w /usr/share/nginx/html -d example.com -d www.example.com -d port.example.com -d pma.example.com -d mail.example.com


위와 같이 command에 -d mail.example.com도 포함해줍니다. 인증서 갱신시 같이 됩니다.



docker-compose.yml 파일 작성하기


이제 docker-mailserverdocker-compose.yml에 추가해줍니다.


밑에 예시가 있으니 참조하세요.



.
.
.
  mail.example.com:
    image: tvial/docker-mailserver:latest
    hostname: mail.example.com
    domainname: example.com
    container_name: mail.example.com
    ports:
      - "25:25"   # SMTP SEND
      #- "110:110" # POP3 RECEIVE
      #- "143:143" # IMAP RECEIVE
      - "465:465" # ESMTP Enforced Encryption Submission
      #- "587:587" # ESMTP SEND
      - "993:993" # IMAP Enforced Encryption Retrieval
      #- "995:995" # POP3 Enforced Encryption Retrieval POP3가 필요하면 제일 앞에 주석을 해제하세요.
    volumes:
      - ./mail/maildata:/var/mail
      - ./mail/mailstate:/var/mail-state
      - ./mail/maillogs:/var/log/mail
      - ./mail/config/:/tmp/docker-mailserver/
      - ./ssl/:/tmp/ssl:ro  # Certificate location
      - /etc/localtime:/etc/localtime:ro
      - /etc/timezone:/etc/timezone:ro
    environment:
      #- mail.env
      #- env-mailserver
      - SSL_TYPE=manual
      - SSL_CERT_PATH=/tmp/ssl/live/mail.example.com/fullchain.pem # Certificate location
      - SSL_KEY_PATH=/tmp/ssl/live/mail.example.com/privkey.pem # Certificate location
      - ENABLE_SPAMASSASSIN=1
      - SPAMASSASSIN_SPAM_TO_INBOX=1
      #- ENABLE_CLAMAV=1  # 바이러스 검사를 원하면 주석을 해제하세요.
      #- ENABLE_FAIL2BAN=1  # Fail2ban을 활성화하려면 주석을 해제하세요.
      - ENABLE_POSTGREY=1
      #- ENABLE_POP3=1  # POP3 접속을 원하면 주석을 해제하세요.
      - ONE_DIR=1
      - DMS_DEBUG=0
      - PERMIT_DOCKER=connected-networks

    cap_add:

      - NET_ADMIN
      - SYS_PTRACE
    restart: always
.
.
.


위 내용에서 example.com를 자신의 도메인으로 바꿉니다.



- ./ssl/:/tmp/ssl:ro  # Certificate location


./ssl/ 폴더를 docker-mailserver의 /tmp/ssl 에 마운트 한다는 말입니다.


여기는 SSL 인증서의 위치를 결정하는 곳입니다.


docker-multi-site 설정대로 했다면



- ./site/ssl/:/tmp/ssl:ro  # Certificate location


위와 같이 해주세요.


개인적으로 와일드카드 인증서를 발급 받았다면 ./ssl/ 대신에 인증서가 있는 폴더를 지정해주세요.



그리고 



- SSL_CERT_PATH=/tmp/ssl/live/mail.example.com/fullchain.pem # Certificate location
- SSL_KEY_PATH=/tmp/ssl/live/mail.example.com/privkey.pem # Certificate location


위 내용의 뜻은 


SSL_CERT_PATH : 인증서 경로를 지정하는 것입니다.

SSL_KEY_PATH : 개인키 경로를 지정하는 것입니다.


따라서 ./ssl에 있는 인증서가 /tmp/ssl에 있으므로 그 이후에는 상대경로 그대로 따라간다고 해석하면 됩니다.


example.com 대신에 자신의 도메인으로 수정하세요.


위와 같이 fullchain.pem 파일과 privkey.pem 파일이 있는 곳을 지정하면 됩니다.


docker-multi-site 설정대로 했다면



- SSL_CERT_PATH=/tmp/ssl/fullchain.pem # Certificate location
- SSL_KEY_PATH=/tmp/ssl/privkey.pem # Certificate location


위와 같이 해주세요.


개인적으로 와일드카드 인증서를 발급 받았다면 경로를 직접 지정해주면 됩니다.



혹시 로컬에서 그누보드, 라이믹스, 워드프레스를 사용하고 있다면



- PERMIT_DOCKER=connected-networks


위 내용을 삭제하면 localhost와 소통이 됩니다.



docker-compose 설정은 끝났습니다.



도커 예시 모음


docker-gnuboard



version: '3'


services:


  db:
    image: mariadb:latest
    container_name: db
    restart: unless-stopped
    env_file: .env
    environment:
      - TZ=Asia/Seoul
    volumes:
      - ./data/dbdata:/var/lib/mysql
  
  certbot:
    image: certbot/certbot:latest
    container_name: certbot
    volumes:
      - ./ssl/:/etc/letsencrypt/
      - /usr/share/nginx/html:/usr/share/nginx/html
    command: certonly -n --webroot -w /usr/share/nginx/html -d example.com -d www.example.com -d port.example.com -d pma.example.com -d mail.example.com


  redis:
    container_name: redis
    image: redis:alpine
    restart: unless-stopped
    volumes:
      - ./data/dataredis:/data
      - /etc/localtime:/etc/localtime:ro
      - /etc/timezone:/etc/timezone:ro
    command: redis-server --maxmemory 256mb --maxmemory-policy allkeys-lru --appendonly yes


  smtp:
    image: namshi/smtp
    container_name: smtp
    restart: always
    env_file: .env


  php:
    depends_on:
      - db
    build:
      context: ./build
    container_name: php
    restart: unless-stopped
    env_file: .env
    volumes:
      - ./site:/var/www/web
      - ./php/php.ini:/usr/local/etc/php/php.ini
      - /etc/localtime:/etc/localtime:ro
      - /etc/timezone:/etc/timezone:ro


  nginx:
    depends_on:
      - php
    image: nginx:alpine
    container_name: nginx
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - phpmyadmin:/var/www/html
      - ./ssl/:/etc/letsencrypt/
      - ./site:/var/www/web
      - ./nginx/conf.d:/etc/nginx/conf.d
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf
      - ./nginx/logs:/var/log/nginx/
      - /usr/share/nginx/html:/usr/share/nginx/html
      - /etc/localtime:/etc/localtime:ro
      - /etc/timezone:/etc/timezone:ro


  phpmyadmin:
    image: phpmyadmin:fpm-alpine
    container_name: phpmyadmin
    #ports:
    #  - "80:80"
    environment:
      - PMA_HOST=db
    restart: unless-stopped
    depends_on:
      - db
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - /etc/timezone:/etc/timezone:ro
      - ./php/php.ini:/usr/local/etc/php/php.ini
      - phpmyadmin:/var/www/html


  portainer:
    image: portainer/portainer-ce:alpine
    container_name: portainer
    restart: unless-stopped
    #ports:
    #  - "9000:9000"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - ./data/portainer_data:/data
      - /etc/localtime:/etc/localtime:ro
      - /etc/timezone:/etc/timezone:ro


  mail.example.com:
    image: tvial/docker-mailserver:latest
    hostname: mail.example.com
    domainname: example.com
    container_name: mail.example.com
    ports:
      - "25:25"   # SMTP SEND
      #- "110:110" # POP3 RECEIVE
      #- "143:143" # IMAP RECEIVE
      - "465:465" # ESMTP Enforced Encryption Submission
      #- "587:587" # ESMTP SEND
      - "993:993" # IMAP Enforced Encryption Retrieval
      #- "995:995" # POP3 Enforced Encryption Retrieval POP3가 필요하면 제일 앞에 주석을 해제하세요.
    volumes:
      - ./mail/maildata:/var/mail
      - ./mail/mailstate:/var/mail-state
      - ./mail/maillogs:/var/log/mail
      - ./mail/config/:/tmp/docker-mailserver/
      - ./ssl/:/tmp/ssl:ro  # Certificate location
      - /etc/localtime:/etc/localtime:ro
      - /etc/timezone:/etc/timezone:ro
    environment:
      #- mail.env
      #- env-mailserver
      - SSL_TYPE=manual
      - SSL_CERT_PATH=/tmp/ssl/live/mail.example.com/fullchain.pem # Certificate location
      - SSL_KEY_PATH=/tmp/ssl/live/mail.example.com/privkey.pem # Certificate location
      - ENABLE_SPAMASSASSIN=1
      - SPAMASSASSIN_SPAM_TO_INBOX=1
      #- ENABLE_CLAMAV=1  # 바이러스 검사를 원하면 주석을 해제하세요.
      #- ENABLE_FAIL2BAN=1  # Fail2ban을 활성화하려면 주석을 해제하세요.
      - ENABLE_POSTGREY=1
      #- ENABLE_POP3=1  # POP3 접속을 원하면 주석을 해제하세요.
      - ONE_DIR=1
      - DMS_DEBUG=0
      - PERMIT_DOCKER=connected-networks
    cap_add:

      - NET_ADMIN
      - SYS_PTRACE
    restart: always
      
volumes:
  phpmyadmin:



docker-rhymix



version: '3'


services:


  db:
    image: mariadb:latest
    container_name: db
    restart: unless-stopped
    env_file: .env
    environment:
      - TZ=Asia/Seoul
    volumes:
      - ./data/dbdata:/var/lib/mysql


  redis:
    container_name: redis
    image: redis:alpine
    restart: unless-stopped
    volumes:
      - ./data/dataredis:/data
      - /etc/localtime:/etc/localtime:ro
      - /etc/timezone:/etc/timezone:ro
    command: redis-server --maxmemory 256mb --maxmemory-policy allkeys-lru --appendonly yes
  
  certbot:
    image: certbot/certbot:latest
    container_name: certbot
    volumes:
      - ./ssl/:/etc/letsencrypt/
      - /usr/share/nginx/html:/usr/share/nginx/html
    command: certonly -n --webroot -w /usr/share/nginx/html -d example.com -d www.example.com -d port.example.com -d pma.example.com -d mail.example.com


  php:
    depends_on:
      - db
    build:
      context: ./build
    container_name: php
    restart: unless-stopped
    env_file: .env
    volumes:
      - ./site:/var/www/web
      - ./php/php.ini:/usr/local/etc/php/php.ini
      - /etc/localtime:/etc/localtime:ro
      - /etc/timezone:/etc/timezone:ro


  nginx:
    depends_on:
      - php
    image: nginx:alpine
    container_name: nginx
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - phpmyadmin:/var/www/html
      - ./ssl/:/etc/letsencrypt/
      - ./site:/var/www/web
      - ./nginx/conf.d:/etc/nginx/conf.d
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf
      - ./nginx/logs:/var/log/nginx/
      - /usr/share/nginx/html:/usr/share/nginx/html
      - /etc/localtime:/etc/localtime:ro
      - /etc/timezone:/etc/timezone:ro


  phpmyadmin:
    image: phpmyadmin:fpm-alpine
    container_name: phpmyadmin
    #ports:
    #  - "80:80"
    environment:
      - PMA_HOST=db
    restart: unless-stopped
    depends_on:
      - db
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - /etc/timezone:/etc/timezone:ro
      - ./php/php.ini:/usr/local/etc/php/php.ini
      - phpmyadmin:/var/www/html


  portainer:
    image: portainer/portainer-ce:alpine
    container_name: portainer
    restart: unless-stopped
    #ports:
    #  - "9000:9000"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - ./data/portainer_data:/data
      - /etc/localtime:/etc/localtime:ro
      - /etc/timezone:/etc/timezone:ro


  mail.example.com:
    image: tvial/docker-mailserver:latest
    hostname: mail.example.com
    domainname: example.com
    container_name: mail.example.com
    ports:
      - "25:25"   # SMTP SEND
      #- "110:110" # POP3 RECEIVE
      #- "143:143" # IMAP RECEIVE
      - "465:465" # ESMTP Enforced Encryption Submission
      #- "587:587" # ESMTP SEND
      - "993:993" # IMAP Enforced Encryption Retrieval
      #- "995:995" # POP3 Enforced Encryption Retrieval POP3가 필요하면 제일 앞에 주석을 해제하세요.
    volumes:
      - ./mail/maildata:/var/mail
      - ./mail/mailstate:/var/mail-state
      - ./mail/maillogs:/var/log/mail
      - ./mail/config/:/tmp/docker-mailserver/
      - ./ssl/:/tmp/ssl:ro  # Certificate location
      - /etc/localtime:/etc/localtime:ro
      - /etc/timezone:/etc/timezone:ro
    environment:
      #- mail.env
      #- env-mailserver
      - SSL_TYPE=manual
      - SSL_CERT_PATH=/tmp/ssl/live/mail.example.com/fullchain.pem # Certificate location
      - SSL_KEY_PATH=/tmp/ssl/live/mail.example.com/privkey.pem # Certificate location
      - ENABLE_SPAMASSASSIN=1
      - SPAMASSASSIN_SPAM_TO_INBOX=1
      #- ENABLE_CLAMAV=1  # 바이러스 검사를 원하면 주석을 해제하세요.
      #- ENABLE_FAIL2BAN=1  # Fail2ban을 활성화하려면 주석을 해제하세요.
      - ENABLE_POSTGREY=1
      #- ENABLE_POP3=1  # POP3 접속을 원하면 주석을 해제하세요.
      - ONE_DIR=1
      - DMS_DEBUG=0
      - PERMIT_DOCKER=connected-networks
    cap_add:

      - NET_ADMIN
      - SYS_PTRACE
    restart: always
      
volumes:
  phpmyadmin:



docker-wordpress



version: '3'


services:


  db:
    image: mariadb:latest
    container_name: db
    restart: unless-stopped
    env_file: .env
    environment:
      - TZ=Asia/Seoul
    volumes:
      - ./data/dbdata:/var/lib/mysql


  redis:
    container_name: redis
    image: redis:alpine
    restart: unless-stopped
    volumes:
      - ./data/dataredis:/data
      - /etc/localtime:/etc/localtime:ro
      - /etc/timezone:/etc/timezone:ro
    command: redis-server --maxmemory 256mb --maxmemory-policy allkeys-lru --appendonly yes
  
  certbot:
    image: certbot/certbot:latest
    container_name: certbot
    volumes:
      - ./ssl/:/etc/letsencrypt/
      - /usr/share/nginx/html:/usr/share/nginx/html
    command: certonly -n --webroot -w /usr/share/nginx/html -d example.com -d www.example.com -d port.example.com -d pma.example.com -d mail.example.com


  php:
    depends_on:
      - db
    build:
      context: ./build
    container_name: php
    restart: unless-stopped
    env_file: .env
    volumes:
      - ./site:/var/www/web
      - ./php/php.ini:/usr/local/etc/php/php.ini
      - /etc/localtime:/etc/localtime:ro
      - /etc/timezone:/etc/timezone:ro


  nginx:
    depends_on:
      - php
    image: nginx:alpine
    container_name: nginx
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - phpmyadmin:/var/www/html
      - ./ssl/:/etc/letsencrypt/
      - ./site:/var/www/web
      - ./nginx/conf.d:/etc/nginx/conf.d
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf
      - ./nginx/logs:/var/log/nginx/
      - /usr/share/nginx/html:/usr/share/nginx/html
      - /etc/localtime:/etc/localtime:ro
      - /etc/timezone:/etc/timezone:ro


  phpmyadmin:
    image: phpmyadmin:fpm-alpine
    container_name: phpmyadmin
    #ports:
    #  - "80:80"
    environment:
      - PMA_HOST=db
    restart: unless-stopped
    depends_on:
      - db
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - /etc/timezone:/etc/timezone:ro
      - ./php/php.ini:/usr/local/etc/php/php.ini
      - phpmyadmin:/var/www/html


  portainer:
    image: portainer/portainer-ce:alpine
    container_name: portainer
    restart: unless-stopped
    #ports:
    #  - "9000:9000"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - ./data/portainer_data:/data
      - /etc/localtime:/etc/localtime:ro
      - /etc/timezone:/etc/timezone:ro


  mail.example.com:
    image: tvial/docker-mailserver:latest
    hostname: mail.example.com
    domainname: example.com
    container_name: mail.example.com
    ports:
      - "25:25"   # SMTP SEND
      #- "110:110" # POP3 RECEIVE
      #- "143:143" # IMAP RECEIVE
      - "465:465" # ESMTP Enforced Encryption Submission
      #- "587:587" # ESMTP SEND
      - "993:993" # IMAP Enforced Encryption Retrieval
      #- "995:995" # POP3 Enforced Encryption Retrieval POP3가 필요하면 제일 앞에 주석을 해제하세요.
    volumes:
      - ./mail/maildata:/var/mail
      - ./mail/mailstate:/var/mail-state
      - ./mail/maillogs:/var/log/mail
      - ./mail/config/:/tmp/docker-mailserver/
      - ./ssl/:/tmp/ssl:ro  # Certificate location
      - /etc/localtime:/etc/localtime:ro
      - /etc/timezone:/etc/timezone:ro
    environment:
      #- mail.env
      #- env-mailserver
      - SSL_TYPE=manual
      - SSL_CERT_PATH=/tmp/ssl/live/mail.example.com/fullchain.pem # Certificate location
      - SSL_KEY_PATH=/tmp/ssl/live/mail.example.com/privkey.pem # Certificate location
      - ENABLE_SPAMASSASSIN=1
      - SPAMASSASSIN_SPAM_TO_INBOX=1
      #- ENABLE_CLAMAV=1  # 바이러스 검사를 원하면 주석을 해제하세요.
      #- ENABLE_FAIL2BAN=1  # Fail2ban을 활성화하려면 주석을 해제하세요.
      - ENABLE_POSTGREY=1
      #- ENABLE_POP3=1  # POP3 접속을 원하면 주석을 해제하세요.
      - ONE_DIR=1
      - DMS_DEBUG=0
      - PERMIT_DOCKER=connected-networks
    cap_add:

      - NET_ADMIN
      - SYS_PTRACE
    restart: always
      
volumes:
  phpmyadmin:



docker-multi-site



version: '3'


services:


  db:
    image: mariadb:latest
    container_name: db
    restart: unless-stopped
    env_file: .env
    environment:
      - TZ=Asia/Seoul
    volumes:
      - ./data/dbdata:/var/lib/mysql
      - ./db:/docker-entrypoint-initdb.d


  redis:
    container_name: redis
    image: redis:alpine
    restart: unless-stopped
    volumes:
      - ./data/dataredis:/data
      - /etc/localtime:/etc/localtime:ro
      - /etc/timezone:/etc/timezone:ro
    command: redis-server --maxmemory 256mb --maxmemory-policy allkeys-lru --appendonly yes


  smtp:
    image: namshi/smtp
    container_name: smtp_relay
    restart: always
    env_file: .env


  php:
    depends_on:
      - db
    build:
      context: ./build
    container_name: php
    restart: unless-stopped
    env_file: .env
    volumes:
      - ./site:/var/www/html
      - ./php/php.ini:/usr/local/etc/php/php.ini
      - /etc/localtime:/etc/localtime:ro
      - /etc/timezone:/etc/timezone:ro


  nginx:
    depends_on:
      - php
    image: nginx:alpine
    container_name: nginx
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./site:/var/www/html
      - ./nginx/conf.d:/etc/nginx/conf.d
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf
      - ./nginx/logs:/var/log/nginx/
      - /etc/localtime:/etc/localtime:ro
      - /etc/timezone:/etc/timezone:ro


  acme.sh:
    image: neilpang/acme.sh
    container_name: acme.sh
    env_file: .env
    volumes:
      - ./data/acme.sh:/acme.sh
      - ./site:/var/www/html
      - /etc/localtime:/etc/localtime:ro
      - /etc/timezone:/etc/timezone:ro
    command: acme.sh --cron --home "/acme.sh"


  phpmyadmin:
    image: phpmyadmin
    container_name: phpmyadmin
    #ports:
    #  - "80:80"
    environment:
      - PMA_HOST=db
    restart: always
    depends_on:
      - db
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - /etc/timezone:/etc/timezone:ro


  portainer:
    image: portainer/portainer-ce:alpine
    container_name: portainer
    restart: always
    #ports:
    #  - "9000:9000"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - ./data/portainer_data:/data
      - /etc/localtime:/etc/localtime:ro
      - /etc/timezone:/etc/timezone:ro


  mail.example.com:
    image: tvial/docker-mailserver:latest
    hostname: mail.example.com
    domainname: example.com
    container_name: mail.example.com
    ports:
      - "25:25"   # SMTP SEND
      #- "110:110" # POP3 RECEIVE
      #- "143:143" # IMAP RECEIVE
      - "465:465" # ESMTP Enforced Encryption Submission
      #- "587:587" # ESMTP SEND
      - "993:993" # IMAP Enforced Encryption Retrieval
      #- "995:995" # POP3 Enforced Encryption Retrieval POP3가 필요하면 제일 앞에 주석을 해제하세요.
    volumes:
      - ./mail/maildata:/var/mail
      - ./mail/mailstate:/var/mail-state
      - ./mail/maillogs:/var/log/mail
      - ./mail/config/:/tmp/docker-mailserver/
      - ./site/ssl:/tmp/ssl:ro  # Certificate location
      - /etc/localtime:/etc/localtime:ro
      - /etc/timezone:/etc/timezone:ro
    environment:
      #- mail.env
      #- env-mailserver
      - SSL_TYPE=manual
      - SSL_CERT_PATH=/tmp/ssl/fullchain.pem # Certificate location
      - SSL_KEY_PATH=/tmp/ssl/privkey.pem # Certificate location
      - ENABLE_SPAMASSASSIN=1
      - SPAMASSASSIN_SPAM_TO_INBOX=1
      #- ENABLE_CLAMAV=1  # 바이러스 검사를 원하면 주석을 해제하세요.
      #- ENABLE_FAIL2BAN=1  # Fail2ban을 활성화하려면 주석을 해제하세요.
      - ENABLE_POSTGREY=1
      #- ENABLE_POP3=1  # POP3 접속을 원하면 주석을 해제하세요.
      - ONE_DIR=1
      - DMS_DEBUG=0
      - PERMIT_DOCKER=connected-networks
    cap_add:

      - NET_ADMIN
      - SYS_PTRACE
    restart: always



도커 권한 설정


root의 경우 권한 설정을 할 필요가 없지만 일반 계정이라면 docker를 바로 실행할 수 있게 권한을 주는 것이 좋습니다.



sudo usermod -aG docker $USER


위 명령어로 일반 계정이 docker 명령어를 sudo 없이 바로 실행할 수 있게 해줍니다.


SSH 창을 끄고 다시 접속하면, sudo 없이 바로 docker나 docker-compose 명령어를 쓸 수 있습니다.



실행하기



docker-compose up -d mail.example.com


mail.example.com 를 자신의 메일서버 이름으로 바꿉니다.


위 명령어로 docker-mailserver 이미지를 다운 받고, 실행합니다.



계정생성하기



위 명령어로 셋업 스크립트를 다운 받습니다.


이제 계정을 생성해야겠죠?



./setup.sh email add admin@example.com mypassword


위와 같이 admin@example.com 대신 자신이 원하는 이메일 계정, mypassword 대신 자신이 원하는 비번을 넣고 실행합니다.


지금 만든 아이디와 비번이 마지막에 적용 예시에 들어갈 아이디 비번이라고 보면 됩니다.


계정 생성이 끝났습니다.



DKIM 생성하기



./setup.sh config dkim


위 명령어로 생성합니다.



$ ./setup.sh config dkim
Creating DKIM private key /tmp/docker-mailserver/opendkim/keys/example.com/mail.private
Creating DKIM KeyTable
Creating DKIM SigningTable
Creating DKIM TrustedHosts


위와 같이 생성됩니다. 정말 쉽죠?


./mail/config/opendkim/keys/example.com/mail.txt


위 파일에 우리가 원하는 DKIM 설정 내용이 들어있습니다.


해당 경로에 가서 mail.txt 파일을 열어보거나



sudo cat ./mail/config/opendkim/keys/example.com/mail.txt


위 명령어로 출력하면



mail._domainkey	IN	TXT	( "v=DKIM1; h=sha256; k=rsa; "
	  "p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvD0NKCyd0/7C25kQ75/cY+upZzGlxNiY8GMELiUoBox9FFaxuGZMImrJVQW/tGt9fQgEyKobQTZjqguEcp4Yg4e9GRFCcSKBOWyvZqjUpl3+cY/csov89kD98V4BUeVQ/GdDYt81HV7IuYlJ57JyS/idYyKe9WuvCmnZMQJvfCjZEBtk2+OIeQqJHWVKILyIEdpq3PzeFm1V0p"
	  "t0D8wpGPoBD6aE2FQkQTpZN5APcmAIHAO6RVnjo1ihGujh2aiuRGI7ECXBFIo8os9G85WnFJVUdkjD55x1ZLLNeo6WmQnd3JteuarcLVFjWOCSKPVmcS8/YorZrbR7qleDEnD/6QIDAQAB" )  ; ----- DKIM key mail for example.com


위와 같이 엄청나게 긴 내용이 나옵니다.


이제 DNS 설정에 들어갈 내용으로 변환할 것입니다. 쌍따옴표를 제거하면 된다고 보면 되는데요.



"p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvD0NKCyd0/7C25kQ75/cY+upZzGlxNiY8GMELiUoBox9FFaxuGZMImrJVQW/tGt9fQgEyKobQTZjqguEcp4Yg4e9GRFCcSKBOWyvZqjUpl3+cY/csov89kD98V4BUeVQ/GdDYt81HV7IuYlJ57JyS/idYyKe9WuvCmnZMQJvfCjZEBtk2+OIeQqJHWVKILyIEdpq3PzeFm1V0p"
	  "t0D8wpGPoBD6aE2FQkQTpZN5APcmAIHAO6RVnjo1ihGujh2aiuRGI7ECXBFIo8os9G85WnFJVUdkjD55x1ZLLNeo6WmQnd3JteuarcLVFjWOCSKPVmcS8/YorZrbR7qleDEnD/6QIDAQAB"


특히 위 부분이 나눠진게 아니라 합쳐줘야 됩니다. 따라서 최종 완성본은



v=DKIM1; h=sha256; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvD0NKCyd0/7C25kQ75/cY+upZzGlxNiY8GMELiUoBox9FFaxuGZMImrJVQW/tGt9fQgEyKobQTZjqguEcp4Yg4e9GRFCcSKBOWyvZqjUpl3+cY/csov89kD98V4BUeVQ/GdDYt81HV7IuYlJ57JyS/idYyKe9WuvCmnZMQJvfCjZEBtk2+OIeQqJHWVKILyIEdpq3PzeFm1V0pt0D8wpGPoBD6aE2FQkQTpZN5APcmAIHAO6RVnjo1ihGujh2aiuRGI7ECXBFIo8os9G85WnFJVUdkjD55x1ZLLNeo6WmQnd3JteuarcLVFjWOCSKPVmcS8/YorZrbR7qleDEnD/6QIDAQAB


위와 같이 한줄로 만들면 됩니다. 메모장에 저장해두세요. DNS설정에 꼭 들어가야 됩니다.


이제 도커에서 작업은 끝났습니다.




sudo docker-compose up -d --force-recreate mail.example.com


mail.example.com 를 자신의 메일서버 이름으로 바꿉니다.


위 명령어로 재생성해줍니다.


이제 서버에서 메일서버가 구동되기 시작했습니다.


혹시 portainer에서 log를 봤을 때 dovecot 로그가 지속적으로 찍힌다면 SSL 인증서 경로가 잘못된 것이니 꼭 수정해주세요.



DNS 설정하기


이제 DNS 설정을 해줍니다.


총 5개를 입력할 것입니다.



1. Type : A, Name : mail, Content : 서버IP주소
2. Type : MX, Name : @(host), Content : mail.example.com, Priority : 10
3. Type : TXT, Name : @(host), Content : v=spf1 mx ~all
4. Type : TXT, Name : _dmarc, Content : v=DMARC1; p=none; rua=mailto:admin@example.com; ruf=mailto:admin@example.com; sp=none; ri=86400
5. Type : TXT, Name : mail._domainkey, Content : v=DKIM1; h=sha256; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvD0NKCyd0/7C25kQ75/cY+upZzGlxNiY8GMELiUoBox9FFaxuGZMImrJVQW/tGt9fQgEyKobQTZjqguEcp4Yg4e9GRFCcSKBOWyvZqjUpl3+cY/csov89kD98V4BUeVQ/GdDYt81HV7IuYlJ57JyS/idYyKe9WuvCmnZMQJvfCjZEBtk2+OIeQqJHWVKILyIEdpq3PzeFm1V0pt0D8wpGPoBD6aE2FQkQTpZN5APcmAIHAO6RVnjo1ihGujh2aiuRGI7ECXBFIo8os9G85WnFJVUdkjD55x1ZLLNeo6WmQnd3JteuarcLVFjWOCSKPVmcS8/YorZrbR7qleDEnD/6QIDAQAB


위와 같이 5개를 넣어주면 됩니다.


참고로 @(host)는 클라우드플레어에서는 @를 넣으면 host가 되고, luadns에서는 비워둬야 hostname이 됩니다.


1번은 mail.example.com이 서버의 IP를 가리키게 하는 것입니다. 참고로 클라우드플레어 사용시 DNS only로 지정해야 됩니다. 서버 IP 노출은 어쩔 수 없습니다.

2번은 MX 레코드를 지정하는 것입니다.

3번은 SPF 설정입니다. MX레코드에 지정된 도메인의 IP를 찾아서 검증하는 것입니다.

4번은 DMARC 설정입니다. 스팸이메일 처리에 대한 규칙을 적은 것입니다. none은 아무것도 안하는 것이고, mailto가 2개 있는데 보고서를 받는 이메일을 넣으면 됩니다. https://samsikworld.tistory.com/488 링크를 참조하세요.

5번은 DKIM 설정입니다. 아까 생성했던 그 DKIM을 입력하면 됩니다. 제가 넣은 것과 똑같이 넣으면 절대로 안되고 자신에게 맞는 값을 넣어야 됩니다.



rDNS, 리버스 DNS, 리버스 도메인, PTR 레코드 설정하기


리버스 도메인이란 일반적인 도메인 조회가 영문 주소(도메인 네임)를 DNS에 질의하여 IP 주소를 찾는 것과 반대로, IP 주소를 사용하여 도메인 네임을 찾는 것을 의미합니다. (나무위키 펌)


구글, 다음 메일은 리버스 DNS를 설정하지 않아도 SPF, DMARC, DKIM 작업만 하면 정상 메일로 인식합니다. (네이버 메일은 공지에는 스팸처리한다고 합니다. 저는 스팸처리 안되고 정상 메일처리되더군요 ㅎㅎ)


하지만 카카오메일, 프로톤메일 등 몇몇 업체들은 리버스 DNS 설정을 하지 않으면 바로 스팸메일로 분류합니다.


AWS : https://aws.amazon.com/ko/premiumsupport/knowledge-center/ec2-port-25-throttle


Azure : https://docs.microsoft.com/ko-kr/azure/virtual-network/troubleshoot-outbound-smtp-connectivity 


오라클 클라우드 : https://docs.cloud.oracle.com/en-us/iaas/Content/DNS/Tasks/reversedns.htm


구글 클라우드 : https://cloud.google.com/compute/docs/instances/create-ptr-record


KT : https://dms.kornet.net/reverse


나무 위키 리버스 도메인 설명 : https://namu.wiki/w/리버스 도메인


리버스 도메인 설정방법은 각 IP 제공자에 따라 다르므로 호스팅이나 클라우드 사업자에게 직접 요청하거나 설정해야 합니다.


그리고 KISA 불법스팸대응센터에 화이트 도메인 신청을 해야 국내업체의 스팸처리 확률이 줄어듭니다.


화이트도메인 등록 신청하기 : https://spam.kisa.or.kr/white/sub4.do


이제 DNS 설정, rDNS 설정, 화이트도메인 신청이 끝났습니다.



각 프로그램 별 설정 방법


대부분의 프로그램 및 어플에서 IMAP 방식의 접속을 지원합니다.


우리가 설정했던 IMAP SSL/TLS 방식과 완벽 호환되므로, 그 방식으로 설정하면 됩니다.


그누보드 설정


./config.php 173번째줄



// SMTP
// lib/mailer.lib.php 에서 사용
define('G5_SMTP',      'mail.example.com');
define('G5_SMTP_PORT', '25');


위와 같이 변경하기



라이믹스 설정



발송방법 : SMTP
SMTP 서버 : mail.example.com
SMTP 포트 : 465
SMTP 보안 : SSL
아이디 : admin@example.com
비번 : 설정한 비밀번호


이러면 끝



워드프레스


Post SMTP Mailer/Email Log 플러그인 기준


https://wordpress.org/plugins/post-smtp/



Transport


Type : SMTP
Mailer Type : PostSMTP


Transport Settings


Outgoing Mail Server Hostname : mail.example.com
Outgoing Mail Server Port : 465
Envelope-From Email Address : admin@example.com
Security : SMTPS
Authentication : Plain


Authentication
Username : admin@example.com
Password : 설정한 비밀번호


아웃룩


계정 넣고 고급 옵션을 누른 후 "내 계정을 수동으로 설정합니다." 체크 후 연결 클릭, IMAP 선택하기


받는 메일



사용자이름 : admin@example.com
비번 : 설정한 비밀번호


서버 : mail.example.com
포트: 993
암호화방법 : SSL/TLS
SPA(보안 암호 인증)를 사용한 로그인 필요 체크 해제


보내는 메일



서버 : mail.example.com
포트 : 465
암호화방법 : SSL/TLS
SPA(보안 암호 인증)를 사용한 로그온 필요 : 체크 해제
발신(SMTP) 서버에 인증필요 체크 : 받는 메일 서버와 동일한 설정 사용


구글 안드로이드 지메일 설정하기


수신설정



사용자이름 admin@example.com
비번 : 설정한 비밀번호
서버 : mail.example.com
포트 : 993
보안유형 : SSL/TLS


발신설정



사용자이름 : admin@example.com
비번 : 설정한 비밀번호
SMTP 서버 : mail.example.com
포트 : 465
보안유형 : SSL/TLS



참고로 POP3 방식으로 접속하려면 받는 서버를 995번으로 설정하고 보안유형에 SSL/TLS로 설정하면 됩니다.



참고 사이트


도커 메일서버 : https://github.com/tomav/docker-mailserver 


sfuhost에서 메일서버 셋팅방법 : https://sfuhost.com/manual/700380 


구글클라우드 메일서버 셋팅방법 : https://medium.com/minds-in-the-cloud/spin-off-an-email-server-with-containers-using-docker-compose-on-google-cloud-platform-debian-9-aa0fe8bf3d88 


DMARC 개념 정리 : https://samsikworld.tistory.com/488 


Opening Mail Server : https://blog.0chan.dev/2020-06-16-Opening-Mail-Server/ 

관련자료

댓글 4 / 1 페이지

ancine님의 댓글

관련내용을 잘아시는것 같아 염치불구 질문하나 드립니다.

저는 모든문서를 에버노트에 업로드해두는데 에버노트계정의 이메일에 문서를 보내면 에버노트 특정폴더에 업로드가 됩니다.
그래서 복합기에서 "스캔후 이메일보내기" 기능을 이용해서  스캔후 바로 복합기에서 이메일을 발송하고 싶은데 가능한 방법이 있는지 여쭤봅니다.

현재환경은 삼성K3300NR 복합기, IPTIME nas2dual(smtp미지원)을 사용중인데 복합기에 smtp서버명만 넣어주면 되는데 사무실에서는 서버가 없고 작은 nas만 사용중인데  혹시  특별한 하드웨어 없이 가능한건지 여쭤봅니다. 감사합니다.

우성짱님의 댓글의 댓글

SMTP 서버를 구성해야 합니다. NAS2DUAL에 해당 기능이 없다면 힘들 것으로 보입니다.
오라클 클라우드에 도커 메일서버를 열고 해당 정보로 SMTP를 넣으면 가능하지만 공부해야 될 부분이 많을 것 같습니다.
수량이 적다면 https://www.mailgun.com 에 계정 생성 후 편하게 쓸 수도 있겠네요.
전체 33 / 1 페이지
RSS

최근글


새댓글


알림 0