Skip to content

Commit

Permalink
Merge pull request #58 from Nordix/add_git_tls
Browse files Browse the repository at this point in the history
Add cabundle tls support for git repo comms
  • Loading branch information
nephio-prow[bot] authored May 31, 2024
2 parents e190648 + 327ab11 commit 463f777
Show file tree
Hide file tree
Showing 12 changed files with 237 additions and 6 deletions.
2 changes: 1 addition & 1 deletion .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
"--function-runner=172.18.255.201:9445",
"--repo-sync-frequency=60s"
],
"cwd": "${workspaceFolder}"
"cwd": "${workspaceFolder}",
},
{
"name": "Launch Func Client",
Expand Down
32 changes: 32 additions & 0 deletions docs/adding-external-git-ca-bundle.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Adding an external Git CaBundle

To enable the porch server to communicate with a custom git deployment over HTTPS, we must:
1. Provide a additional args flag `use-git-cabundle=true` to the porch-server deployment.
2. Provide an additional kubernetes secret containing the relevant certificate chain in the form of a cabundle.

The secret itself must meet the following criteria:

- exist in the same `namespace` as the Repository CR (Custom Resource) that requires it
- be named specifically `<namespace>-ca-bundle`
- have a Data key named `ca.crt` containing the relevant ca certificate (chain)

For example, a Git Repository is hosted over HTTPS at the following URL:

`https://my-gitlab.com/joe.bloggs/blueprints.git`

Before creating the new Repository in the **gitlab** namespace, we must create a secret that fulfils the criteria above.

`kubectl create secret generic gitlab-ca-bundle --namespace=gitlab --from-file=ca.crt`

Which would produce the following:

```
apiVersion: v1
kind: Secret
metadata:
name: gitlab-ca-bundle
namespace: gitlab
type: Opaque
data:
ca.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUNuakNDQWdHZ0F3SUJBZ0lRTEdmUytUK3YyRDZDczh1MVBlUUlKREFLQmdncWhrak9QUVFEQkRBZE1Sc3cKR1FZRFZRUURFeEpqWlhKMExXMWhibUZuWlhJdWJHOWpZV3d3SGhjTk1qUXdOVE14TVRFeU5qTXlXaGNOTWpRdwpPREk1TVRFeU5qTXlXakFWTVJNd0VRWURWUVFGRXdveE1qTTBOVFkzT0Rrd01JSUJJakFOQmdrcWhraUc5dzBCCkFRRUZBQU9DQVE4QU1JSUJDZ0tDQVFFQXhCUUtWMEVzQ1JOOGxuV3lQR1ZWNXJwam5QZkI2emszK0N4cEp2NVMKUWhpMG1KbDI0elV1WWZjRzNxdFUva1NuREdjK3NQRUY0RmlOcUlsSTByWHBQSXBPazhKbjEvZU1VT3RkZUUyNgpSWEZBWktjeDVvdUJyZVNja3hsN2RPVkJnOE1EM1h5RU1PQU5nM0hJZ1J4ZWx2U2p1dy8vMURhSlRnK0lBS0dUCkgrOVlRVFcrZDIwSk5wQlR3NkdnQlRsYmdqL2FMRWEwOXVYSVBjK0JUSkpXRThIeDhkVjFNbEtHRFlDU29qZFgKbG9TN1FIa0dsSVk3M0NPZVVGWEVnTlFVVmZaZHdreXNsT3F4WmdXUTNZTFZHcEFyRitjOVdyUGpQQU5NQWtORQpPdHRvaG8zTlRxQ3FST3JEa0RMYWdsU1BKSUd1K25TcU5veVVxSUlWWkV5R1dRSURBUUFCbzJBd1hqQU9CZ05WCkhROEJBZjhFQkFNQ0JhQXdEQVlEVlIwVEFRSC9CQUl3QURBZkJnTlZIU01FR0RBV2dCUitFZTVDTnVJSkcwZjkKV3J3VzdqYUZFeVdzb1RBZEJnTlZIUkVFRmpBVWdoSm5hWFJzWVdJdVpYaGhiWEJzWlM1amIyMHdDZ1lJS29aSQp6ajBFQXdRRGdZb0FNSUdHQWtGLzRyNUM4bnkwdGVIMVJlRzdDdXJHYk02SzMzdTFDZ29GTkthajIva2ovYzlhCnZwODY0eFJKM2ZVSXZGMEtzL1dNUHNad2w2bjMxUWtXT2VpM01aYWtBUUpCREw0Kyt4UUxkMS9uVWdqOW1zN2MKUUx3NXVEMGxqU0xrUS9mOTJGYy91WHc4QWVDck5XcVRqcDEycDJ6MkUzOXRyWWc1a2UvY2VTaWFPUm16eUJuTwpTUTg9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0=
```
4 changes: 3 additions & 1 deletion pkg/apiserver/apiserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ type ExtraConfig struct {
FunctionRunnerAddress string
DefaultImagePrefix string
RepoSyncFrequency time.Duration
UseGitCaBundle bool
}

// Config defines the config for the apiserver
Expand Down Expand Up @@ -212,6 +213,7 @@ func (c completedConfig) New() (*PorchServer, error) {

resolverChain := []porch.Resolver{
porch.NewBasicAuthResolver(),
porch.NewCaBundleResolver(),
porch.NewGcloudWIResolver(coreV1Client, stsClient),
}

Expand All @@ -223,7 +225,7 @@ func (c completedConfig) New() (*PorchServer, error) {

watcherMgr := engine.NewWatcherManager()

cache := cache.NewCache(c.ExtraConfig.CacheDirectory, c.ExtraConfig.RepoSyncFrequency, cache.CacheOptions{
cache := cache.NewCache(c.ExtraConfig.CacheDirectory, c.ExtraConfig.RepoSyncFrequency, c.ExtraConfig.UseGitCaBundle, cache.CacheOptions{
CredentialResolver: credentialResolver,
UserInfoProvider: userInfoProvider,
MetadataStore: metadataStore,
Expand Down
5 changes: 4 additions & 1 deletion pkg/cache/cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ type Cache struct {
metadataStore meta.MetadataStore
repoSyncFrequency time.Duration
objectNotifier objectNotifier
useGitCaBundle bool
}

type objectNotifier interface {
Expand All @@ -64,7 +65,7 @@ type CacheOptions struct {
ObjectNotifier objectNotifier
}

func NewCache(cacheDir string, repoSyncFrequency time.Duration, opts CacheOptions) *Cache {
func NewCache(cacheDir string, repoSyncFrequency time.Duration, useGitCaBundle bool, opts CacheOptions) *Cache {
return &Cache{
repositories: make(map[string]*cachedRepository),
cacheDir: cacheDir,
Expand All @@ -73,6 +74,7 @@ func NewCache(cacheDir string, repoSyncFrequency time.Duration, opts CacheOption
metadataStore: opts.MetadataStore,
objectNotifier: opts.ObjectNotifier,
repoSyncFrequency: repoSyncFrequency,
useGitCaBundle: useGitCaBundle,
}
}

Expand Down Expand Up @@ -136,6 +138,7 @@ func (c *Cache) OpenRepository(ctx context.Context, repositorySpec *configapi.Re
CredentialResolver: c.credentialResolver,
UserInfoProvider: c.userInfoProvider,
MainBranchStrategy: mbs,
UseGitCaBundle: c.useGitCaBundle,
}); err != nil {
return nil, err
} else {
Expand Down
3 changes: 2 additions & 1 deletion pkg/cache/cache_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,9 +135,10 @@ func openRepositoryFromArchive(t *testing.T, ctx context.Context, testPath, name
repo, address := git.ServeGitRepository(t, tarfile, tempdir)
metadataStore := createMetadataStoreFromArchive(t, "", "")

cache := NewCache(t.TempDir(), 60*time.Second, CacheOptions{
cache := NewCache(t.TempDir(), 60*time.Second, true, CacheOptions{
MetadataStore: metadataStore,
ObjectNotifier: &fakecache.ObjectNotifier{},
CredentialResolver: &fakecache.CredentialResolver{},
})
cachedGit, err := cache.OpenRepository(ctx, &v1alpha1.Repository{
TypeMeta: metav1.TypeMeta{
Expand Down
47 changes: 47 additions & 0 deletions pkg/cache/fake/credentialresolver.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// Copyright 2024 The kpt and Nephio Authors
//
// 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 fake

import (
"context"
"github.com/nephio-project/porch/pkg/repository"
"github.com/go-git/go-git/v5/plumbing/transport"
)

type credential struct {
cabundle string
}

func (c *credential) ToString() string {
return c.cabundle
}

func (c *credential) Valid() bool {
return true
}

func (c *credential) ToAuthMethod() transport.AuthMethod {
panic("unimplemented")
}

type CredentialResolver struct{
cabundle string
}

func (cr *CredentialResolver) ResolveCredential(ctx context.Context, namespace, name string) (repository.Credential, error) {
return &credential{
cabundle: cr.cabundle,
}, nil
}
3 changes: 3 additions & 0 deletions pkg/cmd/server/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ type PorchServerOptions struct {
FunctionRunnerAddress string
DefaultImagePrefix string
RepoSyncFrequency time.Duration
UseGitCaBundle bool

SharedInformerFactory informers.SharedInformerFactory
StdOut io.Writer
Expand Down Expand Up @@ -189,6 +190,7 @@ func (o *PorchServerOptions) Config() (*apiserver.Config, error) {
RepoSyncFrequency: o.RepoSyncFrequency,
FunctionRunnerAddress: o.FunctionRunnerAddress,
DefaultImagePrefix: o.DefaultImagePrefix,
UseGitCaBundle: o.UseGitCaBundle,
},
}
return config, nil
Expand Down Expand Up @@ -234,5 +236,6 @@ func (o *PorchServerOptions) AddFlags(fs *pflag.FlagSet) {
fs.StringVar(&o.FunctionRunnerAddress, "function-runner", "", "Address of the function runner gRPC service.")
fs.StringVar(&o.DefaultImagePrefix, "default-image-prefix", "gcr.io/kpt-fn/", "Default prefix for unqualified function names")
fs.StringVar(&o.CacheDirectory, "cache-directory", "", "Directory where Porch server stores repository and package caches.")
fs.BoolVar(&o.UseGitCaBundle, "use-git-cabundle", false, "Determine whether to use a user-defined CaBundle for TLS towards git.")
fs.DurationVar(&o.RepoSyncFrequency, "repo-sync-frequency", 60*time.Second, "Frequency in seconds at which registered repositories will be synced.")
}
5 changes: 5 additions & 0 deletions pkg/engine/clone_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,11 @@ type credential struct {
username, password string
}

// ToString implements repository.Credential.
func (c *credential) ToString() string {
panic("unimplemented")
}

func (c *credential) Valid() bool {
return true
}
Expand Down
16 changes: 16 additions & 0 deletions pkg/git/git.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ type GitRepositoryOptions struct {
CredentialResolver repository.CredentialResolver
UserInfoProvider repository.UserInfoProvider
MainBranchStrategy MainBranchStrategy
UseGitCaBundle bool
}

func OpenRepository(ctx context.Context, name, namespace string, spec *configapi.GitRepository, deployment bool, root string, opts GitRepositoryOptions) (GitRepository, error) {
Expand Down Expand Up @@ -138,6 +139,14 @@ func OpenRepository(ctx context.Context, name, namespace string, spec *configapi
deployment: deployment,
}

if opts.UseGitCaBundle {
if caBundle, err := opts.CredentialResolver.ResolveCredential(ctx, namespace, namespace + "-ca-bundle"); err != nil {
klog.Errorf("failed to obtain caBundle from secret %s/%s: %v", namespace, namespace + "-ca-bundle", err)
} else {
repository.caBundle = []byte(caBundle.ToString())
}
}

if err := repository.fetchRemoteRepository(ctx); err != nil {
return nil, err
}
Expand Down Expand Up @@ -178,6 +187,9 @@ type gitRepository struct {
deletionProposedCache map[BranchName]bool

mutex sync.Mutex

// caBundle to use for TLS communication towards git
caBundle []byte
}

var _ GitRepository = &gitRepository{}
Expand Down Expand Up @@ -884,6 +896,7 @@ func (r *gitRepository) fetchRemoteRepository(ctx context.Context) error {
RemoteName: OriginName,
Auth: auth,
Prune: true,
CABundle: r.caBundle,
})
}); err {
case nil: // OK
Expand Down Expand Up @@ -1007,6 +1020,7 @@ func (r *gitRepository) createPackageDeleteCommit(ctx context.Context, branch pl
RefSpecs: []config.RefSpec{config.RefSpec(fmt.Sprintf("+%s:%s", local, branch))},
Auth: auth,
Tags: git.NoTags,
CABundle: r.caBundle,
})
}); err {
case nil, git.NoErrAlreadyUpToDate:
Expand Down Expand Up @@ -1082,6 +1096,7 @@ func (r *gitRepository) pushAndCleanup(ctx context.Context, ph *pushRefSpecBuild
RequireRemoteRefs: require,
// TODO(justinsb): Need to ensure this is a compare-and-swap
Force: true,
CABundle: r.caBundle,
})
}); err != nil {
return err
Expand Down Expand Up @@ -1609,6 +1624,7 @@ func (r *gitRepository) commitPackageToMain(ctx context.Context, d *gitPackageDr
RemoteName: OriginName,
RefSpecs: []config.RefSpec{branch.ForceFetchSpec()},
Auth: auth,
CABundle: r.caBundle,
})
}); err {
case nil, git.NoErrAlreadyUpToDate:
Expand Down
51 changes: 49 additions & 2 deletions pkg/registry/porch/secret.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,15 @@ import (
)

const (
// Values for scret types supported by porch.
// Values for secret types supported by porch.
BasicAuthType = core.SecretTypeBasicAuth
WorkloadIdentityAuthType = "kpt.dev/workload-identity-auth"

// Annotation used to specify the gsa for a ksa.
WIGCPSAAnnotation = "iam.gke.io/gcp-service-account"
WIGCPSAAnnotation = "iam.gke.io/gcp-service-account"

//Secret.Data key required for the caBundle
CaBundleDataName = "ca.crt"
)

func NewCredentialResolver(coreClient client.Reader, resolverChain []Resolver) repository.CredentialResolver {
Expand Down Expand Up @@ -123,6 +126,10 @@ type BasicAuthCredential struct {
Password string
}

func (b *BasicAuthCredential) ToString() string {
panic("unimplemented")
}

var _ repository.Credential = &BasicAuthCredential{}

func (b *BasicAuthCredential) Valid() bool {
Expand All @@ -136,6 +143,42 @@ func (b *BasicAuthCredential) ToAuthMethod() transport.AuthMethod {
}
}

func NewCaBundleResolver() Resolver {
return &CaBundleResolver{}
}

var _ Resolver = &CaBundleResolver{}

type CaBundleResolver struct{}

func (c *CaBundleResolver) Resolve(_ context.Context, secret core.Secret) (repository.Credential, bool, error) {
if secret.Data[CaBundleDataName] == nil {
return nil, false, fmt.Errorf("CaBundle secret.Data key must be set as %s", CaBundleDataName)
}

return &CaBundleCredential{
CaBundle: string(secret.Data[CaBundleDataName]),
}, true, nil
}

type CaBundleCredential struct {
CaBundle string
}

func (c *CaBundleCredential) ToString() string {
return c.CaBundle
}

var _ repository.Credential = &CaBundleCredential{}

func (c *CaBundleCredential) Valid() bool {
return true
}

func (c *CaBundleCredential) ToAuthMethod() transport.AuthMethod {
panic("unimplemented")
}

func NewGcloudWIResolver(corev1Client *corev1client.CoreV1Client, stsClient *stsv1.Service) Resolver {
return &GcloudWIResolver{
coreV1Client: corev1Client,
Expand Down Expand Up @@ -213,6 +256,10 @@ type GcloudWICredential struct {
token *oauth2.Token
}

func (b *GcloudWICredential) ToString() string {
panic("unimplemented")
}

var _ repository.Credential = &GcloudWICredential{}

func (b *GcloudWICredential) Valid() bool {
Expand Down
Loading

0 comments on commit 463f777

Please sign in to comment.