Support mTLS Authentication in Webhooks (#9777)

This commit is contained in:
Praveen raj Mani 2020-06-08 18:25:44 +05:30 committed by GitHub
parent c7599d323b
commit 2ce2e88adf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 84 additions and 6 deletions

View File

@ -59,6 +59,18 @@ var (
Optional: true,
Type: "sentence",
},
config.HelpKV{
Key: target.WebhookClientCert,
Description: "client cert for Webhook mTLS auth",
Optional: true,
Type: "string",
},
config.HelpKV{
Key: target.WebhookClientKey,
Description: "client cert key for Webhook mTLS auth",
Optional: true,
Type: "string",
},
}
HelpAMQP = config.HelpKVS{

View File

@ -281,6 +281,14 @@ func SetNotifyWebhook(s config.Config, whName string, cfg target.WebhookArgs) er
Key: target.WebhookQueueLimit,
Value: strconv.Itoa(int(cfg.QueueLimit)),
},
config.KV{
Key: target.WebhookClientCert,
Value: cfg.ClientCert,
},
config.KV{
Key: target.WebhookClientKey,
Value: cfg.ClientKey,
},
}
return nil

View File

@ -1428,6 +1428,14 @@ var (
Key: target.WebhookQueueDir,
Value: "",
},
config.KV{
Key: target.WebhookClientCert,
Value: "",
},
config.KV{
Key: target.WebhookClientKey,
Value: "",
},
}
)
@ -1471,6 +1479,15 @@ func GetNotifyWebhook(webhookKVS map[string]config.KVS, transport *http.Transpor
if k != config.Default {
authEnv = authEnv + config.Default + k
}
clientCertEnv := target.EnvWebhookClientCert
if k != config.Default {
clientCertEnv = clientCertEnv + config.Default + k
}
clientKeyEnv := target.EnvWebhookClientKey
if k != config.Default {
clientKeyEnv = clientKeyEnv + config.Default + k
}
webhookArgs := target.WebhookArgs{
Enable: enabled,
@ -1479,6 +1496,8 @@ func GetNotifyWebhook(webhookKVS map[string]config.KVS, transport *http.Transpor
AuthToken: env.Get(authEnv, kv.Get(target.WebhookAuthToken)),
QueueDir: env.Get(queueDirEnv, kv.Get(target.WebhookQueueDir)),
QueueLimit: uint64(queueLimit),
ClientCert: env.Get(clientCertEnv, kv.Get(target.WebhookClientCert)),
ClientKey: env.Get(clientKeyEnv, kv.Get(target.WebhookClientKey)),
}
if err = webhookArgs.Validate(); err != nil {
return nil, err

View File

@ -1241,6 +1241,8 @@ endpoint* (url) webhook server endpoint e.g. http://localhost:8080/mini
auth_token (string) opaque string or JWT authorization token
queue_dir (path) staging dir for undelivered messages e.g. '/home/events'
queue_limit (number) maximum limit for undelivered messages, defaults to '100000'
client_cert (string) client cert for Webhook mTLS auth
client_key (string) client cert key for Webhook mTLS auth
comment (sentence) optionally add a comment to this setting
```
@ -1256,11 +1258,13 @@ MINIO_NOTIFY_WEBHOOK_AUTH_TOKEN (string) opaque string or JWT authorization
MINIO_NOTIFY_WEBHOOK_QUEUE_DIR (path) staging dir for undelivered messages e.g. '/home/events'
MINIO_NOTIFY_WEBHOOK_QUEUE_LIMIT (number) maximum limit for undelivered messages, defaults to '100000'
MINIO_NOTIFY_WEBHOOK_COMMENT (sentence) optionally add a comment to this setting
MINIO_NOTIFY_WEBHOOK_CLIENT_CERT (string) client cert for Webhook mTLS auth
MINIO_NOTIFY_WEBHOOK_CLIENT_KEY (string) client cert key for Webhook mTLS auth
```
```sh
$ mc admin config get myminio/ notify_webhook
notify_webhook:1 queue_limit="0" endpoint="" queue_dir=""
notify_webhook:1 endpoint="" auth_token="" queue_limit="0" queue_dir="" client_cert="" client_key=""
```
Use `mc admin config set` command to update the configuration for the deployment. Here the endpoint is the server listening for webhook notifications. Save the settings and restart the MinIO server for changes to take effect. Note that the endpoint needs to be live and reachable when you restart your MinIO server.

View File

@ -185,6 +185,14 @@ func (c *Certs) GetCertificate(hello *tls.ClientHelloInfo) (*tls.Certificate, er
return c.cert, nil
}
// GetClientCertificate returns the loaded certificate for use by
// the TLSConfig fields GetClientCertificate field in a http.Server.
func (c *Certs) GetClientCertificate(_ *tls.CertificateRequestInfo) (*tls.Certificate, error) {
c.RLock()
defer c.RUnlock()
return c.cert, nil
}
// Stop tells loader to stop watching for changes to the
// certificate and key files.
func (c *Certs) Stop() {

View File

@ -93,6 +93,16 @@ func TestValidPairAfterWrite(t *testing.T) {
if !reflect.DeepEqual(gcert.Certificate, expectedCert.Certificate) {
t.Error("certificate doesn't match expected certificate")
}
rInfo := &tls.CertificateRequestInfo{}
gcert, err = c.GetClientCertificate(rInfo)
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(gcert.Certificate, expectedCert.Certificate) {
t.Error("client certificate doesn't match expected certificate")
}
}
func TestStop(t *testing.T) {

View File

@ -19,6 +19,7 @@ package target
import (
"bytes"
"context"
"crypto/tls"
"encoding/json"
"errors"
"fmt"
@ -30,6 +31,7 @@ import (
"path/filepath"
"time"
"github.com/minio/minio/pkg/certs"
"github.com/minio/minio/pkg/event"
xnet "github.com/minio/minio/pkg/net"
)
@ -40,12 +42,16 @@ const (
WebhookAuthToken = "auth_token"
WebhookQueueDir = "queue_dir"
WebhookQueueLimit = "queue_limit"
WebhookClientCert = "client_cert"
WebhookClientKey = "client_key"
EnvWebhookEnable = "MINIO_NOTIFY_WEBHOOK_ENABLE"
EnvWebhookEndpoint = "MINIO_NOTIFY_WEBHOOK_ENDPOINT"
EnvWebhookAuthToken = "MINIO_NOTIFY_WEBHOOK_AUTH_TOKEN"
EnvWebhookQueueDir = "MINIO_NOTIFY_WEBHOOK_QUEUE_DIR"
EnvWebhookQueueLimit = "MINIO_NOTIFY_WEBHOOK_QUEUE_LIMIT"
EnvWebhookClientCert = "MINIO_NOTIFY_WEBHOOK_CLIENT_CERT"
EnvWebhookClientKey = "MINIO_NOTIFY_WEBHOOK_CLIENT_KEY"
)
// WebhookArgs - Webhook target arguments.
@ -56,6 +62,8 @@ type WebhookArgs struct {
Transport *http.Transport `json:"-"`
QueueDir string `json:"queueDir"`
QueueLimit uint64 `json:"queueLimit"`
ClientCert string `json:"clientCert"`
ClientKey string `json:"clientKey"`
}
// Validate WebhookArgs fields
@ -71,6 +79,9 @@ func (w WebhookArgs) Validate() error {
return errors.New("queueDir path should be absolute")
}
}
if w.ClientCert != "" && w.ClientKey == "" || w.ClientCert == "" && w.ClientKey != "" {
return errors.New("cert and key must be specified as a pair")
}
return nil
}
@ -209,14 +220,20 @@ func NewWebhookTarget(id string, args WebhookArgs, doneCh <-chan struct{}, logge
var store Store
target := &WebhookTarget{
id: event.TargetID{ID: id, Name: "webhook"},
args: args,
httpClient: &http.Client{
Transport: transport,
},
id: event.TargetID{ID: id, Name: "webhook"},
args: args,
loggerOnce: loggerOnce,
}
if target.args.ClientCert != "" && target.args.ClientKey != "" {
c, err := certs.New(target.args.ClientCert, target.args.ClientKey, tls.LoadX509KeyPair)
if err != nil {
return target, err
}
transport.TLSClientConfig.GetClientCertificate = c.GetClientCertificate
}
target.httpClient = &http.Client{Transport: transport}
if args.QueueDir != "" {
queueDir := filepath.Join(args.QueueDir, storePrefix+"-webhook-"+id)
store = NewQueueStore(queueDir, args.QueueLimit)