-
Notifications
You must be signed in to change notification settings - Fork 23
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
ACME client integration into webpkgserver. #53
Conversation
cc:@twifkak |
a6c457f
to
64d31ef
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mostly good. Sorry for slow response.
"io/ioutil" | ||
"net/http" | ||
"net/url" | ||
"os" | ||
|
||
"github.com/WICG/webpackage/go/signedexchange" | ||
"github.com/google/webpackager/certchain" | ||
"github.com/pkg/errors" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't use pkg/errors. Use xerrors instead.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Apologies, I forgot about this. Fixed.
server/from_toml.go
Outdated
mc := certmanager.Config{ | ||
RawChainSource: certmanager.NewLocalCertFile( | ||
var rcs certmanager.RawChainSource | ||
rcs = certmanager.NewLocalCertFile( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks to be unused.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, already removed but perhaps you saw an older copy.
@@ -77,6 +80,24 @@ func ReadPrivateKeyFile(filename string) (crypto.PrivateKey, error) { | |||
return signedexchange.ParsePrivateKey(pem) | |||
} | |||
|
|||
// ReadCertificateRequestFile reads a PEM file and returns an x508 Certificate |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"x508" looks like typo. It should be "x509" or "X.509", but I guess we can just remove it given (IIRC) we call it just "Certificate Request" elsewhere.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed.
server/from_toml.go
Outdated
return nil, err | ||
} | ||
|
||
rcs, err = acmeclient.NewClient( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consistency:
rcs, err = acmeclient.NewClient(
acmeclient.Config{
// ...
}, // Note the trailing comma.
)
OR
rcs, err = acmeclient.NewClient(acmeclient.Config{
// ...
})
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed (2nd choice).
server/from_toml.go
Outdated
return nil, err | ||
} | ||
} else { | ||
rcs = certmanager.NewLocalCertFile( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
func ReadCertificateRequestFile(filename string) (*x509.CertificateRequest, error) { | ||
data, err := ioutil.ReadFile(filename) | ||
if err != nil { | ||
return nil, errors.Wrapf(err, "reading %s", filename) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need to wrap. ioutil.ReadFile
(naturally) calls os.Open
internally, os.Open
always returns *os.PathError
, thus the message already contains the filename.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
} | ||
block, _ := pem.Decode(data) | ||
if block == nil { | ||
return nil, errors.Errorf("pem decode: no key found in %s", filename) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does "no key found" describe the problem correctly?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
@@ -130,13 +127,18 @@ func (a *Augmentor) daemon(rcNext, orNext futureevent.Event) { | |||
orNext, err = a.maintainOCSPResp() | |||
if err != nil { | |||
log.Printf("cannot update the OCSP response: %v", err) | |||
} else { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that I added these log statements to indicate that the OCSP retrieval was successful after several failed retries.
@@ -107,23 +107,23 @@ func (c OCSPClient) Fetch(chain *certchain.RawChain, now func() time.Time) (ocsp | |||
|
|||
raw, expiry, err := c.fetch(chain) | |||
if err != nil { | |||
err = fmt.Errorf("cannot fetch OCSP response: %v", err) | |||
err = fmt.Errorf("cannot fetch OCSP response: %v, retrying in %v", err, retryWait) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I also added the amount of time before next retry in the error, because it was useful to know how long the next retry would be.
@@ -107,9 +107,6 @@ func (a *Augmentor) Start() error { | |||
return err | |||
} | |||
orNext, err := a.maintainOCSPResp() | |||
if err != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yusuke, I had to remove this check and return statement. In the event that during start-up, we had an expired certificate and ACME retrieved it, it is very common for DigiCert OCSP response to fail for a period of time (due to updates of their data in the CDN). In this case, it will be beneficial to retry until we get the proper OCSP response. In the previous state of the code, no retry was attempted and the server exits with an error.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I understand there is a problem but I'm not fully sure if you had to ― I don't think dropping this if statement is the only option. At the very least the code is now violating the contract written in GoDoc: "It produces the first AugmentedChain before starting the goroutine and blocks until it is ready." This change can introduce subtle bugs since the rest of codebase is written with relying on that contract.
I have to say removing this check is over-focused on the DigiCert+ACME case. With this change Augmentor won't be able to handle some bad certificates in a proper way, including:
- those truly revoked, for which the OCSP responder will never return okay.
- those with a bogus OCSP URI, which can't be accessed in the first place.
The most straightforward approach is to do a "white-true" loop to wait until the function receives a valid ocsp response:
var orNext futureevent.Event
for {
orNext,err = a.maintainOCSPResp()
if err == nil {
break
}
log.Print(err)
<-orNext.Chan() // I don't mean a busy loop.
}
If possible, it's nicer to determine whether the failure is transient or permanent, and exit the loop on a permanent error.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the comments. Updated to your suggested logic but added a retry counter so that it can break out of the loop after N retries.
96d516f
to
95abb3a
Compare
certchain/certmanager/augmentor.go
Outdated
|
||
var orNext futureevent.Event | ||
i := 0 | ||
maxRetries := 3 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Make it a const.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
certchain/certmanager/augmentor.go
Outdated
} | ||
if i >= maxRetries { | ||
log.Println("Exceeded number of retries for OCSP.") | ||
break |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
return err
. otherwise my point is not (fully) addressed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
certchain/certmanager/augmentor.go
Outdated
return err | ||
|
||
var orNext futureevent.Event | ||
i := 0 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Give a little more meaningful name, say, retryCount
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
server/from_toml.go
Outdated
OCSPRespSource: certmanager.NewOCSPClient( | ||
certmanager.OCSPClientConfig{ | ||
AllowTestCert: c.SXG.Cert.AllowTestCert, | ||
}, | ||
), | ||
} | ||
if c.SXG.Cert.CacheDir == "" { | ||
if c.SXG.Cert.CacheDir != "" { | ||
fmt.Printf("Creating SXG Cert Cache Directory: %s\n", c.SXG.Cert.CacheDir) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"Creating SXG cert cache directory" ― sentence case rather than title case. Maybe s/cert/certificate/.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
@@ -77,6 +80,24 @@ func ReadPrivateKeyFile(filename string) (crypto.PrivateKey, error) { | |||
return signedexchange.ParsePrivateKey(pem) | |||
} | |||
|
|||
// ReadCertificateRequestFile reads a PEM file and returns a Certificate | |||
// Request usable for requesting Signed Exchange certificates. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
GoDoc should be just "... returns a Certificate Request." This function checks nothing of SXG compatibility.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
certchain/certmanager/augmentor.go
Outdated
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you find this empty line increase the readability, add another above the line case <-orNext.Chan()
for consistency. Otherwise, remove this empty line.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed.
certchain/certmanager/augmentor.go
Outdated
|
||
var orNext futureevent.Event | ||
retryCount := 0 | ||
const maxRetries = 3 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe maxRetryCount
(for consistency with retryCount
). Alternative is to rename retryCount
to retries
, but I think retryCount
is more descriptive.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
Integrate support for ACME client into webpkgserver start-up.