Passerelle antispam

Création d’une passerelle pour serveur de messagerie avec antispam et antivirus

Sur notre réseau nous avons un serveur SBS qui réceptionne les emails arrivant par internet. Ces emails passent par notre passerelle IPCop qui avec l’ajout de l’add-on CopFilter permet un premier filtrage des emails. Seulement la passerelle perd de son efficacité et de sa rapidité en accumulant trop de rôles : Passerelle NAT / firewall / détection intrusion + gardian / proxy web / filtrage http / filtrage email, etc…

Du coup pour faire plus simple j’ai rajouté une machine virtuelle sur Hyper-V qui va servir de relais sur la messagerie ainsi que de filtrage antispam et antivirus. La passerelle redirige le port 25 de l’IP publique directement vers la machine virtuelle, qui a sont tour redirigera les emails vers le serveur SBS / Exchange. De plus on rajoute un stockage sur le réseau local des emails en cas de redémarrage ou de coupure du SBS. cela évite également un échec de dépôt des messages qui pour certain serveur n’es retenté qu’après plusieurs minutes voir une ou deux heures. La passerelle ne traite pas par contre les emails sortant du SBS.

Création de la VM et installation de Ubuntu

Sous hyper-V j’ai simplement créé une nouvelle machine virtuelle avec 1 coeur, 1Go de ram et 6Go de disque dur (j’ai parfois vu 8Go conseillé, pour l’instant ça va…). Je démarre ensuite la machine avec l’image ISO du disque de Ubuntu 12.04 LTS 64bits.

L’installation se fait par défaut. Après l’installation du système de base je rajoute juste les fonctions de OpenSSH server pour pouvoir continuer de travailler avec WinSCP et Putty depuis mon PC.

Configuration du réseau

modification du fichier « /etc/network/interfaces » pour passer en adresse statique. J’en profite pour rajouter l’adresse du serveur DNS, qui est en fait notre passerelle internet qui sert de relais DNS :

auto eth0
iface eth0 inet static
    address 192.168.0.212
    netmask 255.255.0.0
    gateway 192.168.0.210
    dns-nameservers 192.168.0.210

 Installation de Postfix

On va attaquer par le serveur postfix :

sudo apt-get install postfix

l’installateur permet d’aller plus vite pour configurer postfix, mais j’ai par la suite modifié le fichier de configuration pour rajouter des fonctions spécifiques. J’ai donc fait l’installation avec les options suivantes :

- système satellite,
- nom de courrier : mondomaine.fr,
- serveur relais SMTP : [192.168.0.202],  (l'ip du SBS à mettre entre crochet, important)

Puis je suis retourné modifier la configuration dans le fichier de postfix. Au final le fichier « /etc/postfix/main.cf » ressemble à ça :

[...] 
myhostname = x.y.abo.wanadoo.fr ## j'ai noté la résolution DNS de notre IP publique 
alias_maps = hash:/etc/aliases 
alias_database = hash:/etc/aliases 
myorigin = mondomaine.fr ## le nom de domaine 
mydestination = 
local_recipient_maps = 
local_transport = error :local mail delivery is disabled 
relayhost = [192.168.0.202] ## l'ip interne d'exchange 
relay_domains = mondomaine.fr ## le nom de domaine 
relay_recipient_maps = 
smtpd_recipient_restrictions =         
     permit_mynetworks,         
     reject_unauth_destination 
#    check_policy_service inet:127.0.0.1:60000 ## si usage de postgrey 
mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128 192.168.0.0/8 ## réseau interne 
mailbox_command = 
mailbox_size_limit = 51200000 
recipient_delimiter = 
inet_interfaces = all 
inet_protocols = all

et on termine par :

sudo service postfix restart

A partir de ce moment là j’ai testé le relai des emails en utilisant outlook express / windows live mail en interne en utilisant l’adresse IP de cette machine comme adresse de serveur SMTP du client mail. Postfix doit déjà refuser les relais et donc ne laisser que les mails sur le domaine en transfert vers le serveur SBS.

Pour la gestion de TLS, je vous laisse lire la procédure sur cette page, très bien présentée : http://blog.crifo.org/post/2010/04/24/Activer-TLS-dans-postfix.

Installation de ClamAV, SpamAssassin et Amavis

sudo apt-get install clamav
sudo apt-get install spamassassin
sudo apt-get install amavis

Après l’installation de amavis il faut ajouter l’utilisateur clamav au groupe amavis :

sudo adduser clamav amavis

Il faut ensuite modifier les fichiers de Amavis. 

J’ai modifié le fichier « etc/amavis/conf.d/21-ubuntu-defaults » comme ceci :

$final_virus_destiny      = D_REJECT; # (defaults to D_BOUNCE)
$final_banned_destiny     = D_BOUNCE;  # (defaults to D_BOUNCE)
$final_spam_destiny       = D_PASS;  # (defaults to D_REJECT)
$final_bad_header_destiny = D_PASS;  # (defaults to D_PASS), D_BOUNCE suggested

Ainsi les virus sont refusés et les spams sont tous transmis mais avec un tag dans l’objet « ***SPAM*** « .

Puis à la fin de « etc/postfix/master.cf » il faut ajouter :

smtp-amavis unix - - y - 2 smtp
 -o smtp_data_done_timeout=1200
 -o disable_dns_lookups=yes
127.0.0.1:10025 inet              n          -          y           -         -         smtpd
 -o content_filter=
 -o local_recipient_maps=
 -o relay_recipient_maps=
 -o smtpd_restriction_classes=
 -o smtpd_client_restrictions=
 -o smtpd_helo_restrictions=
 -o smtpd_sender_restrictions=
 -o smtpd_recipient_restrictions=permit_mynetworks,reject
 -o mynetworks=192.168.0.0/8
 -o strict_rfc821_envelopes=yes

On retourne alors dans « /etc/postfix/main.cf » pour rajouter à la fin :

content_filter = smtp-amavis:[127.0.0.1]:10024

il ne reste qu’à redémarrer les services amavis et postfix pour prendre en compte les modifications.

A partir de là vous pouvez tester en envoyant des faux emails de spam contenant la ligne suivante dans le corps du message :

XJS*C4JDBQADN1.NSBN3*2IDNEN*GTUBE-STANDARD-ANTI-UBE-TEST-EMAIL*C.34X

SpamAssassin va alors identifier votre email comme du spam.

Installation de fail2ban

Pour finir j’ai ajouté fail2ban pour bannir temporairement les tentatives de relai ou d’intrusion sur le serveur :

sudo apt-get install fail2ban

dans le fichier « /etc/fail2ban/jail.conf » j’ai modifier les sections DEFAULT et POSTFIX pour tenir compte des ip du réseau local et pour activer la surveillance des log postfix :

[DEFAULT]
<...>
ignoreip = 127.0.0.1/8 192.168.0.0/8
<...>
[postfix]
enabled  = true
port     = smtp,ssmtp
filter   = postfix
logpath  = /var/log/mail.log
bantime = 18000

Après un redémarrage du service fail2ban, chaque tentative de relais smtp se solde par un bannissement de l’ip concerné pour les 5 prochaines heures.

 Conclusion

Après ces installations et plusieurs test réussi j’ai redirigé le port 25 de notre passerelle internet vers l’ip de cette nouvelle passerelle email.

Il reste surement d’autre super outils à rajouter, pour l’instant cette configuration me semble efficace et peu gourmande en ressource pour obtenir un minimum de passerelle email et soulager le serveur SBS de ces traitements inutiles. Pour la configuration de chaque logiciels il est bon de rechercher d’autre exemple pour trouver la meilleur solution (et encore, on trouve toujours d’autre options par la suite…).

J’ai testé postgrey mais finalement je ne l’utilise pas car le principe est de retarder les emails pour que les spammeurs abandonnent. Seulement il faut bien parfois recevoir rapidement les emails…

Binding d’une énumération avec WPF

A l’aide du binding d’un objet sous WPF,  il est possible de laisser WPF se charger de l’ensemble des mises à jour entre l’interface et l’objet ce qui permet d’accélérer (a mon sens) le temps de développement de l’interface.

Pour ce qui est du binding d’une chaine vers un textbox, un boolean vers une checkbox, pas de problème. Par contre pour ce qui est des énumérations c’est moins simple … Voici quelques bouts de solutions pour traiter ce problème et pouvoir charger une ComboBox ou listbox avec notre énumération.

1- Utiliser le GetValues de l’enum

Pour commencer il faut ajouter deux namespaces à notre objet Window dans l’XAML, le namespace system et celui de notre assembly :

<Window x:Class="Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:System="clr-namespace:System;assembly=mscorlib"
    xmlns:local="clr-namespace:WpfApplication1"
    Title="Window1" 
    mc:Ignorable="d" 
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
    d:DataContext="{d:DesignInstance Type=local:MonObjet}" 
    Height="300" Width="567">

Ensuite nous allons ajouter une ressources qui contiendra la liste des choix de l’énumération :

<Window.Resources>
        <ObjectDataProvider x:Key="dataFromEnum"
       MethodName="GetValues" ObjectType="{x:Type System:Enum}">
            <ObjectDataProvider.MethodParameters>
                <x:Type TypeName="local:MonTypeEnum"/>
            </ObjectDataProvider.MethodParameters>
        </ObjectDataProvider>
</Window.Resources>

Il reste enfin a ajouter cette ressources comme ItemSource de la ComboBox :

<ComboBox Name="ComboBox1"
     ItemsSource="{Binding Source={StaticResource dataFromEnum}}" 
     SelectedItem="{Binding Path=MaVariable}" >

2 – Utiliser une ListBox en liste de radiobutton

La c’est un complément de la solution précédente, si comme moi vous préférez utiliser des radiobutton il est possible de modifier le style d’une listbox en liste de radiobutton. Pour cela rajoutez dans les ressources le style suivant :

<Style x:Key="RadioButtonList" TargetType="{x:Type ListBox}">
            <Setter Property="BorderBrush" Value="{x:Null}" />
            <Setter Property="BorderThickness" Value="0" />
            <Setter Property="ItemContainerStyle">
                <Setter.Value>
                    <Style TargetType="{x:Type ListBoxItem}" >
                        <Setter Property="Margin" Value="2" />
                        <Setter Property="Template">
                            <Setter.Value>
                                <ControlTemplate TargetType="{x:Type ListBoxItem}">
                                    <Border Background="Transparent">
                                        <RadioButton Focusable="False"
                        IsHitTestVisible="False"
                        IsChecked="{TemplateBinding IsSelected}">
                                            <ContentPresenter />
                                        </RadioButton>
                                    </Border>
                                </ControlTemplate>
                            </Setter.Value>
                        </Setter>
                    </Style>
                </Setter.Value>
            </Setter>
        </Style>

Il reste enfin a ajouter ce style à la listbox et vous obtiendrez une liste de radiobutton :

<ListBox Name="ListBox1" Style="{StaticResource RadioButtonList}"
     ItemsSource="{Binding Source={StaticResource dataFromEnum}}" 
     SelectedItem="{Binding Path=MaVariable}" >

 3 – Utiliser une classe de convertion

Cette solution provient du site suivant :http://www.karmian.org/net-tips-tricks-examples-articles/enum-description-typeconverter.

Le principe est d’utiliser une classe pour convertir l’affichage des choix de l’énumération. Ajouter ce code a votre solution :

Public Class EnumDescriptionTypeConverter
    Inherits EnumConverter
    Public Sub New(type As Type)
        MyBase.New(type)
    End Sub

    Public Overrides Function CanConvertFrom(context As ITypeDescriptorContext, sourceType As Type) As Boolean
        Return sourceType Is GetType(String) OrElse TypeDescriptor.GetConverter(GetType([Enum])).CanConvertFrom(context, sourceType)
    End Function

    Public Overrides Function ConvertFrom(context As ITypeDescriptorContext, culture As System.Globalization.CultureInfo, value As Object) As Object
        If TypeOf value Is String Then
            Return GetEnumValue(EnumType, DirectCast(value, String))
        End If
        If TypeOf value Is [Enum] Then
            Return GetEnumDescription(DirectCast(value, [Enum]))
        End If
        Return MyBase.ConvertFrom(context, culture, value)
    End Function

    Public Overrides Function ConvertTo(context As ITypeDescriptorContext, culture As System.Globalization.CultureInfo, value As Object, destinationType As Type) As Object
        Return If(TypeOf value Is [Enum] AndAlso destinationType Is GetType(String), GetEnumDescription(DirectCast(value, [Enum])), (If(TypeOf value Is String AndAlso destinationType Is GetType(String), GetEnumDescription(EnumType, DirectCast(value, String)), MyBase.ConvertTo(context, culture, value, destinationType))))
    End Function

    Public Shared Function GetEnumDescription(value As [Enum]) As String
        Dim fieldInfo = value.[GetType]().GetField(value.ToString())
        Dim attributes = DirectCast(fieldInfo.GetCustomAttributes(GetType(DescriptionAttribute), False), DescriptionAttribute())
        Return If((attributes.Length > 0), attributes(0).Description, value.ToString())
    End Function

    Public Shared Function GetEnumDescription(value As Type, name As String) As String
        Dim fieldInfo = value.GetField(name)
        Dim attributes = DirectCast(fieldInfo.GetCustomAttributes(GetType(DescriptionAttribute), False), DescriptionAttribute())
        Return If((attributes.Length > 0), attributes(0).Description, name)
    End Function

    Public Shared Function GetEnumValue(value As Type, description As String) As Object
        Dim fields = value.GetFields()
        For Each fieldInfo As Reflection.FieldInfo In fields
            Dim attributes = DirectCast(fieldInfo.GetCustomAttributes(GetType(DescriptionAttribute), False), DescriptionAttribute())
            If attributes.Length > 0 AndAlso attributes(0).Description = description Then
                Return fieldInfo.GetValue(fieldInfo.Name)
            End If
            If fieldInfo.Name = description Then
                Return fieldInfo.GetValue(fieldInfo.Name)
            End If
        Next
        Return description
    End Function
End Class

Vous pouvez ensuite « décrire » votre énumération comme ceci :

<TypeConverter(GetType(EnumDescriptionTypeConverter))> _
Public Enum MonEnum
    <Description("Choix 1")> test1 = 1
    <Description("Choix 2")> test2 = 2
    <Description("Choix 3")> test4 = 3
    test5 = 4
End Enum

Le texte situé dans la description sera alors utilisé pour l’affichage des choix dans l’interface WPF.

Typage du datacontext – binding avec WPF directement dans le XAML

Pour pouvoir typer le binding d’une fenêtre en WPF il suffit d’utiliser une simple astuce a défaut de pouvoir le faire plus simplement dans visual studio. Cela fonctionne pour moi dans visual studio 2010, avec un projet en vb.Net sous le framework 4.

Le principe est simple : nous allons modifier la définition XAML de la fenêtre pour lui ajouter des propriétés et surtout définir notre datacontext.

En premier, vous devez ajouter le namespace contenant l’objet utilisé pour la liaison de données (dans l’exemple ce sera le namespace du projet actuel) :

<Window x:Class="Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:WpfApplication1"
    Title="Window1"
    Height="300" Width="567">

Ensuite vous allez cliquer sur votre « objet » fenêtre dans le designer WPF et cliquez sur le petit carré en bas à droite de la fenêtre :

WPF

Vous devez cliquer deux fois pour revenir en mode normal (taille fixe). En effectuant cette opération vous allez ajouter à votre code XAML deux namespaces XML :

mc:Ignorable="d" 
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

Vous devez enfin ajouter la description du datacontext avec votre objet :

d:DataContext="{d:DesignInstance Type=local:MonObjet}"

Ce qui nous donne au final dans le XAML :

<Window x:Class="Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:WpfApplication1"
    Title="Window1" 
    mc:Ignorable="d" 
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
    d:DataContext="{d:DesignInstance Type=local:MonObjet}"
    Height="300" Width="567">

A partir de maintenant il est possible depuis l’éditeur WPF de parcourir les propriétés du datacontext et donc de notre objet pour sélectionner les liaisons de données.

Voici un exemple avec la propriété Text d’un TextBox ayant une liaison de données vers le datacontext, qui est du type « Personne » qui sert d’exemple :

WPF2

Qui se traduit dans le XAML par :

Text="{Binding Path=Nom}"

Mais c’est plus sympa de le faire visuellement, surtout si on a beaucoup de propriété…