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())
|
syncedRepo, err := r.sync(*repository.DeepCopy())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(err, "Helm repository sync failed")
|
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
|
// update status
|
||||||
|
@ -89,7 +93,6 @@ func (r *HelmRepositoryReconciler) Reconcile(req ctrl.Request) (ctrl.Result, err
|
||||||
log.Error(err, "unable to update HelmRepository status")
|
log.Error(err, "unable to update HelmRepository status")
|
||||||
return ctrl.Result{Requeue: true}, err
|
return ctrl.Result{Requeue: true}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Info("Helm repository sync succeeded", "msg", sourcev1.HelmRepositoryReadyMessage(syncedRepo))
|
log.Info("Helm repository sync succeeded", "msg", sourcev1.HelmRepositoryReadyMessage(syncedRepo))
|
||||||
|
|
||||||
// requeue repository
|
// 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
|
package controllers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -200,4 +216,85 @@ var _ = Describe("HelmRepositoryReconciler", func() {
|
||||||
}, timeout, interval).Should(BeTrue())
|
}, 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 testEnv *envtest.Environment
|
||||||
var storage *Storage
|
var storage *Storage
|
||||||
|
|
||||||
|
var examplePublicKey []byte
|
||||||
|
var examplePrivateKey []byte
|
||||||
|
var exampleCA []byte
|
||||||
|
|
||||||
func TestAPIs(t *testing.T) {
|
func TestAPIs(t *testing.T) {
|
||||||
RegisterFailHandler(Fail)
|
RegisterFailHandler(Fail)
|
||||||
|
|
||||||
|
@ -88,6 +92,8 @@ var _ = BeforeSuite(func(done Done) {
|
||||||
|
|
||||||
// +kubebuilder:scaffold:scheme
|
// +kubebuilder:scaffold:scheme
|
||||||
|
|
||||||
|
Expect(loadExampleKeys()).To(Succeed())
|
||||||
|
|
||||||
tmpStoragePath, err := ioutil.TempDir("", "helmrepository")
|
tmpStoragePath, err := ioutil.TempDir("", "helmrepository")
|
||||||
Expect(err).NotTo(HaveOccurred(), "failed to create tmp storage dir")
|
Expect(err).NotTo(HaveOccurred(), "failed to create tmp storage dir")
|
||||||
|
|
||||||
|
@ -136,6 +142,19 @@ func init() {
|
||||||
rand.Seed(time.Now().UnixNano())
|
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")
|
var letterRunes = []rune("abcdefghijklmnopqrstuvwxyz1234567890")
|
||||||
|
|
||||||
func randStringRunes(n int) string {
|
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
|
package testserver
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"crypto/x509"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"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() {
|
func (s *HTTP) Stop() {
|
||||||
if s.server != nil {
|
if s.server != nil {
|
||||||
s.server.Close()
|
s.server.Close()
|
||||||
|
|
Loading…
Reference in New Issue