I’m migrating DemureSoft over to the new gitplace at t/suki, but during the migration one of the members got banned by fail2ban. This is what they did:

  1. https clone (failed)
  2. set their password on forgejo
  3. https clone (success)
  4. https fetch (could not connect, banned by fail2ban)

I was not expecting them to be banned at all, much less so quickly, so I took a look at the fail2ban logs at log/fail2ban/fail2ban.log (member’s ip address redacted):

2025-04-14 16:17:35,681 fail2ban.filter         [747]: INFO    [nginx-unauthorized] Found x.x.x.x - 2025-04-14 16:17:35
2025-04-14 16:17:45,689 fail2ban.filter         [747]: INFO    [nginx-unauthorized] Found x.x.x.x - 2025-04-14 16:17:44
2025-04-14 16:17:45,690 fail2ban.filter         [747]: INFO    [nginx-unauthorized] Found x.x.x.x - 2025-04-14 16:17:45
2025-04-14 16:18:47,145 fail2ban.filter         [747]: INFO    [nginx-unauthorized] Found x.x.x.x - 2025-04-14 16:18:47
2025-04-14 16:20:44,052 fail2ban.filter         [747]: INFO    [nginx-unauthorized] Found x.x.x.x - 2025-04-14 16:20:44
2025-04-14 16:20:44,287 fail2ban.actions        [747]: NOTICE  [nginx-unauthorized] Ban x.x.x.x
2025-04-14 16:30:44,586 fail2ban.actions        [747]: NOTICE  [nginx-unauthorized] Unban x.x.x.x

Taking a look at the nginx log log/nginx/access.log, I see (member’s username and ip address redacted):

x.x.x.x - - [14/Apr/2025:16:17:35 +0000] "GET /DemureSoft/Ultraprocessor-Ribbon.git/info/refs?service=git-upload-pack HTTP/2.0" 401 13 "-" "git/2.43.0"
x.x.x.x - username [14/Apr/2025:16:17:44 +0000] "GET /DemureSoft/Ultraprocessor-Ribbon.git/info/refs?service=git-upload-pack HTTP/2.0" 401 139 "-" "git/2.43.0"
x.x.x.x - username [14/Apr/2025:16:17:45 +0000] "GET /DemureSoft/Ultraprocessor-Ribbon.git/info/refs?service=git-upload-pack HTTP/2.0" 401 139 "-" "git/2.43.0"
x.x.x.x - - [14/Apr/2025:16:17:55 +0000] "GET /user/events HTTP/2.0" 200 27 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0"
x.x.x.x - - [14/Apr/2025:16:17:55 +0000] "GET /user/settings HTTP/2.0" 200 28177 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0"
x.x.x.x - - [14/Apr/2025:16:18:01 +0000] "GET /user/events HTTP/2.0" 200 0 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0"
x.x.x.x - - [14/Apr/2025:16:18:01 +0000] "GET /user/settings/security HTTP/2.0" 200 31335 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0"
x.x.x.x - - [14/Apr/2025:16:18:04 +0000] "GET /user/events HTTP/2.0" 200 0 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0"
x.x.x.x - - [14/Apr/2025:16:18:04 +0000] "GET /user/settings HTTP/2.0" 200 28176 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0"
x.x.x.x - - [14/Apr/2025:16:18:05 +0000] "GET /user/events HTTP/2.0" 200 0 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0"
x.x.x.x - - [14/Apr/2025:16:18:05 +0000] "GET /user/settings/account HTTP/2.0" 200 30995 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0"
x.x.x.x - - [14/Apr/2025:16:18:06 +0000] "GET /user/settings/account HTTP/2.0" 200 30993 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0"
x.x.x.x - - [14/Apr/2025:16:18:35 +0000] "GET /user/events HTTP/2.0" 200 0 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0"
x.x.x.x - - [14/Apr/2025:16:18:36 +0000] "POST /user/settings/account HTTP/2.0" 303 0 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0"
x.x.x.x - - [14/Apr/2025:16:18:36 +0000] "GET /user/settings/account HTTP/2.0" 200 31362 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0"
x.x.x.x - - [14/Apr/2025:16:18:47 +0000] "GET /DemureSoft/Ultraprocessor-Ribbon.git/info/refs?service=git-upload-pack HTTP/2.0" 401 13 "-" "git/2.43.0"
x.x.x.x - username [14/Apr/2025:16:18:58 +0000] "GET /DemureSoft/Ultraprocessor-Ribbon.git/info/refs?service=git-upload-pack HTTP/2.0" 200 172 "-" "git/2.43.0"
x.x.x.x - username [14/Apr/2025:16:18:59 +0000] "POST /DemureSoft/Ultraprocessor-Ribbon.git/git-upload-pack HTTP/2.0" 200 472 "-" "git/2.43.0"
x.x.x.x - username [14/Apr/2025:16:19:14 +0000] "POST /DemureSoft/Ultraprocessor-Ribbon.git/git-upload-pack HTTP/2.0" 200 257310866 "-" "git/2.43.0"
x.x.x.x - - [14/Apr/2025:16:20:44 +0000] "GET /DemureSoft/Ultraprocessor-Ribbon.git/info/refs?service=git-upload-pack HTTP/2.0" 401 13 "-" "git/2.43.0"

It appears that whenever git tries tries to do HTTPS authentication, it always tries unauthenticated first. This must be what’s causing nginx to ban the user so quickly, since every normal https git action results in an unauthorized response before the username and password form is prompted to the user. Looking up this issue online to figure out how I can get around it with SWAG, I find a conversation about this issue, but it’s largely unhelpful as the maintainers and the person who opened the support request just get into an argument.

I also found a thread from someone who has the same issue with gitea, but it was also shut down in the same way.

Neither of these topics describe how to get around this issue, so I guess I have to figure it out on my own. Apparently, there’s a configuration file to change what jails are enabled at /etc/fail2ban/jail.local. I can see this file in the docker container for swag, but it’s not clear now I’m supposed to update it.

Eventually, I find this official SWAG documentation which mentions that jail.local is found at fail2ban/jail.local. I find the entry for nginx-unauthorized and disable it:

 [nginx-unauthorized]
-enabled  = true
+enabled  = false
 port     = http,https
 filter   = nginx-unauthorized
 logpath  = /config/log/nginx/access.log

Running docker exec -it <container id> fail2ban-client status, I can see that the jail has been disabled:

Status
|- Number of jail:	4
`- Jail list:	nginx-badbots, nginx-botsearch, nginx-deny, nginx-http-auth

After asking the user to pull the repo again, I can see that fail2ban doesn’t log the 401s anymore. It seems to have worked!