How to block any useragent but two on whitelist with basicauth rule?

1. Output of caddy version:

v2.5.2 h1:eCJdLyEyAGzuQTa5Mh3gETnYWDClo1LjtQm2q9RNZrs=

a. System environment:

Windows 11

d. Part of Caddy config:

	@notLocal {
		not remote_ip 192.168.0.0/16 172.16.0.0/12 10.0.0.0/8 127.0.0.1/8
		not remote_ip forwarded wanip
	}

	basicauth @notLocal {
		username password
	}

3. The problem I’m having:

Cannot figure out how to implement blocking all useragents but two I would add as whitelist with BasicAuth rule

I don’t understand the question. Please be more specific.

I would like to block any useragents request but “(string1 OR string2)”

either deny them in some way or redirect them to google or any given page.

Is that possible and how would it be done.

So you mean the User-Agent header?

You can use the header_regexp matcher to match values of that header.

Yes User-Agent header

I have already looked at header_regexp and searched for examples, and could not find any example of how it can be done by blocking any but 1 or 2 as whitelist

Match the two things you want, then put not in front to invert the result.

@notBlock not header_regexp User-Agent (?i)(string1|string2)

	handle @notBlock {
		reverse_proxy ip
	}

Would this work?

Try it and find out. You can use curl -v -H'User-Agent: string1' and see what happens.

1 Like

I made a sample on my own pc testing local…

{
        http_port 680
        https_port 643
}

:80 {
					####@notBlock not header_regexp User-Agent (?i)linux.*android|windows\s+(?:ce|phone)
					####@notBlock not header_regexp User-Agent "(?i)(firefox|windows|mozilla)"
					####@notBlock not header_regexp User-Agent "(*firefox|windows|mozilla)"
					####@notBlock not header_regexp User-Agent "((?i)Firefox|Windows|Mozilla)"
					####@notBlock not header_regexp User-Agent (?i)windows.*mozilla|
		
		
		@notBlock not header_regexp User-Agent (Mozilla/5.0)
        handle @notBlock {
                reverse_proxy IP:Port {
                }

                @notLocal {
                               not remote_ip 192.168.0.0/16 172.16.0.0/12 10.0.0.0/8 127.0.0.1/8
                }
                basicauth @notLocal {
                        USER PASS
                }
        }
}

I have tried so many combination I scratching and pulling hair out, I don’t get it.

I want to allow these only and nothing else the name1 and name2 should be enough?

name1/1.2.3 (Linux; Android 11)
name2/1.2.3 (SHIELD Android TV)

When I added

@notBlock not header_regexp User-Agent (?i)linux.*android|windows\s+(?:ce|phone)

It obvious worked and I could get in with my windows desktop with user-agent

Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:103.0) Gecko/20100101 Firefox/103.0

But I only found 2 results on google to what I want and this was apparently for cellphone, so its no good…

So I’m now on this and I removed “not” even tho you said to add it, I’m confused.

{
        http_port 680
        https_port 643
}

:80 {

    @notBlock header_regexp User-Agent TestName1*|TestName2*
    handle @notBlock {
            reverse_proxy IP:PORT {

            }


            @notLocal {
                    not remote_ip 192.168.0.0/16 172.16.0.0/12 10.0.0.0/8 127.0.0.1/8
            }
            basicauth @notLocal {
                    USER PASS
            }
    }

Well, it depends what you’re trying to do with the matcher. You said “how to block”, and the answer to that is to negate the matcher and then do something like abort or error or respond "Nope" 403 with that.

It’s just like an if/else block. You need to decide if you want the “happy path” to be in the if or in the else.

I want to block ANYTHING but those 2 names, so that I don’t have to add 1000 different agents to the block list.

And then give either error,redirect or maybe notfound

That’s exactly what I’m saying. Do something like this then:

@blocked not header_regexp User-Agent TestName1*|TestName2*
abort @blocked

Sorry I don’t get it it, don’t you mean I need to remove “not”?

If I do

@blocked not header_regexp User-Agent TestName1*|TestName2*
abort @blocked

Everything except TestName1*|TestName2* is allowed which is not what I want, I want those 2 to be allowed only.

but if I remove the “not”

@blocked header_regexp User-Agent TestName1*|TestName2*
abort @blocked

Only allowed User-Agents is now

TestName1*|TestName2*

I tested with curl and manually going to the url, with not present both included names are blocked and everything else is accepted as user-agents, removing not blocks everything but allows TestName1*|TestName2* to bypass which is what I want, am I doing something wrong?

What I’m trying to say is that for me it works opposite of what you are writing it should do.

Conf

{
        http_port 680
        https_port 643
}

:80 {
        encode gzip
        log {
                format transform [{ts}] - User={user_id} - X-Forwarded-For={request>headers>X-Forwarded-For} - remote_ip={request>remote_ip} Country={request>headers>Cf-Ipcountry} {request>method} {request>headers>X-Forwarded-Proto} {request>host} {request>uri} {request>headers>Referer>[0]} {request>headers>User-Agent>[0]} - {request>proto} {status} {size} - {
                        #{request>headers} <--- add this if you want full log
                        time_format "02/Jan/2006 15:04:05 -0700"
                }

                output file C:\Users\wazer\Desktop\caddy\logs\localhost.log {
                        roll true # Rotate logs, enabled by default
                        roll_size_mb 5 # Set max size 5 MB
                        roll_gzip true # Whether to compress rolled files
                        #roll_local_time true # Use localhost time
                        roll_keep 2 # Keep at most 2 log files
                        roll_keep_days 7 # Keep log files for 7 days
                }
        }

        @blocked header_regexp User-Agent TestName1*|TestName2*
        abort @blocked

        handle @blocked {
                handle_path /test {
                        root * C:\Users\wazer\Desktop\caddy\test.txt
                        file_server browse
                }
        }

        handle @blocked {
                reverse_proxy ip:port {
                }

                @notLocal {
                        not remote_ip 192.168.0.0/16 172.16.0.0/12 10.0.0.0/8 127.0.0.1/8
                }
                basicauth @notLocal {
                        USER PASS
                }
        }
}

I’m quite sure I’m correct.

Caddyfile:

:8883 {
    @blocked not header_regexp User-Agent TestName1*|TestName2*
    abort @blocked

    respond "Hello"
}

Test:

$ curl -H'User-Agent: TestName1' http://localhost:8883
Hello
$ curl -H'User-Agent: aaa' http://localhost:8883
curl: (52) Empty reply from server

FWIW, I’m not sure your regexp does what you expect. TestName1* means “TestName followed by zero or more 1 characters”. You probably mean to do TestName1.* or something to that effect. But I digress.

1 Like

Hey I made a video, I hope I’m allowed to post it so you can see what I mean.

1 Like

Ah right, that’s because handle is higher on the directive order than abort, so abort gets sorted to the end or your list of handlers.

If you apply the matcher to a handle block which then has abort within it, then it would do what you want.

1 Like

How to do that correct, my brain is malfunctioning right now, because I’m soo confused :smiley:

Adding it above root * C:\Users\wazer\Desktop\caddy\works.txt from my user config will say empty to wrong useragent now but it wont output anything from allowed user agent, will show Content-Length: 0

		@blocked not header_regexp User-Agent TestName1*|TestName2*
		abort @blocked
	
        handle @blocked   {
				
                handle_path /works  {
			abort @blocked
                        root * C:\Users\wazer\Desktop\caddy\works.txt
                        file_server browse
                }
        }

Also I’m trying to think of how to let

@blocked header_regexp User-Agent TestName1*|TestName2*
abort @blocked

Be ignored when you are on the same network, like the basicAuth, so its only on the outside/external ip restrictions is for both basicauth and user-agent block

:80 {
	@blocked not header_regexp User-Agent TestName1*|TestName2*
	handle @blocked {
		abort
	}

	handle {
		@notLocal not remote_ip 192.168.0.0/16 172.16.0.0/12 10.0.0.0/8 127.0.0.1/8
		basicauth @notLocal {
			USER PASS
		}

		reverse_proxy ip:port
	}
}
1 Like

Okay now the user-agent is working as you say, but now the basicauth is no longer working as it did before, I now have to remove “not” from remote_ip to let it allow to bypass basicauth when on same local network.

and by that how would I add bypass to the not header_regexp when on same local network too?