Configuration Logstash - Fail2ban

De EjnTricks

Fail2ban est un outil très pratique pour interdire l'accès à des machines en fonction des tentatives d'accès et l'analyse des traces, sur serveur Web ou en SSH par exemple.

Cet article présente la mise en place d'une configuration pour Logstash afin de parcourir les fichiers traces de Fail2ban et de stocker les informations des actions de bannissement dans Elasticsearch. Ainsi, il est possible de construire des tableaux de bords pour visualiser l'activité.


Hand-icon.png Votre avis

Nobody voted on this yet

 You need to enable JavaScript to vote


Study icon.png Pré requis

Le compte d'exécution de Logstash, soit logstash dans le cadre de cette installation, doit accéder aux fichiers trace de Fail2ban, se trouvant à l'emplacement /var/log/fail2ban.log. Seule la lecture est nécessaire sur ces fichiers.

User-group-icon.png Groupe

Le nouveau groupe loganalyzer est créé afin de faciliter la gestion des droits d'accès.

#sudo groupadd --system loganalyzer

User-icon.png Utilisateurs

Par défaut, les droits d'accès aux fichiers trace de Fail2ban sont accordés au groupe adm. Il est donc recommandé d'injecter les utilisateurs de celui-ci dans le nouveau groupe loganalyzer et de le compléter avec le compte d'exécution de Logstash, soit logstash dans le cadre de cette installation.

#sudo usermod -a -G loganalyzer syslog
#sudo usermod -a -G loganalyzer logstash

Icon ACL.png Sécurisation log

Une configuration Logrotate est disponible pour Fail2ban au niveau du fichier /etc/logrotate.d/fail2ban, où sont configurés les droits d'accès pour le fichier trace.

Il faut le modifier afin d'utiliser le nouveau groupe. Le contenu du fichier est alors le suivant.

/var/log/fail2ban.log {

    weekly
    rotate 4
    compress

    delaycompress
    missingok
    postrotate
        fail2ban-client flushlogs 1>/dev/null
    endscript

    # If fail2ban runs as non-root it still needs to have write access
    # to logfiles.
    create 640 root loganalyzer
}

La mise en place des permissions s'effectuant lors de la rotation, il peut être nécessaire de modifier les permissions pour les fichiers déjà existants.

#sudo chown -R root:loganalyzer /var/log/fail2ban*


Icon-Configuration-Settings.png Configuration

Warning-icon.png Attention, la configuration doit être adaptée en fonction des version de Fail2ban installée. En effet, le contenu des fichiers traces peut varier.

Icon-database-process.png Configuration mapping

Dans le cadre de cet article, des indexes personnalisés sont utilisés. Afin de bénéficier de toutes les fonctionnalitées, comme la géolocalisation, il est nécessaire de déclaration le mapping pour ces indexes. La déclaration s'effectue comme décrit dans l'article suivant.

La définition du modèle s'inspire de celui qui est créé par défaut lors du démarrage de Logstash, à savoir le modèle logstash.

#curl -X GET "localhost:9200/_template/logstash?pretty"
{
  "logstash" : {
    "order" : 0,
    "version" : 60001,
    "index_patterns" : [
      "logstash-*"
    ],
    "settings" : {
      "index" : {
        "refresh_interval" : "5s"
      }
    },
    "mappings" : {
      "_default_" : {
        "dynamic_templates" : [
          {
            "message_field" : {
              "path_match" : "message",
              "match_mapping_type" : "string",
              "mapping" : {
                "type" : "text",
                "norms" : false
              }
            }
          },
          {
            "string_fields" : {
              "match" : "*",
              "match_mapping_type" : "string",
              "mapping" : {
                "type" : "text",
                "norms" : false,
                "fields" : {
                  "keyword" : {
                    "type" : "keyword",
                    "ignore_above" : 256
                  }
                }
              }
            }
          }
        ],
        "properties" : {
          "@timestamp" : {
            "type" : "date"
          },
          "@version" : {
            "type" : "keyword"
          },
          "geoip" : {
            "dynamic" : true,
            "properties" : {
              "ip" : {
                "type" : "ip"
              },
              "location" : {
                "type" : "geo_point"
              },
              "latitude" : {
                "type" : "half_float"
              },
              "longitude" : {
                "type" : "half_float"
              }
            }
          }
        }
      }
    },
    "aliases" : { }
  }
}

Pour le modèle personnalisé, les modifications apportées portent sur le nom, soit fail2ban, et le périmètre d'application sur les indexes sont le nom commence par f2b-. La configuration du mapping standard est intégralement reprise.

#curl -X PUT "localhost:9200/_template/fail2ban" -H 'Content-Type: application/json' -d'
{
  "order" : 0,
  "version" : 1,
  "index_patterns" : [
    "f2b-*"
  ],
  "settings" : {
    "index" : {
      "refresh_interval" : "5s"
    }
  },
  "mappings" : {
    "_default_" : {
      "dynamic_templates" : [
        {
          "message_field" : {
            "path_match" : "message",
            "match_mapping_type" : "string",
            "mapping" : {
              "type" : "text",
              "norms" : false
            }
          }
        },
        {
          "string_fields" : {
            "match" : "*",
            "match_mapping_type" : "string",
            "mapping" : {
              "type" : "text",
              "norms" : false,
              "fields" : {
                "keyword" : {
                  "type" : "keyword",
                  "ignore_above" : 256
                }
              }
            }
          }
        }
      ],
      "properties" : {
        "@timestamp" : {
          "type" : "date"
        },
        "@version" : {
          "type" : "keyword"
        },
        "geoip" : {
          "dynamic" : true,
          "properties" : {
            "ip" : {
              "type" : "ip"
            },
            "location" : {
              "type" : "geo_point"
            },
            "latitude" : {
              "type" : "half_float"
            },
            "longitude" : {
              "type" : "half_float"
            }
          }
        }
      }
    }
  },
  "aliases" : { }
}
'
{"acknowledged":true}

A noter les configurations spécifiques des propriétés. location sera utilisée pour référencer les données de géolocalisation et ip est déclaré en type ip.

La réponse {"acknowledged":true} indique que le modèle a été correctement installé, ainsi que le mapping associé.

Study icon.png Analyse

Dans le cadre de cet article, les messages sont de la forme suivantes.

2018-04-30 11:03:37,826 fail2ban.actions        [1027]: NOTICE  [sshd-invalid-user] Unban 121.18.238.115
2018-04-30 11:07:19,742 fail2ban.filter         [1027]: INFO    [sshd] Found 221.194.47.243
2018-04-30 11:07:21,788 fail2ban.filter         [1027]: INFO    [sshd-invalid-user] Found 221.194.47.243
2018-04-30 11:07:21,792 fail2ban.filter         [1027]: INFO    [sshd] Found 221.194.47.243
2018-04-30 11:07:22,326 fail2ban.actions        [1027]: NOTICE  [sshd-invalid-user] Ban 221.194.47.243
2018-04-30 11:09:01,942 fail2ban.filter         [1027]: INFO    [sshd] Found 221.194.47.243
2018-04-30 11:09:01,944 fail2ban.filter         [1027]: INFO    [sshd-invalid-user] Found 221.194.47.243
2018-04-30 11:09:02,704 fail2ban.actions        [1027]: NOTICE  [sshd-invalid-user] 221.194.47.243 already banned
2018-04-30 11:13:25,987 fail2ban.filter         [1027]: INFO    [sshd] Found 85.29.138.107
2018-04-30 11:13:25,990 fail2ban.filter         [1027]: INFO    [sshd-invalid-user] Found 85.29.138.107
2018-04-30 11:13:26,033 fail2ban.actions        [1027]: NOTICE  [sshd-invalid-user] Ban 85.29.138.107
2018-04-30 11:13:26,056 fail2ban.filter         [1027]: INFO    [sshd] Found 85.29.138.107
2018-04-30 11:13:28,293 fail2ban.filter         [1027]: INFO    [sshd-invalid-user] Found 85.29.138.107
2018-04-30 11:13:28,295 fail2ban.filter         [1027]: INFO    [sshd] Found 85.29.138.107
2018-04-30 11:13:29,270 fail2ban.actions        [1027]: NOTICE  [sshd-invalid-user] 85.29.138.107 already banned

L'objectif est d'extraire l'adresse IP, le nom du filtre (sshd / sshd-invalid-user) ainsi que "l'action", Found / Ban / Unban.

Folder-icon.png Emplacement

La configuration sera placée dans le répertoire conf.d, dont l'emplacement a été externalisé durant l'installation.

#sudo touch /var/opt/logstash/common/conf.d/fail2ban.conf
#sudo chown logstash:logstash /var/opt/logstash/common/conf.d/fail2ban.conf
#sudo chmod 600 /var/opt/logstash/common/conf.d/fail2ban.conf

Process-icon.png Paramétrage

La section input doit référencer l'emplacement du fichier trace.

input {
  file {
    path => "/var/log/fail2ban.log"
    type => "fail2ban"
  }
}

La section output doit référencer l'instance Elasticsearch dans laquelle les données seront injectées. Les instances Logstash et Elasticsearch étant sur la même machine, le nom de la machine est spécifiée avec localhost:9200

output {
  elasticsearch {
    hosts => ["localhost:9200"]
    index => "fail2ban-%{+YYYY.MM}"
  }
}

A noter le nom de l'index mis en place. Celcui contient la variable %{+YYYY.MM} qi va permettre de mettre en place un indexe par mois.

Enfin la section filter, la plus compliquée, permet de configurer la lecture des fichiers traces. Dans le cadre de cette installation, le contenu est le suivant.

filter {
  if [type] == "fail2ban" {
    grok {
      match => [ "message", "%{TIMESTAMP_ISO8601:bantime} %{PROG}%{SPACE}(\[%{POSINT}\])?: %{WORD}%{SPACE}\[%{GREEDYDATA:jail}\] %{WORD:action} %{IPV4:ip}" ]
      add_tag => [ "%{action}" ]
      named_captures_only => true
    }

    if "_grokparsefailure" in [tags] {
      drop { }
    }

    date {
      match => [ "bantime", "ISO8601" ]
      target => "bantime"
    }

    geoip {
      source => "ip"
    }
  }
}

Le filtre sur le type n'est pas forcément nécessaire, car une seule valeur n'est configuré dans le fichier. L'expression régulière dans la section grok utiliser de nombreuse fonctions disponibles en standard.

  • %{TIMESTAMP_ISO8601:bantime} permet d'extraire l'heure de la trace et de stocker la valeur dans le champ bantime;
  • %{PROG} permet de référencer le nom du programme. L'objectif est juste à titre informatif, car aucun nom de champs n'est explicitement spécifié;
  • %{GREEDYDATA:jail} permet d'extraire le nom du bannissement et de stocker la valeur dans le champ jail;
  • %{WORD:action} permet d'extraire l'action et de stocker la valeur dans le champ action;
  • %{IPV4:ip} permet d'extraire l'adresse IP et de stocker la valeur dans le champ ip.

Les autres champs sont configurés pour essayer de rendre lisible l'expression.

A noter que la valeur du champ action est également utilisé pour compléter les tag à l'aide de l'instruction add_tag => [ "%{action}" ]. L'instruction named_captures_only => true permet de n'enregistrer que les camps qui sont explicitement nommés, soit les suivants pour cette configuration.

  • bantime;
  • jail;
  • action;
  • ip.

La section date permet de spécifier les champs de type date, ainsi que le format, soit bantime dans cette configuration.

De la même façon, la section geoip permet de compléter les données à l'aide du plugin Ingest-GeoIP à partir du champs ip qui contient l'adresse IP.

La dernière section mutate permet de supprimer la valeur potentielle _grokparsefailure dans les données. Mais cela pourrait être supprimé, car les données sont supprimées si la valeur est mise dans les tags.

Le contenu complet du fichier est le suivant.

input {
  file {
    path => "/var/log/fail2ban.log"
    type => "fail2ban"
    # start_position => "beginning"
  }
}

filter {
  if [type] == "fail2ban" {
    grok {
      match => [ "message", "%{TIMESTAMP_ISO8601:bantime} %{PROG}%{SPACE}(\[%{POSINT}\])?: %{WORD}%{SPACE}\[%{GREEDYDATA:jail}\] %{WORD:action} %{IPV4:ip}" ]
      add_tag => [ "%{action}" ]
      named_captures_only => true
    }

    if "_grokparsefailure" in [tags] {
      drop { }
    }

    date {
      match => [ "bantime", "ISO8601" ]
      target => "bantime"
    }

    geoip {
      source => "ip"
    }
  }
}

output {
  elasticsearch {
    hosts => ["localhost:9200"]
    index => "f2b-%{+YYYY.MM}"
  }
}

A noter le nom de l'index est f2b-%{+YYYY.MM} afin de rentrer dans le cadre du mapping préalablement mis en place, s'applicant aux indexes commençant par f2b-. La variable %{+YYYY.MM} sera substituée par l'année et le numéro de mois.


Viewer icon.png Voir aussi

La mise en place de cette configuration s'est inspirée de différentes publications sur Internet. https://blog.projectnine.com/fail2ban-with-elk/

https://miteshshah.github.io/linux/elk/how-to-monitor-fail2ban-logs-on-elk-stack/

Mais attention, les configurations mises en place fonctionnent sur d'anciennes version de Fail2ban et ne permettent pas de lire les traces pour une version 0.10.2 par exemple.