certmaker

package module
v0.0.0-...-0d0d77d Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 4, 2022 License: MIT Imports: 15 Imported by: 0

README

CertMaker Go SDK

Go Reference

The Golang software development kit for the CertMaker.

The SDK allows you to easily obtain and revoke certificates in your custom Go applications. There are quite a few convenience methods but also some lower level methods for more fine-tuned control.

Installation

Usage

There are two important structs you should understand before diving into development, the Client and the Cache:

type Client struct {
	// unexported fields
}

The Client's purpose is to request and download certificates (and private keys, if any). That's why you can supply an API Key and a base URL to the NewClient() function. The Client requires a valid Cache to work properly.

type Cache struct {
    CacheDir                string
    PrivateKeyFilename      string
    CertificateFilename     string
    RootCertificateFilename string
}

Unlike the Client, the Cache only has exported fields which can be manipulated by you, the developer. There is also a NewCache() function, which sets up a Cache with useful defaults. The Cache is required to interact with the filesystem. CacheDir is the directory (without filename) where the certificate/private key reside, PrivateKeyFilename contains just the private key file name, CertificateFilename contains just the certificate file name.

There are two methods to obtain the complete certificate/private key paths:

certPath := cache.GetCertificatePath() // e.g. /opt/app-certs/localhost/cert.pem
keyPath := cache.GetPrivateKeyPath() // e.g. /opt/app-certs/localhost/key.pem
http.ListenAndServeTLS(":1234", certPath, keyPath, nil)
Usage example

To put it all together, here is an example (errors ignored for brevity):

certMakerInstance := "http://12.34.56.78:8880" // no trailing slash needed
token := "Ar8S71NBblDCMVJD0dkftX36ea5zG7QSI7Q2trkEVwBZpqsQzNTFneSgMcM1" // taken from the account info

cache, _ := certmaker.NewCache()
client := certmaker.NewClient(certMakerInstance, token, nil)

You can modify the Client's underlying HTTP client and other options by supplying client settings as third parameter, e.g.:

// ...
cache, _ := certmaker.NewCache()
client := certmaker.NewClient(certMakerInstance, token, &certmaker.ClientSettings{
    // a custom *http.Transport{} for the HTTP client
    Transport:     nil, 
    // the timeout for the HTTP client (default is 5 seconds)
    ClientTimeout: 4 * time.Second,
    // when checking if a local certificate is still valid, also check with the CertMaker API 
    // if it's been revoked
    StrictMode:    true,
    // the port is only required if the verification challenge (see below) is enabled
    // on the API's side and make sure it is open
    ChallengePort: 8000, 
})

The DNS name/IP address verification challenge

If the challenge is enabled on the server side, a token has to be reachable via every DNS name or IP address. This means that at least one port must be open to be used for the solving part. You can supply it using the certmaker.ClientSettings{}. Email addresses have no relevance in the verification challenge.

Request types

There are two types of certificate requests. One is the SimpleRequest, the other is the *x509.CertificateRequest. A SimpleRequest requests a private key with the corresponding certificate. A *x509.CertificateRequest is created from an existing private key, so just a certificate is requested.

Convenience methods

RequestForDomains(cache *Cache, domains []string, days int) error tries to obtain a certificate and private key with a validity of days for every DNS name in domains and writes the downloaded data into cache.

RequestForIps(cache *Cache, ips []string, days int) error tries to obtain a certificate and private key for with a validity of days every IP address in ips and writes the downloaded data into cache.

RequestForEmails(cache *Cache, emails []string, days int) error tries to obtain a certificate and private key for with a validity of days every email address in emails and writes the downloaded data into cache.

Lower-level methods

Request(cache *Cache, cr *SimpleRequest) error requires a SimpleRequest to obtain a certificate and private key into cache. This method allows for more fine-grained control.

Example:

// Just omitwhat is not needed, e.g. the email addresses
err = client.Request(cache, &certmaker.SimpleRequest{
    Domains:        []string{"localhost"},
    IPs:            []string{"127.0.0.1", "::1"},
    EmailAddresses: []string{"some@mail.org", "other@mail.com"}
    Subject: certmaker.SimpleRequestSubject{
        Organization:  "KAISERWERK Ltd.",
        Country:       "Germany",
        Province:      "NRW",
        Locality:      "Cologne",
        StreetAddress: "Random Street 1337",
        PostalCode:    "12345",
    },
    Days: 25,
})

RequestWithCSR(cache *Cache, csr *x509.CertificateRequest) error takes a Certificate Signing Request and tries to obtain a certificate (no private key) as requested and writes the downloaded data into cache.

Example:

var data []byte // from file or HTTP request or byte buffer or...
b, _ := pem.Decode(data)
csr, _ := x509.ParseCertificateRequest(b.Bytes)
err = client.RequestWithCSR(cache, csr)

Special utility methods

SetupWithSimpleRequest(cache *Cache, sr *SimpleRequest) and SetupWithCSR(cache *Cache, csr *x509.CertificateRequest) are preparing calls to make before using GetCertificateFunc.

GetCertificateFunc(chi *tls.ClientHelloInfo) (*tls.Certificate, error) can be used by anything that uses a *tls.Config{} to read a certificate from an arbitrary source. Also, certificates are automatically re-read when required (shortly before expiration at the latest). That means fire and forget, no need for loops or cronjobs.

This feature is Work in Progress and does not yet work as intended!

Example:

client.SetupWithSimpleRequest(cache, &certmaker.SimpleRequest{
    Domains:        []string{"localhost"},
    IPs:            []string{"127.0.0.1", "::1"},
    EmailAddresses: []string{"some@mail.org", "other@mail.com"},
    Subject: certmaker.SimpleRequestSubject{
        Organization:  "KAISERWERK Ltd.",
        Country:       "Germany",
        Province:      "NRW",
        Locality:      "Cologne",
        StreetAddress: "Random Street 1337",
        PostalCode:    "12345",
    },
    Days: 25,
})

// set up the HTTP server
router := http.NewServeMux()
router.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
    fmt.Fprint(w, "Hello World!")
})
srv := http.Server{
    Addr: ":1337",
    Handler: router,
    TLSConfig: &tls.Config{
        GetCertificate: client.GetCertificateFunc,
    }, 
}

log.Fatal(srv.ListenAndServeTLS("", "")) // leave these two fields empty
// now make a call to https://localhost:1337/ (https, not http!)
// If you get an error message stating that the certificate is invalid, that just
// means that you didn't install the root certificate yet.

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Cache

type Cache struct {
	CacheDir                string
	PrivateKeyFilename      string
	CertificateFilename     string
	RootCertificateFilename string
}

Cache represents local directory and file paths for certificates and private keys

func NewCache

func NewCache() (*Cache, error)

NewCache returns a *Cache with default values: CacheDir is .certs, CertificateFilename is cert.pem, PrivateKeyFilename is key.pem

func (*Cache) GetCertificatePath

func (c *Cache) GetCertificatePath() string

GetCertificatePath returns the full path the Cache's certificate file

func (*Cache) GetPrivateKeyPath

func (c *Cache) GetPrivateKeyPath() string

GetPrivateKeyPath returns the full path the Cache's private key file

func (*Cache) GetRootCertificate

func (c *Cache) GetRootCertificate() (*x509.Certificate, error)

func (*Cache) GetRootCertificatePath

func (c *Cache) GetRootCertificatePath() string

GetRootCertificatePath returns the full path the Cache's root certificate file Usually there is no need to touch that at all

func (*Cache) GetTlsCertificate

func (c *Cache) GetTlsCertificate() (*tls.Certificate, error)

func (*Cache) SetDir

func (c *Cache) SetDir(dir string) error

func (*Cache) Valid

func (c *Cache) Valid(client *Client) bool

Valid returns whether the certificate in *Cache is valid (file exists, it is not expired, and if strictmode is enabled, also checks if it is revoked)

type Client

type Client struct {
	// contains filtered or unexported fields
}

Client represents the structure required to obtain certificates (and private keys) from a remote location.

func NewClient

func NewClient(baseUrl, token string, settings *ClientSettings) *Client

NewClient returns a *Client with a new *http.Client and baseUrl and token fields set to their parameter values

func (*Client) DownloadRootCertificate

func (c *Client) DownloadRootCertificate(cache *Cache) error

func (*Client) GetCertificateFunc

func (c *Client) GetCertificateFunc(chi *tls.ClientHelloInfo) (*tls.Certificate, error)

func (*Client) Request

func (c *Client) Request(cache *Cache, cr *SimpleRequest) error

Request requests a fresh certificate and private key with the metadata contained in the *SimpleRequest and puts it into *Cache.

func (*Client) RequestForDomains

func (c *Client) RequestForDomains(cache *Cache, domains []string, days int) error

RequestForDomains is a convenience method to fetch a certificate and a private key for just the selected domain(s) without a care about other settings.

func (*Client) RequestForEmails

func (c *Client) RequestForEmails(cache *Cache, emails []string, days int) error

RequestForEmails is a convenience method to fetch a certificate and a private key for just the selected email address(es) without a care about other settings.

func (*Client) RequestForIps

func (c *Client) RequestForIps(cache *Cache, ips []string, days int) error

RequestForIps is a convenience method to fetch a certificate and a private key for just the selected IP address(es) without a care about other settings.

func (*Client) RequestWithCSR

func (c *Client) RequestWithCSR(cache *Cache, csr *x509.CertificateRequest) error

RequestWithCSR is like Request but with the subtle difference that it takes a x509.CertificateRequest, which is commonly known as a Certificate Signing Request (CSR). The *Cache must have the PrivateKeyFilename field set to a file containing a valid private key. Otherwise the process will fail.

func (*Client) SetupWithCSR

func (c *Client) SetupWithCSR(cache *Cache, csr *x509.CertificateRequest)

SetupWithCSR is a preparatory call in order to use GetCertificateFunc with an http.Server struct

func (*Client) SetupWithSimpleRequest

func (c *Client) SetupWithSimpleRequest(cache *Cache, sr *SimpleRequest)

SetupWithSimpleRequest is a preparatory call in order to use GetCertificateFunc with an http.Server struct

type ClientSettings

type ClientSettings struct {
	Transport     *http.Transport
	ClientTimeout time.Duration
	StrictMode    bool
	ChallengePort uint16
}

ClientSettings represent meta data useful for altering the behaviour of a *Client

type ErrStillValid

type ErrStillValid struct{}

func (ErrStillValid) Error

func (e ErrStillValid) Error() string

type SimpleRequest

type SimpleRequest struct {
	Domains        []string             `json:"domains"`
	IPs            []string             `json:"ips"`
	EmailAddresses []string             `json:"emails"`
	Subject        SimpleRequestSubject `json:"subject,omitempty"`
	Days           int                  `json:"days"`
}

SimpleRequest defines a request for a new certificate and private key. The field Subject is optional and can be omitted. Days can be between 1 and 182. If the value is higher than 182, then it will be set to 182 on the server side. If it is lower than 1, it will be set to 1 on the server side.

You can either supply zero or more Domains, zero or more IPs and zero or more EmailAddresses.

type SimpleRequestSubject

type SimpleRequestSubject struct {
	Organization  string `json:"organization"`
	Country       string `json:"country"`
	Province      string `json:"province"`
	Locality      string `json:"locality"`
	StreetAddress string `json:"street_address"`
	PostalCode    string `json:"postal_code"`
}

SimpleRequestSubject represents the subject of a SimpleRequest

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL