-
-
Notifications
You must be signed in to change notification settings - Fork 2.5k
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
Expose a library API #45
Comments
I had a couple people ask to use the tool as a library, so I'm going to leave this open to think about it, but it will take some care to design a simple API, and I think the ACME server will cover most of those use cases. In the PR you mention not liking "hardcoding CA org name", but I definitely don't want mkcert to grow options, whether through the API or the CLI. It's a simple tool that serves a simple use case with no configuration. The API should not be different. |
Mostly agree. That's why you have a set of options all with sensible defaults. So you take an options struct, or if nil you create use the default struct. In the struct you have something like a If welcomed, I would be happy to do this refactoring, but just be advised that while the CLI args would look the exact same, the code might change considerably which is why I wouldn't go down this path without your blessing. Of course, when refactoring, I always attempt to remain extremely simple and do not over-abstract for the sake of over-abstracting and wouldn't even think of bringing in any dependencies. |
I think a library for add/delete operations on certificate stores should be a separate repository. Then this (and many others) project could use that. What about starting with this interface? type Store interface {
Add(*x509.Certificate) error
Remove(*x509.Certificate) error
} |
@adamdecaf - Disagree a bit. Just a separate folder in this project would allow anyone else to use it. That interface also doesn't really have removal right. Darwin/Linux just remove a cert path, Windows removes based on subject org. And I think as an initial first step, just doing what mkcert does is enough. So more like: type Mkcert struct {
// ...
}
func (*Mkcert) Install() error { panic("TODO: use currPlatform") }
func (*Mkcert) Uninstall() error { panic("TODO: use currPlatform") }
type platform interface {
install(*Mkcert) error
uninstall(*Mkcert) error
}
// then in platform_windows.go
var currPlatform platform = windowsPlatform{}
type windowsPlatform struct{}
func (windowsPlatform) install(m *Mkcert) error { panic("TODO") }
func (windowsPlatform) uninstall(m *Mkcert) error { panic("TODO") } Then a simple If there is more value in abstracting how to communicate with OS-specific CAs, then that is a separate concern and probably a separate library, yes. |
Why can't org be pulled from the certificate? Would additional certificates be revoked/untrusted? I see in your windows PR the org value used is copied from the certificate. https://github.com/FiloSottile/mkcert/blob/master/cert.go#L53 |
@adamdecaf - Answered in that PR. |
I find myself having a need to use this tool as a library from my Go program. What can I do to help make this happen? (Specifically, I need to install a root CA that is unique to that client machine, and then call a function and get back a *x509.Certificate for some given names and is good for some duration. Or a tls.Certificate -- doesn't matter too much. Could even be ASN bytes, just something that's not necessarily written to disk.) |
Hi Matt, thanks for helping with context! A couple questions:
|
I need it to be trusted by the web browser(s). My Go program exposes a local static file server that needs to use HTTPS because scripts on the page require HTTPS.
As long as its certificates are trusted in browsers, but an ACME server and client configuration does seem a little overkill for what I need. (Perhaps writing the files to disk is OK too, if that's recommended -- as long as I can say where they go. Since Go's ListenAndServeTLS option takes filenames rather than bytes.) |
I am resisting adding an API because I don't want to encourage depending on mkcert (and hence on installing a root) as part of end-user software. Instead I want mkcert to be a drop-in replacement for a part of the process while testing or developing things. In that way, an ACME server makes sense, even one that you can start with an API. It's just replacing the endpoint you'd hit on prod. (And it works with Caddy!) You could then use x/acme/autocert without a cache to avoid disk writes. Note that mkcert will write the CA to disk. Can you tell me a bit more about what this is for? Maybe I have to accept that after all there is a use case in shippable software, but that would take me more examples and some thinking. (I'm having similar issues deciding if it makes sense to use mkcert for internal infrastructure, but the right answer there is probably minica and manual installs.) (BTW, you can use ListenAndServeTLS with a tls.Certificate generated via tls.X509KeyPair by setting http.Server.TLSConfig. It's more obscure than it should be.) |
Yes, you're right -- I suppose I can integrate a small ACME client (like autocert, or lego) that hits a small ACME server (but then I need to find a good server that's production-ready but also minimal). However, the CA still has to be trusted by the browser.
It's for the web UI for a Go application that will be distributed to numerous independent clients. As you know, I'm no fan of messing with root stores on client machines, but I think our use case here is legitimate. The web UI is accessible only via the loopback interface, so no network transmissions are even happening. However, certain scripts such as Stripe.js refuse to work over HTTP, even in secure localhost contexts (I've already emailed them about this). This means our locally-served web app can't let the customer set their payment card, for example, even though all network transmissions use HTTPS. 🤷♂️ So, in summary, this is for software for non-technical users. No TLS interception going on, no nefarious use of the root store or custom CA: just need to ensure that the root key is generated per-client (and stored safely) and not shipped with the application, of course. :) I can understand if this is outside the use case of mkcert. Feel free to say so!
Yeah, we do this in Caddy. I just thought I'd mention it here since I don't know what the predominant use case will be for other users of this lib. I'm not sure I've even decided which method to use. 😅 |
Thanks for making this project! I also have a use case for separating the library from main: Printing to thermal printers (shipping/receipt/name badges/etc) unfortunately requires raw access to send print codes which browsers don't support for obvious security reasons. To get around this, the client can run a "print server" on http(s)://localhost:8080 and This workflow has worked great for years but recent changes in browsers and the number of HTTPS only sites has had an impact on this workflow. You can no longer effectively send an HTTP request to To get around this we give our users instructions to create their own local CA and run through all the manual steps and arcane commands to create their own "legitimate" self-signed certs to provide to our client software. We would love to automate this across multiple OSes within our software. The I believe not exposing a library along with the utility reduces the usefulness of this project even if it means some users abuse it. Separating the main utility from the library also provides better code organization and makes creating additional/future utilities easier. You can still warn users not to use the library without good cause if you are worried about abuse. Because I need this change now, I'm going to fork and make it happen. I'll simply make a I really don't want to maintain a fork so please consider. And, thanks again for all the hard work creating this! |
I also have a use case for that, which is to manage a local-domain CA for simplified local-domain certificates generation over a custom interface. The I don't really know how I could contribute to this feature though, any idea's gladly received. |
I've got a use-case for a mkcert library. I'm currently working on a local development environment for our WordPress clients, and I'd love to be able to use mkcert as a library in our go app to handle cert setup and installation. Thanks for the great tool! |
@cretz @FiloSottile I also have a use case in a project that I'm working on, so I went ahead and created a package with the install/uninstall functionality. PS: I haven't tested it in all the OSs, so feel free to fill an issue if this is not working for you. |
I'd like to be able use this via my task runner https://github.com/brad-jones/gomake (plenty of other go based task runners that would benefit too). The end goal being that a developer can just run some task like Right now I am shelling out to the |
@FiloSottile Hello. First of all, thanks for this great project which helps lot of people around as it seems (and me by the same occasion). I was wondering if mkcert was exposing any API, and found this thread. I know that issue is about a library API and not just an API exposed to the system but I thought it would be the best place to talk about this instead of opening an other issue. From the previous comments, and some other issues that I read, I understand your vision about mkcert and what are the risks that you want to reduce. Though, I think, as some others, that exposing an API for mkcert would greatly improve its usage. So, as the previous comments in this thread, I would like to also expose a use-case that I am running into. I am working on a project that I would like to make available through a Docker container. So, my idea was: it would be cool to be able to call I think a schema is shorter and more explicit than long explanations to illustrate the idea: For security concerns, from my point of view, the API MUST request an authorization token. On mkcert side, what would it mean?
On client side, what would it mean? (I will use my use case)
What would be in your opinion the downsides with this approach? If you don't see any advantages, what would be for you a nice and simple alternative? Let me know what you think. As @mholt said:
|
@noglitchyo you might want to look into smallstep/certificates. You will first need to configure the CA (certificates) using Disclosure: I'm involved in the development of those projects. |
@noglitchyo I forgot that you can |
@noglitchyo Thanks for the detailed comment. I see that as definitely out of scope for mkcert itself, and something that can be built on top of it. In particular because there are so many ways to do authorization and authentication that we can't implement them all well. It should be fairly doable by shelling out to mkcert, as well. |
Thoroughly enjoying mkcert, and I was thinking about extending @unravelin's cert, err := mkcert.Exec(
// Domains tells mkcert what certificate to generate.
mkcert.Domains("localhost"),
// RequireTrusted(true) tells Exec to return an error if the CA isn't
// in the trust stores.
mkcert.RequireTrusted(true),
// CertFile and KeyFile override the default behaviour of generating
// the keys in the local directory.
mkcert.CertFile(filepath.Join(dir, "cert.pem")),
mkcert.KeyFile(filepath.Join(dir, "key.pem")),
)
log.Fatal(http.ListenAndServeTLS(*bind, cert.File, cert.KeyFile, h)) Users still need to have |
@icio Since you mentioned it, mkcert would make an excellent certmagic.Manager implementation. This way you get all the same API as CertMagic for public certs but with private certs using mkcert under the hood. Basically you'd get the holy grail of // serves handler on https://localhost with HTTP->HTTPS redirects
certmagic.HTTPS([]string{"localhost"}, handler) to work with only one extra line: // use mkcert to manage certificates instead of the default ACME manager
certmagic.Default.NewManager = MakeMkcertThing |
Hi @FiloSottile, My use case: at my company we use a private root CA for internal services. Right now we have a onboarding process that requires manually doing the stuff mkcert does to copy the cert into various trust stores. I'd like to replace this step with a tool that uses the same fiddly code mkcert already takes care of. This is especially important if we want to move to shorter-lived certificates. This use case might be different from most of the ones mentioned on this thread since I don't want to issue new certificates at all, just reuse the copy-to-trust-store code. I think I can use mkcert itself for this use case today: I'd have to make a temp CAROOT and copy my cert into there as rootCA.pem, then call (By the way, I was also thinking that maybe we're doing it wrong and we should use Let's Encrypt even for internal services. However, some investigation seems to indicate that it's not intended to be used for non-public domains. I'm happy to be told I'm doing it wrong, though.) |
@cespare take a look to https://github.com/smallstep/truststore, it implements mkcert install logic but you can use it in your code. |
This is not usable as a library dependency for others via
go get
. Can keep the CLI iface the same, just pull the code into a package and expose things with reasonable names. Everything that can be done via the CLI should be doable programmatically (of course, the CLI code just invokes the lib code). I'd send a PR to do this myself, but I figure such a large refactor (as in size, not effort) should be done by the maintainer.(also, during the refactoring, fix things like exposed internal variables, use of bool returns instead of error, etc)
The text was updated successfully, but these errors were encountered: