forked from eggsampler/acme
-
Notifications
You must be signed in to change notification settings - Fork 0
/
options.go
173 lines (152 loc) · 5.28 KB
/
options.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
package acme
import (
"crypto"
"crypto/hmac"
"crypto/tls"
"crypto/x509"
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"net/http"
"time"
)
// OptionFunc function prototype for passing options to NewClient
type OptionFunc func(client *Client) error
// WithHTTPTimeout sets a timeout on the http client used by the Client
func WithHTTPTimeout(duration time.Duration) OptionFunc {
return func(client *Client) error {
client.httpClient.Timeout = duration
return nil
}
}
// WithInsecureSkipVerify sets InsecureSkipVerify on the http client transport tls client config used by the Client
func WithInsecureSkipVerify() OptionFunc {
return func(client *Client) error {
client.httpClient.Transport = &http.Transport{
TLSClientConfig: &tls.Config{
InsecureSkipVerify: true,
},
}
return nil
}
}
// WithUserAgentSuffix appends a user agent suffix for http requests to acme resources
func WithUserAgentSuffix(userAgentSuffix string) OptionFunc {
return func(client *Client) error {
client.userAgentSuffix = userAgentSuffix
return nil
}
}
// WithAcceptLanguage sets an Accept-Language header on http requests
func WithAcceptLanguage(acceptLanguage string) OptionFunc {
return func(client *Client) error {
client.acceptLanguage = acceptLanguage
return nil
}
}
// WithRetryCount sets the number of times the acme client retries when receiving an api error (eg, nonce failures, etc).
// Default: 5
func WithRetryCount(retryCount int) OptionFunc {
return func(client *Client) error {
if retryCount < 1 {
return errors.New("retryCount must be > 0")
}
client.retryCount = retryCount
return nil
}
}
// WithHTTPClient Allows setting a custom http client for acme connections
func WithHTTPClient(httpClient *http.Client) OptionFunc {
return func(client *Client) error {
if httpClient == nil {
return errors.New("client must not be nil")
}
client.httpClient = httpClient
return nil
}
}
// WithRootCerts sets the httpclient transport to use a given certpool for root certs
func WithRootCerts(pool *x509.CertPool) OptionFunc {
return func(client *Client) error {
client.httpClient.Transport = &http.Transport{
TLSClientConfig: &tls.Config{
RootCAs: pool,
},
}
return nil
}
}
// NewAccountOptionFunc function prototype for passing options to NewClient
type NewAccountOptionFunc func(crypto.Signer, *Account, *NewAccountRequest, Client) error
// NewAcctOptOnlyReturnExisting sets the new client request to only return existing accounts
func NewAcctOptOnlyReturnExisting() NewAccountOptionFunc {
return func(privateKey crypto.Signer, account *Account, request *NewAccountRequest, client Client) error {
request.OnlyReturnExisting = true
return nil
}
}
// NewAcctOptAgreeTOS sets the new account request as agreeing to the terms of service
func NewAcctOptAgreeTOS() NewAccountOptionFunc {
return func(privateKey crypto.Signer, account *Account, request *NewAccountRequest, client Client) error {
request.TermsOfServiceAgreed = true
return nil
}
}
// NewAcctOptWithContacts adds contacts to a new account request
func NewAcctOptWithContacts(contacts ...string) NewAccountOptionFunc {
return func(privateKey crypto.Signer, account *Account, request *NewAccountRequest, client Client) error {
request.Contact = contacts
return nil
}
}
// NewAcctOptExternalAccountBinding adds an external account binding to the new account request
// Code adopted from jwsEncodeJSON
func NewAcctOptExternalAccountBinding(binding ExternalAccountBinding) NewAccountOptionFunc {
return func(privateKey crypto.Signer, account *Account, request *NewAccountRequest, client Client) error {
if binding.KeyIdentifier == "" {
return errors.New("acme: NewAcctOptExternalAccountBinding has no KeyIdentifier set")
}
if binding.MacKey == "" {
return errors.New("acme: NewAcctOptExternalAccountBinding has no MacKey set")
}
if binding.Algorithm == "" {
return errors.New("acme: NewAcctOptExternalAccountBinding has no Algorithm set")
}
if binding.HashFunc == 0 {
return errors.New("acme: NewAcctOptExternalAccountBinding has no HashFunc set")
}
jwk, err := jwkEncode(privateKey.Public())
if err != nil {
return fmt.Errorf("acme: external account binding error encoding public key: %v", err)
}
payload := base64.RawURLEncoding.EncodeToString([]byte(jwk))
phead := fmt.Sprintf(`{"alg":%q,"kid":%q,"url":%q}`,
binding.Algorithm, binding.KeyIdentifier, client.Directory().NewAccount)
phead = base64.RawURLEncoding.EncodeToString([]byte(phead))
decodedAccountMac, err := base64.RawURLEncoding.DecodeString(binding.MacKey)
if err != nil {
return fmt.Errorf("acme: external account binding error decoding mac key: %v", err)
}
macHash := hmac.New(binding.HashFunc.New, decodedAccountMac)
if _, err := macHash.Write([]byte(phead + "." + payload)); err != nil {
return err
}
enc := struct {
Protected string `json:"protected"`
Payload string `json:"payload"`
Sig string `json:"signature"`
}{
Protected: phead,
Payload: payload,
Sig: base64.RawURLEncoding.EncodeToString(macHash.Sum(nil)),
}
jwsEab, err := json.Marshal(&enc)
if err != nil {
return fmt.Errorf("acme: external account binding error marshalling struct: %v", err)
}
request.ExternalAccountBinding = jwsEab
account.ExternalAccountBinding = binding
return nil
}
}