Merge pull request #184 from fluxcd/feature/self-signed-certs
Add self signed cert to provider
This commit is contained in:
commit
490c9550c9
|
|
@ -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 (
|
||||
|
|
|
|||
|
|
@ -215,6 +215,11 @@ func (in *ProviderSpec) DeepCopyInto(out *ProviderSpec) {
|
|||
*out = new(meta.LocalObjectReference)
|
||||
**out = **in
|
||||
}
|
||||
if in.CertSecretRef != nil {
|
||||
in, out := &in.CertSecretRef, &out.CertSecretRef
|
||||
*out = new(meta.LocalObjectReference)
|
||||
**out = **in
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProviderSpec.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ package controllers
|
|||
|
||||
import (
|
||||
"context"
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
|
|
@ -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 %s", provider.Spec.CertSecretRef.Name)
|
||||
}
|
||||
|
||||
certPool = x509.NewCertPool()
|
||||
ok = certPool.AppendCertsFromPEM(caFile)
|
||||
if !ok {
|
||||
return fmt.Errorf("could not append to cert pool: invalid CA found in %s", provider.Spec.CertSecretRef.Name)
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -298,6 +298,21 @@ github.com/fluxcd/pkg/apis/meta.LocalObjectReference
|
|||
using “address” 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>
|
||||
|
|
@ -761,6 +776,21 @@ github.com/fluxcd/pkg/apis/meta.LocalObjectReference
|
|||
using “address” 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>
|
||||
|
|
|
|||
|
|
@ -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
|
||||
```
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
)
|
||||
|
|
@ -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")
|
||||
}
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
@ -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")
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,8 @@ limitations under the License.
|
|||
package notifier
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net"
|
||||
|
|
@ -30,16 +32,30 @@ 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 certPool != nil {
|
||||
httpClient.HTTPClient.Transport = &http.Transport{
|
||||
TLSClientConfig: &tls.Config{
|
||||
RootCAs: certPool,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
if proxy != "" {
|
||||
proxyURL, err := url.Parse(proxy)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to parse proxy URL '%s', error: %w", proxy, err)
|
||||
}
|
||||
var tlsConfig *tls.Config
|
||||
if certPool != nil {
|
||||
tlsConfig = &tls.Config{
|
||||
RootCAs: certPool,
|
||||
}
|
||||
}
|
||||
httpClient.HTTPClient.Transport = &http.Transport{
|
||||
Proxy: http.ProxyURL(proxyURL),
|
||||
Proxy: http.ProxyURL(proxyURL),
|
||||
TLSClientConfig: tlsConfig,
|
||||
DialContext: (&net.Dialer{
|
||||
Timeout: 15 * time.Second,
|
||||
KeepAlive: 30 * time.Second,
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ limitations under the License.
|
|||
package notifier
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
|
|
@ -42,7 +43,28 @@ 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)
|
||||
}
|
||||
|
||||
func Test_postSelfSignedCert(t *testing.T) {
|
||||
ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
b, err := ioutil.ReadAll(r.Body)
|
||||
require.NoError(t, err)
|
||||
|
||||
var payload = make(map[string]string)
|
||||
err = json.Unmarshal(b, &payload)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, "success", payload["status"])
|
||||
}))
|
||||
defer ts.Close()
|
||||
|
||||
cert, err := x509.ParseCertificate(ts.TLS.Certificates[0].Certificate[0])
|
||||
require.NoError(t, err)
|
||||
certpool := x509.NewCertPool()
|
||||
certpool.AddCert(cert)
|
||||
err = postMessage(ts.URL, "", certpool, map[string]string{"status": "success"})
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ limitations under the License.
|
|||
package notifier
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
|
||||
"github.com/fluxcd/notification-controller/api/v1beta1"
|
||||
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,10 +17,12 @@ limitations under the License.
|
|||
package notifier
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"github.com/fluxcd/pkg/runtime/events"
|
||||
"net/url"
|
||||
|
||||
"github.com/fluxcd/pkg/runtime/events"
|
||||
|
||||
"github.com/hashicorp/go-retryablehttp"
|
||||
)
|
||||
|
||||
|
|
@ -33,9 +35,10 @@ const NotificationHeader = "gotk-component"
|
|||
type Forwarder struct {
|
||||
URL string
|
||||
ProxyURL string
|
||||
CertPool *x509.CertPool
|
||||
}
|
||||
|
||||
func NewForwarder(hookURL string, proxyURL string) (*Forwarder, error) {
|
||||
func NewForwarder(hookURL string, proxyURL string, certPool *x509.CertPool) (*Forwarder, error) {
|
||||
if _, err := url.ParseRequestURI(hookURL); err != nil {
|
||||
return nil, fmt.Errorf("invalid hook URL %s: %w", hookURL, err)
|
||||
}
|
||||
|
|
@ -47,7 +50,7 @@ func NewForwarder(hookURL string, proxyURL string) (*Forwarder, error) {
|
|||
}
|
||||
|
||||
func (f *Forwarder) Post(event events.Event) error {
|
||||
err := postMessage(f.URL, f.ProxyURL, event, func(req *retryablehttp.Request) {
|
||||
err := postMessage(f.URL, f.ProxyURL, f.CertPool, event, func(req *retryablehttp.Request) {
|
||||
req.Header.Set(NotificationHeader, event.ReportingController)
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -18,12 +18,13 @@ package notifier
|
|||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/fluxcd/pkg/runtime/events"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/fluxcd/pkg/runtime/events"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
|
|
@ -41,7 +42,7 @@ func TestForwarder_Post(t *testing.T) {
|
|||
}))
|
||||
defer ts.Close()
|
||||
|
||||
forwarder, err := NewForwarder(ts.URL, "")
|
||||
forwarder, err := NewForwarder(ts.URL, "", nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = forwarder.Post(testEvent())
|
||||
|
|
|
|||
|
|
@ -18,13 +18,17 @@ package notifier
|
|||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/fluxcd/pkg/runtime/events"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/fluxcd/pkg/runtime/events"
|
||||
|
||||
"github.com/google/go-github/v32/github"
|
||||
"golang.org/x/oauth2"
|
||||
)
|
||||
|
|
@ -35,7 +39,7 @@ type GitHub struct {
|
|||
Client *github.Client
|
||||
}
|
||||
|
||||
func NewGitHub(addr string, token string) (*GitHub, error) {
|
||||
func NewGitHub(addr string, token string, certPool *x509.CertPool) (*GitHub, error) {
|
||||
if len(token) == 0 {
|
||||
return nil, errors.New("github token cannot be empty")
|
||||
}
|
||||
|
|
@ -59,6 +63,17 @@ func NewGitHub(addr string, token string) (*GitHub, error) {
|
|||
tc := oauth2.NewClient(context.Background(), ts)
|
||||
client := github.NewClient(tc)
|
||||
if baseUrl.Host != "github.com" {
|
||||
if certPool != nil {
|
||||
tr := &http.Transport{
|
||||
TLSClientConfig: &tls.Config{
|
||||
RootCAs: certPool,
|
||||
},
|
||||
}
|
||||
hc := &http.Client{Transport: tr}
|
||||
ctx := context.WithValue(context.Background(), oauth2.HTTPClient, hc)
|
||||
tc = oauth2.NewClient(ctx, ts)
|
||||
}
|
||||
|
||||
client, err = github.NewEnterpriseClient(host, host, tc)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not create enterprise GitHub client: %v", err)
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ import (
|
|||
)
|
||||
|
||||
func TestNewGitHubBasic(t *testing.T) {
|
||||
g, err := NewGitHub("https://github.com/foo/bar", "foobar")
|
||||
g, err := NewGitHub("https://github.com/foo/bar", "foobar", nil)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, g.Owner, "foo")
|
||||
assert.Equal(t, g.Repo, "bar")
|
||||
|
|
@ -32,7 +32,7 @@ func TestNewGitHubBasic(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestNewEmterpriseGitHubBasic(t *testing.T) {
|
||||
g, err := NewGitHub("https://foobar.com/foo/bar", "foobar")
|
||||
g, err := NewGitHub("https://foobar.com/foo/bar", "foobar", nil)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, g.Owner, "foo")
|
||||
assert.Equal(t, g.Repo, "bar")
|
||||
|
|
@ -40,12 +40,12 @@ func TestNewEmterpriseGitHubBasic(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestNewGitHubInvalidUrl(t *testing.T) {
|
||||
_, err := NewGitHub("https://github.com/foo/bar/baz", "foobar")
|
||||
_, err := NewGitHub("https://github.com/foo/bar/baz", "foobar", nil)
|
||||
assert.NotNil(t, err)
|
||||
}
|
||||
|
||||
func TestNewGitHubEmptyToken(t *testing.T) {
|
||||
_, err := NewGitHub("https://github.com/foo/bar", "")
|
||||
_, err := NewGitHub("https://github.com/foo/bar", "", nil)
|
||||
assert.NotNil(t, err)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -17,7 +17,10 @@ limitations under the License.
|
|||
package notifier
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
"github.com/fluxcd/pkg/runtime/events"
|
||||
"github.com/xanzy/go-gitlab"
|
||||
|
|
@ -28,7 +31,7 @@ type GitLab struct {
|
|||
Client *gitlab.Client
|
||||
}
|
||||
|
||||
func NewGitLab(addr string, token string) (*GitLab, error) {
|
||||
func NewGitLab(addr string, token string, certPool *x509.CertPool) (*GitLab, error) {
|
||||
if len(token) == 0 {
|
||||
return nil, errors.New("gitlab token cannot be empty")
|
||||
}
|
||||
|
|
@ -38,8 +41,17 @@ func NewGitLab(addr string, token string) (*GitLab, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
opt := gitlab.WithBaseURL(host)
|
||||
client, err := gitlab.NewClient(token, opt)
|
||||
opts := []gitlab.ClientOptionFunc{gitlab.WithBaseURL(host)}
|
||||
if certPool != nil {
|
||||
tr := &http.Transport{
|
||||
TLSClientConfig: &tls.Config{
|
||||
RootCAs: certPool,
|
||||
},
|
||||
}
|
||||
hc := &http.Client{Transport: tr}
|
||||
opts = append(opts, gitlab.WithHTTPClient(hc))
|
||||
}
|
||||
client, err := gitlab.NewClient(token, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,25 +23,25 @@ import (
|
|||
)
|
||||
|
||||
func TestNewGitLabBasic(t *testing.T) {
|
||||
g, err := NewGitLab("https://gitlab.com/foo/bar", "foobar")
|
||||
g, err := NewGitLab("https://gitlab.com/foo/bar", "foobar", nil)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, g.Id, "foo/bar")
|
||||
}
|
||||
|
||||
func TestNewGitLabSubgroups(t *testing.T) {
|
||||
g, err := NewGitLab("https://gitlab.com/foo/bar/baz", "foobar")
|
||||
g, err := NewGitLab("https://gitlab.com/foo/bar/baz", "foobar", nil)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, g.Id, "foo/bar/baz")
|
||||
}
|
||||
|
||||
func TestNewGitLabSelfHosted(t *testing.T) {
|
||||
g, err := NewGitLab("https://example.com/foo/bar", "foo:bar")
|
||||
g, err := NewGitLab("https://example.com/foo/bar", "foo:bar", nil)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, g.Id, "foo/bar")
|
||||
assert.Equal(t, g.Client.BaseURL().Host, "example.com")
|
||||
}
|
||||
|
||||
func TestNewGitLabEmptyToken(t *testing.T) {
|
||||
_, err := NewGitLab("https://gitlab.com/foo/bar", "")
|
||||
_, err := NewGitLab("https://gitlab.com/foo/bar", "", nil)
|
||||
assert.NotNil(t, err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -141,7 +141,7 @@ func (s *GoogleChat) Post(event events.Event) error {
|
|||
Cards: []GoogleChatCard{card},
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ limitations under the License.
|
|||
package notifier
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
|
|
@ -31,10 +32,11 @@ type Rocket struct {
|
|||
ProxyURL string
|
||||
Username string
|
||||
Channel string
|
||||
CertPool *x509.CertPool
|
||||
}
|
||||
|
||||
// NewRocket validates the Rocket URL and returns a Rocket object
|
||||
func NewRocket(hookURL string, proxyURL string, username string, channel string) (*Rocket, error) {
|
||||
func NewRocket(hookURL string, proxyURL string, certPool *x509.CertPool, username string, channel string) (*Rocket, error) {
|
||||
_, err := url.ParseRequestURI(hookURL)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid Rocket hook URL %s", hookURL)
|
||||
|
|
@ -53,6 +55,7 @@ func NewRocket(hookURL string, proxyURL string, username string, channel string)
|
|||
URL: hookURL,
|
||||
ProxyURL: proxyURL,
|
||||
Username: username,
|
||||
CertPool: certPool,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
|
@ -88,7 +91,7 @@ func (s *Rocket) Post(event events.Event) error {
|
|||
|
||||
payload.Attachments = []SlackAttachment{a}
|
||||
|
||||
err := postMessage(s.URL, s.ProxyURL, payload)
|
||||
err := postMessage(s.URL, s.ProxyURL, s.CertPool, payload)
|
||||
if err != nil {
|
||||
return fmt.Errorf("postMessage failed: %w", err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ func TestRocket_Post(t *testing.T) {
|
|||
}))
|
||||
defer ts.Close()
|
||||
|
||||
rocket, err := NewRocket(ts.URL, "", "test", "test")
|
||||
rocket, err := NewRocket(ts.URL, "", nil, "test", "test")
|
||||
require.NoError(t, err)
|
||||
|
||||
err = rocket.Post(testEvent())
|
||||
|
|
|
|||
|
|
@ -17,7 +17,11 @@ limitations under the License.
|
|||
package notifier
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/fluxcd/pkg/runtime/events"
|
||||
"github.com/getsentry/sentry-go"
|
||||
)
|
||||
|
|
@ -28,9 +32,18 @@ type Sentry struct {
|
|||
}
|
||||
|
||||
// NewSentry creates a Sentry client from the provided Data Source Name (DSN)
|
||||
func NewSentry(dsn string) (*Sentry, error) {
|
||||
func NewSentry(certPool *x509.CertPool, dsn string) (*Sentry, error) {
|
||||
var tr *http.Transport
|
||||
if certPool != nil {
|
||||
tr = &http.Transport{
|
||||
TLSClientConfig: &tls.Config{
|
||||
RootCAs: certPool,
|
||||
},
|
||||
}
|
||||
}
|
||||
client, err := sentry.NewClient(sentry.ClientOptions{
|
||||
Dsn: dsn,
|
||||
Dsn: dsn,
|
||||
HTTPTransport: tr,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
|
|||
|
|
@ -17,19 +17,20 @@ limitations under the License.
|
|||
package notifier
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/fluxcd/pkg/runtime/events"
|
||||
"github.com/getsentry/sentry-go"
|
||||
"github.com/stretchr/testify/assert"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestNewSentry(t *testing.T) {
|
||||
s, err := NewSentry("https://test@localhost/1")
|
||||
s, err := NewSentry(nil, "https://test@localhost/1")
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, s.Client.Options().Dsn, "https://test@localhost/1")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -112,7 +112,7 @@ func (s *Slack) 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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -98,7 +98,7 @@ func (s *MSTeams) Post(event events.Event) error {
|
|||
payload.ThemeColor = "FF0000"
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ limitations under the License.
|
|||
package notifier
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
|
@ -28,6 +29,7 @@ import (
|
|||
type Webex struct {
|
||||
URL string
|
||||
ProxyURL string
|
||||
CertPool *x509.CertPool
|
||||
}
|
||||
|
||||
// WebexPayload holds the message text
|
||||
|
|
@ -37,7 +39,7 @@ type WebexPayload struct {
|
|||
}
|
||||
|
||||
// NewWebex validates the Webex URL and returns a Webex object
|
||||
func NewWebex(hookURL, proxyURL string) (*Webex, error) {
|
||||
func NewWebex(hookURL, proxyURL string, certPool *x509.CertPool) (*Webex, error) {
|
||||
_, err := url.ParseRequestURI(hookURL)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid Webex hook URL %s", hookURL)
|
||||
|
|
@ -71,7 +73,7 @@ func (s *Webex) Post(event events.Event) error {
|
|||
Markdown: markdown,
|
||||
}
|
||||
|
||||
if err := postMessage(s.URL, s.ProxyURL, payload); err != nil {
|
||||
if err := postMessage(s.URL, s.ProxyURL, s.CertPool, payload); err != nil {
|
||||
return fmt.Errorf("postMessage failed: %w", err)
|
||||
}
|
||||
return nil
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ func TestWebex_Post(t *testing.T) {
|
|||
}))
|
||||
defer ts.Close()
|
||||
|
||||
webex, err := NewWebex(ts.URL, "")
|
||||
webex, err := NewWebex(ts.URL, "", nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = webex.Post(testEvent())
|
||||
|
|
@ -47,7 +47,7 @@ func TestWebex_Post(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestWebex_PostUpdate(t *testing.T) {
|
||||
webex, err := NewWebex("http://localhost", "")
|
||||
webex, err := NewWebex("http://localhost", "", nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
event := testEvent()
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ package server
|
|||
|
||||
import (
|
||||
"context"
|
||||
"crypto/x509"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
|
|
@ -156,6 +157,40 @@ func (s *EventServer) handleEvent() func(w http.ResponseWriter, r *http.Request)
|
|||
}
|
||||
}
|
||||
|
||||
var certPool *x509.CertPool
|
||||
if provider.Spec.CertSecretRef != nil {
|
||||
var secret corev1.Secret
|
||||
secretName := types.NamespacedName{Namespace: alert.Namespace, Name: provider.Spec.CertSecretRef.Name}
|
||||
|
||||
err = s.kubeClient.Get(ctx, secretName, &secret)
|
||||
if err != nil {
|
||||
s.logger.Error(err, "failed to read secret",
|
||||
"reconciler kind", v1beta1.ProviderKind,
|
||||
"name", providerName.Name,
|
||||
"namespace", providerName.Namespace)
|
||||
continue
|
||||
}
|
||||
|
||||
caFile, ok := secret.Data["caFile"]
|
||||
if !ok {
|
||||
s.logger.Error(err, "failed to read secret key caFile",
|
||||
"reconciler kind", v1beta1.ProviderKind,
|
||||
"name", providerName.Name,
|
||||
"namespace", providerName.Namespace)
|
||||
continue
|
||||
}
|
||||
|
||||
certPool = x509.NewCertPool()
|
||||
ok = certPool.AppendCertsFromPEM(caFile)
|
||||
if !ok {
|
||||
s.logger.Error(err, "could not append to cert pool",
|
||||
"reconciler kind", v1beta1.ProviderKind,
|
||||
"name", providerName.Name,
|
||||
"namespace", providerName.Namespace)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if webhook == "" {
|
||||
s.logger.Error(nil, "provider has no address",
|
||||
"reconciler kind", v1beta1.ProviderKind,
|
||||
|
|
@ -164,7 +199,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)
|
||||
factory := notifier.NewFactory(webhook, provider.Spec.Proxy, provider.Spec.Username, provider.Spec.Channel, token, certPool)
|
||||
sender, err := factory.Notifier(provider.Spec.Type)
|
||||
if err != nil {
|
||||
s.logger.Error(err, "failed to initialise provider",
|
||||
|
|
|
|||
Loading…
Reference in New Issue