Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
C
caddy
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
caddy
Commits
8eefeb67
Commit
8eefeb67
authored
Aug 09, 2016
by
Matthew Holt
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Begin improved OCSP stapling by persisting staple to disk
parent
5fb3c504
Changes
3
Show whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
95 additions
and
25 deletions
+95
-25
caddytls/certificates.go
caddytls/certificates.go
+3
-10
caddytls/crypto.go
caddytls/crypto.go
+76
-6
caddytls/maintain.go
caddytls/maintain.go
+16
-9
No files found.
caddytls/certificates.go
View file @
8eefeb67
...
...
@@ -10,7 +10,6 @@ import (
"sync"
"time"
"github.com/xenolf/lego/acme"
"golang.org/x/crypto/ocsp"
)
...
...
@@ -183,19 +182,13 @@ func makeCertificate(certPEMBlock, keyPEMBlock []byte) (Certificate, error) {
}
}
cert
.
NotAfter
=
leaf
.
NotAfter
cert
.
Certificate
=
tlsCert
// Staple OCSP
ocspBytes
,
ocspResp
,
err
:=
acme
.
GetOCSPForCert
(
certPEMBlock
)
err
=
stapleOCSP
(
&
cert
,
certPEMBlock
)
if
err
!=
nil
{
// An error here is not a problem because a certificate may simply
// not contain a link to an OCSP server. But we should log it anyway.
log
.
Printf
(
"[WARNING] No OCSP stapling for %v: %v"
,
cert
.
Names
,
err
)
}
else
if
ocspResp
.
Status
==
ocsp
.
Good
{
tlsCert
.
OCSPStaple
=
ocspBytes
cert
.
OCSP
=
ocspResp
log
.
Printf
(
"[WARNING] Stapling OCSP: %v"
,
err
)
}
cert
.
Certificate
=
tlsCert
return
cert
,
nil
}
...
...
caddytls/crypto.go
View file @
8eefeb67
...
...
@@ -13,11 +13,19 @@ import (
"encoding/pem"
"errors"
"fmt"
"hash/fnv"
"io"
"io/ioutil"
"log"
"math/big"
"net"
"os"
"path/filepath"
"time"
"golang.org/x/crypto/ocsp"
"github.com/mholt/caddy"
"github.com/xenolf/lego/acme"
)
...
...
@@ -59,7 +67,7 @@ func savePrivateKey(key crypto.PrivateKey) ([]byte, error) {
// stapleOCSP staples OCSP information to cert for hostname name.
// If you have it handy, you should pass in the PEM-encoded certificate
// bundle; otherwise the DER-encoded cert will have to be PEM-encoded.
// If you don't have the PEM blocks
han
dy, just pass in nil.
// If you don't have the PEM blocks
alrea
dy, just pass in nil.
//
// Errors here are not necessarily fatal, it could just be that the
// certificate doesn't have an issuer URL.
...
...
@@ -73,13 +81,66 @@ func stapleOCSP(cert *Certificate, pemBundle []byte) error {
pemBundle
=
bundle
.
Bytes
()
}
ocspBytes
,
ocspResp
,
err
:=
acme
.
GetOCSPForCert
(
pemBundle
)
var
ocspBytes
[]
byte
var
ocspResp
*
ocsp
.
Response
var
ocspErr
error
var
gotNewOCSP
bool
// First try to load OCSP staple from storage and see if
// we can still use it.
// TODO: Use Storage interface instead of disk directly
ocspFolder
:=
filepath
.
Join
(
caddy
.
AssetsPath
(),
"ocsp"
)
ocspFileName
:=
cert
.
Names
[
0
]
+
"-"
+
fastHash
(
pemBundle
)
ocspCachePath
:=
filepath
.
Join
(
ocspFolder
,
ocspFileName
)
cachedOCSP
,
err
:=
ioutil
.
ReadFile
(
ocspCachePath
)
if
err
==
nil
{
resp
,
err
:=
ocsp
.
ParseResponse
(
cachedOCSP
,
nil
)
if
err
==
nil
{
if
freshOCSP
(
resp
)
{
// staple is still fresh; use it
ocspBytes
=
cachedOCSP
ocspResp
=
resp
}
}
else
{
// invalid contents; delete the file
err
:=
os
.
Remove
(
ocspCachePath
)
if
err
!=
nil
{
return
err
log
.
Printf
(
"[WARNING] Unable to delete invalid OCSP staple file: %v"
,
err
)
}
}
}
// If we couldn't get a fresh staple by reading the cache,
// then we need to request it from the OCSP responder
if
ocspResp
==
nil
||
len
(
ocspBytes
)
==
0
{
ocspBytes
,
ocspResp
,
ocspErr
=
acme
.
GetOCSPForCert
(
pemBundle
)
if
ocspErr
!=
nil
{
// An error here is not a problem because a certificate may simply
// not contain a link to an OCSP server. But we should log it anyway.
// There's nothing else we can do to get OCSP for this certificate,
// so we can return here with the error.
return
fmt
.
Errorf
(
"no OCSP stapling for %v: %v"
,
cert
.
Names
,
ocspErr
)
}
gotNewOCSP
=
true
}
// By now, we should have a response. If good, staple it to
// the certificate. If the OCSP response was not loaded from
// storage, we persist it for next time.
if
ocspResp
.
Status
==
ocsp
.
Good
{
cert
.
Certificate
.
OCSPStaple
=
ocspBytes
cert
.
OCSP
=
ocspResp
if
gotNewOCSP
{
err
:=
os
.
MkdirAll
(
filepath
.
Join
(
caddy
.
AssetsPath
(),
"ocsp"
),
0700
)
if
err
!=
nil
{
return
fmt
.
Errorf
(
"unable to make OCSP staple path for %v: %v"
,
cert
.
Names
,
err
)
}
err
=
ioutil
.
WriteFile
(
ocspCachePath
,
ocspBytes
,
0644
)
if
err
!=
nil
{
return
fmt
.
Errorf
(
"unable to write OCSP staple file for %v: %v"
,
cert
.
Names
,
err
)
}
}
}
return
nil
}
...
...
@@ -235,6 +296,15 @@ func standaloneTLSTicketKeyRotation(c *tls.Config, ticker *time.Ticker, exitChan
}
}
// fastHash hashes input using a hashing algorithm that
// is fast, and returns the hash as a hex-encoded string.
// Do not use this for cryptographic purposes.
func
fastHash
(
input
[]
byte
)
string
{
h
:=
fnv
.
New32a
()
h
.
Write
([]
byte
(
input
))
return
fmt
.
Sprintf
(
"%x"
,
h
.
Sum32
())
}
const
(
// NumTickets is how many tickets to hold and consider
// to decrypt TLS sessions.
...
...
caddytls/maintain.go
View file @
8eefeb67
...
...
@@ -17,11 +17,11 @@ const (
// RenewInterval is how often to check certificates for renewal.
RenewInterval
=
12
*
time
.
Hour
// OCSPInterval is how often to check if OCSP stapling needs updating.
OCSPInterval
=
1
*
time
.
Hour
// RenewDurationBefore is how long before expiration to renew certificates.
RenewDurationBefore
=
(
24
*
time
.
Hour
)
*
30
// OCSPInterval is how often to check if OCSP stapling needs updating.
OCSPInterval
=
1
*
time
.
Hour
)
// maintainAssets is a permanently-blocking function
...
...
@@ -154,6 +154,10 @@ func RenewManagedCertificates(allowPrompts bool) (err error) {
// UpdateOCSPStaples updates the OCSP stapling in all
// eligible, cached certificates.
//
// OCSP maintenance strives to abide the relevant points on
// Ryan Sleevi's recommendations for good OCSP support:
// https://gist.github.com/sleevi/5efe9ef98961ecfb4da8
func
UpdateOCSPStaples
()
{
// Create a temporary place to store updates
// until we release the potentially long-lived
...
...
@@ -187,12 +191,9 @@ func UpdateOCSPStaples() {
var
lastNextUpdate
time
.
Time
if
cert
.
OCSP
!=
nil
{
// start checking OCSP staple about halfway through validity period for good measure
lastNextUpdate
=
cert
.
OCSP
.
NextUpdate
refreshTime
:=
cert
.
OCSP
.
ThisUpdate
.
Add
(
lastNextUpdate
.
Sub
(
cert
.
OCSP
.
ThisUpdate
)
/
2
)
// since OCSP is already stapled, we need only check if we're in that "refresh window"
if
time
.
Now
()
.
Before
(
refreshTime
)
{
if
freshOCSP
(
cert
.
OCSP
)
{
// no need to update staple if ours is still fresh
continue
}
}
...
...
@@ -201,7 +202,7 @@ func UpdateOCSPStaples() {
if
err
!=
nil
{
if
cert
.
OCSP
!=
nil
{
// if there was no staple before, that's fine; otherwise we should log the error
log
.
Printf
(
"[ERROR] Checking OCSP
for %v: %v"
,
cert
.
Names
,
err
)
log
.
Printf
(
"[ERROR] Checking OCSP
: %v"
,
err
)
}
continue
}
...
...
@@ -229,3 +230,9 @@ func UpdateOCSPStaples() {
}
certCacheMu
.
Unlock
()
}
func
freshOCSP
(
resp
*
ocsp
.
Response
)
bool
{
// start checking OCSP staple about halfway through validity period for good measure
refreshTime
:=
resp
.
ThisUpdate
.
Add
(
resp
.
NextUpdate
.
Sub
(
resp
.
ThisUpdate
)
/
2
)
return
time
.
Now
()
.
Before
(
refreshTime
)
}
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