SecretScan: Remove placeholder image and polish errors (#61785)

* remove placeholder image

* improve http client options

* add http clients options for webhook notifier

* ensure http is only used in dev mode

* cleanup errors
This commit is contained in:
Jo 2023-01-19 17:33:27 +00:00 committed by GitHub
parent ed076adde5
commit e2ec219f6a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 81 additions and 20 deletions

View File

@ -78,7 +78,13 @@ func ProvideServiceAccountsService(
s.secretScanInterval = cfg.SectionWithEnvOverrides("secretscan").
Key("interval").MustDuration(defaultSecretScanInterval)
if s.secretScanEnabled {
s.secretScanService = secretscan.NewService(s.store, cfg)
var errSecret error
s.secretScanService, errSecret = secretscan.NewService(s.store, cfg)
if errSecret != nil {
s.secretScanEnabled = false
s.log.Warn("failed to initialize secret scan service. secret scan is disabled",
"error", errSecret.Error())
}
}
return s, nil

View File

@ -3,9 +3,12 @@ package secretscan
import (
"bytes"
"context"
"crypto/tls"
"encoding/json"
"fmt"
"net"
"net/http"
"strings"
"time"
"github.com/pkg/errors"
@ -34,19 +37,37 @@ type Token struct {
ReportedAt string `json:"reported_at"` //nolint
}
var ErrInvalidStatusCode = errors.New("invalid status code")
var (
ErrInvalidStatusCode = errors.New("invalid status code")
errSecretScanURL = errors.New("secretscan url must be https")
)
func newClient(url, version string, dev bool) (*client, error) {
if !strings.HasPrefix(url, "https://") && !dev {
return nil, errSecretScanURL
}
func newClient(url, version string) *client {
return &client{
version: version,
baseURL: url,
httpClient: &http.Client{
Timeout: timeout,
Transport: nil,
CheckRedirect: nil,
Jar: nil,
Transport: &http.Transport{
TLSClientConfig: &tls.Config{
Renegotiation: tls.RenegotiateFreelyAsClient,
},
Proxy: http.ProxyFromEnvironment,
DialContext: (&net.Dialer{
Timeout: timeout,
KeepAlive: 15 * time.Second,
}).DialContext,
TLSHandshakeTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
MaxIdleConns: 100,
IdleConnTimeout: 30 * time.Second,
},
Timeout: time.Second * 30,
},
}
}, nil
}
// checkTokens checks if any leaked tokens exist.

View File

@ -11,7 +11,7 @@ import (
"github.com/grafana/grafana/pkg/setting"
)
const defaultURL = "https://secretscan.grafana.com"
const defaultURL = "https://secret-scanning.grafana.net"
type Checker interface {
CheckTokens(ctx context.Context) error
@ -40,20 +40,34 @@ type Service struct {
revoke bool // whether to revoke leaked tokens
}
func NewService(store SATokenRetriever, cfg *setting.Cfg) *Service {
func NewService(store SATokenRetriever, cfg *setting.Cfg) (*Service, error) {
secretscanBaseURL := cfg.SectionWithEnvOverrides("secretscan").Key("base_url").MustString(defaultURL)
// URL to send outgoing webhook when a token is leaked.
oncallURL := cfg.SectionWithEnvOverrides("secretscan").Key("oncall_url").MustString("")
revoke := cfg.SectionWithEnvOverrides("secretscan").Key("revoke").MustBool(true)
client, err := newClient(secretscanBaseURL, cfg.BuildVersion, cfg.Env == setting.Dev)
if err != nil {
return nil, fmt.Errorf("failed to create secretscan client: %w", err)
}
var webHookClient WebHookClient
if oncallURL != "" {
var errWebhook error
webHookClient, errWebhook = newWebHookClient(oncallURL, cfg.BuildVersion, cfg.Env == setting.Dev)
if errWebhook != nil {
return nil, fmt.Errorf("failed to create secretscan webhook client: %w", errWebhook)
}
}
return &Service{
store: store,
client: newClient(secretscanBaseURL, cfg.BuildVersion),
webHookClient: newWebHookClient(oncallURL, cfg.BuildVersion),
client: client,
webHookClient: webHookClient,
logger: log.New("secretscan"),
webHookNotify: oncallURL != "",
revoke: revoke,
}
}, nil
}
func (s *Service) RetrieveActiveTokens(ctx context.Context) ([]apikey.APIKey, error) {

View File

@ -3,14 +3,20 @@ package secretscan
import (
"bytes"
"context"
"crypto/tls"
"encoding/json"
"fmt"
"net"
"net/http"
"strings"
"time"
"github.com/google/uuid"
"github.com/pkg/errors"
)
var errWebHookURL = errors.New("webhook url must be https")
// webHookClient is a client for sending leak notifications.
type webHookClient struct {
httpClient *http.Client
@ -20,17 +26,32 @@ type webHookClient struct {
var ErrInvalidWebHookStatusCode = errors.New("invalid webhook status code")
func newWebHookClient(url, version string) *webHookClient {
func newWebHookClient(url, version string, dev bool) (*webHookClient, error) {
if !strings.HasPrefix(url, "https://") && !dev {
return nil, errWebHookURL
}
return &webHookClient{
version: version,
url: url,
httpClient: &http.Client{
Transport: nil,
CheckRedirect: nil,
Jar: nil,
Timeout: timeout,
Transport: &http.Transport{
TLSClientConfig: &tls.Config{
Renegotiation: tls.RenegotiateFreelyAsClient,
},
Proxy: http.ProxyFromEnvironment,
DialContext: (&net.Dialer{
Timeout: timeout,
KeepAlive: 15 * time.Second,
}).DialContext,
TLSHandshakeTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
MaxIdleConns: 100,
IdleConnTimeout: 30 * time.Second,
},
Timeout: time.Second * 30,
},
}
}, nil
}
func (wClient *webHookClient) Notify(ctx context.Context,
@ -45,7 +66,6 @@ func (wClient *webHookClient) Notify(ctx context.Context,
values := map[string]interface{}{
"alert_uid": uuid.NewString(),
"title": "SecretScan Alert: Grafana Token leaked",
"image_url": "https://images.pexels.com/photos/5119737/pexels-photo-5119737.jpeg?auto=compress&cs=tinysrgb&w=300", //nolint
"state": "alerting",
"link_to_upstream_details": token.URL,
"message": "Token of type " +