Send real ip to application for fail2ban

Hello @qrkourier,

I was able to see my ip by that method, but when I go and boot up my forgejo and the zrok and look to the logs I get a infrastructure ip, not mine, does someone with some caddy knowledge be able to help me?

Thanks for mentioning Forgejo. I didn't realize there was an even free-er alternative to Gitea. :slightly_smiling_face:

Do you know how Forgejo is programmed to determine the client IP address?

I understand you were able to inspect the HTTP headers that are being sent to the backend by running the test endpoint and visiting it in a web browser.

I think the next step is to configure or program Forgejo to reference one of the available HTTP headers to determine the client IP address.

Hi,

No worries, always good to help some brothers out :slight_smile:

I didn't look in the source code but since it is using the zrok's infrastructure ip it either means it using the last value of X-Forwarded-For or it is ignoring it completely and using the ip of who does a request but for my testing I tried bitwarden and it also gave me the infrastructure's ip so I assume it using the X-Forwarded-For

1 Like

For sure. It really comes down to precisely how those apps determine the client IP. You've noted the most likely possibilities, and all zrok can do is report that IP in some header(s). If the other apps are reverse-proxy-friendly, they probably have a way to configure an HTTP header or documentation, at least, to let you know which header needs setting.

You're using zrok's Caddy backend mode, so you some additional flexibility to handle the HTTP header with your Caddyfile template (the target file for --backend-mode caddy).

For example, if Forgejo happens to require some HTTP header that zrok doesn't set, you may be able to set it in your Caddyfile.

Good luck. Let me know how it goes.

Hey,

Looking at the docs of forgejo for reverso proxy I found that is the nginx needed stuff:

proxy_set_header Connection $http_connection;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;

So that means X-Real-Ip is what is uses?

1 Like

Update:

Log from normal query:

2024/06/21 21:22:11 ...eb/routing/logger.go:68:func1() [I] router: polling   GET /user/events for XXX.XX.X.X:X, elapsed 3918.5ms @ events/events.go:18(events.Events)

Log with zrok:

2024/06/22 01:56:27 ...eb/routing/logger.go:102:func1() [I] router: completed POST /user/login for [ziti-edge-router connId=2147483649, logical=ziti-sdk[router=tls:ad2b1ffe-8cee-465d-b3fc-8d1100bb5f32.production.netfoundry.io:443]]:0, 200 OK in 366.2ms @ auth/auth.go:193(auth.SignInPost)                2024/06/22 01:58:21 ...s/actions/cleanup.go:29:CleanupArtifacts() [I] Found 0 expired artifacts

Caddyfile:

{                                                                               
    debug                                                                   
}
                                                                                                                                                       http:// {                                                                       
    bind {{ .ZrokBindAddress }}                                                        
    header {                                                                                  
        Strict-Transport-Security "max-age=31536000; 
        includeSubdomains"
        Content-Security-Policy "frame-ancestors 'none';  
        object-src 'none'"         
        X-Frame-Options "DENY"
        X-Content-Type-Options "nosniff"                                        
     }                                                                           
     reverse_proxy forgejo:3000 {                                                    
         header_up X-Real-IP {remote}                                                       
     }                                                                      
}
1 Like

Nice going. Does {remote} (the client IP:port pair) give you what you need for fail2ban to parse the logs?

If fail2ban is keying on the IP:port pair then you might need to use Caddyfile placeholder {remote_host} (short for {http.request.remote.host}) instead of {remote} (short for {http.request.remote}).

Hey,

Crowdsec parses the ip from the service log using a regex sort of way.

With your suggestion, I get this log:

OK in 87.6ms @ web/home.go:32(web.Home)
2024/06/25 22:34:02 ...eb/routing/logger.go:102:func1() [I] router: completed GET / for [ziti-edge-router connId=2147483654, logical=ziti-sdk[router=tls:f12da3a5-c651-4dbe-ace0-67723a5dda65.production.netfoundry.io:443]]:0, 200 OK in 49.2ms @ web/home.go:32(web.Home)

Is that message emitted by a zrok public share process? I think you're trying to parse the real IP of the client that sent a request to a public share into Crowdsec.

That message is what I get in my forgejo log while using zrok public share, and yes I would like to instead get the ip of the person accessing so i.e if they bruteforcing I can just ban them

I see the same thing. If I proxy the request to the zrok test endpoint with the same Caddyfile then I get X-Real-Ip header of the Ziti Go SDK's router connection ID, not the client IP address.

X-Real-Ip	ziti-edge-router connId=2147483648, logical=ziti-sdk[router=tls:77cb8af4-b721-434d-aaf8-2582a6fce48d.production.netfoundry.io:443]

Changing the Caddyfile to use placeholder {http.request.header.x-forwarded-for}:

     reverse_proxy forgejo:3000 {                                                    
         header_up X-Real-IP {http.request.header.x-forwarded-for}
     }                                                                      

I now get the IP from which the public share received the request in the first comma-sep position (e.g., 1.2.3.4).

X-Real-Ip	1.2.3.4, 10.51.129.128

It's unclear where the second IP came from, but maybe forgejo will use this comma-sep'd list correctly if it appears in the expected header.

Thanks a lot it worked, I also saw that someoneThat someone is you! Thank you for doing it... made a commit explaining how to do what you just told me, I hope it helps anyone with the same issue.

Two thing,sorry for so many question but forgejo also supports git using ssh, how can I expose the port 22? Compiling the module l4 could be a option but since that is unlikely to happen, I would also think that I can run two docker's, one with the caddy file and other using backend tcptunnel, is that possible?

And also, is it possible to use the crowdsec module with the caddy?

You're welcome!

You've got two alternatives for pushing commits to forgejo:

  1. create an "app" token in your profile/security settings and use it as the password with a Git credential helper like store, cache, pass, etc to push to the HTTP Git remote
  2. create a second zrok share with mode tcpTunnel and push to the private access address as an SSH Git remote like git@localhost:9191:myproject/myrepo.git

Each zrok share is a container. They can all share the same environment in a docker volume or bind.

I couldn't remove the second ip but I found this:

Yes, if a request is chained through more than one proxy server, then each proxy should add the IP of the preceding one to the existing X-Forwarded-For header so that the entire chain is preserved.

Could the two shares point to the same subdomain? So I could do git push to the same domain I use in the browser?

You're suggesting two zrok shares, one with proxy backend for HTTP requests, and one with a tcpTunnel backend for SSH. Both have a target of the same address, but with different ports. That'll work just fine!

[ERROR] invalid sharing mode for a public share: tcpTunnel

The public share modes all use HTTP.

Public shares support the modes reported by running zrok share public --help, and likewise, private share modes are listed in zrok share private --help.

However, you can make a private share's access port listen on a public IP address if you set up your own DNS, etc: Personalized Frontend | Zrok

I guess than I can't use ssh, too bad to me

You've got a couple of options to use SSH with zrok. Both require running zrok on both client and server.

You'd run a zrok share private on the server, and zrok access private on the client.

You can use either tcpTunnel or vpn mode for SSH. You could do it wish socks backend mode too, technically.