-
Notifications
You must be signed in to change notification settings - Fork 25
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
Implement mTLS resources and configuration for Target Allocator server #284
Open
musa-asad
wants to merge
21
commits into
main
Choose a base branch
from
mtls
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+628
−97
Open
Changes from all commits
Commits
Show all changes
21 commits
Select commit
Hold shift + click to select a range
273c558
Implement mTLS for Target Allocator server
musa-asad 5877fef
revert docs/api.md
musa-asad 6808981
add logging
musa-asad 1f0f0d9
set certs initially
musa-asad dfd7ce6
fix nil
musa-asad 0b8d26c
fix nil
musa-asad 0cb2e1d
simplify config
musa-asad 91f3008
clean
musa-asad 397b8dc
allow dynamic updates to CA pool
musa-asad 00911e4
add tls_config
musa-asad 3487d4a
add tls_config
musa-asad 62f794d
revert
musa-asad a713e78
add tls_config
musa-asad dd199c2
revert
musa-asad 08af589
fix typo
musa-asad 7771eac
temp
musa-asad f8440f2
temp
musa-asad 223ef59
add unit test
musa-asad d08dfb6
add unit test
musa-asad d804cd0
fix lint
musa-asad d6c5c6d
remove comment
musa-asad 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
113 changes: 113 additions & 0 deletions
113
cmd/amazon-cloudwatch-agent-target-allocator/config/certwatcher.go
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,113 @@ | ||
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
package config | ||
|
||
import ( | ||
"context" | ||
"crypto/tls" | ||
"crypto/x509" | ||
"fmt" | ||
"os" | ||
"sync" | ||
"time" | ||
|
||
"github.com/fsnotify/fsnotify" | ||
"sigs.k8s.io/controller-runtime/pkg/certwatcher" | ||
) | ||
|
||
type CertAndCAWatcher struct { | ||
certWatcher *certwatcher.CertWatcher | ||
|
||
caFilePath string | ||
caPool *x509.CertPool | ||
caWatcher *fsnotify.Watcher | ||
|
||
mu sync.RWMutex | ||
} | ||
|
||
func NewCertAndCAWatcher(certPath, keyPath, caPath string) (*CertAndCAWatcher, error) { | ||
certWatcher, err := certwatcher.New(certPath, keyPath) | ||
if err != nil { | ||
return nil, fmt.Errorf("error creating cert watcher: %w", err) | ||
} | ||
|
||
caPool, err := loadCAPool(caPath) | ||
if err != nil { | ||
return nil, fmt.Errorf("error loading CA pool: %w", err) | ||
} | ||
|
||
caWatcher, err := fsnotify.NewWatcher() | ||
if err != nil { | ||
return nil, fmt.Errorf("error creating CA file watcher: %w", err) | ||
} | ||
if err := caWatcher.Add(caPath); err != nil { | ||
return nil, fmt.Errorf("error adding CA file to watcher: %w", err) | ||
} | ||
|
||
return &CertAndCAWatcher{ | ||
certWatcher: certWatcher, | ||
caFilePath: caPath, | ||
caPool: caPool, | ||
caWatcher: caWatcher, | ||
}, nil | ||
} | ||
|
||
func loadCAPool(caPath string) (*x509.CertPool, error) { | ||
caCert, err := os.ReadFile(caPath) | ||
caCertPool := x509.NewCertPool() | ||
if err != nil { | ||
return nil, fmt.Errorf("error reading CA file: %w", err) | ||
} | ||
caCertPool.AppendCertsFromPEM(caCert) | ||
return caCertPool, nil | ||
} | ||
|
||
func (w *CertAndCAWatcher) Start(ctx context.Context) error { | ||
go func() { | ||
_ = w.certWatcher.Start(ctx) | ||
}() | ||
|
||
go w.watchCA(ctx) | ||
|
||
<-ctx.Done() | ||
return nil | ||
} | ||
|
||
func (w *CertAndCAWatcher) watchCA(ctx context.Context) { | ||
for { | ||
select { | ||
case event, ok := <-w.caWatcher.Events: | ||
if !ok { | ||
return | ||
} | ||
if event.Op.Has(fsnotify.Write) || event.Op.Has(fsnotify.Create) || event.Op.Has(fsnotify.Remove) { | ||
newPool, err := loadCAPool(w.caFilePath) | ||
if err != nil { | ||
continue | ||
} | ||
w.mu.Lock() | ||
w.caPool = newPool | ||
w.mu.Unlock() | ||
|
||
// needed incase file removed | ||
if event.Op.Has(fsnotify.Remove) { | ||
time.Sleep(100 * time.Millisecond) | ||
_ = w.caWatcher.Add(w.caFilePath) | ||
} | ||
} | ||
case <-ctx.Done(): | ||
return | ||
} | ||
} | ||
} | ||
|
||
func (w *CertAndCAWatcher) GetCertificate(clientHello *tls.ClientHelloInfo) (*tls.Certificate, error) { | ||
return w.certWatcher.GetCertificate(clientHello) | ||
} | ||
|
||
func (w *CertAndCAWatcher) GetCAPool() *x509.CertPool { | ||
w.mu.RLock() | ||
defer w.mu.RUnlock() | ||
return w.caPool | ||
} |
140 changes: 140 additions & 0 deletions
140
cmd/amazon-cloudwatch-agent-target-allocator/config/certwatcher_test.go
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,140 @@ | ||
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
package config | ||
|
||
import ( | ||
"bytes" | ||
"context" | ||
"crypto/rand" | ||
"crypto/rsa" | ||
"crypto/x509" | ||
"crypto/x509/pkix" | ||
"encoding/pem" | ||
"math/big" | ||
"os" | ||
"path/filepath" | ||
"testing" | ||
"time" | ||
) | ||
|
||
func generateSelfSignedCertAndKey(commonName string) (certPEM, keyPEM []byte, err error) { | ||
// Generate RSA key | ||
priv, err := rsa.GenerateKey(rand.Reader, 2048) | ||
if err != nil { | ||
return nil, nil, err | ||
} | ||
|
||
// Create a minimal self-signed certificate template | ||
serial, err := rand.Int(rand.Reader, big.NewInt(1<<63-1)) | ||
if err != nil { | ||
return nil, nil, err | ||
} | ||
|
||
template := &x509.Certificate{ | ||
SerialNumber: serial, | ||
Subject: pkix.Name{ | ||
CommonName: commonName, | ||
}, | ||
NotBefore: time.Now().Add(-time.Hour), | ||
NotAfter: time.Now().Add(time.Hour), | ||
|
||
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment | x509.KeyUsageCertSign, | ||
IsCA: true, | ||
BasicConstraintsValid: true, | ||
} | ||
|
||
// Self-sign the certificate | ||
der, err := x509.CreateCertificate(rand.Reader, template, template, &priv.PublicKey, priv) | ||
if err != nil { | ||
return nil, nil, err | ||
} | ||
|
||
// Encode cert + key to PEM | ||
var certBuf, keyBuf bytes.Buffer | ||
err = pem.Encode(&certBuf, &pem.Block{Type: "CERTIFICATE", Bytes: der}) | ||
if err != nil { | ||
return nil, nil, err | ||
} | ||
err = pem.Encode(&keyBuf, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(priv)}) | ||
if err != nil { | ||
return nil, nil, err | ||
} | ||
|
||
return certBuf.Bytes(), keyBuf.Bytes(), nil | ||
} | ||
|
||
func TestCertAndCAWatcher_UpdatesCA(t *testing.T) { | ||
t.Parallel() | ||
|
||
// Generate a server cert/key for certwatcher | ||
certPEM, keyPEM, err := generateSelfSignedCertAndKey("test-server") | ||
if err != nil { | ||
t.Fatalf("failed to generate server cert/key: %v", err) | ||
} | ||
|
||
// Generate two distinct self-signed certs to represent old CA vs new CA | ||
oldCAPEM, _, err := generateSelfSignedCertAndKey("old-ca") | ||
if err != nil { | ||
t.Fatalf("failed to generate old CA: %v", err) | ||
} | ||
newCAPEM, _, err := generateSelfSignedCertAndKey("new-ca") | ||
if err != nil { | ||
t.Fatalf("failed to generate new CA: %v", err) | ||
} | ||
|
||
// Write all these PEM files into a temp dir | ||
tmpDir := t.TempDir() | ||
|
||
certPath := filepath.Join(tmpDir, "tls.crt") | ||
keyPath := filepath.Join(tmpDir, "tls.key") | ||
caPath := filepath.Join(tmpDir, "ca.crt") | ||
|
||
if err := os.WriteFile(certPath, certPEM, 0600); err != nil { | ||
t.Fatalf("failed to write cert file: %v", err) | ||
} | ||
if err := os.WriteFile(keyPath, keyPEM, 0600); err != nil { | ||
t.Fatalf("failed to write key file: %v", err) | ||
} | ||
if err := os.WriteFile(caPath, oldCAPEM, 0600); err != nil { | ||
t.Fatalf("failed to write initial CA file: %v", err) | ||
} | ||
|
||
// Create the CertAndCAWatcher using our files | ||
watcher, err := NewCertAndCAWatcher(certPath, keyPath, caPath) | ||
if err != nil { | ||
t.Fatalf("failed to create CertAndCAWatcher: %v", err) | ||
} | ||
|
||
// Start the watcher in the background | ||
ctx, cancel := context.WithCancel(context.Background()) | ||
defer cancel() | ||
go func() { | ||
_ = watcher.Start(ctx) | ||
}() | ||
|
||
// Record the initial CA pool pointer | ||
oldPool := watcher.GetCAPool() | ||
if oldPool == nil { | ||
t.Fatal("expected non-nil initial CA pool") | ||
} | ||
|
||
// Overwrite the CA file with newCAPEM, triggering a reload | ||
if err := os.WriteFile(caPath, newCAPEM, 0600); err != nil { | ||
t.Fatalf("failed to write new CA file: %v", err) | ||
} | ||
|
||
// Loop until the watcher updates the CA pool (or times out) | ||
deadline := time.Now().Add(2 * time.Second) | ||
for { | ||
newPool := watcher.GetCAPool() | ||
if newPool != oldPool { | ||
t.Log("CA pool successfully updated.") | ||
return | ||
} | ||
if time.Now().After(deadline) { | ||
t.Fatal("timed out waiting for CA pool to be updated") | ||
} | ||
time.Sleep(100 * time.Millisecond) | ||
} | ||
} |
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
Oops, something went wrong.
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.
Temporarily needs to be added since their certificate expired, which is breaking our workflow.