Help us shape the future of Go! Take the 2026 Go Developer Survey

est

package module
v1.4.0 Latest Latest
Warning

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

Go to latest
Published: Oct 26, 2026 License: MIT Imports: 36 Imported by: 0

README

go-pki-est

GitHub Release Go Reference License Tests Supply chain OpenSSF Scorecard

Go library for Enrollment over Secure Transport (EST), the certificate enrollment protocol defined in RFC 7030. It provides:

  • an EST client library
  • an http.Handler that serves EST in front of your CA (NewRouter and the CA interface)
  • estclient, a command line client built on the library
  • estserver, a server with a mock CA for testing and development

Contents

Install

go get github.com/misiektoja/go-pki-est

The package name is est. Requires Go 1.27.3 or newer.

The command line tools install with:

go install github.com/misiektoja/go-pki-est/cmd/estclient@latest
go install github.com/misiektoja/go-pki-est/cmd/estserver@latest
Verifying a release

Each GitHub release carries the complete source tree as ZIP and tar archives, a CycloneDX SBOM, their SHA-256 checksums and a signed provenance bundle. Check a downloaded file against the checksums and confirm that the release workflow of this repository built it:

shasum -a 256 -c go-pki-est-v1.4.0_SHA256SUMS.txt
gh attestation verify go-pki-est-v1.4.0-source.tar.gz --repo misiektoja/go-pki-est

To verify offline, add --bundle go-pki-est-v1.4.0.intoto.jsonl.

Quickstart

Starting the server

When started with no configuration file, the EST server listens on localhost:8443 and generates a random, transient Certificate Authority (CA) which can be used for testing. The CA issues a certificate to anyone, so serve other hosts only through listen_address in a configuration file:

user@host:$ estserver &
[1] 62405

estserver -sampleconfig prints a sample configuration file.

Getting the CA certificates

Because we're using a random, transient CA, we must retrieve the CA certificates in insecure mode to establish an explicit trust anchor for subsequent EST operations. Since we only need the root CA certificate to establish a trust anchor, we use the -rootout flag:

user@host:$ estclient cacerts -server localhost:8443 -insecure -rootout -out anchor.pem

We will also obtain and store the full CA certificates chain, since we'll use it shortly to demonstrate reenrollment. Since we now have an explicit trust anchor, we can use it instead of the -insecure option. Since we're storing the full chain, we don't use the -rootout option here:

user@host:$ estclient cacerts -server localhost:8443 -explicit anchor.pem -out cacerts.pem
Enrolling with an existing private key

First we generate a new private key, here using openssl:

user@host:$ openssl genrsa 4096 > key.pem
Generating RSA private key, 4096 bit long modulus
.................+++
.............+++
e is 65537 (0x10001)

Then we generate a PKCS#10 certificate signing request, and enroll using the explicit trust anchor we previously obtained:

user@host:$ estclient csr -key key.pem -cn 'John Doe' -emails 'john@doe.com' -out csr.pem
user@host:$ estclient enroll -server localhost:8443 -explicit anchor.pem -csr csr.pem -out cert.pem

Using a configuration file, we can enroll with a private key resident on a hardware module, such as a hardware security module (HSM) or a Trusted Platform Module 2.0 (TPM) device. estclient sampleconfig prints a sample configuration file.

Enforcing proof of possession

RFC 7030 section 3.5 binds the request to the TLS session by putting the tls-unique value in the CSR's challenge password. To add it, pass the key that signs the CSR so the client can sign the request again:

user@host:$ estclient enroll -server localhost:8443 -explicit anchor.pem -csr csr.pem -signingkey key.pem -out cert.pem
  • tls-unique exists only in TLS 1.2.
  • -key and -signingkey can differ. -key authenticates the TLS connection, as in an initial enrollment. -signingkey signs the CSR and belongs to the key being enrolled.
  • The bundled test server does not check the challenge password. A CA implementation that needs the binding must check it.
Enrolling with a server-generated private key

If we're unable or unwilling to create our own private key, the EST server can generate one for us, and return it along with our certificate:

user@host:$ estclient serverkeygen -server localhost:8443 -explicit anchor.pem -cn 'Jane Doe' -out cert.pem -keyout key.pem

Note that we can omit the -csr option when enrolling and the EST client can dynamically generate a CSR for us using fields passed at the command line and the private key we specified, or an automatically-generated ephemeral private key if we are requesting server-side private key generation.

Reenrolling

Whichever way we generated our private key, we can now use it to reenroll.

To reenroll a previously obtained certificate, we must use it to authenticate ourselves during the TLS handshake with the EST server. Since our random, transient CA uses an intermediate CA certificate, we must provide a chain of certificates to the EST client, or the TLS handshake may fail.

Although providing the root CA certificate is optional for a TLS handshake, the simplest option is to provide the certificate we received along with the full chain of CA certificates which we previously obtained. To do this, we can just append those CA certificates to the certificate we received, and use that chain to reenroll:

user@host:$ cat cert.pem cacerts.pem >> certs.pem
user@host:$ estclient reenroll -server localhost:8443 -explicit anchor.pem -key key.pem -certs certs.pem -out newcert.pem

Note that when we omit the -csr option when reenrolling, the EST client automatically generates a CSR for us by copying the subject field and subject alternative name extension from the certificate we're renewing.

Features

Operation Coverage
/cacerts CA certificate distribution
/simpleenroll and /simplereenroll Enrollment and reenrollment with a PKCS#10 request
/serverkeygen Server-generated private keys, returned with or without additional encryption
/csrattrs CSR attributes
/tpmenroll Non-standard enrollment of TPM 2.0 keys with the credential protocol in Part 1, section 24 of the TPM 2.0 Library specification
/fullcmc Not implemented

Every operation accepts the optional additional path segment. The server can require HTTP Basic authentication on top of TLS client certificates. Following RFC 8951, receivers ignore the Content-Transfer-Encoding header and accept whitespace in base64.

/serverkeygen returns any PKCS#8 key the CA provides, including ML-KEM. It ignores the request signature, because RFC 7030 section 4.4.1 tells the server to.

Embedding the server

NewRouter returns an http.Handler that serves EST next to the host application's own endpoints. These ServerConfig fields hand decisions to the host:

  • CheckBasicAuth checks every enrollment request, also one without a password, so the host can accept a client certificate instead. It can answer 401, 403 or 429 with Retry-After.
  • CheckBearerToken checks the token of an Authorization: Bearer header (RFC 6750) in place of CheckBasicAuth. Without it a bearer token is not checked and does not sign a client in.
  • CheckCSRSignature verifies CSR signatures that crypto/x509 cannot, such as composite ML-DSA.
  • VerifyClientCertificate checks the client certificate of a renewal in place of the check against /cacerts, for certificates crypto/x509 cannot verify, such as those an SLH-DSA CA signed. The Subject and SubjectAltName comparison still runs first.
  • Refusals reports requests refused before they reach the CA, for audit records or rate limits.
  • PasswordRenewal lets /simplereenroll accept a password CheckBasicAuth accepted or a bearer token CheckBearerToken accepted instead of a client certificate, as RFC 7030 section 2.3 allows. It is off by default.
  • OmitTransferEncoding drops the Content-Transfer-Encoding header for clients that reject it.
  • OmitStrictTransportSecurity drops the Strict-Transport-Security header, so the host can send its own value or none.
  • MaxRequestBody raises the 64 KiB request limit for CSRs of large post-quantum keys. An SLH-DSA-SHA2-256f CSR is about 67 KB in base64. Keep it below 4 MiB, because /tpmenroll decodes a PKCS#7 part recursively and a structure nested about 10 MB deep ends the process.

The additional path segment of a request may be at most 255 bytes. A longer one answers 414. The server keeps the CA certificates of at most 1024 segments for client certificate checks.

TLS-SRP (RFC 5054) signs a client in with a username and password during the TLS handshake, as libest does with --srp. A host that terminates TLS-SRP passes the username to the router with WithSRPUsername. CheckBasicAuth and the CA read it with SRPUsernameFromContext. /simplereenroll then accepts the request without a client certificate. As with PasswordRenewal, the CA receives a nil certificate and decides which certificate is renewed. It must choose it by the signed-in identity, not by the CSR subject. The server also leaves revocation checks to the CA. estserver serves TLS-SRP and the client signs in with it through go-tls-srp.

Embedding the client

These Client fields let an application watch and relax the client:

  • WrapTransport wraps the HTTP transport of every request, for example to record a protocol trace with each request and response.
  • OnConnect receives the TLS connection of each operation after the handshake, for example to show the TLS version, cipher suite and server certificate.
  • AllowCertificateChain accepts an enrollment response that carries the CA chain next to the issued certificate. RFC 7030 section 4.2.3 allows only the issued certificate, so the client refuses such a response by default.
  • SRPUsername and SRPPassword sign in with TLS-SRP instead of a certificate check of the server, as RFC 7030 section 3.3.3 allows. The password authenticates both sides, so the trust anchors and client certificates are not used. TLS-SRP runs over TLS 1.2, so the tls-unique challenge password works. SRPMinGroupBits sets the smallest SRP group the client accepts. The default is 2048 bits.
  • Proxy chooses the proxy for the connection to the EST server, like http.Transport.Proxy. When it is nil, the HTTPS_PROXY and NO_PROXY environment variables apply. The client tunnels through an http or https proxy with CONNECT, so TLS-SRP, OnConnect and the tls-unique value work as without a proxy. Credentials in the proxy URL are sent as Basic Proxy-Authorization.
  • BearerToken signs in with an HTTP bearer token (RFC 6750), sent as Authorization: Bearer on every request. Username and Password take precedence.
  • MaxResponseBody raises the 1 MiB limit on a response body, for a chain of large post-quantum CA certificates.

The client does not follow redirects. A 3xx answer is returned as an error with its status.

Post-quantum algorithms

ML-DSA (FIPS 204, RFC 9881) works for enrollment, renewal and /serverkeygen in the library and in estclient. The tests use ML-DSA-65. Limits:

  • An ML-DSA client certificate needs TLS 1.3, which has no tls-unique value. The client then leaves out the proof of possession challenge password.
  • Composite ML-DSA requests need CheckCSRSignature, and the client cannot create them. No TLS library presents a composite client certificate, so composite certificates renew with PasswordRenewal or TLS-SRP.
  • ML-KEM keys cannot sign a CSR, so they can only come from /serverkeygen.
  • SLH-DSA (FIPS 205, RFC 9909) requests need CheckCSRSignature, and the client cannot create them. The SHA2-256f and SHAKE-256f sets also need a larger MaxRequestBody. TLS has no standard signature scheme for SLH-DSA keys, so these certificates renew with PasswordRenewal or TLS-SRP.
  • A certificate that an SLH-DSA or composite ML-DSA CA signed renews over TLS only with VerifyClientCertificate, because crypto/x509 cannot verify it. The listener must then request the client certificate without verifying it, with tls.RequestClientCert.

Credits

This project began as a fork of globalsign/est by GMO GlobalSign. The protocol implementation, both command line tools and the test server come from that project. The first fixes made here were offered upstream in globalsign/est#59 to globalsign/est#66.

This fork is maintained separately so fixes and new features ship in its own releases without waiting for upstream review. Changes that suit upstream are still offered there. NOTICE records the attribution.

Support and contributing

SUPPORT.md says where to ask questions and what to include in a bug report. Report vulnerabilities privately as SECURITY.md describes. The Contributing Guide lists the development checks and the Code of conduct applies to every project space. CHANGELOG.md records the changes in each release and DEPENDENCIES.md lists the third-party modules and their licenses.

License

MIT License. See LICENSE and NOTICE.

Documentation

Overview

Package est provides an EST client and server as defined by RFC7030.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func NewRouter

func NewRouter(cfg *ServerConfig) (http.Handler, error)

NewRouter creates a new EST server mux. The additional path segment of a request may be at most 255 bytes, a longer one answers 414. The CA certificates of at most 1024 segments are kept for client certificate checks.

func SRPUsernameFromContext

func SRPUsernameFromContext(ctx context.Context) string

SRPUsernameFromContext returns the username set by WithSRPUsername, or an empty string if the request did not sign in with TLS-SRP.

func WithSRPUsername

func WithSRPUsername(ctx context.Context, username string) context.Context

WithSRPUsername returns a copy of ctx that carries the username a client proved in a TLS-SRP handshake (RFC 5054). A server that terminates TLS-SRP sets it on each request before the router handles it. The router then accepts /simplereenroll without a client certificate, as libest does. An empty username returns ctx unchanged.

Types

type Attribute

type Attribute struct {
	Type   asn1.ObjectIdentifier
	Values AttributeValueSET
}

Attribute is a CSR Attribute type, as defined by RFC7030 4.5.2.

type AttributeValueSET

type AttributeValueSET []interface{}

AttributeValueSET is an ASN.1 SET of CSR attribute values.

type CA

type CA interface {
	// CACerts requests a copy of the current CA certificates. See RFC7030 4.1.
	CACerts(ctx context.Context, aps string, r *http.Request) ([]*x509.Certificate, error)

	// CSRAttrs requests a list of CA-desired CSR attributes. The returned list
	// may be empty. See RFC7030 4.5.
	CSRAttrs(ctx context.Context, aps string, r *http.Request) (CSRAttrs, error)

	// Enroll requests a new certificate. See RFC7030 4.2.
	Enroll(ctx context.Context, csr *x509.CertificateRequest, aps string, r *http.Request) (*x509.Certificate, error)

	// Reenroll requests renewal/rekey of an existing certificate. See RFC7030
	// 4.2. cert is the TLS client certificate being renewed. The server has
	// checked that it chains to the certificates CACerts returned, is within
	// its validity period and allows client authentication or has let
	// ServerConfig.VerifyClientCertificate decide. It does not check
	// revocation, so a CA that revokes certificates must refuse a revoked
	// one here. cert is nil when the client sent no certificate and signed
	// in with TLS-SRP or, with ServerConfig.PasswordRenewal, with HTTP Basic
	// credentials or a bearer token. Then the CA decides which certificate
	// is renewed and must choose it by the signed-in identity, not by the
	// Subject of csr, which any user could name. SRPUsernameFromContext
	// returns the TLS-SRP username, r.BasicAuth the HTTP Basic one and the
	// Authorization header the bearer token.
	Reenroll(ctx context.Context, cert *x509.Certificate, csr *x509.CertificateRequest, aps string, r *http.Request) (*x509.Certificate, error)

	// ServerKeyGen requests a new certificate and a private key. The key must
	// be returned as a DER-encoded PKCS8 PrivateKeyInfo structure if additional
	// encryption is not being employed, or returned inside a CMS SignedData
	// structure which itself is inside a CMS EnvelopedData structure. See
	// RFC7030 4.4.
	ServerKeyGen(ctx context.Context, csr *x509.CertificateRequest, aps string, r *http.Request) (*x509.Certificate, []byte, error)

	// TPMEnroll requests a new certificate using the TPM 2.0 privacy-preserving
	// protocol. An EK certificate chain with a length of at least one must be
	// provided, along with the EK and AK public areas. The return values are an
	// encrypted credential blob, an encrypted seed, and the certificate itself
	// inside a CMS EnvelopedData encrypted with the credential as a pre-shared
	// key.
	TPMEnroll(ctx context.Context, csr *x509.CertificateRequest, ekcerts []*x509.Certificate, ekPub, akPub []byte, aps string, r *http.Request) ([]byte, []byte, []byte, error)
}

CA is a Certificate Authority backing an EST server. The server can be connected to any backing CA by providing an implementation of this interface.

All operations receive:

  • a context, from which the EST server logger can be retrieved by calling LoggerFromContext
  • the optional URI additional path segment (RFC7030 3.2.2)
  • the HTTP request object from the server, from which the HTTP headers passed by the client (including the Host header, to support virtual servers) can be obtained

Any error object returned from these functions which implements Error will be used by the EST server to determine the HTTP response code, human-readable error description, and Retry-After header value, if applicable. Any other error will be treated as an internal server error.

type CSRAttrs

type CSRAttrs struct {
	OIDs       []asn1.ObjectIdentifier
	Attributes []Attribute
}

CSRAttrs contains CSR attributes as defined by RFC7030 4.5.

CSR attributes are defined by RFC7030 as a sequence of AttrOrOID, where AttrOrOID is a CHOICE of Object Identifier or Attribute. For ease of use, CRSAttrs provides separately a list of Object Identifiers, and a list of Attributes.

When the EST client retrieves and parses CSR attributes from an EST server, attribute values of the ASN.1 types: - OBJECT IDENTIFIER - BOOLEAN - INTEGER - most STRING types

will be unmarshalled into the Attribute.Values field as standard Go asn1.ObjectIdentifier, bool, *big.Int and string types where possible, and they can be retrieved via a type assertion. Other types will be unmarshalled into an asn1.RawValue structure and must be interpreted by the caller.

func (CSRAttrs) Marshal

func (a CSRAttrs) Marshal() ([]byte, error)

Marshal returns the ASN.1 DER-encoding of a value.

func (*CSRAttrs) Unmarshal

func (a *CSRAttrs) Unmarshal(b []byte) error

Unmarshal parses an DER-encoded ASN.1 data structure and stores the result in the object. Attribute values of the ASN.1 types: - OBJECT IDENTIFIER - BOOLEAN - INTEGER - most STRING types

will be unmarshalled into standard Go asn1.ObjectIdentifier, bool, *big.Int and string types where possible, and can be retrieved via a type assertion. Other types will be unmarshalled into an asn1.RawValue structure and must be interpreted by the caller.

type Client

type Client struct {
	// Host is the host:path of the EST server excluding any URL path
	// component, e.g. est.server.com:8443
	Host string

	// AdditionalPathSegment is an optional label for EST servers to provide
	// service for multiple CAs. See RFC7030 3.2.2.
	AdditionalPathSegment string

	// ExplicitAnchor is the optional Explicit TA database. See RFC7030 3.6.1.
	ExplicitAnchor *x509.CertPool

	// ImplicitAnchor is an optional Implicit TA database. If nil, the system
	// certificate pool will be used. See RFC7030 3.6.2.
	ImplicitAnchor *x509.CertPool

	// Certificates are the client certificates to present to the EST server
	// during the handshake. If more than one certificate is provided, they
	// should be provided in order, with the end-entity certificate first, and
	// the root CA (or last intermediate CA) certificate last.
	Certificates []*x509.Certificate

	// PrivateKey is the private key associated with the end-entity TLS
	// certificate. Any object implementing crypto.Signer may be used, to
	// support private keys resident on a hardware security module (HSM),
	// Trusted Platform Module (TPM) or other hardware device.
	PrivateKey interface{}

	// SigningKey is an optional private key to use for signing CSRs during initial enrollment.
	//
	// If not set, the challenge password field will not be included in the CSR.
	SigningKey interface{}

	// AdditionalHeaders are additional HTTP headers to include with the
	// request to the EST server.
	AdditionalHeaders map[string]string

	// HostHeader overrides the default Host header for the HTTP request to the
	// EST server, and is mostly useful for testing.
	HostHeader string

	// Username is an optional HTTP Basic Authentication username.
	Username string

	// Password is an optional HTTP Basic Authentication password.
	Password string

	// BearerToken is an optional HTTP Bearer authentication token (RFC
	// 6750). It is sent as "Authorization: Bearer <token>" when Username is
	// empty, so Basic authentication takes precedence.
	BearerToken string

	// DisableKeepAlives disables HTTP keep-alives if set.
	DisableKeepAlives bool

	// Proxy returns the proxy for the connection to the EST server, as
	// http.Transport.Proxy does, or nil for a direct connection. If Proxy
	// is nil, http.ProxyFromEnvironment applies, so the HTTPS_PROXY and
	// NO_PROXY environment variables choose the proxy. The client reaches
	// the server through an http or https proxy with a CONNECT tunnel, so
	// the TLS handshake, OnConnect and the tls-unique value work as without
	// a proxy. The user information of the proxy URL is sent as Basic
	// Proxy-Authorization. An https proxy is checked against the system
	// roots.
	Proxy func(*http.Request) (*url.URL, error)

	// InsecureSkipVerify controls whether the client verifies the EST server's
	// certificate chain and host name. If true, the client accepts any
	// certificate presented by the server and any host name in that certificate.
	// In this mode, TLS is susceptible to man-in-the-middle attacks. This should
	// be used only for testing. If used to obtain the CA certificates from an
	// otherwise untrusted EST server, those certificates must be manually
	// verified in some out-of-band manner before any further EST operations with
	// that server are performed.
	InsecureSkipVerify bool

	// WrapTransport, if set, wraps the HTTP transport of every request. The
	// wrapper sees each request and response, for example to record a
	// protocol trace, and must pass every request on to the transport it
	// wraps. The transport it wraps already bounds response bodies with
	// MaxResponseBody.
	WrapTransport func(http.RoundTripper) http.RoundTripper

	// OnConnect, if set, is called with the connection of each operation
	// after the TLS handshake and before the request is sent. The connection
	// is a *tls.Conn, or a *srptls.Conn with TLS-SRP. OnConnect must not read
	// from, write to or close it.
	OnConnect func(net.Conn)

	// AllowCertificateChain accepts an enroll, reenroll or serverkeygen
	// response that carries CA certificates besides the issued certificate.
	// RFC 7030 section 4.2.3 allows only the issued certificate, but some
	// servers add its chain. The client then returns the one certificate
	// that issued none of the others.
	AllowCertificateChain bool

	// MaxResponseBody bounds the size of a response body in bytes, including
	// an error message and all parts of a multipart response. A larger
	// response fails the operation. If zero, 1 MiB is used. A chain of large
	// post-quantum CA certificates can exceed that, such as SLH-DSA-SHA2-256f
	// certificates at about 50 KB each in base64. A negative value fails
	// every operation.
	MaxResponseBody int64

	// SRPUsername, if set, signs in with TLS-SRP (RFC 5054) as this user
	// with SRPPassword, as RFC 7030 section 3.3.3 allows. The password also
	// authenticates the server, so ExplicitAnchor, ImplicitAnchor,
	// InsecureSkipVerify and Certificates are not used. TLS-SRP runs over
	// TLS 1.2 only. OnConnect then receives a *srptls.Conn from
	// github.com/misiektoja/go-tls-srp.
	SRPUsername string

	// SRPPassword is the TLS-SRP password of SRPUsername.
	SRPPassword string

	// SRPMinGroupBits is the size of the smallest SRP group the client
	// accepts from the server. If zero, 2048 bits.
	SRPMinGroupBits int
}

Client is an EST client implementing the Enrollment over Secure Transport protocol as defined in RFC7030.

All EST client operations will return an error object implementing Error when the EST server returns any status other than 200 OK. The status code, description and, if applicable, retry-after seconds can be extracted from that object. In order to make the retry-after seconds available through this mechanism, note that a 202 Accepted response to enroll and reenroll operations is returned as an error for this purpose, even though 2** status codes indicate success.

func (*Client) CACerts

func (c *Client) CACerts(ctx context.Context) ([]*x509.Certificate, error)

CACerts requests a copy of the current CA certificates.

func (*Client) CSRAttrs

func (c *Client) CSRAttrs(ctx context.Context) (CSRAttrs, error)

CSRAttrs requests a list of CA-desired CSR attributes.

func (*Client) Enroll

Enroll requests a new certificate.

func (*Client) Reenroll

Reenroll renews an existing certificate.

func (*Client) ServerKeyGen

func (c *Client) ServerKeyGen(ctx context.Context, r *x509.CertificateRequest) (*x509.Certificate, []byte, error)

ServerKeyGen requests a new certificate and a server-generated private key.

func (*Client) TPMEnroll

func (c *Client) TPMEnroll(
	ctx context.Context,
	r *x509.CertificateRequest,
	ekCerts []*x509.Certificate,
	ekPub []byte,
	akPub []byte,
) ([]byte, []byte, []byte, error)

TPMEnroll requests a certificate using the TPM 2.0 privacy preserving protocol for distributing credentials for keys on a TPM.

type Error

type Error interface {
	// StatusCode returns the HTTP status code.
	StatusCode() int

	// Error returns a human-readable description of the error.
	Error() string

	// RetryAfter returns the value in seconds after which the client should
	// retry the request.
	RetryAfter() int
}

Error represents an error which can be translated into an HTTP status code and message, and optionally specify a Retry-After period.

type Logger

type Logger interface {
	// Errorf uses fmt.Sprintf to log a formatted message.
	Errorf(format string, args ...interface{})

	// Errorw logs a message with some additional context. The variadic
	// key-value pairs are treated as they are in With.
	Errorw(format string, keysAndValues ...interface{})

	// Infof uses fmt.Sprintf to log a formatted message.
	Infof(format string, args ...interface{})

	// Infow logs a message with some additional context. The variadic
	// key-value pairs are treated as they are in With.
	Infow(format string, keysAndValues ...interface{})

	// With adds a variadic number of key-values pairs to the logging context.
	// The first element of the pair is used as the field key and should be a
	// string. Passing a non-string key or passing an orphaned key panics.
	With(keysAndValues ...interface{}) Logger
}

Logger is an interface for an EST server logger.

func LoggerFromContext

func LoggerFromContext(ctx context.Context) Logger

LoggerFromContext returns a logger included in a context.

type Refusal

type Refusal struct {
	// Operation is the last segment of the request path, such as
	// "simpleenroll" or "simplereenroll".
	Operation string

	// AdditionalPathSegment is the optional label from the request path.
	AdditionalPathSegment string

	// Status is the HTTP status code sent to the client.
	Status int

	// Reason is the error text sent to the client.
	Reason string

	// Request is the refused request. Its body may already have been read.
	Request *http.Request
}

Refusal describes a request the server refused without asking the CA.

type RefusalRecorder

type RefusalRecorder interface {
	RecordRefusal(ctx context.Context, refusal Refusal) error
}

RefusalRecorder records the requests the server refuses without asking the CA, such as a malformed request, an unsupported content type or a renewal whose subject changed. Authentication failures from CheckBasicAuth and errors returned by the CA are not reported, because the host made those decisions. Server faults are not reported either.

RecordRefusal runs before the response is written, with a context that is not cancelled when the client disconnects, so it must bound its own work. A returned error is logged and does not change the response.

type ServerConfig

type ServerConfig struct {
	// CA is an interface to the Certificate Authority backing the EST server.
	CA CA

	// Logger is an optional logger. The logger can be retrieved by the backing
	// CA from the HTTP request context using LoggerFromContext.
	Logger Logger

	// Timeout sets a request timeout. If zero, a reasonable default will be
	// used.
	Timeout time.Duration

	// AllowedHosts is an optional list of fully-qualified domain names
	// representing hosts which are allowed to serve the EST service.
	AllowedHosts []string

	// RateLimit is an optional rate limit expressed in requests per second.
	// If zero, no rate limit will be applied. NewRouter refuses a negative
	// value.
	RateLimit int

	// CheckBasicAuth is an optional callback function to check HTTP Basic
	// Authentication credentials. It runs for every enrollment request that
	// CheckBearerToken does not check, with empty credentials when the
	// request carries no HTTP Basic credentials, so it can also accept a TLS
	// client certificate from the request. A nil error means that
	// authentication was successful. An error that implements Error with a
	// status other than 401, such as 403 or 429, is sent as is with its
	// Retry-After value. Any other error answers 401 with a Basic challenge.
	// On a TLS-SRP connection, SRPUsernameFromContext(ctx) returns the
	// username the client signed in with. If CheckBasicAuth is nil, then HTTP
	// Basic Authentication is not required for any EST operation. The
	// /healthcheck endpoint then only answers a request CheckBearerToken
	// accepts.
	CheckBasicAuth func(ctx context.Context, r *http.Request, aps, username, password string) error

	// CheckBearerToken is an optional callback function to check the token of
	// an "Authorization: Bearer" header (RFC 6750). It runs in place of
	// CheckBasicAuth for every request that carries one. Its errors are
	// handled like those of CheckBasicAuth. The 401 answer also carries a
	// Bearer challenge. If CheckBearerToken is nil, a bearer token is not
	// checked: CheckBasicAuth runs with empty credentials, as for a request
	// without an Authorization header. The token then does not sign the
	// client in for PasswordRenewal.
	CheckBearerToken func(ctx context.Context, r *http.Request, aps, token string) error

	// PasswordRenewal lets /simplereenroll accept a request without a TLS
	// client certificate when it carries HTTP Basic credentials with a
	// username that CheckBasicAuth accepted or a bearer token that
	// CheckBearerToken accepted. RFC 7030 section 2.3 allows this when the
	// certificate being renewed cannot be used for TLS client authentication,
	// such as a composite ML-DSA certificate. The CA receives a nil
	// certificate and decides which certificate is renewed. NewRouter refuses
	// it without CheckBasicAuth or CheckBearerToken. It defaults to false.
	PasswordRenewal bool

	// CheckCSRSignature optionally verifies the signature of each PKCS#10
	// request to /simpleenroll and /simplereenroll. /serverkeygen ignores the
	// signature, as RFC 7030 section 4.4.1 requires. If nil, the server uses
	// (*x509.CertificateRequest).CheckSignature, which refuses algorithms
	// crypto/x509 cannot verify, such as composite ML-DSA. crypto/x509 leaves
	// PublicKey nil for keys it does not know. An error that implements Error
	// is sent as is. Any other error answers 400.
	CheckCSRSignature func(csr *x509.CertificateRequest) error

	// VerifyClientCertificate optionally checks the TLS client certificate of
	// a /simplereenroll request in place of the built-in check, which verifies
	// it with crypto/x509 against the certificates CACerts returns. crypto/x509
	// cannot verify a certificate signed by some post-quantum CAs, such as an
	// SLH-DSA or composite ML-DSA CA. The Subject and SubjectAltName
	// comparison with the request runs first. A nil error accepts the
	// certificate. An error that implements Error is sent as is. Any other
	// error answers 403. The TLS listener must request the certificate
	// without verifying it, for example with tls.RequestClientCert, because
	// crypto/tls would otherwise refuse the handshake first.
	VerifyClientCertificate func(ctx context.Context, r *http.Request, aps string, cert *x509.Certificate) error

	// Refusals optionally receives the requests the server refuses itself,
	// before or instead of calling the CA.
	Refusals RefusalRecorder

	// OmitTransferEncoding leaves the Content-Transfer-Encoding header out of
	// responses and their multipart parts. Bodies stay base64 encoded. RFC 8951
	// tells receivers to ignore the header. Some clients fail when it is
	// present.
	OmitTransferEncoding bool

	// OmitStrictTransportSecurity leaves the Strict-Transport-Security header
	// out of responses, so the host can send its own value or none. A browser
	// that has stored the header refuses certificate warnings for the host
	// name on every port. It defaults to false, which sends max-age=31536000.
	OmitStrictTransportSecurity bool

	// MaxRequestBody bounds the size of a request body in bytes. Larger
	// requests are refused with 413. If zero, 64 KiB is used. A CSR for a
	// large post-quantum key can exceed that, such as an SLH-DSA-SHA2-256f
	// request at about 67 KB in base64. NewRouter refuses a negative value.
	// Keep it below 4 MiB: /tpmenroll decodes its PKCS#7 part with a parser
	// that recurses once per nested structure. A structure nested about
	// 10 MB deep exhausts the stack and ends the process.
	MaxRequestBody int64

	// TrustProxyHeaders sets the request's RemoteAddr from the X-Real-IP or
	// X-Forwarded-For header. It defaults to false. Enable it only when every
	// request passes through a trusted proxy that removes or overwrites both
	// headers. Proxy source addresses are not checked.
	TrustProxyHeaders bool
}

ServerConfig contains EST server configuration options.

Directories

Path Synopsis
cmd
estclient command
Application estclient is an EST client.
Application estclient is an EST client.
estserver command
Application estserver is a basic EST server for testing purposes.
Application estserver is a basic EST server for testing purposes.
internal
basiclogger
Package basiclogger provides a basic logger implementing est.Logger, for demonstration and testing purposes.
Package basiclogger provides a basic logger implementing est.Logger, for demonstration and testing purposes.
mockca
Package mockca provides a mock certificate authority for testing purposes.
Package mockca provides a mock certificate authority for testing purposes.
tpm
Package tpm provides TPM 2.0 credential-related functionality.
Package tpm provides TPM 2.0 credential-related functionality.

Jump to

Keyboard shortcuts

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