diff --git a/.github/ISSUE_TEMPLATE/story.md b/.github/ISSUE_TEMPLATE/story.md
index ddb71b87b3..dd3c1190c9 100644
--- a/.github/ISSUE_TEMPLATE/story.md
+++ b/.github/ISSUE_TEMPLATE/story.md
@@ -44,7 +44,7 @@ What else should contributors [keep in mind](https://fleetdm.com/handbook/compan
- [ ] YAML changes: TODO
- [ ] REST API changes: TODO
- [ ] Fleet's agent (fleetd) changes: TODO
-- [ ] Activity changes: TODO
+- [ ] Activity changes: TODO
- [ ] Permissions changes: TODO
- [ ] Changes to paid features or tiers: TODO
- [ ] Other reference documentation changes: TODO
diff --git a/.github/workflows/config/randokiller.json b/.github/workflows/config/randokiller.json
new file mode 100644
index 0000000000..e229cee2ec
--- /dev/null
+++ b/.github/workflows/config/randokiller.json
@@ -0,0 +1,8 @@
+{
+ "mysql_matrix": [
+ "mysql:8.0.36"
+ ],
+ "pkg_to_test": "server/service",
+ "tests_to_run": "^TestIntegrationsMDM\\$$",
+ "num_tries": 20
+}
\ No newline at end of file
diff --git a/.github/workflows/generate-desktop-targets.yml b/.github/workflows/generate-desktop-targets.yml
index 8f28f858fe..e44d8d904b 100644
--- a/.github/workflows/generate-desktop-targets.yml
+++ b/.github/workflows/generate-desktop-targets.yml
@@ -19,7 +19,7 @@ defaults:
shell: bash
env:
- FLEET_DESKTOP_VERSION: 1.36.0
+ FLEET_DESKTOP_VERSION: 1.37.0
permissions:
contents: read
diff --git a/.github/workflows/goreleaser-orbit.yaml b/.github/workflows/goreleaser-orbit.yaml
index 54f4721f5a..1f57957caf 100644
--- a/.github/workflows/goreleaser-orbit.yaml
+++ b/.github/workflows/goreleaser-orbit.yaml
@@ -62,11 +62,6 @@ jobs:
AC_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
CODESIGN_IDENTITY: 51049B247B25B3119FAE7E9C0CC4375A43E47237
- - name: Attest binary
- uses: actions/attest-build-provenance@619dbb2e03e0189af0c55118e7d3c5e129e99726 # v2.0
- with:
- subject-path: "dist/orbit-macos_darwin_all/orbit"
-
- name: Upload
uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # 4.3.3
with:
@@ -100,11 +95,6 @@ jobs:
- name: Run GoReleaser
run: go run github.com/goreleaser/goreleaser/v2@606c0e724fe9b980cd01090d08cbebff63cd0f72 release --verbose --clean --skip=publish -f orbit/goreleaser-linux.yml # v2.4.4
- - name: Attest binary
- uses: actions/attest-build-provenance@619dbb2e03e0189af0c55118e7d3c5e129e99726 # v2.0
- with:
- subject-path: "dist/orbit_linux_amd64_v1/orbit"
-
- name: Upload
uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # 4.3.3
with:
@@ -138,16 +128,11 @@ jobs:
- name: Run GoReleaser
run: go run github.com/goreleaser/goreleaser/v2@606c0e724fe9b980cd01090d08cbebff63cd0f72 release --verbose --clean --skip=publish -f orbit/goreleaser-linux-arm64.yml # v2.4.4
- - name: Attest binary
- uses: actions/attest-build-provenance@619dbb2e03e0189af0c55118e7d3c5e129e99726 # v2.0
- with:
- subject-path: "dist/orbit_linux_arm64/orbit"
-
- name: Upload
uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # 4.3.3
with:
name: orbit-linux-arm64
- path: dist/orbit_linux_arm64/orbit
+ path: dist/orbit_linux_arm64_v8.0/orbit
goreleaser-windows:
runs-on: windows-2022
@@ -176,11 +161,6 @@ jobs:
- name: Run GoReleaser
run: go run github.com/goreleaser/goreleaser/v2@606c0e724fe9b980cd01090d08cbebff63cd0f72 release --verbose --clean --skip=publish -f orbit/goreleaser-windows.yml # v2.4.4
- - name: Attest binary
- uses: actions/attest-build-provenance@619dbb2e03e0189af0c55118e7d3c5e129e99726 # v2.0
- with:
- subject-path: "dist/orbit_windows_amd64_v1/orbit.exe"
-
- name: Upload
uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # 4.3.3
with:
diff --git a/.github/workflows/randokiller-go.yml b/.github/workflows/randokiller-go.yml
new file mode 100644
index 0000000000..a714506cfe
--- /dev/null
+++ b/.github/workflows/randokiller-go.yml
@@ -0,0 +1,150 @@
+name: Stress Test Go Test
+
+on:
+ push:
+ branches:
+ - "**-randokiller"
+ paths:
+ - "**.go"
+ - "go.mod"
+ - "go.sum"
+ - ".github/workflows/randokiller-go.yml"
+ - "docker-compose.yml"
+ - ".github/workflows/config/randokiller.json"
+ workflow_dispatch: # manual
+
+# This allows a subsequently queued workflow run to interrupt previous runs
+concurrency:
+ group: ${{ github.workflow }}-${{ github.head_ref || github.run_id}}
+ cancel-in-progress: true
+
+defaults:
+ run:
+ # fail-fast using bash -eo pipefail. See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#exit-codes-and-error-action-preference
+ shell: bash
+
+permissions:
+ contents: read
+
+jobs:
+ parse_config:
+ runs-on: ubuntu-latest
+ outputs:
+ json: ${{steps.get_config_json.outputs.json}}
+ steps:
+ - name: Checkout Code
+ uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
+
+ - name: Parse Config
+ id: get_config_json
+ run: echo "json=$(jq -c . < .github/workflows/config/randokiller.json)" >> $GITHUB_OUTPUT
+
+ test-go:
+ needs: parse_config
+ strategy:
+ matrix:
+ os: [ubuntu-latest]
+ mysql: ${{fromJson(needs.parse_config.outputs.json).mysql_matrix}}
+ runs-on: ${{ matrix.os }}
+
+ env:
+ RACE_ENABLED: false
+ GO_TEST_TIMEOUT: 20m
+
+ steps:
+ - name: Harden Runner
+ uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0
+ with:
+ egress-policy: audit
+
+ - name: Checkout Code
+ uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
+
+ - name: Install Go
+ uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2
+ with:
+ go-version-file: "go.mod"
+
+ # Pre-starting dependencies here means they are ready to go when we need them.
+ - name: Start Infra Dependencies
+ # Use & to background this
+ run: FLEET_MYSQL_IMAGE=${{ matrix.mysql }} docker compose -f docker-compose.yml -f docker-compose-redis-cluster.yml up -d mysql_test mysql_replica_test redis redis-cluster-1 redis-cluster-2 redis-cluster-3 redis-cluster-4 redis-cluster-5 redis-cluster-6 redis-cluster-setup minio saml_idp mailhog mailpit smtp4dev_test &
+
+ - name: Add TLS certificate for SMTP Tests
+ run: |
+ sudo cp tools/smtp4dev/fleet.crt /usr/local/share/ca-certificates/
+ sudo update-ca-certificates
+
+ # It seems faster not to cache Go dependencies
+ - name: Install Go Dependencies
+ run: make deps-go
+
+ - name: Generate static files
+ run: |
+ export PATH=$PATH:~/go/bin
+ make generate-go
+
+ - name: Install ZSH
+ run: sudo apt update && sudo apt install -y zsh
+
+ - name: Wait for mysql
+ run: |
+ echo "waiting for mysql..."
+ until docker compose exec -T mysql_test sh -c "mysql -uroot -p\"\${MYSQL_ROOT_PASSWORD}\" -e \"SELECT 1=1\" fleet" &> /dev/null; do
+ echo "."
+ sleep 1
+ done
+ echo "mysql is ready"
+ echo "waiting for mysql replica..."
+ until docker compose exec -T mysql_replica_test sh -c "mysql -uroot -p\"\${MYSQL_ROOT_PASSWORD}\" -e \"SELECT 1=1\" fleet" &> /dev/null; do
+ echo "."
+ sleep 1
+ done
+ echo "mysql replica is ready"
+
+ - name: Run Go Tests
+ run: |
+ set +e
+ counter=0
+ NUM_TRIES=${{ fromJSON(needs.parse_config.outputs.json).num_tries }}
+ while [ $counter -lt ${NUM_TRIES:-20} ]; do
+ ((counter++))
+ echo
+ echo "----- TEST RUN #$counter -----"
+ echo
+
+ GO_TEST_EXTRA_FLAGS="-v -race=$RACE_ENABLED -timeout=$GO_TEST_TIMEOUT" \
+ TEST_LOCK_FILE_PATH=$(pwd)/lock \
+ TEST_CRON_NO_RECOVER=1 \
+ NETWORK_TEST_GITHUB_TOKEN=${{ secrets.FLEET_RELEASE_GITHUB_PAT }} \
+ make run-go-tests TESTS_TO_RUN=${{ fromJSON(needs.parse_config.outputs.json).tests_to_run }} PKG_TO_TEST=${{ fromJSON(needs.parse_config.outputs.json).pkg_to_test }} 2>&1 | tee /tmp/gotest.log
+
+ exit_code=$?
+
+ if [ $exit_code -ne 0 ]; then
+ echo "Test run #$counter failed with exit code $exit_code"
+ exit $exit_code
+ fi
+
+ done
+
+ - name: Create mysql identifier without colon
+ if: always()
+ run: |
+ echo "MATRIX_MYSQL_ID=$(echo ${{ matrix.mysql }} | tr -d ':')" >> $GITHUB_ENV
+
+ - name: Generate summary of errors
+ if: failure()
+ run: |
+ c1grep() { grep "$@" || test $? = 1; }
+ c1grep -oP 'FAIL: .*$' /tmp/gotest.log > /tmp/summary.txt
+ c1grep 'test timed out after' /tmp/gotest.log >> /tmp/summary.txt
+ c1grep 'fatal error:' /tmp/gotest.log >> /tmp/summary.txt
+ c1grep -A 10 'panic: runtime error: ' /tmp/gotest.log >> /tmp/summary.txt
+ c1grep ' FAIL\t' /tmp/gotest.log >> /tmp/summary.txt
+ GO_FAIL_SUMMARY=$(head -n 5 /tmp/summary.txt | sed ':a;N;$!ba;s/\n/\\n/g')
+ echo "GO_FAIL_SUMMARY=$GO_FAIL_SUMMARY"
+ if [[ -z "$GO_FAIL_SUMMARY" ]]; then
+ GO_FAIL_SUMMARY="unknown, please check the build URL"
+ fi
+ GO_FAIL_SUMMARY=$GO_FAIL_SUMMARY envsubst < .github/workflows/config/slack_payload_template.json > ./payload.json
diff --git a/.github/workflows/tfvalidate.yml b/.github/workflows/tfvalidate.yml
index 6513377a08..209218ab47 100644
--- a/.github/workflows/tfvalidate.yml
+++ b/.github/workflows/tfvalidate.yml
@@ -41,8 +41,8 @@ jobs:
- name: Install terraform
uses: hashicorp/setup-terraform@633666f66e0061ca3b725c73b2ec20cd13a8fdd1 # v2.0.3
with:
- terraform_version: 1.3.0
-
+ terraform_version: 1.9.0
+ # If we want to test more of these, consider using a matrix. With a matrix of directories, all terraform modules could be fully tested and potentially in parallel.
- name: Validate loadtesting
working-directory: ./infrastructure/loadtesting/terraform
run: |
@@ -54,3 +54,15 @@ jobs:
run: |
terraform init -backend=false
terraform validate
+
+ - name: Validate dogfood
+ working-directory: ./infrastructure/dogfood/terraform/aws-tf-module
+ run: |
+ terraform init -backend=false
+ terraform validate
+
+ - name: Validate root terraform module
+ working-directory: ./terraform
+ run: |
+ terraform init -backend=false
+ terraform validate
diff --git a/CODEOWNERS b/CODEOWNERS
index bce1e230f6..c8512f309c 100644
--- a/CODEOWNERS
+++ b/CODEOWNERS
@@ -65,9 +65,9 @@ go.mod @fleetdm/go
#
# (see website/config/custom.js for DRIs of other paths not listed here)
##############################################################################################
-/docs @rachaelshaw @noahtalerman
-/docs/REST\ API/rest-api.md @rachaelshaw @noahtalerman # « REST API reference documentation
-/docs/Contributing/API-for-contributors.md @rachaelshaw @noahtalerman # « Advanced / contributors-only API reference documentation
+/docs @rachaelshaw
+/docs/REST\ API/rest-api.md @rachaelshaw # « REST API reference documentation
+/docs/Contributing/API-for-contributors.md @rachaelshaw # « Advanced / contributors-only API reference documentation
/schema @eashaw # « Data tables (osquery/fleetd schema) documentation
/render.yaml @edwardsb
diff --git a/articles/consolidate-multiple-tools-with-fleet.md b/articles/consolidate-multiple-tools-with-fleet.md
index cbcd88ea88..cfc4da7199 100644
--- a/articles/consolidate-multiple-tools-with-fleet.md
+++ b/articles/consolidate-multiple-tools-with-fleet.md
@@ -1,31 +1,39 @@
# Leading financial company consolidates multiple tools with Fleet
+
+
+We've been using Fleet for a few years and we couldn't be happier. The fact that it's also open-source made it easy for us to try it out, customize it to our needs, and seamlessly integrate it into our existing environment.
+
+**- Head of Developer Infrastructure & Corporate Technology**
+
+
+## Challenge
+
Scaling organizations face a common challenge: managing every device efficiently across varying teams and locations. Here, we take a deeper look at the impact that Fleet has made at a leading financial technology company.
-## Fleet’s impact
+The leading financial company looked to simplify how they manage devices and reduce tool overlap without sacrificing control over their infrastructure. The use of multiple proprietary device management tools was creating operational silos, and it required specialized expertise for different legacy systems, leading to inefficiencies.
-* **Eliminate tool overlap:**
-Fleet reduced tool overlap by consolidating multiple legacy solutions - improving efficiency and reducing SaaS spending without compromising functionality.
+## Solution
-* **Next-gen change management:**
-GitOps capabilities reduce mistakes through peer reviews and keep track of changes for faster auditing.
+The leading financial company migrated to Fleet without sacrificing a single point of their 99.99% uptime, replacing multiple device management suppliers with a single multi-platform system supporting macOS, desktop Linux, and Windows. They also implemented next-change management, reducing mistakes through peer reviews, and using the user interface and [Fleet API](https://fleetdm.com/docs/rest-api/rest-api) for reporting, automation, and to enable smarter end-user self-service.
-* **Definitive data:**
-Reliable, live access to their infrastructure to verify device data for better decisions surrounding end-user access and auditing context.
+## Results
-* **Seamless customization and integration:**
-Electing to self-host Fleet, and as the company continued to scale, they did so without a single point to their impressive 99.99% uptime.
+
-"We've been using Fleet for a few years and we couldn't be happier. The fact that it's also open-source made it easy for us to try it out, customize it to our needs, and seamlessly integrate it into our existing environment." - Head of Developer Infrastructure & Corporate Technology
+Reduced tool overlap by consolidating multiple legacy solutions, improving efficiency and reducing SaaS spending without compromising functionality.
-**Challenge:** They looked to simplify how they manage devices and reduce tool overlap without sacrificing control over their infrastructure. The use of multiple proprietary device management tools was creating operational silos, and it required specialized expertise for different legacy systems, leading to inefficiencies.
+Adopted Fleet’s GitOps capabilities to help reduce mistakes through peer reviews and keep track of changes for faster auditing.
-**Solution:** The leading financial company migrated to Fleet without sacrificing a single point of their 99.99% uptime, replacing multiple device management suppliers with a single multi-platform system supporting macOS, desktop Linux, and Windows. They also implemented next-change management, reducing mistakes through peer reviews, and using the user interface and [Fleet API](https://fleetdm.com/docs/rest-api/rest-api) for reporting, automation, and to enable smarter end-user self-service.
+Use Fleet to get reliable, live access to their infrastructure to verify device data for better decisions surrounding end-user access and auditing context.
-**Impact:** They saw a reduction in wasted time by unblocking data collection for audits and overcame change inertia, allowing IT to move faster with less maintenance through convention over configuration and bare metal access to every supported platform, including Apple and desktop Linux. In this way, they were able to offer employees device choice without adding to their risk register.
+Elected to self-host Fleet, and as the company continued to scale, they did so without a single point to their impressive 99.99% uptime.
+
+
+By switching to Fleet, the leading financial company saw a reduction in wasted time by unblocking data collection for audits and overcame change inertia, allowing IT to move faster with less maintenance through convention over configuration and bare metal access to every supported platform, including Apple and desktop Linux. In this way, they were able to offer employees device choice without adding to their risk register.
-## The challenge
+## Their story
This company is a global technology company building economic infrastructure for the Internet. Businesses of every size, from new startups to public companies, use its software to accept payments and manage their businesses online and in person.
@@ -33,39 +41,30 @@ As they expanded, it faced a growing complexity in managing a vast array of devi
To address these challenges, they set out to achieve four key goals:
-- **Reduce tool overlap:** Replace multiple tools with a single solution that supports macOS, Windows, and Linux, ensuring consistency across all platforms.
-- **Adopt next-generation change management:** Leverage GitOps workflows for tasks such as deploying configuration profiles, delivering MDM commands, updating custom settings, and reporting on application installations.
-- **Streamline device health assessments:** Enable quick access to asset data to evaluate device health and make informed network access decisions efficiently.
-- **Empower end-user self-service:** Provide users with clear instructions to resolve common issues independently, reducing dependence on IT teams.
-
-
-## The solution
+- Reduce tool overlap
+- Adopt next-generation change management
+- Streamline device health assessments
+- Empower end-user self-service
The company was already using Fleet in early 2023 to manage osquery from a threat detection and compliance perspective with [scheduled queries](https://fleetdm.com/guides/queries). However, they mentioned the growing need to quickly reach out to users to educate them on enabling compliance checks.
-Not soon after in April 2023, Fleet announced open-source, [cross-platform MDM capabilities](https://www.computerworld.com/article/1622574/fleet-announces-open-source-cross-platform-mdm-solution.html) building on top of osquery which they were already familiar with. Seeing this as an opportunity to leverage Fleet and reduce the amount of tools they had to manage. Fleet's combination of cross-platform support, open-source transparency, and scalability made it worthwhile to migrate MDMs.
+Not long after, Fleet announced open-source, [cross-platform MDM capabilities](https://www.computerworld.com/article/1622574/fleet-announces-open-source-cross-platform-mdm-solution.html) building on top of osquery which they were already familiar with. Seeing this as an opportunity to leverage Fleet and reduce the amount of tools they had to manage. Fleet's combination of cross-platform support, open-source transparency, and scalability made it worthwhile to migrate MDMs.
### Eliminate tool overlap with easy deployment
-"Mad props to how easy making a deploy pkg of the agent was. I wish everyone made stuff that easy."
-— Staff Client Platform Engineer
+
+
+Mad props to how easy making a deploy pkg of the agent was. I wish everyone made stuff that easy.
+
+**— Staff Client Platform Engineer**
+
Fleet's straightforward deployment package allowed a quick install of the agent across all of its devices. By supporting macOS, Windows, and Linux, Fleet enabled them to not only continue managing osquery but also consolidate its legacy device management tools into a single self-hosted MDM without sacrificing existing control.
### Next-gen change management and open-source flexibility
-"We've been using Fleet for a few years and we couldn't be happier. The fact that it's also open-source made it easy for us to try it out, customize it to our needs, and seamlessly integrate it into our existing environment." — Head of Developer Infrastructure & Corporate Technology
-
Being [open-source](http://fleetdm.com/handbook/company/why-this-way?utm_content=eo-security#why-open-source), Fleet provided the transparency and flexibility to tailor the platform to their specific requirements. This fostered trust among engineering teams and allows them to audit, customize, and extend the platform as needed.
-**Fleet's next-gen change management capabilities:**
-
-- Enforce custom settings updates and deployment: Manage custom settings across all devices using GitOps workflows.
-- Implement change control: Reduce mistakes through peer reviews.
-- Deploy configuration profiles to macOS devices: Update system settings and controls on macOS devices efficiently.
-- Deliver MDM commands: Manage and execute MDM commands like lock, sleep, and wipe.
-- Report on installed applications and versions: Generate comprehensive reports on installed applications, aiding in software management and compliance checks.
-
### Definitive data and end-user empowerment
Fleet can pull detailed information on assets across every operating system in seconds, allowing quick assessments of device health, installed applications, and verified configurations. Because Fleet is API-first, programmable, and built for automation, they configure all of their devices to access its network but only if they passed its predetermined policies.
@@ -82,6 +81,9 @@ Fleet's cross-platform support and open-source transparency set it apart from co
To learn more about how Fleet can support your organization, visit [fleetdm.com/mdm](https://fleetdm.com/mdm).
+
+
+
diff --git a/articles/creating-windows-csps.md b/articles/creating-windows-csps.md
new file mode 100644
index 0000000000..e3fbfeaec4
--- /dev/null
+++ b/articles/creating-windows-csps.md
@@ -0,0 +1,178 @@
+## Introduction
+
+Deploying Configuration Service Providers (CSPs) for Windows devices—the Windows equivalent of Apple's configuration profiles—can feel daunting, especially if you're new to the process or accustomed to ClickOps and other UI-driven approaches. The scarcity of straightforward documentation and guides can make it feel like you're venturing into a configuration rabbit hole.
+
+
+
+This guide will help you understand the building blocks to crafting CSPs of varying complexity – from simple payloads to more complex ones that involve modification of ADMX underpinnings.
+
+> In Fleet, Windows CSPs are called "Custom OS settings." Learn more about Custom OS settings [here](https://fleetdm.com/guides/custom-os-settings).
+
+## What is ADMX and why should you care?
+
+From Microsoft:
+
+>“Mobile Device Management (MDM) policy configuration support expanded to allow access of a selected set of Group Policy administrative templates (ADMX policies) for Windows PCs via the Policy configuration service provider (CSP). This expanded access ensures that enterprises can keep their devices compliant and prevent the risk of compromising security of their devices managed through the cloud.”
+
+In an ADMX policy, an administrative template contains the metadata of a Windows Group Policy. Each setting in a Group Policy corresponds to a specific registry value. These Group Policy settings are defined in an XML file format known as an ADMX file. MDM doesn’t use the same tools as Group Policy, the traditional way to control Windows settings. Instead, it uses the Policy CSP to read the ADMX instructions.
+
+ADMX policies can be applied in two ways:
+
+1. Shipped with Windows, located at ```SystemRoot\policydefinitions``` and processed into MDM policies at OS-build time
+
+2. Ingested to a device through the Policy CSP URI, for example, ```./Vendor/MSFT/Policy/ConfigOperations/ADMXInstall``` which we will cover further on in this guide.
+
+
+Windows maps the name and category path of a Group Policy to an MDM policy by parsing the associated ADMX file, finding the specified Group Policy, and storing the metadata in the MDM Policy CSP client store. When the MDM policy contains a SyncML command AND the Policy CSP URI ```.\[device|user]\vendor\msft\policy\[config|result]\\```, this metadata is referenced and determines which registry keys are configured.
+
+## Unpacking the “How”
+
+Unfortunately, to capture handling of ADMX the admin building the policies must use a UI, such as the Group Policy Editor, to gather the necessary data. For this example, we will use the ```WindowsPowerShell``` which controls PowerShell settings and is an ADMX-backed policy. [This](https://learn.microsoft.com/en-us/windows/client-management/mdm/policy-csp-windowspowershell) is the official documentation that we will work from if you want to follow along. Notice this banner that indicates the ADMX requirement:
+
+
+
+
+In the Windows documentation, you will notice a section called ADMX Mapping:
+
+
+
+Pay attention to the line **ADMX File Name**, which will show you the name of the .admx file you need to open to help craft your CSP. All ADMX files are located at:
+```C:\Windows\PolicyDefinitions\{ADMXFileName.admx}```
+
+In this XML file are the keys, and their type listed which indicates the values the CSP can modify. In this example there are 5 parameters:
+ - ExecutionPolicy
+ - EnableModuleLogging
+ - EnableTranscripting
+ - EnableScriptBlockLogging
+ - EnableUpdateHelpDefaultSourcePath
+
+Values can take one of the following types:
+
+1. Text Element - The text element simply corresponds to a string and correspondingly to an edit box in a policy panel display by gpedit. The string is stored in the registry of type REG_SZ.
+
+2. MultiText Element - The multiText element simply corresponds to a REG_MULTISZ registry string and correspondingly to a grid to enter multiple strings in a policy panel display by gpedit.
+
+3. List Element - The list element corresponds to a hive of REG_SZ registry strings and correspondingly to a grid to enter multiple strings in a policy panel display by gpedit.msc.
+ - PRO TIP: Each pair is a REG_SZ name/value key. When applying policies through gpedit, visit the corresponding registry location to understand how list values are stored.
+
+4. No Elements - Just an Enable/Disable of the policy, represented like ``
+
+5. Enum - Think of these as options from a dropdown in gpedit. This is the data type we are working with in the example
+
+6. Decimal Element
+
+7. Boolean Element
+
+At this point in the build we know the ADMX keys for this specific policy, which values those keys accept, and now to translate that into a CSP that Fleet can interpret.
+You can also see in the group policy editor the values that are being modified by the profile.
+
+
+
+In this example, we will modify the ExecutionPolicy value, which in group policy editor translates to “Turn on Script Execution”, the XML from the .admx looks like such:
+
+```
+
+
+
+ AllSigned
+
+
+
+
+ RemoteSigned
+
+
+
+
+ Unrestricted
+
+```
+
+To write this into the CSP, pull out the `id`, “ExecutionPolicy”, and the accepted values - an enum type with one of three possibilities, and use the `value` tag to create an entry in the CSP that looks like this:
+
+``````
+
+Going through all the keys in this policies, the payload will look like such:
+
+```
+
+
+
+
+
+```
+
+>Data passed in these CSPs need to be represented in the CDATA format.
+>CDATA (Character Data) is a section that allows including text data that should not be treated as XML markup. This is particularly useful for embedding content that might otherwise be misinterpreted as XML elements or attributes, such as raw text.
+>A CDATA section starts with ``````. The text between these delimiters is treated as character data, not as XML.
+
+Our block looks like:
+
+```
+
+
+
+
+]]>
+```
+
+Note, I added the `` at the start of the block, this is required to enable the settings overall. All CSPs will at least need to have that ``. And it’s possible, depending on the setting, that this is the only key.
+
+Now that we have the data block, we can finally put it all together to generate a CSP.
+
+```
+
+
+
+ chr
+
+
+ ./Device/Vendor/MSFT/Policy/Config/WindowsPowerShell/TurnOnPowerShellScriptBlockLogging
+
+
+
+
+
+
+ ]]>
+
+
+
+```
+
+Since we’re working with an ADMX-backed setting, the `Format` section needs to be
+```chr```
+
+The LocURL is listed in the CSP documentation and the `` block goe after in the CDATA format.
+
+Pay attention to the verbs `` vs `` when creating as these need to match the system configuration we are targeting or it oftentimes will fail.
+
+## Debugging
+
+There are a couple of ways to start troubleshooting issues with MDM.
+
+In the Settings > Accounts > Access work or school > Click on Connected by Fleet and then Info > Create Report. This will generate a snapshot of the device, policies it has ingested and ADMX keys that have been modified.
+
+Windows Event Logs can also be a helpful place to look.
+
+Applications and Service Logs > Microsoft > Windows > DeviceManagement-Enterprise-Diagnostics-Provider.
+
+The `Admin` logs will show you all profiles that have been pushed to the device and their status. It helps to use the `Find` function to look for keywords in your profile to narrow your search. Here is an example of the logs that show when the CSP we created here was deployed.
+
+
+
+[This](https://blog.mindcore.dk/2022/09/intune-error-codes-and-solutions/) blog post can also help you translate error codes that are present here.
+
+
+## Conclusion
+
+Deploying CSPs for your Windows fleet may seem complex at first, but with a structured approach and the right tools, it becomes a powerful way to standardize and secure your device configurations. Fleet provides the flexibility to deploy MDM profiles across platforms from a single, centralized management platform, enabling IT teams to maintain consistency, security, and efficiency.
+
+Ready to take control of your Windows device configurations? Explore Fleet’s powerful API and GitOps workflows to start building and managing your CSPs today. Visit fleetdm.com to learn more or get in touch with our team to see how we can help streamline your device management strategy.
+
+
+
+
+
+
+
diff --git a/articles/foursquare-quickly-migrates-to-fleet.md b/articles/foursquare-quickly-migrates-to-fleet.md
new file mode 100644
index 0000000000..697cee4044
--- /dev/null
+++ b/articles/foursquare-quickly-migrates-to-fleet.md
@@ -0,0 +1,89 @@
+# Foursquare quickly migrates to Fleet for device management
+
+
+
+“After several fast-paced weeks of planning, testing, and collaboration, our corporate engineering team at Foursquare has officially completed the migration to Fleet Device Management as our new device management platform. This move represents a big step forward for us.”
+
+**— Mike Meyer, Manager, Corporate Engineering at Foursquare**
+
+
+## Challenge
+
+[Foursquare](https://foursquare.com/), a leading location technology company, faced significant challenges with their existing device management solution. The platform's limited support and slow feature rollout hindered innovation and responsiveness, while its inflexible integrations with solutions like Okta Workflows and external logging destinations constrained infrastructure flexibility. Additionally, the lack of advanced DevOps capabilities and a complex, cluttered user interface resulted in operational inefficiencies and a poor user experience for their teams.
+
+## Solution
+
+Foursquare recently migrated to Fleet to unify their macOS and Windows devices into a single platform. Fleet also enabled infrastructure-as-code workflows and flexible [integrations](https://fleetdm.com/integrations) with their automation workflows and external logging systems. Additionally, Fleet offers advanced query capabilities, a user-friendly interface, and robust support, which led to an easy and quick migration.
+
+## Results
+
+
+
+Foursquare unified its cross-platform management for macOS and Windows.
+
+Introduced new infrastructure as code workflows.
+
+Improved observability and vulnerability management through Fleet’s live query engine and open API.
+
+Smooth and seamless migration.
+
+
+The Foursquare engineering team valued Fleet's cleaner and more intuitive user interface, especially when managing multiple operating systems. They appreciate Fleet’s commitment to [transparency](https://fleetdm.com/better), enabling full visibility for both the team and their users, and dedication to providing industry-leading support. Fleet also provided ready-to-use GUI tools that streamlined the [migration](https://fleetdm.com/guides/seamless-mdm-migration) process without additional development.
+
+## Their story
+
+Foursquare is a global technology company specializing in combining the physical world with the digital through location intelligence and data science. By leveraging extensive location data, Foursquare provides businesses with actionable insights to drive engagement, improve customer experiences, and optimize operations.
+
+They were looking to:
+
+- Eliminate tool overlap.
+
+- Centralize definitive data.
+
+- Provide a better user experience.
+
+- Integrate and surface data across teams.
+
+- Enable next-gen change management and open-source flexibility.
+
+
+### Eliminate tool overlap
+
+Foursquare was able to replace multiple legacy tools with a single, [multi-platform](https://fleetdm.com/device-management) system, reducing tool overlap and operational silos.
+
+### Definitive data
+
+Fleet improves observability and [vulnerability management](https://fleetdm.com/software-management) through live query capabilities and more [user-friendly APIs](https://fleetdm.com/docs/rest-api/rest-api). These improvements in device monitoring strengthened Foursquare’s overall security posture with a unified reporting language.
+
+### Better user experience
+
+
+
+“The UI is cleaner, and much simpler to navigate, truly a breath of fresh air.”
+
+**— Mike Meyer, Manager, Corporate Engineering at Foursquare**
+
+
+An intuitive UI and self-service tools help empower end-users where they can resolve common policy issues without IT intervention, reducing support tickets and increasing efficiency. For the migration itself, Fleet provided native tools for end-users to understand and complete the migration with full transparency.
+
+### Next-gen change management and flexibility
+
+Fleet provides the flexibility for engineering teams to audit, customize, and extend the platform as needed with infrastructure-as-code workflows, making device management more agile and automated. While Fleet’s ability to support additional platforms, like Linux, positions Foursquare for future growth and diversification.
+
+### Improved support and feature access
+
+Fleet is open-source and has a three-week [release schedule](https://github.com/fleetdm/fleet/releases) that quickly rolls out new features like automated software updates, VPP app support, and [policy-based scripts](https://fleetdm.com/guides/policy-automation-run-script). Faster rollouts and best-in-class support from Fleet enable Foursquare to stay ahead of their device management needs.
+
+
+## Conclusion
+
+Foursquare’s migration to Fleet for device management highlights its commitment to leveraging advanced, flexible, and user-friendly tools to support its infrastructure. Fleet’s forward-thinking feature set, superior support, and emphasis on transparency not only resolved the limitations faced with their previous platform but also positioned Foursquare to better support their teams and build smarter, more efficient workflows.
+
+
+
+
+
+
+
+
+
diff --git a/articles/global-cloud-platform-simplifies-device-management-with-fleet.md b/articles/global-cloud-platform-simplifies-device-management-with-fleet.md
index 0c1308bbf4..79eee619d2 100644
--- a/articles/global-cloud-platform-simplifies-device-management-with-fleet.md
+++ b/articles/global-cloud-platform-simplifies-device-management-with-fleet.md
@@ -1,31 +1,39 @@
# Global edge cloud platform simplifies device management with Fleet
-_“We had a different MDM for Mac, a different MDM for Windows, and a different MDM for Linux... it’s just too much.”_
+
-## Fleet's impact:
+We had a different MDM for Mac, a different MDM for Windows, and a different MDM for Linux... it’s just too much.
-* **Eliminate tool overlap:**
-Fleet’s unified platform eliminates the need to manage multiple MDM platforms for each operating system. By providing in-depth data insights in real-time, this global cloud platform was also able to replace other tools often deployed alongside other MDM solutions to maintain full control over their infrastructure.
+**— Staff Client Platform Engineer**
+
-* **Definitive data for instant audits:**
-Fleet provides trustworthy data and portability to send their data to systems other teams can also work out of, simplifying compliance processes and making audits less burdensome.
+## Challenge
-* **Automated updates**
-Policy automation eliminated manual intervention for software updates and patching, keeping their devices more secure and freeing IT resources.
+The cloud platform was managing thousands of devices with multiple tools across multiple operating systems and global locations, resulting in fragmented processes and unreliable asset data. This complexity compromised security, compliance, and operational efficiency, making it difficult to maintain consistent device check-ins and accurate inventory.
-* **Improved support efficiency**
-Accurate, real-time data allowed support teams to resolve issues faster and assist employees more effectively.
+## Solution
-* **Zero-touch onboarding to self-service**
-Fleet provides a great out-of-the-box user experience from day one, while also empowering employees to stay productive without needing to reach out to IT.
+They implemented Fleet, a unified device management platform that centralizes operations for macOS, Windows, and Linux into a single system. It delivers real-time data insights alongside universal MDM capabilities and provides self-service tools for employees. Fleet also seamlessly integrates with existing tools and supports GitOps workflows to facilitate their rapid global growth.
-**Challenge:** The cloud platform was managing thousands of devices with multiple tools across multiple operating systems and global locations, resulting in fragmented processes and unreliable asset data. This complexity compromised security, compliance, and operational efficiency, making it difficult to maintain consistent device check-ins and accurate inventory.
+## Results
-**Solution:** They implemented Fleet, a unified device management platform that centralizes operations for macOS, Windows, and Linux into a single system. It delivers real-time data insights alongside universal MDM capabilities and provides self-service tools for employees. Fleet also seamlessly integrates with existing tools and supports GitOps workflows to facilitate their rapid global growth.
+
-**Impact:** Fleet consolidated the previous device management strategy by eliminating multiple MDM tools, enhancing operational efficiency, and ensuring robust security and compliance. Automation and next-gen change management freed IT resources for strategic tasks, and self-service capabilities improved support efficiency and employee productivity.
+Reduced tool overlap by eliminating the need to manage multiple MDM platforms for each operating system. Furthermore, Fleet’s in-depth data insights allowed them to replace other tools that were deployed alongside their previous MDM solution to maintain full control over their infrastructure.
-## The challenge
+Use Fleet to gather and share definitive data across their other teams and systems, simplifying compliance processes and making audits less of a burden.
+
+Automated updates with Fleet’s policy automations to reduce manual intervention for software updates and patching, keeping their devices more secure and freeing IT resources.
+
+Improved support efficiency with accurate, real-time data, allowing support teams to resolve issues faster and assist employees more effectively.
+
+Use Fleet’s zero-touch onboarding and self-service capabilities to provide a great out-of-the-box user experience for onboarding, while also empowering employees to stay productive without needing to reach out to IT.
+
+
+Fleet consolidated the previous device management strategy by eliminating multiple MDM tools, enhancing operational efficiency, and ensuring robust security and compliance. Automation and next-gen change management freed IT resources for strategic tasks, and self-service capabilities improved support efficiency and employee productivity.
+
+
+## Their story
This company is a global edge cloud platform that empowers developers to create fast, secure, and scalable digital experiences. By providing a programmable edge cloud, they enable businesses to deliver content quickly and securely to users around the world.
@@ -36,11 +44,6 @@ While continuing to expand, managing a diverse range of devices across multiple
- Simplify processes across multiple platforms to reduce operational complexity.
- Integrate existing tools for accurate, up-to-date inventory data.
-
-## The solution
-
-_“We had a different MDM for Mac, a different MDM for Windows, a different MDM for Linux, and a different MDM for Android build devices… it’s just too much.” — Staff Client Platform Engineer_
-
A unified solution was needed to replace multiple device management suppliers with a single platform. Fleet provided the tools and flexibility required to address these challenges through:
### Eliminating tool overlap
@@ -74,9 +77,17 @@ In addition to automated patching, Fleet empowers employees with self-service to
## Conclusion
Fleet has become an essential part of this cloud platform's infrastructure, allowing the company to manage its devices more effectively. By consolidating tools across platforms, Fleet simplified device operations and ensured compliance with stringent requirements. The platform’s automation and real-time data capabilities reduced manual efforts and provided accurate insights, while its self-service tools helped employees resolve unique issues independently. These improvements have supported the goals of maintaining security, reliability, and efficient operations as the company continues to grow.
+
By adopting Fleet, they've transformed its device management processes, achieving greater security, reliability, and scalability to support its global operations.
-_“I love Fleet.” — Staff Client Platform Engineer_
+
+
+
diff --git a/articles/large-gaming-company-enhances-server-observability-with-fleet.md b/articles/large-gaming-company-enhances-server-observability-with-fleet.md
new file mode 100644
index 0000000000..7eec305bc3
--- /dev/null
+++ b/articles/large-gaming-company-enhances-server-observability-with-fleet.md
@@ -0,0 +1,80 @@
+# Large gaming company enhances server observability with Fleet
+
+
+
+Fleet's extremely wide and diverse set of data allows us to answer questions that we didn't even know we had. On top of that, the experience is near instantaneous. Seconds to sort through billions of data points and return the exact handful that we need, with complete auditing and transparency. We're able to address reliability and compliance concerns without sacrificing a single point-of-a-percent of performance for our servers. All of this done consistently and continuously.
+
+**— Principal Infrastructure Engineer**
+
+
+## Challenge
+
+The leading gaming company was looking for better visibility into an expansive server infrastructure without impacting the performance for millions of users. Existing tools would either leave gaps in visibility or require incredible amounts of manual intervention to make sure configurations were set to specification.
+
+## Solution
+
+Fleet is designed to scale seamlessly from tens of servers to hundreds of thousands of servers with negligible performance impact. This dramatically simplifies gathering data for compliance audits and makes it possible to build more advanced security paradigms.
+
+## Results
+
+
+
+Fleet scaled out of the box, from managing tens to hundreds of thousands of servers.
+
+They were able to get real-time [observability](https://fleetdm.com/observability) across every enrolled server, even within previous blindspots
+
+They can now quickly answer complex questions, providing near-instantaneous access to precise data points with complete auditing and [transparency](https://fleetdm.com/better) across multiple teams.
+
+They reduced the need for manual interventions and were able to integrate Fleet easily with their existing tools.
+
+
+By switching to Fleet, they were able to save time utilizing Fleet's native automations, instead of writing logic manually and incorporating previous blind spots into their security program. With real-time data insights across hundreds of thousands of servers, they were able to answer questions before they had them, all without sacrificing performance or reliability.
+
+
+## Their story
+
+A leading online platform for user-generated games faced significant challenges in managing and observing its extensive server infrastructure. They were looking to:
+
+- Manage a rapidly growing fleet of servers, with deployments scaling up to 100,000 servers, each with substantial memory and processing capabilities.
+
+- Ensure server observability within edge data centers.
+
+- Avoid even more fragmented processes and reduce the overhead of managing their servers.
+
+- Facilitate easier data exports and integration with other systems, such as Splunk
+
+To address these challenges, they adopted Fleet for server observability, leveraging its query engine and open API to enhance its infrastructure management alongside:
+
+### Scalable deployment
+
+Fleet’s architecture ensures minimal performance impact even as the server count grows exponentially.
+
+### Comprehensive security and compliance
+
+The gaming company now utilizes Fleet’s customizable [compliance checks](https://fleetdm.com/queries) and vulnerability assessments to maintain high-security standards across multiple teams.
+
+### Robust API and integration
+
+[Fleet API](https://fleetdm.com/docs/rest-api/rest-api) and webhook support enables automation and integration with their existing systems, eliminating the need for additional middleware and reducing reliance on manual configurations.
+
+### Advanced data handling
+
+Fleet’s ability to handle large data sets efficiently allows them to perform complex queries and generate accurate [inventories of software](https://fleetdm.com/software-management) components, everything from different Python versions to identified vulnerable software across varying server environments.
+
+### User-friendly management
+
+Fleet facilitates the deployment and maintenance of agents without the need for ongoing manual intervention, aligning with the goal of reducing operational overhead and enhancing reliability.
+
+
+## Conclusion
+
+By adopting Fleet for server observability, they've successfully addressed scalability, security, and operational challenges within their infrastructure. Fleet’s comprehensive and automated management capabilities have enabled them to maintain high-performance standards, ensure compliance, and support their expansive and dynamic server environment. As Fleet continues to integrate with their existing systems, it remains a critical component in the company’s strategy to securely enable millions to create and exist in virtual worlds without any measurable performance hits.
+
+
+
+
+
+
+
+
+
diff --git a/articles/macos-setup-experience.md b/articles/macos-setup-experience.md
index ec1d638efd..b72370bac6 100644
--- a/articles/macos-setup-experience.md
+++ b/articles/macos-setup-experience.md
@@ -178,6 +178,8 @@ If you configure software and/or a script for setup experience, users will see a
This window shows the status of the software installations as well as the script exectution. Once all steps have completed, the window can be closed and Setup Assistant will proceed as usual.
+> The setup experience script always runs after setup experience software is installed. Currently, software that [automatically installs](https://fleetdm.com/guides/automatic-software-install-in-fleet) and scripts that [automatically run](https://fleetdm.com/guides/policy-automation-run-script) are also installed and run during Setup Assistant but won't appear in the window. Automatic software and scripts may run before or after setup the experience software/script. They aren't installed/run in any particular order.
+
### Install software
To configure software to be installed during setup experience:
diff --git a/articles/vehicle-manufacturer-transitions-to-fleet-for-endpoint-security.md b/articles/vehicle-manufacturer-transitions-to-fleet-for-endpoint-security.md
new file mode 100644
index 0000000000..94aa329bd3
--- /dev/null
+++ b/articles/vehicle-manufacturer-transitions-to-fleet-for-endpoint-security.md
@@ -0,0 +1,84 @@
+# Vehicle manufacturer transitions to Fleet for endpoint security
+
+
+
+Fleet has become the central source for a lot of things. The visibility down into the assets covered by the agent is phenomenal.
+
+**— Staff Cybersecurity Engineer**
+
+
+## Challenge
+
+A leading electric vehicle manufacturer was experiencing rapid growth that strained its existing IT and security infrastructure. Managing an expanding and unique fleet of endpoints within on-premise systems presented significant challenges. Their current solution was falling short in providing real-time data on their assets and enabling a more efficient vulnerability management program.
+
+## Solution
+
+They purchased Fleet as a replacement for their existing solution to reduce manual work and foster collaboration with their cybersecurity teams. Fleet offered visibility into their endpoints with real-time data, automated reporting, and robust vulnerability management capabilities.
+
+## Results
+
+
+
+Fleet provided real-time visibility into all of their endpoints.
+
+Automated patch compliance and vulnerability mitigation reduce their risk of security breaches and keep their systems up-to-date.
+
+Automation of routine IT tasks and streamlined reporting processes.
+
+Fleet verifies ongoing compliance with security policies, maintaining robust security configurations across all environments.
+
+
+By switching to Fleet, they gained a centralized platform that significantly improved its ability to monitor and manage critical security processes. While new automations allowed them to proactively address and prioritize uncovered vulnerabilities that actually matter. The solution also facilitated better collaboration within their cybersecurity teams and ensured compliance across all devices, both within and outside the cloud environments.
+
+
+## Their story
+
+The leading electric vehicle manufacturer was experiencing rapid growth that strained its existing IT and security infrastructure. Managing a diverse and expanding fleet of endpoints across various environments presented significant challenges. Their current solution was failing to provide effective asset management and, at one point, even brought their production line to a halt.
+
+To address these challenges, they set out to achieve four key goals:
+
+- Gain real-time visibility into their endpoints.
+
+- Institute proactive vulnerability management.
+
+- Report on automated remediation efforts.
+
+- Integrate and surface data across teams.
+
+
+### Real-time visibility
+
+
+
+Security is a data problem. We felt confident being able to know what we wanted to look for. We just needed the data and a platform to go and get it. With the hope of really pinpointing, these were the issues, these were not, and ignore the rest of the noise.
+
+**— Senior Cybersecurity Manager**
+
+
+Fleet enables comprehensive monitoring and management by communicating with online devices in real-time. It delivers chip-level data insights that surface critical information, ensuring they can maintain a secure and efficient operational environment. Additionally, Fleet’s query engine and open API enable them to create custom detections for zero-day threats, increasing their ability to identify and respond to emerging vulnerabilities swiftly.
+
+### Proactive Vulnerability Management
+
+By ensuring automated patch compliance and timely vulnerability mitigation, Fleet significantly reduces the risk of security breaches and keeps all systems consistently within pre-configured policies. This proactive approach not only compacts a typically lengthy vulnerability management process but also ensures continuous protection and risk reduction.
+
+### Automating processes and day-to-day reporting
+
+Fleet is built for automation, reducing the manual workload of routine tasks such as software installations, updates, and vulnerability mitigations from its IT and cybersecurity teams.
+Integrating with their existing tools like Torq, made it easier to generate accurate and timely reports on day-to-day security, including the ongoing status of vulnerabilities and remediation efforts.
+
+### Definitive data
+
+By adhering to standard data shapes and formats, Fleet makes sure that data is easily interpretable and usable across various teams and applications. While serving as the central hub for security data, it provides a single source of truth that enhances the precision and efficiency of its asset inventory.
+
+## Conclusion
+
+The decision to purchase Fleet was driven by the need for a more reliable, comprehensive, visible, and collaborative solution that could effectively replace their existing platform. Fleet's real-time data access, vulnerability management, and automation helped achieve these objectives. By adopting Fleet, they were able to pinpoint their security issues, prioritize what actually mattered, and proactively manage and mitigate threats while saving time.
+
+
+
+
+
+
+
+
+
diff --git a/articles/worldwide-security-and-authentication-platform-chooses-fleet-for-linux.md b/articles/worldwide-security-and-authentication-platform-chooses-fleet-for-linux.md
index 57c2cb0a74..d2b83d2788 100644
--- a/articles/worldwide-security-and-authentication-platform-chooses-fleet-for-linux.md
+++ b/articles/worldwide-security-and-authentication-platform-chooses-fleet-for-linux.md
@@ -1,6 +1,11 @@
# Worldwide security and authentication platform chooses Fleet for Linux management
-“My biggest issue with Linux is that no one considers Linux. It's never the first thing. It's always the last thing. And as the advocate and evangelist for Linux in the IT org broadly, I have to make the security guys stop and go, hey, hang on a second. We're also here. You know, we matter…” — Technical Systems Engineer
+
+
+My biggest issue with Linux is that no one considers Linux. It's never the first thing. It's always the last thing. And as the advocate and evangelist for Linux in the IT org broadly, I have to make the security guys stop and go, hey, hang on a second. We're also here. You know, we matter…
+
+**— Technical Systems Engineer**
+
## Challenge
@@ -15,13 +20,16 @@ By adopting Fleet, they were able to unify their [device management](https://fle
## Results
-- Fleet enabled Linux device management and replaced legacy tools for telemetry and system configuration. This reduced their previously typical compliance woes and lack of standard MDM features.
+
-- Employees are free to work with their preferred operating systems of choice.
+Fleet enabled Linux device management and replaced legacy tools for telemetry and system configuration. This reduced their previously typical compliance woes and lack of standard MDM features.
-- [Fleet’s API](https://fleetdm.com/docs/rest-api/rest-api) and live query capabilities ensured up-to-date inventory data to meet stringent security and access control requirements while integrating with existing systems.
+Employees are free to work with their preferred operating systems of choice.
-- Fleet’s policy automation ensures that software and configurations can remain secure without requiring manual intervention, thereby freeing up IT resources.
+[Fleet’s API](https://fleetdm.com/docs/rest-api/rest-api) and live query capabilities ensured up-to-date inventory data to meet stringent security and access control requirements while integrating with existing systems.
+
+Fleet’s policy automation ensures that software and configurations can remain secure without requiring manual intervention, thereby freeing up IT resources.
+
Implementing Fleet significantly enhanced their device management strategy by centralizing Linux operations, improving security compliance, and increasing the operational efficiency of these previously overlooked devices. Fleet’s automation and integration capabilities ensured accurate and reliable asset inventory across teams, and empowered technical users with self-service tools, maintaining high security standards across all of their devices.
@@ -69,7 +77,9 @@ Fleet’s ability to scale horizontally allowed the company to manage thousands
By providing tools typically overlooked for Linux, Fleet's open-source platform completes the circle of high-security standards, compliance, and trust with a technically proficient and privacy-conscious Linux user base. Fleet’s automation and integration capabilities meant that IT could still use the tools they wanted, without leaving Linux behind. This proved vital for the company which continues to scale globally.
-To learn more about how Fleet can support your organization, visit [fleetdm.com/mdm](fleetdm.com/mdm)
+To learn more about how Fleet can support your organization, visit [fleetdm.com/mdm](fleetdm.com/mdm).
+
+
diff --git a/changes/21855-paginate-queries b/changes/21855-paginate-queries
new file mode 100644
index 0000000000..a54dfb43c8
--- /dev/null
+++ b/changes/21855-paginate-queries
@@ -0,0 +1,5 @@
+- Fleshed out server response from `queries` endpoint to include `count` and `meta` pagination information.
+- Updated UI queries page to filter, sort, paginate, etc. via query params in call to server.
+- Updated platform filtering on queries page to refer to targeted platforms instead of compatible
+ platforms
+- Updated queries API to support above targeted platform filtering
diff --git a/changes/22523-cve-500 b/changes/22523-cve-500
new file mode 100644
index 0000000000..d2170348a3
--- /dev/null
+++ b/changes/22523-cve-500
@@ -0,0 +1 @@
+* Fixed a panic (and resulting failure to load CVE details) on new installs when OS versions have not been populated yet.
diff --git a/changes/23238-use-secrets-in-scripts-profiles b/changes/23238-use-secrets-in-scripts-profiles
index 1eb445de15..f42a1c6a88 100644
--- a/changes/23238-use-secrets-in-scripts-profiles
+++ b/changes/23238-use-secrets-in-scripts-profiles
@@ -1,2 +1,3 @@
-Added ability to use secrets ($FLEET_SECRET_NAME) in scripts and profiles.
+Added ability to use secrets ($FLEET_SECRET_YOURNAME) in scripts and profiles.
- Added `/fleet/spec/secret_variables` API endpoint.
+- fleetctl gitops identifies secrets in scripts and profiles and saves them on the Fleet server.
diff --git a/changes/23490-null-script-software-on-policies b/changes/23490-null-script-software-on-policies
new file mode 100644
index 0000000000..cf2db860dc
--- /dev/null
+++ b/changes/23490-null-script-software-on-policies
@@ -0,0 +1 @@
+* Allowed team policy endpoint (`PATCH /api/latest/fleet/teams/{team_id}/policies/{policy_id}`) to receive explicit `null` as a value for `script_id` or `software_title_id` to unset a script or software installer respectively.
\ No newline at end of file
diff --git a/changes/24334-policy-truncation b/changes/24334-policy-truncation
new file mode 100644
index 0000000000..a8137dcb3d
--- /dev/null
+++ b/changes/24334-policy-truncation
@@ -0,0 +1 @@
+- Fix policy truncation UI bug
diff --git a/changes/24509-fma-no-team b/changes/24509-fma-no-team
new file mode 100644
index 0000000000..64fa83bc92
--- /dev/null
+++ b/changes/24509-fma-no-team
@@ -0,0 +1 @@
+* Allowed calling `/api/v1/fleet/software/fleet_maintained_apps` with no team ID to retrieve the full global list of maintained apps
diff --git a/changes/24636-UI-redirect-for-invalid-url-param b/changes/24636-UI-redirect-for-invalid-url-param
new file mode 100644
index 0000000000..6bd50ab9ab
--- /dev/null
+++ b/changes/24636-UI-redirect-for-invalid-url-param
@@ -0,0 +1 @@
+* Redirect when user provides an invalid URL param for fleet-maintained software id
diff --git a/changes/24725-no-private-ip-found b/changes/24725-no-private-ip-found
new file mode 100644
index 0000000000..83b72288a5
--- /dev/null
+++ b/changes/24725-no-private-ip-found
@@ -0,0 +1 @@
+Removed server error if no private IP was found by detail_query_network_interface.
diff --git a/cmd/fleetctl/get_test.go b/cmd/fleetctl/get_test.go
index 41b89ea6ba..bba4009174 100644
--- a/cmd/fleetctl/get_test.go
+++ b/cmd/fleetctl/get_test.go
@@ -1250,7 +1250,7 @@ func TestGetQueries(t *testing.T) {
}
return nil, ¬FoundError{}
}
- ds.ListQueriesFunc = func(ctx context.Context, opt fleet.ListQueryOptions) ([]*fleet.Query, error) {
+ ds.ListQueriesFunc = func(ctx context.Context, opt fleet.ListQueryOptions) ([]*fleet.Query, int, *fleet.PaginationMetadata, error) {
if opt.TeamID == nil { //nolint:gocritic // ignore ifElseChain
return []*fleet.Query{
{
@@ -1282,7 +1282,7 @@ func TestGetQueries(t *testing.T) {
Saved: true, // ListQueries always returns the saved ones.
ObserverCanRun: true,
},
- }, nil
+ }, 3, nil, nil
} else if *opt.TeamID == 1 {
return []*fleet.Query{
{
@@ -1299,11 +1299,11 @@ func TestGetQueries(t *testing.T) {
TeamID: ptr.Uint(1),
ObserverCanRun: true,
},
- }, nil
+ }, 1, nil, nil
} else if *opt.TeamID == 2 {
- return []*fleet.Query{}, nil
+ return []*fleet.Query{}, 0, nil, nil
}
- return nil, errors.New("invalid team ID")
+ return nil, 0, nil, errors.New("invalid team ID")
}
expectedGlobal := `+--------+-------------+-----------+-----------+--------------------------------+
@@ -1563,7 +1563,7 @@ func TestGetQueriesAsObserver(t *testing.T) {
}
}
- ds.ListQueriesFunc = func(ctx context.Context, opt fleet.ListQueryOptions) ([]*fleet.Query, error) {
+ ds.ListQueriesFunc = func(ctx context.Context, opt fleet.ListQueryOptions) ([]*fleet.Query, int, *fleet.PaginationMetadata, error) {
return []*fleet.Query{
{
ID: 42,
@@ -1586,7 +1586,7 @@ func TestGetQueriesAsObserver(t *testing.T) {
Query: "select 3;",
ObserverCanRun: false,
},
- }, nil
+ }, 3, nil, nil
}
for _, tc := range []struct {
@@ -1794,7 +1794,7 @@ spec:
GlobalRole: nil,
Teams: []fleet.UserTeam{{Role: fleet.RoleObserver}},
})
- ds.ListQueriesFunc = func(ctx context.Context, opt fleet.ListQueryOptions) ([]*fleet.Query, error) {
+ ds.ListQueriesFunc = func(ctx context.Context, opt fleet.ListQueryOptions) ([]*fleet.Query, int, *fleet.PaginationMetadata, error) {
return []*fleet.Query{
{
ID: 42,
@@ -1810,12 +1810,12 @@ spec:
Query: "select 2;",
ObserverCanRun: false,
},
- }, nil
+ }, 2, nil, nil
}
assert.Equal(t, "", runAppForTest(t, []string{"get", "queries"}))
// No filtering is performed if all are observer_can_run.
- ds.ListQueriesFunc = func(ctx context.Context, opt fleet.ListQueryOptions) ([]*fleet.Query, error) {
+ ds.ListQueriesFunc = func(ctx context.Context, opt fleet.ListQueryOptions) ([]*fleet.Query, int, *fleet.PaginationMetadata, error) {
return []*fleet.Query{
{
ID: 42,
@@ -1831,7 +1831,7 @@ spec:
Query: "select 2;",
ObserverCanRun: true,
},
- }, nil
+ }, 2, nil, nil
}
expected = `+--------+-------------+-----------+-----------+----------------------------+
| NAME | DESCRIPTION | QUERY | TEAM | SCHEDULE |
diff --git a/cmd/fleetctl/gitops.go b/cmd/fleetctl/gitops.go
index 5389065913..8a440551c1 100644
--- a/cmd/fleetctl/gitops.go
+++ b/cmd/fleetctl/gitops.go
@@ -121,6 +121,8 @@ func gitopsCommand() *cli.Command {
// We keep track of the secrets to check if duplicates exist during dry run
secrets := make(map[string]struct{})
+ // We keep track of the environment FLEET_SECRET_* variables
+ allFleetSecrets := make(map[string]string)
for _, flFilename := range flFilenames.Value() {
baseDir := filepath.Dir(flFilename)
config, err := spec.GitOpsFromFile(flFilename, baseDir, appConfig, logf)
@@ -221,6 +223,10 @@ func gitopsCommand() *cli.Command {
}
}
+ err = fleetClient.SaveEnvSecrets(allFleetSecrets, config.FleetSecrets, flDryRun)
+ if err != nil {
+ return err
+ }
assumptions, err := fleetClient.DoGitOps(c.Context, config, flFilename, logf, flDryRun, teamDryRunAssumptions, appConfig, teamsSoftwareInstallers, teamsScripts)
if err != nil {
return err
diff --git a/cmd/fleetctl/gitops_integration_test.go b/cmd/fleetctl/gitops_integration_test.go
index 78d88c7c5f..610f1401ea 100644
--- a/cmd/fleetctl/gitops_integration_test.go
+++ b/cmd/fleetctl/gitops_integration_test.go
@@ -15,6 +15,7 @@ import (
"github.com/fleetdm/fleet/v4/server/service"
"github.com/fleetdm/fleet/v4/server/test"
"github.com/go-git/go-git/v5"
+ "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
)
@@ -38,6 +39,7 @@ func (s *integrationGitopsTestSuite) SetupSuite() {
require.NoError(s.T(), err)
appConf.MDM.EnabledAndConfigured = true
appConf.MDM.AppleBMEnabledAndConfigured = true
+ appConf.MDM.WindowsEnabledAndConfigured = true
err = s.ds.SaveAppConfig(context.Background(), appConf)
require.NoError(s.T(), err)
@@ -100,6 +102,36 @@ func (s *integrationGitopsTestSuite) TestFleetGitops() {
t := s.T()
const fleetGitopsRepo = "https://github.com/fleetdm/fleet-gitops"
+ fleetctlConfig := s.createFleetctlConfig()
+
+ // Clone git repo
+ repoDir := t.TempDir()
+ _, err := git.PlainClone(
+ repoDir, false, &git.CloneOptions{
+ ReferenceName: "main",
+ SingleBranch: true,
+ Depth: 1,
+ URL: fleetGitopsRepo,
+ Progress: os.Stdout,
+ },
+ )
+ require.NoError(t, err)
+
+ // Set the required environment variables
+ t.Setenv("FLEET_URL", s.server.URL)
+ t.Setenv("FLEET_GLOBAL_ENROLL_SECRET", "global_enroll_secret")
+ globalFile := path.Join(repoDir, "default.yml")
+
+ // Dry run
+ _ = runAppForTest(t, []string{"gitops", "--config", fleetctlConfig.Name(), "-f", globalFile, "--dry-run"})
+
+ // Real run
+ _ = runAppForTest(t, []string{"gitops", "--config", fleetctlConfig.Name(), "-f", globalFile})
+
+}
+
+func (s *integrationGitopsTestSuite) createFleetctlConfig() *os.File {
+ t := s.T()
// Create a temporary fleetctl config file
fleetctlConfig, err := os.CreateTemp(t.TempDir(), "*.yml")
require.NoError(t, err)
@@ -116,30 +148,75 @@ contexts:
)
_, err = fleetctlConfig.WriteString(configStr)
require.NoError(t, err)
+ return fleetctlConfig
+}
- // Clone git repo
- repoDir := t.TempDir()
- _, err = git.PlainClone(
- repoDir, false, &git.CloneOptions{
- ReferenceName: "main",
- SingleBranch: true,
- Depth: 1,
- URL: fleetGitopsRepo,
- Progress: os.Stdout,
- },
+func (s *integrationGitopsTestSuite) TestFleetGitopsWithFleetSecrets() {
+ t := s.T()
+ const (
+ secretName1 = "NAME"
+ secretName2 = "length"
)
- require.NoError(t, err)
+ ctx := context.Background()
+ fleetctlConfig := s.createFleetctlConfig()
// Set the required environment variables
t.Setenv("FLEET_URL", s.server.URL)
t.Setenv("FLEET_GLOBAL_ENROLL_SECRET", "global_enroll_secret")
- globalFile := path.Join(repoDir, "default.yml")
- require.NoError(t, err)
+ t.Setenv("FLEET_SECRET_"+secretName1, "secret_value")
+ t.Setenv("FLEET_SECRET_"+secretName2, "2")
+ globalFile := path.Join("testdata", "gitops", "global_integration.yml")
// Dry run
_ = runAppForTest(t, []string{"gitops", "--config", fleetctlConfig.Name(), "-f", globalFile, "--dry-run"})
+ secrets, err := s.ds.GetSecretVariables(ctx, []string{secretName1})
+ require.NoError(t, err)
+ require.Empty(t, secrets)
// Real run
_ = runAppForTest(t, []string{"gitops", "--config", fleetctlConfig.Name(), "-f", globalFile})
+ // Check secrets
+ secrets, err = s.ds.GetSecretVariables(ctx, []string{secretName1, secretName2})
+ require.NoError(t, err)
+ require.Len(t, secrets, 2)
+ for _, secret := range secrets {
+ switch secret.Name {
+ case secretName1:
+ assert.Equal(t, "secret_value", secret.Value)
+ case secretName2:
+ assert.Equal(t, "2", secret.Value)
+ default:
+ t.Fatalf("unexpected secret %s", secret.Name)
+ }
+ }
+
+ // Check script(s)
+ scriptID, err := s.ds.GetScriptIDByName(ctx, "fleet-secret.sh", nil)
+ require.NoError(t, err)
+ expected, err := os.ReadFile("testdata/gitops/lib/fleet-secret.sh")
+ require.NoError(t, err)
+ script, err := s.ds.GetScriptContents(ctx, scriptID)
+ require.NoError(t, err)
+ assert.Equal(t, expected, script)
+
+ // Check Apple profiles
+ profiles, err := s.ds.ListMDMAppleConfigProfiles(ctx, nil)
+ require.NoError(t, err)
+ require.Len(t, profiles, 1)
+ assert.Contains(t, string(profiles[0].Mobileconfig), "$FLEET_SECRET_"+secretName1)
+ // Check Windows profiles
+ allProfiles, _, err := s.ds.ListMDMConfigProfiles(ctx, nil, fleet.ListOptions{})
+ require.NoError(t, err)
+ require.Len(t, allProfiles, 2)
+ var windowsProfileUUID string
+ for _, profile := range allProfiles {
+ if profile.Platform == "windows" {
+ windowsProfileUUID = profile.ProfileUUID
+ }
+ }
+ require.NotEmpty(t, windowsProfileUUID)
+ winProfile, err := s.ds.GetMDMWindowsConfigProfile(ctx, windowsProfileUUID)
+ require.NoError(t, err)
+ assert.Contains(t, string(winProfile.SyncML), "${FLEET_SECRET_"+secretName2+"}")
}
diff --git a/cmd/fleetctl/gitops_test.go b/cmd/fleetctl/gitops_test.go
index ad11066b2a..cfb97a6de5 100644
--- a/cmd/fleetctl/gitops_test.go
+++ b/cmd/fleetctl/gitops_test.go
@@ -69,7 +69,9 @@ func TestGitOpsBasicGlobalFree(t *testing.T) {
return nil
}
ds.ListGlobalPoliciesFunc = func(ctx context.Context, opts fleet.ListOptions) ([]*fleet.Policy, error) { return nil, nil }
- ds.ListQueriesFunc = func(ctx context.Context, opts fleet.ListQueryOptions) ([]*fleet.Query, error) { return nil, nil }
+ ds.ListQueriesFunc = func(ctx context.Context, opts fleet.ListQueryOptions) ([]*fleet.Query, int, *fleet.PaginationMetadata, error) {
+ return nil, 0, nil, nil
+ }
// Mock appConfig
savedAppConfig := &fleet.AppConfig{}
@@ -224,7 +226,9 @@ func TestGitOpsBasicGlobalPremium(t *testing.T) {
return nil
}
ds.ListGlobalPoliciesFunc = func(ctx context.Context, opts fleet.ListOptions) ([]*fleet.Policy, error) { return nil, nil }
- ds.ListQueriesFunc = func(ctx context.Context, opts fleet.ListQueryOptions) ([]*fleet.Query, error) { return nil, nil }
+ ds.ListQueriesFunc = func(ctx context.Context, opts fleet.ListQueryOptions) ([]*fleet.Query, int, *fleet.PaginationMetadata, error) {
+ return nil, 0, nil, nil
+ }
// Mock appConfig
savedAppConfig := &fleet.AppConfig{}
@@ -364,7 +368,9 @@ func TestGitOpsBasicTeam(t *testing.T) {
) (teamPolicies []*fleet.Policy, inheritedPolicies []*fleet.Policy, err error) {
return nil, nil, nil
}
- ds.ListQueriesFunc = func(ctx context.Context, opts fleet.ListQueryOptions) ([]*fleet.Query, error) { return nil, nil }
+ ds.ListQueriesFunc = func(ctx context.Context, opts fleet.ListQueryOptions) ([]*fleet.Query, int, *fleet.PaginationMetadata, error) {
+ return nil, 0, nil, nil
+ }
team := &fleet.Team{
ID: 1,
CreatedAt: time.Now(),
@@ -600,8 +606,8 @@ func TestGitOpsFullGlobal(t *testing.T) {
query.ID = 1
query.Name = "Query to delete"
queryDeleted := false
- ds.ListQueriesFunc = func(ctx context.Context, opts fleet.ListQueryOptions) ([]*fleet.Query, error) {
- return []*fleet.Query{&query}, nil
+ ds.ListQueriesFunc = func(ctx context.Context, opts fleet.ListQueryOptions) ([]*fleet.Query, int, *fleet.PaginationMetadata, error) {
+ return []*fleet.Query{&query}, 1, nil, nil
}
ds.DeleteQueriesFunc = func(ctx context.Context, ids []uint) (uint, error) {
queryDeleted = true
@@ -861,8 +867,8 @@ func TestGitOpsFullTeam(t *testing.T) {
query.TeamID = ptr.Uint(teamID)
query.Name = "Query to delete"
queryDeleted := false
- ds.ListQueriesFunc = func(ctx context.Context, opts fleet.ListQueryOptions) ([]*fleet.Query, error) {
- return []*fleet.Query{&query}, nil
+ ds.ListQueriesFunc = func(ctx context.Context, opts fleet.ListQueryOptions) ([]*fleet.Query, int, *fleet.PaginationMetadata, error) {
+ return []*fleet.Query{&query}, 1, nil, nil
}
ds.DeleteQueriesFunc = func(ctx context.Context, ids []uint) (uint, error) {
queryDeleted = true
@@ -1154,7 +1160,9 @@ func TestGitOpsBasicGlobalAndTeam(t *testing.T) {
}
return nil, nil
}
- ds.ListQueriesFunc = func(ctx context.Context, opts fleet.ListQueryOptions) ([]*fleet.Query, error) { return nil, nil }
+ ds.ListQueriesFunc = func(ctx context.Context, opts fleet.ListQueryOptions) ([]*fleet.Query, int, *fleet.PaginationMetadata, error) {
+ return nil, 0, nil, nil
+ }
ds.NewActivityFunc = func(
ctx context.Context, user *fleet.User, activity fleet.ActivityDetails, details []byte, createdAt time.Time,
) error {
@@ -1485,7 +1493,9 @@ func TestGitOpsBasicGlobalAndNoTeam(t *testing.T) {
ds.ListTeamsFunc = func(ctx context.Context, filter fleet.TeamFilter, opt fleet.ListOptions) ([]*fleet.Team, error) {
return nil, nil
}
- ds.ListQueriesFunc = func(ctx context.Context, opts fleet.ListQueryOptions) ([]*fleet.Query, error) { return nil, nil }
+ ds.ListQueriesFunc = func(ctx context.Context, opts fleet.ListQueryOptions) ([]*fleet.Query, int, *fleet.PaginationMetadata, error) {
+ return nil, 0, nil, nil
+ }
ds.NewActivityFunc = func(
ctx context.Context, user *fleet.User, activity fleet.ActivityDetails, details []byte, createdAt time.Time,
) error {
@@ -2441,7 +2451,9 @@ func setupFullGitOpsPremiumServer(t *testing.T) (*mock.Store, **fleet.AppConfig,
}
return nil, nil
}
- ds.ListQueriesFunc = func(ctx context.Context, opts fleet.ListQueryOptions) ([]*fleet.Query, error) { return nil, nil }
+ ds.ListQueriesFunc = func(ctx context.Context, opts fleet.ListQueryOptions) ([]*fleet.Query, int, *fleet.PaginationMetadata, error) {
+ return nil, 0, nil, nil
+ }
ds.NewActivityFunc = func(
ctx context.Context, user *fleet.User, activity fleet.ActivityDetails, details []byte, createdAt time.Time,
) error {
diff --git a/cmd/fleetctl/query_test.go b/cmd/fleetctl/query_test.go
index 8e5441b58a..6553b41aaf 100644
--- a/cmd/fleetctl/query_test.go
+++ b/cmd/fleetctl/query_test.go
@@ -60,11 +60,11 @@ func TestSavedLiveQuery(t *testing.T) {
ds.AppConfigFunc = func(ctx context.Context) (*fleet.AppConfig, error) {
return &fleet.AppConfig{}, nil
}
- ds.ListQueriesFunc = func(ctx context.Context, opt fleet.ListQueryOptions) ([]*fleet.Query, error) {
+ ds.ListQueriesFunc = func(ctx context.Context, opt fleet.ListQueryOptions) ([]*fleet.Query, int, *fleet.PaginationMetadata, error) {
if opt.MatchQuery == queryName {
- return []*fleet.Query{&query}, nil
+ return []*fleet.Query{&query}, 1, nil, nil
}
- return []*fleet.Query{}, nil
+ return []*fleet.Query{}, 0, nil, nil
}
ds.NewDistributedQueryCampaignFunc = func(ctx context.Context, camp *fleet.DistributedQueryCampaign) (*fleet.DistributedQueryCampaign, error) {
camp.ID = 321
diff --git a/cmd/fleetctl/session_test.go b/cmd/fleetctl/session_test.go
index 62796bcc25..8d06769c0f 100644
--- a/cmd/fleetctl/session_test.go
+++ b/cmd/fleetctl/session_test.go
@@ -14,8 +14,8 @@ import (
func TestEarlySessionCheck(t *testing.T) {
_, ds := runServerWithMockedDS(t)
- ds.ListQueriesFunc = func(ctx context.Context, opt fleet.ListQueryOptions) ([]*fleet.Query, error) {
- return nil, nil
+ ds.ListQueriesFunc = func(ctx context.Context, opt fleet.ListQueryOptions) ([]*fleet.Query, int, *fleet.PaginationMetadata, error) {
+ return nil, 0, nil, nil
}
ds.SessionByKeyFunc = func(ctx context.Context, key string) (*fleet.Session, error) {
return nil, errors.New("invalid session")
diff --git a/cmd/fleetctl/testdata/gitops/global_integration.yml b/cmd/fleetctl/testdata/gitops/global_integration.yml
new file mode 100644
index 0000000000..85bf631675
--- /dev/null
+++ b/cmd/fleetctl/testdata/gitops/global_integration.yml
@@ -0,0 +1,21 @@
+policies:
+queries:
+agent_options:
+ path: ./lib/agent-options.yml
+controls:
+ macos_settings:
+ custom_settings:
+ - path: ./lib/macos-password-secret.mobileconfig
+ windows_enabled_and_configured: true
+ windows_settings:
+ custom_settings:
+ - path: ./lib/windows-screenlock-secret.xml
+ scripts:
+ - path: ./lib/fleet-secret.sh
+org_settings:
+ server_settings:
+ server_url: $FLEET_URL
+ org_info:
+ org_name: Fleet
+ secrets:
+ - secret: "$FLEET_GLOBAL_ENROLL_SECRET"
diff --git a/cmd/fleetctl/testdata/gitops/lib/agent-options.yml b/cmd/fleetctl/testdata/gitops/lib/agent-options.yml
new file mode 100644
index 0000000000..af5a94aa16
--- /dev/null
+++ b/cmd/fleetctl/testdata/gitops/lib/agent-options.yml
@@ -0,0 +1,13 @@
+command_line_flags:
+config:
+ decorators:
+ load:
+ - SELECT uuid AS host_uuid FROM system_info;
+ - SELECT hostname AS hostname FROM system_info;
+ options:
+ disable_distributed: false
+ distributed_interval: 10
+ distributed_plugin: tls
+ distributed_tls_max_attempts: 3
+ logger_tls_endpoint: /api/v1/osquery/log
+ pack_delimiter: /
diff --git a/cmd/fleetctl/testdata/gitops/lib/fleet-secret.sh b/cmd/fleetctl/testdata/gitops/lib/fleet-secret.sh
new file mode 100644
index 0000000000..7ff0d68075
--- /dev/null
+++ b/cmd/fleetctl/testdata/gitops/lib/fleet-secret.sh
@@ -0,0 +1 @@
+echo "$FLEET_SECRET_NAME"
diff --git a/cmd/fleetctl/testdata/gitops/lib/macos-password-secret.mobileconfig b/cmd/fleetctl/testdata/gitops/lib/macos-password-secret.mobileconfig
new file mode 100644
index 0000000000..03b34b6728
--- /dev/null
+++ b/cmd/fleetctl/testdata/gitops/lib/macos-password-secret.mobileconfig
@@ -0,0 +1,55 @@
+
+
+
+
+ PayloadContent
+
+
+ PayloadDescription
+ Configures Passcode settings
+ PayloadDisplayName
+ $FLEET_SECRET_NAME
+ PayloadIdentifier
+ com.github.erikberglund.ProfileCreator.F7CF282E-D91B-44E9-922F-A719634F9C8E.com.apple.mobiledevice.passwordpolicy.231DFC90-D5A7-41B8-9246-564056048AC5
+ PayloadOrganization
+
+ PayloadType
+ com.apple.mobiledevice.passwordpolicy
+ PayloadUUID
+ 231DFC90-D5A7-41B8-9246-564056048AC5
+ PayloadVersion
+ 1
+ allowSimple
+
+ forcePIN
+
+ maxFailedAttempts
+ 11
+ maxGracePeriod
+ 1
+ maxInactivity
+ 15
+ minLength
+ 10
+ requireAlphanumeric
+
+
+
+ PayloadDescription
+ Configures our Macs to require passwords that are 10 character long
+ PayloadDisplayName
+ Password policy - require 10 characters
+ PayloadIdentifier
+ com.github.erikberglund.ProfileCreator.F7CF282E-D91B-44E9-922F-A719634F9C8E
+ PayloadOrganization
+ FleetDM
+ PayloadScope
+ System
+ PayloadType
+ Configuration
+ PayloadUUID
+ F7CF282E-D91B-44E9-922F-A719634F9C8E
+ PayloadVersion
+ 1
+
+
diff --git a/cmd/fleetctl/testdata/gitops/lib/windows-screenlock-secret.xml b/cmd/fleetctl/testdata/gitops/lib/windows-screenlock-secret.xml
new file mode 100644
index 0000000000..aa6c628d81
--- /dev/null
+++ b/cmd/fleetctl/testdata/gitops/lib/windows-screenlock-secret.xml
@@ -0,0 +1,48 @@
+
+
+
+
+ int
+
+
+ ./Device/Vendor/MSFT/Policy/Config/DeviceLock/DevicePasswordEnabled
+
+ 0
+
+
+
+
+
+
+ int
+
+
+ ./Device/Vendor/MSFT/Policy/Config/DeviceLock/MaxInactivityTimeDeviceLock
+
+ 15
+
+
+
+
+
+
+ int
+
+
+ ./Device/Vendor/MSFT/Policy/Config/DeviceLock/MinDevicePasswordLength
+
+ ${FLEET_SECRET_length}
+
+
+
+
+
+
+ int
+
+
+ ./Device/Vendor/MSFT/Policy/Config/DeviceLock/MinDevicePasswordComplexCharacters
+
+ 2
+
+
diff --git a/cmd/fleetctl/upgrade_packs_test.go b/cmd/fleetctl/upgrade_packs_test.go
index c30ae6e2c3..79c96c9891 100644
--- a/cmd/fleetctl/upgrade_packs_test.go
+++ b/cmd/fleetctl/upgrade_packs_test.go
@@ -247,8 +247,8 @@ func TestFleetctlUpgradePacks_EmptyPacks(t *testing.T) {
return fleet.TargetMetrics{}, nil
}
- ds.ListQueriesFunc = func(ctx context.Context, opt fleet.ListQueryOptions) ([]*fleet.Query, error) {
- return nil, nil
+ ds.ListQueriesFunc = func(ctx context.Context, opt fleet.ListQueryOptions) ([]*fleet.Query, int, *fleet.PaginationMetadata, error) {
+ return nil, 0, nil, nil
}
tempDir := t.TempDir()
@@ -314,12 +314,12 @@ func TestFleetctlUpgradePacks_NonEmpty(t *testing.T) {
return fleet.TargetMetrics{}, nil
}
- ds.ListQueriesFunc = func(ctx context.Context, opt fleet.ListQueryOptions) ([]*fleet.Query, error) {
+ ds.ListQueriesFunc = func(ctx context.Context, opt fleet.ListQueryOptions) ([]*fleet.Query, int, *fleet.PaginationMetadata, error) {
return []*fleet.Query{
{Name: "q1", Query: "select 1"},
{Name: "q2", Query: "select 2"},
{Name: "q3", Query: "select 3"},
- }, nil
+ }, 3, nil, nil
}
const expected = `
diff --git a/docs/Contributing/API-for-contributors.md b/docs/Contributing/API-for-contributors.md
index cb8a4e8bf7..c26b57428e 100644
--- a/docs/Contributing/API-for-contributors.md
+++ b/docs/Contributing/API-for-contributors.md
@@ -2410,7 +2410,7 @@ One of `query` and `query_id` must be specified.
#### Example with one host targeted by hostname
-`POST /api/v1/fleet/queries/run_by_names`
+`POST /api/v1/fleet/queries/run_by_identifiers`
##### Request body
@@ -2449,7 +2449,7 @@ One of `query` and `query_id` must be specified.
#### Example with multiple hosts targeted by label name
-`POST /api/v1/fleet/queries/run_by_names`
+`POST /api/v1/fleet/queries/run_by_identifiers`
##### Request body
@@ -2486,6 +2486,39 @@ One of `query` and `query_id` must be specified.
}
```
+#### Example with invalid label
+
+`POST /api/v1/fleet/queries/run_by_identifiers`
+
+##### Request body
+
+```json
+{
+ "query": "SELECT instance_id FROM system_info",
+ "selected": {
+ "labels": ["Windows", "Banana", "Apple"]
+ }
+}
+```
+
+##### Default response
+
+`Status: 400`
+
+```json
+{
+ "message": "Bad request",
+ "errors": [
+ {
+ "name": "base",
+ "reason": "Invalid label name(s): Banana, Apple."
+ }
+ ],
+ "uuid": "303649f4-5e45-4379-bae9-64ec0ef56287"
+}
+```
+
+
### Retrieve live query results (standard WebSocket API)
You can retrieve the results of a live query using the [standard WebSocket API](#https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API/Writing_WebSocket_client_applications).
diff --git a/docs/Contributing/FAQ.md b/docs/Contributing/FAQ.md
index 5d07a73857..3ff06d83fc 100644
--- a/docs/Contributing/FAQ.md
+++ b/docs/Contributing/FAQ.md
@@ -97,4 +97,11 @@ If you also have Fleetd running on hosts, it will need access to these API endpo
* `/api/fleet/orbit/device_mapping`
* `/api/osquery/log`
+Hosts running Fleet Desktop will need access to these API endpoints:
+
+* `/api/latest/fleet/device/.+/desktop`
+* `/api/latest/fleet/device/.+/ping`
+
+> Full list [here](https://github.com/fleetdm/fleet/blob/c080a3b0e1eed2184b4b7bb77a6abd8c2c39b9f4/server/service/handler.go#L791-L839)
+
diff --git a/docs/REST API/rest-api.md b/docs/REST API/rest-api.md
index 4e16ae674e..9ee4cb67e3 100644
--- a/docs/REST API/rest-api.md
+++ b/docs/REST API/rest-api.md
@@ -461,8 +461,7 @@ This is the callback endpoint that the identity provider will use to send securi
### List activities
-Returns a list of the activities that have been performed in Fleet as well as additional metadata.
-for pagination. For a comprehensive list of activity types and detailed information, please see the [audit logs](https://fleetdm.com/docs/using-fleet/audit-activities) page.
+Returns a list of the activities that have been performed in Fleet. For a comprehensive list of activity types and detailed information, please see the [audit logs](https://fleetdm.com/docs/using-fleet/audit-activities) page.
`GET /api/v1/fleet/activities`
@@ -8732,7 +8731,7 @@ Gets the result of a script that was executed.
`GET /api/v1/fleet/scripts/results/:execution_id`
-##### Default Response
+##### Default response
`Status: 200`
@@ -9582,12 +9581,13 @@ _Available in Fleet Premium._
Update a package to install on macOS, Windows, or Linux (Ubuntu) hosts.
-`PATCH /api/v1/fleet/software/titles/:title_id/package`
+`PATCH /api/v1/fleet/software/titles/:id/package`
#### Parameters
| Name | Type | In | Description |
| ---- | ------- | ---- | -------------------------------------------- |
+| id | integer | path | ID of the software title being updated. |
| software | file | form | Installer package file. Supported packages are .pkg, .msi, .exe, .deb, and .rpm. |
| team_id | integer | form | **Required**. The team ID. Updates a software package in the specified team. |
| install_script | string | form | Command that Fleet runs to install software. If not specified Fleet runs the [default install command](https://github.com/fleetdm/fleet/tree/f71a1f183cc6736205510580c8366153ea083a8d/pkg/file/scripts) for each package type. |
@@ -9877,13 +9877,13 @@ Add Fleet-maintained app so it's available for install.
_Available in Fleet Premium._
-`GET /api/v1/fleet/software/titles/:software_title_id/package?alt=media`
+`GET /api/v1/fleet/software/titles/:id/package?alt=media`
#### Parameters
| Name | Type | In | Description |
| ---- | ------- | ---- | -------------------------------------------- |
-| software_title_id | integer | path | **Required**. The ID of the software title to download software package.|
+| id | integer | path | **Required**. The ID of the software title to download software package.|
| team_id | integer | query | **Required**. The team ID. Downloads a software package added to the specified team. |
| alt | integer | query | **Required**. If specified and set to "media", downloads the specified software package. |
diff --git a/ee/bulk-operations-dashboard/README.md b/ee/bulk-operations-dashboard/README.md
index 678396b623..037ce338b9 100644
--- a/ee/bulk-operations-dashboard/README.md
+++ b/ee/bulk-operations-dashboard/README.md
@@ -16,9 +16,17 @@ A dashboard to easily manage profiles and scripts across multiple teams on a Fle
This app has two required custom configuration values:
- `sails.config.custom.fleetBaseUrl`: The full URL of your Fleet instance. (e.g., https://fleet.example.com)
-
- `sails.config.custom.fleetApiToken`: An API token for an API-only user on your Fleet instance.
+### Required configuration for software features
+
+If you are using this app to manage software across multiple teams on a Fleet instance, five additional configuration values are required:
+
+- `sails.config.uploads.bucket` The name of an AWS s3 bucket where unassigned software installers will be stored.
+- `sails.config.uploads.secret` The secret for the S3 bucket where unassigned software installers will be stored.
+- `sails.config.uploads.region` The region the AWS S3 bucket is located.
+- `sails.config.uploads.bucketWithPostfix`: The name of the s3 bucket with the directory that the software installers are stored in on appended to it. If the files will be stored in the root directory of the bucket, this value should be identical to the `sails.config.uploads.bucket` value
+- `sails.config.uploads.prefixForFileDeletion`: The directory path in the S3 bucket where the software installers will be stored. If the installers will be stored in the root directory, then this value can be set to ' '.
## Running the bulk operations dashboard with Docker.
diff --git a/ee/bulk-operations-dashboard/config/uploads.js b/ee/bulk-operations-dashboard/config/uploads.js
new file mode 100644
index 0000000000..3c7efd8c1a
--- /dev/null
+++ b/ee/bulk-operations-dashboard/config/uploads.js
@@ -0,0 +1,35 @@
+/**
+ * File Upload Settings
+ * (sails.config.uploads)
+ *
+ * These options tell Sails where (and how) to store uploaded files.
+ *
+ * > This file is mainly useful for configuring how file uploads in your
+ * > work during development; for example, when lifting on your laptop.
+ * > For recommended production settings, see `config/env/production.js`
+ *
+ * For all available options, see:
+ * https://sailsjs.com/config/uploads
+ */
+
+module.exports.uploads = {
+
+ /***************************************************************************
+ * *
+ * Sails apps upload and download to the local disk filesystem by default, *
+ * using a built-in filesystem adapter called `skipper-disk`. This feature *
+ * is mainly intended for convenience during development since, in *
+ * production, many apps will opt to use a different approach for storing *
+ * uploaded files, such as Amazon S3, Azure, or GridFS. *
+ * *
+ * Most of the time, the following options should not be changed. *
+ * (Instead, you might want to have a look at `config/env/production.js`.) *
+ * *
+ ***************************************************************************/
+ // bucket: '',// The name of the S3 bucket where software installers will be stored.
+ // region: '', // The region where the S3 bucket is located.
+ // secret: '', // The secret for the S3 bucket where unassigned software installers will be stored.
+ // bucketWithPostfix: '', // This value should be set to the same value as the bucket unless the files are stored in a folder in the S3 bucket. In that case, this value needs to be set to `{bucket name}{folder name}` e.g., unassigned-software-installers/staging
+ // prefixForFileDeletion: '', // Only required if the software installers are stored in a folder in the S3 bucket. The name of the folder where the software installers are stored in the S3 bucket with a trailing slash. e.g., staging/
+
+};
diff --git a/ee/bulk-operations-dashboard/docker-compose.yaml b/ee/bulk-operations-dashboard/docker-compose.yaml
index 8e808b3d33..21eaf0be2e 100644
--- a/ee/bulk-operations-dashboard/docker-compose.yaml
+++ b/ee/bulk-operations-dashboard/docker-compose.yaml
@@ -14,6 +14,11 @@ services:
sails_models__migrate: safe
sails_custom__fleetBaseUrl: '' #Add the base url of your Fleet instance: ex: https://fleet.example.com
sails_custom__fleetApiToken: '' # Add the API token of an API-only user [?] Here's how you get one: https://fleetdm.com/docs/using-fleet/fleetctl-cli#get-the-api-token-of-an-api-only-user
+ sails_uploads__secret: '' # (Required to enable software features) The secret for the S3 bucket where unassigned software installers will be stored.
+ sails_uploads__bucket: '' # (Required to enable software features) The name of the S3 bucket where software installers will be stored.
+ sails_uploads__region: '' # (Required to enable software features) The region where the S3 bucket is located.
+ sails_uploads__bucketWithPostfix: '' # (Required to enable software features) This value should be set to the same value as the bucket unless the files are stored in a folder in the S3 bucket. In that case, this value needs to be set to `{bucket name}{folder name}` e.g., unassigned-software-installers/staging
+ sails_uploads__prefixForFileDeletion: '' # (Required to enable software features) Only required if the software installers are stored in a folder in the S3 bucket. The name of the folder where the software installers are stored in the S3 bucket with a trailing slash. e.g., staging/
postgres:
image: "postgres:alpine"
diff --git a/ee/cis/macos-15/README.md b/ee/cis/macos-15/README.md
index 9d37abd51d..6796f047ab 100644
--- a/ee/cis/macos-15/README.md
+++ b/ee/cis/macos-15/README.md
@@ -20,7 +20,9 @@ Missing items:
1. 2.6.3.1 Ensure Share Mac Analytics Is Disabled
2. 2.6.3.3 Ensure Improve Assistive Voice Features Is Disabled
3. 2.6.3.4 Ensure 'Share with app developers' Is Disabled
-4. 5.11 Ensure Logging Is Enabled for Sudo
+4. 2.6.3.5 Ensure Share iCloud Analytics Is Disabled
+5. 2.7.2 Audit iPhone Mirroring
+6. 5.11 Ensure Logging Is Enabled for Sudo
### Checks that require decision
diff --git a/ee/server/service/maintained_apps.go b/ee/server/service/maintained_apps.go
index a574de2813..0171378515 100644
--- a/ee/server/service/maintained_apps.go
+++ b/ee/server/service/maintained_apps.go
@@ -152,11 +152,17 @@ func (svc *Service) AddFleetMaintainedApp(
return titleID, nil
}
-func (svc *Service) ListFleetMaintainedApps(ctx context.Context, teamID uint, opts fleet.ListOptions) ([]fleet.MaintainedApp, *fleet.PaginationMetadata, error) {
- if err := svc.authz.Authorize(ctx, &fleet.SoftwareInstaller{
- TeamID: &teamID,
- }, fleet.ActionRead); err != nil {
- return nil, nil, err
+func (svc *Service) ListFleetMaintainedApps(ctx context.Context, teamID *uint, opts fleet.ListOptions) ([]fleet.MaintainedApp, *fleet.PaginationMetadata, error) {
+ var authErr error
+ // viewing the maintained app list without showing team-specific info can be done by anyone who can view individual FMAs
+ if teamID == nil {
+ authErr = svc.authz.Authorize(ctx, &fleet.MaintainedApp{}, fleet.ActionRead)
+ } else { // viewing the maintained app list when showing team-specific info requires access to that team
+ authErr = svc.authz.Authorize(ctx, &fleet.SoftwareInstaller{TeamID: teamID}, fleet.ActionRead)
+ }
+
+ if authErr != nil {
+ return nil, nil, authErr
}
avail, meta, err := svc.ds.ListAvailableFleetMaintainedApps(ctx, teamID, opts)
diff --git a/ee/server/service/maintained_apps_test.go b/ee/server/service/maintained_apps_test.go
index c357db8539..332bf555ff 100644
--- a/ee/server/service/maintained_apps_test.go
+++ b/ee/server/service/maintained_apps_test.go
@@ -12,6 +12,102 @@ import (
"github.com/stretchr/testify/require"
)
+func TestListMaintainedAppsAuth(t *testing.T) {
+ t.Parallel()
+ ds := new(mock.Store)
+ ds.AppConfigFunc = func(ctx context.Context) (*fleet.AppConfig, error) {
+ return &fleet.AppConfig{}, nil
+ }
+ ds.ListAvailableFleetMaintainedAppsFunc = func(ctx context.Context, teamID *uint, opt fleet.ListOptions) ([]fleet.MaintainedApp, *fleet.PaginationMetadata, error) {
+ return []fleet.MaintainedApp{}, &fleet.PaginationMetadata{}, nil
+ }
+ authorizer, err := authz.NewAuthorizer()
+ require.NoError(t, err)
+ svc := &Service{authz: authorizer, ds: ds}
+
+ testCases := []struct {
+ name string
+ user *fleet.User
+ shouldFailWithNoTeam bool
+ shouldFailWithMatchingTeam bool
+ shouldFailWithDifferentTeam bool
+ }{
+ {
+ "global admin",
+ &fleet.User{GlobalRole: ptr.String(fleet.RoleAdmin)},
+ false,
+ false,
+ false,
+ },
+ {
+ "global maintainer",
+ &fleet.User{GlobalRole: ptr.String(fleet.RoleMaintainer)},
+ false,
+ false,
+ false,
+ },
+ {
+ "global observer",
+ &fleet.User{GlobalRole: ptr.String(fleet.RoleObserver)},
+ true,
+ true,
+ true,
+ },
+ {
+ "team admin",
+ &fleet.User{Teams: []fleet.UserTeam{{Team: fleet.Team{ID: 1}, Role: fleet.RoleAdmin}}},
+ false,
+ false,
+ true,
+ },
+ {
+ "team maintainer",
+ &fleet.User{Teams: []fleet.UserTeam{{Team: fleet.Team{ID: 1}, Role: fleet.RoleMaintainer}}},
+ false,
+ false,
+ true,
+ },
+ {
+ "team observer",
+ &fleet.User{Teams: []fleet.UserTeam{{Team: fleet.Team{ID: 1}, Role: fleet.RoleObserver}}},
+ true,
+ true,
+ true,
+ },
+ }
+
+ var forbiddenError *authz.Forbidden
+ for _, tt := range testCases {
+ t.Run(tt.name, func(t *testing.T) {
+ ctx := viewer.NewContext(context.Background(), viewer.Viewer{User: tt.user})
+
+ _, _, err := svc.ListFleetMaintainedApps(ctx, nil, fleet.ListOptions{})
+ if tt.shouldFailWithNoTeam {
+ require.Error(t, err)
+ require.ErrorAs(t, err, &forbiddenError)
+ } else {
+ require.NoError(t, err)
+ }
+
+ _, _, err = svc.ListFleetMaintainedApps(ctx, ptr.Uint(1), fleet.ListOptions{})
+ if tt.shouldFailWithMatchingTeam {
+ require.Error(t, err)
+ require.ErrorAs(t, err, &forbiddenError)
+ } else {
+ require.NoError(t, err)
+ }
+
+ _, _, err = svc.ListFleetMaintainedApps(ctx, ptr.Uint(2), fleet.ListOptions{})
+ if tt.shouldFailWithDifferentTeam {
+ require.Error(t, err)
+ require.ErrorAs(t, err, &forbiddenError)
+ } else {
+ require.NoError(t, err)
+ }
+ })
+ }
+}
+
func TestGetMaintainedAppAuth(t *testing.T) {
t.Parallel()
ds := new(mock.Store)
diff --git a/ee/server/service/software.go b/ee/server/service/software.go
index 9ba3aafbb4..c11d802f07 100644
--- a/ee/server/service/software.go
+++ b/ee/server/service/software.go
@@ -10,7 +10,7 @@ func (svc *Service) ListSoftware(ctx context.Context, opts fleet.SoftwareListOpt
// reuse ListSoftware, but include cve scores in premium version
// unless without_vulnerability_details is set to true
// including these details causes a lot of memory bloat
- if !opts.WithoutVulnerabilityDetails {
+ if (opts.MaximumCVSS > 0 || opts.MinimumCVSS > 0 || opts.KnownExploit) || !opts.WithoutVulnerabilityDetails {
opts.IncludeCVEScores = true
}
return svc.Service.ListSoftware(ctx, opts)
diff --git a/frontend/components/PlatformCompatibility/PlatformCompatibility.tsx b/frontend/components/PlatformCompatibility/PlatformCompatibility.tsx
index 700726d37e..59d93e71c8 100644
--- a/frontend/components/PlatformCompatibility/PlatformCompatibility.tsx
+++ b/frontend/components/PlatformCompatibility/PlatformCompatibility.tsx
@@ -88,8 +88,13 @@ const PlatformCompatibility = ({
tipContent={
<>
Estimated compatibility based on the
- tables used in the query. Querying
- iPhones & iPads is not supported.
+ tables used in the query.
+
+
+ Only live queries are supported on ChromeOS.
+
+
+ Querying iPhones & iPads is not supported.
>
}
>
diff --git a/frontend/components/buttons/Button/_styles.scss b/frontend/components/buttons/Button/_styles.scss
index 10e02cbce2..db6a279e43 100644
--- a/frontend/components/buttons/Button/_styles.scss
+++ b/frontend/components/buttons/Button/_styles.scss
@@ -70,7 +70,6 @@ $base-class: "button";
border: 0;
position: relative;
cursor: pointer;
- min-width: max-content;
&:focus {
outline: none;
diff --git a/frontend/components/forms/fields/DropdownWrapper/DropdownWrapper.tsx b/frontend/components/forms/fields/DropdownWrapper/DropdownWrapper.tsx
index 51c5c3cd69..789d17679c 100644
--- a/frontend/components/forms/fields/DropdownWrapper/DropdownWrapper.tsx
+++ b/frontend/components/forms/fields/DropdownWrapper/DropdownWrapper.tsx
@@ -54,7 +54,8 @@ export interface IDropdownWrapper {
isSearchable?: boolean;
isDisabled?: boolean;
placeholder?: string;
- menuPortalTarget?: HTMLElement | null;
+ /** E.g. scroll to view dropdown menu in a scrollable parent container */
+ onMenuOpen?: () => void;
}
const baseClass = "dropdown-wrapper";
@@ -72,7 +73,7 @@ const DropdownWrapper = ({
isSearchable,
isDisabled = false,
placeholder,
- menuPortalTarget,
+ onMenuOpen,
}: IDropdownWrapper) => {
const wrapperClassNames = classnames(baseClass, className);
@@ -326,12 +327,10 @@ const DropdownWrapper = ({
value={getCurrentValue()}
onChange={handleChange}
isDisabled={isDisabled}
- menuPortalTarget={
- menuPortalTarget === undefined ? document.body : menuPortalTarget
- }
noOptionsMessage={() => "No results found"}
tabIndex={isDisabled ? -1 : 0} // Ensures disabled dropdown has no keyboard accessibility
placeholder={placeholder}
+ onMenuOpen={onMenuOpen}
/>
);
diff --git a/frontend/hooks/usePlatformCompatibility.tsx b/frontend/hooks/usePlatformCompatibility.tsx
index ebb4ec1ee4..88a8d1b2fa 100644
--- a/frontend/hooks/usePlatformCompatibility.tsx
+++ b/frontend/hooks/usePlatformCompatibility.tsx
@@ -1,7 +1,7 @@
import React, { useCallback, useState } from "react";
import { useDebouncedCallback } from "use-debounce";
-import { QueryablePlatform, SUPPORTED_PLATFORMS } from "interfaces/platform";
+import { QueryablePlatform, QUERYABLE_PLATFORMS } from "interfaces/platform";
import { checkPlatformCompatibility } from "utilities/sql_tools";
import PlatformCompatibility from "components/PlatformCompatibility";
@@ -37,7 +37,7 @@ const usePlatformCompatibility = (): IPlatformCompatibility => {
);
const getCompatiblePlatforms = useCallback(
- () => SUPPORTED_PLATFORMS.filter((p) => compatiblePlatforms?.includes(p)),
+ () => QUERYABLE_PLATFORMS.filter((p) => compatiblePlatforms?.includes(p)),
[compatiblePlatforms]
);
diff --git a/frontend/hooks/usePlatformSelector.tsx b/frontend/hooks/usePlatformSelector.tsx
index b1fb384dd9..4d7ad70f3a 100644
--- a/frontend/hooks/usePlatformSelector.tsx
+++ b/frontend/hooks/usePlatformSelector.tsx
@@ -3,7 +3,7 @@ import { forEach } from "lodash";
import {
SelectedPlatformString,
- SUPPORTED_PLATFORMS,
+ QUERYABLE_PLATFORMS,
QueryablePlatform,
} from "interfaces/platform";
@@ -48,7 +48,7 @@ const usePlatformSelector = (
};
const getSelectedPlatforms = useCallback(() => {
- return SUPPORTED_PLATFORMS.filter((p) => checksByPlatform[p]);
+ return QUERYABLE_PLATFORMS.filter((p) => checksByPlatform[p]);
}, [checksByPlatform]);
const isAnyPlatformSelected = Object.values(checksByPlatform).includes(true);
diff --git a/frontend/interfaces/platform.ts b/frontend/interfaces/platform.ts
index 1f33a1639f..d96fbbef39 100644
--- a/frontend/interfaces/platform.ts
+++ b/frontend/interfaces/platform.ts
@@ -22,13 +22,18 @@ export type QueryableDisplayPlatform = Exclude<
>;
export type QueryablePlatform = Exclude;
-export const SUPPORTED_PLATFORMS: QueryablePlatform[] = [
+export const QUERYABLE_PLATFORMS: QueryablePlatform[] = [
"darwin",
"windows",
"linux",
"chrome",
];
+export const isQueryablePlatform = (
+ platform: string | undefined
+): platform is QueryablePlatform =>
+ QUERYABLE_PLATFORMS.includes(platform as QueryablePlatform);
+
// TODO - add "iOS" and "iPadOS" once we support them
export const VULN_SUPPORTED_PLATFORMS: Platform[] = ["darwin", "windows"];
diff --git a/frontend/interfaces/query.ts b/frontend/interfaces/query.ts
index 9485f2da9a..e3d0a41d43 100644
--- a/frontend/interfaces/query.ts
+++ b/frontend/interfaces/query.ts
@@ -14,10 +14,6 @@ export interface IStoredQueryResponse {
query: ISchedulableQuery;
}
-export interface IFleetQueriesResponse {
- queries: ISchedulableQuery[];
-}
-
export interface IQuery {
created_at: string;
updated_at: string;
diff --git a/frontend/interfaces/schedulable_query.ts b/frontend/interfaces/schedulable_query.ts
index fb0fcac74b..ff3698ca57 100644
--- a/frontend/interfaces/schedulable_query.ts
+++ b/frontend/interfaces/schedulable_query.ts
@@ -3,7 +3,11 @@ import PropTypes from "prop-types";
import { IFormField } from "./form_field";
import { IPack } from "./pack";
-import { SelectedPlatformString, QueryablePlatform } from "./platform";
+import {
+ SelectedPlatformString,
+ QueryablePlatform,
+ SelectedPlatform,
+} from "./platform";
// Query itself
export interface ISchedulableQuery {
@@ -15,7 +19,7 @@ export interface ISchedulableQuery {
query: string;
team_id: number | null;
interval: number;
- platform: SelectedPlatformString; // Might more accurately be called `platforms_to_query` – comma-sepparated string of platforms to query, default all platforms if ommitted
+ platform: SelectedPlatformString; // Might more accurately be called `platforms_to_query` or `targeted_platforms` – comma-sepparated string of platforms to query, default all platforms if ommitted
min_osquery_version: string;
automations_enabled: boolean;
logging: QueryLoggingOption;
@@ -32,7 +36,7 @@ export interface ISchedulableQuery {
export interface IEnhancedQuery extends ISchedulableQuery {
performance: string;
- platforms: QueryablePlatform[];
+ targetedPlatforms: QueryablePlatform[];
}
export interface ISchedulableQueryStats {
user_time_p50?: number | null;
@@ -67,7 +71,14 @@ export interface IListQueriesResponse {
export interface IQueryKeyQueriesLoadAll {
scope: "queries";
- teamId: number | undefined;
+ teamId?: number;
+ page?: number;
+ perPage?: number;
+ query?: string;
+ orderDirection?: "asc" | "desc";
+ orderKey?: string;
+ mergeInherited?: boolean;
+ targetedPlatform?: SelectedPlatform;
}
// Create a new query
/** POST /api/v1/fleet/queries */
diff --git a/frontend/pages/DashboardPage/DashboardPage.tsx b/frontend/pages/DashboardPage/DashboardPage.tsx
index 5c72c33fc9..056acc965c 100644
--- a/frontend/pages/DashboardPage/DashboardPage.tsx
+++ b/frontend/pages/DashboardPage/DashboardPage.tsx
@@ -460,7 +460,7 @@ const DashboardPage = ({ router, location }: IDashboardProps): JSX.Element => {
setShowAddHostsModal(!showAddHostsModal);
};
- // NOTE: this is called once on the initial rendering. The initial render of
+ // This is called once on the initial rendering. The initial render of
// the TableContainer child component will call this handler.
const onSoftwareQueryChange = async ({
pageIndex: newPageIndex,
diff --git a/frontend/pages/SoftwarePage/SoftwareAddPage/SoftwareFleetMaintained/FleetMaintainedAppDetailsPage/FleetMaintainedAppDetailsPage.tsx b/frontend/pages/SoftwarePage/SoftwareAddPage/SoftwareFleetMaintained/FleetMaintainedAppDetailsPage/FleetMaintainedAppDetailsPage.tsx
index 5d88909a07..657851e5b2 100644
--- a/frontend/pages/SoftwarePage/SoftwareAddPage/SoftwareFleetMaintained/FleetMaintainedAppDetailsPage/FleetMaintainedAppDetailsPage.tsx
+++ b/frontend/pages/SoftwarePage/SoftwareAddPage/SoftwareFleetMaintained/FleetMaintainedAppDetailsPage/FleetMaintainedAppDetailsPage.tsx
@@ -98,6 +98,9 @@ const FleetMaintainedAppDetailsPage = ({
}: IFleetMaintainedAppDetailsPageProps) => {
const teamId = location.query.team_id;
const appId = parseInt(routeParams.id, 10);
+ if (isNaN(appId)) {
+ router.push(PATHS.SOFTWARE_ADD_FLEET_MAINTAINED);
+ }
const { renderFlash } = useContext(NotificationContext);
const { isPremiumTier } = useContext(AppContext);
diff --git a/frontend/pages/admin/TeamManagementPage/TeamDetailsWrapper/UsersPage/UsersPage.tsx b/frontend/pages/admin/TeamManagementPage/TeamDetailsWrapper/UsersPage/UsersPage.tsx
index 4be24d49d3..ca3e1a1731 100644
--- a/frontend/pages/admin/TeamManagementPage/TeamDetailsWrapper/UsersPage/UsersPage.tsx
+++ b/frontend/pages/admin/TeamManagementPage/TeamDetailsWrapper/UsersPage/UsersPage.tsx
@@ -413,7 +413,7 @@ const UsersPage = ({ location, router }: ITeamSubnavProps): JSX.Element => {
defaultSortDirection="asc"
actionButton={{
name: isGlobalAdmin ? "add user" : "create user",
- buttonText: isGlobalAdmin ? "Add users" : "Create users",
+ buttonText: isGlobalAdmin ? "Add users" : "Create user",
variant: "brand",
onActionButtonClick: isGlobalAdmin
? toggleAddUserModal
@@ -476,7 +476,7 @@ const UsersPage = ({ location, router }: ITeamSubnavProps): JSX.Element => {
onCancel={toggleCreateUserModal}
onSubmit={onCreateUserSubmit}
defaultGlobalRole={null}
- defaultTeamRole="observer"
+ defaultTeamRole="Observer"
defaultTeams={[
{ id: currentTeamDetails.id, name: "", role: "observer" },
]}
diff --git a/frontend/pages/admin/UserManagementPage/components/AddUserModal/AddUserModal.tsx b/frontend/pages/admin/UserManagementPage/components/AddUserModal/AddUserModal.tsx
index 766008ceaa..4fb36d066e 100644
--- a/frontend/pages/admin/UserManagementPage/components/AddUserModal/AddUserModal.tsx
+++ b/frontend/pages/admin/UserManagementPage/components/AddUserModal/AddUserModal.tsx
@@ -59,7 +59,6 @@ const AddUserModal = ({
onCancel={onCancel}
onSubmit={onSubmit}
availableTeams={availableTeams || []}
- submitText="Add"
isPremiumTier={isPremiumTier}
smtpConfigured={smtpConfigured}
sesConfigured={sesConfigured}
diff --git a/frontend/pages/admin/UserManagementPage/components/EditUserModal/EditUserModal.tsx b/frontend/pages/admin/UserManagementPage/components/EditUserModal/EditUserModal.tsx
index 0edfcfe53c..47a9b7edaf 100644
--- a/frontend/pages/admin/UserManagementPage/components/EditUserModal/EditUserModal.tsx
+++ b/frontend/pages/admin/UserManagementPage/components/EditUserModal/EditUserModal.tsx
@@ -69,7 +69,6 @@ const EditUserModal = ({
onCancel={onCancel}
onSubmit={onSubmit}
availableTeams={availableTeams}
- submitText="Save"
isPremiumTier={isPremiumTier}
smtpConfigured={smtpConfigured}
sesConfigured={sesConfigured}
diff --git a/frontend/pages/admin/UserManagementPage/components/SelectRoleForm/SelectRoleForm.tsx b/frontend/pages/admin/UserManagementPage/components/SelectRoleForm/SelectRoleForm.tsx
index c0236b5f88..0ab3012f9f 100644
--- a/frontend/pages/admin/UserManagementPage/components/SelectRoleForm/SelectRoleForm.tsx
+++ b/frontend/pages/admin/UserManagementPage/components/SelectRoleForm/SelectRoleForm.tsx
@@ -13,6 +13,7 @@ interface ISelectRoleFormProps {
teams: ITeam[];
onFormChange: (teams: ITeam[]) => void;
isApiOnly?: boolean;
+ onMenuOpen?: () => void;
}
const generateSelectedTeamData = (
@@ -33,6 +34,7 @@ const SelectRoleForm = ({
teams,
onFormChange,
isApiOnly,
+ onMenuOpen,
}: ISelectRoleFormProps): JSX.Element => {
const { isPremiumTier } = useContext(AppContext);
@@ -55,10 +57,12 @@ const SelectRoleForm = ({
return (
);
};
diff --git a/frontend/pages/admin/UserManagementPage/components/SelectedTeamsForm/SelectedTeamsForm.tsx b/frontend/pages/admin/UserManagementPage/components/SelectedTeamsForm/SelectedTeamsForm.tsx
index 4f8635f995..9953515054 100644
--- a/frontend/pages/admin/UserManagementPage/components/SelectedTeamsForm/SelectedTeamsForm.tsx
+++ b/frontend/pages/admin/UserManagementPage/components/SelectedTeamsForm/SelectedTeamsForm.tsx
@@ -17,6 +17,7 @@ interface ISelectedTeamsFormProps {
usersCurrentTeams: ITeam[];
onFormChange: (teams: ITeam[]) => void;
isApiOnly?: boolean;
+ onMenuOpen?: () => void;
}
const baseClass = "selected-teams-form";
@@ -106,6 +107,7 @@ const SelectedTeamsForm = ({
usersCurrentTeams,
onFormChange,
isApiOnly,
+ onMenuOpen,
}: ISelectedTeamsFormProps): JSX.Element => {
const [teamsFormList, updateSelectedTeams] = useSelectedTeamState(
availableTeams,
@@ -137,6 +139,7 @@ const SelectedTeamsForm = ({
onChange={(newValue: SingleValue) =>
updateSelectedTeams(teamItem.id, newValue as CustomOptionType)
}
+ onMenuOpen={onMenuOpen}
/>
);
diff --git a/frontend/pages/admin/UserManagementPage/components/UserForm/UserForm.tests.tsx b/frontend/pages/admin/UserManagementPage/components/UserForm/UserForm.tests.tsx
index 8584206105..903d77eacd 100644
--- a/frontend/pages/admin/UserManagementPage/components/UserForm/UserForm.tests.tsx
+++ b/frontend/pages/admin/UserManagementPage/components/UserForm/UserForm.tests.tsx
@@ -11,7 +11,6 @@ describe("UserForm - component", () => {
availableTeams: [],
onCancel: noop,
onSubmit: noop,
- submitText: "Add",
isModifiedByGlobalAdmin: true,
isPremiumTier: true,
smtpConfigured: true,
@@ -42,7 +41,7 @@ describe("UserForm - component", () => {
it("renders SSO option when canUseSso is true", () => {
render();
- expect(screen.getByLabelText("Enable single sign-on")).toBeInTheDocument();
+ expect(screen.getByLabelText("Single sign-on")).toBeInTheDocument();
});
it("disables invite user option when SMTP and SES are not configured", () => {
@@ -72,20 +71,51 @@ describe("UserForm - component", () => {
// Verify that non-premium elements are still present
expect(screen.getByLabelText("Full name")).toBeInTheDocument();
expect(screen.getByLabelText("Email")).toBeInTheDocument();
- expect(screen.getByLabelText("Password")).toBeInTheDocument();
+ expect(
+ screen.queryByRole("radio", { name: "Password" })
+ ).toBeInTheDocument();
});
- it("does not render password and 2FA sections when SSO is enabled", () => {
+ it("does not render password and 2FA sections when SSO is selected", () => {
render();
// Enable SSO
- const ssoRadio = screen.getByLabelText("Enable single sign-on");
+ const ssoRadio = screen.getByLabelText("Single sign-on");
ssoRadio.click();
- // Check that password and 2FA sections are not present
- expect(screen.queryByText("Password")).not.toBeInTheDocument();
+ // Check that the password radio is present
+ const passwordRadio = screen.getByRole("radio", { name: "Password" });
+ expect(passwordRadio).not.toBeDisabled();
+
+ // Check that password input field and 2FA sections are not present
+ expect(
+ screen.queryByRole("input", { name: "Password" })
+ ).not.toBeInTheDocument();
expect(
screen.queryByText("Enable two-factor authentication")
).not.toBeInTheDocument();
});
+
+ it("displays disabled SSO option when SSO is globally disabled but was previously enabled for the user", async () => {
+ const props = {
+ ...defaultProps,
+ defaultName: "User 1",
+ defaultEmail: "user@example.com",
+ currentUserId: 1,
+ canUseSso: false,
+ isSsoEnabled: true,
+ isNewUser: false,
+ };
+
+ const { user } = renderWithSetup();
+
+ // Check that the SSO radio is disabled
+ const ssoRadio = screen.getByLabelText("Single sign-on");
+ expect(ssoRadio).toBeDisabled();
+
+ await user.click(screen.getByText("Save"));
+ expect(
+ screen.getByText(/Password field must be completed/i)
+ ).toBeInTheDocument();
+ });
});
diff --git a/frontend/pages/admin/UserManagementPage/components/UserForm/UserForm.tsx b/frontend/pages/admin/UserManagementPage/components/UserForm/UserForm.tsx
index 132e81ea57..4f7db7bfa9 100644
--- a/frontend/pages/admin/UserManagementPage/components/UserForm/UserForm.tsx
+++ b/frontend/pages/admin/UserManagementPage/components/UserForm/UserForm.tsx
@@ -32,7 +32,7 @@ import SelectedTeamsForm from "../SelectedTeamsForm/SelectedTeamsForm";
import SelectRoleForm from "../SelectRoleForm/SelectRoleForm";
import { roleOptions } from "../../helpers/userManagementHelpers";
-const baseClass = "add-user-form";
+const baseClass = "user-form";
export enum NewUserType {
AdminInvited = "ADMIN_INVITED",
@@ -63,7 +63,6 @@ interface IUserFormProps {
availableTeams: ITeam[];
onCancel: () => void;
onSubmit: (formData: IUserFormData) => void;
- submitText: string;
defaultName?: string;
defaultEmail?: string;
currentUserId?: number;
@@ -90,7 +89,6 @@ const UserForm = ({
availableTeams,
onCancel,
onSubmit,
- submitText,
defaultName,
defaultEmail,
currentUserId,
@@ -140,6 +138,14 @@ const UserForm = ({
const [isGlobalUser, setIsGlobalUser] = useState(!!defaultGlobalRole);
+ useEffect(() => {
+ // If SSO is globally disabled but user previously signed in via SSO,
+ // require password is automatically selected on first render
+ if (!canUseSso && !isNewUser && isSsoEnabled) {
+ setFormData({ ...formData, sso_enabled: false });
+ }
+ }, []);
+
// For scrollable modal (re-rerun when formData changes)
useEffect(() => {
checkScroll();
@@ -160,6 +166,19 @@ const UserForm = ({
};
};
+ // Used to show entire dropdown when a dropdown menu is open in scrollable component of a modal
+ // menuPortalTarget solution not used as scrolling is weird
+ const scrollToFitDropdownMenu = () => {
+ if (topDivRef?.current) {
+ setTimeout(() => {
+ if (topDivRef.current) {
+ topDivRef.current.scrollTop =
+ topDivRef.current.scrollHeight - topDivRef.current.clientHeight;
+ }
+ }, 50); // Delay needed for scrollHeight to update first
+ }
+ };
+
const onCheckboxChange = (formField: string): ((evt: string) => void) => {
return (evt: string) => {
return onInputChange(formField)(evt);
@@ -248,12 +267,16 @@ const UserForm = ({
} else if (!validEmail(formData.email)) {
newErrors.email = `${formData.email} is not a valid email`;
}
- // Only test password on new created user (not invited user) or if sso not enabled
- if (
+
+ // Password auth required for new "create user" (not new "invite user") with SSO disabled
+ const isNewAdminCreatedUserWithoutSSO =
isNewUser &&
formData.newUserType === NewUserType.AdminCreated &&
- !formData.sso_enabled
- ) {
+ !formData.sso_enabled;
+ // Force switch existing user to password auth if SSO is disabled globally but was enabled
+ const isExistingUserForcedToPasswordAuth = !canUseSso && isSsoEnabled;
+
+ if (isNewAdminCreatedUserWithoutSSO || isExistingUserForcedToPasswordAuth) {
if (formData.password !== null && !validPassword(formData.password)) {
newErrors.password = "Password must meet the criteria below";
}
@@ -308,6 +331,7 @@ const UserForm = ({
}
}}
isSearchable={false}
+ onMenuOpen={scrollToFitDropdownMenu}
/>
>
);
@@ -356,15 +380,17 @@ const UserForm = ({
usersCurrentTeams={formData.teams}
onFormChange={onSelectedTeamChange}
isApiOnly={isApiOnly}
+ onMenuOpen={scrollToFitDropdownMenu}
/>
>
) : (
))}
{!availableTeams.length && renderNoTeamsMessage()}
@@ -470,50 +496,39 @@ const UserForm = ({
Authentication
+ SSO is not enabled in organization settings.
+
+ User must sign in with a password.
+ >
+ }
+ >
+ Single sign-on
+
+ )
+ }
+ id="single-sign-on-authentication"
checked={!!formData.sso_enabled}
value="true"
name="authentication-type"
onChange={() => onSsoChange(true)}
+ disabled={!canUseSso}
/>
onSsoChange(false)}
- /** Note: This was a checkbox that is now a radio button, waiting on
- * product to confirm if we're adding help text to radio buttons as
- * it's not in Figma design system, the Figma here, or in the Radio
- * component, but the helpText already existed on the checkbox version
- */
- // helpText={
- // canUseSso ? (
- //
- // Password authentication will be disabled for this user.
- //
<%= primaryBuyingSituation==='vm'? 'Instrument your endpoints' : 'Talk to your computers'%>
A <%= primaryBuyingSituation==='vm'? 'lightweight' : 'quick-fast' %> way to gather <%= primaryBuyingSituation==='vm'? 'patch level and custom reports across all your computing devices, even in OT and production environments' : primaryBuyingSituation==='vm'? 'deep context and custom telemetry across all your endpoints, even servers' : primaryBuyingSituation==='mdm'||primaryBuyingSituation==='eo-it'? 'compliance and inventory data across all your devices' : 'device data across all your computers' %>. Pulse check or automate anything on any platform.
Remote-control IT tasks on every kind of computer – even you, Linux.
-
-
Write and run scripts remotely, report progress, and replay queued up tasks on computers that went offline.
-
Optionally integrate Google Calendar to make changes when certain users’ devices are actually free.
-
-
-
-
-
-
- <% } %>
-
-
Pulse check anything
+
On-demand data
Use a live connection to every endpoint to simplify audit, compliance, and reporting from workstations to data centers.
@@ -133,20 +116,53 @@
*Currently limited to: Apple, Linux, Windows, Chromebooks, OT, data centers, Amazon Web Services (AWS), Google Cloud (GCP), and the Microsoft Cloud (Azure).
+
+
+
+
+
+
Orchestrate anything
+
Remote-control IT tasks on every kind of computer – even you, Linux.
+
+
Write and run scripts remotely, report progress, and replay queued up tasks on computers that went offline.
+
Optionally integrate Google Calendar to make changes when certain users’ devices are actually free.
+
+
+
+
-
Ship data to any platform
-
Export anything. Ship data to any platform like Splunk, Snowflake, or any streaming infrastructure like AWS Kinesis and Apache Kafka.
+
Deep context from your environment
+
Get device context down to the chip level on every endpoint to help you make the right decisions.
+
+
Gather real-time data by executing lightweight queries across all devices.
+
Access over 300 tables of system state data. Use presets or create your own queries.
+
+
+ Explore data
+
+
+
+
+
+
+
+
+
+
+
+
+
Data portability
+
Ship data to any platform like Splunk, Snowflake, or any streaming infrastructure like AWS Kinesis and Apache Kafka.
Extract data and correlate it with your log aggregator, SIEM, or data lake.
Accelerate deployment and get more out of osquery. You don’t need to be an osquery expert to get the answers you need from your <%= ['vm', 'eo-security'].includes(pagePersonalization) ? 'endpoints' : 'devices' %>.
+
Accelerate deployment and get more out of osquery. You don’t need to be an osquery expert to get the answers you need from your endpoints.
Remotely disable/enable agent features, choose plugins, and keep osquery up to date.
Import community queries from other security teams at top brands like Palantir and Fastly.
Deploy software from any source using the method that works best for you—whether it's through the UI, API, or GitOps.
+
+
Install from a built-in catalog of Fleet-maintained apps without the need for additional configuration.
+
Upload any third-party software as a custom package to deploy all of the tools your end users need.
+
Manage app deployments and updates for your computers and mobile devices from app stores.
-
-
-
-
-
-
Mitigate CVEs automatically
-
Build automations that close tickets or mitigate vulnerabilities by updating or removing software and running custom scripts.
-
-
Automatically detect when old vulnerabilities are reintroduced, or mitigations falter.
-
Track and mitigate zero days, with live queries and policies, even before they're officially published as CVEs.
-
Only create tickets for vulnerabilities that affect your environment. Automate ticketing with Jira, ServiceNow, or integrate your own system.
-
Deep-link live data with internal IT and platform teams.
-
+
+
+
+
+
Automate patch management
+
Take the guesswork out of keeping devices up-to-date and secure by asking yes or no questions tied to automations.
+
+
Use policy automations to trigger app updates, execute scripts, or create tickets in Jira and Zendesk.
+
Generate webhooks to integrate with your favorite tools and systems.
+
Optionally integrate Google Calendar to schedule time for your users to update software when it’s convenient for them.
-
-
-
-
-
-
Deep context from the environment
-
Fleet gives you data down to the chip level on every endpoint to help you make sense of which vulnerabilities to prioritize.
-
-
Automate prioritization with an admin-level agent that has visibility down to the chipset of any endpoint
-
Access over 300 tables of system state data. Use presets or create your own queries.
-
Automatically match CVEs to your operating systems, apps, extensions, browser plugins, Python libraries, everything.
-
-
- Explore data
-
-
+
+
-
-
-
-
-
-
Report what matters
-
Let's face it, most built-in graphs leave you wanting more. Report MTTR and any other custom metrics exactly the way you want to using fresh data from real computers.
-
-
Track any “celebrity” CVE across every OS, every cloud, and every device, and show how quickly it was mitigated.
-
Avoid distracting your CISO with bloated reports. Only communicate facts important to your organization's KPIs. Show real-time mitigation progress (%) or sync with your favorite data tool like Microsoft BI.
-
+
+
+
+
+
+
+
+
The right software at the right moment
+
Use self-service to keep employees productive and secure with software curated to their needs.
+
+
Install organization-approved software for macOS, Windows, and Linux.
+
Quickly deliver software for teams of any size, tailored to small groups or the entire organization.
Build automations that close tickets or mitigate vulnerabilities by updating or removing software and running custom scripts.
+
+
Automatically detect when old vulnerabilities are reintroduced, or mitigations falter.
+
Track and mitigate zero days, with live queries and policies, even before they're officially published as CVEs.
+
Only create tickets for vulnerabilities that affect your environment. Automate ticketing with Jira, ServiceNow, or integrate your own system.
+
Deep-link live data with internal IT and platform teams.
+
+
+
+
+
+
+
+
+
Deep context from the environment
+
Fleet gives you data down to the chip level on every endpoint to help you make sense of which vulnerabilities to prioritize.
+
+
Automate prioritization with an admin-level agent that has visibility down to the chipset of any endpoint
+
Access over 300 tables of system state data. Use presets or create your own queries.
+
Automatically match CVEs to your operating systems, apps, extensions, browser plugins, Python libraries, everything.
+
+
+ Explore data
+
+
+
+
+
+
+
+
+
Report what matters
+
Let's face it, most built-in graphs leave you wanting more. Report MTTR and any other custom metrics exactly the way you want to using fresh data from real computers.
+
+
Track any “celebrity” CVE across every OS, every cloud, and every device, and show how quickly it was mitigated.
+
Avoid distracting your CISO with bloated reports. Only communicate facts important to your organization's KPIs. Show real-time mitigation progress (%) or sync with your favorite data tool like Microsoft BI.
+
+
+
+
+
Up-to-date data without scans
Traditional network vulnerability scans can clog your network and even haunt your printers with pages full of wingdings. Fleet does things differently.
@@ -143,9 +185,14 @@
Quickly pull data about important CVEs and zero days during an incident or audit.
+
+
+
-
-
+
+
+
+
Untangle your security stack
Use open data and APIs to connect your vulnerability solution with osquery, the agent you might already have deployed.
@@ -155,11 +202,8 @@
Process CVEs across cloud and non-cloud assets in a single platform and see who's responsible for what.
- Fleet can enforce settings like password length on your device. This is useful for IT teams to keep your Mac up to date so you don’t have to.
+ Fleet knows when your device is on or off. This is shown via the online/offline status in Fleet Desktop. This is useful for understanding the state of your device and for troubleshooting should a problem ever arise.
- Remote actions
+ Browser history
- Device actions are helpful for IT teams to troubleshoot your device remotely if you run into any issues. If your IT team uses Fleet's device management (MDM) features, your computer can be restarted, locked or even wiped remotely by your administrator.
+ Fleet can see browser history to protect you in the rare event of bad actors gaining access to you device and personal information.
- Shell scripts
+ System settings
- Fleet can run any shell script on your device remotely. This is useful for IT teams to help you troubleshoot remotely if you run into any issues with your device.
+ Fleet can enforce settings like password length on your device. This is useful for IT teams to keep your Mac up to date so you don’t have to.
- User account logins
+ Remote actions
- Fleet can see details about the user accounts associated with your device, including which accounts have logged in recently. This is useful for IT and security teams to identify logins from suspicious accounts.
+ Device actions are helpful for IT teams to troubleshoot your device remotely if you run into any issues. If your IT team uses Fleet's device management (MDM) features, your computer can be restarted, locked or even wiped remotely by your administrator.
- Device health & performance
+ Shell scripts
- Fleet can see details about your device’s hardware. E.g., what processor is used, how much memory is installed, storage capacity, battery health, etc. This allows IT teams to preemptively address device health problems, which can mitigate data loss and reduce disruption to your workflow caused by IT related issues.
+ Fleet can run any shell script on your device remotely. This is useful for IT teams to help you troubleshoot remotely if you run into any issues with your device.
- Installed software packages
+ User account logins
- Fleet can access a detailed list of the software installed on your device. With this information, IT teams can better manage software update schedules, and reduce disruption to your workflow. Security teams can also use this data to determine if any of your software has been compromised, by referencing your software’s version number against known vulnerable software databases.
+ Fleet can see details about the user accounts associated with your device, including which accounts have logged in recently. This is useful for IT and security teams to identify logins from suspicious accounts.
- Running processes
+ Device health & performance
- Fleet can access a list of processes running on your device. These are processes you interact with graphically i.e., opened software; and processes that are running tasks in the background, such as sending data over network connections, running backups, or scheduled auto-updates. IT and security teams can use osquery to view this list in order to detect suspicious activity that may be a threat to your system.
+ Fleet can see details about your device’s hardware. E.g., what processor is used, how much memory is installed, storage capacity, battery health, etc. This allows IT teams to preemptively address device health problems, which can mitigate data loss and reduce disruption to your workflow caused by IT related issues.
- Fleet can see information about the status of firewalls and other security software installed on your device.
+ Fleet can access a detailed list of the software installed on your device. With this information, IT teams can better manage software update schedules, and reduce disruption to your workflow. Security teams can also use this data to determine if any of your software has been compromised, by referencing your software’s version number against known vulnerable software databases.
- Connected hardware devices
+ Running processes
- Fleet can see information about connected hardware devices. This is typically limited to only the type of hardware connected, and not specific details about the device. E.g., connected smartphones, USB devices, network devices, audio/visual hardware.
+ Fleet can access a list of processes running on your device. These are processes you interact with graphically i.e., opened software; and processes that are running tasks in the background, such as sending data over network connections, running backups, or scheduled auto-updates. IT and security teams can use osquery to view this list in order to detect suspicious activity that may be a threat to your system.
- Device location
+ Security configurations
- Fleet uses IP geolocation to provide an approximate location of your device. Accuracy of IP geolocation services vary depending on where you are, but can typically be pinpointed within the nearest state or city. Organizations typically use this feature to track stolen or misplaced devices, and in some cases to ensure the safety of employees.
+ Fleet can see information about the status of firewalls and other security software installed on your device.
- File contents
+ Connected hardware devices
+ Fleet can see information about connected hardware devices. This is typically limited to only the type of hardware connected, and not specific details about the device. E.g., connected smartphones, USB devices, network devices, audio/visual hardware.
+
+
+
+
+ Device location
+
+
+
+ Fleet uses IP geolocation to provide an approximate location of your device. Accuracy of IP geolocation services vary depending on where you are, but can typically be pinpointed within the nearest state or city. Organizations typically use this feature to track stolen or misplaced devices, and in some cases to ensure the safety of employees.
+
+
+
+
+ File contents
+
+
+
In the case of a cyber attack, it is possible with Fleet to gain read access to files on your system. This is not a feature designed for privacy invasion, but rather a means for security teams to locate files on your device that may have been created or affected by a malicious virus.
Additionally, Fleet can be configured to store disk encryption keys that can be used to recover encrypted data from a macOS device.
diff --git a/website/views/partials/primary-tagline.partial.ejs b/website/views/partials/primary-tagline.partial.ejs
index bc922f4382..bb04b6a2dc 100644
--- a/website/views/partials/primary-tagline.partial.ejs
+++ b/website/views/partials/primary-tagline.partial.ejs
@@ -1,7 +1,7 @@
<%=
- typeof primaryBuyingSituation === 'undefined' ? 'Open-source device management for everyone' // Default (no buying situation)
+ typeof primaryBuyingSituation === 'undefined' ? 'Open device management for everyone' // Default (no buying situation)
: primaryBuyingSituation === 'vm' ? 'Focus on vulnerabilities, not vendors' // vm
: primaryBuyingSituation === 'eo-security' ? 'Easily get security data'// eo-security
: primaryBuyingSituation === 'eo-it' ? 'Untangle your endpoints' : // eo-it
- 'Open-source device management for everyone'// mdm
+ 'Open device management for everyone'// mdm
%>