In English——På svenska——En español——En français
Wichtiger Hinweis: Leider verfälschen die Google-Übersetzungen die Quellcodes. Bitte öffnet den Quellcode in einem anderen Fenster, wie es das nachstehende Bild zeigt. Von dort lassen sich die Quellcodes auch leichter herauskopieren:
28. Februar 2023
Fail2Ban ist ein Programm, das auf einem Server läuft und Log-Dateien von Asterisk überwacht, um ungewöhnliche Aktivitäten aufzuspüren und diese in Echtzeit zu blockieren. Es ist ein wichtiger Bestandteil der Sicherheitsstrategie, da es verhindert, dass unauthorisierte Nutzer auf den Asterisk-Server zugreifen und somit sensible Daten oder Dienste manipulieren oder zerstören. Fail2Ban minimiert das Risiko von Angriffen auf Asterisk, indem es potenzielle Bedrohungen schnell erkennt und automatisch blockiert, bevor sie Schaden anrichten können.
Voraussetzungen: Diese Anleitung bezieht sich auf folgende Umgebung und wird auf neueren Versionen aller Wahrscheinlichkeit ebenso funktionieren. Meine Version von Linux auf einem Raspberry Pi 3 B+ lässt sich auf der Kommandozeilenebene wie folgt mit „cat /etc/*-release“ in Erfahrung bringen:
root@raspberrypi:/home/pi# cat /etc/*-release PRETTY_NAME="Raspbian GNU/Linux 10 (buster)" NAME="Raspbian GNU/Linux" VERSION_ID="10" VERSION="10 (buster)" VERSION_CODENAME=buster ID=raspbian ID_LIKE=debian HOME_URL="http://www.raspbian.org/" SUPPORT_URL="http://www.raspbian.org/RaspbianForums" BUG_REPORT_URL="http://www.raspbian.org/RaspbianBugs"
Asterisk ist bereits installiert und konfiguriert. Hier meine Version:
root@raspberrypi:/home/pi# asterisk-rvvvvvvv bash: asterisk-rvvvvvvv: Kommando nicht gefunden. root@raspberrypi:/home/pi# asterisk -rvvvvvvv Asterisk 16.2.1~dfsg-1+deb10u2, Copyright (C) 1999 - 2018, Digium, Inc. and others. Created by Mark Spencer <markster@digium.com> Asterisk comes with ABSOLUTELY NO WARRANTY; type 'core show warranty' for details. This is free software, with components licensed under the GNU General Public License version 2 and other licenses; you are welcome to redistribute it under certain conditions. Type 'core show license' for details. ========================================================================= Connected to Asterisk 16.2.1~dfsg-1+deb10u2 currently running on raspberrypi (pid = 2288) raspberrypi*CLI>
Iptables, die „Firewall von Linux“ ist bereits durch die Installation von Linux in der Regel vorinstalliert und für die Zusammenarbeit mit Fail2Ban notwendig. Iptables ist ein Werkzeug für die Paketfilterung und -weiterleitung in Linux-basierten Betriebssystemen. Es bietet eine flexible Möglichkeit, Netzwerkverbindungen zu steuern, indem es Firewall-Regeln definiert, die den Datenverkehr blockieren, akzeptieren oder umleiten können. Iptables ist ein wichtiger Bestandteil für die Netzwerksicherheit und wird häufig verwendet, um die Integrität von Systemen zu schützen. So finden wir mit „iptables –version“ die Version von Iptables heraus:
root@raspberrypi:/home/pi# iptables --version iptables v1.8.2 (nf_tables)
Falls Iptables nicht installiert ist, kann es auf Ubuntu/Debian wie folgt installiert werden:
sudo apt-get install iptables
Die Regeln von Iptables erfahren wir mit:
iptables -L
Wahrscheinlich sind noch keine Regeln vorhanden.
Fail2Ban installieren: Wenn Iptables installiert ist, können wir Fail2Ban installieren. Für Linux-Debian, also Raspbian auf unserem Raspberry lautet der Befehl:
sudo apt-get install fail2ban
Die Version von Fail2Ban erfahren wir dann mit dem Befehl:
fail2ban-client version
Das Ergebnis:
root@raspberrypi:/home/pi# fail2ban-client version 0.10.2
Konfiguration von Fail2Ban für Asterisk: Asterisk, Iptables und Fail2Ban sind also installiert und nun geht es an die Konfiguration von Fail2Ban für Asterisk.
logger.conf editieren: Wir müssen nun die /etc/asterisk/logger.conf konfigurieren. Die Datei /etc/asterisk/logger.conf ist die Konfigurationsdatei für das Logging-System von Asterisk. Hier können verschiedene Logging-Parameter definiert werden, wie zum Beispiel die Art des Loggings (Console, File), das Log-Level (Debug, Notice, Warning, Error, etc.) und welche Module und Ereignisse geloggt werden sollen. Durch das Anpassen der Einstellungen in dieser Datei kann das Logging-Verhalten von Asterisk konfiguriert und an spezifische Anforderungen angepasst werden.
Wir passen sie an und editieren sie, damit sie wie folgt aussieht:
[general] dateformat = %F %T [logfiles] console => notice,warning,error,debug messages => security,notice,warning,error
Die Datei /etc/asterisk/logger.conf
wird verwendet, um die Konfiguration der Asterisk-Protokollierung zu steuern. In der [general]
-Sektion können allgemeine Einstellungen wie das Datumsformat festgelegt werden. Die [logfiles]
-Sektion definiert die Protokollierungseinstellungen für verschiedene Protokolldateien, einschließlich der Konsole und der Nachrichtenprotokolldatei. In diesem spezifischen Beispiel sind die Protokollierungseinstellungen so konfiguriert, dass auf der Konsole alle Ereignisse auf Stufe notice
, warning
, error
und debug
protokolliert werden und in der Datei messages
Ereignisse auf Stufe security
, notice
, warning
und error
protokolliert werden.
Nun müssen wir die logger.conf in der Asterisk-Konsole neu laden:
sudo asterisk -rvvv logger reload
Das Ergebnis wäre dann:
raspberrypi*CLI> logger reload Asterisk Queue Logger restarted raspberrypi*CLI>
Die logger.conf ist sehr wichtig. Vor allen Dingen muss „dateformat = %F %T“ in dieser Form editiert sein. Die logger.conf bestimmt die Hinweise, die wir in der Asterisk-Konsole sehen.
defaults-debian.conf oder asterisk.conf editieren: Nun öffnen wir die Datei
/etc/fail2ban/jail.d/defaults-debian.conf
Sollte diese nicht vorhanden sein, dann nehmen wir mit der dortigen asterisk.conf vorlieb. In die defaults-debian.conf tragen wir zusätzlich ein:
[asterisk] enabled = true bantime = 7200
Die bantime bannt IP-Adressen, die gegen die Regeln von Fail2Ban verstoßen haben, für 7200 Sekunden. Da sind zwei Stunden. Die
Der gesamte Inhalt der /etc/fail2ban/jail.d/defaults-debian.conf sieht bei mir dann so aus:
[sshd] enabled = true [asterisk] enabled = true bantime = 7200
Die Datei /etc/fail2ban/jail.d/defaults-debian.conf
ist eine Konfigurationsdatei für Fail2Ban, die auf Debian-Systemen standardmäßig installiert ist. Sie enthält Standardkonfigurationen für verschiedene Dienste, die von Fail2Ban überwacht werden können.
In diesem konkreten Beispiel sind die Einstellungen für die jails sshd
und asterisk
enthalten. enabled = true
gibt an, dass diese jails aktiviert sind. bantime = 7200
definiert die Zeit in Sekunden, für die eine IP-Adresse gesperrt bleibt, wenn sie gegen die Regeln des jails verstößt. Für den sshd
jail wird die Standardbantime von 10 Minuten verwendet.
Wir kontrollieren nun die bisherige Konfiguration mit:
sudo fail2ban-client reload
und bekommen nur ein „OK“ zurück, wenn alles bis dahin in Ordnung ist:
root@raspberrypi:/home/pi# sudo fail2ban-client reload OK root@raspberrypi:/home/pi#
jail.conf: Wir müssen noch die /etc/fail2ban/jail.conf editieren, damit Fail2Ban auch in der Lage ist entsprechende Einträge in Iptables vorzunehmen, damit die IP-Adressen, welche die Regeln von Fail2Ban verletzen, gebannt werden. Wir tragen zusätzlich folgendes gleich am Anfang der Datei ein:
[asterisk-iptables] enabled = true filter = asterisk action = iptables-allports[name=ASTERISK, protocol=all] logpath = /var/log/asterisk/messages maxretry = 5 bantime = 7200 findtime = 600 [DEFAULT] # Multiple addresses can be specified, # separated by a space. # Zum Beispiel werden folgende IPs verschont # damit nicht der eigene Server und das eigene LAN gesperrt wird ignoreip = 127.0.0.1 10.1.1.1 94.254.10.244 192.168.1.111
Die jail.conf ist sehr groß. Die vorhandenen Einträge nicht löschen! Bitte vergewissern, ob die Log-Datei /var/log/asterisk/messages auch tatsächlich existiert.
Dieser ergänzende Eintrag in der /etc/fail2ban/jail.conf
definiert eine Regel für das Fail2Ban-System, um Asterisk-Anmeldeversuche über die IPTables-Firewall zu blockieren.
enabled = true
aktiviert die Regel für Fail2Ban.filter = asterisk
gibt an, dass die Regeln für die Asterisk-Anmeldungen angewendet werden sollen.action = iptables-allports[name=ASTERISK, protocol=all]
legt die Aktion fest, die bei einem Verstoß gegen die Regel ausgeführt werden soll, nämlich alle Ports für die IP-Adresse zu blockieren.logpath = /var/log/asterisk/messages
gibt den Pfad zur Log-Datei von Asterisk an, wo die Anmeldeversuche protokolliert werden.maxretry = 5
legt die Anzahl der zulässigen Anmeldeversuche innerhalb der definiertenfindtime
-Zeit fest, bevor die IP-Adresse blockiert wird.bantime = 7200
gibt an, wie lange die IP-Adresse blockiert werden soll, wenn sie gegen die Regeln verstoßen hat (hier: 7200 Sekunden, also 2 Stunden).findtime = 600
legt fest, innerhalb welcher Zeitspanne (hier: 600 Sekunden, also 10 Minuten) die Anzahl der Anmeldeversuchemaxretry
erreicht haben muss, damit die IP-Adresse blockiert wird.ignoreip = 127.0.0.1 10.1.1.1 94.254.10.244 192.168.1.111
gibt eine Liste von IP-Adressen an, die von der Regel ausgeschlossen werden sollen und somit nicht blockiert werden, auch wenn sie gegen die Regeln verstoßen. Hier sind die IPs des eigenen Servers und des eigenen LANs aufgeführt, um zu verhindern, dass diese aus Versehen gesperrt werden. Die einzelnen IP-Adressen können auch Zeile für Zeile eingetragen werden. Zum Beispiel so:
[DEFAULT] ignoreip = 127.0.0.1 94.254.12.240 192.168.1.111 192.168.1.101 192.168.1.102 192.168.1.103 192.168.1.63 192.168.1.254 192.168.1.43 192.168.1.44 188.108.118.12 94.23.17.185 199.87.144.68 83.125.8.71 5.44.104.134 194.169.214.30 212.79.111.155 136.243.23.236 85.17.186.23 52.3.123.231 212.117.203.60 217.10.79.9 185.146.140.40 46.31.225.185 5.9.87.18 185.45.152.161
Mit folgenden beiden Befehlen starten wir Fail2Ban neu, damit es nach den Änderungen aktiviert wird:
systemctl restart fail2ban sudo fail2ban-client restart
Wenn alles in Ordnung ist, gibt es folgende Rückmeldung:
root@raspberrypi:/home/pi# systemctl restart fail2ban root@raspberrypi:/home/pi# sudo fail2ban-client restart Shutdown successful Server ready root@raspberrypi:/home/pi#
Wenn etwas schief gelaufen ist mit der Definition von Regeln, wird dies angezeigt.
Nun führen wir folgende Befehle aus:
sudo fail2ban-client status sudo fail2ban-client status asterisk
Das Ergebnis sieht so aus:
root@raspberrypi:/home/pi# sudo fail2ban-client status Status |- Number of jail: 3 `- Jail list: asterisk, asterisk-iptables, sshd root@raspberrypi:/home/pi# sudo fail2ban-client status asterisk Status for the jail: asterisk |- Filter | |- Currently failed: 0 | |- Total failed: 0 | `- File list: /var/log/asterisk/messages `- Actions |- Currently banned: 0 |- Total banned: 0 `- Banned IP list: root@raspberrypi:/home/pi#
Es sind also drei jails konfiguriert und Fail2Ban konnte noch keine IPs bannen, bis jetzt jedenfalls. In der Praxis werden hauptsächlich IPs gebannt, die sich mit falschen Passwörtern anmelden. Gratulation! Wir haben Fail2Ban für Asterisk mit den vordefinierten Regeln erfolgreich zum Laufen gebracht.
Weitere Regeln hinzufügen: Die bereits vordefinierten Regeln für Fail2Ban und Asterisk finden wir in der Datei „/etc/fail2ban/filter.d/asterisk.conf“ dort unter failregex:
failregex = ^Registration from '[^']*' failed for '<HOST>(:\d+)?' - (?:Wrong password|Username/auth name mismatch|No matching peer found|Not a local domain|Device does not match ACL|Peer is not supposed to register|ACL error \(permit/deny\)|Not a local domain)$ ^Call from '[^']*' \(<HOST>:\d+\) to extension '[^']*' rejected because extension not found in context ^(?:Host )?<HOST> (?:failed (?:to authenticate\b|MD5 authentication\b)|tried to authenticate with nonexistent user\b) ^No registration for peer '[^']*' \(from <HOST>\)$ ^hacking attempt detected '<HOST>'$ ^SecurityEvent="(?:FailedACL|InvalidAccountID|ChallengeResponseFailed|InvalidPassword)"(?:(?:,(?!RemoteAddress=)\w+="[^"]*")*|.*?),RemoteAddress="IPV[46]/(UDP|TCP|WS)/<HOST>/\d+"(?:,(?!RemoteAddress=)\w+="[^"]*")*$ ^"Rejecting unknown SIP connection from <HOST>"$ ^Request (?:'[^']*' )?from '(?:[^']*|.*?)' failed for '<HOST>(?::\d+)?'\s\(callid: [^\)]*\) - (?:No matching endpoint found|Not match Endpoint(?: Contact)? ACL|(?:Failed|Error) to authenticate)\s*$
Die erste Regel
^Registration from '[^']*' failed for '<HOST>(:\d+)?' - (?:Wrong password|Username/auth name mismatch|No matching peer found|Not a local domain|Device does not match ACL|Peer is not supposed to register|ACL error \(permit/deny\)|Not a local domain)$
in der Datei „/etc/fail2ban/filter.d/asterisk.conf“ ist eine Regel, die Fail2ban verwendet, um ungültige Anmeldeversuche im Asterisk-Protokoll zu erkennen und zu blockieren. Diese Regel wird ausgelöst, wenn eine Registrierung fehlschlägt und eine der angegebenen Bedingungen erfüllt ist. Zum Beispiel wird diese Regel ausgelöst, wenn der falsche Passwort eingegeben wird, der Benutzername nicht gefunden wird, das Gerät nicht mit der ACL übereinstimmt, oder wenn ein Peer sich nicht registrieren soll. „<HOST>“ ist ein Platzhalter, der die IP-Adresse des Versuchs enthält.
In jeder Zeile steht eine Regel. Die anderen Regeln können wir uns von ChatGPT erklären lassen. Wir können noch weitere Regeln hinzufügen. Vier weitere Regeln habe ich unter
https://bradleyclayton.io/posts/homelab/protecting_asterisk_fail2ban/
gefunden:
^Endpoint 'anonymous' \(<HOST>:\d+\) has no configured AORs$ ^SecurityEvent="(?:InvalidPassword|FailedACL)".*,RemoteAddress="IPV[46]\/(?:UDP|TCP|TLS)\/<HOST>\/\d+ Call \((?:UDP|TCP|TLS):<HOST>:\d+\) to extension '\+?\d*' rejected because extension not found in context '.*'.$ Error processing \d+ bytes packet from (?:UDP|TCP|TLS) <HOST>:\d+ : PJSIP syntax error
Wenn wir diese vier Zeilen zusätzlich eintragen, sieht das so aus:
failregex = ^Registration from '[^']*' failed for '<HOST>(:\d+)?' - (?:Wrong password|Username/auth name mismatch|No matching peer found|Not a local domain|Device does not match ACL|Peer is not supposed to register|ACL error \(permit/deny\)|Not a local domain)$ ^Call from '[^']*' \(<HOST>:\d+\) to extension '[^']*' rejected because extension not found in context ^(?:Host )?<HOST> (?:failed (?:to authenticate\b|MD5 authentication\b)|tried to authenticate with nonexistent user\b) ^No registration for peer '[^']*' \(from <HOST>\)$ ^hacking attempt detected '<HOST>'$ ^SecurityEvent="(?:FailedACL|InvalidAccountID|ChallengeResponseFailed|InvalidPassword)"(?:(?:,(?!RemoteAddress=)\w+="[^"]*")*|.*?),RemoteAddress="IPV[46]/[^/"]+/<HOST>/\d+"(?:,(?!RemoteAddress=)\w+="[^"]*")*$ ^"Rejecting unknown SIP connection from <HOST>(?::\d+)?"$ ^Request (?:'[^']*' )?from '(?:[^']*|.*?)' failed for '<HOST>(?::\d+)?'\s\(callid: [^\)]*\) - (?:No matching endpoint found|Not match Endpoint(?: Contact)? ACL|(?:Failed|Error) to authenticate)\s*$ ^Endpoint 'anonymous' \(<HOST>:\d+\) has no configured AORs$ ^SecurityEvent="(?:InvalidPassword|FailedACL)".*,RemoteAddress="IPV[46]\/(?:UDP|TCP|TLS)\/<HOST>\/\d+ Call \((?:UDP|TCP|TLS):<HOST>:\d+\) to extension '\+?\d*' rejected because extension not found in context '.*'.$ Error processing \d+ bytes packet from (?:UDP|TCP|TLS) <HOST>:\d+ : PJSIP syntax error
Diese vier zusätzlichen Regeln sind spezielle Ausdrücke für das Fail2Ban-System, das verwendet wird, um potenzielle Sicherheitsprobleme in den Log-Dateien von Asterisk zu erkennen und zu blockieren.
Die erste Regel „^Endpoint 'anonymous' \(<HOST>:\d+\) has no configured AORs$
“ erkennt einen Fehler, bei dem ein SIP-Endpunkt ohne konfigurierte Adressierungsobjektreferenzen (AORs) gefunden wird. Dies ist ein potenzielles Sicherheitsproblem, da es darauf hindeuten kann, dass jemand versucht hat, sich als anonymer Benutzer zu registrieren, um unbefugten Zugriff auf den Server zu erhalten.
Die zweite Regel „^SecurityEvent="(?:InvalidPassword|FailedACL)".*,RemoteAddress="IPV[46]\/(?:UDP|TCP|TLS)\/<HOST>\/\d+
“ erkennt fehlgeschlagene Anmeldeversuche oder Zugriffsversuche auf den Server von einem Remote-Host. Diese Regel identifiziert Einträge in den Log-Dateien, die die Meldungen „InvalidPassword“ oder „FailedACL“ enthalten und eine Remote-IP-Adresse angeben.
Die dritte Regel „Call \((?:UDP|TCP|TLS):<HOST>:\d+\) to extension '\+?\d*' rejected because extension not found in context '.*'.$
“ erkennt Anrufe, bei denen eine nicht vorhandene Rufnummer oder Erweiterung gewählt wurde. Dies kann auf potenzielle Angriffe oder auf fehlerhafte Konfigurationen hinweisen.
Die vierte Regel „Error processing \d+ bytes packet from (?:UDP|TCP|TLS) <HOST>:\d+ : PJSIP syntax error
“ erkennt Syntaxfehler in SIP-Paketen, die vom Remote-Host empfangen wurden. Solche Fehler können dazu führen, dass der Asterisk-Server abstürzt oder fehlerhafte Verbindungen akzeptiert, die für Angriffe genutzt werden können.
Die Regeln in der Datei /etc/fail2ban/filter.d/asterisk.conf
sind in der Syntax der Programmiersprache „regular expressions“ (kurz „regex“) geschrieben. Dabei handelt es sich nicht um eine vollständige Programmiersprache, sondern um eine Notation zur Beschreibung von Such- und Ersetzungsmustern in Zeichenketten. Regex wird in vielen Programmiersprachen unterstützt, darunter auch in Python, Perl, Java, C++, JavaScript und vielen anderen.
Die Regeln in der /etc/fail2ban/filter.d/asterisk.conf
beziehen sich auf die Log-Datei /var/log/asterisk/messages
. Dies ist die Standard-Log-Datei für Asterisk, in der verschiedene Ereignisse und Aktionen von Asterisk protokolliert werden.
Folgende drei Regeln könnten auch interessant sein:
^(%(__prefix_line)s|\[\]\s*WARNING%(__pid_re)s:?(?:\[C-[\da-f]*\])? )[^:]+: Friendly Scanner from <HOST>$ ^(%(__prefix_line)s|\[\]\s*WARNING%(__pid_re)s:?(?:\[C-[\da-f]*\])? )Ext\. s: "Rejecting unknown SIP connection from <HOST>"$ ^(%(__prefix_line)s|\[\]\s*)%(log_prefix)s (?:handle_request_subscribe: )?Sending fake auth rejection for (device|user) \d*<sip:[^@]+@<HOST>>;tag=\w+\S*$
Die Grenzen von Fail2Ban: Fail2Ban kann nur auf Meldungen in der messages.conf reagieren, die auch die IP des Verursachers enthält. Gegen Warnmeldung wie z.B.
[2023-02-19 15:03:17] WARNING[686] chan_sip.c: Timeout on C6XHMJYPD5YK.1551751.6MN69ZY43UM7 on non-critical invite transaction. [2023-02-19 15:03:17] WARNING[686] chan_sip.c: Timeout on 9RSIKPMTSDSN.1551751.22YQKAPQSFFP on non-critical invite transaction
kann Fail2Ban nichts ausrichten, weil die IP nicht ersichtlich ist. Solche non-critical invite transactions können allerdings massenhaft auftreten und lassen sich durch aktualisierte Blacklists blockieren. Im Asterisk CLI kann ein Angriff so aussehen:
Wie man sich Blacklists von Hand oder automatisch für Iptables anlegen kann, um sich dagegen zu wehren, ist nachfolgend beschrieben:
APIBAN schützt SIP-Telefon-Server automatisch vor bösartigen Angriffen aus dem Internet – 2. Februar 2023: APIBAN ist ein kostenloser Dienst, der nicht nur für Asterisk-SIP-Server laufend aktualisierte Blacklists böswilliger IP-Adressen zur Verfügung stellt, die automatisch in regelmäßigen Abständen in Iptables eingetragen werden. Damit die Blacklist in Iptables nicht zu groß wird, lässt sich die maximale Anzahl der Einträge (Regeln) begrenzen. Ältere Einträge werden automatisch aus Iptables entfernt. Man muss sich um die Pflege der Blacklists im Idealfall nicht mehr kümmern. – weiter – |
SIP-Scanner blockieren, die Fail2Ban nicht entdecken kann – 17.1.2022: Wenn im Asterisk-CLI Meldungen wie „Timeout on … on non-critical invite transaction.“ als „WARNING“ auftreten, handelt es sich meistens um einen Angriff durch einen SIP-Scanner. Sie sind für Betreiber von Asterisk-Servern eine Gefahr, da Fail2Ban oft nicht in der Lage ist diese unerwünschten Eindringlinge zu blockieren. Manchmal treten die Attacken der SIP-Scanner massenhaft auf und können sogar den Betrieb eines Asterisk-Servers empfindlich stören. Nachfolgend eine Lösung ohne Einsatz nicht ausreichend getesteter Skripte, die den Asterisk-Server eventuell zum Abstürzen bringen könnten. – weiter –
|
Übergeordnet:
Asterisk-Telefonserver auf einem Raspberry Pi – Installation, Konfiguration, Programmierung, SIP, IAX2, AGI-Skripte, Sicherheit und Tipps zum praktischen Betrieb – 2.11.2022: Diese Seite richtet sich an jene, welche einen Asterisk-Telefon-Server auf einem Raspberry Pi betreiben möchten und später ein kleines Netzwerk aus Asterisk-Servern planen, um ein eigenständiges Telefonnetz aufzubauen. Los geht es mit der Installation von Raspbian und Asterisk auf einem Raspberry Pi und dann nach Lust und Laune immer tiefer in die Programmierung von Asterisk. Die Themen werden laufend erweitert.
Selbstverständlich muss es nicht unbedingt ein Raspberry Pi sein. Andere Linux-Rechner gehen auch. – weiter – |