CMTS info

Helpful linux and DOCSIS/CMTS howtos and tips

Installing Exim

1) Instalacja PAM

Instalacja PAM przebiega standardowo, źródło należy pobrać z http://www.kernel.org/pub/linux/libs/pam/library/ Po skompilowaniu i zainstalowaniu tworzymy katalog /etc/pam.d – tam znajdować się będą pliki konfigurujące poszczególne usługi. Aby PAM działał poprawnie musi być też plik domyślny – dla niezdefiniowanych usług - other. Tworzymy więc pliki: other i exim.

/etc/pam.d/other

auth     required       pam_deny.so
account  required       pam_deny.so
password required       pam_deny.so
session  required       pam_deny.so

/etc/pam.d/exim

auth            required       pam_unix.so
account         required       pam_unix.so
session         required       pam_permit.so

To wszystko - PAM powinien działać.

2) Instalacja mysql – opisana gdzie indziej
3) Instalacja libspf

Do instalacji wystarczy rozpakować, skompilować i zainstalować bibliotekę. Tzn: ./configure, make, make install

4) Instalacja Spamassassina

Instalować można dwojako: poprzez źródła dostępne na stronie albo przez cpan. Łatwiej zainstalować jest go przez CPAN

cpan Mail::SpamAssassin 

Na pewno wyświetli się wiele złamanych zależności i cześć pakietów trzeba będzie doinstalować ręcznie ( nie wszystkie testy będą chciały przejść itp.)

Potem należy stworzyć katalog /.spamassassin i nadać zmienić właściciela na nobody:nogroup

Spamassassina uruchamia się za pomocą polecenia spamd

5)Instalacja exima

Ściągamy źródła z http://exim.org/ Następnie kopiujemy plik przykładowy src/EDITME do Local/Makefile.

Do naszej instalacji plik należy następująco zmodyfikować:

a. EXIM_USER oraz EXIM_GROUP – odpowiada za użytkowników na jakich będzie działał demon exima – należy później ręcznie utworzyć tego użytkownika i grupę. Ważne: użytkownik exima musi należeć (też) do grupy mającej prawo odczytu pliku shadow – jeśli PAM dla exima ustawiony jest na pam_unix.so.
b. Odhashować SUPPORT_MAILDIR – format pliku(katalogu) z pocztą użytkownika.
c. Odhashować LOOKUP_MYSQL – obsługa MySQL – przyda się do greylistingu
d.Odhashować WITH_CONTENT_SCAN – obsługa skanera spamu / antywirusa
e.Odhashować AUTH_PLAINTEXT – obsługa uwierzytelniania w otwartym tekście, w połączeniu z TLS jest to dobre rozwiązanie.
f.Odhashować SUPPORT_TLS oraz TLS_LIBS
g.Odhashować EXPERIMENTAL_SPF=yes, CFLAGS += -I/usr/local/include/spf2 oraz LDFLAGS += -lspf2

Teraz można skompilować program – wykonujemy:

./configure && make && make install

Jeśli przy kompilacji pokazują się błędy o brakach plików bibliotek należy odpowiednio pododawać katalogi zawierające pliki nagłówkowe do zmiennych include w pliku Makefile.

6) Konfiguracja środowiska pracy Exima

Po zainstalowaniu należy ustawić zmienna PATH do binarek exima: wykonuje się to ręcznie dopisując do plików login.defs lub profile. W pliku login.defs można jeszcze ustawić katalog z pocztą dla użytkowników z kontami shellowymi – linia „MAIL_DIR Maildir/” , gdzie Maildir to nazwa katalogu z pocztą.

Następnie należny dodać bazy do greylistingu w MySQLu:

1. Bazę dla greylistingu

CREATE DATABASE nazwa_bazy;

2. Tablicę greylisty

CREATE TABLE exim_greylist (
  id integer NOT NULL auto_increment,
  relay_ip varchar(64),
  from_domain varchar(255),
  block_expires datetime NOT NULL,
  record_expires datetime NOT NULL,
  create_time datetime NOT NULL,
  PRIMARY KEY (id)
); 

3. tablice whitelisty

 CREATE TABLE exim_whitelist (
  id integer NOT NULL auto_increment,
  relay_ip varchar(64),
  from_domain varchar(255),
  create_time datetime NOT NULL,
  PRIMARY KEY (id)
);
7) Plik konfiguracyjny Exima

Główny plik konfiguracyjny znajduje się w /usr/exim/configure.

Na początku pliku są makra do greylistingu:

1. GREYLIST_TEST sprawdza czy okres greylistingu upłynął oraz czy wpis nie wygasł. Zwraca: 0 – nie ma na greyliście, 1 – jeszcze jest blokowany, 2 – czas greylistowania dobiegł końca – można pozwolić na wysyłkę poczty.

GREYLIST_TEST = SELECT CASE WHEN now() - block_expires > 0 THEN 2 ELSE 1\
END \
FROM exim_greylist WHERE relay_ip='${quote_mysql:$sender_host_address}'\
AND from_domain = '${quote_mysql:$sender_address_domain}'

2. WHITELIST_TEST sprawdza czy damy wpis nie znalazł się już na whiteliście. Wpisy w whiteliście nie wygasają, są kasowane za pomocą skryptu shellowego. Zwraca: 0 – brak wpisu, 1 - istnieje wpis.

WHITELIST_TEST = SELECT CASE WHEN id > 0 THEN 1 ELSE 0 END \
FROM exim_whitelist WHERE relay_ip='${quote_mysql:$sender_host_address}'\
AND from_domain = '${quote_mysql:$sender_address_domain}';

3. GREYLIST_ADD - dodaje domenę oraz IP do greylistowania.

GREYLIST_ADD = INSERT INTO exim_greylist (relay_ip, from_domain, \
block_expires, record_expires, create_time) \
VALUES ( '${quote_mysql:$sender_host_address}', \
'${quote_mysql:$sender_address_domain}', \
 DATE_ADD(now(), INTERVAL 10 MINUTE), \
 DATE_ADD(now(), INTERVAL 3 DAY), \
 now() \
 )

4. WHITELIST_ADD - dodaje domenę oraz IP jako zaufane - do whitelisty.

WHITELIST_ADD = INSERT INTO exim_whitelist(relay_ip, from_domain,\
create_time)VALUES( '${quote_mysql:$sender_host_address}',\
'${quote_mysql:$sender_address_domain}', now());

5. GREYLIST_REMOVE – usuwa wpis z greylisty (aby go dodać do whitelisty)

GREYLIST_REMOVE = DELETE FROM exim_greylist \
WHERE '${quote_mysql:$sender_host_address}' = relay_ip \
AND '${quote_mysql:$sender_address_domain}' = from_domain;

Następnie należy ustawić parametry bazy dla exima:

hide mysql_servers = adres_servera/nazwa_bazy/nazwa_użytkownika/hasło
6) Główne ustawienia

1.Globalne zmienne

Definicja głównej domeny:

primary_hostname = nazwa_domeny

Definicja domen dodatkowych:

domainlist local_domains = @:domena2:domena3

Hosty z których wyślemy pocztę gdziekolwiek, prawie bezwarunkowo:

relay_from_hosts = 127.0.0.1 

Pozwala wszystkim używać TLS:

tls_advertise_hosts = * 

Zwiększenie wydajności przy dużej ilości poczty:

split_spool_directory = true

2.Ustawienia greylistingu w acl_check_rcpt:

Ludziom z whitelisty pozwalamy wysyłać pocztę:

warn set acl_m3 = ${lookup mysql{WHITELIST_TEST}{$value}{0}}
accept
	condition = ${if eq{$acl_m3}{1}{1}}

Tak samo jeśli istnieje poprawny wpis SPF dla domeny:

accept
  spf = pass

Ludzie podszywający się są blokowani:

deny 
  message = SPF check FAILED for $sender_address_domain
  spf = fail

Sprawdzamy greylistę:

warn set acl_m2 = ${lookup mysql{GREYLIST_TEST}{$value}{0}}
  #case 1 – nowa poczta, wpisujemy na greyliste
  defer
    message = Greylisted - please try again a little later.
    condition = ${if eq{$acl_m2}{0}{1}}
    condition = ${lookup mysql{GREYLIST_ADD}{yes}{no}}

  #case 2 - nowy mail jeszcze greylistowany
  defer
    message = Greylisted - please try again shortly.
    condition = ${if eq{$acl_m2}{1}{1}}

  #case 3 - greylistowanie dobiegło końca, wpisujemy na whiteliste
  accept
    condition = ${if eq{$acl_m2}{2}{1}}
    condition = ${lookup mysql{WHITELIST_ADD}{yes}{no}}
    condition = ${lookup mysql{GREYLIST_REMOVE}{yes}{no}}

3.Sprawdzanie zawartości maili w acl_check_data.

Sprawdzanie wielkości maila:

deny message = message too big: $message_size bytes
    condition = ${if > {$message_size}{10M}}

Sprawdzanie przez spamassassina:

#więcej niż 10pkt
deny  
	message = This message scored $spam_score spam points.
	condition = ${if < {$message_size}{100K}}
	spam = nobody
	condition = ${if >{$spam_score_int}{100}{1}{0}}
#5-10pkt
warn
	condition = ${if < {$message_size}{100K}}
	condition = ${if >{$spam_score_int}{50}{1}{0}}
	add_header = X-Spam_score: $spam_score\n\
	X-Spam_score_int: $spam_score_int\n\
	X-Spam_bar: $spam_bar

4.Ustawienia skrzynki pocztowej.

Ustawienie dla formatu Maildir, w przypadku braku katalogu jest tworzony automatycznie.

local_delivery:
  driver = appendfile
  maildir_format = true
  directory = /home/users/$local_part\
      /Maildir/${if eq{$local_part_suffix}{}{}\
      {/.${substr_1:$local_part_suffix}}}
  maildirfolder_create_regex = /\.[^/]+$
  use_fcntl_lock = true

5.Uwierzytelnianie PAM.

LOGIN:
    driver                     = plaintext
    server_set_id              = $auth1
    server_prompts             = <| Username: | Password:
    server_condition = ${if pam{$auth1:$auth2}{1}{0}}
    server_advertise_condition = yes
8) Dodatkowe skrypty

1.Skrypt bashowy usuwający nadmiarowe wpisy w whiteliście:

mysql -u nazwa_użytkownika --password=hasło nazwa_bazy < db_cl.sql

db_cl.sql

DELETE from exim_whitelist \
where from_domain not like '%.pl' \
and from_domain not like '%.com' \
and from_domain not like '%.org';

Skrypt uruchamiany jest raz na miesiąc (umieszczony w /etc/cron.monthly), wykonuje polecenie SQL z pliku db_cl.sql. Polecenie kasuje wszystkie wpisy z domen nie będących domenami pl, com, .org.

2.Skrypt bashowy usuwający wygasłe wpisy z greylisty:

mysql -u nazwa_użytkownika --password=hasło nazwa_baz> < db_cl.sql

db_cl.sql:

DELETE FROM exim_greylist WHERE record_expires < now();

Skrypt uruchamiany jest raz dziennie (umieszcozny w /etc/cron.daily), wykonuje polecenie SQL z pliku db_cl.sql. Polecenie kasuje wszystkie wpisy z domen, gdzie czas ważności wpisu wygasł.

9) Kompletny plik konfiguracyjny
  GREYLIST_TEST = SELECT CASE \
     WHEN now() - block_expires > 0 THEN 2 \
     ELSE 1 \
     END \
     FROM exim_greylist \
     WHERE relay_ip = '${quote_mysql:$sender_host_address}' \
     AND from_domain = '${quote_mysql:$sender_address_domain}'

  GREYLIST_ADD = INSERT INTO exim_greylist (relay_ip, from_domain, \
     block_expires, record_expires, create_time) \
     VALUES ( '${quote_mysql:$sender_host_address}', \
     '${quote_mysql:$sender_address_domain}', \
     DATE_ADD(now(), INTERVAL 10 MINUTE), \
     DATE_ADD(now(), INTERVAL 3 DAY), \
     now() \
     )

 hide mysql_servers = adres_servera/baza/nazwa_usera/haslo

WHITELIST_TEST = SELECT CASE WHEN id > 0 THEN 1 ELSE 0 END \
  FROM exim_whitelist \
  WHERE relay_ip = '${quote_mysql:$sender_host_address}'\
  AND from_domain = '${quote_mysql:$sender_address_domain}';

WHITELIST_ADD = INSERT INTO exim_whitelist(relay_ip, from_domain,\
  create_time) VALUES( '${quote_mysql:$sender_host_address}',\
  '${quote_mysql:$sender_address_domain}', now());

GREYLIST_REMOVE = DELETE FROM exim_greylist \
  WHERE '${quote_mysql:$sender_host_address}' \
  =relay_ip AND '${quote_mysql:$sender_address_domain}' = from_domain;

######################################################################
#                    MAIN CONFIGURATION SETTINGS                 
######################################################################
primary_hostname =domena.pl
domainlist local_domains = @:costam.pl
domainlist relay_to_domains =
hostlist   relay_from_hosts = 127.0.0.1
acl_smtp_rcpt = acl_check_rcpt
acl_smtp_data = acl_check_data
spamd_address = 127.0.0.1 783
tls_advertise_hosts = *
tls_certificate = /sciezka_do_certyfikatu/exim.crt
tls_privatekey = /sciezka_do_klucza_prywatnego/exim.pem
daemon_smtp_ports = 25
never_users = root:nobody
host_lookup = 0
rfc1413_hosts =
rfc1413_query_timeout = 1s
ignore_bounce_errors_after = 1d
timeout_frozen_after = 2d
split_spool_directory = true
######################################################################
#                       ACL CONFIGURATION                           
######################################################################
begin acl
acl_check_rcpt:
   accept  hosts = : 127.0.0.1

  deny    message       = Restricted characters in address
          domains       = +local_domains
          local_parts   = ^[.] : ^.*[@%!/|]

   deny    message       = Restricted characters in address
          domains       = !+local_domains
          local_parts   = ^[./|] : ^.*[@%!] : ^.*/\\.\\./

#tutaj dopisani wybredni
  accept  local_parts   = postmaster : abuse
          domains       = +local_domains

  require verify        = sender

  accept  hosts         = +relay_from_hosts
          control       = submission

  accept  authenticated = *
          control       = submission

  require message = relay not permitted
          domains = +local_domains : +relay_to_domains

  require verify = recipient

#sprawdzamy whiteliste
warn set acl_m3 = ${lookup mysql{WHITELIST_TEST}{$value}{0}}

    #pozwalamy whitelistowanym
    accept
        condition = ${if eq{$acl_m3}{1}{1}}

#sprawdzamy czy ma dobrego SPFa jesli tak to mu pozwalamy
    accept
	spf = pass

#jesli gosciu 100% sie podrabia to go deny
    deny message = SPF check FAILED for $sender_address_domain
	spf = fail

#nie znalazl sie? to moze jest w greyu?
warn set acl_m2 = ${lookup mysql{GREYLIST_TEST}{$value}{0}}

    #case 1 - nowy mail, wpisujemy na greyliste
    defer
	message = Greylisted - please try again a little later.
        condition = ${if eq{$acl_m2}{0}{1}}
	condition = ${lookup mysql{GREYLIST_ADD}{yes}{no}}

    #case 2 - nowy mail jeszcze greylistowany
    defer
	message = Greylisted - please try again shortly.
        condition = ${if eq{$acl_m2}{1}{1}}

    #case 3 - greylisotwanie dobieglo konca, wpisujemy na whiteliste
    accept
        condition = ${if eq{$acl_m2}{2}{1}}
        condition = ${lookup mysql{WHITELIST_ADD}{yes}{no}}
        condition = ${lookup mysql{GREYLIST_REMOVE}{yes}{no}}

  accept

acl_check_data:

    deny message = message too big: $message_size bytes
    condition = ${if > {$message_size}{100M}}

  deny  message = This message scored $spam_score spam points.
        condition = ${if < {$message_size}{100K}}
        spam = nobody
        condition = ${if >{$spam_score_int}{100}{1}{0}}

   warn
   condition = ${if < {$message_size}{100K}}
   condition = ${if >{$spam_score_int}{50}{1}{0}}
           add_header = X-Spam_score: $spam_score\n\
                        X-Spam_score_int: $spam_score_int\n\
                        X-Spam_bar: $spam_bar

                        #X-Spam_bar: $spam_bar\n\
                        #X-Spam_report: $spam_report

  accept



######################################################################
#                      ROUTERS CONFIGURATION                         
######################################################################

begin routers

dnslookup:
  driver = dnslookup
  domains = ! +local_domains
  transport = remote_smtp
  ignore_target_hosts = 0.0.0.0 : 127.0.0.0/8
  no_more

system_aliases:
  driver = redirect
  allow_fail
  allow_defer
  data = ${lookup{$local_part}lsearch{/etc/aliases}}
  file_transport = address_file
  pipe_transport = address_pipe

#AUTO-REPLAY
uservacation:
driver = accept
domains = +local_domains
require_files = /home/$local_part/.vacation
# do not reply to errors or lists
  condition =  ${if or { \
  {match {$h_precedence:} {(?i)junk|bulk|list}} \
  {eq {$sender_address} {}} \
  } {no} {yes}}
# do not reply to errors or bounces or lists
  senders = ! ^.*-request@.*:\
     ! ^bounce-.*@.*:\
     ! ^.*-bounce@.*:\
     ! ^owner-.*@.*:\
     ! ^postmaster@.*:\
     ! ^webmaster@.*:\
     ! ^listmaster@.*:\
     ! ^mailer-daemon@.*:\
     ! ^root@.*
    no_expn
    transport = uservacation_transport
    unseen
    no_verify

userforward:
  driver = redirect
  check_local_user
  file = $home/.forward
  no_verify
  no_expn
  check_ancestor
  file_transport = address_file
  pipe_transport = address_pipe
  reply_transport = address_reply

localuser:
  driver = accept
  check_local_user
  transport = local_delivery
  cannot_route_message = Unknown user

######################################################################
#                      TRANSPORTS CONFIGURATION                       
######################################################################

begin transports

remote_smtp:
  driver = smtp

uservacation_transport:
   driver = autoreply
  file = /home/$local_part/.vacation
  file_expand
  once = /home/$local_part/.vacation.db
 once_repeat = 14d
  from = $local_part@$domain
  to = $sender_address
  subject = "Auto reply: $h_subject"
  headers = Content-Type: text/plain; charset=ISO-8859-2; format=flowed

local_delivery:

  driver = appendfile
#    maildir_use_size_file = true
#    quota = 50M
    maildir_format = true
    directory = /home/$local_part\
      /Maildir/${if eq{$local_part_suffix}{}{}\
      {/.${substr_1:$local_part_suffix}}}
     maildirfolder_create_regex = /\.[^/]+$
    use_fcntl_lock = true

address_pipe:
  driver = pipe
  return_output

address_file:
  driver = appendfile
  delivery_date_add
  envelope_to_add
  return_path_add

address_reply:
  driver = autoreply

######################################################################
#                      RETRY CONFIGURATION                           
######################################################################

begin retry

# Address or Domain    Error       Retries
# -----------------    -----       -------

*                      *           F,2h,10m; G,16h,1h,1.5; F,4d,6h

######################################################################
#                      REWRITE CONFIGURATION                         
######################################################################

begin rewrite

######################################################################
#                   AUTHENTICATION CONFIGURATION                      
######################################################################
begin authenticators

LOGIN:
    driver                     = plaintext
    server_set_id              = $auth1
    server_prompts             = <| Username: | Password:
    server_condition = ${if pam{$auth1:$auth2}{1}{0}}
    server_advertise_condition = yes

Template: designsbydarren.com on license
All trademarks belong to their respective owners. All materials presented here for informational purposes only.