Skip to content
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

feat: hot-reload TLS certificate #3265

Merged
merged 5 commits into from Oct 4, 2022
Merged

feat: hot-reload TLS certificate #3265

merged 5 commits into from Oct 4, 2022

Conversation

alnr
Copy link
Contributor

@alnr alnr commented Sep 22, 2022

Automatic TLS certificate hot-reloading when the cert changes on disk.

#2850

@codecov
Copy link

codecov bot commented Sep 22, 2022

Codecov Report

Merging #3265 (6d0c63c) into master (0b643a3) will increase coverage by 0.05%.
The diff coverage is 47.91%.

❗ Current head 6d0c63c differs from pull request most recent head ee88061. Consider uploading reports for the commit ee88061 to get more accurate results

@@            Coverage Diff             @@
##           master    #3265      +/-   ##
==========================================
+ Coverage   76.80%   76.85%   +0.05%     
==========================================
  Files         123      123              
  Lines        8847     8875      +28     
==========================================
+ Hits         6795     6821      +26     
  Misses       1627     1627              
- Partials      425      427       +2     
Impacted Files Coverage Δ
cmd/server/handler.go 1.36% <0.00%> (-0.03%) ⬇️
cmd/server/helper_cert.go 26.08% <26.31%> (+12.13%) ⬆️
driver/config/tls.go 85.71% <78.26%> (ø)
persistence/sql/persister_oauth2.go 82.88% <0.00%> (+0.76%) ⬆️

Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here.

@alnr alnr marked this pull request as ready for review September 23, 2022 13:53
@alnr alnr requested a review from aeneasr as a code owner September 23, 2022 13:53
@alnr alnr changed the title feat: TLS certificate hot-reloading feat: hot-reload TLS certificate Sep 26, 2022
Copy link
Member

@aeneasr aeneasr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please always return the error if it is available. In a possible scenario, someone could accidentally overwrite the hook to not fatal, which would then end up in incorrectly returning a nil error, most likely causing a panic somewhere down the line. Thanks!

priv, err := jwk.GetOrGenerateKeys(ctx, d, d.SoftwareKeyManager(), TlsKeyName, uuid.Must(uuid.NewV4()).String(), "RS256")
if err != nil {
d.Logger().WithError(err).Fatal("Unable to fetch or generate HTTPS TLS key pair")
return nil // in case Fatal is hooked
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should instead return the error

} else if !errors.Is(err, tlsx.ErrNoCertificatesConfigured) {
d.Logger().WithError(err).Fatalf("Unable to load HTTPS TLS Certificate")
d.Logger().WithError(err).Fatal("Unable to load HTTPS TLS Certificate")
return nil // in case Fatal is hooked
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should instead return the error

}

if len(priv.Certificates) == 0 {
cert, err := tlsx.CreateSelfSignedCertificate(priv.Key)
if err != nil {
d.Logger().WithError(err).Fatalf(`Could not generate a self signed TLS certificate`)
d.Logger().WithError(err).Fatal(`Could not generate a self signed TLS certificate`)
return nil // in case Fatal is hooked
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should instead return the error

}

AttachCertificate(priv, cert)
if err := d.SoftwareKeyManager().DeleteKey(ctx, TlsKeyName, priv.KeyID); err != nil {
d.Logger().WithError(err).Fatal(`Could not update (delete) the self signed TLS certificate`)
return nil // in case Fatal is hooked
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should instead return the error

}

if err := d.SoftwareKeyManager().AddKey(ctx, TlsKeyName, priv); err != nil {
d.Logger().WithError(err).Fatalf(`Could not update (add) the self signed TLS certificate: %s %x %d`, cert.SignatureAlgorithm, cert.Signature, len(cert.Signature))
return nil // in case Fatalf is hooked
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should instead return the error

}

pemCert := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: priv.Certificates[0].Raw})
pemKey := pem.EncodeToMemory(block)
ct, err := tls.X509KeyPair(pemCert, pemKey)
if err != nil {
d.Logger().WithError(err).Fatalf("Could not decode certificate")
d.Logger().WithError(err).Fatal("Could not decode certificate")
return nil // in case Fatal is hooked
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should instead return the error

}

if len(priv.Certificates) == 0 {
d.Logger().Fatal("TLS certificate chain can not be empty")
return nil // in case Fatal is hooked
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should instead return the error

}
}

block, err := jwk.PEMBlockForKey(priv.Key)
if err != nil {
d.Logger().WithError(err).Fatalf("Could not encode key to PEM")
d.Logger().WithError(err).Fatal("Could not encode key to PEM")
return nil // in case Fatal is hooked
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should instead return the error

@alnr
Copy link
Contributor Author

alnr commented Oct 4, 2022

Please always return the error if it is available. In a possible scenario, someone could accidentally overwrite the hook to not fatal, which would then end up in incorrectly returning a nil error, most likely causing a panic somewhere down the line. Thanks!

I did not change the interface semantics of GetOrCreateTLSCertificate it this PR: it is designed to terminate the process through the logger's ExitFunc if any error is encountered. That may or may not be the best design, but I decided to keep the semantics in this PR.

Without the return nil lines added in this PR, the logic was super broken if ExitFunc was hooked as the execution would simply continue nonsensically.

We have two options:

  • replace return nil by panic() to guarantee the execution doesn't continue after logger.Fatalf() (easy fix)
  • change the interface of GetOrCreateTLSCertificate to return an error in addition to the certificate info, and leave the logging to the caller (more involved fix).

@aeneasr
Copy link
Member

aeneasr commented Oct 4, 2022

Ah, my bad, I overlooked that we return the certificate here and not the error. Ok, then this behavior is acceptable :)

@aeneasr aeneasr merged commit 1d13be6 into ory:master Oct 4, 2022
@StarAurryon StarAurryon mentioned this pull request Oct 6, 2023
7 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants