Skip to content

Refactor certificates role to normalize server and client certificate…#89

Merged
evgeni merged 2 commits intotheforeman:masterfrom
ehelms:refactor-certificates
Mar 5, 2025
Merged

Refactor certificates role to normalize server and client certificate…#89
evgeni merged 2 commits intotheforeman:masterfrom
ehelms:refactor-certificates

Conversation

@ehelms
Copy link
Copy Markdown
Member

@ehelms ehelms commented Feb 24, 2025

… creation

@ehelms ehelms force-pushed the refactor-certificates branch from 67f6872 to d68d99a Compare February 24, 2025 19:24
@ehelms ehelms force-pushed the refactor-certificates branch 2 times, most recently from ca0e6fb to b41d1f9 Compare February 26, 2025 17:38
@ekohl
Copy link
Copy Markdown
Member

ekohl commented Feb 26, 2025

Again, this also makes me think about how we could implement an ACME client to get what we used to call custom certificates

@ehelms
Copy link
Copy Markdown
Member Author

ehelms commented Feb 26, 2025

Again, this also makes me think about how we could implement an ACME client to get what we used to call custom certificates

I am more focused on the "input", the interface for providing certificates and so think of this certificate role as a reference implementation.

@ekohl
Copy link
Copy Markdown
Member

ekohl commented Feb 26, 2025

I often feel Ansible with all of its variables doesn't make a clear distinction between (user) input and (intermediate) output. It's late so I'm not going to thoroughly review it now, but perhaps it's already something to think about

@ehelms ehelms force-pushed the refactor-certificates branch from b41d1f9 to f801daf Compare February 27, 2025 02:19
@ehelms
Copy link
Copy Markdown
Member Author

ehelms commented Feb 27, 2025

I often feel Ansible with all of its variables doesn't make a clear distinction between (user) input and (intermediate) output. It's late so I'm not going to thoroughly review it now, but perhaps it's already something to think about

Oh I have :) But baby steps.. first this re-factor, then my take on that part as this helps pave the way.

@ehelms ehelms force-pushed the refactor-certificates branch 2 times, most recently from 07b9334 to d16c3fe Compare February 27, 2025 14:07
Copy link
Copy Markdown
Member

@ekohl ekohl left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This may be taking it a step too far in this PR, but I'd be tempted to work towards getting rid of the separate server and client certs. In vanilla Foreman we (by default) always used the same certificates to be the server and the client.

In other words, stop generating separate server certs and make server_certificate and server_key default to client_certificate and client_key. If a user provides server certs, they are expected to already be signed by some CA.

This will be an issue for services that we run on localhost today because no respected CA should ever sign a certificate for localhost, but that's the general direction I'd like to move to.

-config "{{ certificates_ca_directory }}/openssl.cnf"
-key "{{ certificates_ca_directory_keys }}/{{ certificates_hostname }}.key"
-subj "/CN={{ certificates_hostname }}"
-addext "subjectAltName = DNS:{{ certificates_hostname }}"
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have a recollection that by default the extension isn't copied over when calling openssl ca. https://stackoverflow.com/questions/33989190/subject-alternative-name-is-not-copied-to-signed-certificate mentions you should use -copy_extensions copyall when signing

@ehelms
Copy link
Copy Markdown
Member Author

ehelms commented Feb 28, 2025

This may be taking it a step too far in this PR, but I'd be tempted to work towards getting rid of the separate server and client certs. In vanilla Foreman we (by default) always used the same certificates to be the server and the client.

I think this is a fair design goal that we can work to support. The current certificates implementation for upgrades will be the puppet-certs implementation that does split server and client certificates and indicates their purpose. That's one implementation. This reference implementation attempts to not stray far from that basic design principle. I think, at the start, this also helps make sure we consider that there are places where client certificates are expected and places where server certificates are expected. Early on, I don't want to lose site of that and have a gap.

The place I'd like to end up with is to have the following:

  1. A reference implementation for certificate generation, that is used by default and by developers.
  2. Documentation that lays out what the required certificates are and any requirements.
  3. Eliminate the server vs. default concept that we created - either use the defaults or bring them all.

Then for example, we can document it was a client certificate is needed here and a server certificate need there. You the user can choose to have a single certificate for both, that has both client and server purpose. Or you can choose to split them into dedicated purposes if you want that level of separation of concern.

@ekohl
Copy link
Copy Markdown
Member

ekohl commented Feb 28, 2025

I was wondering how to verify this. Could we somehow run katello-certs-check against the result of this in CI? Do we have any molecule tests already that we could leverage?

@ehelms
Copy link
Copy Markdown
Member Author

ehelms commented Feb 28, 2025

I was wondering how to verify this. Could we somehow run katello-certs-check against the result of this in CI? Do we have any molecule tests already that we could leverage?

I think adding some tests to ensure this role does what we expect and vets the generated certificates against katello-certs-check is a good idea.

This started as a re-factor to better be in service of https://github.com/theforeman/foreman-quadlet/pull/90 to help out with our installation research.

I'll look at adding some tests and verification.

@ehelms ehelms force-pushed the refactor-certificates branch 3 times, most recently from 104be95 to 72da991 Compare March 2, 2025 15:15
@ehelms
Copy link
Copy Markdown
Member Author

ehelms commented Mar 2, 2025

I added a role to run certificate check using katello-certs-check. And, for this iteration, pulled out the certificate variables generated into a vars fie. This draws the line between user input and "internal" input parameters.

@evgeni I am thinking then for the installer split work, instead of defining them directly (f425856) you can provide a vars file that defines the certificates coming from puppet-certs and then just include that file conditionally rather than the one from the certificates role.

@ehelms ehelms force-pushed the refactor-certificates branch 2 times, most recently from 4c98c80 to f6116f6 Compare March 3, 2025 14:16
@ehelms ehelms force-pushed the refactor-certificates branch 3 times, most recently from d9a82a0 to ec884a8 Compare March 3, 2025 14:59
@ehelms ehelms force-pushed the refactor-certificates branch from ec884a8 to ff352bd Compare March 3, 2025 18:15
creates: "{{ certificates_ca_directory_certs }}/{{ certificates_client }}.crt"
- name: 'Generate CA certificate'
ansible.builtin.include_tasks: ca.yml
when: certificates_ca
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Under which circumstances would this be false?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
Member

@evgeni evgeni Mar 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, so you ran this once with certificates_ca: true to create a CA, and then you run it with false (on the same machine) to create the bundle (with a new hostname -- for a different machine) without regenerating the CA?

Does that imply ca.yml is not idempotent? Or is that pure speed optimization?
(Reading the playbook I think it's idempotent?)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Speed and safety optimization I suppose. I was also not sure how the design would fall out where something else provided the certificates yet (e.g. puppet-certs). I am still not 100% and this will get re-factored as we iterate.

@evgeni
Copy link
Copy Markdown
Member

evgeni commented Mar 4, 2025

I like this, especially as it makes my "use puppet-certs certs with quadlet" frankenstein easier. And tests are also passing, so 🎉
So unless @ekohl has objections, I'd love to merge this, as it's an improvement and we can layer on further ones on top.

Copy link
Copy Markdown
Member

@ekohl ekohl left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the change where a lot of the fields on the cert become optional. It always bothered me we had those weird defaults that probably nobody changed and served no purpose.

Co-authored-by: Ewoud Kohl van Wijngaarden <[email protected]>
@evgeni evgeni merged commit ea3c88a into theforeman:master Mar 5, 2025
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants