Skip to content

Commit 7156479

Browse files
(chore): adds tls config for GitHub App auth
this commit ensures that if ca.crt or caFile is available in the github app secret, a tls config with user provided certs is appended to system cert pool and passed to the underlying http transport Signed-off-by: abhijith-darshan <[email protected]> (chore): update target URL for TLSConfigFromSecret this commit ensures that the target URL for runtime/secrets.TLSConfigFromSecret has the scheme and host Signed-off-by: abhijith-darshan <[email protected]> (chore): adds test scenarios this commit adds test scenarios for mTLS GitHub app in reconcile source auth strategy Signed-off-by: abhijith-darshan <[email protected]> (chore): use runtime/secrets authMethods this commit ensures that GitHubApp secret resolution happens via pkg/runtime/secrets Signed-off-by: abhijith-darshan <[email protected]> (chore): update docs Signed-off-by: abhijith-darshan <[email protected]> (chore): adds github app data check this commit ensures that when provider is github and no github app data is present in the secret, it will error out with invalid configuration Signed-off-by: abhijith-darshan <[email protected]> (chore): removes getProxyOpts helper func this commit removes the helper method getProxyOpts and uses the standardized pkg/runtime/secrets APIs to get proxy options. Signed-off-by: abhijith-darshan <[email protected]> (chore): removes getProxyOpts test Signed-off-by: abhijith-darshan <[email protected]> (chore): improves test coverage Signed-off-by: abhijith-darshan <[email protected]>
1 parent a84403c commit 7156479

File tree

5 files changed

+190
-141
lines changed

5 files changed

+190
-141
lines changed

docs/spec/v1/gitrepositories.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,8 @@ same pattern.
357357
- The private key that was generated in the pre-requisites.
358358
- (Optional) GitHub Enterprise Server users can set the base URL to
359359
`http(s)://HOSTNAME/api/v3`.
360+
- (Optional) If GitHub Enterprise Server uses a private CA, include its bundle (root and any intermediates) in `ca.crt`.
361+
If the `ca.crt` is specified, then it will be used for TLS verification for all API / Git over `HTTPS` requests to the GitHub Enterprise Server.
360362

361363
```yaml
362364
apiVersion: v1
@@ -372,6 +374,10 @@ stringData:
372374
...
373375
-----END RSA PRIVATE KEY-----
374376
githubAppBaseURL: "<github-enterprise-api-url>" #optional, required only for GitHub Enterprise Server users
377+
ca.crt: | #optional, for GitHub Enterprise Server users
378+
-----BEGIN CERTIFICATE-----
379+
...
380+
-----END CERTIFICATE-----
375381
```
376382

377383
Alternatively, the Flux CLI can be used to automatically create the secret with

go.mod

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,15 +30,15 @@ require (
3030
github.com/fluxcd/pkg/apis/meta v1.18.0
3131
github.com/fluxcd/pkg/auth v0.21.0
3232
github.com/fluxcd/pkg/cache v0.10.0
33-
github.com/fluxcd/pkg/git v0.34.0
34-
github.com/fluxcd/pkg/git/gogit v0.37.0
33+
github.com/fluxcd/pkg/git v0.35.0
34+
github.com/fluxcd/pkg/git/gogit v0.38.0
3535
github.com/fluxcd/pkg/gittestserver v0.18.0
3636
github.com/fluxcd/pkg/helmtestserver v0.26.0
3737
github.com/fluxcd/pkg/http/transport v0.6.0
3838
github.com/fluxcd/pkg/lockedfile v0.6.0
3939
github.com/fluxcd/pkg/masktoken v0.7.0
4040
github.com/fluxcd/pkg/oci v0.52.0
41-
github.com/fluxcd/pkg/runtime v0.78.0
41+
github.com/fluxcd/pkg/runtime v0.79.0
4242
github.com/fluxcd/pkg/sourceignore v0.13.0
4343
github.com/fluxcd/pkg/ssh v0.20.0
4444
github.com/fluxcd/pkg/tar v0.13.0

go.sum

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -382,10 +382,10 @@ github.com/fluxcd/pkg/auth v0.21.0 h1:ckAQqP12wuptXEkMY18SQKWEY09m9e6yI0mEMsDV15
382382
github.com/fluxcd/pkg/auth v0.21.0/go.mod h1:MXmpsXT97c874HCw5hnfqFUP7TsG8/Ss1vFrk8JccfM=
383383
github.com/fluxcd/pkg/cache v0.10.0 h1:M+OGDM4da1cnz7q+sZSBtkBJHpiJsLnKVmR9OdMWxEY=
384384
github.com/fluxcd/pkg/cache v0.10.0/go.mod h1:pPXRzQUDQagsCniuOolqVhnAkbNgYOg8d2cTliPs7ME=
385-
github.com/fluxcd/pkg/git v0.34.0 h1:qTViWkfpEDnjzySyKRKliqUeGj/DznqlkmPhaDNIsFY=
386-
github.com/fluxcd/pkg/git v0.34.0/go.mod h1:F9Asm3MlLW4uZx3FF92+bqho+oktdMdnTn/QmXe56NE=
387-
github.com/fluxcd/pkg/git/gogit v0.37.0 h1:JINylFYpwrxS3MCu5Ei+g6XPgxbs5lv9PppIYYr07KY=
388-
github.com/fluxcd/pkg/git/gogit v0.37.0/go.mod h1:X7YzW5mb4srA05h4SpL2OEGEHq02tbXQF5DPJen9hlc=
385+
github.com/fluxcd/pkg/git v0.35.0 h1:mAauhsdfxNW4yQdXviVlvcN/uCGGG0+6p5D1+HFZI9w=
386+
github.com/fluxcd/pkg/git v0.35.0/go.mod h1:F9Asm3MlLW4uZx3FF92+bqho+oktdMdnTn/QmXe56NE=
387+
github.com/fluxcd/pkg/git/gogit v0.38.0 h1:222KmjpKf9pxqi8rAtm1omDcpGTY4JkahLrAwZ3AcwU=
388+
github.com/fluxcd/pkg/git/gogit v0.38.0/go.mod h1:kHStdfd/AtkH5ED0UEWP2tmMGnfxg1GG92D29M+lRJ0=
389389
github.com/fluxcd/pkg/gittestserver v0.18.0 h1:jkuLmzWFfq+v1ziI0LspZrUzc5WzCO98BaWb8OVRPtk=
390390
github.com/fluxcd/pkg/gittestserver v0.18.0/go.mod h1:2wDLqUkPuixk/8pGQdef9ewaGJXf7Z+xHDVq8PIFG4E=
391391
github.com/fluxcd/pkg/helmtestserver v0.26.0 h1:gKw1MGqWwN94nzs2yg3WKgMxi1RqqlDZXlGziaNCcv4=
@@ -398,8 +398,8 @@ github.com/fluxcd/pkg/masktoken v0.7.0 h1:pitmyOg2pUVdW+nn2Lk/xqm2TaA08uxvOC0ns3
398398
github.com/fluxcd/pkg/masktoken v0.7.0/go.mod h1:Lc1uoDjO1GY6+YdkK+ZqqBIBWquyV58nlSJ5S1N1IYU=
399399
github.com/fluxcd/pkg/oci v0.52.0 h1:rkHMtXYm21MtDrjNcR5KScqOe6C1JHPExoShuVdNm8M=
400400
github.com/fluxcd/pkg/oci v0.52.0/go.mod h1:5J6IhHoDVYCVeBEC+4E3nPeKh7d0kjJ8IEL6NVCiTx4=
401-
github.com/fluxcd/pkg/runtime v0.78.0 h1:xwNZqnazmgURGuLiHDbzST6BI5K9fvZuNS4eMVY35Es=
402-
github.com/fluxcd/pkg/runtime v0.78.0/go.mod h1:iGhdaEq+lMJQTJNAFEPOU4gUJ7kt3yeDcJPZy7O9IUw=
401+
github.com/fluxcd/pkg/runtime v0.79.0 h1:9tv79EiQDx/QJH9mYDd9kZ9WybCVWBUGoiBHij+eKkc=
402+
github.com/fluxcd/pkg/runtime v0.79.0/go.mod h1:iGhdaEq+lMJQTJNAFEPOU4gUJ7kt3yeDcJPZy7O9IUw=
403403
github.com/fluxcd/pkg/sourceignore v0.13.0 h1:ZvkzX2WsmyZK9cjlqOFFW1onHVzhPZIqDbCh96rPqbU=
404404
github.com/fluxcd/pkg/sourceignore v0.13.0/go.mod h1:Z9H1GoBx0ljOhptnzoV0PL6Nd/UzwKcSphP27lqb4xI=
405405
github.com/fluxcd/pkg/ssh v0.20.0 h1:Ak0laIYIc/L8lEfqls/LDWRW8wYPESGaravQsCRGLb8=

internal/controller/gitrepository_controller.go

Lines changed: 37 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import (
3131
authutils "github.com/fluxcd/pkg/auth/utils"
3232
"github.com/fluxcd/pkg/git/github"
3333
"github.com/fluxcd/pkg/runtime/logger"
34+
"github.com/fluxcd/pkg/runtime/secrets"
3435
"github.com/go-git/go-git/v5/plumbing/transport"
3536
corev1 "k8s.io/api/core/v1"
3637
"k8s.io/apimachinery/pkg/runtime"
@@ -486,7 +487,11 @@ func (r *GitRepositoryReconciler) reconcileSource(ctx context.Context, sp *patch
486487
var proxyURL *url.URL
487488
if obj.Spec.ProxySecretRef != nil {
488489
var err error
489-
proxyOpts, proxyURL, err = r.getProxyOpts(ctx, obj.Spec.ProxySecretRef.Name, obj.GetNamespace())
490+
secretRef := types.NamespacedName{
491+
Name: obj.Spec.ProxySecretRef.Name,
492+
Namespace: obj.GetNamespace(),
493+
}
494+
proxyURL, err = secrets.ProxyURLFromSecretRef(ctx, r.Client, secretRef)
490495
if err != nil {
491496
e := serror.NewGeneric(
492497
fmt.Errorf("failed to configure proxy options: %w", err),
@@ -496,6 +501,7 @@ func (r *GitRepositoryReconciler) reconcileSource(ctx context.Context, sp *patch
496501
// Return error as the world as observed may change
497502
return sreconcile.ResultEmpty, e
498503
}
504+
proxyOpts = &transport.ProxyOptions{URL: proxyURL.String()}
499505
}
500506

501507
u, err := url.Parse(obj.Spec.URL)
@@ -618,52 +624,16 @@ func (r *GitRepositoryReconciler) reconcileSource(ctx context.Context, sp *patch
618624
return sreconcile.ResultSuccess, nil
619625
}
620626

621-
// getProxyOpts fetches the secret containing the proxy settings, constructs a
622-
// transport.ProxyOptions object using those settings and then returns it.
623-
func (r *GitRepositoryReconciler) getProxyOpts(ctx context.Context, proxySecretName,
624-
proxySecretNamespace string) (*transport.ProxyOptions, *url.URL, error) {
625-
proxyData, err := r.getSecretData(ctx, proxySecretName, proxySecretNamespace)
626-
if err != nil {
627-
return nil, nil, fmt.Errorf("failed to get proxy secret '%s/%s': %w", proxySecretNamespace, proxySecretName, err)
628-
}
629-
b, ok := proxyData["address"]
630-
if !ok {
631-
return nil, nil, fmt.Errorf("invalid proxy secret '%s/%s': key 'address' is missing", proxySecretNamespace, proxySecretName)
632-
}
633-
634-
address := string(b)
635-
username := string(proxyData["username"])
636-
password := string(proxyData["password"])
637-
638-
proxyOpts := &transport.ProxyOptions{
639-
URL: address,
640-
Username: username,
641-
Password: password,
642-
}
643-
644-
proxyURL, err := url.Parse(string(address))
645-
if err != nil {
646-
return nil, nil, fmt.Errorf("invalid address in proxy secret '%s/%s': %w", proxySecretNamespace, proxySecretName, err)
647-
}
648-
switch {
649-
case username != "" && password == "":
650-
proxyURL.User = url.User(username)
651-
case username != "" && password != "":
652-
proxyURL.User = url.UserPassword(username, password)
653-
}
654-
655-
return proxyOpts, proxyURL, nil
656-
}
657-
658627
// getAuthOpts fetches the secret containing the auth options (if specified),
659628
// constructs a git.AuthOptions object using those options along with the provided
660629
// URL and returns it.
661630
func (r *GitRepositoryReconciler) getAuthOpts(ctx context.Context, obj *sourcev1.GitRepository,
662631
u url.URL, proxyURL *url.URL) (*git.AuthOptions, error) {
632+
var secret *corev1.Secret
663633
var authData map[string][]byte
664634
if obj.Spec.SecretRef != nil {
665635
var err error
666-
authData, err = r.getSecretData(ctx, obj.Spec.SecretRef.Name, obj.GetNamespace())
636+
secret, err = r.getSecret(ctx, obj.Spec.SecretRef.Name, obj.GetNamespace())
667637
if err != nil {
668638
e := serror.NewGeneric(
669639
fmt.Errorf("failed to get secret '%s/%s': %w", obj.GetNamespace(), obj.Spec.SecretRef.Name, err),
@@ -672,6 +642,7 @@ func (r *GitRepositoryReconciler) getAuthOpts(ctx context.Context, obj *sourcev1
672642
conditions.MarkTrue(obj, sourcev1.FetchFailedCondition, e.Reason, "%s", e)
673643
return nil, e
674644
}
645+
authData = secret.Data
675646
}
676647

677648
// Configure authentication strategy to access the source
@@ -718,24 +689,38 @@ func (r *GitRepositoryReconciler) getAuthOpts(ctx context.Context, obj *sourcev1
718689
conditions.MarkTrue(obj, sourcev1.FetchFailedCondition, e.Reason, "%s", e)
719690
return nil, e
720691
}
721-
692+
targetURL := fmt.Sprintf("%s://%s", u.Scheme, u.Host)
693+
authMethods, err := secrets.AuthMethodsFromSecret(ctx, secret, secrets.WithTargetURL(targetURL), secrets.WithTLSSystemCertPool())
694+
if err != nil {
695+
return nil, err
696+
}
697+
if !authMethods.HasGitHubAppData() {
698+
e := serror.NewStalling(
699+
fmt.Errorf("secretRef with github app data must be specified when provider is set to github"),
700+
sourcev1.InvalidProviderConfigurationReason,
701+
)
702+
conditions.MarkTrue(obj, sourcev1.FetchFailedCondition, e.Reason, "%s", e)
703+
return nil, e
704+
}
722705
getCreds = func() (*authutils.GitCredentials, error) {
723-
var opts []github.OptFunc
706+
var appOpts []github.OptFunc
724707

725-
if len(authData) > 0 {
726-
opts = append(opts, github.WithAppData(authData))
727-
}
708+
appOpts = append(appOpts, github.WithAppData(authMethods.GitHubAppData))
728709

729710
if proxyURL != nil {
730-
opts = append(opts, github.WithProxyURL(proxyURL))
711+
appOpts = append(appOpts, github.WithProxyURL(proxyURL))
731712
}
732713

733714
if r.TokenCache != nil {
734-
opts = append(opts, github.WithCache(r.TokenCache, sourcev1.GitRepositoryKind,
715+
appOpts = append(appOpts, github.WithCache(r.TokenCache, sourcev1.GitRepositoryKind,
735716
obj.GetName(), obj.GetNamespace(), cache.OperationReconcile))
736717
}
737718

738-
username, password, err := github.GetCredentials(ctx, opts...)
719+
if authMethods.HasTLS() {
720+
appOpts = append(appOpts, github.WithTLSConfig(authMethods.TLS))
721+
}
722+
723+
username, password, err := github.GetCredentials(ctx, appOpts...)
739724
if err != nil {
740725
return nil, err
741726
}
@@ -772,16 +757,16 @@ func (r *GitRepositoryReconciler) getAuthOpts(ctx context.Context, obj *sourcev1
772757
return opts, nil
773758
}
774759

775-
func (r *GitRepositoryReconciler) getSecretData(ctx context.Context, name, namespace string) (map[string][]byte, error) {
760+
func (r *GitRepositoryReconciler) getSecret(ctx context.Context, name, namespace string) (*corev1.Secret, error) {
776761
key := types.NamespacedName{
777762
Namespace: namespace,
778763
Name: name,
779764
}
780-
var secret corev1.Secret
781-
if err := r.Client.Get(ctx, key, &secret); err != nil {
782-
return nil, err
765+
secret := &corev1.Secret{}
766+
if err := r.Client.Get(ctx, key, secret); err != nil {
767+
return nil, fmt.Errorf("failed to get secret '%s/%s': %w", namespace, name, err)
783768
}
784-
return secret.Data, nil
769+
return secret, nil
785770
}
786771

787772
// reconcileArtifact archives a new Artifact to the Storage, if the current

0 commit comments

Comments
 (0)