2016-09-26 18:48:55 +00:00
|
|
|
package service
|
2016-09-01 04:51:38 +00:00
|
|
|
|
|
|
|
import (
|
2017-03-15 15:55:30 +00:00
|
|
|
"context"
|
2021-09-10 17:48:33 +00:00
|
|
|
"encoding/json"
|
2021-11-22 14:13:26 +00:00
|
|
|
"fmt"
|
2016-09-29 04:21:39 +00:00
|
|
|
"reflect"
|
2016-09-01 04:51:38 +00:00
|
|
|
|
2021-08-02 22:06:27 +00:00
|
|
|
"github.com/fleetdm/fleet/v4/server/contexts/logging"
|
2021-06-26 04:46:51 +00:00
|
|
|
"github.com/fleetdm/fleet/v4/server/fleet"
|
2021-09-10 17:48:33 +00:00
|
|
|
"github.com/go-kit/kit/log"
|
|
|
|
"github.com/go-kit/kit/log/level"
|
2021-06-06 22:07:29 +00:00
|
|
|
|
2021-06-26 04:46:51 +00:00
|
|
|
hostctx "github.com/fleetdm/fleet/v4/server/contexts/host"
|
|
|
|
"github.com/fleetdm/fleet/v4/server/contexts/token"
|
|
|
|
"github.com/fleetdm/fleet/v4/server/contexts/viewer"
|
2021-03-10 15:34:37 +00:00
|
|
|
"github.com/go-kit/kit/endpoint"
|
2016-09-01 04:51:38 +00:00
|
|
|
)
|
|
|
|
|
2021-09-10 17:48:33 +00:00
|
|
|
func logJSON(logger log.Logger, v interface{}, key string) {
|
|
|
|
jsonV, err := json.Marshal(v)
|
|
|
|
if err != nil {
|
2021-11-22 14:13:26 +00:00
|
|
|
level.Debug(logger).Log("err", fmt.Errorf("marshaling %s for debug: %w", key, err))
|
2021-09-10 17:48:33 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
level.Debug(logger).Log(key, string(jsonV))
|
|
|
|
}
|
|
|
|
|
2016-09-29 04:21:39 +00:00
|
|
|
// authenticatedHost wraps an endpoint, checks the validity of the node_key
|
|
|
|
// provided in the request, and attaches the corresponding osquery host to the
|
|
|
|
// context for the request
|
2021-09-10 17:48:33 +00:00
|
|
|
func authenticatedHost(svc fleet.Service, logger log.Logger, next endpoint.Endpoint) endpoint.Endpoint {
|
2016-09-08 01:24:11 +00:00
|
|
|
return func(ctx context.Context, request interface{}) (interface{}, error) {
|
2016-09-29 04:21:39 +00:00
|
|
|
nodeKey, err := getNodeKey(request)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2021-09-10 17:48:33 +00:00
|
|
|
host, debug, err := svc.AuthenticateHost(ctx, nodeKey)
|
2016-09-29 04:21:39 +00:00
|
|
|
if err != nil {
|
2021-08-23 22:40:00 +00:00
|
|
|
logging.WithErr(ctx, err)
|
2016-09-29 04:21:39 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2021-09-10 17:48:33 +00:00
|
|
|
hlogger := log.With(logger, "host-id", host.ID)
|
|
|
|
if debug {
|
|
|
|
logJSON(hlogger, request, "request")
|
|
|
|
}
|
|
|
|
|
2016-09-29 04:21:39 +00:00
|
|
|
ctx = hostctx.NewContext(ctx, *host)
|
2021-08-23 22:40:00 +00:00
|
|
|
resp, err := next(ctx, request)
|
|
|
|
if err != nil {
|
|
|
|
logging.WithErr(ctx, err)
|
|
|
|
return nil, err
|
|
|
|
}
|
2021-09-10 17:48:33 +00:00
|
|
|
|
|
|
|
if debug {
|
|
|
|
logJSON(hlogger, request, "response")
|
|
|
|
}
|
|
|
|
|
2021-08-23 22:40:00 +00:00
|
|
|
if errResp, ok := resp.(errorer); ok {
|
|
|
|
err = errResp.error()
|
|
|
|
if err != nil {
|
|
|
|
logging.WithErr(ctx, err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return resp, nil
|
2016-09-29 04:21:39 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func getNodeKey(r interface{}) (string, error) {
|
|
|
|
// Retrieve node key by reflection (note that our options here
|
|
|
|
// are limited by the fact that request is an interface{})
|
|
|
|
v := reflect.ValueOf(r)
|
|
|
|
if v.Kind() != reflect.Struct {
|
|
|
|
return "", osqueryError{
|
2019-01-24 17:39:32 +00:00
|
|
|
message: "request type is not struct. This is likely a Fleet programmer error.",
|
2016-09-29 04:21:39 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
nodeKeyField := v.FieldByName("NodeKey")
|
|
|
|
if !nodeKeyField.IsValid() {
|
|
|
|
return "", osqueryError{
|
2019-01-24 17:39:32 +00:00
|
|
|
message: "request struct missing NodeKey. This is likely a Fleet programmer error.",
|
2016-09-29 04:21:39 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if nodeKeyField.Kind() != reflect.String {
|
|
|
|
return "", osqueryError{
|
2019-01-24 17:39:32 +00:00
|
|
|
message: "NodeKey is not a string. This is likely a Fleet programmer error.",
|
2016-09-29 04:21:39 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return nodeKeyField.String(), nil
|
|
|
|
}
|
|
|
|
|
2019-01-24 17:39:32 +00:00
|
|
|
// authenticatedUser wraps an endpoint, requires that the Fleet user is
|
2016-09-29 04:21:39 +00:00
|
|
|
// authenticated, and populates the context with a Viewer struct for that user.
|
2021-06-16 17:55:41 +00:00
|
|
|
//
|
|
|
|
// If auth fails or the user must reset their password, an error is returned.
|
2021-06-07 01:10:58 +00:00
|
|
|
func authenticatedUser(svc fleet.Service, next endpoint.Endpoint) endpoint.Endpoint {
|
2021-08-02 22:06:27 +00:00
|
|
|
authUserFunc := func(ctx context.Context, request interface{}) (interface{}, error) {
|
2016-09-29 04:21:39 +00:00
|
|
|
// first check if already successfully set
|
2021-06-16 17:55:41 +00:00
|
|
|
if v, ok := viewer.FromContext(ctx); ok {
|
2021-11-10 22:30:15 +00:00
|
|
|
if v.User.IsAdminForcedPasswordReset() {
|
2021-06-16 17:55:41 +00:00
|
|
|
return nil, fleet.ErrPasswordResetRequired
|
|
|
|
}
|
|
|
|
|
2016-09-26 17:14:39 +00:00
|
|
|
return next(ctx, request)
|
|
|
|
}
|
2016-09-29 04:21:39 +00:00
|
|
|
|
2016-09-26 17:14:39 +00:00
|
|
|
// if not succesful, try again this time with errors
|
2021-06-07 01:10:58 +00:00
|
|
|
sessionKey, ok := token.FromContext(ctx)
|
2016-09-26 17:14:39 +00:00
|
|
|
if !ok {
|
2021-07-05 13:17:31 +00:00
|
|
|
return nil, fleet.NewAuthHeaderRequiredError("no auth token")
|
2016-09-26 17:14:39 +00:00
|
|
|
}
|
2016-09-29 04:21:39 +00:00
|
|
|
|
2021-06-07 01:10:58 +00:00
|
|
|
v, err := authViewer(ctx, string(sessionKey), svc)
|
2016-09-08 01:24:11 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2016-09-29 04:21:39 +00:00
|
|
|
|
2021-11-10 22:30:15 +00:00
|
|
|
if v.User.IsAdminForcedPasswordReset() {
|
2021-06-16 17:55:41 +00:00
|
|
|
return nil, fleet.ErrPasswordResetRequired
|
|
|
|
}
|
|
|
|
|
2016-09-26 17:14:39 +00:00
|
|
|
ctx = viewer.NewContext(ctx, *v)
|
2016-09-08 01:24:11 +00:00
|
|
|
return next(ctx, request)
|
|
|
|
}
|
2021-08-02 22:06:27 +00:00
|
|
|
|
|
|
|
return func(ctx context.Context, request interface{}) (response interface{}, err error) {
|
|
|
|
res, err := authUserFunc(ctx, request)
|
|
|
|
if err != nil {
|
|
|
|
logging.WithErr(ctx, err)
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return res, nil
|
|
|
|
}
|
2016-09-08 01:24:11 +00:00
|
|
|
}
|
|
|
|
|
2021-08-23 22:40:00 +00:00
|
|
|
// logged wraps an endpoint and adds the error if the context supports it
|
|
|
|
func logged(next endpoint.Endpoint) endpoint.Endpoint {
|
|
|
|
return func(ctx context.Context, request interface{}) (response interface{}, err error) {
|
|
|
|
res, err := next(ctx, request)
|
|
|
|
if err != nil {
|
|
|
|
logging.WithErr(ctx, err)
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if errResp, ok := res.(errorer); ok {
|
|
|
|
err = errResp.error()
|
|
|
|
if err != nil {
|
|
|
|
logging.WithErr(ctx, err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return res, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-07 01:10:58 +00:00
|
|
|
// authViewer creates an authenticated viewer by validating the session key.
|
|
|
|
func authViewer(ctx context.Context, sessionKey string, svc fleet.Service) (*viewer.Viewer, error) {
|
2016-09-26 17:14:39 +00:00
|
|
|
session, err := svc.GetSessionByKey(ctx, sessionKey)
|
|
|
|
if err != nil {
|
2021-06-06 22:07:29 +00:00
|
|
|
return nil, fleet.NewAuthRequiredError(err.Error())
|
2016-09-26 17:14:39 +00:00
|
|
|
}
|
2021-06-03 23:24:15 +00:00
|
|
|
user, err := svc.UserUnauthorized(ctx, session.UserID)
|
2016-09-26 17:14:39 +00:00
|
|
|
if err != nil {
|
2021-06-06 22:07:29 +00:00
|
|
|
return nil, fleet.NewAuthRequiredError(err.Error())
|
2016-09-26 17:14:39 +00:00
|
|
|
}
|
|
|
|
return &viewer.Viewer{User: user, Session: session}, nil
|
|
|
|
}
|
|
|
|
|
2018-09-13 20:59:53 +00:00
|
|
|
func canPerformPasswordReset(next endpoint.Endpoint) endpoint.Endpoint {
|
|
|
|
return func(ctx context.Context, request interface{}) (interface{}, error) {
|
|
|
|
vc, ok := viewer.FromContext(ctx)
|
|
|
|
if !ok {
|
2021-06-06 22:07:29 +00:00
|
|
|
return nil, fleet.ErrNoContext
|
2018-09-13 20:59:53 +00:00
|
|
|
}
|
|
|
|
if !vc.CanPerformPasswordReset() {
|
2021-06-06 22:07:29 +00:00
|
|
|
return nil, fleet.NewPermissionError("cannot reset password")
|
2018-09-13 20:59:53 +00:00
|
|
|
}
|
|
|
|
return next(ctx, request)
|
|
|
|
}
|
|
|
|
}
|