diff --git a/changes/14500-disable-scripts-impl b/changes/14500-disable-scripts-impl
new file mode 100644
index 0000000000..d26e5e832f
--- /dev/null
+++ b/changes/14500-disable-scripts-impl
@@ -0,0 +1 @@
+- Adds ability to disable Scripts feature in Fleet through a new config flag: `scripts_disabled`.
\ No newline at end of file
diff --git a/cmd/fleetctl/testdata/expectedGetConfigAppConfigJson.json b/cmd/fleetctl/testdata/expectedGetConfigAppConfigJson.json
index 05d1de8ef3..cf01e91342 100644
--- a/cmd/fleetctl/testdata/expectedGetConfigAppConfigJson.json
+++ b/cmd/fleetctl/testdata/expectedGetConfigAppConfigJson.json
@@ -13,7 +13,8 @@
"live_query_disabled": false,
"query_reports_disabled": false,
"enable_analytics": false,
- "deferred_save_host": false
+ "deferred_save_host": false,
+ "scripts_disabled": false
},
"smtp_settings": {
"enable_smtp": false,
diff --git a/cmd/fleetctl/testdata/expectedGetConfigAppConfigYaml.yml b/cmd/fleetctl/testdata/expectedGetConfigAppConfigYaml.yml
index 9d4d4a6e3a..7597345851 100644
--- a/cmd/fleetctl/testdata/expectedGetConfigAppConfigYaml.yml
+++ b/cmd/fleetctl/testdata/expectedGetConfigAppConfigYaml.yml
@@ -56,6 +56,7 @@ spec:
live_query_disabled: false
query_reports_disabled: false
server_url: ""
+ scripts_disabled: false
smtp_settings:
authentication_method: ""
authentication_type: ""
diff --git a/cmd/fleetctl/testdata/expectedGetConfigIncludeServerConfigJson.json b/cmd/fleetctl/testdata/expectedGetConfigIncludeServerConfigJson.json
index 2964165e03..216dbd75b3 100644
--- a/cmd/fleetctl/testdata/expectedGetConfigIncludeServerConfigJson.json
+++ b/cmd/fleetctl/testdata/expectedGetConfigIncludeServerConfigJson.json
@@ -13,7 +13,8 @@
"live_query_disabled": false,
"query_reports_disabled": false,
"enable_analytics": false,
- "deferred_save_host": false
+ "deferred_save_host": false,
+ "scripts_disabled": false
},
"smtp_settings": {
"enable_smtp": false,
diff --git a/cmd/fleetctl/testdata/expectedGetConfigIncludeServerConfigYaml.yml b/cmd/fleetctl/testdata/expectedGetConfigIncludeServerConfigYaml.yml
index 69539a4d27..3d614097fa 100644
--- a/cmd/fleetctl/testdata/expectedGetConfigIncludeServerConfigYaml.yml
+++ b/cmd/fleetctl/testdata/expectedGetConfigIncludeServerConfigYaml.yml
@@ -95,6 +95,7 @@ spec:
live_query_disabled: false
query_reports_disabled: false
server_url: ""
+ scripts_disabled: false
smtp_settings:
authentication_method: ""
authentication_type: ""
diff --git a/cmd/fleetctl/testdata/macosSetupExpectedAppConfigEmpty.yml b/cmd/fleetctl/testdata/macosSetupExpectedAppConfigEmpty.yml
index 1ce4b5737f..a453a2f7f5 100644
--- a/cmd/fleetctl/testdata/macosSetupExpectedAppConfigEmpty.yml
+++ b/cmd/fleetctl/testdata/macosSetupExpectedAppConfigEmpty.yml
@@ -56,6 +56,7 @@ spec:
live_query_disabled: false
query_reports_disabled: false
server_url: https://example.org
+ scripts_disabled: false
smtp_settings:
authentication_method: ""
authentication_type: ""
diff --git a/cmd/fleetctl/testdata/macosSetupExpectedAppConfigSet.yml b/cmd/fleetctl/testdata/macosSetupExpectedAppConfigSet.yml
index bff74ddc1c..5826922729 100644
--- a/cmd/fleetctl/testdata/macosSetupExpectedAppConfigSet.yml
+++ b/cmd/fleetctl/testdata/macosSetupExpectedAppConfigSet.yml
@@ -56,6 +56,7 @@ spec:
live_query_disabled: false
query_reports_disabled: false
server_url: https://example.org
+ scripts_disabled: false
smtp_settings:
authentication_method: ""
authentication_type: ""
diff --git a/frontend/__mocks__/configMock.ts b/frontend/__mocks__/configMock.ts
index e73ce1a729..26b996662a 100644
--- a/frontend/__mocks__/configMock.ts
+++ b/frontend/__mocks__/configMock.ts
@@ -13,6 +13,7 @@ const DEFAULT_CONFIG_MOCK: IConfig = {
enable_analytics: true,
deferred_save_host: false,
query_reports_disabled: false,
+ scripts_disabled: false,
},
smtp_settings: {
enable_smtp: false,
diff --git a/frontend/components/TableContainer/DataTable/DropdownCell/DropdownCell.tsx b/frontend/components/TableContainer/DataTable/DropdownCell/DropdownCell.tsx
index 24cdb8521f..6eb4fad3ea 100644
--- a/frontend/components/TableContainer/DataTable/DropdownCell/DropdownCell.tsx
+++ b/frontend/components/TableContainer/DataTable/DropdownCell/DropdownCell.tsx
@@ -12,12 +12,14 @@ interface IDropdownCellProps {
options: IDropdownOption[];
placeholder: string;
onChange: (value: string) => void;
+ disabled?: boolean;
}
const DropdownCell = ({
options,
placeholder,
onChange,
+ disabled,
}: IDropdownCellProps): JSX.Element => {
return (
@@ -26,6 +28,7 @@ const DropdownCell = ({
placeholder={placeholder}
searchable={false}
options={options}
+ disabled={disabled ?? false}
/>
);
diff --git a/frontend/components/TableContainer/DataTable/DropdownCell/_styles.scss b/frontend/components/TableContainer/DataTable/DropdownCell/_styles.scss
index f04bca94d7..eaf1d1c3c2 100644
--- a/frontend/components/TableContainer/DataTable/DropdownCell/_styles.scss
+++ b/frontend/components/TableContainer/DataTable/DropdownCell/_styles.scss
@@ -21,6 +21,14 @@
}
}
+ &.is-disabled {
+ .Select-control {
+ .Select-placeholder {
+ @include disabled;
+ }
+ }
+ }
+
.Select-control {
display: flex;
background-color: initial;
diff --git a/frontend/interfaces/config.ts b/frontend/interfaces/config.ts
index 3305330548..e34992fdcc 100644
--- a/frontend/interfaces/config.ts
+++ b/frontend/interfaces/config.ts
@@ -118,6 +118,7 @@ export interface IConfig {
enable_analytics: boolean;
deferred_save_host: boolean;
query_reports_disabled: boolean;
+ scripts_disabled: boolean;
};
smtp_settings: {
enable_smtp: boolean;
diff --git a/frontend/pages/ManageControlsPage/Scripts/Scripts.tsx b/frontend/pages/ManageControlsPage/Scripts/Scripts.tsx
index 0140fdc7f0..4e0d8eff2c 100644
--- a/frontend/pages/ManageControlsPage/Scripts/Scripts.tsx
+++ b/frontend/pages/ManageControlsPage/Scripts/Scripts.tsx
@@ -15,6 +15,7 @@ import CustomLink from "components/CustomLink";
import Spinner from "components/Spinner";
import DataError from "components/DataError";
import PremiumFeatureMessage from "components/PremiumFeatureMessage";
+import InfoBanner from "components/InfoBanner";
import ScriptListHeading from "./components/ScriptListHeading";
import ScriptListItem from "./components/ScriptListItem";
import ScriptListPagination from "./components/ScriptListPagination";
@@ -75,6 +76,9 @@ const Scripts = ({ router, currentPage, teamIdForApi }: IScriptsProps) => {
router.push(path.concat(`&page=${currentPage + 1}`));
}, [router, path, currentPage]);
+ const { config } = useContext(AppContext);
+ if (!config) return null;
+
// The user is not a premium tier, so show the premium feature message.
if (!isPremiumTier) {
return (
@@ -137,6 +141,15 @@ const Scripts = ({ router, currentPage, teamIdForApi }: IScriptsProps) => {
);
};
+ const renderScriptsDisabledBanner = () => (
+
+
+ Running scripts is disabled in organization settings. You can
+ still manage your library of macOS and Windows scripts below.
+
+
+ );
+
return (
@@ -148,6 +161,8 @@ const Scripts = ({ router, currentPage, teamIdForApi }: IScriptsProps) => {
newTab
/>
+
+ {config.server_settings.scripts_disabled && renderScriptsDisabledBanner()}
{renderScriptsList()}
{showDeleteScriptModal && selectedScript.current && (
diff --git a/frontend/pages/admin/OrgSettingsPage/cards/Advanced/Advanced.tsx b/frontend/pages/admin/OrgSettingsPage/cards/Advanced/Advanced.tsx
index ae20bbbbf6..98999e1102 100644
--- a/frontend/pages/admin/OrgSettingsPage/cards/Advanced/Advanced.tsx
+++ b/frontend/pages/admin/OrgSettingsPage/cards/Advanced/Advanced.tsx
@@ -28,6 +28,7 @@ const Advanced = ({
disableLiveQuery: appConfig.server_settings.live_query_disabled || false,
disableQueryReports:
appConfig.server_settings.query_reports_disabled || false,
+ disableScripts: appConfig.server_settings.scripts_disabled || false,
});
const {
@@ -37,6 +38,7 @@ const Advanced = ({
enableHostExpiry,
hostExpiryWindow,
disableLiveQuery,
+ disableScripts,
disableQueryReports,
} = formData;
@@ -68,6 +70,7 @@ const Advanced = ({
live_query_disabled: disableLiveQuery,
enable_analytics: appConfig.server_settings.enable_analytics,
query_reports_disabled: disableQueryReports,
+ scripts_disabled: disableScripts,
},
smtp_settings: {
enable_smtp: appConfig.smtp_settings.enable_smtp || false,
@@ -210,6 +213,24 @@ const Advanced = ({
>
Disable live queries
+
+ Disabling scripts will block access to run scripts. Scripts{" "}
+
may still be added and removed in the UI and API.{" "}
+
+
+ (Default: Off)
+
+
+ }
+ >
+ Disable scripts
+
{
@@ -93,7 +97,10 @@ const Scripts = ({
if (isErrorScriptData) {
return ;
}
- const scriptColumnConfigs = generateTableColumnConfigs(onActionSelection);
+ const scriptColumnConfigs = generateTableColumnConfigs(
+ onActionSelection,
+ config.server_settings.scripts_disabled
+ );
const data = generateDataSet(
currentUser,
host,
diff --git a/frontend/pages/hosts/details/cards/Scripts/ScriptsTableConfig.tsx b/frontend/pages/hosts/details/cards/Scripts/ScriptsTableConfig.tsx
index 16010fb87b..ec24c041f4 100644
--- a/frontend/pages/hosts/details/cards/Scripts/ScriptsTableConfig.tsx
+++ b/frontend/pages/hosts/details/cards/Scripts/ScriptsTableConfig.tsx
@@ -17,6 +17,7 @@ import {
isGlobalObserver,
isTeamObserver,
} from "utilities/permissions/permissions";
+import TooltipWrapper from "components/TooltipWrapper";
import ScriptStatusCell from "./components/ScriptStatusCell";
@@ -67,7 +68,8 @@ const ScriptRunActionDropdownLabel = ({
// eslint-disable-next-line import/prefer-default-export
export const generateTableColumnConfigs = (
- actionSelectHandler: (value: string, script: IHostScript) => void
+ actionSelectHandler: (value: string, script: IHostScript) => void,
+ disableActions = false
) => {
return [
{
@@ -90,15 +92,35 @@ export const generateTableColumnConfigs = (
Header: "",
disableSortBy: true,
accessor: "actions",
- Cell: (cellProps: IDropdownCellProps) => (
-
- actionSelectHandler(value, cellProps.row.original)
- }
- placeholder={"Actions"}
- />
- ),
+ Cell: (cellProps: IDropdownCellProps) =>
+ disableActions ? (
+
+ Running scripts is disabled in organization settings
+ }
+ >
+
+ actionSelectHandler(value, cellProps.row.original)
+ }
+ placeholder={"Actions"}
+ disabled={disableActions}
+ />
+
+
+ ) : (
+
+ actionSelectHandler(value, cellProps.row.original)
+ }
+ placeholder={"Actions"}
+ disabled={disableActions}
+ />
+ ),
},
];
};
diff --git a/server/datastore/mysql/schema.sql b/server/datastore/mysql/schema.sql
index 4a294028df..b2c221cd40 100644
--- a/server/datastore/mysql/schema.sql
+++ b/server/datastore/mysql/schema.sql
@@ -41,7 +41,7 @@ CREATE TABLE `app_config_json` (
UNIQUE KEY `id` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
-INSERT INTO `app_config_json` VALUES (1,'{\"mdm\": {\"macos_setup\": {\"bootstrap_package\": null, \"macos_setup_assistant\": null, \"enable_end_user_authentication\": false}, \"macos_updates\": {\"deadline\": null, \"minimum_version\": null}, \"macos_settings\": {\"custom_settings\": null}, \"macos_migration\": {\"mode\": \"\", \"enable\": false, \"webhook_url\": \"\"}, \"windows_updates\": {\"deadline_days\": null, \"grace_period_days\": null}, \"windows_settings\": {\"custom_settings\": null}, \"apple_bm_default_team\": \"\", \"apple_bm_terms_expired\": false, \"enable_disk_encryption\": false, \"enabled_and_configured\": false, \"end_user_authentication\": {\"idp_name\": \"\", \"metadata\": \"\", \"entity_id\": \"\", \"issuer_uri\": \"\", \"metadata_url\": \"\"}, \"windows_enabled_and_configured\": false, \"apple_bm_enabled_and_configured\": false}, \"scripts\": null, \"features\": {\"enable_host_users\": true, \"enable_software_inventory\": false}, \"org_info\": {\"org_name\": \"\", \"contact_url\": \"\", \"org_logo_url\": \"\", \"org_logo_url_light_background\": \"\"}, \"integrations\": {\"jira\": null, \"zendesk\": null}, \"sso_settings\": {\"idp_name\": \"\", \"metadata\": \"\", \"entity_id\": \"\", \"enable_sso\": false, \"issuer_uri\": \"\", \"metadata_url\": \"\", \"idp_image_url\": \"\", \"enable_jit_role_sync\": false, \"enable_sso_idp_login\": false, \"enable_jit_provisioning\": false}, \"agent_options\": {\"config\": {\"options\": {\"logger_plugin\": \"tls\", \"pack_delimiter\": \"/\", \"logger_tls_period\": 10, \"distributed_plugin\": \"tls\", \"disable_distributed\": false, \"logger_tls_endpoint\": \"/api/osquery/log\", \"distributed_interval\": 10, \"distributed_tls_max_attempts\": 3}, \"decorators\": {\"load\": [\"SELECT uuid AS host_uuid FROM system_info;\", \"SELECT hostname AS hostname FROM system_info;\"]}}, \"overrides\": {}}, \"fleet_desktop\": {\"transparency_url\": \"\"}, \"smtp_settings\": {\"port\": 587, \"domain\": \"\", \"server\": \"\", \"password\": \"\", \"user_name\": \"\", \"configured\": false, \"enable_smtp\": false, \"enable_ssl_tls\": true, \"sender_address\": \"\", \"enable_start_tls\": true, \"verify_ssl_certs\": true, \"authentication_type\": \"0\", \"authentication_method\": \"0\"}, \"server_settings\": {\"server_url\": \"\", \"enable_analytics\": false, \"deferred_save_host\": false, \"live_query_disabled\": false, \"query_reports_disabled\": false}, \"webhook_settings\": {\"interval\": \"0s\", \"host_status_webhook\": {\"days_count\": 0, \"destination_url\": \"\", \"host_percentage\": 0, \"enable_host_status_webhook\": false}, \"vulnerabilities_webhook\": {\"destination_url\": \"\", \"host_batch_size\": 0, \"enable_vulnerabilities_webhook\": false}, \"failing_policies_webhook\": {\"policy_ids\": null, \"destination_url\": \"\", \"host_batch_size\": 0, \"enable_failing_policies_webhook\": false}}, \"host_expiry_settings\": {\"host_expiry_window\": 0, \"host_expiry_enabled\": false}, \"vulnerability_settings\": {\"databases_path\": \"\"}}','2020-01-01 01:01:01','2020-01-01 01:01:01');
+INSERT INTO `app_config_json` VALUES (1,'{\"mdm\": {\"macos_setup\": {\"bootstrap_package\": null, \"macos_setup_assistant\": null, \"enable_end_user_authentication\": false}, \"macos_updates\": {\"deadline\": null, \"minimum_version\": null}, \"macos_settings\": {\"custom_settings\": null}, \"macos_migration\": {\"mode\": \"\", \"enable\": false, \"webhook_url\": \"\"}, \"windows_updates\": {\"deadline_days\": null, \"grace_period_days\": null}, \"windows_settings\": {\"custom_settings\": null}, \"apple_bm_default_team\": \"\", \"apple_bm_terms_expired\": false, \"enable_disk_encryption\": false, \"enabled_and_configured\": false, \"end_user_authentication\": {\"idp_name\": \"\", \"metadata\": \"\", \"entity_id\": \"\", \"issuer_uri\": \"\", \"metadata_url\": \"\"}, \"windows_enabled_and_configured\": false, \"apple_bm_enabled_and_configured\": false}, \"scripts\": null, \"features\": {\"enable_host_users\": true, \"enable_software_inventory\": false}, \"org_info\": {\"org_name\": \"\", \"contact_url\": \"\", \"org_logo_url\": \"\", \"org_logo_url_light_background\": \"\"}, \"integrations\": {\"jira\": null, \"zendesk\": null}, \"sso_settings\": {\"idp_name\": \"\", \"metadata\": \"\", \"entity_id\": \"\", \"enable_sso\": false, \"issuer_uri\": \"\", \"metadata_url\": \"\", \"idp_image_url\": \"\", \"enable_jit_role_sync\": false, \"enable_sso_idp_login\": false, \"enable_jit_provisioning\": false}, \"agent_options\": {\"config\": {\"options\": {\"logger_plugin\": \"tls\", \"pack_delimiter\": \"/\", \"logger_tls_period\": 10, \"distributed_plugin\": \"tls\", \"disable_distributed\": false, \"logger_tls_endpoint\": \"/api/osquery/log\", \"distributed_interval\": 10, \"distributed_tls_max_attempts\": 3}, \"decorators\": {\"load\": [\"SELECT uuid AS host_uuid FROM system_info;\", \"SELECT hostname AS hostname FROM system_info;\"]}}, \"overrides\": {}}, \"fleet_desktop\": {\"transparency_url\": \"\"}, \"smtp_settings\": {\"port\": 587, \"domain\": \"\", \"server\": \"\", \"password\": \"\", \"user_name\": \"\", \"configured\": false, \"enable_smtp\": false, \"enable_ssl_tls\": true, \"sender_address\": \"\", \"enable_start_tls\": true, \"verify_ssl_certs\": true, \"authentication_type\": \"0\", \"authentication_method\": \"0\"}, \"server_settings\": {\"server_url\": \"\", \"enable_analytics\": false, \"scripts_disabled\": false, \"deferred_save_host\": false, \"live_query_disabled\": false, \"query_reports_disabled\": false}, \"webhook_settings\": {\"interval\": \"0s\", \"host_status_webhook\": {\"days_count\": 0, \"destination_url\": \"\", \"host_percentage\": 0, \"enable_host_status_webhook\": false}, \"vulnerabilities_webhook\": {\"destination_url\": \"\", \"host_batch_size\": 0, \"enable_vulnerabilities_webhook\": false}, \"failing_policies_webhook\": {\"policy_ids\": null, \"destination_url\": \"\", \"host_batch_size\": 0, \"enable_failing_policies_webhook\": false}}, \"host_expiry_settings\": {\"host_expiry_window\": 0, \"host_expiry_enabled\": false}, \"vulnerability_settings\": {\"databases_path\": \"\"}}','2020-01-01 01:01:01','2020-01-01 01:01:01');
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `carve_blocks` (
diff --git a/server/fleet/app.go b/server/fleet/app.go
index a3f2cc122d..edb106876c 100644
--- a/server/fleet/app.go
+++ b/server/fleet/app.go
@@ -840,6 +840,7 @@ type ServerSettings struct {
DebugHostIDs []uint `json:"debug_host_ids,omitempty"`
DeferredSaveHost bool `json:"deferred_save_host"`
QueryReportsDisabled bool `json:"query_reports_disabled"`
+ ScriptsDisabled bool `json:"scripts_disabled"`
}
// HostExpirySettings contains settings pertaining to automatic host expiry.
diff --git a/server/service/integration_enterprise_test.go b/server/service/integration_enterprise_test.go
index 3575977411..5eab467a97 100644
--- a/server/service/integration_enterprise_test.go
+++ b/server/service/integration_enterprise_test.go
@@ -4343,8 +4343,30 @@ func (s *integrationEnterpriseTestSuite) TestRunHostScript() {
require.False(t, scriptResultResp.HostTimeout)
require.Contains(t, scriptResultResp.Message, fleet.RunScriptAlreadyRunningErrMsg)
- // verify that orbit would get the notification that it has a script to run
+ // Disable scripts and verify that there are no Orbit notifs
+ acr := appConfigResponse{}
+ s.DoJSON("PATCH", "/api/latest/fleet/config", json.RawMessage(`{
+ "server_settings": {
+ "scripts_disabled": true
+ }
+ }`), http.StatusOK, &acr)
+ require.True(t, acr.AppConfig.ServerSettings.ScriptsDisabled)
+
var orbitResp orbitGetConfigResponse
+ s.DoJSON("POST", "/api/fleet/orbit/config",
+ json.RawMessage(fmt.Sprintf(`{"orbit_node_key": %q}`, *host.OrbitNodeKey)),
+ http.StatusOK, &orbitResp)
+ require.Empty(t, orbitResp.Notifications.PendingScriptExecutionIDs)
+
+ acr = appConfigResponse{}
+ s.DoJSON("PATCH", "/api/latest/fleet/config", json.RawMessage(`{
+ "server_settings": {
+ "scripts_disabled": false
+ }
+ }`), http.StatusOK, &acr)
+ require.False(t, acr.AppConfig.ServerSettings.ScriptsDisabled)
+
+ // verify that orbit would get the notification that it has a script to run
s.DoJSON("POST", "/api/fleet/orbit/config",
json.RawMessage(fmt.Sprintf(`{"orbit_node_key": %q}`, *host.OrbitNodeKey)),
http.StatusOK, &orbitResp)
diff --git a/server/service/orbit.go b/server/service/orbit.go
index a74472de46..9dff69620e 100644
--- a/server/service/orbit.go
+++ b/server/service/orbit.go
@@ -229,16 +229,18 @@ func (svc *Service) GetOrbitConfig(ctx context.Context) (fleet.OrbitConfig, erro
}
// load the pending script executions for that host
- pending, err := svc.ds.ListPendingHostScriptExecutions(ctx, host.ID, pendingScriptMaxAge)
- if err != nil {
- return fleet.OrbitConfig{}, err
- }
- if len(pending) > 0 {
- execIDs := make([]string, 0, len(pending))
- for _, p := range pending {
- execIDs = append(execIDs, p.ExecutionID)
+ if !appConfig.ServerSettings.ScriptsDisabled {
+ pending, err := svc.ds.ListPendingHostScriptExecutions(ctx, host.ID, pendingScriptMaxAge)
+ if err != nil {
+ return fleet.OrbitConfig{}, err
+ }
+ if len(pending) > 0 {
+ execIDs := make([]string, 0, len(pending))
+ for _, p := range pending {
+ execIDs = append(execIDs, p.ExecutionID)
+ }
+ notifs.PendingScriptExecutionIDs = execIDs
}
- notifs.PendingScriptExecutionIDs = execIDs
}
// team ID is not nil, get team specific flags and options
diff --git a/tools/cloner-check/generated_files/appconfig.txt b/tools/cloner-check/generated_files/appconfig.txt
index 4ee37214f6..bc42602a84 100644
--- a/tools/cloner-check/generated_files/appconfig.txt
+++ b/tools/cloner-check/generated_files/appconfig.txt
@@ -10,6 +10,7 @@ github.com/fleetdm/fleet/v4/server/fleet/ServerSettings EnableAnalytics bool
github.com/fleetdm/fleet/v4/server/fleet/ServerSettings DebugHostIDs []uint
github.com/fleetdm/fleet/v4/server/fleet/ServerSettings DeferredSaveHost bool
github.com/fleetdm/fleet/v4/server/fleet/ServerSettings QueryReportsDisabled bool
+github.com/fleetdm/fleet/v4/server/fleet/ServerSettings ScriptsDisabled bool
github.com/fleetdm/fleet/v4/server/fleet/AppConfig SMTPSettings *fleet.SMTPSettings
github.com/fleetdm/fleet/v4/server/fleet/SMTPSettings SMTPEnabled bool
github.com/fleetdm/fleet/v4/server/fleet/SMTPSettings SMTPConfigured bool