dotnet / aspire

An opinionated, cloud ready stack for building observable, production ready, distributed applications in .NET
https://learn.microsoft.com/dotnet/aspire
MIT License
3.38k stars 351 forks source link

Feature: Make Certificates first class citizens #4132

Open JohnGalt1717 opened 2 months ago

JohnGalt1717 commented 2 months ago

One of the hardest things to do in K8s and other orchestrators (amongst others) is certificate management. And this isn't just at the ingress. Best practices say that all coms within your k8s cluster should be encrypted and you should be using certificate validation because of the hacks against containers that can compromise your entire cluster.

There is a solution to this in the k8s world: cert-manager. And in Azure it's KeyVault certificates.

In an ideal world, we would be able to define in Aspire Host something like the following:

var ca = .AddCA({some params});

Then, on each Micro Service/Resource add .AddSigningCert(ca) .AddCommsCert(ca)

And then

.AddYarp() .AddSigningCert(ca) .AddPublicCert(domains, or whatever)

For developers, what this would do is use step-ca or something similar in a docker container, and then spin the certs from there for the whole thing, and on the Yarp ingress those certs would be added to trust on every platform (I know, hard on Linux, but do it anyhow please)

And this would then generate the manifest information that would be necessary for Azure/AWS/Aspirate etc. to create the ingress, and setup all of the tooling internally and map it.

I.e. in K8s this would spin cert-manager, define the CA resource, define certificates for each of the micro services spun from the cert-manager CA, setup Yarp for ingress, define all of the micro-services endpoints on that Yarp instance, and use cert-manager to acquire the cert from Azure KeyVault/let's encrypt/AWS's version etc.

You would be a hero if this worked, and you'd be providing people with a HUGE reason to use C# in the world of micro-services and orchestration.

While I understand this is probably not doable for V1, it would be the next major win for V2 to have this because it squares the circle of everything other than just plain secrets and configmaps and is THE hardest thing to get right in this space.

webprofusion-chrisc commented 1 month ago

A primary main concern for certificate setup is how you acquire the cert and keep it fresh, issuing a new cert on service startup is a massive no-no if using a public CA and instead you should probably pull from a secrets vault (otherwise you will quickly achieve your own denial-of-service via rate limits for issuance). Likewise if a service is long lived then it needs to refresh it's certificate(s) periodically. E.g. some CAs might issue a 24hr cert that would reasonably expire before the next service restart.

If issuance is outside the scope, then generically it's a secrets fetch/refresh problem. If issuance is in scope then you would need generic cert lifetime management hooks. Certs are typically for dns identifiers but they can cover many other things too, issuance can take seconds, minutes or hours (depending on the CA issuance process), so you can't block on issuance/renewal anywhere.

Background: I make a dotnet based ACME cert manager (for windows) and we have prototype k8s stuff, but it's clearly not a k8s specific problem.

JohnGalt1717 commented 1 month ago

So @webprofusion-chrisc did i miss anything that I should update in my proposal above? My concept is that it's declaritive, and at dev time it's really spinning docker containers that do the CA and everything else, and then it just dumps the config, and then Aspirate etc. would then go and orchastrate cert-manager on k8s as an example, and Azure could do the same with Azure KeyVault. (and there could be permutations with k8s and Azure Keyvault which can map those certs to file shares if you want)

This would abstract out what's actually happening, while doing the right work.

On spin up, it just loads the cer/key from the mapped directory into the containers with the mappings on the resources, the CA is responsible for (and should block until) it has refreshed all tokens on spin up in dev, and in k8s etc, that's a service that's running automatically doing it.