Skip to main content

Intrusion prevention

Antelao runs Fail2Ban in its own container, watching the nginx logs and banning addresses that behave like attackers.

That container is Antelao Fail2Ban, a product in its own right. This server pushes its own jails into it - ohm-login, ohm-4xx, ohm-429 and ohm-ssh - because it is the one that knows what its logs mean. Its own manual, under Related below, covers the jail format, and the check that tells a listed ban from an enforced one.

What is and is not licensed​

Running Fail2Ban is never restricted. It is installed with the server at every edition, including unregistered, and its jails always watch the logs and decide who to ban.

Blocking is what the license decides. Automatic attack blocking is carried by Community, Professional and Provider. Without it - on the Developer edition and on an unregistered server - Fail2Ban detects and logs but writes no firewall rule: each ban is written to its log as WOULD BAN instead, and the Threat protection page says it is detecting only. Fail2Ban enforces this itself, and starts blocking as soon as a license that carries it is installed. Banning an address by hand is not on the Developer edition either.

Reading jails and bans, lifting a ban, and removing Fail2Ban altogether all stay available at every edition. An unregistered server must never be locked out of its own remedy, which is exactly what a gated unban would create.

When this server's license stops carrying automatic attack blocking - a lower edition, or a subscription that ended - it withdraws its sponsorship of the Fail2Ban service. The service then lifts every ban it holds, once, and goes on detecting only. The jails and their settings are kept, and the sponsorship returns by itself when a license carries blocking again; bans lifted in between are not put back. See When the license changes.

Working with bans​

Remote or localagainst pelmo
$ ohm server fail2ban list --server pelmo
Remote or localagainst pelmo
$ ohm server fail2ban ban 203.0.113.10 ohm-login --server pelmo
Remote or localagainst pelmo
$ ohm server fail2ban unban --ban 203.0.113.10 --server pelmo

Omit --ban and the CLI shows a picker. In the console the same lives on the Threat protection page, which updates live.

The jails, and which are on​

JailBans onShips
ohm-loginFailed sign-ins on an instance's sign-in pageEnabled
ohm-4xxRepeated 400, 403 or 404 answers from Odoo itself, counted from the web server's access log; refusals by the firewall or the rate limit, and browser icon requests, do not countEnabled on a new server
ohm-429Callers who keep hitting an instance's rate limitEnabled on a new server
ohm-sshFailed connections to the developers' SSH gatewayEnabled while the SSH gateway is switched on, and off while it is not

The first three are on for a new server: a scanner walking /.env, /.ssh and the like produces one 4xx or 429 after another, and without these jails it is only ever slowed down, never banned. A server installed before keeps what its settings say. ohm-4xx and ohm-429 can also ban a caller who is merely clumsy - a broken integration polling a dead URL, or a busy office behind one address - so switch one off here when you know your traffic needs it. ohm-ssh follows the gateway and has no switch of its own.

ohm-429 is the useful pairing with an instance's own rate limits. Those answer 429 indefinitely and never ban; this jail is what turns persistence into a ban.

What counts as a failed Odoo sign-in​

ohm-login, and CrowdSec's Failed Odoo logins scenario when CrowdSec is on, count one thing: a sign-in on an instance's sign-in page (/web/login) that Odoo answered with the page again instead of letting the visitor in. They read it from the web server's own log, with the address the web server saw, never from the instance's Odoo log, so nothing a visitor types and no code running in an instance can make them ban another address.

Failed sign-ins through Odoo's external API no longer count toward a ban, because Odoo answers those the same way whether the password was right or wrong. Those requests still count toward the instance's rate limit and the ohm-4xx and ohm-429 jails. If you counted on the login protection to stop password guessing through the external API, keep that instance's Rate limiting on, on its Firewall tab, with ohm-4xx and ohm-429 on here. See Each instance's own site is rate limited too.

Failed Turnstile checks on an Odoo portal

A database whose portal sign-in is guarded by Cloudflare Turnstile, with the portal sign-in check installed, sends a sign-in that fails the check back to the sign-in page. From 0.24.0 ohm-login counts every sign-in that comes back to the sign-in page, as What counts as a failed Odoo sign-in describes, so it can ban an address that keeps failing the check. Count failed checks as failed sign-ins on the database's Sign-in protection tab (ohm instance database turnstile --count-failures) decides only the line the check writes to the instance's Odoo log, which the jails no longer read. See Sign-in protection.

Configuration​

Whitelist, blacklist, ban duration, search window and retry count are part of the server's policy configuration, in the Fail2Ban options card on that same page. The installer's setup page links to it, and nothing about it is required: a server with no intrusion prevention configured is a complete server.

Notifications​

Ban mail is set on the Notifications card of the same tab:

SettingWhat it does
EnabledE-mail ban notices from the Fail2Ban service. Off, nothing is sent, whatever the switches below say
To, Cc, BccWho gets them. Bcc keeps those addresses out of sight of the others
Sender name, Sender e-mailWho the notices come from
BansMail bans as they happen. The service collects them for a few minutes and sends one message, so an attack does not flood the inbox
UnbansMail when a ban is lifted
Daily summaryOnce a day, one message with a world map of where the banned addresses came from and a table of the day's bans. Nothing is sent on a day without bans

New servers mail bans and nothing else. Leave To, Cc and Bcc all empty and the notices go to the server's own notification recipients instead - Append To on the E-mail tab of Configuration - and the instance health check Ban notifications warns when neither list has an address, so ban notices cannot quietly go to nobody. Sending them at all needs the Attack blocking notices capability; see Mail.

Where the bans come from​

Bans & actions lists each ban with the country of its address, and a map above the list counts the bans per country. It follows the list: switch on include expired and lifted bans are counted too.

Whitelist your own address before you narrow anything

Locking yourself out of a server you are configuring remotely is a common and avoidable way to lose an afternoon. Lifting a ban is available at every edition precisely because of this, but it is easier not to need it.

Remote or local
$ ohm server fail2ban config install

Which instance a ban belongs to​

The jails are server-wide - ohm-login, ohm-4xx and ohm-429 watch the web server's access log of every instance, so no instance can get an address banned by writing to its own log, and ohm-ssh the SSH gateway's - so a ban is not tagged with an instance. Antelao attributes a ban to an instance by the database named in the match that triggered it. That is why a ban's instance column can be empty for jails that do not carry one.

Behind a reverse proxy, bans can block nothing

If something else terminates the connection in front of the managed nginx, every packet arrives from the proxy. Fail2Ban then bans the proxy's address, or bans a client address that no longer appears in the packets at all.

Worse, nginx's real-IP handling rewrites the access log to show the true client address, so the logs look exactly like a directly exposed server and the bans look correct. They are applied perfectly and stop nothing.

The fix is to declare the proxy in the server configuration's trusted proxies list, so the real client address is recovered from the forwarded headers. The server then enforces every ban at the web server as well: for a site behind a trusted proxy, nginx refuses each banned address the proxy forwards with a 403, within half a minute of the ban and at once after a ban or an unban made by hand, and admits it again once the ban is lifted. A trusted proxy, the Fail2Ban whitelist and the server itself are never refused this way. The Fail2Ban tab and the instance's Trusted proxies notice say so.

Without a trusted proxy, check that bans are actually dropping traffic in the per-rule packet counters of the jail's chain rather than trusting the ban list, for example for ohm-login:

sudo iptables -L f2b-ohm-login -n -v

Counters that stay at zero while the ban list grows mean the bans stop nothing. Behind a trusted proxy they stay at zero by design: the refusals are the web server's, with status 403 in the instance's access log.

A proxy that is not listed

When a proxy forwards visitors to an instance without being in that list, the Fail2Ban and CrowdSec tabs show a notice naming it: every visitor behind it looks like the proxy, so a ban of its address locks all of them out at once. CrowdSec judges a proxy on your own network like any other address, as Fail2Ban does: it trusts only this server itself, its own whitelist and the trusted proxies, so a few failed sign-ins through an unlisted proxy can ban it and everyone behind it. Trust this proxy on the notice adds it to the list. See An untrusted proxy.

Removing it​

Remote or localagainst pelmo
$ ohm server fail2ban remove --server pelmo

Available at every edition, for the same reason lifting a ban is.

The console's own sign-in

The jails watch the instances, not the console: the console's sign-in is protected by the Sign-in limit on Security > Firewall > Configuration, the account lockout and, when it has keys, Cloudflare Turnstile.