Hacker News new | ask | show | jobs
by wahern 1034 days ago
Here's something I bet few people know: the OpenSSH configuration parser ignores duplicate directives; only the first such directive has any effect. This is more than a little counter intuitive as IME the more common semantic in configuration parsers and rules engines is for subsequent directives to take precedence over previous ones.

This may seem inconsequential, but IME when changing defaults in, e.g., /etc/ssh/sshd_config, people and software tend to append their changes to the end of a file or directive block, not the beginning, expecting those changes to be effective. Even security companies and organizations get this wrong, including various SSH bastion products I've seen. CIS Benchmarks recommendations (IIRC) and most (all?) third-party CIS audit suites don't consider precedence at all or get it wrong--e.g. by recommending appending a value, or by providing a compliance test that accepts a broken configuration. FWIW, the proper way to check whether an OpenSSH configuration directive is defined as expected is to use `sshd -T` or `ssh -G` to dump the derived, internal configuration, not by directly inspecting the configuration file(s).

2 comments

> Here's something I bet few people know: the OpenSSH configuration parser ignores duplicate directives; only the first such directive has any effect. This is more than a little counter intuitive

This is how the sudoers file works as well. I think this is desirable in software that authenticates or authorizes users, and maybe more broadly wherever security concerns are essential. That's because this logic makes it easy to create settings that can't be overridden in by adding a new file in a whatever.conf.d directory: you define those settings in the main config file before you source whatever.conf.d/* and you put some kind of special protections on that file.

Even where you're not worried about somebody evading your controls per se, it can be nice from a configuration management perspective in giving you a soft 'guarantee' that if some new hire who doesn't have the whole picture adds a new file in there, or some package tries to install a stupid default for its own service, your baseline settings can retain priority.

In other contexts you probably see the opposite behavior because what you really want is not a 'baseline configuration' but a collection of defaults in the strict sense: fallback settings to be used in case nothing is explicitly configured by the user, developer, or administrator (as the case may be).

> This is how the sudoers file works as well. I think this is desirable in software that authenticates or authorizes users, and maybe more broadly wherever security concerns are essential.

I strongly disagree - the most important trait for security-relevant configuration is that it works like you'd expect.

> That's because this logic makes it easy to create settings that can't be overridden in by adding a new file in a whatever.conf.d directory: you define those settings in the main config file before you source whatever.conf.d/* and you put some kind of special protections on that file.

What is gained by this? The administrator controls both of these locations.

> Even where you're not worried about somebody evading your controls per se, it can be nice from a configuration management perspective in giving you a soft 'guarantee' that if some new hire who doesn't have the whole picture adds a new file in there, or some package tries to install a stupid default for its own service, your baseline settings can retain priority.

Instead the new hire or a package adds important settings that now have no effect. In either case you need to review the whole configuration - reversing the order of precedence doesn't do anyting except confuse users.

If you want to have a configuration file that has the final say then make that explicit - and that absolutely doesn't require the order inside that file to be reversed. If the subdirectory is explicitly included in the main config then you can add your overrides after that include anyway.

I gave my best effort at a rationalization for what we see in OpenSSH, but in light of some of my misremembered peripheral details and your arguments here, I've changed my mind.

Sometimes you do want to set a baseline, and other times you do want to set a default. But using an unconventional ordering for overrides in your config file format isn't the right way to do anything.

Thanks for laying this out.

I suspect OpenSSH's precedence order might be a consequence of the semantics and syntax of the Host and Match directives. But I haven't looked into the history.
I'm an idiot about sudoers and got this completely backwards
I think some directives can be duplicated -- like AllowUsers.

But I got bit by something related yesterday when NixOS suddenly changed the merge order and put my AllowUsers from own file below a Match from another file and locked me out :(