mirror of
https://github.com/Velocidex/velociraptor.git
synced 2026-10-12 02:09:59 +00:00
The queue manager is used to pass messages between parts of Velociraptor. Previously, the queue name corresponded with the artifact name that defines the queue. However, this allowed messages intended to client monitoring to be passed to server monitoring artifacts under some conditions. This refactor changes the queue name to also contain the artifact type. This change better keeps track of messages and which queues they are sent to. Additionally, the journal manager now records two records in each monitoring row: 1. The _Source records who authorized the row. 2. The _From records who actually sent the row in the first place. ACL decisions are now made based on the _Source only making it more consistent to determine if a row can be sent on a particular queue.
144 lines
3.9 KiB
Go
144 lines
3.9 KiB
Go
package api
|
|
|
|
import (
|
|
"context"
|
|
|
|
errors "github.com/go-errors/errors"
|
|
"google.golang.org/grpc/codes"
|
|
"google.golang.org/grpc/status"
|
|
"google.golang.org/protobuf/types/known/emptypb"
|
|
"www.velocidex.com/golang/velociraptor/acls"
|
|
actions_proto "www.velocidex.com/golang/velociraptor/actions/proto"
|
|
api_proto "www.velocidex.com/golang/velociraptor/api/proto"
|
|
"www.velocidex.com/golang/velociraptor/json"
|
|
"www.velocidex.com/golang/velociraptor/services"
|
|
"www.velocidex.com/golang/velociraptor/utils"
|
|
)
|
|
|
|
func (self *ApiServer) PushEvents(
|
|
ctx context.Context,
|
|
in *api_proto.PushEventRequest) (*emptypb.Empty, error) {
|
|
|
|
defer Instrument("PushEvents")()
|
|
|
|
users := services.GetUserManager()
|
|
user_record, org_config_obj, err := users.GetUserFromContext(ctx)
|
|
if err != nil {
|
|
return nil, Status(self.verbose, err)
|
|
}
|
|
|
|
// User is asking to switch orgs
|
|
if in.OrgId != "" {
|
|
org_manager, err := services.GetOrgManager()
|
|
if err != nil {
|
|
return nil, Status(self.verbose, err)
|
|
}
|
|
|
|
org_config_obj, err = org_manager.GetOrgConfig(in.OrgId)
|
|
if err != nil {
|
|
return nil, Status(self.verbose, err)
|
|
}
|
|
}
|
|
|
|
user_name := user_record.Name
|
|
|
|
// Now check permissions in the org if the user is not the superuser.
|
|
// For superuser users, we trust the username they relayed.
|
|
if user_name != utils.GetSuperuserName(org_config_obj) {
|
|
token, err := services.GetEffectivePolicy(org_config_obj, user_name)
|
|
if err != nil {
|
|
return nil, Status(self.verbose, err)
|
|
}
|
|
|
|
// Check that the principal is allowed to push to this specific queue.
|
|
ok, err := services.CheckAccessWithToken(token, acls.PUBLISH, in.Artifact)
|
|
if err != nil {
|
|
return nil, Status(self.verbose, err)
|
|
}
|
|
|
|
if !ok {
|
|
return nil, status.Error(codes.PermissionDenied,
|
|
"Permission denied: PUBLISH "+user_name+" to "+in.Artifact)
|
|
}
|
|
|
|
// For regular users append the sender field so we can track
|
|
// where the message came from.
|
|
in.Jsonl = json.AppendJsonlItem(in.Jsonl, "_Sender", user_name)
|
|
in.Username = user_name
|
|
|
|
// Always write user events.
|
|
in.Write = true
|
|
}
|
|
|
|
if in.Username == "" {
|
|
in.Username = user_name
|
|
}
|
|
|
|
rows, err := utils.ParseJsonToDicts([]byte(in.Jsonl))
|
|
if err != nil {
|
|
return nil, Status(self.verbose, err)
|
|
}
|
|
|
|
// Only return the first row
|
|
journal, err := services.GetJournal(org_config_obj)
|
|
if err != nil {
|
|
return nil, Status(self.verbose, err)
|
|
}
|
|
|
|
journal_opts := services.JournalOptions{
|
|
ArtifactName: in.Artifact,
|
|
ClientId: in.ClientId,
|
|
FlowId: in.FlowId,
|
|
Username: in.Username,
|
|
From: in.Username,
|
|
}
|
|
|
|
// only broadcast the events for local listeners. Minions
|
|
// write the events themselves, so we just need to broadcast
|
|
// for any server event artifacts that occur.
|
|
if in.Write {
|
|
err = journal.PushRowsToArtifact(ctx, org_config_obj,
|
|
rows, journal_opts)
|
|
|
|
} else {
|
|
err = journal.Broadcast(ctx, org_config_obj, rows, journal_opts)
|
|
}
|
|
|
|
return &emptypb.Empty{}, err
|
|
}
|
|
|
|
func (self *ApiServer) WriteEvent(
|
|
ctx context.Context,
|
|
in *actions_proto.VQLResponse) (*emptypb.Empty, error) {
|
|
return nil, Status(self.verbose,
|
|
errors.New("WriteEvent is deprecated, please use PushEvents instead"))
|
|
}
|
|
|
|
func (self *ApiServer) ListAvailableEventResults(
|
|
ctx context.Context,
|
|
in *api_proto.ListAvailableEventResultsRequest) (
|
|
*api_proto.ListAvailableEventResultsResponse, error) {
|
|
|
|
defer Instrument("ListAvailableEventResults")()
|
|
|
|
users := services.GetUserManager()
|
|
user_record, org_config_obj, err := users.GetUserFromContext(ctx)
|
|
if err != nil {
|
|
return nil, Status(self.verbose, err)
|
|
}
|
|
|
|
permissions := acls.READ_RESULTS
|
|
perm, err := services.CheckAccess(org_config_obj, user_record.Name, permissions)
|
|
if !perm || err != nil {
|
|
return nil, PermissionDenied(err,
|
|
"User is not allowed to view results.")
|
|
}
|
|
|
|
client_monitoring_service, err := services.ClientEventManager(org_config_obj)
|
|
if err != nil {
|
|
return nil, Status(self.verbose, err)
|
|
}
|
|
|
|
return client_monitoring_service.ListAvailableEventResults(ctx, in)
|
|
}
|