-
Notifications
You must be signed in to change notification settings - Fork 26
Refs #38478 - Introduce SSH CA certificate support #126
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
Only drafting stage. |
lib/smart_proxy_remote_execution_ssh/multiplexed_ssh_connection.rb
Outdated
Show resolved
Hide resolved
lib/smart_proxy_remote_execution_ssh/multiplexed_ssh_connection.rb
Outdated
Show resolved
Hide resolved
| ssh_options << "-o CertificateFile=#{@client_ca_cert_file}" if @use_ssh_certificates && @client_ca_cert_file | ||
| ssh_options << "-o IdentitiesOnly=yes" | ||
| ssh_options << "-o StrictHostKeyChecking=accept-new" | ||
| ssh_options << "-o UserKnownHostsFile=#{prepare_known_hosts}" if @host_public_key | ||
| ssh_options << "-o UserKnownHostsFile=#{@client_ca_known_hosts_file}" if @use_ssh_certificates && @client_ca_known_hosts_file | ||
| ssh_options << "-o UserKnownHostsFile=#{prepare_known_hosts}" if !@use_ssh_certificates && @host_public_key |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If client_ca_cert_file are client_ca_known_hosts_file are set, but the client doesn't accept them, will it fallback to traditional non-ca pubkey authentication?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will need to test this once I have the ssh cert setup in a functional state.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It appears that the known hosts file does not need to exist at all, but the authentication seems to fail if the client does not accept the certificate. I will do some further testing, but I would say that authentication attempt failing upon not accepting the certificate is the desired outcome in regards to security.
3103fb7 to
ca0debd
Compare
10ac419 to
dd262da
Compare
|
I will keep this in draft because there will be four PRs in total that should get merged at roughly the same time, but I do believe that this is now ready for review. |
1702658 to
d3f27fa
Compare
| def cert_file | ||
| File.expand_path("#{private_key_file}-cert.pub") | ||
| end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we require this strict naming convention for cert file
similarly as we do for public key file, or should we make this an editable
value in settings, with this format being the possible default?
| if @host_public_key | ||
| ssh_options << "-o UserKnownHostsFile=#{prepare_known_hosts}" | ||
| elsif @client_ca_known_hosts_file | ||
| ssh_options << "-o UserKnownHostsFile=#{@client_ca_known_hosts_file}" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we have a known_hosts file with cert authorities listed, should we change StrictHostKeyChecking to yes? To me, it doesn't make much sense to have it otherwise.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMO yes. Otherwise, we get "if you trust this cert, connect and if you don't, connect anyway".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For host keys, those should be all the combinations that can happen:
| Proxy | Host | Result |
|---|---|---|
| No record for host | plain SSH key | TOFU - success + save key |
| Record for host | plain SSH key, matching record | success |
| Record for host | plain SSH key, not matching record | failure |
| CA cert | plain SSH key | |
| CA cert | trusted certificate | success |
| CA cert | untrusted certificate | failure |
| No record for host, no CA cert | untrusted certificate | Fallback to row 1 |
| Record for host, no CA cert | untrusted certificate | Fallback to rows 2-3 |
Could we agree that this table describes the expected behaviour?
One could argue that proxy expecting a cert and host not providing any (row 4) should fail rather than falling back to traditional pubkey auth.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I too would say that row 4 could fail, but there is currently option to only have one host CA per smart proxy so it would be setting behavior for all hosts that use that proxy for REX. That is probably the only point that I see why it should fallback to plain SSH key instead of fail.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Until we decide on one or the other, I will treat the expected behavior of row 4 to be failure. I updated the StrictHostKeyChecking param accordingly. I will also make the same edits to ansible.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So row 4 should indeed result in a failure, added two more rows to the table describing one other case that we previously missed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can confirm that rows 7 and 8 also behave as intended, so the behavior of the whole feature should work as expected.
| @client_ca_known_hosts_file = settings.ssh_ca_known_hosts_file | ||
| @client_ca_public_key_file = settings.ssh_user_ca_public_key_file | ||
| @client_ca_cert_file = settings.ssh_ca_cert_file | ||
| @client_cert_file = Proxy::RemoteExecution::Ssh.cert_file |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do you treat these inconsistently? There is also Proxy::RemoteExecution::Ssh.ca_public_key_file but you don't use it. And for the others, there is no such method.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It relates to my question to the comment above: #126 (comment)
cert_file is, for now, a non-editable value whose name depends on the private key file, so it is not in settings. Personally, I do not see any problem to making the cert file name an editable value, I just wanted to confirm first whether there are any objections against it. For example, public key file name is also a non-editable value and depends on the private key file.
lib/smart_proxy_remote_execution_ssh/multiplexed_ssh_connection.rb
Outdated
Show resolved
Hide resolved
c3b0ef1 to
f8928c2
Compare
|
Switching back to draft since the feature got postponed to 3.16 |
f8928c2 to
179c7a5
Compare
179c7a5 to
01350ad
Compare
|
I have been able to verify the SSH CA cert feature as a complex of the following 4 PRs: Including the following use cases: Including hosts created by: Including scenarios: Both positive scenarios and negative scenarios, that is incorrect CA, incorrect cert, incorrect principal. => ACK to this PR |
|
@adamlazik1 could you please sort out rubocop and tests and then undraft? |
01350ad to
56dec0f
Compare
6580cbf to
99d382c
Compare
adamruzicka
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
one last nitpick, otherwise looking good
| ssh_options << "-o User=#{@ssh_user}" | ||
| ssh_options << "-o Port=#{@ssh_port}" if @ssh_port | ||
| ssh_options << "-o IdentityFile=#{@client_private_key_file}" if @client_private_key_file | ||
| ssh_options << "-o CertificateFile=#{@client_cert_file}" if @client_cert_file |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if @client_cert_file
won't this always evaluate to true because Proxy::RemoteExecution::Ssh.cert_file always returns a path, even if doesn't point at an actual file?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That is true, and also it definitely should not work like that. I Fixed the cert_file method to only return path if file is present.
99d382c to
afc3804
Compare
| { ca_public_key_file: 'CA public key', cert_file: 'certificate' }.each do |file, label| | ||
| file_path = public_send(file) | ||
| unless file_path && File.exist?(file_path) | ||
| raise "SSH #{label} file '#{file_path}' doesn't exist" | ||
| end | ||
| end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With the recent changes to how ca_public_key_file setting is handled (possibly returning nil), you can run into errors like this if you only set ssh_user_ca_public_key_file
2025-12-19T13:17:30 [W] Error details for Couldn't enable 'script': <RuntimeError>: SSH certificate file '' doesn't exist
/Users/aruzicka/vcs/foreman/smart-proxy-remote-execution-ssh/lib/smart_proxy_remote_execution_ssh.rb:75:in `block in validate_ssh_settings!'
it is not technically wrong, but it looks odd
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for catching it, I decided to handle client_cert_file differently.
afc3804 to
f5f5cc4
Compare
adamruzicka
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good
|
Thank you @adamlazik1 & @lhellebr ! |
No description provided.