├── lib
├── macos
│ ├── scripts
│ │ ├── uninstall-santa.sh
│ │ ├── set-timezone.script.sh
│ │ ├── remove-zoom-artifacts.script.sh
│ │ ├── install-santa.sh
│ │ ├── collect-fleetd-logs.sh
│ │ └── macos-password.mobileconfig
│ ├── software
│ │ └── santa.yml
│ ├── configuration-profiles
│ │ └── passcode-settings-ddm.json
│ ├── enrollment-profiles
│ │ └── automatic-enrollment.dep.json
│ └── policies
│ │ └── macos-device-health.policies.yml
├── windows
│ ├── software
│ │ └── slack.yml
│ ├── scripts
│ │ ├── uninstall-slack.ps1
│ │ ├── default-exe-install-script.ps1
│ │ └── windows-screenlock.xml
│ ├── configuration-profiles
│ │ └── passcode-settings-ddm.json
│ └── policies
│ │ └── windows-device-health.policies.yml
├── all
│ └── queries
│ │ ├── collect-usb-devices.queries.yml
│ │ ├── collect-fleetd-update-channels.queries.yml
│ │ └── collect-failed-login-attempts.queries.yml
├── linux
│ └── policies
│ │ └── linux-device-health.policies.yml
├── agent-options.yml
└── README.md
├── teams
├── no-team.yml
├── workstations.yml
└── workstations-canary.yml
├── default.yml
├── .gitlab-ci.yml
├── LICENSE
├── CODEOWNERS
├── .github
├── workflows
│ └── workflow.yml
└── gitops-action
│ └── action.yml
├── gitops.sh
└── README.md
/lib/macos/scripts/uninstall-santa.sh:
--------------------------------------------------------------------------------
1 | # This will be a script that uninstalls Santa from macOS hosts.
--------------------------------------------------------------------------------
/lib/macos/scripts/set-timezone.script.sh:
--------------------------------------------------------------------------------
1 | # This will be a script that sets the timezone on macOS hosts.
2 |
--------------------------------------------------------------------------------
/lib/macos/scripts/remove-zoom-artifacts.script.sh:
--------------------------------------------------------------------------------
1 | # This will be a script that removes Zoom artifacts from macOS hosts.
2 |
--------------------------------------------------------------------------------
/lib/macos/scripts/install-santa.sh:
--------------------------------------------------------------------------------
1 | # This will be a script that installs Santa onto macOS hosts.
2 | # Documentation: https://fleetdm.com/docs/configuration/yaml-files#packages
--------------------------------------------------------------------------------
/lib/macos/software/santa.yml:
--------------------------------------------------------------------------------
1 | # This will be the configuration for a custom package on macOS hosts.
2 | # Documentation: https://fleetdm.com/docs/configuration/yaml-files#packages
3 |
--------------------------------------------------------------------------------
/lib/windows/software/slack.yml:
--------------------------------------------------------------------------------
1 | # This will be the configuration for a custom package on Windows hosts.
2 | # Documentation: https://fleetdm.com/docs/configuration/yaml-files#packages
3 |
--------------------------------------------------------------------------------
/lib/windows/scripts/uninstall-slack.ps1:
--------------------------------------------------------------------------------
1 | # This will be a script that uninstalls Slack from Windows hosts.
2 | # Documentation: https://fleetdm.com/docs/configuration/yaml-files#packages
3 |
--------------------------------------------------------------------------------
/lib/windows/scripts/default-exe-install-script.ps1:
--------------------------------------------------------------------------------
1 | # This will be a default script that can install packages on Windows hosts.
2 | # Documentation: https://fleetdm.com/docs/configuration/yaml-files#packages
3 |
--------------------------------------------------------------------------------
/lib/all/queries/collect-usb-devices.queries.yml:
--------------------------------------------------------------------------------
1 | - name: Collect USB devices
2 | description: Collects the USB devices that are currently connected to macOS and Linux hosts.
3 | query: SELECT model, vendor FROM usb_devices;
4 | interval: 360 # 6 minutes
5 | observer_can_run: true
6 | automations_enabled: false
7 | platform: darwin,linux
8 |
--------------------------------------------------------------------------------
/lib/linux/policies/linux-device-health.policies.yml:
--------------------------------------------------------------------------------
1 | - name: Linux - Enable disk encryption
2 | platform: linux
3 | description: This policy checks if disk encryption is enabled.
4 | resolution: As an IT admin, deploy an image that includes disk encryption.
5 | query: SELECT 1 FROM disk_encryption WHERE encrypted=1 AND name LIKE '/dev/dm-1';
6 |
--------------------------------------------------------------------------------
/teams/no-team.yml:
--------------------------------------------------------------------------------
1 | # Teams are available in Fleet Premium.
2 |
3 | # This file updates policies, controls, and software for hosts assigned to "No team."
4 |
5 | # To update queries and agent options for hosts assigned to "No team," use the default.yml file.
6 |
7 | name: No team
8 | policies:
9 | controls: # This cannot be set here and in default.yml
10 | software:
11 |
--------------------------------------------------------------------------------
/lib/macos/configuration-profiles/passcode-settings-ddm.json:
--------------------------------------------------------------------------------
1 | {
2 | "Type": "com.apple.configuration.passcode.settings",
3 | "Identifier": "956e0d14-6019-479b-a6f9-a69ef77668c5",
4 | "Payload": {
5 | "MaximumFailedAttempts": 10,
6 | "MaximumInactivityInMinutes": 5,
7 | "MinimumLength": 12,
8 | "MinimumComplexCharacters": 1
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/lib/windows/configuration-profiles/passcode-settings-ddm.json:
--------------------------------------------------------------------------------
1 | {
2 | "Type": "com.apple.configuration.passcode.settings",
3 | "Identifier": "956e0d14-6019-479b-a6f9-a69ef77668c5",
4 | "Payload": {
5 | "MaximumFailedAttempts": 10,
6 | "MaximumInactivityInMinutes": 5,
7 | "MinimumLength": 12,
8 | "MinimumComplexCharacters": 1
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/lib/agent-options.yml:
--------------------------------------------------------------------------------
1 | command_line_flags:
2 | config:
3 | decorators:
4 | load:
5 | - SELECT uuid AS host_uuid FROM system_info;
6 | - SELECT hostname AS hostname FROM system_info;
7 | options:
8 | disable_distributed: false
9 | distributed_interval: 10
10 | distributed_plugin: tls
11 | distributed_tls_max_attempts: 3
12 | logger_tls_endpoint: /api/v1/osquery/log
13 | pack_delimiter: /
14 |
--------------------------------------------------------------------------------
/lib/macos/scripts/collect-fleetd-logs.sh:
--------------------------------------------------------------------------------
1 | cp /var/log/orbit/orbit.stderr.log ~/Library/Logs/Fleet/fleet-desktop.log /Users/Shared
2 |
3 | echo "Successfully copied fleetd logs to the /Users/Shared folder."
4 |
5 | echo "To retrieve logs, ask the end user to open Finder and in the menu bar select Go > Go to Folder."
6 |
7 | echo "Then, ask the end user to type in /Users/Shared, press Return, and locate orbit.stderr.log (Orbit logs) and fleet-desktop.log (Fleet Desktop logs) files."
--------------------------------------------------------------------------------
/lib/all/queries/collect-fleetd-update-channels.queries.yml:
--------------------------------------------------------------------------------
1 | - name: Collect fleetd update channels
2 | description: "Collects the update channels for all fleetd components: osquery, Orbit, and Fleet Desktop. To see which version number each channel is on, ask in #help-engineering."
3 | query: SELECT desktop_channel, orbit_channel, osqueryd_channel FROM orbit_info;
4 | interval: 300 # 5 minutes
5 | observer_can_run: true
6 | automations_enabled: false
7 | platform: darwin,linux,windows
8 |
--------------------------------------------------------------------------------
/lib/all/queries/collect-failed-login-attempts.queries.yml:
--------------------------------------------------------------------------------
1 | - name: Collect failed login attempts
2 | description: Lists the users at least one failed login attempt and timestamp of failed login. Number of failed login attempts reset to zero after a user successfully logs in.
3 | query: SELECT users.username, account_policy_data.failed_login_count, account_policy_data.failed_login_timestamp FROM users INNER JOIN account_policy_data using (uid) WHERE account_policy_data.failed_login_count > 0;
4 | interval: 300 # 5 minutes
5 | observer_can_run: false
6 | automations_enabled: false
7 | platform: darwin,linux,windows
8 |
--------------------------------------------------------------------------------
/lib/macos/enrollment-profiles/automatic-enrollment.dep.json:
--------------------------------------------------------------------------------
1 | {
2 | "profile_name": "Fleet's example automatic enrollment profile",
3 | "allow_pairing": true,
4 | "is_mdm_removable": true,
5 | "org_magic": "1",
6 | "language": "en",
7 | "region": "US",
8 | "skip_setup_items": [
9 | "Accessibility",
10 | "Appearance",
11 | "AppleID",
12 | "AppStore",
13 | "Biometric",
14 | "Diagnostics",
15 | "FileVault",
16 | "iCloudDiagnostics",
17 | "iCloudStorage",
18 | "Location",
19 | "Payment",
20 | "Privacy",
21 | "Restore",
22 | "ScreenTime",
23 | "Siri",
24 | "TermsOfAddress",
25 | "TOS",
26 | "UnlockWithWatch"
27 | ]
28 | }
--------------------------------------------------------------------------------
/default.yml:
--------------------------------------------------------------------------------
1 | # For Fleet Free:
2 | # - This file updates policies, queries, agent_options, and controls for all hosts.
3 |
4 | # For Fleet Premium:
5 | # - This file updates policies and queries that run on all hosts ("All teams").
6 | # - Remove "controls" and add this to your YAML files in teams/ instead.
7 |
8 | policies:
9 | queries:
10 | agent_options:
11 | path: ./lib/agent-options.yml
12 | controls: # This cannot be set here and in no-team.yml
13 | org_settings:
14 | server_settings:
15 | server_url: $FLEET_URL
16 | org_info:
17 | org_name: Fleet
18 | secrets:
19 | - secret: "$FLEET_GLOBAL_ENROLL_SECRET"
20 | features:
21 | enable_host_users: true
22 | enable_software_inventory: true
23 |
--------------------------------------------------------------------------------
/lib/README.md:
--------------------------------------------------------------------------------
1 | # `lib/`
2 |
3 | This folder is for files referenced by `path` in Fleet config YAML.
4 |
5 | This can reduce duplication for policies, scripts, and other config that is the same across multiple teams in Fleet Premium.
6 |
7 | ### Examples
8 |
9 | ##### Policies
10 |
11 | ```yaml
12 | # default.yml
13 | policies:
14 | - path: ./lib/macos/policies/macos-device-health.policies.yml
15 | ```
16 |
17 | ##### Queries
18 |
19 | ```yaml
20 | # default.yml
21 | queries:
22 | - path: ./lib/all/queries/collect-usb-devices.queries.yml
23 | ```
24 |
25 | ##### Scripts
26 |
27 | ```yaml
28 | # default.yml
29 | controls:
30 | scripts:
31 | - path: ./lib/macos/scripts/remove-zoom-artifacts.script.sh
32 | ```
33 |
34 | ##### Agent options
35 |
36 | ```yaml
37 | # default.yml
38 | agent_options:
39 | path: ./lib/agent-options.yml
40 | ```
41 |
--------------------------------------------------------------------------------
/.gitlab-ci.yml:
--------------------------------------------------------------------------------
1 | fleet-gitops:
2 | image: node:22
3 | variables:
4 | FLEET_DRY_RUN_ONLY: true
5 | rules:
6 | - if: $CI_PIPELINE_SOURCE == 'merge_request_event'
7 | - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
8 | variables:
9 | FLEET_DRY_RUN_ONLY: false
10 | - if: $CI_PIPELINE_SOURCE == 'schedule' && $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
11 | variables:
12 | FLEET_DRY_RUN_ONLY: false
13 | before_script:
14 | - apt-get -qq update
15 | - apt-get install -y 'jq=1.6-2.1*'
16 | script:
17 | - >
18 | FLEET_VERSION="$(curl "$FLEET_URL/api/v1/fleet/version" --header "Authorization: Bearer $FLEET_API_TOKEN" --fail --silent | jq --raw-output '.version')"
19 | - >
20 | if [[ -n "$FLEET_VERSION" ]] ; then
21 | npm install -g "fleetctl@$FLEET_VERSION" || npm install -g fleetctl
22 | else
23 | echo "Failed to get Fleet version from $FLEET_URL, installing latest version of fleetctl"
24 | npm install -g fleetctl
25 | fi
26 | - fleetctl config set --address $FLEET_URL --token $FLEET_API_TOKEN
27 | - ./gitops.sh
28 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2020-present Fleet Device Management Inc
2 |
3 | This software is available under the "MIT Expat" license as defined below.
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/teams/workstations.yml:
--------------------------------------------------------------------------------
1 | # Teams are available in Fleet Premium.
2 |
3 | # This file updates policies, queries, agent options, controls, and software for hosts assigned to the "Workstations" team.
4 |
5 | # To add another team, create a new file in the teams/ directory and copy and paste the contents from this file.
6 | # Update the secret in the new file, then create the corresponding secret in GitHub Actions secrets.
7 | # Then add that secret to .github/workflows/workflow.yml as an env variable.
8 | # The secret name in the YAML file must match the secret name in GitHub Actions secrets.
9 |
10 | name: Workstations
11 | policies:
12 | - path: ../lib/macos/policies/macos-device-health.policies.yml
13 | - path: ../lib/windows/policies/windows-device-health.policies.yml
14 | - path: ../lib/linux/policies/linux-device-health.policies.yml
15 | queries:
16 | - path: ../lib/all/queries/collect-usb-devices.queries.yml
17 | - path: ../lib/all/queries/collect-failed-login-attempts.queries.yml
18 | agent_options:
19 | path: ../lib/agent-options.yml
20 | controls:
21 | scripts:
22 | - path: ../lib/macos/scripts/remove-zoom-artifacts.script.sh
23 | - path: ../lib/macos/scripts/set-timezone.script.sh
24 | team_settings:
25 | secrets:
26 | - secret: "$FLEET_WORKSTATIONS_ENROLL_SECRET"
27 | features:
28 | enable_host_users: true
29 | enable_software_inventory: true
30 | software:
31 |
--------------------------------------------------------------------------------
/teams/workstations-canary.yml:
--------------------------------------------------------------------------------
1 | # Teams are available in Fleet Premium.
2 |
3 | # This file updates policies, queries, agent options, controls, and software for hosts assigned to the "Workstations (canary)" team.
4 |
5 | # To add another team, create a new file in the teams/ directory and copy and paste the contents from this file.
6 | # Update the secret in the new file, then create the corresponding secret in GitHub Actions secrets.
7 | # Then add that secret to .github/workflows/workflow.yml as an env variable.
8 | # The secret name in the YAML file must match the secret name in GitHub Actions secrets.
9 |
10 | name: Workstations (canary)
11 | policies:
12 | - path: ../lib/macos/policies/macos-device-health.policies.yml
13 | - path: ../lib/windows/policies/windows-device-health.policies.yml
14 | - path: ../lib/linux/policies/linux-device-health.policies.yml
15 | queries:
16 | - path: ../lib/all/queries/collect-usb-devices.queries.yml
17 | - path: ../lib/all/queries/collect-failed-login-attempts.queries.yml
18 | agent_options:
19 | path: ../lib/agent-options.yml
20 | controls:
21 | scripts:
22 | - path: ../lib/macos/scripts/remove-zoom-artifacts.script.sh
23 | - path: ../lib/macos/scripts/set-timezone.script.sh
24 | team_settings:
25 | secrets:
26 | - secret: "$FLEET_WORKSTATIONS_CANARY_ENROLL_SECRET"
27 | features:
28 | enable_host_users: true
29 | enable_software_inventory: true
30 | software:
31 |
32 |
--------------------------------------------------------------------------------
/lib/windows/scripts/windows-screenlock.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
4 |
5 | int
6 |
7 |
8 | ./Device/Vendor/MSFT/Policy/Config/DeviceLock/DevicePasswordEnabled
9 |
10 | 0
11 |
12 |
13 |
14 |
15 | -
16 |
17 | int
18 |
19 |
20 | ./Device/Vendor/MSFT/Policy/Config/DeviceLock/MaxInactivityTimeDeviceLock
21 |
22 | 15
23 |
24 |
25 |
26 |
27 | -
28 |
29 | int
30 |
31 |
32 | ./Device/Vendor/MSFT/Policy/Config/DeviceLock/MinDevicePasswordLength
33 |
34 | 10
35 |
36 |
37 |
38 |
39 | -
40 |
41 | int
42 |
43 |
44 | ./Device/Vendor/MSFT/Policy/Config/DeviceLock/MinDevicePasswordComplexCharacters
45 |
46 | 2
47 |
48 |
49 |
--------------------------------------------------------------------------------
/CODEOWNERS:
--------------------------------------------------------------------------------
1 | ##############################################################################################
2 | # ██████╗ ██████╗ ██████╗ ███████╗ ██████╗ ██╗ ██╗███╗ ██╗███████╗██████╗ ███████╗
3 | # ██╔════╝██╔═══██╗██╔══██╗██╔════╝██╔═══██╗██║ ██║████╗ ██║██╔════╝██╔══██╗██╔════╝
4 | # ██║ ██║ ██║██║ ██║█████╗ ██║ ██║██║ █╗ ██║██╔██╗ ██║█████╗ ██████╔╝███████╗
5 | # ██║ ██║ ██║██║ ██║██╔══╝ ██║ ██║██║███╗██║██║╚██╗██║██╔══╝ ██╔══██╗╚════██║
6 | # ╚██████╗╚██████╔╝██████╔╝███████╗╚██████╔╝╚███╔███╔╝██║ ╚████║███████╗██║ ██║███████║
7 | # ╚═════╝ ╚═════╝ ╚═════╝ ╚══════╝ ╚═════╝ ╚══╝╚══╝ ╚═╝ ╚═══╝╚══════╝╚═╝ ╚═╝╚══════╝
8 | ##############################################################################################
9 | # ⛔ This file indicates REQUIRED reviewers for changes in this repo.
10 | #
11 | # For more information on how this works, see:
12 | # - What is a DRI and how is this configured? https://fleetdm.com/handbook/company/why-this-way#why-direct-responsibility
13 | # - Historical context: https://github.com/fleetdm/fleet/pull/12786
14 | ##############################################################################################
15 |
16 | # Best practice file structure
17 | /lib @harrisonravazzolo
18 | /teams @harrisonravazzolo
19 | default.yml @harrisonravazzolo
20 |
21 | # GitHub Action and GitLab CI/CD
22 | .github @getvictor @allenhouchins
23 | .gitlab-ci.yml @getvictor
24 | gitops.sh @getvictor
25 |
26 | # Everything else
27 | * @getvictor
28 |
--------------------------------------------------------------------------------
/.github/workflows/workflow.yml:
--------------------------------------------------------------------------------
1 | name: 'Apply latest configuration to Fleet'
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 | pull_request:
8 | workflow_dispatch: # allows manual triggering
9 | schedule:
10 | - cron: '0 6 * * *' # Nightly 6AM UTC
11 |
12 | # Prevent concurrent runs of this workflow.
13 | concurrency:
14 | group: ${{ github.workflow }}
15 | cancel-in-progress: false
16 |
17 | defaults:
18 | run:
19 | shell: bash
20 |
21 | # Limit permissions of GITHUB_TOKEN.
22 | permissions:
23 | contents: read
24 |
25 | jobs:
26 | fleet-gitops:
27 | runs-on: ubuntu-latest
28 | steps:
29 | - name: Checkout GitOps repository
30 | uses: actions/checkout@v4
31 |
32 | - name: Apply latest configuration to Fleet
33 | uses: ./.github/gitops-action
34 | with:
35 | # Run GitOps in dry-run mode for pull requests.
36 | dry-run-only: ${{ github.event_name == 'pull_request' && 'true' || 'false' }}
37 | # Add FLEET_URL and FLEET_API_TOKEN to the repository secrets.
38 | # In addition, specify or add secrets for all the environment variables that are mentioned in the global/team YAML files.
39 | env:
40 | FLEET_URL: ${{ secrets.FLEET_URL }}
41 | FLEET_API_TOKEN: ${{ secrets.FLEET_API_TOKEN }}
42 | FLEET_GLOBAL_ENROLL_SECRET: ${{ secrets.FLEET_GLOBAL_ENROLL_SECRET }}
43 | FLEET_WORKSTATIONS_ENROLL_SECRET: ${{ secrets.FLEET_WORKSTATIONS_ENROLL_SECRET }}
44 | FLEET_WORKSTATIONS_CANARY_ENROLL_SECRET: ${{ secrets.FLEET_WORKSTATIONS_CANARY_ENROLL_SECRET }}
45 |
--------------------------------------------------------------------------------
/gitops.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | # -e: Immediately exit if any command has a non-zero exit status.
4 | # -x: Print all executed commands to the terminal.
5 | # -u: Exit if an undefined variable is used.
6 | # -o pipefail: Exit if any command in a pipeline fails.
7 | set -exuo pipefail
8 |
9 | FLEET_GITOPS_DIR="${FLEET_GITOPS_DIR:-.}"
10 | FLEET_GLOBAL_FILE="${FLEET_GLOBAL_FILE:-$FLEET_GITOPS_DIR/default.yml}"
11 | FLEETCTL="${FLEETCTL:-fleetctl}"
12 | FLEET_DRY_RUN_ONLY="${FLEET_DRY_RUN_ONLY:-false}"
13 | FLEET_DELETE_OTHER_TEAMS="${FLEET_DELETE_OTHER_TEAMS:-true}"
14 |
15 | # Validate that global file contains org_settings
16 | grep -Exq "^org_settings:.*" "$FLEET_GLOBAL_FILE"
17 |
18 | # Copy/pasting raw SSO metadata into GitHub secrets will result in malformed yaml.
19 | # Adds spaces to all but the first line of metadata keeps the multiline string in bounds.
20 | # See README for more information
21 |
22 | # FLEET_SSO_METADATA=$( sed '2,$s/^/ /' <<< "${FLEET_MDM_SSO_METADATA}")
23 | # FLEET_MDM_SSO_METADATA=$( sed '2,$s/^/ /' <<< "${FLEET_MDM_SSO_METADATA}")
24 |
25 | if compgen -G "$FLEET_GITOPS_DIR"/teams/*.yml > /dev/null; then
26 | # Validate that every team has a unique name.
27 | # This is a limited check that assumes all team files contain the phrase: `name: `
28 | ! perl -nle 'print $1 if /^name:\s*(.+)$/' "$FLEET_GITOPS_DIR"/teams/*.yml | sort | uniq -d | grep . -cq
29 | fi
30 |
31 | args=(-f "$FLEET_GLOBAL_FILE")
32 | for team_file in "$FLEET_GITOPS_DIR"/teams/*.yml; do
33 | if [ -f "$team_file" ]; then
34 | args+=(-f "$team_file")
35 | fi
36 | done
37 | if [ "$FLEET_DELETE_OTHER_TEAMS" = true ]; then
38 | args+=(--delete-other-teams)
39 | fi
40 |
41 | # Dry run
42 | $FLEETCTL gitops "${args[@]}" --dry-run
43 | if [ "$FLEET_DRY_RUN_ONLY" = true ]; then
44 | exit 0
45 | fi
46 |
47 | # Real run
48 | $FLEETCTL gitops "${args[@]}"
49 |
--------------------------------------------------------------------------------
/lib/macos/scripts/macos-password.mobileconfig:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PayloadContent
6 |
7 |
8 | PayloadDescription
9 | Configures Passcode settings
10 | PayloadDisplayName
11 | Passcode
12 | PayloadIdentifier
13 | com.github.erikberglund.ProfileCreator.F7CF282E-D91B-44E9-922F-A719634F9C8E.com.apple.mobiledevice.passwordpolicy.231DFC90-D5A7-41B8-9246-564056048AC5
14 | PayloadOrganization
15 |
16 | PayloadType
17 | com.apple.mobiledevice.passwordpolicy
18 | PayloadUUID
19 | 231DFC90-D5A7-41B8-9246-564056048AC5
20 | PayloadVersion
21 | 1
22 | allowSimple
23 |
24 | forcePIN
25 |
26 | maxFailedAttempts
27 | 11
28 | maxGracePeriod
29 | 1
30 | maxInactivity
31 | 15
32 | minLength
33 | 10
34 | requireAlphanumeric
35 |
36 |
37 |
38 | PayloadDescription
39 | Configures our Macs to require passwords that are 10 character long
40 | PayloadDisplayName
41 | Password policy - require 10 characters
42 | PayloadIdentifier
43 | com.github.erikberglund.ProfileCreator.F7CF282E-D91B-44E9-922F-A719634F9C8E
44 | PayloadOrganization
45 | FleetDM
46 | PayloadScope
47 | System
48 | PayloadType
49 | Configuration
50 | PayloadUUID
51 | F7CF282E-D91B-44E9-922F-A719634F9C8E
52 | PayloadVersion
53 | 1
54 |
55 |
--------------------------------------------------------------------------------
/.github/gitops-action/action.yml:
--------------------------------------------------------------------------------
1 | name: fleetctl-gitops
2 | description: Runs fleetctl gitops to apply configuration to Fleet
3 |
4 | inputs:
5 | working-directory:
6 | description: 'The working directory, which should be the root of the fleet-gitops repository.'
7 | default: './'
8 | dry-run-only:
9 | description: 'Whether to only run the fleetctl gitops commands in dry-run mode.'
10 | default: 'false'
11 | delete-other-teams:
12 | description: 'Whether to delete other teams in Fleet which are not part of the gitops config.'
13 | default: 'true'
14 |
15 | runs:
16 | using: "composite"
17 | steps:
18 | - name: Install fleetctl
19 | shell: bash
20 | working-directory: ${{ inputs.working-directory }}
21 | run: |
22 | FLEET_VERSION="$(curl "$FLEET_URL/api/v1/fleet/version" --header "Authorization: Bearer $FLEET_API_TOKEN" --fail --silent | jq --raw-output '.version')"
23 | DEFAULT_FLEETCTL_VERSION="4.77"
24 |
25 | # Decide which fleetctl version to install:
26 | # If the server returns a clean version (e.g. 4.74.0), use that.
27 | # If the server returns a snapshot (e.g. 0.0.0-SNAPSHOT-xxxxx) or is empty, pin to DEFAULT_FLEETCTL_VERSION.
28 | if [[ -z "$FLEET_VERSION" ]]; then
29 | INSTALL_VERSION="$DEFAULT_FLEETCTL_VERSION"
30 | elif [[ "$FLEET_VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
31 | INSTALL_VERSION="$FLEET_VERSION"
32 | else
33 | INSTALL_VERSION="$DEFAULT_FLEETCTL_VERSION"
34 | fi
35 |
36 | echo "Installing fleetctl v$INSTALL_VERSION..."
37 | npm install -g "fleetctl@$INSTALL_VERSION" || npm install -g fleetctl@latest
38 |
39 | - name: Configure fleetctl
40 | shell: bash
41 | working-directory: ${{ inputs.working-directory }}
42 | run: fleetctl config set --address ${{ env.FLEET_URL }} --token ${{ env.FLEET_API_TOKEN }}
43 |
44 | - name: Run fleetctl gitops commands
45 | shell: bash
46 | working-directory: ${{ inputs.working-directory }}
47 | env:
48 | FLEET_DRY_RUN_ONLY: ${{ inputs.dry-run-only }}
49 | FLEET_DELETE_OTHER_TEAMS: ${{ inputs.delete-other-teams }}
50 | run: ./gitops.sh
51 |
--------------------------------------------------------------------------------
/lib/windows/policies/windows-device-health.policies.yml:
--------------------------------------------------------------------------------
1 | - name: Windows - Enable BitLocker
2 | platform: windows
3 | description: "This policy checks if BitLocker (disk encryption) is enabled on the C: volume."
4 | resolution: As an IT admin, turn on disk encryption in Fleet.
5 | query: SELECT * FROM bitlocker_info WHERE drive_letter='C:' AND protection_status = 1;
6 | - name: Windows - Disable guest account
7 | platform: windows
8 | description: This policy checks if the guest account is disabled. The Guest account allows unauthenticated network users to gain access to the system.
9 | resolution: "As an IT admin, deploy a Windows profile with the Accounts_EnableGuestAccountStatus option documented here: https://learn.microsoft.com/en-us/windows/client-management/mdm/policy-csp-localpoliciessecurityoptions#accounts_enableguestaccountstatus"
10 | query: SELECT 1 FROM mdm_bridge where mdm_command_input = "1- ./Device/Vendor/MSFT/Policy/Result/LocalPoliciesSecurityOptions/Accounts_EnableGuestAccountStatus
" and CAST(mdm_command_output AS INT) = 0;
11 | - name: Windows - Require 10 character password
12 | platform: windows
13 | description: This policy checks if the end user is required to enter a password, with at least 10 characters, to unlock the host.
14 | resolution: "As an IT admin, deploy a Windows profile with the DevicePasswordEnabled and MinDevicePasswordLength option documented here: https://learn.microsoft.com/en-us/windows/client-management/mdm/policy-csp-devicelock"
15 | query: SELECT 1 FROM mdm_bridge where mdm_command_input = "1- ./Device/Vendor/MSFT/Policy/Result/DeviceLock/DevicePasswordEnabled
" and CAST(mdm_command_output AS INT) = 0;
16 | - name: Windows - Enable screen saver after 20 minutes
17 | platform: windows
18 | description: This policy checks if maximum amount of time (in minutes) the device is allowed to sit idle before the screen is locked. End users can select any value less than the specified maximum.
19 | resolution: "As an IT admin, to deploy a Windows profile with the MaxInactivityTimeDeviceLock option documented here: https://learn.microsoft.com/en-us/windows/client-management/mdm/policy-csp-devicelock#maxinactivitytimedevicelock"
20 | query: SELECT 1 FROM mdm_bridge where mdm_command_input = "1- ./Device/Vendor/MSFT/Policy/Result/DeviceLock/MaxInactivityTimeDeviceLock
" and CAST(mdm_command_output AS INT) <= 20;
21 |
--------------------------------------------------------------------------------
/lib/macos/policies/macos-device-health.policies.yml:
--------------------------------------------------------------------------------
1 | - name: macOS - Enable FileVault
2 | platform: darwin
3 | description: This policy checks if FileVault (disk encryption) is enabled.
4 | resolution: As an IT admin, turn on disk encryption in Fleet.
5 | query: SELECT 1 FROM filevault_status WHERE status = 'FileVault is On.';
6 | - name: macOS - Disable guest account
7 | platform: darwin
8 | description: This policy checks if the guest account is disabled.
9 | resolution: An an IT admin, deploy a macOS, login window profile with the DisableGuestAccount option set to true.
10 | query: SELECT 1 FROM managed_policies WHERE domain='com.apple.loginwindow' AND username = '' AND name='DisableGuestAccount' AND CAST(value AS INT) = 1;
11 | - name: macOS - Enable Firewall
12 | platform: darwin
13 | description: This policy checks if Firewall is enabled.
14 | resolution: An an IT admin, deploy a macOS, Firewall profile with the EnableFirewall option set to true.
15 | query: SELECT 1 FROM managed_policies WHERE domain='com.apple.security.firewall' AND username = '' AND name='EnableFirewall' AND CAST(value AS INT) = 1;
16 | - name: macOS - Require 10 character password
17 | platform: darwin
18 | description: This policy checks if the end user is required to enter a password, with at least 10 characters, to unlock the host.
19 | resolution: An an IT admin, deploy a macOS, screensaver profile with the askForPassword option set to true and minLength option set to 10.
20 | query: |
21 | SELECT 1 WHERE
22 | EXISTS (
23 | SELECT 1 FROM managed_policies WHERE
24 | domain='com.apple.screensaver' AND
25 | name='askForPassword' AND
26 | CAST(value AS INT)
27 | )
28 | AND EXISTS (
29 | SELECT 1 FROM managed_policies WHERE
30 | domain='com.apple.screensaver' AND
31 | name='minLength' AND
32 | CAST(value AS INT) <= 10
33 | );
34 | - name: macOS - Enable screen saver after 20 minutes
35 | platform: darwin
36 | description: This policy checks if maximum amount of time (in minutes) the device is allowed to sit idle before the screen is locked. End users can select any value less than the specified maximum.
37 | resolution: An an IT admin, deploy a macOS, screen saver profile with the maxInactivity option set to 20 minutes.
38 | query: |
39 | SELECT 1 WHERE
40 | EXISTS (
41 | SELECT 1 FROM managed_policies WHERE
42 | domain='com.apple.screensaver' AND
43 | name='idleTime' AND
44 | CAST(value AS INT) <= 1200 AND
45 | username = ''
46 | )
47 | AND NOT EXISTS (
48 | SELECT 1 FROM managed_policies WHERE
49 | domain='com.apple.screensaver' AND
50 | name='idleTime' AND
51 | CAST(value AS INT) > 1200
52 | );
53 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Fleet GitOps
2 |
3 | This is the starter repository for using [Fleet](https://fleetdm.com) with a GitOps workflow.
4 |
5 | [Why use GitOps?](https://fleetdm.com/guides/sysadmin-diaries-gitops-a-strategic-advantage#basic-article)
6 |
7 | ## GitHub setup
8 |
9 | 1. Clone the [GitHub repository](https://github.com/fleetdm/fleet-gitops), create your own GitHub repository, and push your clone to your new repo. Note that a workflow will run once and fail because the required variables haven't been added (step 2 and 3).
10 |
11 | 2. Add `FLEET_URL` and `FLEET_API_TOKEN` secrets to your new repository's secrets. Learn how [here](https://docs.github.com/en/actions/security-guides/using-secrets-in-github-actions#creating-secrets-for-a-repository). Set `FLEET_URL` to your Fleet instance's URL (ex. https://organization.fleet.com). [Create an API-only user](https://fleetdm.com/docs/using-fleet/fleetctl-cli#create-api-only-user) with the "GitOps" role and set `FLEET_API_TOKEN` to your user's API token. If you're using Fleet Free, set the API-only user's role to global admin.
12 |
13 | 3. Add `FLEET_GLOBAL_ENROLL_SECRET` secret to your new repository's secrets. The enroll secret must be an alphanumeric string of at least 32 and at most 255 characters.
14 | - If you have a Premium Fleet license, also add `FLEET_WORKSTATIONS_ENROLL_SECRET` and `FLEET_WORKSTATIONS_CANARY_ENROLL_SECRET`.
15 | - If you do not have a Premium Fleet license, delete the `teams` directory.
16 |
17 | 4. If you are using secrets to manage SSO metadata for Fleet SSO login or MDM SSO login, uncomment lines 22 and 23 in `gitops.sh`.
18 | - If you are using different variable names for your secrets, edit the appropriate line to reflect the correct variable name.
19 |
20 | 5. In GitHub, enable the `Apply latest configuration to Fleet` GitHub Actions workflow, and run workflow manually. Now, when anyone pushes a new commit to the default branch, the action will run and update Fleet. For pull requests, the workflow will do a dry run only.
21 |
22 | ## GitLab setup
23 |
24 | 1. Clone the [GitLab repository](https://gitlab.com/fleetdm/fleet-gitops), create your own GitLab repository, and push your clone to your new repo. Note that a pipeline will run once and fail because the required variables haven't been added (step 2 and 3).
25 |
26 | 2. Add `FLEET_URL` and `FLEET_API_TOKEN` as masked CI/CD variables. Learn how [here](https://docs.gitlab.com/ee/ci/variables/#define-a-cicd-variable-in-the-ui). Set `FLEET_URL` to your Fleet instance's URL (ex. https://organization.fleet.com). Set `FLEET_API_TOKEN` to an API token for an API-only user in Fleet. Learn how [here](https://fleetdm.com/docs/using-fleet/fleetctl-cli#create-api-only-user), then, grant it the `GitOps` role via the **Settings** > **Users** page so it can make changes.
27 |
28 | 3. Add `FLEET_GLOBAL_ENROLL_SECRET` secret as a masked CI/CD variable. The enroll secret must be an alphanumeric string of at least 32 and at most 255 characters.
29 | - If you have a Premium Fleet license, also add `FLEET_WORKSTATIONS_ENROLL_SECRET` and `FLEET_WORKSTATIONS_CANARY_ENROLL_SECRET`.
30 | - If you do not have a Premium Fleet license, delete the `teams` directory.
31 |
32 | 4. If you are using secrets to manage SSO metadata for Fleet SSO login or MDM SSO login, uncomment lines 22 and 23 in `gitops.sh`.
33 | - If you are using different variable names for your secrets, edit the appropriate line to reflect the correct variable name.
34 |
35 | 5. Now, when anyone pushes a new commit to the default branch, the pipeline will run and update Fleet. For merge requests, the pipeline will do a dry run only.
36 |
37 | 6. (Optional) To ensure your Fleet configuration stays up to date even when there are no new commits, set up a scheduled pipeline:
38 | - In your GitLab project, go to the left sidebar and navigate to **Build > Pipeline schedules**. (In some GitLab versions, this may appear as **CI/CD > Schedules**.)
39 | - Click **Create a new pipeline schedule** (or **Schedule a new pipeline**).
40 | - Fill in the form:
41 | - **Description**: e.g., `Daily GitOps sync`
42 | - **Cron timezone**: e.g., `[UTC 0] UTC`
43 | - **Interval pattern**: e.g., Custom: `0 6 * * *` (runs nightly at 6AM UTC)
44 | - **Target branch or tag**: your default branch (e.g., `main`)
45 | - Click **Create pipeline schedule**.
46 |
47 | ## Configuration options
48 |
49 | For all configuration options, go to the [YAML files reference](https://fleetdm.com/docs/using-fleet/gitops) in the Fleet docs.
50 |
51 | ## Fleet UI
52 |
53 | Once you're set up with GitOps in Fleet, you can optionally put the UI in GitOps mode. This prevents you from making changes in the UI that would be overridden by GitOps workflows.
54 |
55 | An admin can enable GitOps mode in **Settings** > **Integrations** > **Change management**.
56 |
57 | Note that this is a UI-only setting. API permissions are restricted based on user role.
58 |
59 |
--------------------------------------------------------------------------------