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
pelmo$ ohm server fail2ban list --server pelmopelmo$ ohm server fail2ban ban 203.0.113.10 ohm-login --server pelmopelmo$ ohm server fail2ban unban --ban 203.0.113.10 --server pelmoOmit --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
| Jail | Bans on | Ships |
|---|---|---|
ohm-login | Failed sign-ins on an instance's sign-in page | Enabled |
ohm-4xx | Repeated 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 count | Enabled on a new server |
ohm-429 | Callers who keep hitting an instance's rate limit | Enabled on a new server |
ohm-ssh | Failed connections to the developers' SSH gateway | Enabled 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.
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:
| Setting | What it does |
|---|---|
| Enabled | E-mail ban notices from the Fail2Ban service. Off, nothing is sent, whatever the switches below say |
| To, Cc, Bcc | Who gets them. Bcc keeps those addresses out of sight of the others |
| Sender name, Sender e-mail | Who the notices come from |
| Bans | Mail bans as they happen. The service collects them for a few minutes and sends one message, so an attack does not flood the inbox |
| Unbans | Mail when a ban is lifted |
| Daily summary | Once 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.
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.
$ ohm server fail2ban config installWhich 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.
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.
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
pelmo$ ohm server fail2ban remove --server pelmoAvailable at every edition, for the same reason lifting a ban is.
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.
Related
- Security overview
- Rate limits
- Users and roles
- Logs
- Antelao Fail2Ban, in the Fail2Ban manual
- Bans, and whether they worked, in the Fail2Ban manual