Add admin required middleware and webhook endpoint
* Add a new middleware that tests for admin access
* Add a new controller ID suffixed webhook endpoint. This will be used
to accept webhook events on a webhook URL that is suffixed with our own
controller ID.
Signed-off-by: Gabriel Adrian Samfira <gsamfira@cloudbasesolutions.com>
This commit is contained in:
parent
aa2b42fddb
commit
f2796f1d5a
5 changed files with 57 additions and 7 deletions
|
|
@ -30,11 +30,16 @@ import (
|
|||
"github.com/cloudbase/garm/runner"
|
||||
wsWriter "github.com/cloudbase/garm/websocket"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func NewAPIController(r *runner.Runner, authenticator *auth.Authenticator, hub *wsWriter.Hub) (*APIController, error) {
|
||||
controllerInfo, err := r.GetControllerInfo(auth.GetAdminContext())
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get controller info")
|
||||
}
|
||||
return &APIController{
|
||||
r: r,
|
||||
auth: authenticator,
|
||||
|
|
@ -43,14 +48,16 @@ func NewAPIController(r *runner.Runner, authenticator *auth.Authenticator, hub *
|
|||
ReadBufferSize: 1024,
|
||||
WriteBufferSize: 16384,
|
||||
},
|
||||
controllerID: controllerInfo.ShortControllerID(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
type APIController struct {
|
||||
r *runner.Runner
|
||||
auth *auth.Authenticator
|
||||
hub *wsWriter.Hub
|
||||
upgrader websocket.Upgrader
|
||||
r *runner.Runner
|
||||
auth *auth.Authenticator
|
||||
hub *wsWriter.Hub
|
||||
upgrader websocket.Upgrader
|
||||
controllerID string
|
||||
}
|
||||
|
||||
func handleError(w http.ResponseWriter, err error) {
|
||||
|
|
@ -138,7 +145,19 @@ func (a *APIController) handleWorkflowJobEvent(w http.ResponseWriter, r *http.Re
|
|||
labelValues = a.webhookMetricLabelValues("true", "")
|
||||
}
|
||||
|
||||
func (a *APIController) CatchAll(w http.ResponseWriter, r *http.Request) {
|
||||
func (a *APIController) WebhookHandler(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
controllerID, ok := vars["controllerID"]
|
||||
// If the webhook URL includes a controller ID, we validate that it's meant for us. We still
|
||||
// support bare webhook URLs, which are tipically configured manually by the user.
|
||||
// The controllerID suffixed webhook URL is useful when configuring the webhook for an entity
|
||||
// via garm. We cannot tag a webhook URL on github, so there is no way to determine ownership.
|
||||
// Using a controllerID suffix is a simple way to denote ownership.
|
||||
if ok && controllerID != a.controllerID {
|
||||
log.Printf("ignoring webhook meant for controller %s", util.SanitizeLogEntry(controllerID))
|
||||
return
|
||||
}
|
||||
|
||||
headers := r.Header.Clone()
|
||||
|
||||
event := runnerParams.Event(headers.Get("X-Github-Event"))
|
||||
|
|
|
|||
|
|
@ -89,8 +89,9 @@ func NewAPIRouter(han *controllers.APIController, logWriter io.Writer, authMiddl
|
|||
|
||||
// Handles github webhooks
|
||||
webhookRouter := router.PathPrefix("/webhooks").Subrouter()
|
||||
webhookRouter.PathPrefix("/").Handler(http.HandlerFunc(han.CatchAll))
|
||||
webhookRouter.PathPrefix("").Handler(http.HandlerFunc(han.CatchAll))
|
||||
webhookRouter.Handle("/", http.HandlerFunc(han.WebhookHandler))
|
||||
webhookRouter.Handle("", http.HandlerFunc(han.WebhookHandler))
|
||||
webhookRouter.Handle("/{controllerID:controllerID\\/?}", http.HandlerFunc(han.WebhookHandler))
|
||||
|
||||
// Handles API calls
|
||||
apiSubRouter := router.PathPrefix("/api/v1").Subrouter()
|
||||
|
|
@ -118,6 +119,7 @@ func NewAPIRouter(han *controllers.APIController, logWriter io.Writer, authMiddl
|
|||
apiRouter := apiSubRouter.PathPrefix("").Subrouter()
|
||||
apiRouter.Use(initMiddleware.Middleware)
|
||||
apiRouter.Use(authMiddleware.Middleware)
|
||||
apiRouter.Use(auth.AdminRequiredMiddleware)
|
||||
|
||||
// Metrics Token
|
||||
apiRouter.Handle("/metrics-token/", http.HandlerFunc(han.MetricsTokenHandler)).Methods("GET", "OPTIONS")
|
||||
|
|
|
|||
14
auth/admin_required.go
Normal file
14
auth/admin_required.go
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
package auth
|
||||
|
||||
import "net/http"
|
||||
|
||||
func AdminRequiredMiddleware(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
if !IsAdmin(ctx) {
|
||||
http.Error(w, "Unauthorized", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
|
|
@ -110,6 +110,9 @@ type Default struct {
|
|||
// MetadataURL is the URL where instances can fetch information they may need
|
||||
// to set themselves up.
|
||||
MetadataURL string `toml:"metadata_url" json:"metadata-url"`
|
||||
// WebhookURL is the URL that will be installed as a webhook target in github.
|
||||
WebhookURL string `toml:"webhook_url" json:"webhook-url"`
|
||||
|
||||
// LogFile is the location of the log file.
|
||||
LogFile string `toml:"log_file,omitempty" json:"log-file"`
|
||||
EnableLogStreamer bool `toml:"enable_log_streamer"`
|
||||
|
|
@ -128,6 +131,7 @@ func (d *Default) Validate() error {
|
|||
if d.MetadataURL == "" {
|
||||
return fmt.Errorf("missing metadata-url")
|
||||
}
|
||||
|
||||
if _, err := url.Parse(d.MetadataURL); err != nil {
|
||||
return errors.Wrap(err, "validating metadata_url")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ package params
|
|||
|
||||
import (
|
||||
"encoding/json"
|
||||
"math/big"
|
||||
"time"
|
||||
|
||||
commonParams "github.com/cloudbase/garm-provider-common/params"
|
||||
|
|
@ -385,6 +386,16 @@ type ControllerInfo struct {
|
|||
CallbackURL string `json:"callback_url"`
|
||||
}
|
||||
|
||||
func (c ControllerInfo) ShortControllerID() string {
|
||||
var i big.Int
|
||||
i.SetBytes(c.ControllerID[:])
|
||||
id := i.Text(62)
|
||||
if id == "0" {
|
||||
return ""
|
||||
}
|
||||
return id
|
||||
}
|
||||
|
||||
type GithubCredentials struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
Description string `json:"description,omitempty"`
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue