Ditch SSSD before getting it
Februari 2024 I've attended the AIM track at FOSDEM.
And -once again- I decided I really should try SSSD.
A great solution to fix authentication to many servers at a central point (but additionally with failover).
Plus, it can also manage authorisation in sudo.
My biggest petpeeve in Linux security is that sudo is working against you when unconfigured or too loosely.
In reality almost always. And so this would be a nice solution to tackle that.
However, after 3 days of battle: configuration of several cocktails of SSSD i.c.w. GSAPI and OpenLDAP and FreeIPA...
...I made the conclusion there are no simple recipies available on the internet to accomplish even something simple.
And so decided to give up, as time is valuable, and so doing it the hard way
remains much easier.
I couldn't imagine OpenSSH never jumped in providing a solution to this need.
So I started re-reading man sshd_config and noticed AuthorizedKeysCommand.
TL;DR
Below method will:
- eliminate unnecessary maintenance of countless ~/.ssh/authorized_keys files
- centralize, but still enjoy failover when using multiple nameservers as DNS is redundant by design
- eliminate fragile dependencies, creating fatal problems, making your server unreachable
- instead allows redundancy in dependencies
- reliable automize verification of hostkey fingerprints - especially when using DNSSEC signed zones
Theo's Agent
Doing a Google search on 'AuthorizedKeysCommand' returned me example usage on OpenBSD by
Theo's Tool
, so likely Theo de Raadt.
Delving deeper in OpenSSH documentation it shows the chosen command only would need to supply the same content as a authorized_keys file.
...which we can also generate using TXT records, especially when having DNSSEC signed DNS zones.
Let's try it
Server side
So let's give it a spin. Starting at the OpenSSH deamon I placed this configuration:
AuthorizedKeysCommand /etc/ssh/dns2authkeys.sh
AuthorizedKeysCommandUser root
Initially I attempted using dnspython to extract public SSH keys from TXT records. However OpenSSL 3+ is causing trouble in Python.
Made me decide to eliminate additional software which is causing trouble instead of convience. Already causing failure before we began.
Instead let's use Bind's dig or Knot's kdig command, i.c.w. POSIX compliant shell scripting.
Since the whole thing developed organicly, it's hard to break down it's explanation chronologicly.
However, as you can see, below script:
- searches for a TXT record starting with 'v=sss'
- then looks for TXT records containing the public keys
- returns data identical to what you can put in authorized_keys
#!/bin/sh
PATH=/bin:/usr/bin:/usr/local/bin
[ -x "$(command -v dig)" ] && dig='env dig';
[ -x "$(command -v kdig)" ] && dig='env kdig';
[ -z "${dig}" ] && exit 1 # exit when dig or kdig is absent.
myself=hostname # hostname should be set correctly, corresponding to DNS records
myself='srv99.vandewoestyne.nl' # manual intervience
ans=`${dig} +short ${myself} -t TXT | grep '"v=sss ' | tail -n 1`
[ -z "${ans}" ] && exit 0 # exit when no SSS record found in DNS
ans="${ans#?}"; # strip opening "
ans="${ans%?}"; # strip closing "
for usr in ${ans}; do # assemble 'authorized_keys' content from TXT record(s)
if [ ! -z "${usr}" ] && [ "${usr}" != "v=sss" ]; then
${dig} +short ${usr} -t TXT | while read key; do
key="${key#?}"; # strip opening "
key="${key%?}"; # strip closing "
echo ${key} # return entry
done
fi
done
# any suggestions/advice to improve are welcome.
# One todo bullet point would be filtering TXT records on supported and accepted key algorithms.
The DNS part of it
We would need to place above mentioned TXT records. Since there is no standard I can make my own.
And so initially I just use one records that can point to groups at different domains (in this example i037.com).
srv99.vandewoestyne.nl A 172.232.157.80 srv99.vandewoestyne.nl TXT v=sss cto.i037.com leo.i037.com tim.i037.com
Then the actually public keys of users I placed in TXT records:
cto.i037.com TXT ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABB ... B5AXLHTbRiBzNtdEWI1J8y2P= harry@laptop leo.i037.com TXT sk-ecdsa-sha2-nistp256@openssh.com AAAAInNrLWVjZHNhLXNoYTItbmlzdHAdyNTZA ... Blb5jb20AAmtleTE= key1 verify-required leo.i037.com TXT ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMHuZJJjjgOKl+4z4j3tR7N/rgQO0WwuN2Cc/FAAA/TI key2 leo.i037.com TXT ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABB ... ebTdM= root@blackbox.ams1.unicycle.net tim.i037.com TXT ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABB ... n5rSS1wdaaagmulQ+Qo= tim@MacBook.local
And while we're at it... also place fingerprints of the public hostkeys in SSHFP records, conform RFC 4255
To do so, there is a usefull command, which -at the server- reads the hostkeys and generates the SSHFP records.
ssh-keygen -r srv99.vandewoestyne.com
srv99.vandewoestyne.nl SSHFP 1 1 84466bc1d5591bb4208ab9f2258b709b267a56a7 srv99.vandewoestyne.nl SSHFP 1 2 2f4ff75830aff217db5fe4c47ef750fee74a921481e208d7386d002aacfe7a9d srv99.vandewoestyne.nl SSHFP 3 1 3fdbb4e13c67f9b7da2e596342326a736c62faa7 srv99.vandewoestyne.nl SSHFP 3 2 8a02bc9ccd0368caa5f7660250fe3eb2bb2cf9493a6587388383886caa55598b srv99.vandewoestyne.nl SSHFP 4 1 718f33624621ba9cc3c13ddcbe20e17f7f952566 srv99.vandewoestyne.nl SSHFP 4 2 0f899ca49b91188a81a12fa869bf7dd98edb9ed69c484957efecbe306cbc2a2a
Local SSH client
Proof of concept / proof of pudding
Actually the local client doesn't need any modification, except for VerifyHostKeyDNS.
Still you may wish to place something like below in your local ~/.ssh/config
Host srv96, srv97, srv98, srv99
Hostname %h.vandewoestyne.nl
User root
VerifyHostKeyDNS yes
VisualHostKey yes
Ensure that sshd loaded it's new config.
Then on your console typing ssh srv99 should be sufficient.
And when you have a current ssh client, this host would not be automatically added to your ~/.ssh/known_hosts file.
Conclusions (so far)
- So far it was pretty simple to accomplish, but can be improved.
- Right now it does only authentication but no user-distinctive authorisation (yet).
- it would great if an SSHPK RR type would exist: "SSH Public Key", aside of the SSHFP RR type - such that public keys could be placed in DNS better
- Proper DNS is making this service redundant by design
- DNSSEC is optional, but highly recommended
- ideally you should delegate a zone to NS records in private IP space (kind of "split horizon")
Todo
- Adjust the script to generate user-specific content, such that bob cannot login as alice - which should be simple using %u
- Test adding per-user restrictions in authorized_keys
AuthorizedKeysCommand /etc/ssh/dns2authkeys.sh %u
AuthorizedKeysCommandUser root
Example of preceding restrictions in "~/.ssh/authorized_keys - which could / should also be in DNS:
no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty,command="~/pipe.sh $SSH_ORIGINAL_COMMAND" ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNo...WPY= nagios@dom.tld
Links
https://theoapp.readthedocs.io/en/latest/theo-agent/verify_signed_keys.htmlhttps://jpmens.net/2019/03/02/sshd-and-authorizedkeyscommand/