acme

package module
v3.6.2 Latest Latest
Warning

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

Go to latest
Published: Dec 6, 2024 License: MIT Imports: 32 Imported by: 0

README

eggsampler/acme

GoDoc Build Status Coverage Status

About

eggsampler/acme is a Go client library implementation for RFC8555 (previously ACME v2). This library can be used with the Let's Encrypt Certificate Authority (CA), but also other ACME compliant CA's such as ZeroSSL.

The library is designed to provide a zero external dependency wrapper over exposed directory endpoints and provide objects in easy to use structures.

Requirements

A Go version of at least 1.11 is required as this repository is designed to be imported as a Go module.

Usage

Simply import the module into a project,

import "github.com/eggsampler/acme/v3"

Note the /v3 major version at the end. Due to the way modules function, this is the major version as represented in the go.mod file and latest git repo semver tag. All functions are still exported and called using the acme package name.

Examples

A simple certbot-like example is provided in the examples/certbot directory. This code demonstrates account registration, new order submission, fulfilling challenges, finalising an order and fetching the issued certificate chain.

An example of how to use the autocert package is also provided in examples/autocert.

Tests

The tests can be run against an instance of boulder or pebble.

Challenge fulfilment is designed to use the new challtestsrv server present inside boulder and pebble which responds to dns queries and challenges as required.

To run tests against an already running instance of boulder or pebble, use the test target in the Makefile.

Some convenience targets for launching pebble/boulder using their respective docker compose files have also been included in the Makefile.

Documentation

Index

Constants

View Source
const (
	// LetsEncryptProduction holds the production directory url
	LetsEncryptProduction = "https://acme-v02.api.letsencrypt.org/directory"

	// LetsEncryptStaging holds the staging directory url
	LetsEncryptStaging = "https://acme-staging-v02.api.letsencrypt.org/directory"

	// ZeroSSLProduction holds the ZeroSSL directory url
	ZeroSSLProduction = "https://acme.zerossl.com/v2/DV90"
)
View Source
const (
	ChallengeTypeDNS01        = "dns-01"
	ChallengeTypeDNSAccount01 = "dns-account-01"
	ChallengeTypeHTTP01       = "http-01"
	ChallengeTypeTLSALPN01    = "tls-alpn-01"

	// ChallengeTypeTLSSNI01 is deprecated and should not be used.
	// See: https://community.letsencrypt.org/t/important-what-you-need-to-know-about-tls-sni-validation-issues/50811
	ChallengeTypeTLSSNI01 = "tls-sni-01"
)

Different possible challenge types provided by an ACME server. See https://tools.ietf.org/html/rfc8555#section-9.7.8

View Source
const (
	ReasonUnspecified          = iota // 0
	ReasonKeyCompromise               // 1
	ReasonCaCompromise                // 2
	ReasonAffiliationChanged          // 3
	ReasonSuperseded                  // 4
	ReasonCessationOfOperation        // 5
	ReasonCertificateHold             // 6

	ReasonRemoveFromCRL      // 8
	ReasonPrivilegeWithdrawn // 9
	ReasonAaCompromise       // 10
)

Constants used for certificate revocation, used for RevokeCertificate See https://tools.ietf.org/html/rfc5280#section-5.3.1

Variables

View Source
var (
	// ErrUnsupportedKey is returned when an unsupported key type is encountered.
	ErrUnsupportedKey = errors.New("acme: unknown key type; only RSA and ECDSA are supported")

	// ErrRenewalInfoNotSupported is returned by Client.GetRenewalInfo if the
	// renewal info entry isn't present on the acme directory (ie, it's not
	// supported by the acme server)
	ErrRenewalInfoNotSupported = errors.New("renewal information endpoint not supported")
)

Functions

func EncodeDNS01KeyAuthorization

func EncodeDNS01KeyAuthorization(keyAuth string) string

EncodeDNS01KeyAuthorization encodes a key authorization and provides a value to be put in the TXT record for the _acme-challenge DNS entry.

func GenerateARICertID

func GenerateARICertID(cert *x509.Certificate) (string, error)

GenerateARICertID constructs a certificate identifier as described in draft-ietf-acme-ari-03, section 4.1.

func JWKThumbprint

func JWKThumbprint(pub crypto.PublicKey) (string, error)

JWKThumbprint creates a JWK thumbprint out of pub as specified in https://tools.ietf.org/html/rfc7638.

Types

type Account

type Account struct {
	Status  string   `json:"status"`
	Contact []string `json:"contact"`
	Orders  string   `json:"orders"`

	// Provided by the Location http header when creating a new account or fetching an existing account.
	URL string `json:"-"`

	// The private key used to create or fetch the account.
	// Not fetched from server.
	PrivateKey crypto.Signer `json:"-"`

	// Thumbprint is the SHA-256 digest JWK_Thumbprint of the account key.
	// See https://tools.ietf.org/html/rfc8555#section-8.1
	Thumbprint string `json:"-"`

	// ExternalAccountBinding is populated when using the NewAcctOptExternalAccountBinding option for NewAccountOption
	// and is otherwise empty. Not populated when account is fetched or created otherwise.
	ExternalAccountBinding ExternalAccountBinding `json:"-"`
}

Account structure representing fields in an account object. See https://tools.ietf.org/html/rfc8555#section-7.1.2 See also https://tools.ietf.org/html/rfc8555#section-9.7.1

type Authorization

type Authorization struct {
	Identifier Identifier  `json:"identifier"`
	Status     string      `json:"status"`
	Expires    time.Time   `json:"expires"`
	Challenges []Challenge `json:"challenges"`
	Wildcard   bool        `json:"wildcard"`

	// For convenience access to the provided challenges
	ChallengeMap   map[string]Challenge `json:"-"`
	ChallengeTypes []string             `json:"-"`

	URL string `json:"-"`
}

Authorization object returned when fetching an authorization in an order. See https://tools.ietf.org/html/rfc8555#section-7.1.4

type AutoCert

type AutoCert struct {
	// Acme directory Url
	// If nil, uses `LetsEncryptStaging`
	DirectoryURL string

	// Options contains the options used for creating the acme client
	Options []OptionFunc

	// A function to check whether a host is allowed or not
	// If nil, all hosts allowed
	// Use `WhitelistHosts(hosts ...string)` for a simple white list of hostnames
	HostCheck HostCheck

	// Cache dir to store account data and certificates
	// If nil, does not write cache data to file
	CacheDir string

	// When using a staging environment, include a root certificate for verification purposes
	RootCert string

	// Called before updating challenges
	PreUpdateChallengeHook func(Account, Challenge)
	// contains filtered or unexported fields
}

AutoCert is a stateful certificate manager for issuing certificates on connecting hosts

func (*AutoCert) GetCertificate

func (m *AutoCert) GetCertificate(hello *tls.ClientHelloInfo) (*tls.Certificate, error)

GetCertificate implements a tls.Config.GetCertificate hook

func (*AutoCert) HTTPHandler

func (m *AutoCert) HTTPHandler(handler http.Handler) http.Handler

HTTPHandler Wraps a handler and provides serving of http-01 challenge tokens from /.well-known/acme-challenge/ If handler is nil, will redirect all traffic otherwise to https

type Challenge

type Challenge struct {
	Type      string  `json:"type"`
	URL       string  `json:"url"`
	Status    string  `json:"status"`
	Validated string  `json:"validated"`
	Error     Problem `json:"error"`

	// Based on the challenge used
	Token            string `json:"token"`
	KeyAuthorization string `json:"keyAuthorization"`

	// Authorization url provided by the rel="up" Link http header
	AuthorizationURL string `json:"-"`
}

Challenge object fetched in an authorization or directly from the challenge url. See https://tools.ietf.org/html/rfc8555#section-7.1.5

type Client

type Client struct {

	// The amount of total time the Client will wait at most for a challenge to be updated or a certificate to be issued.
	// Default 30 seconds if duration is not set or if set to 0.
	PollTimeout time.Duration

	// The time between checking if a challenge has been updated or a certificate has been issued.
	// Default 0.5 seconds if duration is not set or if set to 0.
	PollInterval time.Duration

	// IgnorePolling does not use any simple polling in order finalisation
	IgnorePolling bool

	// IgnoreRetryAfter does not use the retry-after header in order finalisation
	IgnoreRetryAfter bool
	// contains filtered or unexported fields
}

Client structure to interact with an ACME server. This is typically how most, if not all, of the communication between the client and server occurs.

func NewClient

func NewClient(directoryURL string, options ...OptionFunc) (Client, error)

NewClient creates a new acme client given a valid directory url.

func (Client) AccountKeyChange

func (c Client) AccountKeyChange(account Account, newPrivateKey crypto.Signer) (Account, error)

AccountKeyChange rolls over an account to a new key.

func (Client) DeactivateAccount

func (c Client) DeactivateAccount(account Account) (Account, error)

DeactivateAccount deactivates a given account.

func (Client) DeactivateAuthorization

func (c Client) DeactivateAuthorization(account Account, authURL string) (Authorization, error)

DeactivateAuthorization deactivate a provided authorization url from an order.

func (Client) Directory

func (c Client) Directory() Directory

Directory is the object returned by the client connecting to a directory url.

func (Client) Fetch

func (c Client) Fetch(account Account, requestURL string, result interface{}, expectedStatus ...int) error

Fetch is a helper function to assist with POST-AS-GET requests

func (Client) FetchAllCertificates

func (c Client) FetchAllCertificates(account Account, certificateURL string) (map[string][]*x509.Certificate, error)

FetchAllCertificates downloads a certificate chain from a url given in an order certificate, as well as any alternate certificates if provided. Returns a mapping of certificate urls to the certificate chain.

func (Client) FetchAuthorization

func (c Client) FetchAuthorization(account Account, authURL string) (Authorization, error)

FetchAuthorization fetches an authorization from an authorization url provided in an order.

func (Client) FetchCertificates

func (c Client) FetchCertificates(account Account, certificateURL string) ([]*x509.Certificate, error)

FetchCertificates downloads a certificate chain from a url given in an order certificate.

func (Client) FetchChallenge

func (c Client) FetchChallenge(account Account, challengeURL string) (Challenge, error)

FetchChallenge fetches an existing challenge from the given url.

func (Client) FetchOrder

func (c Client) FetchOrder(account Account, orderURL string) (Order, error)

FetchOrder fetches an existing order given an order url.

func (Client) FetchOrderList

func (c Client) FetchOrderList(account Account) (OrderList, error)

FetchOrderList fetches a list of orders from the account url provided in the account Orders field

func (Client) FinalizeOrder

func (c Client) FinalizeOrder(account Account, order Order, csr *x509.CertificateRequest) (Order, error)

FinalizeOrder indicates to the acme server that the client considers an order complete and "finalizes" it. If the server believes the authorizations have been filled successfully, a certificate should then be available. This function assumes that the order status is "ready".

func (Client) GetRenewalInfo

func (c Client) GetRenewalInfo(cert *x509.Certificate) (RenewalInfo, error)

GetRenewalInfo returns the renewal information (if present and supported by the ACME server), and a Retry-After time if indicated in the http response header.

func (Client) NewAccount

func (c Client) NewAccount(privateKey crypto.Signer, onlyReturnExisting, termsOfServiceAgreed bool, contact ...string) (Account, error)

NewAccount registers a new account with the acme service Note this function is essentially deprecated and only present for backwards compatibility. New programs should implement NewAccountOptions instead.

func (Client) NewAccountOptions

func (c Client) NewAccountOptions(privateKey crypto.Signer, options ...NewAccountOptionFunc) (Account, error)

NewAccountOptions registers an account with an acme server with the provided options.

func (Client) NewOrder

func (c Client) NewOrder(account Account, identifiers []Identifier, profile string) (Order, error)

NewOrder initiates a new order for a new certificate. This method does not use ACME Renewal Info.

func (Client) NewOrderDomains

func (c Client) NewOrderDomains(account Account, profile string, domains ...string) (Order, error)

NewOrderDomains takes a list of domain dns identifiers for a new certificate. Essentially a helper function.

func (Client) ReplacementOrder

func (c Client) ReplacementOrder(account Account, oldCert *x509.Certificate, identifiers []Identifier, profile string) (Order, error)

ReplacementOrder takes an existing *x509.Certificate and initiates a new order for a new certificate, but with the order being marked as a replacement. Replacement orders which are valid replacements are (currently) exempt from Let's Encrypt NewOrder rate limits, but may not be exempt from other ACME CAs ACME Renewal Info implementations. At least one identifier must match the list of identifiers from the parent order to be considered as a valid replacement order. See https://datatracker.ietf.org/doc/html/draft-ietf-acme-ari-03#section-5

func (Client) RevokeCertificate

func (c Client) RevokeCertificate(account Account, cert *x509.Certificate, key crypto.Signer, reason int) error

RevokeCertificate revokes a given certificate given the certificate key or account key, and a reason.

func (Client) UpdateAccount

func (c Client) UpdateAccount(account Account, contact ...string) (Account, error)

UpdateAccount updates an existing account with the acme service.

func (Client) UpdateChallenge

func (c Client) UpdateChallenge(account Account, challenge Challenge) (Challenge, error)

UpdateChallenge responds to a challenge to indicate to the server to complete the challenge.

type Directory

type Directory struct {
	NewNonce   string `json:"newNonce"`   // url to new nonce endpoint
	NewAccount string `json:"newAccount"` // url to new account endpoint
	NewOrder   string `json:"newOrder"`   // url to new order endpoint
	NewAuthz   string `json:"newAuthz"`   // url to new authz endpoint
	RevokeCert string `json:"revokeCert"` // url to revoke cert endpoint
	KeyChange  string `json:"keyChange"`  // url to key change endpoint

	// https://datatracker.ietf.org/doc/html/draft-ietf-acme-ari-03
	RenewalInfo string `json:"renewalInfo"` // url to renewal info endpoint

	// meta object containing directory metadata
	Meta struct {
		TermsOfService          string            `json:"termsOfService"`
		Website                 string            `json:"website"`
		CaaIdentities           []string          `json:"caaIdentities"`
		ExternalAccountRequired bool              `json:"externalAccountRequired"`
		Profiles                map[string]string `json:"profiles"`
	} `json:"meta"`

	// Directory url provided when creating a new acme client.
	URL string `json:"-"`
}

Directory object as returned from the client's directory url upon creation of client. See https://tools.ietf.org/html/rfc8555#section-7.1.1

type ExternalAccountBinding

type ExternalAccountBinding struct {
	KeyIdentifier string      `json:"-"`
	MacKey        string      `json:"-"`
	Algorithm     string      `json:"-"`
	HashFunc      crypto.Hash `json:"-"`
}

ExternalAccountBinding holds the key identifier and mac key provided for use in servers that support/require external account binding. The MacKey is a base64url-encoded string. Algorithm is a "MAC-based algorithm" as per RFC8555. Typically this is either,

  • "HS256" for HashFunc: crypto.SHA256
  • "HS384" for HashFunc: crypto.SHA384
  • "HS512" for HashFunc: crypto.SHA512

However this is dependent on the acme server in question and is provided here to give more options for future compatibility.

type HostCheck

type HostCheck func(host string) error

HostCheck function prototype to implement for checking hosts against before issuing certificates

func WhitelistHosts

func WhitelistHosts(hosts ...string) HostCheck

WhitelistHosts implements a simple whitelist HostCheck

type Identifier

type Identifier struct {
	Type  string `json:"type"`
	Value string `json:"value"`
}

Identifier object used in order and authorization objects See https://tools.ietf.org/html/rfc8555#section-7.1.4

type KeyID

type KeyID string

KeyID is the account key identity provided by a CA during registration.

type NewAccountOptionFunc

type NewAccountOptionFunc func(crypto.Signer, *Account, *NewAccountRequest, Client) error

NewAccountOptionFunc function prototype for passing options to NewClient

func NewAcctOptAgreeTOS

func NewAcctOptAgreeTOS() NewAccountOptionFunc

NewAcctOptAgreeTOS sets the new account request as agreeing to the terms of service

func NewAcctOptExternalAccountBinding

func NewAcctOptExternalAccountBinding(binding ExternalAccountBinding) NewAccountOptionFunc

NewAcctOptExternalAccountBinding adds an external account binding to the new account request Code adopted from jwsEncodeJSON

func NewAcctOptOnlyReturnExisting

func NewAcctOptOnlyReturnExisting() NewAccountOptionFunc

NewAcctOptOnlyReturnExisting sets the new client request to only return existing accounts

func NewAcctOptWithContacts

func NewAcctOptWithContacts(contacts ...string) NewAccountOptionFunc

NewAcctOptWithContacts adds contacts to a new account request

type NewAccountRequest

type NewAccountRequest struct {
	OnlyReturnExisting     bool            `json:"onlyReturnExisting"`
	TermsOfServiceAgreed   bool            `json:"termsOfServiceAgreed"`
	Contact                []string        `json:"contact,omitempty"`
	ExternalAccountBinding json.RawMessage `json:"externalAccountBinding"`
}

NewAccountRequest object used for submitting a request for a new account. Primarily used with NewAccountOptionFunc

type OptionFunc

type OptionFunc func(client *Client) error

OptionFunc function prototype for passing options to NewClient

func WithAcceptLanguage

func WithAcceptLanguage(acceptLanguage string) OptionFunc

WithAcceptLanguage sets an Accept-Language header on http requests

func WithHTTPClient

func WithHTTPClient(httpClient *http.Client) OptionFunc

WithHTTPClient Allows setting a custom http client for acme connections

func WithHTTPTimeout

func WithHTTPTimeout(duration time.Duration) OptionFunc

WithHTTPTimeout sets a timeout on the http client used by the Client

func WithInsecureSkipVerify

func WithInsecureSkipVerify() OptionFunc

WithInsecureSkipVerify sets InsecureSkipVerify on the http client transport tls client config used by the Client

func WithRetryCount

func WithRetryCount(retryCount int) OptionFunc

WithRetryCount sets the number of times the acme client retries when receiving an api error (eg, nonce failures, etc). Default: 5

func WithRootCerts

func WithRootCerts(pool *x509.CertPool) OptionFunc

WithRootCerts sets the httpclient transport to use a given certpool for root certs

func WithUserAgentSuffix

func WithUserAgentSuffix(userAgentSuffix string) OptionFunc

WithUserAgentSuffix appends a user agent suffix for http requests to acme resources

type Order

type Order struct {
	Status         string       `json:"status"`
	Expires        time.Time    `json:"expires"`
	Identifiers    []Identifier `json:"identifiers"`
	Profile        string       `json:"profile,omitempty"`
	NotBefore      time.Time    `json:"notBefore"`
	NotAfter       time.Time    `json:"notAfter"`
	Error          Problem      `json:"error"`
	Authorizations []string     `json:"authorizations"`
	Finalize       string       `json:"finalize"`
	Certificate    string       `json:"certificate"`

	// URL for the order object.
	// Provided by the rel="Location" Link http header
	URL string `json:"-"`

	// RetryAfter is the http Retry-After header from the order response
	RetryAfter time.Time `json:"-"`

	// Replaces (optional, string): A string uniquely identifying a
	// previously-issued certificate which this order is intended to replace.
	// See https://datatracker.ietf.org/doc/html/draft-ietf-acme-ari-03#section-5
	Replaces string `json:"replaces,omitempty"`
}

Order object returned when fetching or creating a new order. See https://tools.ietf.org/html/rfc8555#section-7.1.3

type OrderList

type OrderList struct {
	Orders []string `json:"orders"`

	// Order list pagination, url to next orders.
	// Provided by the rel="next" Link http header
	Next string `json:"-"`
}

OrderList of challenge objects.

type Problem

type Problem struct {
	Type        string       `json:"type"`
	Detail      string       `json:"detail,omitempty"`
	Status      int          `json:"status,omitempty"`
	Instance    string       `json:"instance,omitempty"`
	SubProblems []SubProblem `json:"subproblems,omitempty"`
}

Problem represents an error returned by an acme server.

func (Problem) Error

func (err Problem) Error() string

Returns a human readable error string.

type RenewalInfo

type RenewalInfo struct {
	SuggestedWindow struct {
		Start time.Time `json:"start"`
		End   time.Time `json:"end"`
	} `json:"suggestedWindow"`
	ExplanationURL string `json:"explanationURL,omitempty"`

	RetryAfter time.Time `json:"-"`
}

RenewalInfo stores the server-provided suggestions on when to renew certificates.

func (RenewalInfo) ShouldRenewAt

func (r RenewalInfo) ShouldRenewAt(now time.Time, willingToSleep time.Duration) *time.Time

type SubProblem

type SubProblem struct {
	Type       string     `json:"type"`
	Detail     string     `json:"detail"`
	Identifier Identifier `json:"identifier"`
}

Jump to

Keyboard shortcuts

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