controllers: test HelmRepository TLS auth
This commit is contained in:
parent
1cc6464b73
commit
3c70c8d333
|
@ -82,6 +82,10 @@ func (r *HelmRepositoryReconciler) Reconcile(req ctrl.Request) (ctrl.Result, err
|
|||
syncedRepo, err := r.sync(*repository.DeepCopy())
|
||||
if err != nil {
|
||||
log.Error(err, "Helm repository sync failed")
|
||||
if err := r.Status().Update(ctx, &syncedRepo); err != nil {
|
||||
log.Error(err, "unable to update HelmRepository status")
|
||||
}
|
||||
return ctrl.Result{Requeue: true}, err
|
||||
}
|
||||
|
||||
// update status
|
||||
|
@ -89,7 +93,6 @@ func (r *HelmRepositoryReconciler) Reconcile(req ctrl.Request) (ctrl.Result, err
|
|||
log.Error(err, "unable to update HelmRepository status")
|
||||
return ctrl.Result{Requeue: true}, err
|
||||
}
|
||||
|
||||
log.Info("Helm repository sync succeeded", "msg", sourcev1.HelmRepositoryReadyMessage(syncedRepo))
|
||||
|
||||
// requeue repository
|
||||
|
|
|
@ -1,3 +1,19 @@
|
|||
/*
|
||||
Copyright 2020 The Flux CD contributors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package controllers
|
||||
|
||||
import (
|
||||
|
@ -200,4 +216,85 @@ var _ = Describe("HelmRepositoryReconciler", func() {
|
|||
}, timeout, interval).Should(BeTrue())
|
||||
})
|
||||
})
|
||||
|
||||
It("Authenticates when TLS credentials are provided", func() {
|
||||
helmServer, err = testserver.NewTempHelmServer()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
defer os.RemoveAll(helmServer.Root())
|
||||
defer helmServer.Stop()
|
||||
err = helmServer.StartTLS(examplePublicKey, examplePrivateKey, exampleCA)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
Expect(helmServer.PackageChart(path.Join("testdata/helmchart"))).Should(Succeed())
|
||||
Expect(helmServer.GenerateIndex()).Should(Succeed())
|
||||
|
||||
secretKey := types.NamespacedName{
|
||||
Name: "helmrepository-auth-" + randStringRunes(5),
|
||||
Namespace: namespace.Name,
|
||||
}
|
||||
secret := &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: secretKey.Name,
|
||||
Namespace: secretKey.Namespace,
|
||||
},
|
||||
Data: map[string][]byte{},
|
||||
}
|
||||
Expect(k8sClient.Create(context.Background(), secret)).Should(Succeed())
|
||||
|
||||
key := types.NamespacedName{
|
||||
Name: "helmrepository-sample-" + randStringRunes(5),
|
||||
Namespace: namespace.Name,
|
||||
}
|
||||
created := &sourcev1.HelmRepository{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: key.Name,
|
||||
Namespace: key.Namespace,
|
||||
},
|
||||
Spec: sourcev1.HelmRepositorySpec{
|
||||
URL: helmServer.URL(),
|
||||
SecretRef: &corev1.LocalObjectReference{
|
||||
Name: secretKey.Name,
|
||||
},
|
||||
Interval: metav1.Duration{Duration: interval},
|
||||
},
|
||||
}
|
||||
Expect(k8sClient.Create(context.Background(), created)).Should(Succeed())
|
||||
|
||||
By("Expecting unknown authority error")
|
||||
Eventually(func() bool {
|
||||
got := &sourcev1.HelmRepository{}
|
||||
_ = k8sClient.Get(context.Background(), key, got)
|
||||
for _, c := range got.Status.Conditions {
|
||||
if c.Reason == sourcev1.IndexationFailedReason &&
|
||||
strings.Contains(c.Message, "certificate signed by unknown authority") {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}, timeout, interval).Should(BeTrue())
|
||||
|
||||
By("Expecting missing field error")
|
||||
secret.Data["certFile"] = examplePublicKey
|
||||
secret.Data["keyFile"] = examplePrivateKey
|
||||
Expect(k8sClient.Update(context.Background(), secret)).Should(Succeed())
|
||||
Eventually(func() bool {
|
||||
got := &sourcev1.HelmRepository{}
|
||||
_ = k8sClient.Get(context.Background(), key, got)
|
||||
for _, c := range got.Status.Conditions {
|
||||
if c.Reason == sourcev1.AuthenticationFailedReason {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}, timeout, interval).Should(BeTrue())
|
||||
|
||||
By("Expecting artifact")
|
||||
secret.Data["caFile"] = exampleCA
|
||||
Expect(k8sClient.Update(context.Background(), secret)).Should(Succeed())
|
||||
Eventually(func() bool {
|
||||
got := &sourcev1.HelmRepository{}
|
||||
_ = k8sClient.Get(context.Background(), key, got)
|
||||
return got.Status.Artifact != nil
|
||||
}, timeout, interval).Should(BeTrue())
|
||||
})
|
||||
})
|
||||
|
|
|
@ -49,6 +49,10 @@ var k8sManager ctrl.Manager
|
|||
var testEnv *envtest.Environment
|
||||
var storage *Storage
|
||||
|
||||
var examplePublicKey []byte
|
||||
var examplePrivateKey []byte
|
||||
var exampleCA []byte
|
||||
|
||||
func TestAPIs(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
|
||||
|
@ -88,6 +92,8 @@ var _ = BeforeSuite(func(done Done) {
|
|||
|
||||
// +kubebuilder:scaffold:scheme
|
||||
|
||||
Expect(loadExampleKeys()).To(Succeed())
|
||||
|
||||
tmpStoragePath, err := ioutil.TempDir("", "helmrepository")
|
||||
Expect(err).NotTo(HaveOccurred(), "failed to create tmp storage dir")
|
||||
|
||||
|
@ -136,6 +142,19 @@ func init() {
|
|||
rand.Seed(time.Now().UnixNano())
|
||||
}
|
||||
|
||||
func loadExampleKeys() (err error) {
|
||||
examplePublicKey, err = ioutil.ReadFile(filepath.Join("testdata/certs/server.pem"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
examplePrivateKey, err = ioutil.ReadFile(filepath.Join("testdata/certs/server-key.pem"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
exampleCA, err = ioutil.ReadFile(filepath.Join("testdata/certs/ca.pem"))
|
||||
return err
|
||||
}
|
||||
|
||||
var letterRunes = []rune("abcdefghijklmnopqrstuvwxyz1234567890")
|
||||
|
||||
func randStringRunes(n int) string {
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
# Copyright 2020 The Flux CD contributors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
all: server-key.pem
|
||||
|
||||
ca-key.pem: ca-csr.json
|
||||
cfssl gencert -initca ca-csr.json | cfssljson -bare ca –
|
||||
ca.pem: ca-key.pem
|
||||
ca.csr: ca-key.pem
|
||||
|
||||
server-key.pem: server-csr.json ca-config.json ca-key.pem
|
||||
cfssl gencert \
|
||||
-ca=ca.pem \
|
||||
-ca-key=ca-key.pem \
|
||||
-config=ca-config.json \
|
||||
-profile=web-servers \
|
||||
server-csr.json | cfssljson -bare server
|
||||
sever.pem: server-key.pem
|
||||
server.csr: server-key.pem
|
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"signing": {
|
||||
"default": {
|
||||
"expiry": "87600h"
|
||||
},
|
||||
"profiles": {
|
||||
"web-servers": {
|
||||
"usages": [
|
||||
"signing",
|
||||
"key encipherment",
|
||||
"server auth",
|
||||
"client auth"
|
||||
],
|
||||
"expiry": "87600h"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"CN": "example.com CA",
|
||||
"hosts": [
|
||||
"127.0.0.1",
|
||||
"localhost",
|
||||
"example.com",
|
||||
"www.example.com"
|
||||
]
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
-----BEGIN EC PRIVATE KEY-----
|
||||
MHcCAQEEIOH/u9dMcpVcZ0+X9Fc78dCTj8SHuXawhLjhu/ej64WToAoGCCqGSM49
|
||||
AwEHoUQDQgAEruH/kPxtX3cyYR2G7TYmxLq6AHyzo/NGXc9XjGzdJutE2SQzn37H
|
||||
dvSJbH+Lvqo9ik0uiJVRVdCYD1j7gNszGA==
|
||||
-----END EC PRIVATE KEY-----
|
|
@ -0,0 +1,9 @@
|
|||
-----BEGIN CERTIFICATE REQUEST-----
|
||||
MIIBIDCBxgIBADAZMRcwFQYDVQQDEw5leGFtcGxlLmNvbSBDQTBZMBMGByqGSM49
|
||||
AgEGCCqGSM49AwEHA0IABK7h/5D8bV93MmEdhu02JsS6ugB8s6PzRl3PV4xs3Sbr
|
||||
RNkkM59+x3b0iWx/i76qPYpNLoiVUVXQmA9Y+4DbMxigSzBJBgkqhkiG9w0BCQ4x
|
||||
PDA6MDgGA1UdEQQxMC+CCWxvY2FsaG9zdIILZXhhbXBsZS5jb22CD3d3dy5leGFt
|
||||
cGxlLmNvbYcEfwAAATAKBggqhkjOPQQDAgNJADBGAiEAkw85nyLhJssyCYsaFvRU
|
||||
EErhu66xHPJug/nG50uV5OoCIQCUorrflOSxfChPeCe4xfwcPv7FpcCYbKVYtGzz
|
||||
b34Wow==
|
||||
-----END CERTIFICATE REQUEST-----
|
|
@ -0,0 +1,11 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIBhzCCAS2gAwIBAgIUdsAtiX3gN0uk7ddxASWYE/tdv0wwCgYIKoZIzj0EAwIw
|
||||
GTEXMBUGA1UEAxMOZXhhbXBsZS5jb20gQ0EwHhcNMjAwNDE3MDgxODAwWhcNMjUw
|
||||
NDE2MDgxODAwWjAZMRcwFQYDVQQDEw5leGFtcGxlLmNvbSBDQTBZMBMGByqGSM49
|
||||
AgEGCCqGSM49AwEHA0IABK7h/5D8bV93MmEdhu02JsS6ugB8s6PzRl3PV4xs3Sbr
|
||||
RNkkM59+x3b0iWx/i76qPYpNLoiVUVXQmA9Y+4DbMxijUzBRMA4GA1UdDwEB/wQE
|
||||
AwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBQGyUiU1QEZiMAqjsnIYTwZ
|
||||
4yp5wzAPBgNVHREECDAGhwR/AAABMAoGCCqGSM49BAMCA0gAMEUCIQDzdtvKdE8O
|
||||
1+WRTZ9MuSiFYcrEz7Zne7VXouDEKqKEigIgM4WlbDeuNCKbqhqj+xZV0pa3rweb
|
||||
OD8EjjCMY69RMO0=
|
||||
-----END CERTIFICATE-----
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"CN": "example.com",
|
||||
"hosts": [
|
||||
"127.0.0.1",
|
||||
"localhost",
|
||||
"example.com",
|
||||
"www.example.com"
|
||||
]
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
-----BEGIN EC PRIVATE KEY-----
|
||||
MHcCAQEEIKQbEXV6nljOHMmPrWVWQ+JrAE5wsbE9iMhfY7wlJgXOoAoGCCqGSM49
|
||||
AwEHoUQDQgAE+53oBGlrvVUTelSGYji8GNHVhVg8jOs1PeeLuXCIZjQmctHLFEq3
|
||||
fE+mGxCL93MtpYzlwIWBf0m7pEGQre6bzg==
|
||||
-----END EC PRIVATE KEY-----
|
|
@ -0,0 +1,8 @@
|
|||
-----BEGIN CERTIFICATE REQUEST-----
|
||||
MIIBHDCBwwIBADAWMRQwEgYDVQQDEwtleGFtcGxlLmNvbTBZMBMGByqGSM49AgEG
|
||||
CCqGSM49AwEHA0IABPud6ARpa71VE3pUhmI4vBjR1YVYPIzrNT3ni7lwiGY0JnLR
|
||||
yxRKt3xPphsQi/dzLaWM5cCFgX9Ju6RBkK3um86gSzBJBgkqhkiG9w0BCQ4xPDA6
|
||||
MDgGA1UdEQQxMC+CCWxvY2FsaG9zdIILZXhhbXBsZS5jb22CD3d3dy5leGFtcGxl
|
||||
LmNvbYcEfwAAATAKBggqhkjOPQQDAgNIADBFAiB5A6wvQ5x6g/zhiyn+wLzXsOaB
|
||||
Gb/F25p/zTHHQqZbkwIhAPUgWzy/2bs6eZEi97bSlaRdmrqHwqT842t5sEwGyXNV
|
||||
-----END CERTIFICATE REQUEST-----
|
|
@ -0,0 +1,13 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIB7TCCAZKgAwIBAgIUB+17B8PU05wVTzRHLeG+S+ybZK4wCgYIKoZIzj0EAwIw
|
||||
GTEXMBUGA1UEAxMOZXhhbXBsZS5jb20gQ0EwHhcNMjAwNDE3MDgxODAwWhcNMzAw
|
||||
NDE1MDgxODAwWjAWMRQwEgYDVQQDEwtleGFtcGxlLmNvbTBZMBMGByqGSM49AgEG
|
||||
CCqGSM49AwEHA0IABPud6ARpa71VE3pUhmI4vBjR1YVYPIzrNT3ni7lwiGY0JnLR
|
||||
yxRKt3xPphsQi/dzLaWM5cCFgX9Ju6RBkK3um86jgbowgbcwDgYDVR0PAQH/BAQD
|
||||
AgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAMBgNVHRMBAf8EAjAA
|
||||
MB0GA1UdDgQWBBTM8HS5EIlVMBYv/300jN8PEArUgDAfBgNVHSMEGDAWgBQGyUiU
|
||||
1QEZiMAqjsnIYTwZ4yp5wzA4BgNVHREEMTAvgglsb2NhbGhvc3SCC2V4YW1wbGUu
|
||||
Y29tgg93d3cuZXhhbXBsZS5jb22HBH8AAAEwCgYIKoZIzj0EAwIDSQAwRgIhAOgB
|
||||
5W82FEgiTTOmsNRekkK5jUPbj4D4eHtb2/BI7ph4AiEA2AxHASIFBdv5b7Qf5prb
|
||||
bdNmUCzAvVuCAKuMjg2OPrE=
|
||||
-----END CERTIFICATE-----
|
|
@ -1,6 +1,8 @@
|
|||
package testserver
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
|
@ -48,6 +50,36 @@ func (s *HTTP) Start() {
|
|||
}))
|
||||
}
|
||||
|
||||
func (s *HTTP) StartTLS(cert, key, ca []byte) error {
|
||||
s.server = httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
handler := http.FileServer(http.Dir(s.docroot))
|
||||
if s.middleware != nil {
|
||||
s.middleware(handler).ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
handler.ServeHTTP(w, r)
|
||||
}))
|
||||
|
||||
config := tls.Config{}
|
||||
|
||||
keyPair, err := tls.X509KeyPair(cert, key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
config.Certificates = []tls.Certificate{keyPair}
|
||||
|
||||
cp := x509.NewCertPool()
|
||||
cp.AppendCertsFromPEM(ca)
|
||||
config.RootCAs = cp
|
||||
|
||||
config.BuildNameToCertificate()
|
||||
config.ServerName = "example.com"
|
||||
s.server.TLS = &config
|
||||
|
||||
s.server.StartTLS()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *HTTP) Stop() {
|
||||
if s.server != nil {
|
||||
s.server.Close()
|
||||
|
|
Loading…
Reference in New Issue