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 self signed cert to provider #184

Merged
merged 4 commits into from
Apr 21, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
5 changes: 5 additions & 0 deletions api/v1beta1/provider_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,11 @@ type ProviderSpec struct {
// using "address" as data key
// +optional
SecretRef *meta.LocalObjectReference `json:"secretRef,omitempty"`

// CertSecretRef can be given the name of a secret containing
// a PEM-encoded CA certificate (`caFile`)
// +optional
CertSecretRef *meta.LocalObjectReference `json:"certSecretRef,omitempty"`
}

const (
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.

10 changes: 10 additions & 0 deletions config/crd/bases/notification.toolkit.fluxcd.io_providers.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,16 @@ spec:
description: HTTP/S webhook address of this provider
pattern: ^(http|https)://
type: string
certSecretRef:
description: CertSecretRef can be given the name of a secret containing
a PEM-encoded CA certificate (`caFile`)
properties:
name:
description: Name of the referent
type: string
required:
- name
type: object
channel:
description: Alert channel for this provider
type: string
Expand Down
24 changes: 23 additions & 1 deletion controllers/provider_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package controllers

import (
"context"
"crypto/x509"
"fmt"
"time"

Expand Down Expand Up @@ -121,7 +122,28 @@ func (r *ProviderReconciler) validate(ctx context.Context, provider v1beta1.Prov
return fmt.Errorf("no address found in 'spec.address' nor in `spec.secretRef`")
}

factory := notifier.NewFactory(address, provider.Spec.Proxy, provider.Spec.Username, provider.Spec.Channel, token)
var certPool *x509.CertPool
if provider.Spec.CertSecretRef != nil {
var secret corev1.Secret
secretName := types.NamespacedName{Namespace: provider.Namespace, Name: provider.Spec.CertSecretRef.Name}

if err := r.Get(ctx, secretName, &secret); err != nil {
return fmt.Errorf("failed to read secret, error: %w", err)
}

caFile, ok := secret.Data["caFile"]
if !ok {
return fmt.Errorf("no caFile found in secret %q", provider.Spec.CertSecretRef.Name)
}

certPool = x509.NewCertPool()
ok = certPool.AppendCertsFromPEM(caFile)
if !ok {
return fmt.Errorf("could not append to cert pool")
}
}

factory := notifier.NewFactory(address, provider.Spec.Proxy, provider.Spec.Username, provider.Spec.Channel, token, certPool)
if _, err := factory.Notifier(provider.Spec.Type); err != nil {
return fmt.Errorf("failed to initialise provider, error: %w", err)
}
Expand Down
30 changes: 30 additions & 0 deletions docs/api/notification.md
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,21 @@ github.com/fluxcd/pkg/apis/meta.LocalObjectReference
using &ldquo;address&rdquo; as data key</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 can be given the name of a secret containing
a PEM-encoded CA certificate (<code>caFile</code>)</p>
</td>
</tr>
</table>
</td>
</tr>
Expand Down Expand Up @@ -761,6 +776,21 @@ github.com/fluxcd/pkg/apis/meta.LocalObjectReference
using &ldquo;address&rdquo; as data key</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 can be given the name of a secret containing
a PEM-encoded CA certificate (<code>caFile</code>)</p>
</td>
</tr>
</tbody>
</table>
</div>
Expand Down
13 changes: 13 additions & 0 deletions docs/spec/v1beta1/provider.md
Original file line number Diff line number Diff line change
Expand Up @@ -209,3 +209,16 @@ The body of the request looks like this:
```

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

### Self signed certificates

The `certSecretRef` field names a secret with TLS certificate data. This is for the purpose
of enabling a provider to communicate with a server using a self signed cert.

To use the field create a secret, containing a CA file, in the same namespace and reference
it from the provider.
```shell
SECRET_NAME=tls-certs
kubectl create secret generic $SECRET_NAME \
--from-file=caFile=ca.crt
```
12 changes: 10 additions & 2 deletions internal/notifier/azure_devops.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,15 @@ package notifier

import (
"context"
"crypto/tls"
"crypto/x509"
"errors"
"fmt"
"github.com/fluxcd/pkg/runtime/events"
"strings"
"time"

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

"github.com/microsoft/azure-devops-go-api/azuredevops"
"github.com/microsoft/azure-devops-go-api/azuredevops/git"
)
Expand All @@ -38,7 +41,7 @@ type AzureDevOps struct {
}

// NewAzureDevOps creates and returns a new AzureDevOps notifier.
func NewAzureDevOps(addr string, token string) (*AzureDevOps, error) {
func NewAzureDevOps(addr string, token string, certPool *x509.CertPool) (*AzureDevOps, error) {
if len(token) == 0 {
return nil, errors.New("azure devops token cannot be empty")
}
Expand All @@ -58,6 +61,11 @@ func NewAzureDevOps(addr string, token string) (*AzureDevOps, error) {

orgURL := fmt.Sprintf("%v/%v", host, org)
connection := azuredevops.NewPatConnection(orgURL, token)
if certPool != nil {
connection.TlsConfig = &tls.Config{
RootCAs: certPool,
}
}
client := connection.GetClientByUrl(orgURL)
gitClient := &git.ClientImpl{
Client: *client,
Expand Down
6 changes: 3 additions & 3 deletions internal/notifier/azure_devops_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,19 +24,19 @@ import (
)

func TestNewAzureDevOpsBasic(t *testing.T) {
a, err := NewAzureDevOps("https://dev.azure.com/foo/bar/_git/baz", "foo")
a, err := NewAzureDevOps("https://dev.azure.com/foo/bar/_git/baz", "foo", nil)
assert.Nil(t, err)
assert.Equal(t, a.Project, "bar")
assert.Equal(t, a.Repo, "baz")
}

func TestNewAzureDevOpsInvalidUrl(t *testing.T) {
_, err := NewAzureDevOps("https://dev.azure.com/foo/bar/baz", "foo")
_, err := NewAzureDevOps("https://dev.azure.com/foo/bar/baz", "foo", nil)
assert.NotNil(t, err)
}

func TestNewAzureDevOpsMissingToken(t *testing.T) {
_, err := NewAzureDevOps("https://dev.azure.com/foo/bar/baz", "")
_, err := NewAzureDevOps("https://dev.azure.com/foo/bar/baz", "", nil)
assert.NotNil(t, err)
}

Expand Down
18 changes: 16 additions & 2 deletions internal/notifier/bitbucket.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,11 @@ limitations under the License.
package notifier

import (
"crypto/tls"
"crypto/x509"
"errors"
"fmt"
"net/http"
"strings"

"github.com/fluxcd/pkg/runtime/events"
Expand All @@ -33,7 +36,7 @@ type Bitbucket struct {
}

// NewBitbucket creates and returns a new Bitbucket notifier.
func NewBitbucket(addr string, token string) (*Bitbucket, error) {
func NewBitbucket(addr string, token string, certPool *x509.CertPool) (*Bitbucket, error) {
if len(token) == 0 {
return nil, errors.New("bitbucket token cannot be empty")
}
Expand All @@ -57,10 +60,21 @@ func NewBitbucket(addr string, token string) (*Bitbucket, error) {
owner := comp[0]
repo := comp[1]

client := bitbucket.NewBasicAuth(username, password)
if certPool != nil {
tr := &http.Transport{
TLSClientConfig: &tls.Config{
RootCAs: certPool,
},
}
hc := &http.Client{Transport: tr}
client.HttpClient = hc
}

return &Bitbucket{
Owner: owner,
Repo: repo,
Client: bitbucket.NewBasicAuth(username, password),
Client: client,
}, nil
}

Expand Down
6 changes: 3 additions & 3 deletions internal/notifier/bitbucket_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,18 +23,18 @@ import (
)

func TestNewBitbucketBasic(t *testing.T) {
b, err := NewBitbucket("https://bitbucket.org/foo/bar", "foo:bar")
b, err := NewBitbucket("https://bitbucket.org/foo/bar", "foo:bar", nil)
assert.Nil(t, err)
assert.Equal(t, b.Owner, "foo")
assert.Equal(t, b.Repo, "bar")
}

func TestNewBitbucketInvalidUrl(t *testing.T) {
_, err := NewBitbucket("https://bitbucket.org/foo/bar/baz", "foo:bar")
_, err := NewBitbucket("https://bitbucket.org/foo/bar/baz", "foo:bar", nil)
assert.NotNil(t, err)
}

func TestNewBitbucketInvalidToken(t *testing.T) {
_, err := NewBitbucket("https://bitbucket.org/foo/bar", "bar")
_, err := NewBitbucket("https://bitbucket.org/foo/bar", "bar", nil)
assert.NotNil(t, err)
}
7 changes: 6 additions & 1 deletion internal/notifier/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ limitations under the License.
package notifier

import (
"crypto/tls"
"crypto/x509"
"encoding/json"
"fmt"
"net"
Expand All @@ -30,7 +32,7 @@ import (

type requestOptFunc func(*retryablehttp.Request)

func postMessage(address, proxy string, payload interface{}, reqOpts ...requestOptFunc) error {
func postMessage(address, proxy string, certPool *x509.CertPool, payload interface{}, reqOpts ...requestOptFunc) error {
httpClient := retryablehttp.NewClient()

if proxy != "" {
Expand All @@ -40,6 +42,9 @@ func postMessage(address, proxy string, payload interface{}, reqOpts ...requestO
}
httpClient.HTTPClient.Transport = &http.Transport{
Proxy: http.ProxyURL(proxyURL),
TLSClientConfig: &tls.Config{
RootCAs: certPool,
},
DialContext: (&net.Dialer{
Timeout: 15 * time.Second,
KeepAlive: 30 * time.Second,
Expand Down
2 changes: 1 addition & 1 deletion internal/notifier/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ func Test_postMessage(t *testing.T) {
require.Equal(t, "success", payload["status"])
}))
defer ts.Close()
err := postMessage(ts.URL, "", map[string]string{"status": "success"})
err := postMessage(ts.URL, "", nil, map[string]string{"status": "success"})
require.NoError(t, err)
}

Expand Down
2 changes: 1 addition & 1 deletion internal/notifier/discord.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ func (s *Discord) Post(event events.Event) error {

payload.Attachments = []SlackAttachment{a}

err := postMessage(s.URL, s.ProxyURL, payload)
err := postMessage(s.URL, s.ProxyURL, nil, payload)
if err != nil {
return fmt.Errorf("postMessage failed: %w", err)
}
Expand Down
21 changes: 12 additions & 9 deletions internal/notifier/factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ limitations under the License.
package notifier

import (
"crypto/x509"
"fmt"

"github.com/fluxcd/notification-controller/api/v1beta1"
Expand All @@ -28,15 +29,17 @@ type Factory struct {
Username string
Channel string
Token string
CertPool *x509.CertPool
}

func NewFactory(url string, proxy string, username string, channel string, token string) *Factory {
func NewFactory(url string, proxy string, username string, channel string, token string, certPool *x509.CertPool) *Factory {
return &Factory{
URL: url,
ProxyURL: proxy,
Channel: channel,
Username: username,
Token: token,
CertPool: certPool,
}
}

Expand All @@ -49,29 +52,29 @@ func (f Factory) Notifier(provider string) (Interface, error) {
var err error
switch provider {
case v1beta1.GenericProvider:
n, err = NewForwarder(f.URL, f.ProxyURL)
n, err = NewForwarder(f.URL, f.ProxyURL, f.CertPool)
case v1beta1.SlackProvider:
n, err = NewSlack(f.URL, f.ProxyURL, f.Username, f.Channel)
case v1beta1.DiscordProvider:
n, err = NewDiscord(f.URL, f.ProxyURL, f.Username, f.Channel)
case v1beta1.RocketProvider:
n, err = NewRocket(f.URL, f.ProxyURL, f.Username, f.Channel)
n, err = NewRocket(f.URL, f.ProxyURL, f.CertPool, f.Username, f.Channel)
case v1beta1.MSTeamsProvider:
n, err = NewMSTeams(f.URL, f.ProxyURL)
case v1beta1.GitHubProvider:
n, err = NewGitHub(f.URL, f.Token)
n, err = NewGitHub(f.URL, f.Token, f.CertPool)
case v1beta1.GitLabProvider:
n, err = NewGitLab(f.URL, f.Token)
n, err = NewGitLab(f.URL, f.Token, f.CertPool)
case v1beta1.BitbucketProvider:
n, err = NewBitbucket(f.URL, f.Token)
n, err = NewBitbucket(f.URL, f.Token, f.CertPool)
case v1beta1.AzureDevOpsProvider:
n, err = NewAzureDevOps(f.URL, f.Token)
n, err = NewAzureDevOps(f.URL, f.Token, f.CertPool)
case v1beta1.GoogleChatProvider:
n, err = NewGoogleChat(f.URL, f.ProxyURL)
case v1beta1.WebexProvider:
n, err = NewWebex(f.URL, f.ProxyURL)
n, err = NewWebex(f.URL, f.ProxyURL, f.CertPool)
case v1beta1.SentryProvider:
n, err = NewSentry(f.URL)
n, err = NewSentry(f.CertPool, f.URL)
default:
err = fmt.Errorf("provider %s not supported", provider)
}
Expand Down
Loading