Skip to content

Login jails and brute-force protection

NexoVirt bans clients that attack it. Four jails count offences (“strikes”) per client address. When a client reaches the limit inside the time window, it is banned and gets a plain 403 on every request until the ban ends. Open Settings > Security (tile Login jails, admins only).

JailStrike forDefault
LoginA failed password, 2FA code, passkey or login link.10 strikes in 10 minutes, ban 60 minutes
ScannerOne request for a known exploit path (/.env, wp-login.php, ${jndi:, /phpmyadmin and similar).3 strikes in 10 minutes, ban 1440 minutes
404A request for a page that does not exist (directory busting).30 strikes in 5 minutes, ban 60 minutes
APIAn invalid API, reseller API or agent token.Off (strikes 0)

Details worth knowing:

  • Set Strikes to 0 to switch a jail off. The API jail is off by default: API tokens are long random secrets, and a billing system with a revoked token would otherwise ban itself.
  • A client that is banned again within 30 days gets a ban twice as long each time, up to 7 days.
  • IPv6 clients are counted and banned per /64 network. Other per-IP limits in the panel (for example on sign-ups) also count a /64 as one client.
  • Signed-in users never get 404 strikes, and node agents never get strikes at all. The agent heartbeat route is never jailed, even when its address is banned. Guests behind a node’s NAT share the node’s address, so a ban on it must not cut off the node’s agent.
  • Loopback addresses (127.0.0.1, ::1) are never jailed.
  • Scanner signatures are a built-in list plus a list NexoVirt keeps up to date and delivers with the daily license check. Signatures can never match panel, portal, API, login or asset paths, so a wrong list cannot lock real users out. The page shows how many signatures are built in and how many came from NexoVirt.

Active bans lists every ban with the IP, the banned range, the jail, the reason, the strikes, since and until. Unban lifts one. Recent strikes shows the offences of the last day. Unbanning is audited.

Sign-ins lists every sign-in attempt of admins, resellers and customers: time, account, role, result (Signed in, Failed, Refused), method (password, two-factor, passkey, SSO, login link, sign-up), IP address, reason and browser. Filter by result, method or role and search by account, IP or browser. The IP is the client address the jails use, so set your trusted proxies when the panel sits behind a proxy.

The same list per account is the Sign-ins tab on the customer, admin and reseller pages, and every user sees their own recent sign-ins on their account page (Profile > Recent sign-ins for admins, Account settings > Recent sign-ins in the portal, Security in the reseller portal).

Below the table:

  • Keep sign-ins (days): 7 to 3650, default 180. Older entries are deleted by the daily prune.
  • Email users when they sign in from a new address (off by default): after a successful sign-in from an address the account never signed in from before, the user gets the email Sign-in from a new address (Settings > Email). Never for the very first sign-in of an account.

Saving asks you to confirm it is you and is audited.

The jails need the real client address. If the panel sits behind Cloudflare, Plesk or another reverse proxy, every request otherwise looks like it comes from the proxy.

Under Trusted proxies, list the proxy addresses (one IP or CIDR per line). Only requests from these addresses may name the client in a header. Buttons add presets:

  • Add Cloudflare adds Cloudflare’s published ranges and selects the CF-Connecting-IP header.
  • Add local proxy adds 127.0.0.1 and ::1 and selects X-Real-IP.

Client IP header is one of X-Real-IP, CF-Connecting-IP or X-Forwarded-For. Prefer the first two: X-Forwarded-For takes its first entry, which a client can set. Behind Cloudflare, make sure the server only accepts traffic from Cloudflare, otherwise the header can be forged.

Addresses under Allowlist (one IP or CIDR per line) are never struck or banned. The page tells you when your own address is not on the list and how to lift a ban from the shell. Save everything with Save settings (it asks for step-up confirmation).

If you locked yourself out, run on the server as the panel’s system user:

Terminal window
sudo -u nexovirt php spark security:bans # list active bans
sudo -u nexovirt php spark security:unban 203.0.113.7

For an IPv6 client use its address or the network/64 key shown by security:bans. Bans also expire on their own.

The Domains and access card on Settings > Security limits where admins can sign in from and lets the admin area, the customer portal and the reseller area each run on their own domain. Every field is empty (off) until you fill it in. Saving asks for step-up confirmation.

Under Admin addresses list the IP addresses or CIDRs (one per line) admins may use. When the list is set:

  • Every /panel page from another address answers a plain “Not Found”, and an admin who is still signed in is signed out.
  • Admin sign-in from another address fails on every path (password, 2FA, passkey, SSO) with the same message as a wrong password, and counts as a strike in the login jail.
  • The customer portal, the reseller area, the REST API (API tokens have their own address lists) and the node agents are not affected.

The panel uses the real client address after the trusted proxies, so set those first when the panel sits behind a proxy. The card shows your current address with an Add my address button, and it refuses to save a list that does not contain your current address (“you would lock yourself out”).

Fill in Admin domain, Customer portal domain and Reseller domain in any combination:

  • An area with its own domain only works there: its pages answer “Not Found” on other hosts. An admin who signs in on another host gets the same answer as a wrong password.
  • A customer or reseller who signs in with the right password on another host (an old bookmark after the move) is sent to their own sign-in page, which says “Please sign in here.”. Nothing is signed in on the wrong host and no login jail strike counts; a wrong password counts as usual, so the answer never shows whether an account exists.
  • When the customer portal has its own domain, the sign-in page on a host that serves no other sign-in (no admin sign-in for that host and address, no reseller sign-in) redirects to the portal’s sign-in page. The redirect always goes to the configured domain, never to the address the browser sent.
  • An area without its own domain keeps working on the main domain, as before. You can, for example, set only an admin domain.
  • Customers or resellers who land on another area’s domain are sent to their own area (its domain, or the main domain). Admin pages are never redirected, so the admin domain is not revealed elsewhere.
  • Mails link to the right domain per role: customer mails to the portal domain, reseller mails to the reseller domain, admin alerts and approval mails to the admin domain.
  • Two areas cannot share a domain. A domain equal to the main domain counts as off.

Changing a domain takes two steps, so a typo cannot lock you out:

  1. Enter the new hostname and save. It is stored as pending: nothing changes yet. The area keeps working where it did, and the panel also answers on the new hostname (every area works there, including the admin sign-in and /panel, because that is where you confirm it; the admin address list still applies there). The card shows “Pending: open https://<host>/panel to confirm it.” with a link and a Cancel pending change button.

  2. Open https://<host>/panel and sign in. A dialog asks “Use <host> as the <area> domain?”. Activate moves the area to the new hostname at once: from then on it only works there and the old address stops serving it. Discard drops the pending value and sends you back to the current admin address. Activate only works on the new hostname itself. Admins without permission to change settings see “A domain change is waiting for an administrator” instead.

Removing a domain (saving an empty field) takes effect at once, since it only lifts restrictions. For the portal and reseller domains, the save warns when the hostname does not resolve yet or resolves to a different address than the current panel host (behind Cloudflare this can be fine).

Web server and certificates: when Settings > Domain has a hostname, NexoVirt adds a server block and a certificate for each area domain (pending ones too) within a minute, with the same certificate mode as the main hostname. Point the DNS records at the server first. A failed area certificate is shown on the card and never affects the main hostname. Without a hostname under Settings > Domain, point the names at the panel yourself (DNS plus your own reverse proxy or certificate).

Run on the server as the panel’s system user:

Terminal window
sudo -u nexovirt php spark security:admin-access --show # current addresses, domains and pending domains
sudo -u nexovirt php spark security:admin-access --clear-ips # admins from any address again
sudo -u nexovirt php spark security:admin-access --clear-domain # admin area back on the main domain
sudo -u nexovirt php spark security:admin-access --clear-domains # all three areas back on the main domain

--clear-portal-domain and --clear-reseller-domain clear one area each. Every clear removes the active and the pending value.

On servers where the installer manages the web server (nginx), it also installs fail2ban and hands panel bans to it. Bans on a node’s address stay inside the panel and never go to the firewall. Every ban is written to writable/logs/security.log as a NEXOVIRT-BAN line, and a nexovirt fail2ban jail turns it into a firewall ban on ports 80 and 443, so the client is dropped before nginx. The default sshd jail is switched on as well. The updater re-applies this configuration; a failure to set it up only produces a warning and never stops an install or update.

On servers that run behind Plesk or another existing web server the installer does not touch fail2ban: bans stay inside the panel.

NexoVirt is developed and designed by Nucez Software, your partner for custom software and web design.