Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
K
k3s
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Registry
Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Jacklull
k3s
Commits
33a59542
Unverified
Commit
33a59542
authored
Jul 17, 2019
by
Erik Wilson
Committed by
GitHub
Jul 17, 2019
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #649 from erikwilson/update-dynamiclistener
Update dynamiclistener
parents
2d323373
25e9cfb0
Hide whitespace changes
Inline
Side-by-side
Showing
12 changed files
with
169 additions
and
3526 deletions
+169
-3526
trash.lock
trash.lock
+2
-1
vendor.conf
vendor.conf
+1
-1
read.go
vendor/github.com/rancher/dynamiclistener/read.go
+0
-5
server.go
vendor/github.com/rancher/dynamiclistener/server.go
+166
-322
acme.go
vendor/golang.org/x/crypto/acme/acme.go
+0
-911
autocert.go
vendor/golang.org/x/crypto/acme/autocert/autocert.go
+0
-1092
cache.go
vendor/golang.org/x/crypto/acme/autocert/cache.go
+0
-130
listener.go
vendor/golang.org/x/crypto/acme/autocert/listener.go
+0
-160
renewal.go
vendor/golang.org/x/crypto/acme/autocert/renewal.go
+0
-141
http.go
vendor/golang.org/x/crypto/acme/http.go
+0
-281
jws.go
vendor/golang.org/x/crypto/acme/jws.go
+0
-153
types.go
vendor/golang.org/x/crypto/acme/types.go
+0
-329
No files found.
trash.lock
View file @
33a59542
...
...
@@ -225,7 +225,8 @@ import:
- package: github.com/prometheus/procfs
version: 65c1f6f8f0fc1e2185eb9863a3bc751496404259
- package: github.com/rancher/dynamiclistener
version: 4716ac2362986f28bede3f3caf5d1ce347da55b0
version: c08b499d17195fbc2c1764b21c322951811629a5
repo: https://github.com/erikwilson/rancher-dynamiclistener.git
- package: github.com/rancher/helm-controller
version: v0.2.1
- package: github.com/rancher/remotedialer
...
...
vendor.conf
View file @
33a59542
...
...
@@ -13,7 +13,7 @@ k8s.io/kubernetes v1.14.4-k3s.1 ht
github
.
com
/
rancher
/
wrangler
7737
c167e16514a38229bc64c839cee8cd14e6d3
github
.
com
/
rancher
/
wrangler
-
api
v0
.
1
.
4
github
.
com
/
rancher
/
dynamiclistener
4716
ac2362986f28bede3f3caf5d1ce347da55b0
github
.
com
/
rancher
/
dynamiclistener
c08b499d17195fbc2c1764b21c322951811629a5
https
://
github
.
com
/
erikwilson
/
rancher
-
dynamiclistener
.
git
github
.
com
/
rancher
/
remotedialer
4
a5a661be67697d6369df54ef62d5a30b0385697
github
.
com
/
rancher
/
helm
-
controller
v0
.
2
.
1
github
.
com
/
matryer
/
moq
ee5226d43009
https
://
github
.
com
/
rancher
/
moq
.
git
...
...
vendor/github.com/rancher/dynamiclistener/read.go
View file @
33a59542
...
...
@@ -26,11 +26,6 @@ func ReadTLSConfig(userConfig *UserConfig) error {
return
err
}
userConfig
.
Mode
=
"https"
if
len
(
userConfig
.
Domains
)
>
0
{
userConfig
.
Mode
=
"acme"
}
valid
:=
false
if
userConfig
.
Key
!=
""
&&
userConfig
.
Cert
!=
""
{
valid
=
true
...
...
vendor/github.com/rancher/dynamiclistener/server.go
View file @
33a59542
package
dynamiclistener
import
(
"bytes"
"context"
"crypto"
"crypto/ecdsa"
"crypto/md5"
"crypto/rsa"
"crypto/tls"
"crypto/x509"
"encoding/base64"
"encoding/hex"
"encoding/pem"
"errors"
"fmt"
"log"
"net"
"net/http"
"
sor
t"
"
reflec
t"
"strconv"
"strings"
"sync"
"time"
lru
"github.com/hashicorp/golang-lru"
cert
"github.com/rancher/dynamiclistener/cert"
"github.com/sirupsen/logrus"
"golang.org/x/crypto/acme/autocert"
)
const
(
httpsMode
=
"https"
acmeMode
=
"acme"
)
type
server
struct
{
...
...
@@ -39,28 +29,28 @@ type server struct {
userConfig
UserConfig
listenConfigStorage
ListenerConfigStorage
certs
map
[
string
]
*
tls
.
Certificate
ips
*
lru
.
Cache
tlsCert
*
tls
.
Certificate
ips
map
[
string
]
bool
domains
map
[
string
]
bool
cn
string
listeners
[]
net
.
Listener
servers
[]
*
http
.
Server
// dynamic config change on refresh
activeCert
*
tls
.
Certificate
activeCA
*
x509
.
Certificate
activeCAKey
crypto
.
Signer
activeCAKeyString
string
domains
map
[
string
]
bool
activeCA
*
x509
.
Certificate
activeCAKey
crypto
.
Signer
}
func
NewServer
(
listenConfigStorage
ListenerConfigStorage
,
config
UserConfig
)
(
ServerInterface
,
error
)
{
s
:=
&
server
{
userConfig
:
config
,
listenConfigStorage
:
listenConfigStorage
,
c
erts
:
map
[
string
]
*
tls
.
Certificate
{}
,
c
n
:
"cattle"
,
}
s
.
ips
,
_
=
lru
.
New
(
20
)
s
.
ips
=
map
[
string
]
bool
{}
s
.
domains
=
map
[
string
]
bool
{}
if
err
:=
s
.
userConfigure
();
err
!=
nil
{
return
nil
,
err
...
...
@@ -81,16 +71,7 @@ func (s *server) CACert() (string, error) {
if
s
.
userConfig
.
CACerts
!=
""
{
return
s
.
userConfig
.
CACerts
,
nil
}
status
,
err
:=
s
.
listenConfigStorage
.
Get
()
if
err
!=
nil
{
return
""
,
err
}
if
status
.
CACert
==
""
{
return
""
,
fmt
.
Errorf
(
"ca cert not found"
)
}
return
status
.
CACert
,
nil
return
""
,
fmt
.
Errorf
(
"ca cert not found"
)
}
func
marshalPrivateKey
(
privateKey
crypto
.
Signer
)
(
string
,
[]
byte
,
error
)
{
...
...
@@ -127,78 +108,25 @@ func newPrivateKey() (crypto.Signer, error) {
return
caKeyIFace
.
(
crypto
.
Signer
),
nil
}
func
(
s
*
server
)
save
()
{
if
s
.
activeCert
!=
nil
{
return
}
s
.
Lock
()
defer
s
.
Unlock
()
changed
:=
false
cfg
,
err
:=
s
.
listenConfigStorage
.
Get
()
if
err
!=
nil
{
return
}
if
cfg
.
GeneratedCerts
==
nil
{
cfg
.
GeneratedCerts
=
map
[
string
]
string
{}
}
if
cfg
.
KnownIPs
==
nil
{
cfg
.
KnownIPs
=
map
[
string
]
bool
{}
}
for
key
,
cert
:=
range
s
.
certs
{
certStr
,
err
:=
certToString
(
cert
)
if
err
!=
nil
{
continue
}
if
cfg
.
GeneratedCerts
[
key
]
!=
certStr
{
cfg
.
GeneratedCerts
[
key
]
=
certStr
changed
=
true
func
(
s
*
server
)
save
()
(
_err
error
)
{
defer
func
()
{
if
_err
!=
nil
{
logrus
.
Errorf
(
"Saving cert error: %s"
,
_err
)
}
}
}
()
for
_
,
obj
:=
range
s
.
ips
.
Keys
()
{
ip
,
_
:=
obj
.
(
string
)
if
!
cfg
.
KnownIPs
[
ip
]
{
cfg
.
KnownIPs
[
ip
]
=
true
changed
=
true
}
certStr
,
err
:=
certToString
(
s
.
tlsCert
)
if
err
!=
nil
{
return
err
}
if
cfg
.
CAKey
==
""
&&
s
.
activeCAKey
!=
nil
&&
s
.
activeCA
!=
nil
{
caCertBuffer
:=
bytes
.
Buffer
{}
if
err
:=
pem
.
Encode
(
&
caCertBuffer
,
&
pem
.
Block
{
Type
:
cert
.
CertificateBlockType
,
Bytes
:
s
.
activeCA
.
Raw
,
});
err
!=
nil
{
return
}
caKeyBuffer
:=
bytes
.
Buffer
{}
keyType
,
keyBytes
,
err
:=
marshalPrivateKey
(
s
.
activeCAKey
)
if
err
!=
nil
{
return
}
if
err
:=
pem
.
Encode
(
&
caKeyBuffer
,
&
pem
.
Block
{
Type
:
keyType
,
Bytes
:
keyBytes
,
});
err
!=
nil
{
return
}
cfg
.
CACert
=
string
(
caCertBuffer
.
Bytes
())
cfg
.
CAKey
=
string
(
caKeyBuffer
.
Bytes
())
s
.
activeCAKeyString
=
cfg
.
CAKey
changed
=
true
cfg
,
err
:=
s
.
listenConfigStorage
.
Get
()
if
err
!=
nil
{
return
err
}
cfg
.
GeneratedCerts
=
map
[
string
]
string
{
s
.
cn
:
certStr
}
if
changed
{
s
.
listenConfigStorage
.
Set
(
cfg
)
}
_
,
err
=
s
.
listenConfigStorage
.
Set
(
cfg
)
return
err
}
func
(
s
*
server
)
userConfigure
()
error
{
...
...
@@ -206,39 +134,42 @@ func (s *server) userConfigure() error {
s
.
userConfig
.
HTTPSPort
=
8443
}
if
s
.
userConfig
.
Mode
==
""
{
if
len
(
s
.
userConfig
.
Domains
)
>
0
{
s
.
userConfig
.
Mode
=
acmeMode
}
else
{
s
.
userConfig
.
Mode
=
httpsMode
}
}
s
.
domains
=
map
[
string
]
bool
{}
for
_
,
d
:=
range
s
.
userConfig
.
Domains
{
s
.
domains
[
d
]
=
true
}
if
s
.
userConfig
.
Key
!=
""
&&
s
.
userConfig
.
Cert
!=
""
{
cert
,
err
:=
tls
.
X509KeyPair
([]
byte
(
s
.
userConfig
.
Cert
),
[]
byte
(
s
.
userConfig
.
Key
))
if
err
!=
nil
{
return
err
for
_
,
ip
:=
range
s
.
userConfig
.
KnownIPs
{
if
netIP
:=
net
.
ParseIP
(
ip
);
netIP
!=
nil
{
s
.
ips
[
ip
]
=
true
}
s
.
activeCert
=
&
cert
s
.
userConfig
.
Mode
=
httpsMode
return
s
.
reload
()
}
for
_
,
ip
:=
range
s
.
userConfig
.
KnownIPs
{
netIP
:=
net
.
ParseIP
(
ip
)
if
netIP
!=
nil
{
s
.
ips
.
Add
(
ip
,
netIP
)
}
if
bindAddress
:=
net
.
ParseIP
(
s
.
userConfig
.
BindAddress
);
bindAddress
!=
nil
{
s
.
ips
[
s
.
userConfig
.
BindAddress
]
=
true
}
bindAddress
:=
net
.
ParseIP
(
s
.
userConfig
.
BindAddress
)
if
bindAddress
!=
nil
{
s
.
ips
.
Add
(
s
.
userConfig
.
BindAddress
,
bindAddress
)
if
s
.
activeCA
==
nil
&&
s
.
activeCAKey
==
nil
{
if
s
.
userConfig
.
CACerts
!=
""
&&
s
.
userConfig
.
CAKey
!=
""
{
ca
,
err
:=
cert
.
ParseCertsPEM
([]
byte
(
s
.
userConfig
.
CACerts
))
if
err
!=
nil
{
return
err
}
key
,
err
:=
cert
.
ParsePrivateKeyPEM
([]
byte
(
s
.
userConfig
.
CAKey
))
if
err
!=
nil
{
return
err
}
s
.
activeCA
=
ca
[
0
]
s
.
activeCAKey
=
key
.
(
crypto
.
Signer
)
}
else
{
ca
,
key
,
err
:=
genCA
()
if
err
!=
nil
{
return
err
}
s
.
activeCA
=
ca
s
.
activeCAKey
=
key
}
}
return
nil
}
...
...
@@ -259,59 +190,74 @@ func genCA() (*x509.Certificate, crypto.Signer, error) {
return
caCert
,
caKey
,
nil
}
func
(
s
*
server
)
Update
(
status
*
ListenerStatus
)
error
{
func
(
s
*
server
)
Update
(
status
*
ListenerStatus
)
(
_err
error
)
{
s
.
Lock
()
defer
s
.
getCertificate
(
&
tls
.
ClientHelloInfo
{
ServerName
:
"localhost"
})
if
status
.
CACert
!=
""
&&
status
.
CAKey
!=
""
&&
s
.
activeCAKeyString
!=
status
.
CAKey
{
cert
,
err
:=
tls
.
X509KeyPair
([]
byte
(
status
.
CACert
),
[]
byte
(
status
.
CAKey
))
if
err
!=
nil
{
s
.
Unlock
()
return
err
}
s
.
activeCAKey
=
cert
.
PrivateKey
.
(
crypto
.
Signer
)
s
.
activeCAKeyString
=
status
.
CAKey
x509Cert
,
err
:=
x509
.
ParseCertificate
(
cert
.
Certificate
[
0
])
if
err
!=
nil
{
s
.
Unlock
()
return
err
defer
func
()
{
s
.
Unlock
()
if
_err
!=
nil
{
logrus
.
Errorf
(
"Update cert error: %s"
,
_err
)
}
s
.
activeCA
=
x509Cert
s
.
certs
=
map
[
string
]
*
tls
.
Certificate
{}
}
for
ipStr
:=
range
status
.
KnownIPs
{
ip
:=
net
.
ParseIP
(
ipStr
)
if
len
(
ip
)
>
0
{
s
.
ips
.
ContainsOrAdd
(
ipStr
,
ip
)
if
s
.
tlsCert
==
nil
{
s
.
getCertificate
(
&
tls
.
ClientHelloInfo
{
ServerName
:
"localhost"
})
}
}
}
()
for
key
,
certString
:=
range
status
.
GeneratedCerts
{
cert
:=
stringToCert
(
certString
)
if
cert
!=
nil
{
s
.
certs
[
key
]
=
cert
}
certString
:=
status
.
GeneratedCerts
[
s
.
cn
]
tlsCert
,
err
:=
stringToCert
(
certString
)
if
err
!=
nil
{
logrus
.
Errorf
(
"Update cert unable to convert string to cert: %s"
,
err
)
s
.
tlsCert
=
nil
}
if
tlsCert
!=
nil
{
s
.
tlsCert
=
tlsCert
for
i
,
certBytes
:=
range
tlsCert
.
Certificate
{
cert
,
err
:=
x509
.
ParseCertificate
(
certBytes
)
if
err
!=
nil
{
logrus
.
Errorf
(
"Update cert %d parse error: %s"
,
i
,
err
)
s
.
tlsCert
=
nil
break
}
s
.
Unlock
()
return
s
.
reload
()
}
ips
:=
map
[
string
]
bool
{}
for
_
,
ip
:=
range
cert
.
IPAddresses
{
ips
[
ip
.
String
()]
=
true
}
func
(
s
*
server
)
hostPolicy
(
ctx
context
.
Context
,
host
string
)
error
{
s
.
Lock
()
defer
s
.
Unlock
()
domains
:=
map
[
string
]
bool
{}
for
_
,
domain
:=
range
cert
.
DNSNames
{
domains
[
domain
]
=
true
}
if
s
.
domains
[
host
]
{
return
nil
if
!
(
reflect
.
DeepEqual
(
ips
,
s
.
ips
)
&&
reflect
.
DeepEqual
(
domains
,
s
.
domains
))
{
subset
:=
true
for
ip
:=
range
s
.
ips
{
if
!
ips
[
ip
]
{
subset
=
false
break
}
}
if
subset
{
for
domain
:=
range
s
.
domains
{
if
!
domains
[
domain
]
{
subset
=
false
break
}
}
}
if
!
subset
{
s
.
tlsCert
=
nil
}
for
ip
:=
range
ips
{
s
.
ips
[
ip
]
=
true
}
for
domain
:=
range
domains
{
s
.
domains
[
domain
]
=
true
}
}
}
}
return
errors
.
New
(
"acme/autocert: host not configured"
)
}
func
(
s
*
server
)
prompt
(
tos
string
)
bool
{
return
true
return
s
.
reload
()
}
func
(
s
*
server
)
shutdown
()
error
{
...
...
@@ -339,114 +285,53 @@ func (s *server) reload() error {
return
err
}
switch
s
.
userConfig
.
Mode
{
case
acmeMode
:
if
err
:=
s
.
serveACME
();
err
!=
nil
{
return
err
}
case
httpsMode
:
if
err
:=
s
.
serveHTTPS
();
err
!=
nil
{
return
err
}
if
err
:=
s
.
serveHTTPS
();
err
!=
nil
{
return
err
}
return
nil
}
func
(
s
*
server
)
ipMapKey
()
string
{
len
:=
s
.
ips
.
Len
()
keys
:=
s
.
ips
.
Keys
()
if
len
==
0
{
return
fmt
.
Sprintf
(
"local/%d"
,
len
)
}
else
if
len
==
1
{
return
fmt
.
Sprintf
(
"local/%s"
,
keys
[
0
])
}
sort
.
Slice
(
keys
,
func
(
i
,
j
int
)
bool
{
l
,
_
:=
keys
[
i
]
.
(
string
)
r
,
_
:=
keys
[
j
]
.
(
string
)
return
l
<
r
})
if
len
<
6
{
return
fmt
.
Sprintf
(
"local/%v"
,
keys
)
}
digest
:=
md5
.
New
()
for
_
,
k
:=
range
keys
{
s
,
_
:=
k
.
(
string
)
digest
.
Write
([]
byte
(
s
))
}
return
fmt
.
Sprintf
(
"local/%v"
,
hex
.
EncodeToString
(
digest
.
Sum
(
nil
)))
}
func
(
s
*
server
)
getCertificate
(
hello
*
tls
.
ClientHelloInfo
)
(
*
tls
.
Certificate
,
error
)
{
func
(
s
*
server
)
getCertificate
(
hello
*
tls
.
ClientHelloInfo
)
(
_servingCert
*
tls
.
Certificate
,
_err
error
)
{
s
.
Lock
()
if
s
.
activeCert
!=
nil
{
s
.
Unlock
()
return
s
.
activeCert
,
nil
}
changed
:=
false
defer
func
()
{
defer
s
.
Unlock
()
if
_err
!=
nil
{
logrus
.
Errorf
(
"Get certificate error: %s"
,
_err
)
return
}
if
changed
{
s
.
save
()
}
}()
defer
s
.
Unlock
()
mapKey
:=
hello
.
ServerName
cn
:=
hello
.
ServerName
dnsNames
:=
[]
string
{
cn
}
ipBased
:=
false
var
ips
[]
net
.
IP
if
cn
==
""
{
mapKey
=
s
.
ipMapKey
()
ipBased
=
true
if
hello
.
ServerName
!=
""
&&
!
s
.
domains
[
hello
.
ServerName
]
{
s
.
tlsCert
=
nil
s
.
domains
[
hello
.
ServerName
]
=
true
}
serverNameCert
,
ok
:=
s
.
certs
[
mapKey
]
if
ok
{
return
serverNameCert
,
nil
if
s
.
tlsCert
!=
nil
{
return
s
.
tlsCert
,
nil
}
if
ipBased
{
cn
=
"cattle"
for
_
,
ipStr
:=
range
s
.
ips
.
Keys
()
{
ip
:=
net
.
ParseIP
(
ipStr
.
(
string
))
if
len
(
ip
)
>
0
{
ips
=
append
(
ips
,
ip
)
}
ips
:=
[]
net
.
IP
{}
for
ipStr
:=
range
s
.
ips
{
if
ip
:=
net
.
ParseIP
(
ipStr
);
ip
!=
nil
{
ips
=
append
(
ips
,
ip
)
}
}
changed
=
true
if
s
.
activeCA
==
nil
{
if
s
.
userConfig
.
CACerts
!=
""
&&
s
.
userConfig
.
CAKey
!=
""
{
ca
,
err
:=
cert
.
ParseCertsPEM
([]
byte
(
s
.
userConfig
.
CACerts
))
if
err
!=
nil
{
return
nil
,
err
}
key
,
err
:=
cert
.
ParsePrivateKeyPEM
([]
byte
(
s
.
userConfig
.
CAKey
))
if
err
!=
nil
{
return
nil
,
err
}
s
.
activeCA
=
ca
[
0
]
s
.
activeCAKey
=
key
.
(
crypto
.
Signer
)
}
else
{
ca
,
key
,
err
:=
genCA
()
if
err
!=
nil
{
return
nil
,
err
}
s
.
activeCA
=
ca
s
.
activeCAKey
=
key
}
dnsNames
:=
[]
string
{}
for
domain
:=
range
s
.
domains
{
dnsNames
=
append
(
dnsNames
,
domain
)
}
cfg
:=
cert
.
Config
{
CommonName
:
cn
,
CommonName
:
s
.
cn
,
Organization
:
s
.
activeCA
.
Subject
.
Organization
,
Usages
:
[]
x509
.
ExtKeyUsage
{
x509
.
ExtKeyUsageServerAuth
},
AltNames
:
cert
.
AltNames
{
...
...
@@ -472,23 +357,31 @@ func (s *server) getCertificate(hello *tls.ClientHelloInfo) (*tls.Certificate, e
PrivateKey
:
key
,
}
s
.
certs
[
mapKey
]
=
tlsCert
changed
=
true
s
.
tlsCert
=
tlsCert
return
tlsCert
,
nil
}
func
(
s
*
server
)
cache
IP
Handler
(
handler
http
.
Handler
)
http
.
Handler
{
func
(
s
*
server
)
cacheHandler
(
handler
http
.
Handler
)
http
.
Handler
{
return
http
.
HandlerFunc
(
func
(
resp
http
.
ResponseWriter
,
req
*
http
.
Request
)
{
h
,
_
,
err
:=
net
.
SplitHostPort
(
req
.
Host
)
if
err
!=
nil
{
h
=
req
.
Host
}
ip
:=
net
.
ParseIP
(
h
)
if
len
(
ip
)
>
0
{
if
ok
,
_
:=
s
.
ips
.
ContainsOrAdd
(
h
,
ip
);
ok
{
go
s
.
save
()
s
.
Lock
()
if
ip
:=
net
.
ParseIP
(
h
);
ip
!=
nil
{
if
!
s
.
ips
[
h
]
{
s
.
ips
[
h
]
=
true
s
.
tlsCert
=
nil
}
}
else
{
if
!
s
.
domains
[
h
]
{
s
.
domains
[
h
]
=
true
s
.
tlsCert
=
nil
}
}
s
.
Unlock
()
handler
.
ServeHTTP
(
resp
,
req
)
})
...
...
@@ -508,7 +401,7 @@ func (s *server) serveHTTPS() error {
logger
:=
logrus
.
StandardLogger
()
server
:=
&
http
.
Server
{
Handler
:
s
.
cache
IP
Handler
(
s
.
Handler
()),
Handler
:
s
.
cacheHandler
(
s
.
Handler
()),
ErrorLog
:
log
.
New
(
logger
.
WriterLevel
(
logrus
.
DebugLevel
),
""
,
log
.
LstdFlags
),
}
...
...
@@ -522,7 +415,7 @@ func (s *server) serveHTTPS() error {
}
httpServer
:=
&
http
.
Server
{
Handler
:
s
.
cache
IP
Handler
(
httpRedirect
(
s
.
Handler
())),
Handler
:
s
.
cacheHandler
(
httpRedirect
(
s
.
Handler
())),
ErrorLog
:
log
.
New
(
logger
.
WriterLevel
(
logrus
.
DebugLevel
),
""
,
log
.
LstdFlags
),
}
...
...
@@ -598,97 +491,48 @@ func (s *server) newListener(ip string, port int, config *tls.Config) (net.Liste
return
l
,
nil
}
func
(
s
*
server
)
serveACME
()
error
{
manager
:=
autocert
.
Manager
{
Cache
:
autocert
.
DirCache
(
"certs-cache"
),
Prompt
:
s
.
prompt
,
HostPolicy
:
s
.
hostPolicy
,
}
conf
:=
&
tls
.
Config
{
GetCertificate
:
func
(
hello
*
tls
.
ClientHelloInfo
)
(
*
tls
.
Certificate
,
error
)
{
if
hello
.
ServerName
==
"localhost"
||
hello
.
ServerName
==
""
{
newHello
:=
*
hello
newHello
.
ServerName
=
s
.
userConfig
.
Domains
[
0
]
return
manager
.
GetCertificate
(
&
newHello
)
}
return
manager
.
GetCertificate
(
hello
)
},
NextProtos
:
[]
string
{
"h2"
,
"http/1.1"
},
}
if
s
.
userConfig
.
HTTPPort
>
0
{
httpListener
,
err
:=
s
.
newListener
(
s
.
userConfig
.
BindAddress
,
s
.
userConfig
.
HTTPPort
,
nil
)
if
err
!=
nil
{
return
err
}
httpServer
:=
&
http
.
Server
{
Handler
:
manager
.
HTTPHandler
(
nil
),
ErrorLog
:
log
.
New
(
logrus
.
StandardLogger
()
.
Writer
(),
""
,
log
.
LstdFlags
),
}
s
.
servers
=
append
(
s
.
servers
,
httpServer
)
go
func
()
{
if
err
:=
httpServer
.
Serve
(
httpListener
);
err
!=
nil
{
logrus
.
Errorf
(
"http server returned err: %v"
,
err
)
}
}()
}
httpsListener
,
err
:=
s
.
newListener
(
s
.
userConfig
.
BindAddress
,
s
.
userConfig
.
HTTPSPort
,
conf
)
if
err
!=
nil
{
return
err
}
httpsServer
:=
&
http
.
Server
{
Handler
:
s
.
Handler
(),
ErrorLog
:
log
.
New
(
logrus
.
StandardLogger
()
.
Writer
(),
""
,
log
.
LstdFlags
),
}
s
.
servers
=
append
(
s
.
servers
,
httpsServer
)
go
func
()
{
if
err
:=
httpsServer
.
Serve
(
httpsListener
);
err
!=
nil
{
logrus
.
Errorf
(
"https server returned err: %v"
,
err
)
}
}()
return
nil
}
func
stringToCert
(
certString
string
)
*
tls
.
Certificate
{
func
stringToCert
(
certString
string
)
(
*
tls
.
Certificate
,
error
)
{
parts
:=
strings
.
Split
(
certString
,
"#"
)
if
len
(
parts
)
!=
2
{
return
nil
return
nil
,
errors
.
New
(
"Unable to split cert into two parts"
)
}
certPart
,
keyPart
:=
parts
[
0
],
parts
[
1
]
keyBytes
,
err
:=
base64
.
StdEncoding
.
DecodeString
(
keyPart
)
if
err
!=
nil
{
return
nil
return
nil
,
err
}
key
,
err
:=
cert
.
ParsePrivateKeyPEM
(
keyBytes
)
if
err
!=
nil
{
return
nil
return
nil
,
err
}
certBytes
,
err
:=
base64
.
StdEncoding
.
DecodeString
(
certPart
)
if
err
!=
nil
{
return
nil
return
nil
,
err
}
return
&
tls
.
Certificate
{
Certificate
:
[][]
byte
{
certBytes
},
PrivateKey
:
key
,
}
}
,
nil
}
func
certToString
(
cert
*
tls
.
Certificate
)
(
string
,
error
)
{
_
,
keyBytes
,
err
:=
marshalPrivateKey
(
cert
.
PrivateKey
.
(
crypto
.
Signer
))
keyType
,
keyBytes
,
err
:=
marshalPrivateKey
(
cert
.
PrivateKey
.
(
crypto
.
Signer
))
if
err
!=
nil
{
return
""
,
err
}
privateKeyPemBlock
:=
&
pem
.
Block
{
Type
:
keyType
,
Bytes
:
keyBytes
,
}
pemBytes
:=
pem
.
EncodeToMemory
(
privateKeyPemBlock
)
certString
:=
base64
.
StdEncoding
.
EncodeToString
(
cert
.
Certificate
[
0
])
keyString
:=
base64
.
StdEncoding
.
EncodeToString
(
key
Bytes
)
keyString
:=
base64
.
StdEncoding
.
EncodeToString
(
pem
Bytes
)
return
certString
+
"#"
+
keyString
,
nil
}
...
...
vendor/golang.org/x/crypto/acme/acme.go
deleted
100644 → 0
View file @
2d323373
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package acme provides an implementation of the
// Automatic Certificate Management Environment (ACME) spec.
// See https://tools.ietf.org/html/draft-ietf-acme-acme-02 for details.
//
// Most common scenarios will want to use autocert subdirectory instead,
// which provides automatic access to certificates from Let's Encrypt
// and any other ACME-based CA.
//
// This package is a work in progress and makes no API stability promises.
package
acme
import
(
"context"
"crypto"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/sha256"
"crypto/tls"
"crypto/x509"
"crypto/x509/pkix"
"encoding/asn1"
"encoding/base64"
"encoding/hex"
"encoding/json"
"encoding/pem"
"errors"
"fmt"
"io"
"io/ioutil"
"math/big"
"net/http"
"strings"
"sync"
"time"
)
// LetsEncryptURL is the Directory endpoint of Let's Encrypt CA.
const
LetsEncryptURL
=
"https://acme-v01.api.letsencrypt.org/directory"
// idPeACMEIdentifierV1 is the OID for the ACME extension for the TLS-ALPN challenge.
var
idPeACMEIdentifierV1
=
asn1
.
ObjectIdentifier
{
1
,
3
,
6
,
1
,
5
,
5
,
7
,
1
,
30
,
1
}
const
(
maxChainLen
=
5
// max depth and breadth of a certificate chain
maxCertSize
=
1
<<
20
// max size of a certificate, in bytes
// Max number of collected nonces kept in memory.
// Expect usual peak of 1 or 2.
maxNonces
=
100
)
// Client is an ACME client.
// The only required field is Key. An example of creating a client with a new key
// is as follows:
//
// key, err := rsa.GenerateKey(rand.Reader, 2048)
// if err != nil {
// log.Fatal(err)
// }
// client := &Client{Key: key}
//
type
Client
struct
{
// Key is the account key used to register with a CA and sign requests.
// Key.Public() must return a *rsa.PublicKey or *ecdsa.PublicKey.
Key
crypto
.
Signer
// HTTPClient optionally specifies an HTTP client to use
// instead of http.DefaultClient.
HTTPClient
*
http
.
Client
// DirectoryURL points to the CA directory endpoint.
// If empty, LetsEncryptURL is used.
// Mutating this value after a successful call of Client's Discover method
// will have no effect.
DirectoryURL
string
// RetryBackoff computes the duration after which the nth retry of a failed request
// should occur. The value of n for the first call on failure is 1.
// The values of r and resp are the request and response of the last failed attempt.
// If the returned value is negative or zero, no more retries are done and an error
// is returned to the caller of the original method.
//
// Requests which result in a 4xx client error are not retried,
// except for 400 Bad Request due to "bad nonce" errors and 429 Too Many Requests.
//
// If RetryBackoff is nil, a truncated exponential backoff algorithm
// with the ceiling of 10 seconds is used, where each subsequent retry n
// is done after either ("Retry-After" + jitter) or (2^n seconds + jitter),
// preferring the former if "Retry-After" header is found in the resp.
// The jitter is a random value up to 1 second.
RetryBackoff
func
(
n
int
,
r
*
http
.
Request
,
resp
*
http
.
Response
)
time
.
Duration
dirMu
sync
.
Mutex
// guards writes to dir
dir
*
Directory
// cached result of Client's Discover method
noncesMu
sync
.
Mutex
nonces
map
[
string
]
struct
{}
// nonces collected from previous responses
}
// Discover performs ACME server discovery using c.DirectoryURL.
//
// It caches successful result. So, subsequent calls will not result in
// a network round-trip. This also means mutating c.DirectoryURL after successful call
// of this method will have no effect.
func
(
c
*
Client
)
Discover
(
ctx
context
.
Context
)
(
Directory
,
error
)
{
c
.
dirMu
.
Lock
()
defer
c
.
dirMu
.
Unlock
()
if
c
.
dir
!=
nil
{
return
*
c
.
dir
,
nil
}
dirURL
:=
c
.
DirectoryURL
if
dirURL
==
""
{
dirURL
=
LetsEncryptURL
}
res
,
err
:=
c
.
get
(
ctx
,
dirURL
,
wantStatus
(
http
.
StatusOK
))
if
err
!=
nil
{
return
Directory
{},
err
}
defer
res
.
Body
.
Close
()
c
.
addNonce
(
res
.
Header
)
var
v
struct
{
Reg
string
`json:"new-reg"`
Authz
string
`json:"new-authz"`
Cert
string
`json:"new-cert"`
Revoke
string
`json:"revoke-cert"`
Meta
struct
{
Terms
string
`json:"terms-of-service"`
Website
string
`json:"website"`
CAA
[]
string
`json:"caa-identities"`
}
}
if
err
:=
json
.
NewDecoder
(
res
.
Body
)
.
Decode
(
&
v
);
err
!=
nil
{
return
Directory
{},
err
}
c
.
dir
=
&
Directory
{
RegURL
:
v
.
Reg
,
AuthzURL
:
v
.
Authz
,
CertURL
:
v
.
Cert
,
RevokeURL
:
v
.
Revoke
,
Terms
:
v
.
Meta
.
Terms
,
Website
:
v
.
Meta
.
Website
,
CAA
:
v
.
Meta
.
CAA
,
}
return
*
c
.
dir
,
nil
}
// CreateCert requests a new certificate using the Certificate Signing Request csr encoded in DER format.
// The exp argument indicates the desired certificate validity duration. CA may issue a certificate
// with a different duration.
// If the bundle argument is true, the returned value will also contain the CA (issuer) certificate chain.
//
// In the case where CA server does not provide the issued certificate in the response,
// CreateCert will poll certURL using c.FetchCert, which will result in additional round-trips.
// In such a scenario, the caller can cancel the polling with ctx.
//
// CreateCert returns an error if the CA's response or chain was unreasonably large.
// Callers are encouraged to parse the returned value to ensure the certificate is valid and has the expected features.
func
(
c
*
Client
)
CreateCert
(
ctx
context
.
Context
,
csr
[]
byte
,
exp
time
.
Duration
,
bundle
bool
)
(
der
[][]
byte
,
certURL
string
,
err
error
)
{
if
_
,
err
:=
c
.
Discover
(
ctx
);
err
!=
nil
{
return
nil
,
""
,
err
}
req
:=
struct
{
Resource
string
`json:"resource"`
CSR
string
`json:"csr"`
NotBefore
string
`json:"notBefore,omitempty"`
NotAfter
string
`json:"notAfter,omitempty"`
}{
Resource
:
"new-cert"
,
CSR
:
base64
.
RawURLEncoding
.
EncodeToString
(
csr
),
}
now
:=
timeNow
()
req
.
NotBefore
=
now
.
Format
(
time
.
RFC3339
)
if
exp
>
0
{
req
.
NotAfter
=
now
.
Add
(
exp
)
.
Format
(
time
.
RFC3339
)
}
res
,
err
:=
c
.
post
(
ctx
,
c
.
Key
,
c
.
dir
.
CertURL
,
req
,
wantStatus
(
http
.
StatusCreated
))
if
err
!=
nil
{
return
nil
,
""
,
err
}
defer
res
.
Body
.
Close
()
curl
:=
res
.
Header
.
Get
(
"Location"
)
// cert permanent URL
if
res
.
ContentLength
==
0
{
// no cert in the body; poll until we get it
cert
,
err
:=
c
.
FetchCert
(
ctx
,
curl
,
bundle
)
return
cert
,
curl
,
err
}
// slurp issued cert and CA chain, if requested
cert
,
err
:=
c
.
responseCert
(
ctx
,
res
,
bundle
)
return
cert
,
curl
,
err
}
// FetchCert retrieves already issued certificate from the given url, in DER format.
// It retries the request until the certificate is successfully retrieved,
// context is cancelled by the caller or an error response is received.
//
// The returned value will also contain the CA (issuer) certificate if the bundle argument is true.
//
// FetchCert returns an error if the CA's response or chain was unreasonably large.
// Callers are encouraged to parse the returned value to ensure the certificate is valid
// and has expected features.
func
(
c
*
Client
)
FetchCert
(
ctx
context
.
Context
,
url
string
,
bundle
bool
)
([][]
byte
,
error
)
{
res
,
err
:=
c
.
get
(
ctx
,
url
,
wantStatus
(
http
.
StatusOK
))
if
err
!=
nil
{
return
nil
,
err
}
return
c
.
responseCert
(
ctx
,
res
,
bundle
)
}
// RevokeCert revokes a previously issued certificate cert, provided in DER format.
//
// The key argument, used to sign the request, must be authorized
// to revoke the certificate. It's up to the CA to decide which keys are authorized.
// For instance, the key pair of the certificate may be authorized.
// If the key is nil, c.Key is used instead.
func
(
c
*
Client
)
RevokeCert
(
ctx
context
.
Context
,
key
crypto
.
Signer
,
cert
[]
byte
,
reason
CRLReasonCode
)
error
{
if
_
,
err
:=
c
.
Discover
(
ctx
);
err
!=
nil
{
return
err
}
body
:=
&
struct
{
Resource
string
`json:"resource"`
Cert
string
`json:"certificate"`
Reason
int
`json:"reason"`
}{
Resource
:
"revoke-cert"
,
Cert
:
base64
.
RawURLEncoding
.
EncodeToString
(
cert
),
Reason
:
int
(
reason
),
}
if
key
==
nil
{
key
=
c
.
Key
}
res
,
err
:=
c
.
post
(
ctx
,
key
,
c
.
dir
.
RevokeURL
,
body
,
wantStatus
(
http
.
StatusOK
))
if
err
!=
nil
{
return
err
}
defer
res
.
Body
.
Close
()
return
nil
}
// AcceptTOS always returns true to indicate the acceptance of a CA's Terms of Service
// during account registration. See Register method of Client for more details.
func
AcceptTOS
(
tosURL
string
)
bool
{
return
true
}
// Register creates a new account registration by following the "new-reg" flow.
// It returns the registered account. The account is not modified.
//
// The registration may require the caller to agree to the CA's Terms of Service (TOS).
// If so, and the account has not indicated the acceptance of the terms (see Account for details),
// Register calls prompt with a TOS URL provided by the CA. Prompt should report
// whether the caller agrees to the terms. To always accept the terms, the caller can use AcceptTOS.
func
(
c
*
Client
)
Register
(
ctx
context
.
Context
,
a
*
Account
,
prompt
func
(
tosURL
string
)
bool
)
(
*
Account
,
error
)
{
if
_
,
err
:=
c
.
Discover
(
ctx
);
err
!=
nil
{
return
nil
,
err
}
var
err
error
if
a
,
err
=
c
.
doReg
(
ctx
,
c
.
dir
.
RegURL
,
"new-reg"
,
a
);
err
!=
nil
{
return
nil
,
err
}
var
accept
bool
if
a
.
CurrentTerms
!=
""
&&
a
.
CurrentTerms
!=
a
.
AgreedTerms
{
accept
=
prompt
(
a
.
CurrentTerms
)
}
if
accept
{
a
.
AgreedTerms
=
a
.
CurrentTerms
a
,
err
=
c
.
UpdateReg
(
ctx
,
a
)
}
return
a
,
err
}
// GetReg retrieves an existing registration.
// The url argument is an Account URI.
func
(
c
*
Client
)
GetReg
(
ctx
context
.
Context
,
url
string
)
(
*
Account
,
error
)
{
a
,
err
:=
c
.
doReg
(
ctx
,
url
,
"reg"
,
nil
)
if
err
!=
nil
{
return
nil
,
err
}
a
.
URI
=
url
return
a
,
nil
}
// UpdateReg updates an existing registration.
// It returns an updated account copy. The provided account is not modified.
func
(
c
*
Client
)
UpdateReg
(
ctx
context
.
Context
,
a
*
Account
)
(
*
Account
,
error
)
{
uri
:=
a
.
URI
a
,
err
:=
c
.
doReg
(
ctx
,
uri
,
"reg"
,
a
)
if
err
!=
nil
{
return
nil
,
err
}
a
.
URI
=
uri
return
a
,
nil
}
// Authorize performs the initial step in an authorization flow.
// The caller will then need to choose from and perform a set of returned
// challenges using c.Accept in order to successfully complete authorization.
//
// If an authorization has been previously granted, the CA may return
// a valid authorization (Authorization.Status is StatusValid). If so, the caller
// need not fulfill any challenge and can proceed to requesting a certificate.
func
(
c
*
Client
)
Authorize
(
ctx
context
.
Context
,
domain
string
)
(
*
Authorization
,
error
)
{
if
_
,
err
:=
c
.
Discover
(
ctx
);
err
!=
nil
{
return
nil
,
err
}
type
authzID
struct
{
Type
string
`json:"type"`
Value
string
`json:"value"`
}
req
:=
struct
{
Resource
string
`json:"resource"`
Identifier
authzID
`json:"identifier"`
}{
Resource
:
"new-authz"
,
Identifier
:
authzID
{
Type
:
"dns"
,
Value
:
domain
},
}
res
,
err
:=
c
.
post
(
ctx
,
c
.
Key
,
c
.
dir
.
AuthzURL
,
req
,
wantStatus
(
http
.
StatusCreated
))
if
err
!=
nil
{
return
nil
,
err
}
defer
res
.
Body
.
Close
()
var
v
wireAuthz
if
err
:=
json
.
NewDecoder
(
res
.
Body
)
.
Decode
(
&
v
);
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"acme: invalid response: %v"
,
err
)
}
if
v
.
Status
!=
StatusPending
&&
v
.
Status
!=
StatusValid
{
return
nil
,
fmt
.
Errorf
(
"acme: unexpected status: %s"
,
v
.
Status
)
}
return
v
.
authorization
(
res
.
Header
.
Get
(
"Location"
)),
nil
}
// GetAuthorization retrieves an authorization identified by the given URL.
//
// If a caller needs to poll an authorization until its status is final,
// see the WaitAuthorization method.
func
(
c
*
Client
)
GetAuthorization
(
ctx
context
.
Context
,
url
string
)
(
*
Authorization
,
error
)
{
res
,
err
:=
c
.
get
(
ctx
,
url
,
wantStatus
(
http
.
StatusOK
,
http
.
StatusAccepted
))
if
err
!=
nil
{
return
nil
,
err
}
defer
res
.
Body
.
Close
()
var
v
wireAuthz
if
err
:=
json
.
NewDecoder
(
res
.
Body
)
.
Decode
(
&
v
);
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"acme: invalid response: %v"
,
err
)
}
return
v
.
authorization
(
url
),
nil
}
// RevokeAuthorization relinquishes an existing authorization identified
// by the given URL.
// The url argument is an Authorization.URI value.
//
// If successful, the caller will be required to obtain a new authorization
// using the Authorize method before being able to request a new certificate
// for the domain associated with the authorization.
//
// It does not revoke existing certificates.
func
(
c
*
Client
)
RevokeAuthorization
(
ctx
context
.
Context
,
url
string
)
error
{
req
:=
struct
{
Resource
string
`json:"resource"`
Status
string
`json:"status"`
Delete
bool
`json:"delete"`
}{
Resource
:
"authz"
,
Status
:
"deactivated"
,
Delete
:
true
,
}
res
,
err
:=
c
.
post
(
ctx
,
c
.
Key
,
url
,
req
,
wantStatus
(
http
.
StatusOK
))
if
err
!=
nil
{
return
err
}
defer
res
.
Body
.
Close
()
return
nil
}
// WaitAuthorization polls an authorization at the given URL
// until it is in one of the final states, StatusValid or StatusInvalid,
// the ACME CA responded with a 4xx error code, or the context is done.
//
// It returns a non-nil Authorization only if its Status is StatusValid.
// In all other cases WaitAuthorization returns an error.
// If the Status is StatusInvalid, the returned error is of type *AuthorizationError.
func
(
c
*
Client
)
WaitAuthorization
(
ctx
context
.
Context
,
url
string
)
(
*
Authorization
,
error
)
{
for
{
res
,
err
:=
c
.
get
(
ctx
,
url
,
wantStatus
(
http
.
StatusOK
,
http
.
StatusAccepted
))
if
err
!=
nil
{
return
nil
,
err
}
var
raw
wireAuthz
err
=
json
.
NewDecoder
(
res
.
Body
)
.
Decode
(
&
raw
)
res
.
Body
.
Close
()
switch
{
case
err
!=
nil
:
// Skip and retry.
case
raw
.
Status
==
StatusValid
:
return
raw
.
authorization
(
url
),
nil
case
raw
.
Status
==
StatusInvalid
:
return
nil
,
raw
.
error
(
url
)
}
// Exponential backoff is implemented in c.get above.
// This is just to prevent continuously hitting the CA
// while waiting for a final authorization status.
d
:=
retryAfter
(
res
.
Header
.
Get
(
"Retry-After"
))
if
d
==
0
{
// Given that the fastest challenges TLS-SNI and HTTP-01
// require a CA to make at least 1 network round trip
// and most likely persist a challenge state,
// this default delay seems reasonable.
d
=
time
.
Second
}
t
:=
time
.
NewTimer
(
d
)
select
{
case
<-
ctx
.
Done
()
:
t
.
Stop
()
return
nil
,
ctx
.
Err
()
case
<-
t
.
C
:
// Retry.
}
}
}
// GetChallenge retrieves the current status of an challenge.
//
// A client typically polls a challenge status using this method.
func
(
c
*
Client
)
GetChallenge
(
ctx
context
.
Context
,
url
string
)
(
*
Challenge
,
error
)
{
res
,
err
:=
c
.
get
(
ctx
,
url
,
wantStatus
(
http
.
StatusOK
,
http
.
StatusAccepted
))
if
err
!=
nil
{
return
nil
,
err
}
defer
res
.
Body
.
Close
()
v
:=
wireChallenge
{
URI
:
url
}
if
err
:=
json
.
NewDecoder
(
res
.
Body
)
.
Decode
(
&
v
);
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"acme: invalid response: %v"
,
err
)
}
return
v
.
challenge
(),
nil
}
// Accept informs the server that the client accepts one of its challenges
// previously obtained with c.Authorize.
//
// The server will then perform the validation asynchronously.
func
(
c
*
Client
)
Accept
(
ctx
context
.
Context
,
chal
*
Challenge
)
(
*
Challenge
,
error
)
{
auth
,
err
:=
keyAuth
(
c
.
Key
.
Public
(),
chal
.
Token
)
if
err
!=
nil
{
return
nil
,
err
}
req
:=
struct
{
Resource
string
`json:"resource"`
Type
string
`json:"type"`
Auth
string
`json:"keyAuthorization"`
}{
Resource
:
"challenge"
,
Type
:
chal
.
Type
,
Auth
:
auth
,
}
res
,
err
:=
c
.
post
(
ctx
,
c
.
Key
,
chal
.
URI
,
req
,
wantStatus
(
http
.
StatusOK
,
// according to the spec
http
.
StatusAccepted
,
// Let's Encrypt: see https://goo.gl/WsJ7VT (acme-divergences.md)
))
if
err
!=
nil
{
return
nil
,
err
}
defer
res
.
Body
.
Close
()
var
v
wireChallenge
if
err
:=
json
.
NewDecoder
(
res
.
Body
)
.
Decode
(
&
v
);
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"acme: invalid response: %v"
,
err
)
}
return
v
.
challenge
(),
nil
}
// DNS01ChallengeRecord returns a DNS record value for a dns-01 challenge response.
// A TXT record containing the returned value must be provisioned under
// "_acme-challenge" name of the domain being validated.
//
// The token argument is a Challenge.Token value.
func
(
c
*
Client
)
DNS01ChallengeRecord
(
token
string
)
(
string
,
error
)
{
ka
,
err
:=
keyAuth
(
c
.
Key
.
Public
(),
token
)
if
err
!=
nil
{
return
""
,
err
}
b
:=
sha256
.
Sum256
([]
byte
(
ka
))
return
base64
.
RawURLEncoding
.
EncodeToString
(
b
[
:
]),
nil
}
// HTTP01ChallengeResponse returns the response for an http-01 challenge.
// Servers should respond with the value to HTTP requests at the URL path
// provided by HTTP01ChallengePath to validate the challenge and prove control
// over a domain name.
//
// The token argument is a Challenge.Token value.
func
(
c
*
Client
)
HTTP01ChallengeResponse
(
token
string
)
(
string
,
error
)
{
return
keyAuth
(
c
.
Key
.
Public
(),
token
)
}
// HTTP01ChallengePath returns the URL path at which the response for an http-01 challenge
// should be provided by the servers.
// The response value can be obtained with HTTP01ChallengeResponse.
//
// The token argument is a Challenge.Token value.
func
(
c
*
Client
)
HTTP01ChallengePath
(
token
string
)
string
{
return
"/.well-known/acme-challenge/"
+
token
}
// TLSSNI01ChallengeCert creates a certificate for TLS-SNI-01 challenge response.
// Servers can present the certificate to validate the challenge and prove control
// over a domain name.
//
// The implementation is incomplete in that the returned value is a single certificate,
// computed only for Z0 of the key authorization. ACME CAs are expected to update
// their implementations to use the newer version, TLS-SNI-02.
// For more details on TLS-SNI-01 see https://tools.ietf.org/html/draft-ietf-acme-acme-01#section-7.3.
//
// The token argument is a Challenge.Token value.
// If a WithKey option is provided, its private part signs the returned cert,
// and the public part is used to specify the signee.
// If no WithKey option is provided, a new ECDSA key is generated using P-256 curve.
//
// The returned certificate is valid for the next 24 hours and must be presented only when
// the server name of the TLS ClientHello matches exactly the returned name value.
func
(
c
*
Client
)
TLSSNI01ChallengeCert
(
token
string
,
opt
...
CertOption
)
(
cert
tls
.
Certificate
,
name
string
,
err
error
)
{
ka
,
err
:=
keyAuth
(
c
.
Key
.
Public
(),
token
)
if
err
!=
nil
{
return
tls
.
Certificate
{},
""
,
err
}
b
:=
sha256
.
Sum256
([]
byte
(
ka
))
h
:=
hex
.
EncodeToString
(
b
[
:
])
name
=
fmt
.
Sprintf
(
"%s.%s.acme.invalid"
,
h
[
:
32
],
h
[
32
:
])
cert
,
err
=
tlsChallengeCert
([]
string
{
name
},
opt
)
if
err
!=
nil
{
return
tls
.
Certificate
{},
""
,
err
}
return
cert
,
name
,
nil
}
// TLSSNI02ChallengeCert creates a certificate for TLS-SNI-02 challenge response.
// Servers can present the certificate to validate the challenge and prove control
// over a domain name. For more details on TLS-SNI-02 see
// https://tools.ietf.org/html/draft-ietf-acme-acme-03#section-7.3.
//
// The token argument is a Challenge.Token value.
// If a WithKey option is provided, its private part signs the returned cert,
// and the public part is used to specify the signee.
// If no WithKey option is provided, a new ECDSA key is generated using P-256 curve.
//
// The returned certificate is valid for the next 24 hours and must be presented only when
// the server name in the TLS ClientHello matches exactly the returned name value.
func
(
c
*
Client
)
TLSSNI02ChallengeCert
(
token
string
,
opt
...
CertOption
)
(
cert
tls
.
Certificate
,
name
string
,
err
error
)
{
b
:=
sha256
.
Sum256
([]
byte
(
token
))
h
:=
hex
.
EncodeToString
(
b
[
:
])
sanA
:=
fmt
.
Sprintf
(
"%s.%s.token.acme.invalid"
,
h
[
:
32
],
h
[
32
:
])
ka
,
err
:=
keyAuth
(
c
.
Key
.
Public
(),
token
)
if
err
!=
nil
{
return
tls
.
Certificate
{},
""
,
err
}
b
=
sha256
.
Sum256
([]
byte
(
ka
))
h
=
hex
.
EncodeToString
(
b
[
:
])
sanB
:=
fmt
.
Sprintf
(
"%s.%s.ka.acme.invalid"
,
h
[
:
32
],
h
[
32
:
])
cert
,
err
=
tlsChallengeCert
([]
string
{
sanA
,
sanB
},
opt
)
if
err
!=
nil
{
return
tls
.
Certificate
{},
""
,
err
}
return
cert
,
sanA
,
nil
}
// TLSALPN01ChallengeCert creates a certificate for TLS-ALPN-01 challenge response.
// Servers can present the certificate to validate the challenge and prove control
// over a domain name. For more details on TLS-ALPN-01 see
// https://tools.ietf.org/html/draft-shoemaker-acme-tls-alpn-00#section-3
//
// The token argument is a Challenge.Token value.
// If a WithKey option is provided, its private part signs the returned cert,
// and the public part is used to specify the signee.
// If no WithKey option is provided, a new ECDSA key is generated using P-256 curve.
//
// The returned certificate is valid for the next 24 hours and must be presented only when
// the server name in the TLS ClientHello matches the domain, and the special acme-tls/1 ALPN protocol
// has been specified.
func
(
c
*
Client
)
TLSALPN01ChallengeCert
(
token
,
domain
string
,
opt
...
CertOption
)
(
cert
tls
.
Certificate
,
err
error
)
{
ka
,
err
:=
keyAuth
(
c
.
Key
.
Public
(),
token
)
if
err
!=
nil
{
return
tls
.
Certificate
{},
err
}
shasum
:=
sha256
.
Sum256
([]
byte
(
ka
))
extValue
,
err
:=
asn1
.
Marshal
(
shasum
[
:
])
if
err
!=
nil
{
return
tls
.
Certificate
{},
err
}
acmeExtension
:=
pkix
.
Extension
{
Id
:
idPeACMEIdentifierV1
,
Critical
:
true
,
Value
:
extValue
,
}
tmpl
:=
defaultTLSChallengeCertTemplate
()
var
newOpt
[]
CertOption
for
_
,
o
:=
range
opt
{
switch
o
:=
o
.
(
type
)
{
case
*
certOptTemplate
:
t
:=
*
(
*
x509
.
Certificate
)(
o
)
// shallow copy is ok
tmpl
=
&
t
default
:
newOpt
=
append
(
newOpt
,
o
)
}
}
tmpl
.
ExtraExtensions
=
append
(
tmpl
.
ExtraExtensions
,
acmeExtension
)
newOpt
=
append
(
newOpt
,
WithTemplate
(
tmpl
))
return
tlsChallengeCert
([]
string
{
domain
},
newOpt
)
}
// doReg sends all types of registration requests.
// The type of request is identified by typ argument, which is a "resource"
// in the ACME spec terms.
//
// A non-nil acct argument indicates whether the intention is to mutate data
// of the Account. Only Contact and Agreement of its fields are used
// in such cases.
func
(
c
*
Client
)
doReg
(
ctx
context
.
Context
,
url
string
,
typ
string
,
acct
*
Account
)
(
*
Account
,
error
)
{
req
:=
struct
{
Resource
string
`json:"resource"`
Contact
[]
string
`json:"contact,omitempty"`
Agreement
string
`json:"agreement,omitempty"`
}{
Resource
:
typ
,
}
if
acct
!=
nil
{
req
.
Contact
=
acct
.
Contact
req
.
Agreement
=
acct
.
AgreedTerms
}
res
,
err
:=
c
.
post
(
ctx
,
c
.
Key
,
url
,
req
,
wantStatus
(
http
.
StatusOK
,
// updates and deletes
http
.
StatusCreated
,
// new account creation
))
if
err
!=
nil
{
return
nil
,
err
}
defer
res
.
Body
.
Close
()
var
v
struct
{
Contact
[]
string
Agreement
string
Authorizations
string
Certificates
string
}
if
err
:=
json
.
NewDecoder
(
res
.
Body
)
.
Decode
(
&
v
);
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"acme: invalid response: %v"
,
err
)
}
var
tos
string
if
v
:=
linkHeader
(
res
.
Header
,
"terms-of-service"
);
len
(
v
)
>
0
{
tos
=
v
[
0
]
}
var
authz
string
if
v
:=
linkHeader
(
res
.
Header
,
"next"
);
len
(
v
)
>
0
{
authz
=
v
[
0
]
}
return
&
Account
{
URI
:
res
.
Header
.
Get
(
"Location"
),
Contact
:
v
.
Contact
,
AgreedTerms
:
v
.
Agreement
,
CurrentTerms
:
tos
,
Authz
:
authz
,
Authorizations
:
v
.
Authorizations
,
Certificates
:
v
.
Certificates
,
},
nil
}
// popNonce returns a nonce value previously stored with c.addNonce
// or fetches a fresh one from the given URL.
func
(
c
*
Client
)
popNonce
(
ctx
context
.
Context
,
url
string
)
(
string
,
error
)
{
c
.
noncesMu
.
Lock
()
defer
c
.
noncesMu
.
Unlock
()
if
len
(
c
.
nonces
)
==
0
{
return
c
.
fetchNonce
(
ctx
,
url
)
}
var
nonce
string
for
nonce
=
range
c
.
nonces
{
delete
(
c
.
nonces
,
nonce
)
break
}
return
nonce
,
nil
}
// clearNonces clears any stored nonces
func
(
c
*
Client
)
clearNonces
()
{
c
.
noncesMu
.
Lock
()
defer
c
.
noncesMu
.
Unlock
()
c
.
nonces
=
make
(
map
[
string
]
struct
{})
}
// addNonce stores a nonce value found in h (if any) for future use.
func
(
c
*
Client
)
addNonce
(
h
http
.
Header
)
{
v
:=
nonceFromHeader
(
h
)
if
v
==
""
{
return
}
c
.
noncesMu
.
Lock
()
defer
c
.
noncesMu
.
Unlock
()
if
len
(
c
.
nonces
)
>=
maxNonces
{
return
}
if
c
.
nonces
==
nil
{
c
.
nonces
=
make
(
map
[
string
]
struct
{})
}
c
.
nonces
[
v
]
=
struct
{}{}
}
func
(
c
*
Client
)
fetchNonce
(
ctx
context
.
Context
,
url
string
)
(
string
,
error
)
{
r
,
err
:=
http
.
NewRequest
(
"HEAD"
,
url
,
nil
)
if
err
!=
nil
{
return
""
,
err
}
resp
,
err
:=
c
.
doNoRetry
(
ctx
,
r
)
if
err
!=
nil
{
return
""
,
err
}
defer
resp
.
Body
.
Close
()
nonce
:=
nonceFromHeader
(
resp
.
Header
)
if
nonce
==
""
{
if
resp
.
StatusCode
>
299
{
return
""
,
responseError
(
resp
)
}
return
""
,
errors
.
New
(
"acme: nonce not found"
)
}
return
nonce
,
nil
}
func
nonceFromHeader
(
h
http
.
Header
)
string
{
return
h
.
Get
(
"Replay-Nonce"
)
}
func
(
c
*
Client
)
responseCert
(
ctx
context
.
Context
,
res
*
http
.
Response
,
bundle
bool
)
([][]
byte
,
error
)
{
b
,
err
:=
ioutil
.
ReadAll
(
io
.
LimitReader
(
res
.
Body
,
maxCertSize
+
1
))
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"acme: response stream: %v"
,
err
)
}
if
len
(
b
)
>
maxCertSize
{
return
nil
,
errors
.
New
(
"acme: certificate is too big"
)
}
cert
:=
[][]
byte
{
b
}
if
!
bundle
{
return
cert
,
nil
}
// Append CA chain cert(s).
// At least one is required according to the spec:
// https://tools.ietf.org/html/draft-ietf-acme-acme-03#section-6.3.1
up
:=
linkHeader
(
res
.
Header
,
"up"
)
if
len
(
up
)
==
0
{
return
nil
,
errors
.
New
(
"acme: rel=up link not found"
)
}
if
len
(
up
)
>
maxChainLen
{
return
nil
,
errors
.
New
(
"acme: rel=up link is too large"
)
}
for
_
,
url
:=
range
up
{
cc
,
err
:=
c
.
chainCert
(
ctx
,
url
,
0
)
if
err
!=
nil
{
return
nil
,
err
}
cert
=
append
(
cert
,
cc
...
)
}
return
cert
,
nil
}
// chainCert fetches CA certificate chain recursively by following "up" links.
// Each recursive call increments the depth by 1, resulting in an error
// if the recursion level reaches maxChainLen.
//
// First chainCert call starts with depth of 0.
func
(
c
*
Client
)
chainCert
(
ctx
context
.
Context
,
url
string
,
depth
int
)
([][]
byte
,
error
)
{
if
depth
>=
maxChainLen
{
return
nil
,
errors
.
New
(
"acme: certificate chain is too deep"
)
}
res
,
err
:=
c
.
get
(
ctx
,
url
,
wantStatus
(
http
.
StatusOK
))
if
err
!=
nil
{
return
nil
,
err
}
defer
res
.
Body
.
Close
()
b
,
err
:=
ioutil
.
ReadAll
(
io
.
LimitReader
(
res
.
Body
,
maxCertSize
+
1
))
if
err
!=
nil
{
return
nil
,
err
}
if
len
(
b
)
>
maxCertSize
{
return
nil
,
errors
.
New
(
"acme: certificate is too big"
)
}
chain
:=
[][]
byte
{
b
}
uplink
:=
linkHeader
(
res
.
Header
,
"up"
)
if
len
(
uplink
)
>
maxChainLen
{
return
nil
,
errors
.
New
(
"acme: certificate chain is too large"
)
}
for
_
,
up
:=
range
uplink
{
cc
,
err
:=
c
.
chainCert
(
ctx
,
up
,
depth
+
1
)
if
err
!=
nil
{
return
nil
,
err
}
chain
=
append
(
chain
,
cc
...
)
}
return
chain
,
nil
}
// linkHeader returns URI-Reference values of all Link headers
// with relation-type rel.
// See https://tools.ietf.org/html/rfc5988#section-5 for details.
func
linkHeader
(
h
http
.
Header
,
rel
string
)
[]
string
{
var
links
[]
string
for
_
,
v
:=
range
h
[
"Link"
]
{
parts
:=
strings
.
Split
(
v
,
";"
)
for
_
,
p
:=
range
parts
{
p
=
strings
.
TrimSpace
(
p
)
if
!
strings
.
HasPrefix
(
p
,
"rel="
)
{
continue
}
if
v
:=
strings
.
Trim
(
p
[
4
:
],
`"`
);
v
==
rel
{
links
=
append
(
links
,
strings
.
Trim
(
parts
[
0
],
"<>"
))
}
}
}
return
links
}
// keyAuth generates a key authorization string for a given token.
func
keyAuth
(
pub
crypto
.
PublicKey
,
token
string
)
(
string
,
error
)
{
th
,
err
:=
JWKThumbprint
(
pub
)
if
err
!=
nil
{
return
""
,
err
}
return
fmt
.
Sprintf
(
"%s.%s"
,
token
,
th
),
nil
}
// defaultTLSChallengeCertTemplate is a template used to create challenge certs for TLS challenges.
func
defaultTLSChallengeCertTemplate
()
*
x509
.
Certificate
{
return
&
x509
.
Certificate
{
SerialNumber
:
big
.
NewInt
(
1
),
NotBefore
:
time
.
Now
(),
NotAfter
:
time
.
Now
()
.
Add
(
24
*
time
.
Hour
),
BasicConstraintsValid
:
true
,
KeyUsage
:
x509
.
KeyUsageKeyEncipherment
|
x509
.
KeyUsageDigitalSignature
,
ExtKeyUsage
:
[]
x509
.
ExtKeyUsage
{
x509
.
ExtKeyUsageServerAuth
},
}
}
// tlsChallengeCert creates a temporary certificate for TLS-SNI challenges
// with the given SANs and auto-generated public/private key pair.
// The Subject Common Name is set to the first SAN to aid debugging.
// To create a cert with a custom key pair, specify WithKey option.
func
tlsChallengeCert
(
san
[]
string
,
opt
[]
CertOption
)
(
tls
.
Certificate
,
error
)
{
var
key
crypto
.
Signer
tmpl
:=
defaultTLSChallengeCertTemplate
()
for
_
,
o
:=
range
opt
{
switch
o
:=
o
.
(
type
)
{
case
*
certOptKey
:
if
key
!=
nil
{
return
tls
.
Certificate
{},
errors
.
New
(
"acme: duplicate key option"
)
}
key
=
o
.
key
case
*
certOptTemplate
:
t
:=
*
(
*
x509
.
Certificate
)(
o
)
// shallow copy is ok
tmpl
=
&
t
default
:
// package's fault, if we let this happen:
panic
(
fmt
.
Sprintf
(
"unsupported option type %T"
,
o
))
}
}
if
key
==
nil
{
var
err
error
if
key
,
err
=
ecdsa
.
GenerateKey
(
elliptic
.
P256
(),
rand
.
Reader
);
err
!=
nil
{
return
tls
.
Certificate
{},
err
}
}
tmpl
.
DNSNames
=
san
if
len
(
san
)
>
0
{
tmpl
.
Subject
.
CommonName
=
san
[
0
]
}
der
,
err
:=
x509
.
CreateCertificate
(
rand
.
Reader
,
tmpl
,
tmpl
,
key
.
Public
(),
key
)
if
err
!=
nil
{
return
tls
.
Certificate
{},
err
}
return
tls
.
Certificate
{
Certificate
:
[][]
byte
{
der
},
PrivateKey
:
key
,
},
nil
}
// encodePEM returns b encoded as PEM with block of type typ.
func
encodePEM
(
typ
string
,
b
[]
byte
)
[]
byte
{
pb
:=
&
pem
.
Block
{
Type
:
typ
,
Bytes
:
b
}
return
pem
.
EncodeToMemory
(
pb
)
}
// timeNow is useful for testing for fixed current time.
var
timeNow
=
time
.
Now
vendor/golang.org/x/crypto/acme/autocert/autocert.go
deleted
100644 → 0
View file @
2d323373
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package autocert provides automatic access to certificates from Let's Encrypt
// and any other ACME-based CA.
//
// This package is a work in progress and makes no API stability promises.
package
autocert
import
(
"bytes"
"context"
"crypto"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/rsa"
"crypto/tls"
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
"errors"
"fmt"
"io"
mathrand
"math/rand"
"net"
"net/http"
"path"
"strings"
"sync"
"time"
"golang.org/x/crypto/acme"
)
// createCertRetryAfter is how much time to wait before removing a failed state
// entry due to an unsuccessful createCert call.
// This is a variable instead of a const for testing.
// TODO: Consider making it configurable or an exp backoff?
var
createCertRetryAfter
=
time
.
Minute
// pseudoRand is safe for concurrent use.
var
pseudoRand
*
lockedMathRand
func
init
()
{
src
:=
mathrand
.
NewSource
(
timeNow
()
.
UnixNano
())
pseudoRand
=
&
lockedMathRand
{
rnd
:
mathrand
.
New
(
src
)}
}
// AcceptTOS is a Manager.Prompt function that always returns true to
// indicate acceptance of the CA's Terms of Service during account
// registration.
func
AcceptTOS
(
tosURL
string
)
bool
{
return
true
}
// HostPolicy specifies which host names the Manager is allowed to respond to.
// It returns a non-nil error if the host should be rejected.
// The returned error is accessible via tls.Conn.Handshake and its callers.
// See Manager's HostPolicy field and GetCertificate method docs for more details.
type
HostPolicy
func
(
ctx
context
.
Context
,
host
string
)
error
// HostWhitelist returns a policy where only the specified host names are allowed.
// Only exact matches are currently supported. Subdomains, regexp or wildcard
// will not match.
func
HostWhitelist
(
hosts
...
string
)
HostPolicy
{
whitelist
:=
make
(
map
[
string
]
bool
,
len
(
hosts
))
for
_
,
h
:=
range
hosts
{
whitelist
[
h
]
=
true
}
return
func
(
_
context
.
Context
,
host
string
)
error
{
if
!
whitelist
[
host
]
{
return
errors
.
New
(
"acme/autocert: host not configured"
)
}
return
nil
}
}
// defaultHostPolicy is used when Manager.HostPolicy is not set.
func
defaultHostPolicy
(
context
.
Context
,
string
)
error
{
return
nil
}
// Manager is a stateful certificate manager built on top of acme.Client.
// It obtains and refreshes certificates automatically using "tls-sni-01",
// "tls-sni-02" and "http-01" challenge types, as well as providing them
// to a TLS server via tls.Config.
//
// You must specify a cache implementation, such as DirCache,
// to reuse obtained certificates across program restarts.
// Otherwise your server is very likely to exceed the certificate
// issuer's request rate limits.
type
Manager
struct
{
// Prompt specifies a callback function to conditionally accept a CA's Terms of Service (TOS).
// The registration may require the caller to agree to the CA's TOS.
// If so, Manager calls Prompt with a TOS URL provided by the CA. Prompt should report
// whether the caller agrees to the terms.
//
// To always accept the terms, the callers can use AcceptTOS.
Prompt
func
(
tosURL
string
)
bool
// Cache optionally stores and retrieves previously-obtained certificates
// and other state. If nil, certs will only be cached for the lifetime of
// the Manager. Multiple Managers can share the same Cache.
//
// Using a persistent Cache, such as DirCache, is strongly recommended.
Cache
Cache
// HostPolicy controls which domains the Manager will attempt
// to retrieve new certificates for. It does not affect cached certs.
//
// If non-nil, HostPolicy is called before requesting a new cert.
// If nil, all hosts are currently allowed. This is not recommended,
// as it opens a potential attack where clients connect to a server
// by IP address and pretend to be asking for an incorrect host name.
// Manager will attempt to obtain a certificate for that host, incorrectly,
// eventually reaching the CA's rate limit for certificate requests
// and making it impossible to obtain actual certificates.
//
// See GetCertificate for more details.
HostPolicy
HostPolicy
// RenewBefore optionally specifies how early certificates should
// be renewed before they expire.
//
// If zero, they're renewed 30 days before expiration.
RenewBefore
time
.
Duration
// Client is used to perform low-level operations, such as account registration
// and requesting new certificates.
//
// If Client is nil, a zero-value acme.Client is used with acme.LetsEncryptURL
// as directory endpoint. If the Client.Key is nil, a new ECDSA P-256 key is
// generated and, if Cache is not nil, stored in cache.
//
// Mutating the field after the first call of GetCertificate method will have no effect.
Client
*
acme
.
Client
// Email optionally specifies a contact email address.
// This is used by CAs, such as Let's Encrypt, to notify about problems
// with issued certificates.
//
// If the Client's account key is already registered, Email is not used.
Email
string
// ForceRSA used to make the Manager generate RSA certificates. It is now ignored.
//
// Deprecated: the Manager will request the correct type of certificate based
// on what each client supports.
ForceRSA
bool
// ExtraExtensions are used when generating a new CSR (Certificate Request),
// thus allowing customization of the resulting certificate.
// For instance, TLS Feature Extension (RFC 7633) can be used
// to prevent an OCSP downgrade attack.
//
// The field value is passed to crypto/x509.CreateCertificateRequest
// in the template's ExtraExtensions field as is.
ExtraExtensions
[]
pkix
.
Extension
clientMu
sync
.
Mutex
client
*
acme
.
Client
// initialized by acmeClient method
stateMu
sync
.
Mutex
state
map
[
certKey
]
*
certState
// renewal tracks the set of domains currently running renewal timers.
renewalMu
sync
.
Mutex
renewal
map
[
certKey
]
*
domainRenewal
// tokensMu guards the rest of the fields: tryHTTP01, certTokens and httpTokens.
tokensMu
sync
.
RWMutex
// tryHTTP01 indicates whether the Manager should try "http-01" challenge type
// during the authorization flow.
tryHTTP01
bool
// httpTokens contains response body values for http-01 challenges
// and is keyed by the URL path at which a challenge response is expected
// to be provisioned.
// The entries are stored for the duration of the authorization flow.
httpTokens
map
[
string
][]
byte
// certTokens contains temporary certificates for tls-sni challenges
// and is keyed by token domain name, which matches server name of ClientHello.
// Keys always have ".acme.invalid" suffix.
// The entries are stored for the duration of the authorization flow.
certTokens
map
[
string
]
*
tls
.
Certificate
}
// certKey is the key by which certificates are tracked in state, renewal and cache.
type
certKey
struct
{
domain
string
// without trailing dot
isRSA
bool
// RSA cert for legacy clients (as opposed to default ECDSA)
isToken
bool
// tls-sni challenge token cert; key type is undefined regardless of isRSA
}
func
(
c
certKey
)
String
()
string
{
if
c
.
isToken
{
return
c
.
domain
+
"+token"
}
if
c
.
isRSA
{
return
c
.
domain
+
"+rsa"
}
return
c
.
domain
}
// GetCertificate implements the tls.Config.GetCertificate hook.
// It provides a TLS certificate for hello.ServerName host, including answering
// *.acme.invalid (TLS-SNI) challenges. All other fields of hello are ignored.
//
// If m.HostPolicy is non-nil, GetCertificate calls the policy before requesting
// a new cert. A non-nil error returned from m.HostPolicy halts TLS negotiation.
// The error is propagated back to the caller of GetCertificate and is user-visible.
// This does not affect cached certs. See HostPolicy field description for more details.
func
(
m
*
Manager
)
GetCertificate
(
hello
*
tls
.
ClientHelloInfo
)
(
*
tls
.
Certificate
,
error
)
{
if
m
.
Prompt
==
nil
{
return
nil
,
errors
.
New
(
"acme/autocert: Manager.Prompt not set"
)
}
name
:=
hello
.
ServerName
if
name
==
""
{
return
nil
,
errors
.
New
(
"acme/autocert: missing server name"
)
}
if
!
strings
.
Contains
(
strings
.
Trim
(
name
,
"."
),
"."
)
{
return
nil
,
errors
.
New
(
"acme/autocert: server name component count invalid"
)
}
if
strings
.
ContainsAny
(
name
,
`+/\`
)
{
return
nil
,
errors
.
New
(
"acme/autocert: server name contains invalid character"
)
}
// In the worst-case scenario, the timeout needs to account for caching, host policy,
// domain ownership verification and certificate issuance.
ctx
,
cancel
:=
context
.
WithTimeout
(
context
.
Background
(),
5
*
time
.
Minute
)
defer
cancel
()
// check whether this is a token cert requested for TLS-SNI challenge
if
strings
.
HasSuffix
(
name
,
".acme.invalid"
)
{
m
.
tokensMu
.
RLock
()
defer
m
.
tokensMu
.
RUnlock
()
if
cert
:=
m
.
certTokens
[
name
];
cert
!=
nil
{
return
cert
,
nil
}
if
cert
,
err
:=
m
.
cacheGet
(
ctx
,
certKey
{
domain
:
name
,
isToken
:
true
});
err
==
nil
{
return
cert
,
nil
}
// TODO: cache error results?
return
nil
,
fmt
.
Errorf
(
"acme/autocert: no token cert for %q"
,
name
)
}
// regular domain
ck
:=
certKey
{
domain
:
strings
.
TrimSuffix
(
name
,
"."
),
// golang.org/issue/18114
isRSA
:
!
supportsECDSA
(
hello
),
}
cert
,
err
:=
m
.
cert
(
ctx
,
ck
)
if
err
==
nil
{
return
cert
,
nil
}
if
err
!=
ErrCacheMiss
{
return
nil
,
err
}
// first-time
if
err
:=
m
.
hostPolicy
()(
ctx
,
name
);
err
!=
nil
{
return
nil
,
err
}
cert
,
err
=
m
.
createCert
(
ctx
,
ck
)
if
err
!=
nil
{
return
nil
,
err
}
m
.
cachePut
(
ctx
,
ck
,
cert
)
return
cert
,
nil
}
func
supportsECDSA
(
hello
*
tls
.
ClientHelloInfo
)
bool
{
// The "signature_algorithms" extension, if present, limits the key exchange
// algorithms allowed by the cipher suites. See RFC 5246, section 7.4.1.4.1.
if
hello
.
SignatureSchemes
!=
nil
{
ecdsaOK
:=
false
schemeLoop
:
for
_
,
scheme
:=
range
hello
.
SignatureSchemes
{
const
tlsECDSAWithSHA1
tls
.
SignatureScheme
=
0x0203
// constant added in Go 1.10
switch
scheme
{
case
tlsECDSAWithSHA1
,
tls
.
ECDSAWithP256AndSHA256
,
tls
.
ECDSAWithP384AndSHA384
,
tls
.
ECDSAWithP521AndSHA512
:
ecdsaOK
=
true
break
schemeLoop
}
}
if
!
ecdsaOK
{
return
false
}
}
if
hello
.
SupportedCurves
!=
nil
{
ecdsaOK
:=
false
for
_
,
curve
:=
range
hello
.
SupportedCurves
{
if
curve
==
tls
.
CurveP256
{
ecdsaOK
=
true
break
}
}
if
!
ecdsaOK
{
return
false
}
}
for
_
,
suite
:=
range
hello
.
CipherSuites
{
switch
suite
{
case
tls
.
TLS_ECDHE_ECDSA_WITH_RC4_128_SHA
,
tls
.
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA
,
tls
.
TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA
,
tls
.
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256
,
tls
.
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
,
tls
.
TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
,
tls
.
TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305
:
return
true
}
}
return
false
}
// HTTPHandler configures the Manager to provision ACME "http-01" challenge responses.
// It returns an http.Handler that responds to the challenges and must be
// running on port 80. If it receives a request that is not an ACME challenge,
// it delegates the request to the optional fallback handler.
//
// If fallback is nil, the returned handler redirects all GET and HEAD requests
// to the default TLS port 443 with 302 Found status code, preserving the original
// request path and query. It responds with 400 Bad Request to all other HTTP methods.
// The fallback is not protected by the optional HostPolicy.
//
// Because the fallback handler is run with unencrypted port 80 requests,
// the fallback should not serve TLS-only requests.
//
// If HTTPHandler is never called, the Manager will only use TLS SNI
// challenges for domain verification.
func
(
m
*
Manager
)
HTTPHandler
(
fallback
http
.
Handler
)
http
.
Handler
{
m
.
tokensMu
.
Lock
()
defer
m
.
tokensMu
.
Unlock
()
m
.
tryHTTP01
=
true
if
fallback
==
nil
{
fallback
=
http
.
HandlerFunc
(
handleHTTPRedirect
)
}
return
http
.
HandlerFunc
(
func
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
{
if
!
strings
.
HasPrefix
(
r
.
URL
.
Path
,
"/.well-known/acme-challenge/"
)
{
fallback
.
ServeHTTP
(
w
,
r
)
return
}
// A reasonable context timeout for cache and host policy only,
// because we don't wait for a new certificate issuance here.
ctx
,
cancel
:=
context
.
WithTimeout
(
r
.
Context
(),
time
.
Minute
)
defer
cancel
()
if
err
:=
m
.
hostPolicy
()(
ctx
,
r
.
Host
);
err
!=
nil
{
http
.
Error
(
w
,
err
.
Error
(),
http
.
StatusForbidden
)
return
}
data
,
err
:=
m
.
httpToken
(
ctx
,
r
.
URL
.
Path
)
if
err
!=
nil
{
http
.
Error
(
w
,
err
.
Error
(),
http
.
StatusNotFound
)
return
}
w
.
Write
(
data
)
})
}
func
handleHTTPRedirect
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
{
if
r
.
Method
!=
"GET"
&&
r
.
Method
!=
"HEAD"
{
http
.
Error
(
w
,
"Use HTTPS"
,
http
.
StatusBadRequest
)
return
}
target
:=
"https://"
+
stripPort
(
r
.
Host
)
+
r
.
URL
.
RequestURI
()
http
.
Redirect
(
w
,
r
,
target
,
http
.
StatusFound
)
}
func
stripPort
(
hostport
string
)
string
{
host
,
_
,
err
:=
net
.
SplitHostPort
(
hostport
)
if
err
!=
nil
{
return
hostport
}
return
net
.
JoinHostPort
(
host
,
"443"
)
}
// cert returns an existing certificate either from m.state or cache.
// If a certificate is found in cache but not in m.state, the latter will be filled
// with the cached value.
func
(
m
*
Manager
)
cert
(
ctx
context
.
Context
,
ck
certKey
)
(
*
tls
.
Certificate
,
error
)
{
m
.
stateMu
.
Lock
()
if
s
,
ok
:=
m
.
state
[
ck
];
ok
{
m
.
stateMu
.
Unlock
()
s
.
RLock
()
defer
s
.
RUnlock
()
return
s
.
tlscert
()
}
defer
m
.
stateMu
.
Unlock
()
cert
,
err
:=
m
.
cacheGet
(
ctx
,
ck
)
if
err
!=
nil
{
return
nil
,
err
}
signer
,
ok
:=
cert
.
PrivateKey
.
(
crypto
.
Signer
)
if
!
ok
{
return
nil
,
errors
.
New
(
"acme/autocert: private key cannot sign"
)
}
if
m
.
state
==
nil
{
m
.
state
=
make
(
map
[
certKey
]
*
certState
)
}
s
:=
&
certState
{
key
:
signer
,
cert
:
cert
.
Certificate
,
leaf
:
cert
.
Leaf
,
}
m
.
state
[
ck
]
=
s
go
m
.
renew
(
ck
,
s
.
key
,
s
.
leaf
.
NotAfter
)
return
cert
,
nil
}
// cacheGet always returns a valid certificate, or an error otherwise.
// If a cached certificate exists but is not valid, ErrCacheMiss is returned.
func
(
m
*
Manager
)
cacheGet
(
ctx
context
.
Context
,
ck
certKey
)
(
*
tls
.
Certificate
,
error
)
{
if
m
.
Cache
==
nil
{
return
nil
,
ErrCacheMiss
}
data
,
err
:=
m
.
Cache
.
Get
(
ctx
,
ck
.
String
())
if
err
!=
nil
{
return
nil
,
err
}
// private
priv
,
pub
:=
pem
.
Decode
(
data
)
if
priv
==
nil
||
!
strings
.
Contains
(
priv
.
Type
,
"PRIVATE"
)
{
return
nil
,
ErrCacheMiss
}
privKey
,
err
:=
parsePrivateKey
(
priv
.
Bytes
)
if
err
!=
nil
{
return
nil
,
err
}
// public
var
pubDER
[][]
byte
for
len
(
pub
)
>
0
{
var
b
*
pem
.
Block
b
,
pub
=
pem
.
Decode
(
pub
)
if
b
==
nil
{
break
}
pubDER
=
append
(
pubDER
,
b
.
Bytes
)
}
if
len
(
pub
)
>
0
{
// Leftover content not consumed by pem.Decode. Corrupt. Ignore.
return
nil
,
ErrCacheMiss
}
// verify and create TLS cert
leaf
,
err
:=
validCert
(
ck
,
pubDER
,
privKey
)
if
err
!=
nil
{
return
nil
,
ErrCacheMiss
}
tlscert
:=
&
tls
.
Certificate
{
Certificate
:
pubDER
,
PrivateKey
:
privKey
,
Leaf
:
leaf
,
}
return
tlscert
,
nil
}
func
(
m
*
Manager
)
cachePut
(
ctx
context
.
Context
,
ck
certKey
,
tlscert
*
tls
.
Certificate
)
error
{
if
m
.
Cache
==
nil
{
return
nil
}
// contains PEM-encoded data
var
buf
bytes
.
Buffer
// private
switch
key
:=
tlscert
.
PrivateKey
.
(
type
)
{
case
*
ecdsa
.
PrivateKey
:
if
err
:=
encodeECDSAKey
(
&
buf
,
key
);
err
!=
nil
{
return
err
}
case
*
rsa
.
PrivateKey
:
b
:=
x509
.
MarshalPKCS1PrivateKey
(
key
)
pb
:=
&
pem
.
Block
{
Type
:
"RSA PRIVATE KEY"
,
Bytes
:
b
}
if
err
:=
pem
.
Encode
(
&
buf
,
pb
);
err
!=
nil
{
return
err
}
default
:
return
errors
.
New
(
"acme/autocert: unknown private key type"
)
}
// public
for
_
,
b
:=
range
tlscert
.
Certificate
{
pb
:=
&
pem
.
Block
{
Type
:
"CERTIFICATE"
,
Bytes
:
b
}
if
err
:=
pem
.
Encode
(
&
buf
,
pb
);
err
!=
nil
{
return
err
}
}
return
m
.
Cache
.
Put
(
ctx
,
ck
.
String
(),
buf
.
Bytes
())
}
func
encodeECDSAKey
(
w
io
.
Writer
,
key
*
ecdsa
.
PrivateKey
)
error
{
b
,
err
:=
x509
.
MarshalECPrivateKey
(
key
)
if
err
!=
nil
{
return
err
}
pb
:=
&
pem
.
Block
{
Type
:
"EC PRIVATE KEY"
,
Bytes
:
b
}
return
pem
.
Encode
(
w
,
pb
)
}
// createCert starts the domain ownership verification and returns a certificate
// for that domain upon success.
//
// If the domain is already being verified, it waits for the existing verification to complete.
// Either way, createCert blocks for the duration of the whole process.
func
(
m
*
Manager
)
createCert
(
ctx
context
.
Context
,
ck
certKey
)
(
*
tls
.
Certificate
,
error
)
{
// TODO: maybe rewrite this whole piece using sync.Once
state
,
err
:=
m
.
certState
(
ck
)
if
err
!=
nil
{
return
nil
,
err
}
// state may exist if another goroutine is already working on it
// in which case just wait for it to finish
if
!
state
.
locked
{
state
.
RLock
()
defer
state
.
RUnlock
()
return
state
.
tlscert
()
}
// We are the first; state is locked.
// Unblock the readers when domain ownership is verified
// and we got the cert or the process failed.
defer
state
.
Unlock
()
state
.
locked
=
false
der
,
leaf
,
err
:=
m
.
authorizedCert
(
ctx
,
state
.
key
,
ck
)
if
err
!=
nil
{
// Remove the failed state after some time,
// making the manager call createCert again on the following TLS hello.
time
.
AfterFunc
(
createCertRetryAfter
,
func
()
{
defer
testDidRemoveState
(
ck
)
m
.
stateMu
.
Lock
()
defer
m
.
stateMu
.
Unlock
()
// Verify the state hasn't changed and it's still invalid
// before deleting.
s
,
ok
:=
m
.
state
[
ck
]
if
!
ok
{
return
}
if
_
,
err
:=
validCert
(
ck
,
s
.
cert
,
s
.
key
);
err
==
nil
{
return
}
delete
(
m
.
state
,
ck
)
})
return
nil
,
err
}
state
.
cert
=
der
state
.
leaf
=
leaf
go
m
.
renew
(
ck
,
state
.
key
,
state
.
leaf
.
NotAfter
)
return
state
.
tlscert
()
}
// certState returns a new or existing certState.
// If a new certState is returned, state.exist is false and the state is locked.
// The returned error is non-nil only in the case where a new state could not be created.
func
(
m
*
Manager
)
certState
(
ck
certKey
)
(
*
certState
,
error
)
{
m
.
stateMu
.
Lock
()
defer
m
.
stateMu
.
Unlock
()
if
m
.
state
==
nil
{
m
.
state
=
make
(
map
[
certKey
]
*
certState
)
}
// existing state
if
state
,
ok
:=
m
.
state
[
ck
];
ok
{
return
state
,
nil
}
// new locked state
var
(
err
error
key
crypto
.
Signer
)
if
ck
.
isRSA
{
key
,
err
=
rsa
.
GenerateKey
(
rand
.
Reader
,
2048
)
}
else
{
key
,
err
=
ecdsa
.
GenerateKey
(
elliptic
.
P256
(),
rand
.
Reader
)
}
if
err
!=
nil
{
return
nil
,
err
}
state
:=
&
certState
{
key
:
key
,
locked
:
true
,
}
state
.
Lock
()
// will be unlocked by m.certState caller
m
.
state
[
ck
]
=
state
return
state
,
nil
}
// authorizedCert starts the domain ownership verification process and requests a new cert upon success.
// The key argument is the certificate private key.
func
(
m
*
Manager
)
authorizedCert
(
ctx
context
.
Context
,
key
crypto
.
Signer
,
ck
certKey
)
(
der
[][]
byte
,
leaf
*
x509
.
Certificate
,
err
error
)
{
client
,
err
:=
m
.
acmeClient
(
ctx
)
if
err
!=
nil
{
return
nil
,
nil
,
err
}
if
err
:=
m
.
verify
(
ctx
,
client
,
ck
.
domain
);
err
!=
nil
{
return
nil
,
nil
,
err
}
csr
,
err
:=
certRequest
(
key
,
ck
.
domain
,
m
.
ExtraExtensions
)
if
err
!=
nil
{
return
nil
,
nil
,
err
}
der
,
_
,
err
=
client
.
CreateCert
(
ctx
,
csr
,
0
,
true
)
if
err
!=
nil
{
return
nil
,
nil
,
err
}
leaf
,
err
=
validCert
(
ck
,
der
,
key
)
if
err
!=
nil
{
return
nil
,
nil
,
err
}
return
der
,
leaf
,
nil
}
// revokePendingAuthz revokes all authorizations idenfied by the elements of uri slice.
// It ignores revocation errors.
func
(
m
*
Manager
)
revokePendingAuthz
(
ctx
context
.
Context
,
uri
[]
string
)
{
client
,
err
:=
m
.
acmeClient
(
ctx
)
if
err
!=
nil
{
return
}
for
_
,
u
:=
range
uri
{
client
.
RevokeAuthorization
(
ctx
,
u
)
}
}
// verify runs the identifier (domain) authorization flow
// using each applicable ACME challenge type.
func
(
m
*
Manager
)
verify
(
ctx
context
.
Context
,
client
*
acme
.
Client
,
domain
string
)
error
{
// The list of challenge types we'll try to fulfill
// in this specific order.
challengeTypes
:=
[]
string
{
"tls-sni-02"
,
"tls-sni-01"
}
m
.
tokensMu
.
RLock
()
if
m
.
tryHTTP01
{
challengeTypes
=
append
(
challengeTypes
,
"http-01"
)
}
m
.
tokensMu
.
RUnlock
()
// Keep track of pending authzs and revoke the ones that did not validate.
pendingAuthzs
:=
make
(
map
[
string
]
bool
)
defer
func
()
{
var
uri
[]
string
for
k
,
pending
:=
range
pendingAuthzs
{
if
pending
{
uri
=
append
(
uri
,
k
)
}
}
if
len
(
uri
)
>
0
{
// Use "detached" background context.
// The revocations need not happen in the current verification flow.
go
m
.
revokePendingAuthz
(
context
.
Background
(),
uri
)
}
}()
// errs accumulates challenge failure errors, printed if all fail
errs
:=
make
(
map
[
*
acme
.
Challenge
]
error
)
var
nextTyp
int
// challengeType index of the next challenge type to try
for
{
// Start domain authorization and get the challenge.
authz
,
err
:=
client
.
Authorize
(
ctx
,
domain
)
if
err
!=
nil
{
return
err
}
// No point in accepting challenges if the authorization status
// is in a final state.
switch
authz
.
Status
{
case
acme
.
StatusValid
:
return
nil
// already authorized
case
acme
.
StatusInvalid
:
return
fmt
.
Errorf
(
"acme/autocert: invalid authorization %q"
,
authz
.
URI
)
}
pendingAuthzs
[
authz
.
URI
]
=
true
// Pick the next preferred challenge.
var
chal
*
acme
.
Challenge
for
chal
==
nil
&&
nextTyp
<
len
(
challengeTypes
)
{
chal
=
pickChallenge
(
challengeTypes
[
nextTyp
],
authz
.
Challenges
)
nextTyp
++
}
if
chal
==
nil
{
errorMsg
:=
fmt
.
Sprintf
(
"acme/autocert: unable to authorize %q"
,
domain
)
for
chal
,
err
:=
range
errs
{
errorMsg
+=
fmt
.
Sprintf
(
"; challenge %q failed with error: %v"
,
chal
.
Type
,
err
)
}
return
errors
.
New
(
errorMsg
)
}
cleanup
,
err
:=
m
.
fulfill
(
ctx
,
client
,
chal
)
if
err
!=
nil
{
errs
[
chal
]
=
err
continue
}
defer
cleanup
()
if
_
,
err
:=
client
.
Accept
(
ctx
,
chal
);
err
!=
nil
{
errs
[
chal
]
=
err
continue
}
// A challenge is fulfilled and accepted: wait for the CA to validate.
if
_
,
err
:=
client
.
WaitAuthorization
(
ctx
,
authz
.
URI
);
err
!=
nil
{
errs
[
chal
]
=
err
continue
}
delete
(
pendingAuthzs
,
authz
.
URI
)
return
nil
}
}
// fulfill provisions a response to the challenge chal.
// The cleanup is non-nil only if provisioning succeeded.
func
(
m
*
Manager
)
fulfill
(
ctx
context
.
Context
,
client
*
acme
.
Client
,
chal
*
acme
.
Challenge
)
(
cleanup
func
(),
err
error
)
{
switch
chal
.
Type
{
case
"tls-sni-01"
:
cert
,
name
,
err
:=
client
.
TLSSNI01ChallengeCert
(
chal
.
Token
)
if
err
!=
nil
{
return
nil
,
err
}
m
.
putCertToken
(
ctx
,
name
,
&
cert
)
return
func
()
{
go
m
.
deleteCertToken
(
name
)
},
nil
case
"tls-sni-02"
:
cert
,
name
,
err
:=
client
.
TLSSNI02ChallengeCert
(
chal
.
Token
)
if
err
!=
nil
{
return
nil
,
err
}
m
.
putCertToken
(
ctx
,
name
,
&
cert
)
return
func
()
{
go
m
.
deleteCertToken
(
name
)
},
nil
case
"http-01"
:
resp
,
err
:=
client
.
HTTP01ChallengeResponse
(
chal
.
Token
)
if
err
!=
nil
{
return
nil
,
err
}
p
:=
client
.
HTTP01ChallengePath
(
chal
.
Token
)
m
.
putHTTPToken
(
ctx
,
p
,
resp
)
return
func
()
{
go
m
.
deleteHTTPToken
(
p
)
},
nil
}
return
nil
,
fmt
.
Errorf
(
"acme/autocert: unknown challenge type %q"
,
chal
.
Type
)
}
func
pickChallenge
(
typ
string
,
chal
[]
*
acme
.
Challenge
)
*
acme
.
Challenge
{
for
_
,
c
:=
range
chal
{
if
c
.
Type
==
typ
{
return
c
}
}
return
nil
}
// putCertToken stores the token certificate with the specified name
// in both m.certTokens map and m.Cache.
func
(
m
*
Manager
)
putCertToken
(
ctx
context
.
Context
,
name
string
,
cert
*
tls
.
Certificate
)
{
m
.
tokensMu
.
Lock
()
defer
m
.
tokensMu
.
Unlock
()
if
m
.
certTokens
==
nil
{
m
.
certTokens
=
make
(
map
[
string
]
*
tls
.
Certificate
)
}
m
.
certTokens
[
name
]
=
cert
m
.
cachePut
(
ctx
,
certKey
{
domain
:
name
,
isToken
:
true
},
cert
)
}
// deleteCertToken removes the token certificate with the specified name
// from both m.certTokens map and m.Cache.
func
(
m
*
Manager
)
deleteCertToken
(
name
string
)
{
m
.
tokensMu
.
Lock
()
defer
m
.
tokensMu
.
Unlock
()
delete
(
m
.
certTokens
,
name
)
if
m
.
Cache
!=
nil
{
ck
:=
certKey
{
domain
:
name
,
isToken
:
true
}
m
.
Cache
.
Delete
(
context
.
Background
(),
ck
.
String
())
}
}
// httpToken retrieves an existing http-01 token value from an in-memory map
// or the optional cache.
func
(
m
*
Manager
)
httpToken
(
ctx
context
.
Context
,
tokenPath
string
)
([]
byte
,
error
)
{
m
.
tokensMu
.
RLock
()
defer
m
.
tokensMu
.
RUnlock
()
if
v
,
ok
:=
m
.
httpTokens
[
tokenPath
];
ok
{
return
v
,
nil
}
if
m
.
Cache
==
nil
{
return
nil
,
fmt
.
Errorf
(
"acme/autocert: no token at %q"
,
tokenPath
)
}
return
m
.
Cache
.
Get
(
ctx
,
httpTokenCacheKey
(
tokenPath
))
}
// putHTTPToken stores an http-01 token value using tokenPath as key
// in both in-memory map and the optional Cache.
//
// It ignores any error returned from Cache.Put.
func
(
m
*
Manager
)
putHTTPToken
(
ctx
context
.
Context
,
tokenPath
,
val
string
)
{
m
.
tokensMu
.
Lock
()
defer
m
.
tokensMu
.
Unlock
()
if
m
.
httpTokens
==
nil
{
m
.
httpTokens
=
make
(
map
[
string
][]
byte
)
}
b
:=
[]
byte
(
val
)
m
.
httpTokens
[
tokenPath
]
=
b
if
m
.
Cache
!=
nil
{
m
.
Cache
.
Put
(
ctx
,
httpTokenCacheKey
(
tokenPath
),
b
)
}
}
// deleteHTTPToken removes an http-01 token value from both in-memory map
// and the optional Cache, ignoring any error returned from the latter.
//
// If m.Cache is non-nil, it blocks until Cache.Delete returns without a timeout.
func
(
m
*
Manager
)
deleteHTTPToken
(
tokenPath
string
)
{
m
.
tokensMu
.
Lock
()
defer
m
.
tokensMu
.
Unlock
()
delete
(
m
.
httpTokens
,
tokenPath
)
if
m
.
Cache
!=
nil
{
m
.
Cache
.
Delete
(
context
.
Background
(),
httpTokenCacheKey
(
tokenPath
))
}
}
// httpTokenCacheKey returns a key at which an http-01 token value may be stored
// in the Manager's optional Cache.
func
httpTokenCacheKey
(
tokenPath
string
)
string
{
return
path
.
Base
(
tokenPath
)
+
"+http-01"
}
// renew starts a cert renewal timer loop, one per domain.
//
// The loop is scheduled in two cases:
// - a cert was fetched from cache for the first time (wasn't in m.state)
// - a new cert was created by m.createCert
//
// The key argument is a certificate private key.
// The exp argument is the cert expiration time (NotAfter).
func
(
m
*
Manager
)
renew
(
ck
certKey
,
key
crypto
.
Signer
,
exp
time
.
Time
)
{
m
.
renewalMu
.
Lock
()
defer
m
.
renewalMu
.
Unlock
()
if
m
.
renewal
[
ck
]
!=
nil
{
// another goroutine is already on it
return
}
if
m
.
renewal
==
nil
{
m
.
renewal
=
make
(
map
[
certKey
]
*
domainRenewal
)
}
dr
:=
&
domainRenewal
{
m
:
m
,
ck
:
ck
,
key
:
key
}
m
.
renewal
[
ck
]
=
dr
dr
.
start
(
exp
)
}
// stopRenew stops all currently running cert renewal timers.
// The timers are not restarted during the lifetime of the Manager.
func
(
m
*
Manager
)
stopRenew
()
{
m
.
renewalMu
.
Lock
()
defer
m
.
renewalMu
.
Unlock
()
for
name
,
dr
:=
range
m
.
renewal
{
delete
(
m
.
renewal
,
name
)
dr
.
stop
()
}
}
func
(
m
*
Manager
)
accountKey
(
ctx
context
.
Context
)
(
crypto
.
Signer
,
error
)
{
const
keyName
=
"acme_account+key"
// Previous versions of autocert stored the value under a different key.
const
legacyKeyName
=
"acme_account.key"
genKey
:=
func
()
(
*
ecdsa
.
PrivateKey
,
error
)
{
return
ecdsa
.
GenerateKey
(
elliptic
.
P256
(),
rand
.
Reader
)
}
if
m
.
Cache
==
nil
{
return
genKey
()
}
data
,
err
:=
m
.
Cache
.
Get
(
ctx
,
keyName
)
if
err
==
ErrCacheMiss
{
data
,
err
=
m
.
Cache
.
Get
(
ctx
,
legacyKeyName
)
}
if
err
==
ErrCacheMiss
{
key
,
err
:=
genKey
()
if
err
!=
nil
{
return
nil
,
err
}
var
buf
bytes
.
Buffer
if
err
:=
encodeECDSAKey
(
&
buf
,
key
);
err
!=
nil
{
return
nil
,
err
}
if
err
:=
m
.
Cache
.
Put
(
ctx
,
keyName
,
buf
.
Bytes
());
err
!=
nil
{
return
nil
,
err
}
return
key
,
nil
}
if
err
!=
nil
{
return
nil
,
err
}
priv
,
_
:=
pem
.
Decode
(
data
)
if
priv
==
nil
||
!
strings
.
Contains
(
priv
.
Type
,
"PRIVATE"
)
{
return
nil
,
errors
.
New
(
"acme/autocert: invalid account key found in cache"
)
}
return
parsePrivateKey
(
priv
.
Bytes
)
}
func
(
m
*
Manager
)
acmeClient
(
ctx
context
.
Context
)
(
*
acme
.
Client
,
error
)
{
m
.
clientMu
.
Lock
()
defer
m
.
clientMu
.
Unlock
()
if
m
.
client
!=
nil
{
return
m
.
client
,
nil
}
client
:=
m
.
Client
if
client
==
nil
{
client
=
&
acme
.
Client
{
DirectoryURL
:
acme
.
LetsEncryptURL
}
}
if
client
.
Key
==
nil
{
var
err
error
client
.
Key
,
err
=
m
.
accountKey
(
ctx
)
if
err
!=
nil
{
return
nil
,
err
}
}
var
contact
[]
string
if
m
.
Email
!=
""
{
contact
=
[]
string
{
"mailto:"
+
m
.
Email
}
}
a
:=
&
acme
.
Account
{
Contact
:
contact
}
_
,
err
:=
client
.
Register
(
ctx
,
a
,
m
.
Prompt
)
if
ae
,
ok
:=
err
.
(
*
acme
.
Error
);
err
==
nil
||
ok
&&
ae
.
StatusCode
==
http
.
StatusConflict
{
// conflict indicates the key is already registered
m
.
client
=
client
err
=
nil
}
return
m
.
client
,
err
}
func
(
m
*
Manager
)
hostPolicy
()
HostPolicy
{
if
m
.
HostPolicy
!=
nil
{
return
m
.
HostPolicy
}
return
defaultHostPolicy
}
func
(
m
*
Manager
)
renewBefore
()
time
.
Duration
{
if
m
.
RenewBefore
>
renewJitter
{
return
m
.
RenewBefore
}
return
720
*
time
.
Hour
// 30 days
}
// certState is ready when its mutex is unlocked for reading.
type
certState
struct
{
sync
.
RWMutex
locked
bool
// locked for read/write
key
crypto
.
Signer
// private key for cert
cert
[][]
byte
// DER encoding
leaf
*
x509
.
Certificate
// parsed cert[0]; always non-nil if cert != nil
}
// tlscert creates a tls.Certificate from s.key and s.cert.
// Callers should wrap it in s.RLock() and s.RUnlock().
func
(
s
*
certState
)
tlscert
()
(
*
tls
.
Certificate
,
error
)
{
if
s
.
key
==
nil
{
return
nil
,
errors
.
New
(
"acme/autocert: missing signer"
)
}
if
len
(
s
.
cert
)
==
0
{
return
nil
,
errors
.
New
(
"acme/autocert: missing certificate"
)
}
return
&
tls
.
Certificate
{
PrivateKey
:
s
.
key
,
Certificate
:
s
.
cert
,
Leaf
:
s
.
leaf
,
},
nil
}
// certRequest generates a CSR for the given common name cn and optional SANs.
func
certRequest
(
key
crypto
.
Signer
,
cn
string
,
ext
[]
pkix
.
Extension
,
san
...
string
)
([]
byte
,
error
)
{
req
:=
&
x509
.
CertificateRequest
{
Subject
:
pkix
.
Name
{
CommonName
:
cn
},
DNSNames
:
san
,
ExtraExtensions
:
ext
,
}
return
x509
.
CreateCertificateRequest
(
rand
.
Reader
,
req
,
key
)
}
// Attempt to parse the given private key DER block. OpenSSL 0.9.8 generates
// PKCS#1 private keys by default, while OpenSSL 1.0.0 generates PKCS#8 keys.
// OpenSSL ecparam generates SEC1 EC private keys for ECDSA. We try all three.
//
// Inspired by parsePrivateKey in crypto/tls/tls.go.
func
parsePrivateKey
(
der
[]
byte
)
(
crypto
.
Signer
,
error
)
{
if
key
,
err
:=
x509
.
ParsePKCS1PrivateKey
(
der
);
err
==
nil
{
return
key
,
nil
}
if
key
,
err
:=
x509
.
ParsePKCS8PrivateKey
(
der
);
err
==
nil
{
switch
key
:=
key
.
(
type
)
{
case
*
rsa
.
PrivateKey
:
return
key
,
nil
case
*
ecdsa
.
PrivateKey
:
return
key
,
nil
default
:
return
nil
,
errors
.
New
(
"acme/autocert: unknown private key type in PKCS#8 wrapping"
)
}
}
if
key
,
err
:=
x509
.
ParseECPrivateKey
(
der
);
err
==
nil
{
return
key
,
nil
}
return
nil
,
errors
.
New
(
"acme/autocert: failed to parse private key"
)
}
// validCert parses a cert chain provided as der argument and verifies the leaf and der[0]
// correspond to the private key, the domain and key type match, and expiration dates
// are valid. It doesn't do any revocation checking.
//
// The returned value is the verified leaf cert.
func
validCert
(
ck
certKey
,
der
[][]
byte
,
key
crypto
.
Signer
)
(
leaf
*
x509
.
Certificate
,
err
error
)
{
// parse public part(s)
var
n
int
for
_
,
b
:=
range
der
{
n
+=
len
(
b
)
}
pub
:=
make
([]
byte
,
n
)
n
=
0
for
_
,
b
:=
range
der
{
n
+=
copy
(
pub
[
n
:
],
b
)
}
x509Cert
,
err
:=
x509
.
ParseCertificates
(
pub
)
if
err
!=
nil
||
len
(
x509Cert
)
==
0
{
return
nil
,
errors
.
New
(
"acme/autocert: no public key found"
)
}
// verify the leaf is not expired and matches the domain name
leaf
=
x509Cert
[
0
]
now
:=
timeNow
()
if
now
.
Before
(
leaf
.
NotBefore
)
{
return
nil
,
errors
.
New
(
"acme/autocert: certificate is not valid yet"
)
}
if
now
.
After
(
leaf
.
NotAfter
)
{
return
nil
,
errors
.
New
(
"acme/autocert: expired certificate"
)
}
if
err
:=
leaf
.
VerifyHostname
(
ck
.
domain
);
err
!=
nil
{
return
nil
,
err
}
// ensure the leaf corresponds to the private key and matches the certKey type
switch
pub
:=
leaf
.
PublicKey
.
(
type
)
{
case
*
rsa
.
PublicKey
:
prv
,
ok
:=
key
.
(
*
rsa
.
PrivateKey
)
if
!
ok
{
return
nil
,
errors
.
New
(
"acme/autocert: private key type does not match public key type"
)
}
if
pub
.
N
.
Cmp
(
prv
.
N
)
!=
0
{
return
nil
,
errors
.
New
(
"acme/autocert: private key does not match public key"
)
}
if
!
ck
.
isRSA
&&
!
ck
.
isToken
{
return
nil
,
errors
.
New
(
"acme/autocert: key type does not match expected value"
)
}
case
*
ecdsa
.
PublicKey
:
prv
,
ok
:=
key
.
(
*
ecdsa
.
PrivateKey
)
if
!
ok
{
return
nil
,
errors
.
New
(
"acme/autocert: private key type does not match public key type"
)
}
if
pub
.
X
.
Cmp
(
prv
.
X
)
!=
0
||
pub
.
Y
.
Cmp
(
prv
.
Y
)
!=
0
{
return
nil
,
errors
.
New
(
"acme/autocert: private key does not match public key"
)
}
if
ck
.
isRSA
&&
!
ck
.
isToken
{
return
nil
,
errors
.
New
(
"acme/autocert: key type does not match expected value"
)
}
default
:
return
nil
,
errors
.
New
(
"acme/autocert: unknown public key algorithm"
)
}
return
leaf
,
nil
}
type
lockedMathRand
struct
{
sync
.
Mutex
rnd
*
mathrand
.
Rand
}
func
(
r
*
lockedMathRand
)
int63n
(
max
int64
)
int64
{
r
.
Lock
()
n
:=
r
.
rnd
.
Int63n
(
max
)
r
.
Unlock
()
return
n
}
// For easier testing.
var
(
timeNow
=
time
.
Now
// Called when a state is removed.
testDidRemoveState
=
func
(
certKey
)
{}
)
vendor/golang.org/x/crypto/acme/autocert/cache.go
deleted
100644 → 0
View file @
2d323373
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package
autocert
import
(
"context"
"errors"
"io/ioutil"
"os"
"path/filepath"
)
// ErrCacheMiss is returned when a certificate is not found in cache.
var
ErrCacheMiss
=
errors
.
New
(
"acme/autocert: certificate cache miss"
)
// Cache is used by Manager to store and retrieve previously obtained certificates
// and other account data as opaque blobs.
//
// Cache implementations should not rely on the key naming pattern. Keys can
// include any printable ASCII characters, except the following: \/:*?"<>|
type
Cache
interface
{
// Get returns a certificate data for the specified key.
// If there's no such key, Get returns ErrCacheMiss.
Get
(
ctx
context
.
Context
,
key
string
)
([]
byte
,
error
)
// Put stores the data in the cache under the specified key.
// Underlying implementations may use any data storage format,
// as long as the reverse operation, Get, results in the original data.
Put
(
ctx
context
.
Context
,
key
string
,
data
[]
byte
)
error
// Delete removes a certificate data from the cache under the specified key.
// If there's no such key in the cache, Delete returns nil.
Delete
(
ctx
context
.
Context
,
key
string
)
error
}
// DirCache implements Cache using a directory on the local filesystem.
// If the directory does not exist, it will be created with 0700 permissions.
type
DirCache
string
// Get reads a certificate data from the specified file name.
func
(
d
DirCache
)
Get
(
ctx
context
.
Context
,
name
string
)
([]
byte
,
error
)
{
name
=
filepath
.
Join
(
string
(
d
),
name
)
var
(
data
[]
byte
err
error
done
=
make
(
chan
struct
{})
)
go
func
()
{
data
,
err
=
ioutil
.
ReadFile
(
name
)
close
(
done
)
}()
select
{
case
<-
ctx
.
Done
()
:
return
nil
,
ctx
.
Err
()
case
<-
done
:
}
if
os
.
IsNotExist
(
err
)
{
return
nil
,
ErrCacheMiss
}
return
data
,
err
}
// Put writes the certificate data to the specified file name.
// The file will be created with 0600 permissions.
func
(
d
DirCache
)
Put
(
ctx
context
.
Context
,
name
string
,
data
[]
byte
)
error
{
if
err
:=
os
.
MkdirAll
(
string
(
d
),
0700
);
err
!=
nil
{
return
err
}
done
:=
make
(
chan
struct
{})
var
err
error
go
func
()
{
defer
close
(
done
)
var
tmp
string
if
tmp
,
err
=
d
.
writeTempFile
(
name
,
data
);
err
!=
nil
{
return
}
select
{
case
<-
ctx
.
Done
()
:
// Don't overwrite the file if the context was canceled.
default
:
newName
:=
filepath
.
Join
(
string
(
d
),
name
)
err
=
os
.
Rename
(
tmp
,
newName
)
}
}()
select
{
case
<-
ctx
.
Done
()
:
return
ctx
.
Err
()
case
<-
done
:
}
return
err
}
// Delete removes the specified file name.
func
(
d
DirCache
)
Delete
(
ctx
context
.
Context
,
name
string
)
error
{
name
=
filepath
.
Join
(
string
(
d
),
name
)
var
(
err
error
done
=
make
(
chan
struct
{})
)
go
func
()
{
err
=
os
.
Remove
(
name
)
close
(
done
)
}()
select
{
case
<-
ctx
.
Done
()
:
return
ctx
.
Err
()
case
<-
done
:
}
if
err
!=
nil
&&
!
os
.
IsNotExist
(
err
)
{
return
err
}
return
nil
}
// writeTempFile writes b to a temporary file, closes the file and returns its path.
func
(
d
DirCache
)
writeTempFile
(
prefix
string
,
b
[]
byte
)
(
string
,
error
)
{
// TempFile uses 0600 permissions
f
,
err
:=
ioutil
.
TempFile
(
string
(
d
),
prefix
)
if
err
!=
nil
{
return
""
,
err
}
if
_
,
err
:=
f
.
Write
(
b
);
err
!=
nil
{
f
.
Close
()
return
""
,
err
}
return
f
.
Name
(),
f
.
Close
()
}
vendor/golang.org/x/crypto/acme/autocert/listener.go
deleted
100644 → 0
View file @
2d323373
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package
autocert
import
(
"crypto/tls"
"log"
"net"
"os"
"path/filepath"
"runtime"
"time"
)
// NewListener returns a net.Listener that listens on the standard TLS
// port (443) on all interfaces and returns *tls.Conn connections with
// LetsEncrypt certificates for the provided domain or domains.
//
// It enables one-line HTTPS servers:
//
// log.Fatal(http.Serve(autocert.NewListener("example.com"), handler))
//
// NewListener is a convenience function for a common configuration.
// More complex or custom configurations can use the autocert.Manager
// type instead.
//
// Use of this function implies acceptance of the LetsEncrypt Terms of
// Service. If domains is not empty, the provided domains are passed
// to HostWhitelist. If domains is empty, the listener will do
// LetsEncrypt challenges for any requested domain, which is not
// recommended.
//
// Certificates are cached in a "golang-autocert" directory under an
// operating system-specific cache or temp directory. This may not
// be suitable for servers spanning multiple machines.
//
// The returned listener uses a *tls.Config that enables HTTP/2, and
// should only be used with servers that support HTTP/2.
//
// The returned Listener also enables TCP keep-alives on the accepted
// connections. The returned *tls.Conn are returned before their TLS
// handshake has completed.
func
NewListener
(
domains
...
string
)
net
.
Listener
{
m
:=
&
Manager
{
Prompt
:
AcceptTOS
,
}
if
len
(
domains
)
>
0
{
m
.
HostPolicy
=
HostWhitelist
(
domains
...
)
}
dir
:=
cacheDir
()
if
err
:=
os
.
MkdirAll
(
dir
,
0700
);
err
!=
nil
{
log
.
Printf
(
"warning: autocert.NewListener not using a cache: %v"
,
err
)
}
else
{
m
.
Cache
=
DirCache
(
dir
)
}
return
m
.
Listener
()
}
// Listener listens on the standard TLS port (443) on all interfaces
// and returns a net.Listener returning *tls.Conn connections.
//
// The returned listener uses a *tls.Config that enables HTTP/2, and
// should only be used with servers that support HTTP/2.
//
// The returned Listener also enables TCP keep-alives on the accepted
// connections. The returned *tls.Conn are returned before their TLS
// handshake has completed.
//
// Unlike NewListener, it is the caller's responsibility to initialize
// the Manager m's Prompt, Cache, HostPolicy, and other desired options.
func
(
m
*
Manager
)
Listener
()
net
.
Listener
{
ln
:=
&
listener
{
m
:
m
,
conf
:
&
tls
.
Config
{
GetCertificate
:
m
.
GetCertificate
,
// bonus: panic on nil m
NextProtos
:
[]
string
{
"h2"
,
"http/1.1"
},
// Enable HTTP/2
},
}
ln
.
tcpListener
,
ln
.
tcpListenErr
=
net
.
Listen
(
"tcp"
,
":443"
)
return
ln
}
type
listener
struct
{
m
*
Manager
conf
*
tls
.
Config
tcpListener
net
.
Listener
tcpListenErr
error
}
func
(
ln
*
listener
)
Accept
()
(
net
.
Conn
,
error
)
{
if
ln
.
tcpListenErr
!=
nil
{
return
nil
,
ln
.
tcpListenErr
}
conn
,
err
:=
ln
.
tcpListener
.
Accept
()
if
err
!=
nil
{
return
nil
,
err
}
tcpConn
:=
conn
.
(
*
net
.
TCPConn
)
// Because Listener is a convenience function, help out with
// this too. This is not possible for the caller to set once
// we return a *tcp.Conn wrapping an inaccessible net.Conn.
// If callers don't want this, they can do things the manual
// way and tweak as needed. But this is what net/http does
// itself, so copy that. If net/http changes, we can change
// here too.
tcpConn
.
SetKeepAlive
(
true
)
tcpConn
.
SetKeepAlivePeriod
(
3
*
time
.
Minute
)
return
tls
.
Server
(
tcpConn
,
ln
.
conf
),
nil
}
func
(
ln
*
listener
)
Addr
()
net
.
Addr
{
if
ln
.
tcpListener
!=
nil
{
return
ln
.
tcpListener
.
Addr
()
}
// net.Listen failed. Return something non-nil in case callers
// call Addr before Accept:
return
&
net
.
TCPAddr
{
IP
:
net
.
IP
{
0
,
0
,
0
,
0
},
Port
:
443
}
}
func
(
ln
*
listener
)
Close
()
error
{
if
ln
.
tcpListenErr
!=
nil
{
return
ln
.
tcpListenErr
}
return
ln
.
tcpListener
.
Close
()
}
func
homeDir
()
string
{
if
runtime
.
GOOS
==
"windows"
{
return
os
.
Getenv
(
"HOMEDRIVE"
)
+
os
.
Getenv
(
"HOMEPATH"
)
}
if
h
:=
os
.
Getenv
(
"HOME"
);
h
!=
""
{
return
h
}
return
"/"
}
func
cacheDir
()
string
{
const
base
=
"golang-autocert"
switch
runtime
.
GOOS
{
case
"darwin"
:
return
filepath
.
Join
(
homeDir
(),
"Library"
,
"Caches"
,
base
)
case
"windows"
:
for
_
,
ev
:=
range
[]
string
{
"APPDATA"
,
"CSIDL_APPDATA"
,
"TEMP"
,
"TMP"
}
{
if
v
:=
os
.
Getenv
(
ev
);
v
!=
""
{
return
filepath
.
Join
(
v
,
base
)
}
}
// Worst case:
return
filepath
.
Join
(
homeDir
(),
base
)
}
if
xdg
:=
os
.
Getenv
(
"XDG_CACHE_HOME"
);
xdg
!=
""
{
return
filepath
.
Join
(
xdg
,
base
)
}
return
filepath
.
Join
(
homeDir
(),
".cache"
,
base
)
}
vendor/golang.org/x/crypto/acme/autocert/renewal.go
deleted
100644 → 0
View file @
2d323373
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package
autocert
import
(
"context"
"crypto"
"sync"
"time"
)
// renewJitter is the maximum deviation from Manager.RenewBefore.
const
renewJitter
=
time
.
Hour
// domainRenewal tracks the state used by the periodic timers
// renewing a single domain's cert.
type
domainRenewal
struct
{
m
*
Manager
ck
certKey
key
crypto
.
Signer
timerMu
sync
.
Mutex
timer
*
time
.
Timer
}
// start starts a cert renewal timer at the time
// defined by the certificate expiration time exp.
//
// If the timer is already started, calling start is a noop.
func
(
dr
*
domainRenewal
)
start
(
exp
time
.
Time
)
{
dr
.
timerMu
.
Lock
()
defer
dr
.
timerMu
.
Unlock
()
if
dr
.
timer
!=
nil
{
return
}
dr
.
timer
=
time
.
AfterFunc
(
dr
.
next
(
exp
),
dr
.
renew
)
}
// stop stops the cert renewal timer.
// If the timer is already stopped, calling stop is a noop.
func
(
dr
*
domainRenewal
)
stop
()
{
dr
.
timerMu
.
Lock
()
defer
dr
.
timerMu
.
Unlock
()
if
dr
.
timer
==
nil
{
return
}
dr
.
timer
.
Stop
()
dr
.
timer
=
nil
}
// renew is called periodically by a timer.
// The first renew call is kicked off by dr.start.
func
(
dr
*
domainRenewal
)
renew
()
{
dr
.
timerMu
.
Lock
()
defer
dr
.
timerMu
.
Unlock
()
if
dr
.
timer
==
nil
{
return
}
ctx
,
cancel
:=
context
.
WithTimeout
(
context
.
Background
(),
10
*
time
.
Minute
)
defer
cancel
()
// TODO: rotate dr.key at some point?
next
,
err
:=
dr
.
do
(
ctx
)
if
err
!=
nil
{
next
=
renewJitter
/
2
next
+=
time
.
Duration
(
pseudoRand
.
int63n
(
int64
(
next
)))
}
dr
.
timer
=
time
.
AfterFunc
(
next
,
dr
.
renew
)
testDidRenewLoop
(
next
,
err
)
}
// updateState locks and replaces the relevant Manager.state item with the given
// state. It additionally updates dr.key with the given state's key.
func
(
dr
*
domainRenewal
)
updateState
(
state
*
certState
)
{
dr
.
m
.
stateMu
.
Lock
()
defer
dr
.
m
.
stateMu
.
Unlock
()
dr
.
key
=
state
.
key
dr
.
m
.
state
[
dr
.
ck
]
=
state
}
// do is similar to Manager.createCert but it doesn't lock a Manager.state item.
// Instead, it requests a new certificate independently and, upon success,
// replaces dr.m.state item with a new one and updates cache for the given domain.
//
// It may lock and update the Manager.state if the expiration date of the currently
// cached cert is far enough in the future.
//
// The returned value is a time interval after which the renewal should occur again.
func
(
dr
*
domainRenewal
)
do
(
ctx
context
.
Context
)
(
time
.
Duration
,
error
)
{
// a race is likely unavoidable in a distributed environment
// but we try nonetheless
if
tlscert
,
err
:=
dr
.
m
.
cacheGet
(
ctx
,
dr
.
ck
);
err
==
nil
{
next
:=
dr
.
next
(
tlscert
.
Leaf
.
NotAfter
)
if
next
>
dr
.
m
.
renewBefore
()
+
renewJitter
{
signer
,
ok
:=
tlscert
.
PrivateKey
.
(
crypto
.
Signer
)
if
ok
{
state
:=
&
certState
{
key
:
signer
,
cert
:
tlscert
.
Certificate
,
leaf
:
tlscert
.
Leaf
,
}
dr
.
updateState
(
state
)
return
next
,
nil
}
}
}
der
,
leaf
,
err
:=
dr
.
m
.
authorizedCert
(
ctx
,
dr
.
key
,
dr
.
ck
)
if
err
!=
nil
{
return
0
,
err
}
state
:=
&
certState
{
key
:
dr
.
key
,
cert
:
der
,
leaf
:
leaf
,
}
tlscert
,
err
:=
state
.
tlscert
()
if
err
!=
nil
{
return
0
,
err
}
if
err
:=
dr
.
m
.
cachePut
(
ctx
,
dr
.
ck
,
tlscert
);
err
!=
nil
{
return
0
,
err
}
dr
.
updateState
(
state
)
return
dr
.
next
(
leaf
.
NotAfter
),
nil
}
func
(
dr
*
domainRenewal
)
next
(
expiry
time
.
Time
)
time
.
Duration
{
d
:=
expiry
.
Sub
(
timeNow
())
-
dr
.
m
.
renewBefore
()
// add a bit of randomness to renew deadline
n
:=
pseudoRand
.
int63n
(
int64
(
renewJitter
))
d
-=
time
.
Duration
(
n
)
if
d
<
0
{
return
0
}
return
d
}
var
testDidRenewLoop
=
func
(
next
time
.
Duration
,
err
error
)
{}
vendor/golang.org/x/crypto/acme/http.go
deleted
100644 → 0
View file @
2d323373
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package
acme
import
(
"bytes"
"context"
"crypto"
"crypto/rand"
"encoding/json"
"fmt"
"io/ioutil"
"math/big"
"net/http"
"strconv"
"strings"
"time"
)
// retryTimer encapsulates common logic for retrying unsuccessful requests.
// It is not safe for concurrent use.
type
retryTimer
struct
{
// backoffFn provides backoff delay sequence for retries.
// See Client.RetryBackoff doc comment.
backoffFn
func
(
n
int
,
r
*
http
.
Request
,
res
*
http
.
Response
)
time
.
Duration
// n is the current retry attempt.
n
int
}
func
(
t
*
retryTimer
)
inc
()
{
t
.
n
++
}
// backoff pauses the current goroutine as described in Client.RetryBackoff.
func
(
t
*
retryTimer
)
backoff
(
ctx
context
.
Context
,
r
*
http
.
Request
,
res
*
http
.
Response
)
error
{
d
:=
t
.
backoffFn
(
t
.
n
,
r
,
res
)
if
d
<=
0
{
return
fmt
.
Errorf
(
"acme: no more retries for %s; tried %d time(s)"
,
r
.
URL
,
t
.
n
)
}
wakeup
:=
time
.
NewTimer
(
d
)
defer
wakeup
.
Stop
()
select
{
case
<-
ctx
.
Done
()
:
return
ctx
.
Err
()
case
<-
wakeup
.
C
:
return
nil
}
}
func
(
c
*
Client
)
retryTimer
()
*
retryTimer
{
f
:=
c
.
RetryBackoff
if
f
==
nil
{
f
=
defaultBackoff
}
return
&
retryTimer
{
backoffFn
:
f
}
}
// defaultBackoff provides default Client.RetryBackoff implementation
// using a truncated exponential backoff algorithm,
// as described in Client.RetryBackoff.
//
// The n argument is always bounded between 1 and 30.
// The returned value is always greater than 0.
func
defaultBackoff
(
n
int
,
r
*
http
.
Request
,
res
*
http
.
Response
)
time
.
Duration
{
const
max
=
10
*
time
.
Second
var
jitter
time
.
Duration
if
x
,
err
:=
rand
.
Int
(
rand
.
Reader
,
big
.
NewInt
(
1000
));
err
==
nil
{
// Set the minimum to 1ms to avoid a case where
// an invalid Retry-After value is parsed into 0 below,
// resulting in the 0 returned value which would unintentionally
// stop the retries.
jitter
=
(
1
+
time
.
Duration
(
x
.
Int64
()))
*
time
.
Millisecond
}
if
v
,
ok
:=
res
.
Header
[
"Retry-After"
];
ok
{
return
retryAfter
(
v
[
0
])
+
jitter
}
if
n
<
1
{
n
=
1
}
if
n
>
30
{
n
=
30
}
d
:=
time
.
Duration
(
1
<<
uint
(
n
-
1
))
*
time
.
Second
+
jitter
if
d
>
max
{
return
max
}
return
d
}
// retryAfter parses a Retry-After HTTP header value,
// trying to convert v into an int (seconds) or use http.ParseTime otherwise.
// It returns zero value if v cannot be parsed.
func
retryAfter
(
v
string
)
time
.
Duration
{
if
i
,
err
:=
strconv
.
Atoi
(
v
);
err
==
nil
{
return
time
.
Duration
(
i
)
*
time
.
Second
}
t
,
err
:=
http
.
ParseTime
(
v
)
if
err
!=
nil
{
return
0
}
return
t
.
Sub
(
timeNow
())
}
// resOkay is a function that reports whether the provided response is okay.
// It is expected to keep the response body unread.
type
resOkay
func
(
*
http
.
Response
)
bool
// wantStatus returns a function which reports whether the code
// matches the status code of a response.
func
wantStatus
(
codes
...
int
)
resOkay
{
return
func
(
res
*
http
.
Response
)
bool
{
for
_
,
code
:=
range
codes
{
if
code
==
res
.
StatusCode
{
return
true
}
}
return
false
}
}
// get issues an unsigned GET request to the specified URL.
// It returns a non-error value only when ok reports true.
//
// get retries unsuccessful attempts according to c.RetryBackoff
// until the context is done or a non-retriable error is received.
func
(
c
*
Client
)
get
(
ctx
context
.
Context
,
url
string
,
ok
resOkay
)
(
*
http
.
Response
,
error
)
{
retry
:=
c
.
retryTimer
()
for
{
req
,
err
:=
http
.
NewRequest
(
"GET"
,
url
,
nil
)
if
err
!=
nil
{
return
nil
,
err
}
res
,
err
:=
c
.
doNoRetry
(
ctx
,
req
)
switch
{
case
err
!=
nil
:
return
nil
,
err
case
ok
(
res
)
:
return
res
,
nil
case
isRetriable
(
res
.
StatusCode
)
:
retry
.
inc
()
resErr
:=
responseError
(
res
)
res
.
Body
.
Close
()
// Ignore the error value from retry.backoff
// and return the one from last retry, as received from the CA.
if
retry
.
backoff
(
ctx
,
req
,
res
)
!=
nil
{
return
nil
,
resErr
}
default
:
defer
res
.
Body
.
Close
()
return
nil
,
responseError
(
res
)
}
}
}
// post issues a signed POST request in JWS format using the provided key
// to the specified URL.
// It returns a non-error value only when ok reports true.
//
// post retries unsuccessful attempts according to c.RetryBackoff
// until the context is done or a non-retriable error is received.
// It uses postNoRetry to make individual requests.
func
(
c
*
Client
)
post
(
ctx
context
.
Context
,
key
crypto
.
Signer
,
url
string
,
body
interface
{},
ok
resOkay
)
(
*
http
.
Response
,
error
)
{
retry
:=
c
.
retryTimer
()
for
{
res
,
req
,
err
:=
c
.
postNoRetry
(
ctx
,
key
,
url
,
body
)
if
err
!=
nil
{
return
nil
,
err
}
if
ok
(
res
)
{
return
res
,
nil
}
resErr
:=
responseError
(
res
)
res
.
Body
.
Close
()
switch
{
// Check for bad nonce before isRetriable because it may have been returned
// with an unretriable response code such as 400 Bad Request.
case
isBadNonce
(
resErr
)
:
// Consider any previously stored nonce values to be invalid.
c
.
clearNonces
()
case
!
isRetriable
(
res
.
StatusCode
)
:
return
nil
,
resErr
}
retry
.
inc
()
// Ignore the error value from retry.backoff
// and return the one from last retry, as received from the CA.
if
err
:=
retry
.
backoff
(
ctx
,
req
,
res
);
err
!=
nil
{
return
nil
,
resErr
}
}
}
// postNoRetry signs the body with the given key and POSTs it to the provided url.
// The body argument must be JSON-serializable.
// It is used by c.post to retry unsuccessful attempts.
func
(
c
*
Client
)
postNoRetry
(
ctx
context
.
Context
,
key
crypto
.
Signer
,
url
string
,
body
interface
{})
(
*
http
.
Response
,
*
http
.
Request
,
error
)
{
nonce
,
err
:=
c
.
popNonce
(
ctx
,
url
)
if
err
!=
nil
{
return
nil
,
nil
,
err
}
b
,
err
:=
jwsEncodeJSON
(
body
,
key
,
nonce
)
if
err
!=
nil
{
return
nil
,
nil
,
err
}
req
,
err
:=
http
.
NewRequest
(
"POST"
,
url
,
bytes
.
NewReader
(
b
))
if
err
!=
nil
{
return
nil
,
nil
,
err
}
req
.
Header
.
Set
(
"Content-Type"
,
"application/jose+json"
)
res
,
err
:=
c
.
doNoRetry
(
ctx
,
req
)
if
err
!=
nil
{
return
nil
,
nil
,
err
}
c
.
addNonce
(
res
.
Header
)
return
res
,
req
,
nil
}
// doNoRetry issues a request req, replacing its context (if any) with ctx.
func
(
c
*
Client
)
doNoRetry
(
ctx
context
.
Context
,
req
*
http
.
Request
)
(
*
http
.
Response
,
error
)
{
res
,
err
:=
c
.
httpClient
()
.
Do
(
req
.
WithContext
(
ctx
))
if
err
!=
nil
{
select
{
case
<-
ctx
.
Done
()
:
// Prefer the unadorned context error.
// (The acme package had tests assuming this, previously from ctxhttp's
// behavior, predating net/http supporting contexts natively)
// TODO(bradfitz): reconsider this in the future. But for now this
// requires no test updates.
return
nil
,
ctx
.
Err
()
default
:
return
nil
,
err
}
}
return
res
,
nil
}
func
(
c
*
Client
)
httpClient
()
*
http
.
Client
{
if
c
.
HTTPClient
!=
nil
{
return
c
.
HTTPClient
}
return
http
.
DefaultClient
}
// isBadNonce reports whether err is an ACME "badnonce" error.
func
isBadNonce
(
err
error
)
bool
{
// According to the spec badNonce is urn:ietf:params:acme:error:badNonce.
// However, ACME servers in the wild return their versions of the error.
// See https://tools.ietf.org/html/draft-ietf-acme-acme-02#section-5.4
// and https://github.com/letsencrypt/boulder/blob/0e07eacb/docs/acme-divergences.md#section-66.
ae
,
ok
:=
err
.
(
*
Error
)
return
ok
&&
strings
.
HasSuffix
(
strings
.
ToLower
(
ae
.
ProblemType
),
":badnonce"
)
}
// isRetriable reports whether a request can be retried
// based on the response status code.
//
// Note that a "bad nonce" error is returned with a non-retriable 400 Bad Request code.
// Callers should parse the response and check with isBadNonce.
func
isRetriable
(
code
int
)
bool
{
return
code
<=
399
||
code
>=
500
||
code
==
http
.
StatusTooManyRequests
}
// responseError creates an error of Error type from resp.
func
responseError
(
resp
*
http
.
Response
)
error
{
// don't care if ReadAll returns an error:
// json.Unmarshal will fail in that case anyway
b
,
_
:=
ioutil
.
ReadAll
(
resp
.
Body
)
e
:=
&
wireError
{
Status
:
resp
.
StatusCode
}
if
err
:=
json
.
Unmarshal
(
b
,
e
);
err
!=
nil
{
// this is not a regular error response:
// populate detail with anything we received,
// e.Status will already contain HTTP response code value
e
.
Detail
=
string
(
b
)
if
e
.
Detail
==
""
{
e
.
Detail
=
resp
.
Status
}
}
return
e
.
error
(
resp
.
Header
)
}
vendor/golang.org/x/crypto/acme/jws.go
deleted
100644 → 0
View file @
2d323373
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package
acme
import
(
"crypto"
"crypto/ecdsa"
"crypto/rand"
"crypto/rsa"
"crypto/sha256"
_
"crypto/sha512"
// need for EC keys
"encoding/base64"
"encoding/json"
"fmt"
"math/big"
)
// jwsEncodeJSON signs claimset using provided key and a nonce.
// The result is serialized in JSON format.
// See https://tools.ietf.org/html/rfc7515#section-7.
func
jwsEncodeJSON
(
claimset
interface
{},
key
crypto
.
Signer
,
nonce
string
)
([]
byte
,
error
)
{
jwk
,
err
:=
jwkEncode
(
key
.
Public
())
if
err
!=
nil
{
return
nil
,
err
}
alg
,
sha
:=
jwsHasher
(
key
)
if
alg
==
""
||
!
sha
.
Available
()
{
return
nil
,
ErrUnsupportedKey
}
phead
:=
fmt
.
Sprintf
(
`{"alg":%q,"jwk":%s,"nonce":%q}`
,
alg
,
jwk
,
nonce
)
phead
=
base64
.
RawURLEncoding
.
EncodeToString
([]
byte
(
phead
))
cs
,
err
:=
json
.
Marshal
(
claimset
)
if
err
!=
nil
{
return
nil
,
err
}
payload
:=
base64
.
RawURLEncoding
.
EncodeToString
(
cs
)
hash
:=
sha
.
New
()
hash
.
Write
([]
byte
(
phead
+
"."
+
payload
))
sig
,
err
:=
jwsSign
(
key
,
sha
,
hash
.
Sum
(
nil
))
if
err
!=
nil
{
return
nil
,
err
}
enc
:=
struct
{
Protected
string
`json:"protected"`
Payload
string
`json:"payload"`
Sig
string
`json:"signature"`
}{
Protected
:
phead
,
Payload
:
payload
,
Sig
:
base64
.
RawURLEncoding
.
EncodeToString
(
sig
),
}
return
json
.
Marshal
(
&
enc
)
}
// jwkEncode encodes public part of an RSA or ECDSA key into a JWK.
// The result is also suitable for creating a JWK thumbprint.
// https://tools.ietf.org/html/rfc7517
func
jwkEncode
(
pub
crypto
.
PublicKey
)
(
string
,
error
)
{
switch
pub
:=
pub
.
(
type
)
{
case
*
rsa
.
PublicKey
:
// https://tools.ietf.org/html/rfc7518#section-6.3.1
n
:=
pub
.
N
e
:=
big
.
NewInt
(
int64
(
pub
.
E
))
// Field order is important.
// See https://tools.ietf.org/html/rfc7638#section-3.3 for details.
return
fmt
.
Sprintf
(
`{"e":"%s","kty":"RSA","n":"%s"}`
,
base64
.
RawURLEncoding
.
EncodeToString
(
e
.
Bytes
()),
base64
.
RawURLEncoding
.
EncodeToString
(
n
.
Bytes
()),
),
nil
case
*
ecdsa
.
PublicKey
:
// https://tools.ietf.org/html/rfc7518#section-6.2.1
p
:=
pub
.
Curve
.
Params
()
n
:=
p
.
BitSize
/
8
if
p
.
BitSize
%
8
!=
0
{
n
++
}
x
:=
pub
.
X
.
Bytes
()
if
n
>
len
(
x
)
{
x
=
append
(
make
([]
byte
,
n
-
len
(
x
)),
x
...
)
}
y
:=
pub
.
Y
.
Bytes
()
if
n
>
len
(
y
)
{
y
=
append
(
make
([]
byte
,
n
-
len
(
y
)),
y
...
)
}
// Field order is important.
// See https://tools.ietf.org/html/rfc7638#section-3.3 for details.
return
fmt
.
Sprintf
(
`{"crv":"%s","kty":"EC","x":"%s","y":"%s"}`
,
p
.
Name
,
base64
.
RawURLEncoding
.
EncodeToString
(
x
),
base64
.
RawURLEncoding
.
EncodeToString
(
y
),
),
nil
}
return
""
,
ErrUnsupportedKey
}
// jwsSign signs the digest using the given key.
// It returns ErrUnsupportedKey if the key type is unknown.
// The hash is used only for RSA keys.
func
jwsSign
(
key
crypto
.
Signer
,
hash
crypto
.
Hash
,
digest
[]
byte
)
([]
byte
,
error
)
{
switch
key
:=
key
.
(
type
)
{
case
*
rsa
.
PrivateKey
:
return
key
.
Sign
(
rand
.
Reader
,
digest
,
hash
)
case
*
ecdsa
.
PrivateKey
:
r
,
s
,
err
:=
ecdsa
.
Sign
(
rand
.
Reader
,
key
,
digest
)
if
err
!=
nil
{
return
nil
,
err
}
rb
,
sb
:=
r
.
Bytes
(),
s
.
Bytes
()
size
:=
key
.
Params
()
.
BitSize
/
8
if
size
%
8
>
0
{
size
++
}
sig
:=
make
([]
byte
,
size
*
2
)
copy
(
sig
[
size
-
len
(
rb
)
:
],
rb
)
copy
(
sig
[
size
*
2
-
len
(
sb
)
:
],
sb
)
return
sig
,
nil
}
return
nil
,
ErrUnsupportedKey
}
// jwsHasher indicates suitable JWS algorithm name and a hash function
// to use for signing a digest with the provided key.
// It returns ("", 0) if the key is not supported.
func
jwsHasher
(
key
crypto
.
Signer
)
(
string
,
crypto
.
Hash
)
{
switch
key
:=
key
.
(
type
)
{
case
*
rsa
.
PrivateKey
:
return
"RS256"
,
crypto
.
SHA256
case
*
ecdsa
.
PrivateKey
:
switch
key
.
Params
()
.
Name
{
case
"P-256"
:
return
"ES256"
,
crypto
.
SHA256
case
"P-384"
:
return
"ES384"
,
crypto
.
SHA384
case
"P-521"
:
return
"ES512"
,
crypto
.
SHA512
}
}
return
""
,
0
}
// JWKThumbprint creates a JWK thumbprint out of pub
// as specified in https://tools.ietf.org/html/rfc7638.
func
JWKThumbprint
(
pub
crypto
.
PublicKey
)
(
string
,
error
)
{
jwk
,
err
:=
jwkEncode
(
pub
)
if
err
!=
nil
{
return
""
,
err
}
b
:=
sha256
.
Sum256
([]
byte
(
jwk
))
return
base64
.
RawURLEncoding
.
EncodeToString
(
b
[
:
]),
nil
}
vendor/golang.org/x/crypto/acme/types.go
deleted
100644 → 0
View file @
2d323373
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package
acme
import
(
"crypto"
"crypto/x509"
"errors"
"fmt"
"net/http"
"strings"
"time"
)
// ACME server response statuses used to describe Authorization and Challenge states.
const
(
StatusUnknown
=
"unknown"
StatusPending
=
"pending"
StatusProcessing
=
"processing"
StatusValid
=
"valid"
StatusInvalid
=
"invalid"
StatusRevoked
=
"revoked"
)
// CRLReasonCode identifies the reason for a certificate revocation.
type
CRLReasonCode
int
// CRL reason codes as defined in RFC 5280.
const
(
CRLReasonUnspecified
CRLReasonCode
=
0
CRLReasonKeyCompromise
CRLReasonCode
=
1
CRLReasonCACompromise
CRLReasonCode
=
2
CRLReasonAffiliationChanged
CRLReasonCode
=
3
CRLReasonSuperseded
CRLReasonCode
=
4
CRLReasonCessationOfOperation
CRLReasonCode
=
5
CRLReasonCertificateHold
CRLReasonCode
=
6
CRLReasonRemoveFromCRL
CRLReasonCode
=
8
CRLReasonPrivilegeWithdrawn
CRLReasonCode
=
9
CRLReasonAACompromise
CRLReasonCode
=
10
)
// ErrUnsupportedKey is returned when an unsupported key type is encountered.
var
ErrUnsupportedKey
=
errors
.
New
(
"acme: unknown key type; only RSA and ECDSA are supported"
)
// Error is an ACME error, defined in Problem Details for HTTP APIs doc
// http://tools.ietf.org/html/draft-ietf-appsawg-http-problem.
type
Error
struct
{
// StatusCode is The HTTP status code generated by the origin server.
StatusCode
int
// ProblemType is a URI reference that identifies the problem type,
// typically in a "urn:acme:error:xxx" form.
ProblemType
string
// Detail is a human-readable explanation specific to this occurrence of the problem.
Detail
string
// Header is the original server error response headers.
// It may be nil.
Header
http
.
Header
}
func
(
e
*
Error
)
Error
()
string
{
return
fmt
.
Sprintf
(
"%d %s: %s"
,
e
.
StatusCode
,
e
.
ProblemType
,
e
.
Detail
)
}
// AuthorizationError indicates that an authorization for an identifier
// did not succeed.
// It contains all errors from Challenge items of the failed Authorization.
type
AuthorizationError
struct
{
// URI uniquely identifies the failed Authorization.
URI
string
// Identifier is an AuthzID.Value of the failed Authorization.
Identifier
string
// Errors is a collection of non-nil error values of Challenge items
// of the failed Authorization.
Errors
[]
error
}
func
(
a
*
AuthorizationError
)
Error
()
string
{
e
:=
make
([]
string
,
len
(
a
.
Errors
))
for
i
,
err
:=
range
a
.
Errors
{
e
[
i
]
=
err
.
Error
()
}
return
fmt
.
Sprintf
(
"acme: authorization error for %s: %s"
,
a
.
Identifier
,
strings
.
Join
(
e
,
"; "
))
}
// RateLimit reports whether err represents a rate limit error and
// any Retry-After duration returned by the server.
//
// See the following for more details on rate limiting:
// https://tools.ietf.org/html/draft-ietf-acme-acme-05#section-5.6
func
RateLimit
(
err
error
)
(
time
.
Duration
,
bool
)
{
e
,
ok
:=
err
.
(
*
Error
)
if
!
ok
{
return
0
,
false
}
// Some CA implementations may return incorrect values.
// Use case-insensitive comparison.
if
!
strings
.
HasSuffix
(
strings
.
ToLower
(
e
.
ProblemType
),
":ratelimited"
)
{
return
0
,
false
}
if
e
.
Header
==
nil
{
return
0
,
true
}
return
retryAfter
(
e
.
Header
.
Get
(
"Retry-After"
)),
true
}
// Account is a user account. It is associated with a private key.
type
Account
struct
{
// URI is the account unique ID, which is also a URL used to retrieve
// account data from the CA.
URI
string
// Contact is a slice of contact info used during registration.
Contact
[]
string
// The terms user has agreed to.
// A value not matching CurrentTerms indicates that the user hasn't agreed
// to the actual Terms of Service of the CA.
AgreedTerms
string
// Actual terms of a CA.
CurrentTerms
string
// Authz is the authorization URL used to initiate a new authz flow.
Authz
string
// Authorizations is a URI from which a list of authorizations
// granted to this account can be fetched via a GET request.
Authorizations
string
// Certificates is a URI from which a list of certificates
// issued for this account can be fetched via a GET request.
Certificates
string
}
// Directory is ACME server discovery data.
type
Directory
struct
{
// RegURL is an account endpoint URL, allowing for creating new
// and modifying existing accounts.
RegURL
string
// AuthzURL is used to initiate Identifier Authorization flow.
AuthzURL
string
// CertURL is a new certificate issuance endpoint URL.
CertURL
string
// RevokeURL is used to initiate a certificate revocation flow.
RevokeURL
string
// Term is a URI identifying the current terms of service.
Terms
string
// Website is an HTTP or HTTPS URL locating a website
// providing more information about the ACME server.
Website
string
// CAA consists of lowercase hostname elements, which the ACME server
// recognises as referring to itself for the purposes of CAA record validation
// as defined in RFC6844.
CAA
[]
string
}
// Challenge encodes a returned CA challenge.
// Its Error field may be non-nil if the challenge is part of an Authorization
// with StatusInvalid.
type
Challenge
struct
{
// Type is the challenge type, e.g. "http-01", "tls-sni-02", "dns-01".
Type
string
// URI is where a challenge response can be posted to.
URI
string
// Token is a random value that uniquely identifies the challenge.
Token
string
// Status identifies the status of this challenge.
Status
string
// Error indicates the reason for an authorization failure
// when this challenge was used.
// The type of a non-nil value is *Error.
Error
error
}
// Authorization encodes an authorization response.
type
Authorization
struct
{
// URI uniquely identifies a authorization.
URI
string
// Status identifies the status of an authorization.
Status
string
// Identifier is what the account is authorized to represent.
Identifier
AuthzID
// Challenges that the client needs to fulfill in order to prove possession
// of the identifier (for pending authorizations).
// For final authorizations, the challenges that were used.
Challenges
[]
*
Challenge
// A collection of sets of challenges, each of which would be sufficient
// to prove possession of the identifier.
// Clients must complete a set of challenges that covers at least one set.
// Challenges are identified by their indices in the challenges array.
// If this field is empty, the client needs to complete all challenges.
Combinations
[][]
int
}
// AuthzID is an identifier that an account is authorized to represent.
type
AuthzID
struct
{
Type
string
// The type of identifier, e.g. "dns".
Value
string
// The identifier itself, e.g. "example.org".
}
// wireAuthz is ACME JSON representation of Authorization objects.
type
wireAuthz
struct
{
Status
string
Challenges
[]
wireChallenge
Combinations
[][]
int
Identifier
struct
{
Type
string
Value
string
}
}
func
(
z
*
wireAuthz
)
authorization
(
uri
string
)
*
Authorization
{
a
:=
&
Authorization
{
URI
:
uri
,
Status
:
z
.
Status
,
Identifier
:
AuthzID
{
Type
:
z
.
Identifier
.
Type
,
Value
:
z
.
Identifier
.
Value
},
Combinations
:
z
.
Combinations
,
// shallow copy
Challenges
:
make
([]
*
Challenge
,
len
(
z
.
Challenges
)),
}
for
i
,
v
:=
range
z
.
Challenges
{
a
.
Challenges
[
i
]
=
v
.
challenge
()
}
return
a
}
func
(
z
*
wireAuthz
)
error
(
uri
string
)
*
AuthorizationError
{
err
:=
&
AuthorizationError
{
URI
:
uri
,
Identifier
:
z
.
Identifier
.
Value
,
}
for
_
,
raw
:=
range
z
.
Challenges
{
if
raw
.
Error
!=
nil
{
err
.
Errors
=
append
(
err
.
Errors
,
raw
.
Error
.
error
(
nil
))
}
}
return
err
}
// wireChallenge is ACME JSON challenge representation.
type
wireChallenge
struct
{
URI
string
`json:"uri"`
Type
string
Token
string
Status
string
Error
*
wireError
}
func
(
c
*
wireChallenge
)
challenge
()
*
Challenge
{
v
:=
&
Challenge
{
URI
:
c
.
URI
,
Type
:
c
.
Type
,
Token
:
c
.
Token
,
Status
:
c
.
Status
,
}
if
v
.
Status
==
""
{
v
.
Status
=
StatusPending
}
if
c
.
Error
!=
nil
{
v
.
Error
=
c
.
Error
.
error
(
nil
)
}
return
v
}
// wireError is a subset of fields of the Problem Details object
// as described in https://tools.ietf.org/html/rfc7807#section-3.1.
type
wireError
struct
{
Status
int
Type
string
Detail
string
}
func
(
e
*
wireError
)
error
(
h
http
.
Header
)
*
Error
{
return
&
Error
{
StatusCode
:
e
.
Status
,
ProblemType
:
e
.
Type
,
Detail
:
e
.
Detail
,
Header
:
h
,
}
}
// CertOption is an optional argument type for the TLS ChallengeCert methods for
// customizing a temporary certificate for TLS-based challenges.
type
CertOption
interface
{
privateCertOpt
()
}
// WithKey creates an option holding a private/public key pair.
// The private part signs a certificate, and the public part represents the signee.
func
WithKey
(
key
crypto
.
Signer
)
CertOption
{
return
&
certOptKey
{
key
}
}
type
certOptKey
struct
{
key
crypto
.
Signer
}
func
(
*
certOptKey
)
privateCertOpt
()
{}
// WithTemplate creates an option for specifying a certificate template.
// See x509.CreateCertificate for template usage details.
//
// In TLS ChallengeCert methods, the template is also used as parent,
// resulting in a self-signed certificate.
// The DNSNames field of t is always overwritten for tls-sni challenge certs.
func
WithTemplate
(
t
*
x509
.
Certificate
)
CertOption
{
return
(
*
certOptTemplate
)(
t
)
}
type
certOptTemplate
x509
.
Certificate
func
(
*
certOptTemplate
)
privateCertOpt
()
{}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment