Skip to content
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

Add cabundle tls support for git repo comms #58

Merged
merged 7 commits into from
May 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .vscode/launch.json
efiacor marked this conversation as resolved.
Show resolved Hide resolved
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
efiacor marked this conversation as resolved.
Show resolved Hide resolved
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
Loading