Slack bot token authentication

Signed-off-by: Ricardo Lorenzo <rlorenzo@payfone.com>
This commit is contained in:
Ricardo Lorenzo
2022-09-21 16:42:53 +01:00
parent 89b0487376
commit 3dd667f3b3
12 changed files with 39 additions and 16 deletions

View File

@@ -66,6 +66,7 @@ var (
msteamsProxyURL string
includeLabelPrefix string
slackURL string
slackToken string
slackProxyURL string
slackUser string
slackChannel string
@@ -97,6 +98,7 @@ func init() {
flag.StringVar(&logLevel, "log-level", "debug", "Log level can be: debug, info, warning, error.")
flag.StringVar(&port, "port", "8080", "Port to listen on.")
flag.StringVar(&slackURL, "slack-url", "", "Slack hook URL.")
flag.StringVar(&slackToken, "slack-token", "", "Slack bot token.")
flag.StringVar(&slackProxyURL, "slack-proxy-url", "", "Slack proxy URL.")
flag.StringVar(&slackUser, "slack-user", "flagger", "Slack user name.")
flag.StringVar(&slackChannel, "slack-channel", "", "Slack channel.")
@@ -365,6 +367,7 @@ func startLeaderElection(ctx context.Context, run func(), ns string, kubeClient
func initNotifier(logger *zap.SugaredLogger) (client notifier.Interface) {
provider := "slack"
token := fromEnv("SLACK_TOKEN", slackToken)
notifierURL := fromEnv("SLACK_URL", slackURL)
notifierProxyURL := fromEnv("SLACK_PROXY_URL", slackProxyURL)
if msteamsURL != "" || os.Getenv("MSTEAMS_URL") != "" {
@@ -372,7 +375,7 @@ func initNotifier(logger *zap.SugaredLogger) (client notifier.Interface) {
notifierURL = fromEnv("MSTEAMS_URL", msteamsURL)
notifierProxyURL = fromEnv("MSTEAMS_PROXY_URL", msteamsProxyURL)
}
notifierFactory := notifier.NewFactory(notifierURL, notifierProxyURL, slackUser, slackChannel)
notifierFactory := notifier.NewFactory(notifierURL, token, notifierProxyURL, slackUser, slackChannel)
var err error
client, err = notifierFactory.Notifier(provider)

View File

@@ -37,6 +37,8 @@ or if the analysis reached the maximum number of failed checks:
![Slack Notifications](https://raw.githubusercontent.com/fluxcd/flagger/main/docs/screens/slack-canary-failed.png)
For using a Slack bot token, you should add `token` to a secret and use **secretRef**.
### Microsoft Teams
Flagger can be configured to send notifications to Microsoft Teams:
@@ -73,6 +75,7 @@ spec:
channel: on-call-alerts
username: flagger
# webhook address (ignored if secretRef is specified)
# or https://slack.com/api/chat.postMessage if you use token in the secret
address: https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK
# optional http/s proxy
proxy: http://my-http-proxy.com
@@ -87,6 +90,7 @@ metadata:
namespace: flagger
data:
address: <encoded-url>
token: <encoded-token>
```
The alert provider **type** can be: `slack`, `msteams`, `rocket` or `discord`. When set to `discord`,

View File

@@ -123,6 +123,10 @@ func (c *Controller) alert(canary *flaggerv1.Canary, message string, metadata bo
// set hook URL address
url := provider.Spec.Address
// set the token which will be sent in the header
// https://datatracker.ietf.org/doc/html/rfc6750
token := ""
// extract address from secret
if provider.Spec.SecretRef != nil {
secret, err := c.kubeClient.CoreV1().Secrets(providerNamespace).Get(context.TODO(), provider.Spec.SecretRef.Name, metav1.GetOptions{})
@@ -138,6 +142,10 @@ func (c *Controller) alert(canary *flaggerv1.Canary, message string, metadata bo
Errorf("alert provider %s.%s secret does not contain an address", alert.ProviderRef.Name, providerNamespace)
continue
}
if tokenFromSecret, ok := secret.Data["token"]; ok {
token = string(tokenFromSecret)
}
}
// set defaults
@@ -155,7 +163,7 @@ func (c *Controller) alert(canary *flaggerv1.Canary, message string, metadata bo
}
// create notifier based on provider type
f := notifier.NewFactory(url, proxy, username, channel)
f := notifier.NewFactory(url, token, proxy, username, channel)
n, err := f.Notifier(provider.Spec.Type)
if err != nil {
c.logger.With("canary", fmt.Sprintf("%s.%s", canary.Name, canary.Namespace)).

View File

@@ -29,7 +29,7 @@ import (
"time"
)
func postMessage(address string, proxy string, payload interface{}) error {
func postMessage(address, token, proxy string, payload interface{}) error {
var httpClient = &http.Client{}
if proxy != "" {
@@ -64,6 +64,10 @@ func postMessage(address string, proxy string, payload interface{}) error {
}
req.Header.Set("Content-type", "application/json")
if token != "" {
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token))
}
ctx, cancel := context.WithTimeout(req.Context(), 5*time.Second)
defer cancel()

View File

@@ -39,6 +39,6 @@ func Test_postMessage(t *testing.T) {
}))
defer ts.Close()
err := postMessage(ts.URL, "", map[string]string{"status": "success"})
err := postMessage(ts.URL, "", "", map[string]string{"status": "success"})
require.NoError(t, err)
}

View File

@@ -90,7 +90,7 @@ func (s *Discord) Post(workload string, namespace string, message string, fields
payload.Attachments = []SlackAttachment{a}
err := postMessage(s.URL, s.ProxyURL, payload)
err := postMessage(s.URL, "", s.ProxyURL, payload)
if err != nil {
return fmt.Errorf("postMessage failed: %w", err)
}

View File

@@ -22,14 +22,16 @@ import (
type Factory struct {
URL string
Token string
ProxyURL string
Username string
Channel string
}
func NewFactory(url string, proxy string, username string, channel string) *Factory {
func NewFactory(url, token, proxy, username, channel string) *Factory {
return &Factory{
URL: url,
Token: token,
ProxyURL: proxy,
Channel: channel,
Username: username,
@@ -45,7 +47,7 @@ func (f Factory) Notifier(provider string) (Interface, error) {
var err error
switch provider {
case "slack":
n, err = NewSlack(f.URL, f.ProxyURL, f.Username, f.Channel)
n, err = NewSlack(f.URL, f.Token, f.ProxyURL, f.Username, f.Channel)
case "discord":
n, err = NewDiscord(f.URL, f.ProxyURL, f.Username, f.Channel)
case "rocket":

View File

@@ -111,7 +111,7 @@ func (s *GChat) Post(workload string, namespace string, message string, fields [
},
}
err := postMessage(s.URL, s.ProxyURL, payload)
err := postMessage(s.URL, "", s.ProxyURL, payload)
if err != nil {
return fmt.Errorf("postMessage failed: %w", err)
}

View File

@@ -81,7 +81,7 @@ func (s *Rocket) Post(workload string, namespace string, message string, fields
payload.Attachments = []SlackAttachment{a}
err := postMessage(s.URL, s.ProxyURL, payload)
err := postMessage(s.URL, "", s.ProxyURL, payload)
if err != nil {
return fmt.Errorf("postMessage failed: %w", err)
}

View File

@@ -25,6 +25,7 @@ import (
// Slack holds the hook URL
type Slack struct {
URL string
Token string
ProxyURL string
Username string
Channel string
@@ -56,10 +57,10 @@ type SlackField struct {
}
// NewSlack validates the Slack URL and returns a Slack object
func NewSlack(hookURL string, proxyURL string, username string, channel string) (*Slack, error) {
_, err := url.ParseRequestURI(hookURL)
func NewSlack(address, token, proxyURL, username, channel string) (*Slack, error) {
_, err := url.ParseRequestURI(address)
if err != nil {
return nil, fmt.Errorf("invalid Slack hook URL %s", hookURL)
return nil, fmt.Errorf("invalid Slack hook URL %s", address)
}
if username == "" {
@@ -72,7 +73,8 @@ func NewSlack(hookURL string, proxyURL string, username string, channel string)
return &Slack{
Channel: channel,
URL: hookURL,
URL: address,
Token: token,
ProxyURL: proxyURL,
Username: username,
}, nil
@@ -106,7 +108,7 @@ func (s *Slack) Post(workload string, namespace string, message string, fields [
payload.Attachments = []SlackAttachment{a}
err := postMessage(s.URL, s.ProxyURL, payload)
err := postMessage(s.URL, s.Token, s.ProxyURL, payload)
if err != nil {
return fmt.Errorf("postMessage failed: %w", err)
}

View File

@@ -44,7 +44,7 @@ func TestSlack_Post(t *testing.T) {
}))
defer ts.Close()
slack, err := NewSlack(ts.URL, "", "test", "test")
slack, err := NewSlack(ts.URL, "", "", "test", "test")
require.NoError(t, err)
err = slack.Post("podinfo", "test", "test", fields, "error")

View File

@@ -86,7 +86,7 @@ func (s *MSTeams) Post(workload string, namespace string, message string, fields
payload.ThemeColor = "FF0000"
}
err := postMessage(s.URL, s.ProxyURL, payload)
err := postMessage(s.URL, "", s.ProxyURL, payload)
if err != nil {
return fmt.Errorf("postMessage failed: %w", err)
}