diff --git a/accessors/vfs/vfs_test.go b/accessors/vfs/vfs_test.go index 6e7ea5d90cf..c05f0b717b0 100644 --- a/accessors/vfs/vfs_test.go +++ b/accessors/vfs/vfs_test.go @@ -120,6 +120,7 @@ func (self *TestSuite) TestVFSAccessor() { flow_id, err := launcher.ScheduleArtifactCollection(self.Ctx, self.ConfigObj, acl_manager, repository, &flows_proto.ArtifactCollectorArgs{ Artifacts: []string{"System.VFS.ListDirectory", "System.VFS.DownloadFile"}, + Creator: utils.GetSuperuserName(self.ConfigObj), Specs: []*flows_proto.ArtifactSpec{ { Artifact: "System.VFS.DownloadFile", @@ -167,6 +168,7 @@ func (self *TestSuite) TestVFSAccessor() { vtesting.WaitUntil(time.Second*5, self.T(), func() bool { flow, err := launcher.GetFlowDetails(self.Ctx, self.ConfigObj, "server", flow_id) assert.NoError(self.T(), err) + return flow.Context.State == flows_proto.ArtifactCollectorContext_FINISHED }) diff --git a/api/api.go b/api/api.go index ec6ad8a8e3e..5355151b02b 100644 --- a/api/api.go +++ b/api/api.go @@ -162,15 +162,12 @@ func (self *ApiServer) CollectArtifact( if err != nil { return nil, Status(self.verbose, err) } - principal := user_record.Name - // Internal calls from the frontend can set the creator. - if principal != org_config_obj.Client.PinnedServerName { - in.Creator = principal - } + // Ensure the request is marked with the real caller. + in.Creator = user_record.Name acl_manager := acl_managers.NewServerACLManager( - org_config_obj, principal) + org_config_obj, in.Creator) manager, err := services.GetRepositoryManager(org_config_obj) if err != nil { @@ -198,7 +195,7 @@ func (self *ApiServer) CollectArtifact( // Log this event as an Audit event. services.LogAudit(ctx, - org_config_obj, principal, "ScheduleFlow", + org_config_obj, in.Creator, "ScheduleFlow", ordereddict.NewDict(). Set("client", in.ClientId). Set("flow_id", flow_id). diff --git a/api/authenticators/certs.go b/api/authenticators/certs.go index cc442ce1e80..64aa4ba18de 100644 --- a/api/authenticators/certs.go +++ b/api/authenticators/certs.go @@ -84,11 +84,12 @@ import ( "github.com/gorilla/csrf" acl_proto "www.velocidex.com/golang/velociraptor/acls/proto" api_proto "www.velocidex.com/golang/velociraptor/api/proto" - utils "www.velocidex.com/golang/velociraptor/api/utils" + api_utils "www.velocidex.com/golang/velociraptor/api/utils" config_proto "www.velocidex.com/golang/velociraptor/config/proto" "www.velocidex.com/golang/velociraptor/constants" "www.velocidex.com/golang/velociraptor/json" "www.velocidex.com/golang/velociraptor/services" + "www.velocidex.com/golang/velociraptor/utils" ) var ( @@ -111,7 +112,7 @@ func (self *CertAuthenticator) AddHandlers(mux *http.ServeMux) error { // It is not really possible to log off when using client certs func (self *CertAuthenticator) AddLogoff(mux *http.ServeMux) error { - mux.Handle(utils.Join(self.base, "/app/logoff.html"), + mux.Handle(api_utils.Join(self.base, "/app/logoff.html"), IpFilter(self.config_obj, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("WWW-Authenticate", `Basic realm="Restricted"`) @@ -187,7 +188,7 @@ func (self *CertAuthenticator) AuthenticateUserHandler( // Use the super user principal to actually add the // username so we have enough permissions. err = users_manager.AddUserToOrg(r.Context(), services.AddNewUser, - constants.PinnedServerName, username, + utils.GetSuperuserName(self.config_obj), username, []string{"root"}, policy) if err != nil { http.Error(w, diff --git a/api/proxy.go b/api/proxy.go index e9b0667da43..7dabc5ca46e 100644 --- a/api/proxy.go +++ b/api/proxy.go @@ -1,19 +1,19 @@ /* - Velociraptor - Dig Deeper - Copyright (C) 2019-2024 Rapid7 Inc. +Velociraptor - Dig Deeper +Copyright (C) 2019-2024 Rapid7 Inc. - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as published - by the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published +by the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . */ package api @@ -34,13 +34,14 @@ import ( "google.golang.org/protobuf/encoding/protojson" "www.velocidex.com/golang/velociraptor/api/authenticators" api_proto "www.velocidex.com/golang/velociraptor/api/proto" - utils "www.velocidex.com/golang/velociraptor/api/utils" + api_utils "www.velocidex.com/golang/velociraptor/api/utils" config_proto "www.velocidex.com/golang/velociraptor/config/proto" "www.velocidex.com/golang/velociraptor/constants" crypto_utils "www.velocidex.com/golang/velociraptor/crypto/utils" "www.velocidex.com/golang/velociraptor/grpc_client" "www.velocidex.com/golang/velociraptor/logging" "www.velocidex.com/golang/velociraptor/server" + "www.velocidex.com/golang/velociraptor/utils" ) // A Mux for the reverse proxy feature. @@ -140,50 +141,50 @@ func PrepareGUIMux( return nil, err } - base := utils.GetBasePath(config_obj) - mux.Handle(utils.Join(base, "/api/"), ipFilter(config_obj, + base := api_utils.GetBasePath(config_obj) + mux.Handle(api_utils.Join(base, "/api/"), ipFilter(config_obj, csrfProtect(config_obj, auther.AuthenticateUserHandler(h)))) - mux.Handle(utils.Join(base, "/api/v1/DownloadTable"), + mux.Handle(api_utils.Join(base, "/api/v1/DownloadTable"), ipFilter(config_obj, csrfProtect(config_obj, auther.AuthenticateUserHandler(downloadTable())))) - mux.Handle(utils.Join(base, "/api/v1/DownloadVFSFile"), + mux.Handle(api_utils.Join(base, "/api/v1/DownloadVFSFile"), ipFilter(config_obj, csrfProtect(config_obj, auther.AuthenticateUserHandler(vfsFileDownloadHandler())))) - mux.Handle(utils.Join(base, "/api/v1/UploadTool"), + mux.Handle(api_utils.Join(base, "/api/v1/UploadTool"), ipFilter(config_obj, csrfProtect(config_obj, auther.AuthenticateUserHandler(toolUploadHandler())))) - mux.Handle(utils.Join(base, "/api/v1/UploadFormFile"), + mux.Handle(api_utils.Join(base, "/api/v1/UploadFormFile"), ipFilter(config_obj, csrfProtect(config_obj, auther.AuthenticateUserHandler(formUploadHandler())))) // Serve prepared zip files. - mux.Handle(utils.Join(base, "/downloads/"), + mux.Handle(api_utils.Join(base, "/downloads/"), ipFilter(config_obj, csrfProtect(config_obj, auther.AuthenticateUserHandler( http.StripPrefix(base, downloadFileStore([]string{"downloads"})))))) // Serve notebook items - mux.Handle(utils.Join(base, "/notebooks/"), + mux.Handle(api_utils.Join(base, "/notebooks/"), ipFilter(config_obj, csrfProtect(config_obj, auther.AuthenticateUserHandler( http.StripPrefix(base, downloadFileStore([]string{"notebooks"})))))) // Serve files from hunt notebooks - mux.Handle(utils.Join(base, "/hunts/"), + mux.Handle(api_utils.Join(base, "/hunts/"), ipFilter(config_obj, csrfProtect(config_obj, auther.AuthenticateUserHandler( http.StripPrefix(base, downloadFileStore([]string{"hunts"})))))) // Serve files from client notebooks - mux.Handle(utils.Join(base, "/clients/"), + mux.Handle(api_utils.Join(base, "/clients/"), ipFilter(config_obj, csrfProtect(config_obj, auther.AuthenticateUserHandler( http.StripPrefix(base, @@ -202,14 +203,14 @@ func PrepareGUIMux( if err != nil { return nil, err } - mux.Handle(utils.Join(base, "/app/index.html"), + mux.Handle(api_utils.Join(base, "/app/index.html"), ipFilter(config_obj, csrfProtect(config_obj, auther.AuthenticateUserHandler(h)))) // Redirect everything else to the app - mux.Handle(utils.GetBaseDirectory(config_obj), + mux.Handle(api_utils.GetBaseDirectory(config_obj), http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - http.Redirect(w, r, utils.Join(base, "/app/index.html"), 302) + http.Redirect(w, r, api_utils.Join(base, "/app/index.html"), 302) })) return mux, nil @@ -287,7 +288,7 @@ func GetAPIHandler( creds := credentials.NewTLS(&tls.Config{ Certificates: []tls.Certificate{cert}, RootCAs: CA_Pool, - ServerName: config_obj.Client.PinnedServerName, + ServerName: utils.GetSuperuserName(config_obj), }) opts := []grpc.DialOption{ @@ -301,9 +302,9 @@ func GetAPIHandler( return nil, err } - base := utils.GetBasePath(config_obj) + base := api_utils.GetBasePath(config_obj) reverse_proxy_mux := http.NewServeMux() - reverse_proxy_mux.Handle(utils.Join(base, "/api/v1/"), + reverse_proxy_mux.Handle(api_utils.Join(base, "/api/v1/"), http.StripPrefix(base, grpc_proxy_mux)) return reverse_proxy_mux, nil diff --git a/artifacts/testdata/windows/github_actions.config.yaml b/artifacts/testdata/windows/github_actions.config.yaml index 3dc292b0399..8a9c2c6c009 100644 --- a/artifacts/testdata/windows/github_actions.config.yaml +++ b/artifacts/testdata/windows/github_actions.config.yaml @@ -140,6 +140,8 @@ Datastore: implementation: FileBaseDataStore filestore_directory: x:\artifacts\testdata\server +defaults: + backup_period_seconds: -1 Writeback: private_key: | diff --git a/artifacts/testdata/windows/test.config.yaml b/artifacts/testdata/windows/test.config.yaml index f498df0d77f..4ac69a43edc 100644 --- a/artifacts/testdata/windows/test.config.yaml +++ b/artifacts/testdata/windows/test.config.yaml @@ -141,6 +141,9 @@ Datastore: filestore_directory: ./artifacts/testdata/server/ location: ./artifacts/testdata/server/ +defaults: + backup_period_seconds: -1 + Writeback: private_key: | -----BEGIN RSA PRIVATE KEY----- diff --git a/bin/config.go b/bin/config.go index 257af97e160..fab5256861a 100644 --- a/bin/config.go +++ b/bin/config.go @@ -1,19 +1,19 @@ /* - Velociraptor - Dig Deeper - Copyright (C) 2019-2024 Rapid7 Inc. +Velociraptor - Dig Deeper +Copyright (C) 2019-2024 Rapid7 Inc. - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as published - by the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published +by the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . */ package main @@ -34,13 +34,13 @@ import ( api_proto "www.velocidex.com/golang/velociraptor/api/proto" "www.velocidex.com/golang/velociraptor/config" config_proto "www.velocidex.com/golang/velociraptor/config/proto" - "www.velocidex.com/golang/velociraptor/constants" "www.velocidex.com/golang/velociraptor/crypto" - "www.velocidex.com/golang/velociraptor/crypto/utils" + crypto_utils "www.velocidex.com/golang/velociraptor/crypto/utils" "www.velocidex.com/golang/velociraptor/json" "www.velocidex.com/golang/velociraptor/logging" "www.velocidex.com/golang/velociraptor/services" "www.velocidex.com/golang/velociraptor/startup" + "www.velocidex.com/golang/velociraptor/utils" ) var ( @@ -221,7 +221,7 @@ func generateNewKeys(config_obj *config_proto.Config) error { // have a constant common name - clients will refuse to talk // with another common name. frontend_cert, err := crypto.GenerateServerCert( - config_obj, config_obj.Client.PinnedServerName) + config_obj, utils.GetSuperuserName(config_obj)) if err != nil { return fmt.Errorf("Unable to create Frontend cert: %w", err) } @@ -292,7 +292,7 @@ func doRotateKeyConfig() error { // Frontends must have a well known common name. frontend_cert, err := crypto.GenerateServerCert( - config_obj, config_obj.Client.PinnedServerName) + config_obj, utils.GetSuperuserName(config_obj)) if err != nil { return fmt.Errorf("Unable to create Frontend cert: %w", err) } @@ -423,7 +423,7 @@ func doDumpApiClientConfig() error { return err } - if *config_api_client_common_name == config_obj.Client.PinnedServerName { + if *config_api_client_common_name == utils.GetSuperuserName(config_obj) { return errors.New("Name reserved! You may not name your " + "api keys with this name.") } @@ -476,7 +476,7 @@ func doDumpApiClientConfig() error { // Possibly dump out the pkcs12 key if *config_api_client_pkcs12_output != "" { - ca_cert, err := utils.ParseX509CertFromPemStr([]byte( + ca_cert, err := crypto_utils.ParseX509CertFromPemStr([]byte( config_obj.Client.CaCertificate)) if err != nil { return err @@ -551,8 +551,8 @@ func doDumpApiClientConfig() error { // Make sure the user actually exists. user_manager := services.GetUserManager() - _, err = user_manager.GetUser(ctx, constants.PinnedServerName, - *config_api_client_common_name) + _, err = user_manager.GetUser(ctx, + utils.GetSuperuserName(config_obj), *config_api_client_common_name) if err != nil { // Need to ensure we have a user err := user_manager.SetUser(ctx, &api_proto.VelociraptorUser{ diff --git a/bin/grant.go b/bin/grant.go index debdec11e51..3f0c88a2532 100644 --- a/bin/grant.go +++ b/bin/grant.go @@ -8,7 +8,6 @@ import ( jsonpatch "github.com/evanphx/json-patch/v5" "www.velocidex.com/golang/velociraptor/acls" acl_proto "www.velocidex.com/golang/velociraptor/acls/proto" - "www.velocidex.com/golang/velociraptor/constants" "www.velocidex.com/golang/velociraptor/json" logging "www.velocidex.com/golang/velociraptor/logging" "www.velocidex.com/golang/velociraptor/services" @@ -79,7 +78,8 @@ func doGrant() error { // Check the user actually exists first user_manager := services.GetUserManager() - _, err = user_manager.GetUser(ctx, constants.PinnedServerName, principal) + _, err = user_manager.GetUser(ctx, + utils.GetSuperuserName(config_obj), principal) if err != nil { return err } diff --git a/bin/orgs.go b/bin/orgs.go index 4fc53ccc7f5..0636927818b 100644 --- a/bin/orgs.go +++ b/bin/orgs.go @@ -4,11 +4,11 @@ import ( "fmt" api_proto "www.velocidex.com/golang/velociraptor/api/proto" - "www.velocidex.com/golang/velociraptor/constants" "www.velocidex.com/golang/velociraptor/json" logging "www.velocidex.com/golang/velociraptor/logging" "www.velocidex.com/golang/velociraptor/services" "www.velocidex.com/golang/velociraptor/startup" + "www.velocidex.com/golang/velociraptor/utils" ) var ( @@ -88,7 +88,7 @@ func doOrgUserAdd() error { user_manager := services.GetUserManager() record, err := user_manager.GetUserWithHashes( - ctx, constants.PinnedServerName, *orgs_user_add_user) + ctx, utils.GetSuperuserName(config_obj), *orgs_user_add_user) if err != nil { return err } @@ -181,7 +181,7 @@ func doOrgDelete() error { logger.Info("Will remove org %v\n", *orgs_delete_org_id) return org_manager.DeleteOrg(ctx, - constants.PinnedServerName, *orgs_delete_org_id) + utils.GetSuperuserName(config_obj), *orgs_delete_org_id) } func init() { diff --git a/bin/users.go b/bin/users.go index 4a192c47f5c..4f8a1c8218f 100644 --- a/bin/users.go +++ b/bin/users.go @@ -1,19 +1,19 @@ /* - Velociraptor - Dig Deeper - Copyright (C) 2019-2024 Rapid7 Inc. +Velociraptor - Dig Deeper +Copyright (C) 2019-2024 Rapid7 Inc. - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as published - by the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published +by the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . */ package main @@ -25,12 +25,12 @@ import ( "golang.org/x/crypto/ssh/terminal" "www.velocidex.com/golang/velociraptor/api/authenticators" - "www.velocidex.com/golang/velociraptor/constants" "www.velocidex.com/golang/velociraptor/json" logging "www.velocidex.com/golang/velociraptor/logging" "www.velocidex.com/golang/velociraptor/services" "www.velocidex.com/golang/velociraptor/services/users" "www.velocidex.com/golang/velociraptor/startup" + "www.velocidex.com/golang/velociraptor/utils" ) var ( @@ -153,15 +153,15 @@ func doShowUser() error { defer sm.Close() users_manager := services.GetUserManager() - user_record, err := users_manager.GetUser(ctx, constants.PinnedServerName, - *user_show_name) + user_record, err := users_manager.GetUser(ctx, + utils.GetSuperuserName(config_obj), *user_show_name) if err != nil { return err } if *user_show_hashes { user_record, err := users_manager.GetUserWithHashes(ctx, - constants.PinnedServerName, *user_show_name) + utils.GetSuperuserName(config_obj), *user_show_name) if err != nil { return err } @@ -200,8 +200,8 @@ func doLockUser() error { defer sm.Close() users_manager := services.GetUserManager() - user_record, err := users_manager.GetUser(ctx, constants.PinnedServerName, - *user_lock_name) + user_record, err := users_manager.GetUser(ctx, + utils.GetSuperuserName(config_obj), *user_lock_name) if err != nil { return fmt.Errorf("Unable to find user %s", *user_lock_name) } diff --git a/config/config.go b/config/config.go index f5cae27066d..fd0a435ef82 100644 --- a/config/config.go +++ b/config/config.go @@ -1,19 +1,19 @@ /* - Velociraptor - Dig Deeper - Copyright (C) 2019-2024 Rapid7 Inc. +Velociraptor - Dig Deeper +Copyright (C) 2019-2024 Rapid7 Inc. - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as published - by the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published +by the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . */ package config @@ -95,9 +95,8 @@ func GetDefaultConfig() *config_proto.Config { // If set to true this will stop // arbitrary code execution on the // client. - PreventExecve: false, - MaxUploadSize: constants.MAX_MEMORY, - PinnedServerName: "VelociraptorServer", + PreventExecve: false, + MaxUploadSize: constants.MAX_MEMORY, }, API: &config_proto.APIConfig{ // Bind port for gRPC endpoint - this should not diff --git a/config/validate.go b/config/validate.go index 2a0b5ce5d23..6b299393dfb 100644 --- a/config/validate.go +++ b/config/validate.go @@ -48,10 +48,6 @@ func ValidateClientConfig(config_obj *config_proto.Config) error { config_obj.Client.MaxPoll = 60 // One minute } - if config_obj.Client.PinnedServerName == "" { - config_obj.Client.PinnedServerName = "VelociraptorServer" - } - if config_obj.Client.MaxUploadSize == 0 { config_obj.Client.MaxUploadSize = 5242880 } diff --git a/crypto/crypto_test.go b/crypto/crypto_test.go index ccc158652cd..67072059b32 100644 --- a/crypto/crypto_test.go +++ b/crypto/crypto_test.go @@ -1,19 +1,19 @@ /* - Velociraptor - Dig Deeper - Copyright (C) 2019-2024 Rapid7 Inc. +Velociraptor - Dig Deeper +Copyright (C) 2019-2024 Rapid7 Inc. - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as published - by the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published +by the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . */ package crypto_test @@ -32,7 +32,6 @@ import ( "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "google.golang.org/protobuf/proto" - "www.velocidex.com/golang/velociraptor/config" config_proto "www.velocidex.com/golang/velociraptor/config/proto" "www.velocidex.com/golang/velociraptor/crypto/client" crypto_client "www.velocidex.com/golang/velociraptor/crypto/client" @@ -162,9 +161,8 @@ func (self *TestSuite) TestEncDecClientToServerWithSpoof() { Source: "C.1234Spoof", Name: "OMG it's a string"}) - ConfigObj := config.GetDefaultConfig() cipher_text, err := self._EncryptMessageListWithSpoofedPackedMessage( - message_list, ConfigObj.Client.PinnedServerName) + message_list, utils.GetSuperuserName(self.ConfigObj)) assert.NoError(t, err) message_info, err := self.server_manager.Decrypt(cipher_text) @@ -256,7 +254,7 @@ func (self *TestSuite) TestEncDecClientToServer() { cipher_text, err := self.client_manager.EncryptMessageList( message_list, crypto_proto.PackedMessageList_ZCOMPRESSION, - nonce, self.ConfigObj.Client.PinnedServerName) + nonce, utils.GetSuperuserName(self.ConfigObj)) assert.NoError(t, err) initial_c := testutil.ToFloat64(crypto_client.RsaDecryptCounter) @@ -296,7 +294,7 @@ func (self *TestSuite) TestEncryption() { [][]byte{compressed}, crypto_proto.PackedMessageList_ZCOMPRESSION, self.ConfigObj.Client.Nonce, - self.ConfigObj.Client.PinnedServerName) + utils.GetSuperuserName(self.ConfigObj)) assert.NoError(t, err) result, err := self.server_manager.Decrypt(cipher_text) diff --git a/file_store/test_utils/server_config.go b/file_store/test_utils/server_config.go index dc61af6952a..d254228d272 100644 --- a/file_store/test_utils/server_config.go +++ b/file_store/test_utils/server_config.go @@ -48,7 +48,6 @@ Client: version: 0.6.4-rc4 commit: f3264824 build_time: "2022-04-14T02:23:05+10:00" - pinned_server_name: VelociraptorServer max_upload_size: 5242880 local_buffer: memory_size: 52428800 @@ -254,6 +253,7 @@ obfuscation_nonce: RzlAlmdcUyw= defaults: hunt_expiry_hours: 168 notebook_cell_timeout_min: 10 + backup_period_seconds: -1 services: hunt_manager: false diff --git a/flows/artifacts_test.go b/flows/artifacts_test.go index 818e9e689e3..c2f8afa048b 100644 --- a/flows/artifacts_test.go +++ b/flows/artifacts_test.go @@ -90,11 +90,13 @@ func (self *TestSuite) TestGetFlow() { request1 := &flows_proto.ArtifactCollectorArgs{ ClientId: self.client_id, + Creator: utils.GetSuperuserName(self.ConfigObj), Artifacts: []string{"Generic.Client.Info"}, } request2 := &flows_proto.ArtifactCollectorArgs{ ClientId: self.client_id, + Creator: utils.GetSuperuserName(self.ConfigObj), Artifacts: []string{"Generic.Client.Profile"}, } @@ -144,6 +146,7 @@ func (self *TestSuite) TestRetransmission() { request := &flows_proto.ArtifactCollectorArgs{ ClientId: self.client_id, + Creator: utils.GetSuperuserName(self.ConfigObj), Artifacts: []string{"Generic.Client.Info"}, } @@ -202,6 +205,7 @@ func (self *TestSuite) TestResourceLimits() { request := &flows_proto.ArtifactCollectorArgs{ ClientId: self.client_id, + Creator: utils.GetSuperuserName(self.ConfigObj), Artifacts: []string{"Generic.Client.Info"}, // Only accept 5 rows. diff --git a/grpc_client/grpc.go b/grpc_client/grpc.go index c6f0771076c..2ce2e9b8377 100644 --- a/grpc_client/grpc.go +++ b/grpc_client/grpc.go @@ -34,7 +34,7 @@ import ( "google.golang.org/grpc/credentials" api_proto "www.velocidex.com/golang/velociraptor/api/proto" config_proto "www.velocidex.com/golang/velociraptor/config/proto" - "www.velocidex.com/golang/velociraptor/constants" + "www.velocidex.com/golang/velociraptor/utils" ) var ( @@ -71,17 +71,14 @@ func getCreds(config_obj *config_proto.Config) (credentials.TransportCredentials certificate = config_obj.Frontend.Certificate private_key = config_obj.Frontend.PrivateKey ca_certificate = config_obj.Client.CaCertificate - server_name = config_obj.Client.PinnedServerName + server_name = utils.GetSuperuserName(config_obj) } if config_obj.ApiConfig != nil && config_obj.ApiConfig.ClientCert != "" { certificate = config_obj.ApiConfig.ClientCert private_key = config_obj.ApiConfig.ClientPrivateKey ca_certificate = config_obj.ApiConfig.CaCertificate - server_name = config_obj.ApiConfig.PinnedServerName - if server_name == "" { - server_name = constants.PinnedServerName - } + server_name = utils.GetSuperuserGWName(config_obj) } if certificate == "" { diff --git a/http_comms/comms.go b/http_comms/comms.go index 778b23e2c3e..30e8acd68f1 100644 --- a/http_comms/comms.go +++ b/http_comms/comms.go @@ -189,7 +189,7 @@ func NewHTTPConnector( // server. This setting also allows the server to be accessed // by e.g. localhost despite the certificate being issued to // VelociraptorServer. - transport.TLSClientConfig.ServerName = config_obj.Client.PinnedServerName + transport.TLSClientConfig.ServerName = utils.GetSuperuserName(config_obj) } else { // Not self signed - add the public roots for verifications. crypto.AddPublicRoots(transport.TLSClientConfig.RootCAs) @@ -533,7 +533,7 @@ func (self *HTTPConnector) rekeyNextServer(ctx context.Context) error { // We must be talking to the server! The server certificate // must have this common name. - if server_name != self.config_obj.Client.PinnedServerName { + if server_name != utils.GetSuperuserName(self.config_obj) { self.server_name = "" self.logger.Info("Invalid server certificate common name %v!", server_name) return errors.New("Invalid server certificate common name!") diff --git a/http_comms/e2e_test.go b/http_comms/e2e_test.go index a9e348ac21f..25f5d9e6b3b 100644 --- a/http_comms/e2e_test.go +++ b/http_comms/e2e_test.go @@ -230,6 +230,7 @@ func (self *TestSuite) testScheduleCollection() (closer func()) { request := &flows_proto.ArtifactCollectorArgs{ ClientId: self.client_id, Artifacts: []string{"TestArtifact"}, + Creator: utils.GetSuperuserName(self.ConfigObj), } flow_id, err := launcher.ScheduleArtifactCollection(self.Ctx, @@ -294,7 +295,7 @@ func (self *TestSuite) TestServerRotateKeyE2E() { // Now rekey the server frontend_cert, err := crypto.GenerateServerCert( - self.ConfigObj, self.ConfigObj.Client.PinnedServerName) + self.ConfigObj, utils.GetSuperuserName(self.ConfigObj)) assert.NoError(self.T(), err) self.ConfigObj.Frontend.Certificate = frontend_cert.Cert diff --git a/http_comms/sender_test.go b/http_comms/sender_test.go index 84c69d32828..ad55f8cc636 100644 --- a/http_comms/sender_test.go +++ b/http_comms/sender_test.go @@ -1,19 +1,19 @@ /* - Velociraptor - Dig Deeper - Copyright (C) 2019-2024 Rapid7 Inc. +Velociraptor - Dig Deeper +Copyright (C) 2019-2024 Rapid7 Inc. - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as published - by the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published +by the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . */ package http_comms @@ -99,7 +99,7 @@ func (self *MockHTTPConnector) Post(ctx context.Context, func (self *MockHTTPConnector) ReKeyNextServer(ctx context.Context) {} func (self *MockHTTPConnector) ServerName() string { - return "VelociraptorServer" + return utils.GetSuperuserName(self.config_obj) } // Try to send the message immediately. diff --git a/http_comms/test_data/server.config.yaml b/http_comms/test_data/server.config.yaml index be6dc795f01..55e152a631a 100644 --- a/http_comms/test_data/server.config.yaml +++ b/http_comms/test_data/server.config.yaml @@ -245,3 +245,4 @@ obfuscation_nonce: RzlAlmdcUyw= defaults: hunt_expiry_hours: 168 notebook_cell_timeout_min: 10 + backup_period_seconds: -1 diff --git a/server/server_test.go b/server/server_test.go index 41d55b1cd63..587b094b5dd 100644 --- a/server/server_test.go +++ b/server/server_test.go @@ -429,6 +429,7 @@ func (self *ServerTestSuite) TestScheduleCollection() { t := self.T() request := &flows_proto.ArtifactCollectorArgs{ ClientId: self.client_id, + Creator: utils.GetSuperuserName(self.ConfigObj), Artifacts: []string{"Generic.Client.Info"}, } @@ -490,6 +491,7 @@ func (self *ServerTestSuite) createArtifactCollection() (string, error) { repository, &flows_proto.ArtifactCollectorArgs{ ClientId: self.client_id, + Creator: utils.GetSuperuserName(self.ConfigObj), Artifacts: []string{"Generic.Client.Info"}, }, nil) diff --git a/services/acl_manager/acl_manager.go b/services/acl_manager/acl_manager.go index fbd3823bdf4..035b80cd96a 100644 --- a/services/acl_manager/acl_manager.go +++ b/services/acl_manager/acl_manager.go @@ -45,6 +45,13 @@ func NewACLManager( // ACLs do not typically change that quickly, cache for 60 sec. result.lru.SetTTL(timeout) + backups, err := services.GetBackupService(config_obj) + if err == nil { + backups.Register(&ACLBackupProvider{ + config_obj: config_obj, + }) + } + return result, nil } @@ -89,8 +96,7 @@ func (self ACLManager) GetEffectivePolicy( acl_obj := &acl_proto.ApiClientACL{} // The server identity is special - it means the user is an admin. - if config_obj != nil && config_obj.Client != nil && - config_obj.Client.PinnedServerName == principal { + if principal == utils.GetSuperuserName(config_obj) { return &acl_proto.ApiClientACL{SuperUser: true}, nil } @@ -155,7 +161,7 @@ func (self ACLManager) CheckAccess( } // Internal calls from the server are allowed to do anything. - if config_obj.Client != nil && principal == config_obj.Client.PinnedServerName { + if principal == utils.GetSuperuserName(config_obj) { return true, nil } diff --git a/services/acl_manager/backup.go b/services/acl_manager/backup.go new file mode 100644 index 00000000000..dee5625b085 --- /dev/null +++ b/services/acl_manager/backup.go @@ -0,0 +1,85 @@ +package acl_manager + +import ( + "context" + "fmt" + "sync" + + "github.com/Velocidex/ordereddict" + config_proto "www.velocidex.com/golang/velociraptor/config/proto" + "www.velocidex.com/golang/velociraptor/services" + "www.velocidex.com/golang/velociraptor/utils" + "www.velocidex.com/golang/vfilter" +) + +type ACLBackupProvider struct { + config_obj *config_proto.Config + manager *ACLManager +} + +func (self ACLBackupProvider) ProviderName() string { + return "ACLBackupProvider" +} + +func (self ACLBackupProvider) Name() []string { + return []string{"acls.json"} +} + +func (self ACLBackupProvider) BackupResults( + ctx context.Context, wg *sync.WaitGroup) ( + <-chan vfilter.Row, error) { + + users_manager := services.GetUserManager() + user_list, err := users_manager.ListUsers(ctx, + // Superuser privileges .... + utils.GetSuperuserName(self.config_obj), + + // Only export current org. + []string{utils.GetOrgId(self.config_obj)}) + if err != nil { + return nil, err + } + + output := make(chan vfilter.Row) + + wg.Add(1) + go func() { + defer wg.Done() + defer close(output) + + for _, user := range user_list { + policy, err := self.manager.GetPolicy( + self.config_obj, user.Name) + if err == nil { + select { + case <-ctx.Done(): + return + + case output <- ordereddict.NewDict(). + Set("Principal", user). + Set("Policy", policy): + } + } + } + }() + + return output, nil +} + +// We do not want to automatically restore ACL permissions from backup +// because this may represent a security compromise but we want to +// allow users to see the ACL permissions that were backed up. +func (self ACLBackupProvider) Restore(ctx context.Context, + in <-chan vfilter.Row) (stat services.BackupStat, err error) { + + count := 0 + defer func() { + stat.Message = fmt.Sprintf("ACL backups do not automatically restore. There are %v ACL records which may be restored manually.", count) + }() + + for _ = range in { + count++ + } + + return stat, nil +} diff --git a/services/backup/backup.go b/services/backup/backup.go index 5b6e276f2e8..a4e04184359 100644 --- a/services/backup/backup.go +++ b/services/backup/backup.go @@ -151,6 +151,8 @@ func (self *BackupService) RestoreBackup( dest := strings.Join(provider.Name(), "/") logger.Info("BackupService: Error restoring to %v: %v", dest, err) + stat.Name = provider.ProviderName() + stat.Error = err } stats = append(stats, stat) } @@ -161,6 +163,7 @@ func (self *BackupService) RestoreBackup( func (self *BackupService) feedProvider( provider services.BackupProvider, container *zip.Reader) (stat services.BackupStat, err error) { + dest := strings.Join(provider.Name(), "/") member, err := container.Open(dest) if err != nil { @@ -175,6 +178,18 @@ func (self *BackupService) feedProvider( wg := &sync.WaitGroup{} defer wg.Wait() + // The provider will return a result when done. + results := make(chan services.BackupStat) + defer func() { + // Wait here until the provider is done. + stat = <-results + stat.Name = provider.ProviderName() + if stat.Error != nil { + err = stat.Error + } + }() + + // Feed rows into this channel so provider can restore backups. output := make(chan vfilter.Row) defer close(output) @@ -184,14 +199,19 @@ func (self *BackupService) feedProvider( wg.Add(1) go func() { defer wg.Done() - defer cancel() + defer close(results) // Preserve the provider error as our return - stat, err = provider.Restore(sub_ctx, output) - stat.Name = provider.ProviderName() + stat, err := provider.Restore(sub_ctx, output) if err != nil { stat.Error = err } + + // Stop new rows to be written - we dont care any more. + cancel() + + // Pass the result to the main routine. + results <- stat }() // Now dump the rows into the provider. @@ -213,6 +233,7 @@ func (self *BackupService) feedProvider( select { case <-sub_ctx.Done(): return stat, nil + case output <- row: } } @@ -238,10 +259,16 @@ func NewBackupService( // Every day delay := time.Hour * 24 - if config_obj.Defaults != nil && - config_obj.Defaults.BackupPeriodSeconds > 0 { - delay = time.Duration( - config_obj.Defaults.BackupPeriodSeconds) * time.Second + if config_obj.Defaults != nil { + // Backups are disabled. + if config_obj.Defaults.BackupPeriodSeconds < 0 { + return result + } + + if config_obj.Defaults.BackupPeriodSeconds > 0 { + delay = time.Duration( + config_obj.Defaults.BackupPeriodSeconds) * time.Second + } } logger := logging.GetLogger(config_obj, &logging.FrontendComponent) diff --git a/services/backup/backup_test.go b/services/backup/backup_test.go index 2d956ed27db..f6f730afc98 100644 --- a/services/backup/backup_test.go +++ b/services/backup/backup_test.go @@ -3,6 +3,7 @@ package backup_test import ( "archive/zip" "context" + "errors" "io/ioutil" "strings" "sync" @@ -30,7 +31,8 @@ type TestBackupProvider struct { rows []*ordereddict.Dict // Restored rows from backup - restored []vfilter.Row + restored []vfilter.Row + restored_error error } func (self TestBackupProvider) ProviderName() string { @@ -62,6 +64,14 @@ func (self TestBackupProvider) BackupResults( func (self *TestBackupProvider) Restore( ctx context.Context, in <-chan vfilter.Row) (services.BackupStat, error) { + + if self.restored_error != nil { + return services.BackupStat{ + Error: self.restored_error, + Message: self.restored_error.Error(), + }, self.restored_error + } + for row := range in { self.restored = append(self.restored, row) } @@ -130,6 +140,17 @@ func (self *BackupTestSuite) TestBackups() { golden.Set("RestoredTestProvider", provider.restored). Set("RestoredTestProviderStats", filterStats(stats)) + // Now restore the data from backup with an error + provider.restored_error = errors.New("I have an error") + provider.restored = nil + + stats, err = backup_service.(*backup.BackupService). + RestoreBackup(export_path) + assert.NoError(self.T(), err) + + golden.Set("RestoredTestProvider With Error", provider.restored). + Set("RestoredTestProviderStatsWithError", filterStats(stats)) + goldie.Assert(self.T(), "TestBackups", json.MustMarshalIndent(golden)) } diff --git a/services/backup/fixtures/TestBackups.golden b/services/backup/fixtures/TestBackups.golden index 28fa026f171..fb26753b662 100644 --- a/services/backup/fixtures/TestBackups.golden +++ b/services/backup/fixtures/TestBackups.golden @@ -22,5 +22,13 @@ "Error": null, "Message": "All good!" } + ], + "RestoredTestProvider With Error": null, + "RestoredTestProviderStatsWithError": [ + { + "Name": "TestProvider", + "Error": {}, + "Message": "I have an error" + } ] } \ No newline at end of file diff --git a/services/sanity/sanity.go b/services/sanity/sanity.go index 1bbfb0da546..d8268580ec1 100644 --- a/services/sanity/sanity.go +++ b/services/sanity/sanity.go @@ -74,14 +74,12 @@ func (self *SanityChecks) CheckRootOrg( // Make sure our internal VelociraptorServer service account is // properly created. Default accounts are created with org admin // so they can add new orgs as required. - if config_obj.Client != nil && config_obj.Client.PinnedServerName != "" { - service_account_name := config_obj.Client.PinnedServerName - err := services.GrantRoles( - config_obj, service_account_name, - []string{"administrator", "org_admin"}) - if err != nil { - return err - } + service_account_name := utils.GetSuperuserName(config_obj) + err = services.GrantRoles( + config_obj, service_account_name, + []string{"administrator", "org_admin"}) + if err != nil { + return err } if config_obj.Frontend != nil { diff --git a/services/sanity/sanity_test.go b/services/sanity/sanity_test.go index 5ba81c48fcb..52e474f5fff 100644 --- a/services/sanity/sanity_test.go +++ b/services/sanity/sanity_test.go @@ -125,7 +125,8 @@ func (self *ServicesTestSuite) TestCreateUser() { Set("/acl/User1.json", acl_obj) user_manager := services.GetUserManager() - user1_full, err := user_manager.GetUserWithHashes(self.Ctx, "VelociraptorServer", "User1") + user1_full, err := user_manager.GetUserWithHashes( + self.Ctx, utils.GetSuperuserName(self.ConfigObj), "User1") assert.NoError(self.T(), err) // Should include the user hashes and their orgs list @@ -198,7 +199,8 @@ func (self *ServicesTestSuite) TestCreateUserInOrgs() { } user_manager := services.GetUserManager() - user1_full, err := user_manager.GetUserWithHashes(self.Ctx, "VelociraptorServer", "User1") + user1_full, err := user_manager.GetUserWithHashes( + self.Ctx, utils.GetSuperuserName(self.ConfigObj), "User1") assert.NoError(self.T(), err) // Should include the user hashes and all their orgs list diff --git a/services/sanity/server_artifacts.go b/services/sanity/server_artifacts.go index 9e32fd14d11..0b3f81a253e 100644 --- a/services/sanity/server_artifacts.go +++ b/services/sanity/server_artifacts.go @@ -13,6 +13,7 @@ import ( "www.velocidex.com/golang/velociraptor/logging" "www.velocidex.com/golang/velociraptor/paths" "www.velocidex.com/golang/velociraptor/services" + "www.velocidex.com/golang/velociraptor/utils" "www.velocidex.com/golang/velociraptor/vql/acl_managers" ) @@ -84,11 +85,7 @@ func startInitialArtifacts( logger := logging.GetLogger(config_obj, &logging.FrontendComponent) // Run artifacts with full privileges. - principal := "" - if config_obj.Client != nil { - principal = config_obj.Client.PinnedServerName - } - + principal := utils.GetSuperuserName(config_obj) _, err = launcher.ScheduleArtifactCollection(ctx, config_obj, acl_managers.NewRoleACLManager(config_obj, "administrator"), repository, diff --git a/services/sanity/users.go b/services/sanity/users.go index 07f3276cec7..6ec17d6377c 100644 --- a/services/sanity/users.go +++ b/services/sanity/users.go @@ -21,11 +21,7 @@ func createInitialUsers( return nil } - superuser := "VelociraptorServer" - if config_obj.Client != nil { - superuser = config_obj.Client.PinnedServerName - } - + superuser := utils.GetSuperuserName(config_obj) user_names := config_obj.GUI.InitialUsers logger := logging.GetLogger(config_obj, &logging.FrontendComponent) diff --git a/services/server_artifacts/collection_context.go b/services/server_artifacts/collection_context.go index e9c42ef4ec8..43db484a174 100644 --- a/services/server_artifacts/collection_context.go +++ b/services/server_artifacts/collection_context.go @@ -354,8 +354,8 @@ func (self *contextManager) RunQuery( } principal := arg.Principal - if principal == "" && self.config_obj.Client != nil { - principal = self.config_obj.Client.PinnedServerName + if principal == "" { + return errors.New("Principal must be set") } flow_path_manager := paths.NewFlowPathManager("server", self.session_id) diff --git a/services/server_monitoring/server_monitoring.go b/services/server_monitoring/server_monitoring.go index 16555919f58..92db6396713 100644 --- a/services/server_monitoring/server_monitoring.go +++ b/services/server_monitoring/server_monitoring.go @@ -338,8 +338,7 @@ func (self *EventTable) RunQuery( // artifact launches other artifacts then it will indicate the // creator was the server. ACLManager: acl_managers.NewServerACLManager( - self.config_obj, - self.config_obj.Client.PinnedServerName), + self.config_obj, utils.GetSuperuserName(config_obj)), Env: ordereddict.NewDict(), Repository: repository, Logger: log.New(self.logger, "", 0), diff --git a/services/server_monitoring/server_monitoring_test.go b/services/server_monitoring/server_monitoring_test.go index f41fbdaa2db..15bf1d9ec91 100644 --- a/services/server_monitoring/server_monitoring_test.go +++ b/services/server_monitoring/server_monitoring_test.go @@ -360,7 +360,7 @@ sources: // Install a table with an initial artifact filename := filepath.Join(tempdir, "testfile1.txt") err = event_table.Update(self.Ctx, - self.ConfigObj, "VelociraptorServer", + self.ConfigObj, utils.GetSuperuserName(self.ConfigObj), &flows_proto.ArtifactCollectorArgs{ Artifacts: []string{"TestArtifact"}, Specs: []*flows_proto.ArtifactSpec{{ diff --git a/services/users/add_org_test.go b/services/users/add_org_test.go index 85fc4dd74f5..b325c271ffd 100644 --- a/services/users/add_org_test.go +++ b/services/users/add_org_test.go @@ -6,6 +6,7 @@ import ( acl_proto "www.velocidex.com/golang/velociraptor/acls/proto" "www.velocidex.com/golang/velociraptor/json" "www.velocidex.com/golang/velociraptor/services" + "www.velocidex.com/golang/velociraptor/utils" "www.velocidex.com/golang/velociraptor/vtesting/assert" ) @@ -77,7 +78,8 @@ func (self *UserManagerTestSuite) TestAddUserToOrg() { // Try to create a reserved user err = users_manager.AddUserToOrg( self.Ctx, services.AddNewUser, - "AdminO2", "VelociraptorServer", []string{"O2"}, reader_policy) + "AdminO2", utils.GetSuperuserName(self.ConfigObj), + []string{"O2"}, reader_policy) assert.ErrorContains(self.T(), err, "reserved") goldie.Assert(self.T(), "TestAddUserToOrg", json.MustMarshalIndent(golden)) diff --git a/services/users/get.go b/services/users/get.go index 1b69e9d6d01..a7daecde1de 100644 --- a/services/users/get.go +++ b/services/users/get.go @@ -6,6 +6,7 @@ import ( "www.velocidex.com/golang/velociraptor/acls" api_proto "www.velocidex.com/golang/velociraptor/api/proto" "www.velocidex.com/golang/velociraptor/services" + "www.velocidex.com/golang/velociraptor/utils" ) // Returns the user record after stripping sensitive information like @@ -22,7 +23,7 @@ func (self *UserManager) GetUser( // For the server name we dont have a real user record, we make a // hard coded user record instead. - if username == self.config_obj.Client.PinnedServerName { + if username == utils.GetSuperuserName(self.config_obj) { return &api_proto.VelociraptorUser{ Name: username, }, nil diff --git a/services/users/grpc.go b/services/users/grpc.go index 230934fc926..3d8f2e604da 100644 --- a/services/users/grpc.go +++ b/services/users/grpc.go @@ -25,9 +25,8 @@ func (self UserManager) GetUserFromContext(ctx context.Context) ( // This is not a real user but represents the grpc gateway // connection - it is always allowed. - if grpc_user_info.Name == constants.PinnedServerName || - (self.config_obj.API != nil && - self.config_obj.API.PinnedGwName == grpc_user_info.Name) { + if grpc_user_info.Name == utils.GetSuperuserName(org_config_obj) || + grpc_user_info.Name == utils.GetSuperuserGWName(org_config_obj) { user_record = &api_proto.VelociraptorUser{ Name: grpc_user_info.Name, } diff --git a/services/users/users.go b/services/users/users.go index f822d89723b..5ada640abdf 100644 --- a/services/users/users.go +++ b/services/users/users.go @@ -1,19 +1,19 @@ /* - Velociraptor - Dig Deeper - Copyright (C) 2019-2024 Rapid7 Inc. +Velociraptor - Dig Deeper +Copyright (C) 2019-2024 Rapid7 Inc. - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as published - by the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published +by the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . */ package users @@ -28,9 +28,9 @@ import ( api_proto "www.velocidex.com/golang/velociraptor/api/proto" config_proto "www.velocidex.com/golang/velociraptor/config/proto" - "www.velocidex.com/golang/velociraptor/constants" "www.velocidex.com/golang/velociraptor/logging" "www.velocidex.com/golang/velociraptor/services" + "www.velocidex.com/golang/velociraptor/utils" ) const ( @@ -103,17 +103,16 @@ func validateUsername(config_obj *config_proto.Config, name string) error { return fmt.Errorf("Unacceptable username %v", name) } - if config_obj.API != nil && - config_obj.API.PinnedGwName == name { + if utils.GetSuperuserName(config_obj) == name { return fmt.Errorf("Username is reserved: %v", name) } - if config_obj.Client != nil && - config_obj.Client.PinnedServerName == name { + if config_obj.API != nil && + config_obj.API.PinnedGwName == name { return fmt.Errorf("Username is reserved: %v", name) } - if name == constants.PinnedGwName || name == constants.PinnedServerName { + if name == utils.GetSuperuserGWName(config_obj) { return fmt.Errorf("Username is reserved: %v", name) } diff --git a/services/vfs_service/vfs_service_test.go b/services/vfs_service/vfs_service_test.go index a6857325e22..41734882afc 100644 --- a/services/vfs_service/vfs_service_test.go +++ b/services/vfs_service/vfs_service_test.go @@ -54,7 +54,8 @@ func (self *VFSServiceTestSuite) SetupTest() { // Register a user manager that returns the superuser user to skip // any ACLs checks. This helps us test the API server to make sure // the GUI will present the correct data. - users.RegisterTestUserManager(self.ConfigObj, "VelociraptorServer") + users.RegisterTestUserManager( + self.ConfigObj, utils.GetSuperuserName(self.ConfigObj)) self.closer = utils.MockTime(utils.NewMockClock(time.Unix(1000, 0))) } diff --git a/utils/orgs.go b/utils/orgs.go index 1a0ed9e7cc4..43b34f1cffa 100644 --- a/utils/orgs.go +++ b/utils/orgs.go @@ -65,3 +65,7 @@ func OrgIdInList(org_id string, list []string) bool { } return false } + +func GetOrgId(config_obj *config_proto.Config) string { + return NormalizedOrgId(config_obj.OrgId) +} diff --git a/utils/users.go b/utils/users.go new file mode 100644 index 00000000000..806c2e5ebaa --- /dev/null +++ b/utils/users.go @@ -0,0 +1,28 @@ +package utils + +import ( + config_proto "www.velocidex.com/golang/velociraptor/config/proto" + "www.velocidex.com/golang/velociraptor/constants" +) + +func GetSuperuserName( + config_obj *config_proto.Config) string { + if config_obj == nil || + config_obj.Client == nil || + config_obj.Client.PinnedServerName == "" { + return constants.PinnedServerName + } + + return config_obj.Client.PinnedServerName +} + +func GetSuperuserGWName( + config_obj *config_proto.Config) string { + if config_obj == nil || + config_obj.API == nil || + config_obj.API.PinnedGwName == "" { + return constants.PinnedGwName + } + + return config_obj.API.PinnedGwName +} diff --git a/vql/acl_managers/null.go b/vql/acl_managers/null.go index ebeecbd9e33..46ad40d0a2d 100644 --- a/vql/acl_managers/null.go +++ b/vql/acl_managers/null.go @@ -1,6 +1,9 @@ package acl_managers -import "www.velocidex.com/golang/velociraptor/acls" +import ( + "www.velocidex.com/golang/velociraptor/acls" + "www.velocidex.com/golang/velociraptor/constants" +) // Satisfy the interface vql_subsystem.ACLManager @@ -23,3 +26,7 @@ func (self NullACLManager) CheckAccessInOrg( org_id string, permission ...acls.ACL_PERMISSION) (bool, error) { return true, nil } + +func (self NullACLManager) GetPrincipal() string { + return constants.PinnedServerName +} diff --git a/vql/acl_managers/role.go b/vql/acl_managers/role.go index 0d5fda28616..06374888c86 100644 --- a/vql/acl_managers/role.go +++ b/vql/acl_managers/role.go @@ -4,7 +4,6 @@ import ( "www.velocidex.com/golang/velociraptor/acls" acl_proto "www.velocidex.com/golang/velociraptor/acls/proto" config_proto "www.velocidex.com/golang/velociraptor/config/proto" - "www.velocidex.com/golang/velociraptor/constants" "www.velocidex.com/golang/velociraptor/services" "www.velocidex.com/golang/velociraptor/utils" vql_subsystem "www.velocidex.com/golang/velociraptor/vql" @@ -33,7 +32,7 @@ func (self *RoleACLManager) CheckAccess( func (self *RoleACLManager) GetPrincipal() string { if self.is_admin { - return constants.PinnedServerName + return utils.GetSuperuserName(self.config_obj) } return "" } diff --git a/vql/acl_managers/server.go b/vql/acl_managers/server.go index 24552abe1d9..47c0f7fc222 100644 --- a/vql/acl_managers/server.go +++ b/vql/acl_managers/server.go @@ -8,6 +8,7 @@ import ( acl_proto "www.velocidex.com/golang/velociraptor/acls/proto" config_proto "www.velocidex.com/golang/velociraptor/config/proto" "www.velocidex.com/golang/velociraptor/services" + "www.velocidex.com/golang/velociraptor/utils" vql_subsystem "www.velocidex.com/golang/velociraptor/vql" ) @@ -65,8 +66,7 @@ func (self *ServerACLManager) CheckAccess( } // If the principal is the super user we allow them everything. - if self.config_obj.Client != nil && - self.principal == self.config_obj.Client.PinnedServerName { + if self.principal == utils.GetSuperuserName(self.config_obj) { return true, nil } diff --git a/vql/networking/tls.go b/vql/networking/tls.go index 7a3c5b5adda..5f4bcfeb7a4 100644 --- a/vql/networking/tls.go +++ b/vql/networking/tls.go @@ -53,7 +53,7 @@ func normalizeThumbPrints(thumbprints []string) []string { // to be able to trust our own server. Our own server is signed by our // own CA and also may have a different common name (not related to // DNS). For example, in self signed mode, the server certificate is -// signed for VelociraptorServer but may be served over +// signed for "VelociraptorServer" but may be served over // "localhost". Using the default TLS configuration this connection // will be rejected. diff --git a/vql/server/downloads/downloads_test.go b/vql/server/downloads/downloads_test.go index 25ab88c5f87..fba6badc4a4 100644 --- a/vql/server/downloads/downloads_test.go +++ b/vql/server/downloads/downloads_test.go @@ -106,6 +106,7 @@ func (self *TestSuite) TestExportCollectionServerArtifact() { self.acl_manager, repository, &flows_proto.ArtifactCollectorArgs{ Artifacts: []string{"TestArtifact"}, + Creator: utils.GetSuperuserName(self.ConfigObj), ClientId: "server", }, utils.SyncCompleter) assert.NoError(self.T(), err) diff --git a/vql/server/downloads/fixtures/TestExportCollectionServerArtifact.golden b/vql/server/downloads/fixtures/TestExportCollectionServerArtifact.golden index 182ec45ea57..233e8e5716a 100644 --- a/vql/server/downloads/fixtures/TestExportCollectionServerArtifact.golden +++ b/vql/server/downloads/fixtures/TestExportCollectionServerArtifact.golden @@ -6,6 +6,7 @@ "client_id": "server", "session_id": "F.1234", "request": { + "creator": "VelociraptorServer", "client_id": "server", "artifacts": [ "TestArtifact" @@ -92,6 +93,7 @@ { "query_id": 1, "total_queries": 1, + "principal": "VelociraptorServer", "Query": [ { "VQL": "LET TestArtifact_0_0 = SELECT \"Hello\" AS Col, pathspec(parse=\"/bin/ls\", path_type=\"linux\") AS OSPath, upload(accessor=\"data\", file=\"Some Data\", name=\"test.txt\") AS Upload1, upload(accessor=\"data\", file=\"Some Other Data\", name=\"test2.txt\") AS Upload2 FROM scope()" diff --git a/vql/server/flows/parallel_test.go b/vql/server/flows/parallel_test.go index ed825ef97b2..2517bb90e7b 100644 --- a/vql/server/flows/parallel_test.go +++ b/vql/server/flows/parallel_test.go @@ -171,6 +171,7 @@ func (self *TestSuite) TestHuntsSource() { self.ConfigObj, acl_managers.NullACLManager{}, repository, &flows_proto.ArtifactCollectorArgs{ ClientId: client_id, + Creator: utils.GetSuperuserName(self.ConfigObj), Artifacts: []string{"Test.Artifact"}, }, nil) assert.NoError(self.T(), err) diff --git a/vql/tools/collector/import_hunt_test.go b/vql/tools/collector/import_hunt_test.go index 0a928b11176..b67a442f9b8 100644 --- a/vql/tools/collector/import_hunt_test.go +++ b/vql/tools/collector/import_hunt_test.go @@ -103,6 +103,7 @@ func (self *TestSuite) TestCreateAndImportHunt() { flow_id, err := launcher.ScheduleArtifactCollection(self.Ctx, self.ConfigObj, acl_manager, repository, &flows_proto.ArtifactCollectorArgs{ Artifacts: []string{"TestArtifact", "AnotherTestArtifact"}, + Creator: utils.GetSuperuserName(self.ConfigObj), ClientId: "server", }, utils.SyncCompleter) assert.NoError(self.T(), err) diff --git a/vql/tools/collector/import_test.go b/vql/tools/collector/import_test.go index 8cfc4ce3b5a..1d79d87ee9c 100644 --- a/vql/tools/collector/import_test.go +++ b/vql/tools/collector/import_test.go @@ -61,6 +61,7 @@ func (self *TestSuite) TestCreateAndImportCollection() { flow_id, err := launcher.ScheduleArtifactCollection(self.Ctx, self.ConfigObj, acl_manager, repository, &flows_proto.ArtifactCollectorArgs{ Artifacts: []string{"TestArtifact", "AnotherTestArtifact"}, + Creator: utils.GetSuperuserName(self.ConfigObj), ClientId: client_id, }, utils.SyncCompleter) assert.NoError(self.T(), err)