Skip to content

Commit

Permalink
Add custom certificate validation
Browse files Browse the repository at this point in the history
Signed-off-by: Philip Laine <philip.laine@gmail.com>
  • Loading branch information
phillebaba committed Feb 7, 2021
1 parent 0465b12 commit 3ba0e6f
Show file tree
Hide file tree
Showing 13 changed files with 237 additions and 41 deletions.
5 changes: 5 additions & 0 deletions api/v1beta1/gitrepository_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,11 @@ type GitRepositorySpec struct {
// +kubebuilder:default:=go-git
// +optional
GitImplementation string `json:"gitImplementation,omitempty"`

// CertSecretRef
//
// +optional
CertSecretRef *meta.LocalObjectReference `json:"certSecretRef,omitempty"`
}

// GitRepositoryRef defines the Git ref used for pull and checkout operations.
Expand Down
5 changes: 5 additions & 0 deletions api/v1beta1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,15 @@ spec:
spec:
description: GitRepositorySpec defines the desired state of a Git repository.
properties:
certSecretRef:
description: CertSecretRef
properties:
name:
description: Name of the referent
type: string
required:
- name
type: object
gitImplementation:
default: go-git
description: Determines which git client library to use. Defaults
Expand Down
36 changes: 26 additions & 10 deletions controllers/gitrepository_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -179,25 +179,41 @@ func (r *GitRepositoryReconciler) reconcile(ctx context.Context, repository sour

// determine auth method
auth := &common.Auth{}
if repository.Spec.SecretRef != nil {
if repository.Spec.SecretRef != nil || repository.Spec.CertSecretRef != nil {
authStrategy, err := git.AuthSecretStrategyForURL(repository.Spec.URL, repository.Spec.GitImplementation)
if err != nil {
return sourcev1.GitRepositoryNotReady(repository, sourcev1.AuthenticationFailedReason, err.Error()), err
}

name := types.NamespacedName{
Namespace: repository.GetNamespace(),
Name: repository.Spec.SecretRef.Name,
var secret *corev1.Secret
if repository.Spec.SecretRef != nil {
secret = &corev1.Secret{}
name := types.NamespacedName{
Namespace: repository.GetNamespace(),
Name: repository.Spec.SecretRef.Name,
}

err = r.Client.Get(ctx, name, secret)
if err != nil {
err = fmt.Errorf("auth secret error: %w", err)
return sourcev1.GitRepositoryNotReady(repository, sourcev1.AuthenticationFailedReason, err.Error()), err
}
}

var secret corev1.Secret
err = r.Client.Get(ctx, name, &secret)
if err != nil {
err = fmt.Errorf("auth secret error: %w", err)
return sourcev1.GitRepositoryNotReady(repository, sourcev1.AuthenticationFailedReason, err.Error()), err
var certSecret *corev1.Secret
if repository.Spec.CertSecretRef != nil {
certSecret = &corev1.Secret{}
name := types.NamespacedName{
Namespace: repository.GetNamespace(),
Name: repository.Spec.CertSecretRef.Name,
}
err = r.Client.Get(ctx, name, certSecret)
if err != nil {
return sourcev1.GitRepositoryNotReady(repository, sourcev1.AuthenticationFailedReason, err.Error()), err
}
}

auth, err = authStrategy.Method(secret)
auth, err = authStrategy.Method(secret, certSecret)
if err != nil {
err = fmt.Errorf("auth error: %w", err)
return sourcev1.GitRepositoryNotReady(repository, sourcev1.AuthenticationFailedReason, err.Error()), err
Expand Down
80 changes: 79 additions & 1 deletion controllers/gitrepository_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ package controllers

import (
"context"
"crypto/tls"
"fmt"
"net/http"
"net/url"
"os"
"path"
Expand All @@ -30,6 +32,8 @@ import (
"github.com/go-git/go-git/v5/config"
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/object"
"github.com/go-git/go-git/v5/plumbing/transport/client"
httptransport "github.com/go-git/go-git/v5/plumbing/transport/http"
"github.com/go-git/go-git/v5/storage/memory"
. "github.com/onsi/ginkgo"
. "github.com/onsi/ginkgo/extensions/table"
Expand All @@ -38,6 +42,7 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"

"github.com/fluxcd/pkg/apis/meta"
"github.com/fluxcd/pkg/gittestserver"

sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
Expand Down Expand Up @@ -65,6 +70,18 @@ var _ = Describe("GitRepositoryReconciler", func() {
err = k8sClient.Create(context.Background(), namespace)
Expect(err).NotTo(HaveOccurred(), "failed to create test namespace")

cert := corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: "cert",
Namespace: namespace.Name,
},
Data: map[string][]byte{
"caFile": exampleCA,
},
}
err = k8sClient.Create(context.Background(), &cert)
Expect(err).NotTo(HaveOccurred())

gitServer, err = gittestserver.NewTempGitServer()
Expect(err).NotTo(HaveOccurred())
gitServer.AutoCreate()
Expand All @@ -88,6 +105,7 @@ var _ = Describe("GitRepositoryReconciler", func() {
expectRevision string

gitImplementation string
certSecretRef *meta.LocalObjectReference
}

DescribeTable("Git references tests", func(t refTestCase) {
Expand Down Expand Up @@ -274,6 +292,55 @@ var _ = Describe("GitRepositoryReconciler", func() {
Expect(err).NotTo(HaveOccurred())
u.Path = path.Join(u.Path, fmt.Sprintf("repository-%s.git", randStringRunes(5)))

var transport = httptransport.NewClient(&http.Client{
Transport: &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
},
})
client.InstallProtocol("https", transport)

fs := memfs.New()
gitrepo, err := git.Init(memory.NewStorage(), fs)
Expect(err).NotTo(HaveOccurred())

wt, err := gitrepo.Worktree()
Expect(err).NotTo(HaveOccurred())

ff, _ := fs.Create("fixture")
_ = ff.Close()
_, err = wt.Add(fs.Join("fixture"))
Expect(err).NotTo(HaveOccurred())

commit, err := wt.Commit("Sample", &git.CommitOptions{Author: &object.Signature{
Name: "John Doe",
Email: "john@example.com",
When: time.Now(),
}})
Expect(err).NotTo(HaveOccurred())

gitrepo.Worktree()

for _, ref := range t.createRefs {
hRef := plumbing.NewHashReference(plumbing.ReferenceName(ref), commit)
err = gitrepo.Storer.SetReference(hRef)
Expect(err).NotTo(HaveOccurred())
}

remote, err := gitrepo.CreateRemote(&config.RemoteConfig{
Name: "origin",
URLs: []string{u.String()},
})
Expect(err).NotTo(HaveOccurred())

err = remote.Push(&git.PushOptions{
RefSpecs: []config.RefSpec{"refs/heads/*:refs/heads/*", "refs/tags/*:refs/tags/*"},
})
Expect(err).NotTo(HaveOccurred())

t.reference.Commit = strings.Replace(t.reference.Commit, "<commit>", commit.String(), 1)

client.InstallProtocol("https", httptransport.DefaultClient)

key := types.NamespacedName{
Name: fmt.Sprintf("git-ref-test-%s", randStringRunes(5)),
Namespace: namespace.Name,
Expand All @@ -288,6 +355,8 @@ var _ = Describe("GitRepositoryReconciler", func() {
Interval: metav1.Duration{Duration: indexInterval},
Reference: t.reference,
GitImplementation: t.gitImplementation,
SecretRef: nil,
CertSecretRef: t.certSecretRef,
},
}
Expect(k8sClient.Create(context.Background(), created)).Should(Succeed())
Expand Down Expand Up @@ -316,13 +385,22 @@ var _ = Describe("GitRepositoryReconciler", func() {
expectStatus: metav1.ConditionFalse,
expectMessage: "x509: certificate signed by unknown authority",
}),
Entry("self signed v2", refTestCase{
Entry("self signed v2 without CA", refTestCase{
reference: &sourcev1.GitRepositoryRef{Branch: "main"},
waitForReason: sourcev1.GitOperationFailedReason,
expectStatus: metav1.ConditionFalse,
expectMessage: "error: user rejected certificate",
gitImplementation: sourcev1.LibGit2Implementation,
}),
Entry("self signed v2 with CA", refTestCase{
reference: &sourcev1.GitRepositoryRef{Branch: "some-branch"},
createRefs: []string{"refs/heads/some-branch"},
waitForReason: sourcev1.GitOperationSucceedReason,
expectStatus: metav1.ConditionTrue,
expectRevision: "some-branch",
certSecretRef: &meta.LocalObjectReference{Name: "cert"},
gitImplementation: sourcev1.LibGit2Implementation,
}),
)
})
})
28 changes: 28 additions & 0 deletions docs/api/source.md
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,20 @@ string
Defaults to go-git, valid values are (&lsquo;go-git&rsquo;, &lsquo;libgit2&rsquo;).</p>
</td>
</tr>
<tr>
<td>
<code>certSecretRef</code><br>
<em>
<a href="https://godoc.org/github.com/fluxcd/pkg/apis/meta#LocalObjectReference">
github.com/fluxcd/pkg/apis/meta.LocalObjectReference
</a>
</em>
</td>
<td>
<em>(Optional)</em>
<p>CertSecretRef</p>
</td>
</tr>
</table>
</td>
</tr>
Expand Down Expand Up @@ -1246,6 +1260,20 @@ string
Defaults to go-git, valid values are (&lsquo;go-git&rsquo;, &lsquo;libgit2&rsquo;).</p>
</td>
</tr>
<tr>
<td>
<code>certSecretRef</code><br>
<em>
<a href="https://godoc.org/github.com/fluxcd/pkg/apis/meta#LocalObjectReference">
github.com/fluxcd/pkg/apis/meta.LocalObjectReference
</a>
</em>
</td>
<td>
<em>(Optional)</em>
<p>CertSecretRef</p>
</td>
</tr>
</tbody>
</table>
</div>
Expand Down
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,12 @@ require (
github.com/go-git/go-billy/v5 v5.0.0
github.com/go-git/go-git/v5 v5.2.0
github.com/go-logr/logr v0.3.0
github.com/libgit2/git2go/v31 v31.3.0
github.com/libgit2/git2go/v31 v31.4.7
github.com/minio/minio-go/v7 v7.0.5
github.com/onsi/ginkgo v1.14.1
github.com/onsi/gomega v1.10.2
github.com/spf13/pflag v1.0.5
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0
golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e
helm.sh/helm/v3 v3.5.0
k8s.io/api v0.20.2
Expand Down
12 changes: 10 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -427,6 +427,8 @@ github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hf
github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y=
Expand Down Expand Up @@ -553,8 +555,8 @@ github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.8.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/lib/pq v1.9.0 h1:L8nSXQQzAYByakOFMTwpjRoHsMJklur4Gi59b6VivR8=
github.com/lib/pq v1.9.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/libgit2/git2go/v31 v31.3.0 h1:d8ciyYVKir+gKwra3KuNxTyVvbgGKn4admdt1PNNAOg=
github.com/libgit2/git2go/v31 v31.3.0/go.mod h1:mnc0hPGPs0nDi9INrurTpioeRzje9DvSXqON/+JEhwY=
github.com/libgit2/git2go/v31 v31.4.7 h1:P85qB5at5un4qPqUcvOZbAom7P0G4KAG/OLVyD29kQ0=
github.com/libgit2/git2go/v31 v31.4.7/go.mod h1:c/rkJcBcUFx6wHaT++UwNpKvIsmPNqCeQ/vzO4DrEec=
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0=
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE=
github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM=
Expand Down Expand Up @@ -903,6 +905,8 @@ golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899/go.mod h1:LzIPMQfyMNhhGPh
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0 h1:hb9wdF1z5waM+dSIICn1l0DkLVDT3hqhhQsDNUmHPRE=
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c h1:9HhBz5L/UjnK9XLtiZhYAdue5BVKep3PMmS2LuPDt8k=
golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
Expand Down Expand Up @@ -1032,6 +1036,10 @@ golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201112073958-5cba982894dd h1:5CtCZbICpIOFdgO940moixOPjc0178IU44m4EjOO5IY=
golang.org/x/sys v0.0.0-20201112073958-5cba982894dd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201204225414-ed752295db88 h1:KmZPnMocC93w341XZp26yTJg8Za7lhb2KhkYmixoeso=
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221 h1:/ZHdbVpdR/jk3g30/d4yUL0JU9kksj8+F/bnQUVLGDM=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
Expand Down
2 changes: 1 addition & 1 deletion pkg/git/common/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,5 +46,5 @@ type Auth struct {
}

type AuthSecretStrategy interface {
Method(secret corev1.Secret) (*Auth, error)
Method(secret *corev1.Secret, certSecret *corev1.Secret) (*Auth, error)
}
13 changes: 11 additions & 2 deletions pkg/git/v1/transport.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ limitations under the License.
package v1

import (
"errors"
"fmt"
"net/url"

Expand Down Expand Up @@ -46,7 +47,11 @@ func AuthSecretStrategyForURL(URL string) (common.AuthSecretStrategy, error) {

type BasicAuth struct{}

func (s *BasicAuth) Method(secret corev1.Secret) (*common.Auth, error) {
func (s *BasicAuth) Method(secret *corev1.Secret, certSecret *corev1.Secret) (*common.Auth, error) {
if certSecret != nil {
return nil, errors.New("git2go HTTP transport does not support custom certificates.")
}

auth := &http.BasicAuth{}
if username, ok := secret.Data["username"]; ok {
auth.Username = string(username)
Expand All @@ -64,7 +69,11 @@ type PublicKeyAuth struct {
user string
}

func (s *PublicKeyAuth) Method(secret corev1.Secret) (*common.Auth, error) {
func (s *PublicKeyAuth) Method(secret *corev1.Secret, certSecret *corev1.Secret) (*common.Auth, error) {
if certSecret != nil {
return nil, errors.New("git2go SSH transport does not support custom certificates.")
}

identity := secret.Data["identity"]
knownHosts := secret.Data["known_hosts"]
if len(identity) == 0 || len(knownHosts) == 0 {
Expand Down
4 changes: 2 additions & 2 deletions pkg/git/v1/transport_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ func TestBasicAuthStrategy_Method(t *testing.T) {
tt.modify(secret)
}
s := &BasicAuth{}
got, err := s.Method(*secret)
got, err := s.Method(secret, nil)
if (err != nil) != tt.wantErr {
t.Errorf("Method() error = %v, wantErr %v", err, tt.wantErr)
return
Expand Down Expand Up @@ -145,7 +145,7 @@ func TestPublicKeyStrategy_Method(t *testing.T) {
tt.modify(secret)
}
s := &PublicKeyAuth{}
_, err := s.Method(*secret)
_, err := s.Method(secret, nil)
if (err != nil) != tt.wantErr {
t.Errorf("Method() error = %v, wantErr %v", err, tt.wantErr)
return
Expand Down
Loading

0 comments on commit 3ba0e6f

Please sign in to comment.