Skip to content

Commit

Permalink
Pass headers to generic provider through secretRef
Browse files Browse the repository at this point in the history
Co-authored-by: Moritz Schmitz von H<C3><BC>lst <moritz@hauptstadtoffice.com>
Signed-off-by: Somtochi Onyekwere <somtochionyekwere@gmail.com>
  • Loading branch information
Moritz Schmitz von Hülst authored and somtochiama committed Jan 26, 2022
1 parent ea37642 commit 405a43c
Show file tree
Hide file tree
Showing 10 changed files with 88 additions and 10 deletions.
3 changes: 2 additions & 1 deletion .github/workflows/e2e.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,8 @@ jobs:
- name: Run smoke tests
run: |
kubectl -n notification-system apply -f ./config/samples
kubectl -n notification-system wait provider/provider-sample --for=condition=ready --timeout=1m
kubectl -n notification-system wait provider/slack-provider-sample --for=condition=ready --timeout=1m
kubectl -n notification-system wait provider/generic-provider-sample --for=condition=ready --timeout=1m
kubectl -n notification-system wait alert/alert-sample --for=condition=ready --timeout=1m
kubectl -n notification-system wait receiver/receiver-sample --for=condition=ready --timeout=1m
- name: Logs
Expand Down
20 changes: 19 additions & 1 deletion config/samples/notification_v1beta1_provider.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
apiVersion: notification.toolkit.fluxcd.io/v1beta1
kind: Provider
metadata:
name: provider-sample
name: slack-provider-sample
spec:
type: slack
channel: general
Expand All @@ -14,3 +14,21 @@ metadata:
name: slack-url
data:
address: aHR0cHM6Ly9ob29rcy5zbGFjay5jb20vc2VydmljZXMv
---
apiVersion: notification.toolkit.fluxcd.io/v1beta1
kind: Provider
metadata:
name: generic-provider-sample
spec:
type: generic
address: https://api.github.com/repos/fluxcd/notification-controller/dispatches
secretRef:
name: generic-secret
---
apiVersion: v1
kind: Secret
metadata:
name: generic-secret
stringData:
headers: |
Authorization: token
11 changes: 10 additions & 1 deletion controllers/provider_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import (
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller"
"sigs.k8s.io/controller-runtime/pkg/predicate"
"sigs.k8s.io/yaml"

"github.com/fluxcd/pkg/apis/meta"
"github.com/fluxcd/pkg/runtime/conditions"
Expand Down Expand Up @@ -148,6 +149,7 @@ func (r *ProviderReconciler) reconcile(ctx context.Context, obj *v1beta1.Provide
func (r *ProviderReconciler) validate(ctx context.Context, provider *v1beta1.Provider) error {
address := provider.Spec.Address
token := ""
headers := make(map[string]string)
if provider.Spec.SecretRef != nil {
var secret corev1.Secret
secretName := types.NamespacedName{Namespace: provider.Namespace, Name: provider.Spec.SecretRef.Name}
Expand All @@ -163,6 +165,13 @@ func (r *ProviderReconciler) validate(ctx context.Context, provider *v1beta1.Pro
if t, ok := secret.Data["token"]; ok {
token = string(t)
}

if h, ok := secret.Data["headers"]; ok {
err := yaml.Unmarshal(h, headers)
if err != nil {
return fmt.Errorf("failed to read headers from secret, error: %w", err)
}
}
}

if address == "" {
Expand Down Expand Up @@ -190,7 +199,7 @@ func (r *ProviderReconciler) validate(ctx context.Context, provider *v1beta1.Pro
}
}

factory := notifier.NewFactory(address, provider.Spec.Proxy, provider.Spec.Username, provider.Spec.Channel, token, certPool)
factory := notifier.NewFactory(address, provider.Spec.Proxy, provider.Spec.Username, provider.Spec.Channel, token, headers, certPool)
if _, err := factory.Notifier(provider.Spec.Type); err != nil {
return fmt.Errorf("failed to initialize provider, error: %w", err)
}
Expand Down
26 changes: 26 additions & 0 deletions docs/spec/v1beta1/provider.md
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,32 @@ The body of the request looks like this:

The `involvedObject` key contains the object that triggered the event.

You can add additional headers to the POST request by providing a `headers` field to the secret
referenced by the provider. An example is given below:

```yaml
apiVersion: notification.toolkit.fluxcd.io/v1beta1
kind: Provider
metadata:
name: generic
namespace: default
spec:
type: generic
address: https://api.github.com/repos/owner/repo/dispatches
secretRef:
name: generic-secret
---
apiVersion: v1
kind: Secret
metadata:
name: generic-secret
namespace: default
stringData:
headers: |
Authorization: token
X-Forwarded-Proto: https
```
### Self-signed certificates
The `certSecretRef` field names a secret with TLS certificate data. This is for the purpose
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ require (
sigs.k8s.io/kustomize/api v0.10.1 // indirect
sigs.k8s.io/kustomize/kyaml v0.13.0 // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.2.0 // indirect
sigs.k8s.io/yaml v1.3.0 // indirect
sigs.k8s.io/yaml v1.3.0
)

// Fix for CVE-2020-29652: https://github.com/golang/crypto/commit/8b5274cf687fd9316b4108863654cc57385531e8
Expand Down
6 changes: 4 additions & 2 deletions internal/notifier/factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,16 +29,18 @@ type Factory struct {
Username string
Channel string
Token string
Headers map[string]string
CertPool *x509.CertPool
}

func NewFactory(url string, proxy string, username string, channel string, token string, certPool *x509.CertPool) *Factory {
func NewFactory(url string, proxy string, username string, channel string, token string, headers map[string]string, certPool *x509.CertPool) *Factory {
return &Factory{
URL: url,
ProxyURL: proxy,
Channel: channel,
Username: username,
Token: token,
Headers: headers,
CertPool: certPool,
}
}
Expand All @@ -52,7 +54,7 @@ func (f Factory) Notifier(provider string) (Interface, error) {
var err error
switch provider {
case v1beta1.GenericProvider:
n, err = NewForwarder(f.URL, f.ProxyURL, f.CertPool)
n, err = NewForwarder(f.URL, f.ProxyURL, f.Headers, f.CertPool)
case v1beta1.SlackProvider:
n, err = NewSlack(f.URL, f.ProxyURL, f.Token, f.CertPool, f.Username, f.Channel)
case v1beta1.DiscordProvider:
Expand Down
7 changes: 6 additions & 1 deletion internal/notifier/forwarder.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,24 +35,29 @@ const NotificationHeader = "gotk-component"
type Forwarder struct {
URL string
ProxyURL string
Headers map[string]string
CertPool *x509.CertPool
}

func NewForwarder(hookURL string, proxyURL string, certPool *x509.CertPool) (*Forwarder, error) {
func NewForwarder(hookURL string, proxyURL string, headers map[string]string, certPool *x509.CertPool) (*Forwarder, error) {
if _, err := url.ParseRequestURI(hookURL); err != nil {
return nil, fmt.Errorf("invalid hook URL %s: %w", hookURL, err)
}

return &Forwarder{
URL: hookURL,
ProxyURL: proxyURL,
Headers: headers,
CertPool: certPool,
}, nil
}

func (f *Forwarder) Post(event events.Event) error {
err := postMessage(f.URL, f.ProxyURL, f.CertPool, event, func(req *retryablehttp.Request) {
req.Header.Set(NotificationHeader, event.ReportingController)
for key, val := range f.Headers {
req.Header.Set(key, val)
}
})

if err != nil {
Expand Down
5 changes: 4 additions & 1 deletion internal/notifier/forwarder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ func TestForwarder_Post(t *testing.T) {
require.NoError(t, err)

require.Equal(t, "source-controller", r.Header.Get("gotk-component"))
require.Equal(t, "token", r.Header.Get("Authorization"))
var payload = events.Event{}
err = json.Unmarshal(b, &payload)
require.NoError(t, err)
Expand All @@ -42,7 +43,9 @@ func TestForwarder_Post(t *testing.T) {
}))
defer ts.Close()

forwarder, err := NewForwarder(ts.URL, "", nil)
headers := make(map[string]string)
headers["Authorization"] = "token"
forwarder, err := NewForwarder(ts.URL, "", headers, nil)
require.NoError(t, err)

err = forwarder.Post(testEvent())
Expand Down
15 changes: 14 additions & 1 deletion internal/server/event_handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import (
"github.com/fluxcd/pkg/runtime/conditions"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/yaml"

"github.com/fluxcd/pkg/runtime/events"

Expand Down Expand Up @@ -139,6 +140,7 @@ func (s *EventServer) handleEvent() func(w http.ResponseWriter, r *http.Request)

webhook := provider.Spec.Address
token := ""
headers := make(map[string]string)
if provider.Spec.SecretRef != nil {
var secret corev1.Secret
secretName := types.NamespacedName{Namespace: alert.Namespace, Name: provider.Spec.SecretRef.Name}
Expand All @@ -159,6 +161,17 @@ func (s *EventServer) handleEvent() func(w http.ResponseWriter, r *http.Request)
if t, ok := secret.Data["token"]; ok {
token = string(t)
}

if h, ok := secret.Data["headers"]; ok {
err := yaml.Unmarshal(h, headers)
if err != nil {
s.logger.Error(err, "failed to read headers from secret",
"reconciler kind", v1beta1.ProviderKind,
"name", providerName.Name,
"namespace", providerName.Namespace)
continue
}
}
}

var certPool *x509.CertPool
Expand Down Expand Up @@ -203,7 +216,7 @@ func (s *EventServer) handleEvent() func(w http.ResponseWriter, r *http.Request)
continue
}

factory := notifier.NewFactory(webhook, provider.Spec.Proxy, provider.Spec.Username, provider.Spec.Channel, token, certPool)
factory := notifier.NewFactory(webhook, provider.Spec.Proxy, provider.Spec.Username, provider.Spec.Channel, token, headers, certPool)
sender, err := factory.Notifier(provider.Spec.Type)
if err != nil {
s.logger.Error(err, "failed to initialize provider",
Expand Down
3 changes: 2 additions & 1 deletion tests/fuzz/forwarder_fuzzer.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ func FuzzForwarder(data []byte) int {
}))
defer ts.Close()

forwarder, err := NewForwarder(ts.URL, "", nil)
header := make(map[string]string)
forwarder, err := NewForwarder(ts.URL, "", header, nil)
if err != nil {
return 0
}
Expand Down

0 comments on commit 405a43c

Please sign in to comment.