-
-
Notifications
You must be signed in to change notification settings - Fork 512
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(wait): tls strategy #2896
Merged
Merged
feat(wait): tls strategy #2896
Changes from 1 commit
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
0bf53e2
feat: tls strategy and strategy walk
stevenh a5c2c0e
chore: add comment about file locations
stevenh 27489b5
chore(wait/tls): improve docs
stevenh 77cc34a
chore(wait/tls): added docs link
stevenh 58ba86c
chore(wait/tls): clarify cert in image
stevenh File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
# TLS Strategy | ||
|
||
TLS Strategy waits for one or more files to exist in the container and uses them | ||
and other details to construct a `tls.Config` which can be used to create secure | ||
connections. | ||
|
||
It supports: | ||
|
||
- x509 PEM Certificate loaded from a certificate / key file pair. | ||
- Root Certificate Authorities aka RootCAs loaded from PEM encoded files. | ||
- Server name. | ||
- Startup timeout to be used in seconds, default is 60 seconds. | ||
- Poll interval to be used in milliseconds, default is 100 milliseconds. | ||
|
||
## Waiting for certificate pair to exist and construct a tls.Config | ||
|
||
<!--codeinclude--> | ||
[Waiting for certificate pair to exist and construct a tls.Config](../../../wait/tls_test.go) inside_block:waitForTLSCert | ||
<!--/codeinclude--> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
-----BEGIN EC PRIVATE KEY----- | ||
MHcCAQEEIM8HuDwcZyVqBBy2C6db6zNb/dAJ69bq5ejAEz7qGOIQoAoGCCqGSM49 | ||
AwEHoUQDQgAEBL2ioRmfTc70WT0vyx+amSQOGbMeoMRAfF2qaPzpzOqpKTk0aLOG | ||
0735iy9Fz16PX4vqnLMiM/ZupugAhB//yA== | ||
-----END EC PRIVATE KEY----- |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
-----BEGIN CERTIFICATE----- | ||
MIIBxTCCAWugAwIBAgIUWBLNpiF1o4r+5ZXwawzPOfBM1F8wCgYIKoZIzj0EAwIw | ||
ADAeFw0yMDA4MTkxMzM4MDBaFw0zMDA4MTcxMzM4MDBaMBkxFzAVBgNVBAMTDnRl | ||
c3Rjb250YWluZXJzMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEBL2ioRmfTc70 | ||
WT0vyx+amSQOGbMeoMRAfF2qaPzpzOqpKTk0aLOG0735iy9Fz16PX4vqnLMiM/Zu | ||
pugAhB//yKOBqTCBpjAOBgNVHQ8BAf8EBAMCBaAwEwYDVR0lBAwwCgYIKwYBBQUH | ||
AwEwDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUTMdz5PIZ+Gix4jYUzRIHfByrW+Yw | ||
HwYDVR0jBBgwFoAUFdfV6PSYUlHs+lSQNouRwSfR2ZgwMQYDVR0RBCowKIIVdGVz | ||
dGNvbnRhaW5lci5nby50ZXN0gglsb2NhbGhvc3SHBH8AAAEwCgYIKoZIzj0EAwID | ||
SAAwRQIhAJznPNumi2Plf0GsP9DpC+8WukT/jUhnhcDWCfZ6Ini2AiBLhnhFebZX | ||
XWfSsdSNxIo20OWvy6z3wqdybZtRUfdU+g== | ||
-----END CERTIFICATE----- |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,167 @@ | ||
package wait | ||
|
||
import ( | ||
"context" | ||
"crypto/tls" | ||
"crypto/x509" | ||
"fmt" | ||
"io" | ||
"time" | ||
) | ||
|
||
// Validate we implement interface. | ||
var _ Strategy = (*TLSStrategy)(nil) | ||
|
||
// TLSStrategy is a strategy for handling TLS. | ||
type TLSStrategy struct { | ||
// General Settings. | ||
timeout *time.Duration | ||
pollInterval time.Duration | ||
|
||
// Custom Settings. | ||
certFiles *x509KeyPair | ||
rootFiles []string | ||
|
||
// State. | ||
tlsConfig *tls.Config | ||
} | ||
|
||
// x509KeyPair is a pair of certificate and key files. | ||
type x509KeyPair struct { | ||
certPEMFile string | ||
keyPEMFile string | ||
} | ||
|
||
// ForTLSCert returns a CertStrategy that will add a Certificate to the [tls.Config] | ||
// constructed from PEM formatted certificate key file pair in the container. | ||
func ForTLSCert(certPEMFile, keyPEMFile string) *TLSStrategy { | ||
return &TLSStrategy{ | ||
certFiles: &x509KeyPair{ | ||
certPEMFile: certPEMFile, | ||
keyPEMFile: keyPEMFile, | ||
}, | ||
tlsConfig: &tls.Config{}, | ||
pollInterval: defaultPollInterval(), | ||
} | ||
} | ||
|
||
// ForTLSRootCAs returns a CertStrategy that sets the root CAs for the [tls.Config] | ||
// using the given PEM formatted files from the container. | ||
func ForTLSRootCAs(pemFiles ...string) *TLSStrategy { | ||
return &TLSStrategy{ | ||
rootFiles: pemFiles, | ||
tlsConfig: &tls.Config{}, | ||
pollInterval: defaultPollInterval(), | ||
} | ||
} | ||
|
||
// WithRootCAs sets the root CAs for the [tls.Config] using the given files from | ||
// the container. | ||
func (ws *TLSStrategy) WithRootCAs(files ...string) *TLSStrategy { | ||
ws.rootFiles = files | ||
return ws | ||
} | ||
|
||
// WithCert sets the [tls.Config] Certificates using the given files from the container. | ||
func (ws *TLSStrategy) WithCert(certPEMFile, keyPEMFile string) *TLSStrategy { | ||
ws.certFiles = &x509KeyPair{ | ||
certPEMFile: certPEMFile, | ||
keyPEMFile: keyPEMFile, | ||
} | ||
return ws | ||
} | ||
|
||
// WithServerName sets the server for the [tls.Config]. | ||
func (ws *TLSStrategy) WithServerName(serverName string) *TLSStrategy { | ||
ws.tlsConfig.ServerName = serverName | ||
return ws | ||
} | ||
|
||
// WithStartupTimeout can be used to change the default startup timeout. | ||
func (ws *TLSStrategy) WithStartupTimeout(startupTimeout time.Duration) *TLSStrategy { | ||
ws.timeout = &startupTimeout | ||
return ws | ||
} | ||
|
||
// WithPollInterval can be used to override the default polling interval of 100 milliseconds. | ||
func (ws *TLSStrategy) WithPollInterval(pollInterval time.Duration) *TLSStrategy { | ||
ws.pollInterval = pollInterval | ||
return ws | ||
} | ||
|
||
// TLSConfig returns the TLS config once the strategy is ready. | ||
// If the strategy is nil, it returns nil. | ||
func (ws *TLSStrategy) TLSConfig() *tls.Config { | ||
if ws == nil { | ||
return nil | ||
} | ||
|
||
return ws.tlsConfig | ||
} | ||
|
||
// WaitUntilReady implements the [Strategy] interface. | ||
// It waits for the CA, client cert and key files to be available in the container and | ||
// uses them to setup the TLS config. | ||
func (ws *TLSStrategy) WaitUntilReady(ctx context.Context, target StrategyTarget) error { | ||
size := len(ws.rootFiles) | ||
if ws.certFiles != nil { | ||
size += 2 | ||
} | ||
strategies := make([]Strategy, 0, size) | ||
for _, file := range ws.rootFiles { | ||
strategies = append(strategies, | ||
ForFile(file).WithMatcher(func(r io.Reader) error { | ||
buf, err := io.ReadAll(r) | ||
if err != nil { | ||
return fmt.Errorf("read CA cert file %q: %w", file, err) | ||
} | ||
|
||
if ws.tlsConfig.RootCAs == nil { | ||
ws.tlsConfig.RootCAs = x509.NewCertPool() | ||
} | ||
|
||
if !ws.tlsConfig.RootCAs.AppendCertsFromPEM(buf) { | ||
return fmt.Errorf("invalid CA cert file %q", file) | ||
} | ||
|
||
return nil | ||
}).WithPollInterval(ws.pollInterval), | ||
) | ||
} | ||
|
||
if ws.certFiles != nil { | ||
var certPEMBlock []byte | ||
strategies = append(strategies, | ||
ForFile(ws.certFiles.certPEMFile).WithMatcher(func(r io.Reader) error { | ||
var err error | ||
if certPEMBlock, err = io.ReadAll(r); err != nil { | ||
return fmt.Errorf("read certificate cert %q: %w", ws.certFiles.certPEMFile, err) | ||
} | ||
|
||
return nil | ||
}).WithPollInterval(ws.pollInterval), | ||
ForFile(ws.certFiles.keyPEMFile).WithMatcher(func(r io.Reader) error { | ||
keyPEMBlock, err := io.ReadAll(r) | ||
if err != nil { | ||
return fmt.Errorf("read certificate key %q: %w", ws.certFiles.keyPEMFile, err) | ||
} | ||
|
||
cert, err := tls.X509KeyPair(certPEMBlock, keyPEMBlock) | ||
if err != nil { | ||
return fmt.Errorf("x509 key pair %q %q: %w", ws.certFiles.certPEMFile, ws.certFiles.keyPEMFile, err) | ||
} | ||
|
||
ws.tlsConfig.Certificates = []tls.Certificate{cert} | ||
|
||
return nil | ||
}).WithPollInterval(ws.pollInterval), | ||
) | ||
} | ||
|
||
strategy := ForAll(strategies...) | ||
if ws.timeout != nil { | ||
strategy.WithStartupTimeout(*ws.timeout) | ||
} | ||
|
||
return strategy.WaitUntilReady(ctx, target) | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
question: do you think it would be interesting to provide a way for consumers to forget about generating certs and the library build them on the fly? Something like
wait.ForTLSTestCert
? The library would generate the file under the hood.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.
Yep love that idea, something for a follow up PR.