<@U071QGDLH> We have just updated our URL rewrite ...
# docker-commandbox
d
@jclausen We have just updated our URL rewrite rules from tuckey to the built in server rules, today I got a notice from hackmycf that CFIDE was available on our production server which was odd. We use the commandbox docker image running 6.1.0+00813 and ACF 2021 currently. It turns out that switching to the server rules seems to have broken the commandbox server.json profile "production". If I use tuckey rewrites
profile:production
blocks CFIDE, if I switch to the server rules I can access CFIDE. Adding
"blockCFAdmin":"external"
to server.json when running the server rules also blocks CFIDE as expected. So it seems that
profile:production
doesn't work as expected with the server rules, has anyone else seen this?
j
This would be a @bdw429s question.
👍 1
f
One thing that is worth mentioning is that it should be blocked with
blockSensitivePaths: true
which Doug also mentioned to me that he had. According to line 2282 of ServerService.cfc it looks like it should block the RDS path when profile=production https://github.com/Ortus-Solutions/commandbox/blob/4c528dc8ed17fddf02c6041ab21c6b395157d076/src/cfml/system/services/ServerService.cfc#L2265-L2286
I haven't seen Doug's full server.json, but he said that he had both blockSensitivePaths=true, and profile=production configured, but the request was def making it through to RDSServlet.
b
@dougcain I'd need to see more.
When starting the server, capture the verbose output.
There should be a whole section detailing all the lockdown items and which ones are enabled or not
Start there to verify what is actualy enabled
Determine what specific URL is not being blocked that you feel should be blocked, then start the server with the
--trace
flag and watch the debugging in the console when a request comes in to determine what rules fired and what their result was.
It's also possible you have a custom rule firing first which is opening that URL back up (as I show in the docs)
d
will have a go at getting the trace @bdw429s
ok this is in the commandbox startup -
Copy code
2025-03-11 16:59:10    | √ | Setting site [93d692bfd1fa] Profile to [production]
2025-03-11 16:59:10    |   |------------------------------------------------------------------
2025-03-11 16:59:10    |   | Profile set from profile property in server.json
2025-03-11 16:59:10    |   | Block CF Admin enabled
2025-03-11 16:59:10    |   | Block Sensitive Paths enabled
2025-03-11 16:59:10    |   | Block Flash Remoting enabled
2025-03-11 16:59:10    |   | Directory Browsing disabled
2025-03-11 16:59:10    |   | File Caching enabled
Going to CFIDE/administration/index.cfm with tracing shows the site and I get this trace
b
That's the admin though, not RDS, right?
Are we troubleshooting the admin or RDS?
d
both - the RDS was a secondary part - when admin was showing incorrectly I added
block-cf-admin()
to the server rules as profile / blockCFAdmin in server.json were not working (see above). But the secondary part is the regex behind
blockCFAdmin
doesn;t include CFIDE/main which triggered the hackmycf RDS alert I asked Pete about. So server.json rules don't seem to be honoured when using server rules and server rules when
blockCFAdmin
is used doesn't stop CFIDE/main (RDS)
b
well hold on, cfide/main is not the cf admin!
so it's not supposed to block that
cfide/administrator is the admin
cfide/main is RDS
which is blocked by the sensitive path rule when in production mode
Can you show me your custom server rules?
d
no it's the RDS bit - tuckey did block it as I hadn;t changed my server.json - we just switched to server rules
b
There's a LOT going on in that log snippet and I'm not sure which rules are baked in and which ones are your custom rules
d
I will try taking the custom rules out as they may be over riding what ever server.json is doing
b
I'm not sure what you had going on with tuckey in the past, but that tuckey file doesn't sound like it was part of the CommandBox core so I can't speak to what it may or may not have done
d
hmm seems we miss understood the running order does it go: 1. server rules 2. server.json We had this in our server rules:
Copy code
# stop processing rules when direct call to cf file
#regex( ".*\.(cfm|cfc)$" ) -> done
#regex-nocase( ".*/index\.cfm" ) -> done
which must run before what ever server.json does so /CFIDE/administrator/index.cfm was allowed by these two and then not run any more rules.
b
yes, that's why I wanted to see your rules 🙂
I saw the cfm/cfc rule int he logs and it looked like a core rule, but I could tell it was aborting your predicates
That is exactly what is happening
when you run the
done
predicate then NO MORE rules run
it's over
finished
Which is exactly how you bypass core rules!
in the docs I linked to above I show this
d
i'll blame @aliaspooryorik getting all clever 🙂
So for example, you can enable RDS on production like so
Copy code
server set web.rules="['path(/CFIDE/main/ide.cfm)->done']" --append
your custom rules run first
👀 1
allowing you final say in what happens
The lockdown rules run last
💡 1
so if you abort the rule set, then the lockdowns won't run
I was searching for
done
in your log files when I had found this
Copy code
io.undertow.predicate - Regex pattern [.*\.(cfm|cfc)$] MATCHES input [/CFIDE/administrator/index.cfm] for HttpServerExchange{ GET /CFIDE/administrator/index.cfm}.
io.undertow.predicate - Storing regex match group [0] as [/CFIDE/administrator/index.cfm] for HttpServerExchange{ GET /CFIDE/administrator/index.cfm}.
io.undertow.predicate - Storing regex match group [1] as [cfm] for HttpServerExchange{ GET /CFIDE/administrator/index.cfm}.
io.undertow.predicate - Predicate [regex( pattern='.*\.(cfm|cfc)$', value='%{RELATIVE_PATH}', full-match='false', case-sensitive='false' )] resolved to true. Next handler is [done] for HttpServerExchange{ GET /CFIDE/administrator/index.cfm}.
io.undertow.predicate - Predicate chain marked done. Next handler is [Runwar PathHandler] for HttpServerExchange{ GET /CFIDE/administrator/index.cfm}.
and that's when/why I asked to see your custom rules
a
Yeah - was me. I thought that the in-built rules ran first, before our rules, but seems it is the other way around.
I used that done rule to make the subsequent rules easier to read as you don't have quite so much in there (as it all has to be a one-liner using the .txt file)
d
thanks @bdw429s once you see it it's easy as ever 🙂
(ish)
b
You can group related handlers with a single predicate, but the syntax can be a little touchy, and it's near impossible to do in the JSON file, so you'd want an external rule text file
Copy code
predicate() -> {
  handler();
  another-handler();
  more-here()
}
That could be useful to group a bunch of non-CF rules by reversing your predicate above
Copy code
not regex( ".*\.(cfm|cfc)$" ) {
  ... non CF rules here
}
a
Just looking in the docs to see if I missed that the in-built rules run last
reminds me - I did open a PR on the docs a while back https://github.com/ortus-docs/commandbox-docs/pull/239
not regex( ".*\.(cfm|cfc)$" )
that's exactly what I've just done (need to test it all again) 🙂
b
I linked to the page that covers that above
1
image.png
👀 1
☝️ Which includes that warning 🙂
a
You expect me to read the whole page?
😛 1
Anyway - thanks all
b
There is even an
else
keyword to do basic if/then constructs
but I've found it to be a little buggy in the past https://issues.redhat.com/browse/UNDERTOW-1740
👀 1
a
I did have a hard time converting from tuckey to undertow predicate rules. It all works, well, apart from some minor security issues.... 🙈
b
a
I did already - see my PR :)
b
sweet!
Off to github to search
b
I get so many E-mails from github every day, I may as well get none 😕
a
No problem
Hopefully, I've added some useful examples
A lot of trial and error
b
I'm not entirely sure what the utility of
Copy code
true -> anything-here()
is, but it works just as well as the code
Copy code
if( true ) {
  code here...
}
curious about the choice of 410
To me, that tells me the server knows about the thing I'm looking for and is hiding it, where as 404 is the server just playing dumb like it's never heard of it in its life 🙂
FWIW, I use
set-error( 404 )
for that because it allows custom error page to work
the response-code() handler just stops processing and won't use servlet error pages. That may not matter as of CommandBox 6 however, where I have error page handling duplicated outside the servlet
a
I don't use
410
just giving examples of different response codes
👍 1
set-error
is a commandbox thing only?
b
You know that a few years from now, there will be CF/CommandBox apps all over the internet returning 410, because that's what the docs "recommended" right? 🙂
It's funny how any copy/paste example, no matter how bad, will be taken as gospel truth and used in people's setups
a
ha - feel free to change it
b
set-error is both. I added a ticket to undertow (and maybe even sent them a pull for it) but it takes bloody ages for them to do anything so I also added it to Runwar directly
👍 1
They've since added it to the undertow core as well
a
ooh - nice
b
I was actually at the Redhat booth at Devnexus harassing them about some unmerged pulls from 3 YEARS AGO in Undertow
😁 1
open source is hard sometimes even for big companies with people paid to manage the products
a
So
response-code()
is returning the nice commandbox error page, how does set-error different - is it that it returns a custom page (if you have one defined?)
b
ok, then it's prolly the same as of CommandBox 6
In CommandBox 5 and prior where error pages were in the servlet, it would just return undertow's default will error page
a
gotcha
b
In CommandBox 6, only requests that pass the servlet pass predicate are passed to the servlet, all others are dealt with directly in undertow's java web server that I control
And I basically reproduced the error page functionality at that level as well so that would work
👍 1
a
When I hit
/missingfolder/missingfile.cfm
I get the ACF missing template handler. Is there an in-built way to just 404 for any missing cfml file?
b
Not really
The problem is all CF files must be resolved by CF
🤔 1
a
This seems to work
path-suffix( { ".cfc", ".cfm" } ) and not is-file and not is-directory -> { set-error( 404 ); done }
but would rather use an in-built rule (or work out why in-built rule isn't kicking in!)
b
since CF knows about CF mappings and servlet mappings that the web server doesn't know about
So CF controls the error message for those
a
ah right - mappings, yeah... hmmm
b
but you can customize CF's behavior there
otherwise, requests like
/CFIDE/administrator/enter.cfm
would always be a 404 from the web server
Now, if you happen to ONLY hit CF files that exist in the webroot, you can prolly get away with that
a
I can hit
/CFIDE/administrator/index.cfm
with that rule above in play
b
lol, that's a bad example
the reason that works is because the custom resource manager in runwar has special knowledge of CFIDE
a
Yeah - I get your point 🙂 Will have a think about it
b
Can you explain what you mean when you say "in built rule" not kicking in?
a
It was only if commandbox already had something to handle this. I was typing my comment as you answered so we crossed over a bit.
Was just checking some other custom rule I'd created wasn't stopping something else
b
Here's the interesting thing-- CommandBox default error pages kick in if the request is done, there is an "error" status code AND no response has been written
So the check always runs, but since CF has always already written out it's default error page, CommandBox/runwar doesn't touch it any further
If you can get CF's default missing template handler to not write anything, it MIGHT kick in CommandBox's page, but I'm not 100% about how it detects the empty response
a
interesting....
b
I register a
addDefaultResponseListener()
in Undertow, and it does the logic of deciding whether to fire it or not
actually, looking at the undertow code, it looks to always run the default response listeners so long as the response hasn't been flushed already. And then in my listener, I do this check again
Copy code
if (!exchange.isResponseChannelAvailable()) {
                return false;
            }
I'm guessing, even if you had an empty missing template handler, the core of Lucee/ACF prolly automatically run a flush through to the servlet to write out the response even if it was empty which means you'd never be able to fire the undertow default response listener.
So you're prolly stuck with just sharing the same include across the both of them. You can use the missing template method in your application.cfc for responding to missing includes, and it would prolly need configured in the server settings as well since the application.cfc has chicken/egg issues with top level missing files.
a
Thanks Brad. Just playing around with options at the moment, but useful to know what undertow is expecting.
b
yep, it's a deep dark world under the covers
It took me quite some time to even understand all that runwar/undertow was doing
Denny V originally wrote runwar, and I've taken it over at Ortus
a
I'm always amazed (and grateful) for what you do
👍 1
curious about the choice of 410
updated PR to a 404 🙂