forked from Velocidex/velociraptor
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Initial implementation of a replication service. (Velocidex#999)
This allows Velociraptor to start multiple frontends. There are two types of frontends - a master and slave. By default the master will start up - giving the same experience for single frontend deployments as in previous versions. Start the master: ``` velociraptor -v frontend ``` To start slave frontends you can use the --slave option: ``` velociraptor frontend -v --slave --config.frontend-bind-port 8001 --config.monitoring-bind-port 8004 ``` Note that you can override arbitrary config options using the command line now. You will need this in order to specify dynamic ports for the master and slave (e.g. in kubernetes configs). Notes: * Slaves connect to the master over the API port so if they are going to run on a different machine you will need to change the API.bind_address to "0.0.0.0" * If master and slave are running on different machines you need to share the filesystem using EFS or NFS.
- Loading branch information
Showing
69 changed files
with
3,228 additions
and
1,984 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,155 @@ | ||
package api | ||
|
||
import ( | ||
"crypto/x509" | ||
|
||
"github.com/golang/protobuf/ptypes/empty" | ||
context "golang.org/x/net/context" | ||
"google.golang.org/grpc/codes" | ||
"google.golang.org/grpc/credentials" | ||
"google.golang.org/grpc/peer" | ||
"google.golang.org/grpc/status" | ||
"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/crypto" | ||
"www.velocidex.com/golang/velociraptor/services" | ||
"www.velocidex.com/golang/velociraptor/utils" | ||
) | ||
|
||
func (self *ApiServer) PushEvents( | ||
ctx context.Context, | ||
in *api_proto.PushEventRequest) (*empty.Empty, error) { | ||
|
||
// Get the TLS context from the peer and verify its | ||
// certificate. | ||
peer, ok := peer.FromContext(ctx) | ||
if !ok { | ||
return nil, status.Error(codes.InvalidArgument, "cant get peer info") | ||
} | ||
|
||
tlsInfo, ok := peer.AuthInfo.(credentials.TLSInfo) | ||
if !ok { | ||
return nil, status.Error(codes.InvalidArgument, "unable to get credentials") | ||
} | ||
|
||
// Authenticate API clients using certificates. | ||
for _, peer_cert := range tlsInfo.State.PeerCertificates { | ||
chains, err := peer_cert.Verify( | ||
x509.VerifyOptions{Roots: self.ca_pool}) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
if len(chains) == 0 { | ||
return nil, status.Error(codes.InvalidArgument, "no chains verified") | ||
} | ||
|
||
peer_name := crypto.GetSubjectName(peer_cert) | ||
if peer_name != self.config.Client.PinnedServerName { | ||
token, err := acls.GetEffectivePolicy(self.config, peer_name) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
// Check that the principal is allowed to push to the queue. | ||
ok, err := acls.CheckAccessWithToken(token, acls.PUBLISH, in.Artifact) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
if !ok { | ||
return nil, status.Error(codes.PermissionDenied, | ||
"Permission denied: PUBLISH "+peer_name+" to "+in.Artifact) | ||
} | ||
} | ||
|
||
rows, err := utils.ParseJsonToDicts([]byte(in.Jsonl)) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
// Only return the first row | ||
if true { | ||
journal, err := services.GetJournal() | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
err = journal.PushRowsToArtifact(self.config, | ||
rows, in.Artifact, in.ClientId, in.FlowId) | ||
return &empty.Empty{}, err | ||
} | ||
} | ||
|
||
return nil, status.Error(codes.InvalidArgument, "no peer certs?") | ||
} | ||
|
||
func (self *ApiServer) WriteEvent( | ||
ctx context.Context, | ||
in *actions_proto.VQLResponse) (*empty.Empty, error) { | ||
|
||
// Get the TLS context from the peer and verify its | ||
// certificate. | ||
peer, ok := peer.FromContext(ctx) | ||
if !ok { | ||
return nil, status.Error(codes.InvalidArgument, "cant get peer info") | ||
} | ||
|
||
tlsInfo, ok := peer.AuthInfo.(credentials.TLSInfo) | ||
if !ok { | ||
return nil, status.Error(codes.InvalidArgument, "unable to get credentials") | ||
} | ||
|
||
// Authenticate API clients using certificates. | ||
for _, peer_cert := range tlsInfo.State.PeerCertificates { | ||
chains, err := peer_cert.Verify( | ||
x509.VerifyOptions{Roots: self.ca_pool}) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
if len(chains) == 0 { | ||
return nil, status.Error(codes.InvalidArgument, "no chains verified") | ||
} | ||
|
||
peer_name := crypto.GetSubjectName(peer_cert) | ||
|
||
token, err := acls.GetEffectivePolicy(self.config, peer_name) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
// Check that the principal is allowed to push to the queue. | ||
ok, err := acls.CheckAccessWithToken(token, | ||
acls.MACHINE_STATE, in.Query.Name) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
if !ok { | ||
return nil, status.Error(codes.PermissionDenied, | ||
"Permission denied: MACHINE_STATE "+ | ||
peer_name+" to "+in.Query.Name) | ||
} | ||
|
||
rows, err := utils.ParseJsonToDicts([]byte(in.Response)) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
// Only return the first row | ||
if true { | ||
journal, err := services.GetJournal() | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
err = journal.PushRowsToArtifact(self.config, | ||
rows, in.Query.Name, peer_name, "") | ||
return &empty.Empty{}, err | ||
} | ||
} | ||
|
||
return nil, status.Error(codes.InvalidArgument, "no peer certs?") | ||
} |
Oops, something went wrong.