diff --git a/articles/deploy-fleet-on-aws-ecs.md b/articles/deploy-fleet-on-aws-ecs.md index db0a440218..ac9428865b 100644 --- a/articles/deploy-fleet-on-aws-ecs.md +++ b/articles/deploy-fleet-on-aws-ecs.md @@ -1,6 +1,6 @@ # Deploy Fleet on AWS ECS -> **This article was archived on May 15, 2024** and may be out of date. Check out [Deploy Fleet](https://fleetdm.com/docs/deploy/deploy-fleet) for our recommended deployment method. +> **This article was archived on May 16, 2024,** and may be outdated. Check out [Deploy Fleet](https://fleetdm.com/docs/deploy/deploy-fleet) for our recommended deployment method. ![Deploy Fleet on AWS ECS](../website/assets/images/articles/deploy-fleet-on-aws-ecs-800x450@2x.png) @@ -30,7 +30,7 @@ Alternatively you can bake the prepare command into the same task definition see - + \ No newline at end of file diff --git a/articles/deploy-fleet-on-aws-with-terraform.md b/articles/deploy-fleet-on-aws-with-terraform.md index e441730bc6..6853032a50 100644 --- a/articles/deploy-fleet-on-aws-with-terraform.md +++ b/articles/deploy-fleet-on-aws-with-terraform.md @@ -1,13 +1,13 @@ # Deploy Fleet on AWS with Terraform +> **This article was archived on May 16, 2024,** and may be outdated. Check out [Deploy Fleet](https://fleetdm.com/docs/deploy/deploy-fleet) for our recommended deployment method. + ![Deploy Fleet on AWS ECS](../website/assets/images/articles/deploy-fleet-on-aws-with-terraform-800x450@2x.png) Learn how to deploy Fleet on AWS with Terraform IaC (infrastructure as code). Deploying on AWS with Fleet’s reference architecture is an easy way to get a fully functional Fleet instance that can scale to your needs. -> Updated May 2023 to reflect Fleet's current Terraform Module setup. - ## Prerequisites: - AWS CLI installed and configured. @@ -23,8 +23,7 @@ Remote state can be simple (local state) or complicated (S3, state locking, etc. ### Modules -[Fleet terraform](https://github.com/fleetdm/fleet/tree/main/terraform) is made up of multiple modules. These modules can be used independently, or as group to stand up an opinionated -set of infrastructure that we have found success with. +[Fleet terraform](https://github.com/fleetdm/fleet/tree/main/terraform) is made up of multiple modules. These modules can be used independently, or as group to stand up an opinionated set of infrastructure that we have found success with. Each module defines the required resource and consumes the next nested module. The root module creates the VPC and then pulls in the `byo-vpc` module configuring it as necessary. The `byo-vpc` module creates the database and cache instances that get passed into the `byo-db` module. And finally the `byo-db` module creates the ECS cluster and load balancer to be consumed by the `byo-ecs` module. diff --git a/articles/deploy-fleet-on-centos.md b/articles/deploy-fleet-on-centos.md index efb7fefc80..b832e0f4f1 100644 --- a/articles/deploy-fleet-on-centos.md +++ b/articles/deploy-fleet-on-centos.md @@ -1,7 +1,11 @@ # Deploy Fleet on CentOS +> **This article was archived on May 16, 2024,** and may be outdated. Check out [Deploy Fleet](https://fleetdm.com/docs/deploy/deploy-fleet) for our recommended deployment method. + ![Deploy Fleet on CentOS](../website/assets/images/articles/deploy-fleet-on-centos-800x450@2x.png) +> Updated on December 2, 2020, by [Noah Talerman](https://github.com/noahtalerman). + In this guide, we're going to install Fleet and all of its application dependencies on a CentOS 7.1 server. Once we have Fleet up and running, we're going to install osquery on that same CentOS 7.1 host and enroll it in Fleet. This should give you a good understanding of both how to install Fleet as well as how to install and configure osquery such that it can communicate with Fleet. ## Setting up a host @@ -219,7 +223,7 @@ If you go back to [https://localhost:8080/hosts/manage](https://localhost:8080/h - + diff --git a/articles/deploy-fleet-on-cloudgov.md b/articles/deploy-fleet-on-cloudgov.md index 506ef1ca96..5e5e2b1644 100644 --- a/articles/deploy-fleet-on-cloudgov.md +++ b/articles/deploy-fleet-on-cloudgov.md @@ -1,5 +1,7 @@ # Deploy Fleet on Cloud.gov (Cloud Foundry) +> **This article was archived on May 16, 2024,** and may be outdated. Check out [Deploy Fleet](https://fleetdm.com/docs/deploy/deploy-fleet) for our recommended deployment method. + ![Deploy Fleet on Cloud.gov](../website/assets/images/articles/deploy-fleet-on-cloudgov-800x450@2x.png) Cloud.gov is a [FEDRAMP moderate Platform-as-a-Service @@ -115,7 +117,7 @@ variables](https://fleetdm.com/docs/deploying/configuration#using-only-environme - + diff --git a/articles/deploy-fleet-on-hetzner-cloud.md b/articles/deploy-fleet-on-hetzner-cloud.md index bc61e48b69..82dac9495c 100644 --- a/articles/deploy-fleet-on-hetzner-cloud.md +++ b/articles/deploy-fleet-on-hetzner-cloud.md @@ -1,5 +1,7 @@ # Deploy Fleet on Hetzner Cloud +> **This article was archived on May 16, 2024,** and may be outdated. Check out [Deploy Fleet](https://fleetdm.com/docs/deploy/deploy-fleet) for our recommended deployment method. + ![Deploy Fleet on Hetzner Cloud](../website/assets/images/articles/deploy-fleet-on-hetzner-cloud-800x450@2x.png) Learn how to deploy Fleet on Hetzner Cloud using cloud-init and Docker. [Hetzner](https://hetzner.com) is a great price-performance provider for “root” (dedicated) and Virtual Private Servers (VPS), offering high performance and generous bandwidth. diff --git a/articles/deploy-fleet-on-kubernetes.md b/articles/deploy-fleet-on-kubernetes.md index e8f4a85fae..ea85bbb07d 100644 --- a/articles/deploy-fleet-on-kubernetes.md +++ b/articles/deploy-fleet-on-kubernetes.md @@ -1,7 +1,11 @@ # Deploy Fleet on Kubernetes +> **This article was archived on May 16, 2024,** and may be outdated. Check out [Deploy Fleet](https://fleetdm.com/docs/deploy/deploy-fleet) for our recommended deployment method. + ![Deploy Fleet on Kubernetes](../website/assets/images/articles/deploy-fleet-on-kubernetes-800x450@2x.png) +> Updated on May 10, 2022, by [Kelvin Oghenerhoro Omereshone](https://github.com/DominusKelvin). + In this guide, we will focus on deploying Fleet only on a Kubernetes cluster. Kubernetes is a container orchestration tool that was open sourced by Google in 2014. There are 2 primary ways to deploy the Fleet server to a Kubernetes cluster. The first is via `kubectl` with a `deployment.yml` file. The second is using Helm, the Kubernetes Package Manager. @@ -194,7 +198,7 @@ Once you have the public IP address for the load balancer, create an A record in - + diff --git a/articles/deploy-fleet-on-render.md b/articles/deploy-fleet-on-render.md index aae6749182..1ec25ae1ed 100644 --- a/articles/deploy-fleet-on-render.md +++ b/articles/deploy-fleet-on-render.md @@ -1,12 +1,12 @@ # Deploy Fleet on Render +> **This article was archived on May 16, 2024,** and may be outdated. Check out [Deploy Fleet](https://fleetdm.com/docs/deploy/deploy-fleet) for our recommended deployment method. + ![Deploy Fleet on Render](../website/assets/images/articles/deploy-fleet-on-render-800x450@2x.png) -> Updated May 2024 to reflect Fleet's recommended Render deployment. - [Render](https://render.com/) is a cloud hosting service that makes it easy to get up and running fast, without the typical configuration headaches of larger enterprise hosting providers. Our Render blueprint offers a one-click deploy of Fleet in under five minutes, and provides a scalable cloud environment with a lower barrier to entry, making it a great place to get some experience with [Fleet](https://fleetdm.com/) and [osquery](https://osquery.io/). -With one click, our Render blueprint will provision a Fleet web service, a MySQL database, and a Redis in-memory data store. Each service requires Render's `standard` plan at a cost of $7/mo each, totaling $21/mo to host your Fleet instance. If you prefer to follow a video, you can [watch us demonstrating the Render deployment process](https://www.loom.com/share/9373fb6bab2c43aa91949490ceb2678c?sid=081d70de-d736-4681-8107-2d80ec525235). +With one click, our Render blueprint will provision a Fleet web service, a MySQL database, and a Redis in-memory data store. Each service requires Render's `standard` plan at a cost of $7/mo each, totaling $21/mo to host your Fleet instance. If you prefer to follow a video, you can [watch us demonstrating the Render deployment process](https://youtu.be/hly0tAOqveA). ## Deployment steps diff --git a/changes/18852-sw-versions-tooltips-bug b/changes/18852-sw-versions-tooltips-bug new file mode 100644 index 0000000000..798bbc7c4d --- /dev/null +++ b/changes/18852-sw-versions-tooltips-bug @@ -0,0 +1 @@ +- Fix a bug where a singular software version in the Software table generated a tooltip unnecessarily. diff --git a/changes/18953-case-insensitive-host-queries-sort b/changes/18953-case-insensitive-host-queries-sort new file mode 100644 index 0000000000..cc1ea8be17 --- /dev/null +++ b/changes/18953-case-insensitive-host-queries-sort @@ -0,0 +1 @@ +- Sort the host details page queries table case-insensitively. diff --git a/docs/Deploy/deploy-fleet.md b/docs/Deploy/deploy-fleet.md index f772f8e751..478264344e 100644 --- a/docs/Deploy/deploy-fleet.md +++ b/docs/Deploy/deploy-fleet.md @@ -6,8 +6,8 @@ Deploy on Render for an easy one-click POC. Or, choose AWS with Terraform to dep
-

One click deploy

- Deploy on render in 5 minutes +

One-click deploy

+ Deploy on Render in 5 minutes
@@ -31,14 +31,14 @@ Render is a cloud hosting service that makes it easy to get up and running fast, -

Prerequisites

+### Prerequisites - A Render account with payment information. >The Fleet Render Blueprint will provision a web service, a MySQL database, and a Redis in-memory data store. Each service requires Render's standard plan at a cost of **$7/month** each, totaling **$21/month**. -

Instructions

+### Instructions 1. [Click here](https://render.com/deploy?repo=https://github.com/fleetdm/fleet) to open the Fleet Blueprint on Render. You will be prompted to create or log in to your Render account with associated payment information. @@ -60,7 +60,7 @@ The simplest way to get started with Fleet at scale is to use AWS with Terraform This workflow takes about 30 minutes to complete and supports between 10 and 350,000 hosts. -

Prerequisites

+### Prerequisites - A new or existing Amazon Web Services (AWS) account @@ -72,7 +72,7 @@ This workflow takes about 30 minutes to complete and supports between 10 and 350 - A Fully-Qualified Domain Name (FQDN) for hosting Fleet -

Instructions

+### Instructions 1. [Download](https://github.com/fleetdm/fleet/blob/main/terraform/example/main.tf) the Fleet `main.tf` Terraform file. @@ -137,4 +137,4 @@ This workflow takes about 30 minutes to complete and supports between 10 and 350 12. That’s it! You should now be able to log in to Fleet and [enroll a host](https://fleetdm.com/docs/using-fleet/enroll-hosts). - + \ No newline at end of file diff --git a/docs/Using Fleet/Supported-host-operating-systems.md b/docs/Using Fleet/Supported-host-operating-systems.md index ce1567877e..f3dbf77196 100644 --- a/docs/Using Fleet/Supported-host-operating-systems.md +++ b/docs/Using Fleet/Supported-host-operating-systems.md @@ -22,10 +22,11 @@ If a table is not available for your host, Fleet will generally handle things be Fleet's agent (fleetd) generated for MacOS by `fleetctl package` does not include native support for M1 Macs. Some values returned may reflect the information returned by Rosetta rather than the system. For example, a CPU will show up as `i486`. ### Linux -Fleet's agent (fleetd) will run on Linux distributions where `glibc` is >= 2.2 (there is ongoing work to make osquery work with `glibc` 2.12+). -If you aren't sure what version of `glibc` your distribution is using, [DistroWatch](https://distrowatch.com/) is a great resource. -> On Linux, Fleet Desktop only supports $DISPLAY `:0`. +> Ubuntu Linux: +> Fleet Desktop currently supports Xorg as X11 server, Wayland is currently not supported. +> Ubuntu 24.04 comes with Wayland enabled by default. To use X11 instead of Wayland you can set +> `WaylandEnable=false` in `/etc/gdm3/custom.conf` and reboot. > Fedora, CentOS 8 and 9 require a [gnome extension](https://extensions.gnome.org/extension/615/appindicator-support/) and Google Chrome set to the default browser for Fleet Desktop. diff --git a/docs/Using Fleet/Vulnerability-Processing.md b/docs/Using Fleet/Vulnerability-Processing.md index dbefe9960b..1b7f986fa6 100644 --- a/docs/Using Fleet/Vulnerability-Processing.md +++ b/docs/Using Fleet/Vulnerability-Processing.md @@ -21,6 +21,7 @@ Fleet detects vulnerabilities for these software types: | Apps | ✅ | ✅ | ❌ | | Browser plugins | Chrome extensions, Firefox extensions | Chrome extensions, Firefox extensions | ❌ | | Packages | Python, Homebrew | Python, Atom, Chocolatey | Adhere to whatever is defined in the [OVAL definitions](https://github.com/fleetdm/nvd/blob/master/oval_sources.json), except for kernel vulnerabilities and vulnerabilities involving configuration files. Supported distributions: | +| IDE extensions | VS Code extensions | VS Code extensions | VS Code extensions | As of right now, only app names with all ASCII characters are supported. Apps with names featuring non-ASCII characters, such as Cyrillic, will not generate matches. diff --git a/frontend/components/forms/fields/SearchField/SearchField.tsx b/frontend/components/forms/fields/SearchField/SearchField.tsx index ed8a1240f1..d487d89324 100644 --- a/frontend/components/forms/fields/SearchField/SearchField.tsx +++ b/frontend/components/forms/fields/SearchField/SearchField.tsx @@ -4,8 +4,6 @@ import { IconNames } from "components/icons"; // @ts-ignore import InputFieldWithIcon from "../InputFieldWithIcon"; -const baseClass = "search-field"; - export interface ISearchFieldProps { placeholder: string; defaultValue?: string; @@ -37,7 +35,6 @@ const SearchField = ({ name={icon} placeholder={placeholder} value={searchQueryInput} - // inputWrapperClass={`${baseClass}__input-wrapper`} onChange={onInputChange} onClick={onClick} iconPosition="start" diff --git a/frontend/docs/patterns.md b/frontend/docs/patterns.md index ebea4d4212..66d6e6b245 100644 --- a/frontend/docs/patterns.md +++ b/frontend/docs/patterns.md @@ -11,6 +11,7 @@ should be discussed within the team and documented before merged. - [Typing](#typing) - [Utilities](#utilities) - [Components](#components) +- [Forms] (#forms) - [React hooks](#react-hooks) - [React Context](#react-context) - [Fleet API calls](#fleet-api-calls) @@ -194,11 +195,30 @@ const PackComposerPage = ({ router }: IPackComposerPageProps): JSX.Element => { export default PackComposerPage; ``` + +## Forms + +### Data validation + +Forms should make use of a pure `validate` function whose input(s) correspond to form data (may include +new and possibly former form data) and whose output is an object of formFieldName:errorMessage +key-value pairs (`Record`) e.g. + +``` +const validate = (newFormData: IFormData) => { + const errors = {}; + ... + return errors; +} +``` +The output of `validate` should be used by the calling `onChange` handler to set a `formErrors` +state. + ## React hooks [Hooks](https://reactjs.org/docs/hooks-intro.html) are used to track state and use other features of React. Hooks are only allowed in functional components, which are created like so: - + ```typescript import React, { useState, useEffect } from "React"; diff --git a/frontend/pages/SoftwarePage/components/VersionCell/VersionCell.tsx b/frontend/pages/SoftwarePage/components/VersionCell/VersionCell.tsx index 45441e779f..ddfd166804 100644 --- a/frontend/pages/SoftwarePage/components/VersionCell/VersionCell.tsx +++ b/frontend/pages/SoftwarePage/components/VersionCell/VersionCell.tsx @@ -44,14 +44,14 @@ interface IVersionCellProps { const VersionCell = ({ versions, }: IVersionCellProps) => { - const tooltipId = uniqueId(); - // only one version, no need for tooltip const cellText = generateText(versions); if (!versions || versions.length <= 1) { return <>{cellText}; } + const tooltipId = uniqueId(); + const versionTooltip = generateTooltip(versions, tooltipId); return ( <> diff --git a/frontend/pages/hosts/details/cards/Queries/HostQueriesTableConfig.tsx b/frontend/pages/hosts/details/cards/Queries/HostQueriesTableConfig.tsx index 18684dbff2..a262acc56f 100644 --- a/frontend/pages/hosts/details/cards/Queries/HostQueriesTableConfig.tsx +++ b/frontend/pages/hosts/details/cards/Queries/HostQueriesTableConfig.tsx @@ -50,6 +50,7 @@ interface IDataColumn { | ((props: IPerformanceImpactCell) => JSX.Element); disableHidden?: boolean; disableSortBy?: boolean; + sortType?: string; } // NOTE: cellProps come from react-table @@ -66,6 +67,7 @@ const generateColumnConfigs = ( Cell: (cellProps: ICellProps) => ( ), + sortType: "caseInsensitive", }, { Header: () => { diff --git a/frontend/pages/queries/ManageQueriesPage/components/QueriesTable/QueriesTableConfig.tsx b/frontend/pages/queries/ManageQueriesPage/components/QueriesTable/QueriesTableConfig.tsx index 32b712ef82..995ea6d7f3 100644 --- a/frontend/pages/queries/ManageQueriesPage/components/QueriesTable/QueriesTableConfig.tsx +++ b/frontend/pages/queries/ManageQueriesPage/components/QueriesTable/QueriesTableConfig.tsx @@ -160,26 +160,6 @@ const generateTableHeaders = ({ Observers can run this query. - - // <> - // - // - // - // - // Observers can run this query. - // - // )} {viewingTeamScope && // inherited diff --git a/handbook/digital-experience/README.md b/handbook/digital-experience/README.md index c54c36ca35..4c5d329a9c 100644 --- a/handbook/digital-experience/README.md +++ b/handbook/digital-experience/README.md @@ -290,7 +290,7 @@ Agenda: ### Prepare for CEO office minutes -Before the start of the meeting, the Apprentice to the CEO will prepare the "CEO office minutes" meeting [agenda](https://docs.google.com/document/d/12cd0N8KvHkfJxYlo7ggdisrvqw4MCErDoIzLjmBIdj4/edit) such that the following is true: +Before the start of the meeting, the Apprentice will prepare the "CEO office minutes" meeting [agenda](https://docs.google.com/document/d/12cd0N8KvHkfJxYlo7ggdisrvqw4MCErDoIzLjmBIdj4/edit) such that the following is true: 1. All agenda items are prefixed with a date of when the item will be covered and name of the person requesting to discuss the issue. 2. All team members with an agenda item have added themselves **and their manager** to the correct calendar event. If the team member or manager hasn't been added to the calendar event before the meeting begins, the agenda item is de-prioritized in favor of others with representatives in attendance. 3. If there are more that two team members attending, the Apprentice will work with the team members to schedule additional time to cover the agenda. @@ -482,7 +482,7 @@ Once a day the Apprentice will confirm check LinkedIn for unread messages. ### Unroll a Slack thread -From time to time the CEO will ask the Apprentice to the CEO to unroll a Slack thread into a well-named whiteboard google doc for safekeeping and future searching. +From time to time the CEO will ask the Apprentice to unroll a Slack thread into a well-named whiteboard Google doc for safekeeping and future searching. 1. Start with a new doc. 2. Name the file with "yyyy-mm-dd - topic" (something empathetic and easy to find). 3. Use CMD+SHFT+V to paste the Slack convo into the doc. diff --git a/it-and-security/teams/explore-data.yml b/it-and-security/teams/explore-data.yml index 4fef4ef021..abea724c2a 100644 --- a/it-and-security/teams/explore-data.yml +++ b/it-and-security/teams/explore-data.yml @@ -1,4 +1,4 @@ -name: "Explore data (fleetdm.com) [DO NOT DELETE]" +name: "Explore data (fleetdm.com)" team_settings: features: enable_host_users: true diff --git a/orbit/changes/18925-fleet-desktop-support-ubuntu-24.04-x11 b/orbit/changes/18925-fleet-desktop-support-ubuntu-24.04-x11 new file mode 100644 index 0000000000..acf781e91a --- /dev/null +++ b/orbit/changes/18925-fleet-desktop-support-ubuntu-24.04-x11 @@ -0,0 +1 @@ +* Added code to detect value of `DISPLAY` variable of user instead of defaulting to `:0` (to support Ubuntu 24.04 with Xorg). diff --git a/orbit/pkg/execuser/execuser_linux.go b/orbit/pkg/execuser/execuser_linux.go index dc95667d17..748576e7ff 100644 --- a/orbit/pkg/execuser/execuser_linux.go +++ b/orbit/pkg/execuser/execuser_linux.go @@ -1,11 +1,15 @@ package execuser import ( + "bufio" + "bytes" "errors" "fmt" + "io" "os" "os/exec" "path/filepath" + "regexp" "strconv" "strings" @@ -19,23 +23,44 @@ func run(path string, opts eopts) error { return fmt.Errorf("get user: %w", err) } + // TODO(lucas): Default to display :0 if user DISPLAY environment variable + // could not be found, revisit when working on multi-user/multi-session support. + // This assumes there's only one desktop session and belongs to the + // user returned in `getLoginUID'. + defaultDisplay := ":0" + log.Info(). Str("user", user.name). Int64("id", user.id). - Msg("running sudo") + Msg("attempting to get user's DISPLAY") - // Flag `-i` is needed to run the command with the user's context, from `man sudo`: - // "The command is run with an environment similar to the one a user would receive at log in" - arg := []string{"-i", "-u", user.name, "-H"} - for _, nv := range opts.env { - arg = append(arg, fmt.Sprintf("%s=%s", nv[0], nv[1])) + display, err := getUserDisplay(user.name, opts) + if err != nil { + log.Error(). + Str("user", user.name). + Int64("id", user.id). + Err(err). + Msgf("failed to get user's DISPLAY, using default %s", defaultDisplay) + display = defaultDisplay + } else if display == "" { + log.Warn(). + Str("user", user.name). + Int64("id", user.id). + Msgf("user's DISPLAY not found, using default %s", defaultDisplay) + display = defaultDisplay } - arg = append(arg, - // TODO(lucas): Default to display 0, revisit when working on - // multi-user/multi-session support. This assumes there's only - // one desktop session and belongs to the user returned in `getLoginUID'. - "DISPLAY=:0", + log.Info(). + Str("path", path). + Str("user", user.name). + Int64("id", user.id). + Str("display", display). + Msg("running sudo") + + args := argsForSudo(user, opts) + + args = append(args, + "DISPLAY="+display, // DBUS_SESSION_BUS_ADDRESS sets the location of the user login session bus. // Required by the libayatana-appindicator3 library to display a tray icon // on the desktop session. @@ -44,11 +69,14 @@ func run(path string, opts eopts) error { // (because it's already part of the user). fmt.Sprintf("DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/%d/bus", user.id), // Append the packaged libayatana-appindicator3 libraries path to LD_LIBRARY_PATH. + // + // Fleet Desktop doesn't use libayatana-appindicator3 since 1.18.3, but we need to + // keep this to support older versions of Fleet Desktop. fmt.Sprintf("LD_LIBRARY_PATH=%s:%s", filepath.Dir(path), os.ExpandEnv("$LD_LIBRARY_PATH")), path, ) - cmd := exec.Command("sudo", arg...) + cmd := exec.Command("sudo", args...) cmd.Stderr = os.Stderr cmd.Stdout = os.Stdout log.Printf("cmd=%s", cmd.String()) @@ -64,6 +92,18 @@ type user struct { id int64 } +func argsForSudo(u *user, opts eopts) []string { + // -H: "[...] to set HOME environment to what's specified in the target's user password database entry." + // -i: needed to run the command with the user's context, from `man sudo`: + // "The command is run with an environment similar to the one a user would receive at log in" + // -u: "[..]Run the command as a user other than the default target user (usually root)." + args := []string{"-i", "-u", u.name, "-H"} + for _, nv := range opts.env { + args = append(args, fmt.Sprintf("%s=%s", nv[0], nv[1])) + } + return args +} + // getLoginUID returns the name and uid of the first login user // as reported by the `users' command. // @@ -120,3 +160,30 @@ func parseIDOutput(s string) (int64, error) { } return uid, nil } + +var whoLineRegexp = regexp.MustCompile(`(\w+)\s+(:\d+)\s+`) + +func getUserDisplay(user string, opts eopts) (string, error) { + cmd := exec.Command("who") + var stdout bytes.Buffer + cmd.Stdout = &stdout + if err := cmd.Run(); err != nil { + return "", fmt.Errorf("run 'who' to get user display: %w", err) + } + return parseWhoOutputForDisplay(&stdout, user) +} + +func parseWhoOutputForDisplay(output io.Reader, user string) (string, error) { + scanner := bufio.NewScanner(output) + for scanner.Scan() { + line := scanner.Text() + matches := whoLineRegexp.FindStringSubmatch(line) + if len(matches) > 1 && matches[1] == user { + return matches[2], nil + } + } + if err := scanner.Err(); err != nil { + return "", fmt.Errorf("scanner error: %w", err) + } + return "", nil +} diff --git a/orbit/pkg/execuser/execuser_linux_test.go b/orbit/pkg/execuser/execuser_linux_test.go new file mode 100644 index 0000000000..c227df3512 --- /dev/null +++ b/orbit/pkg/execuser/execuser_linux_test.go @@ -0,0 +1,57 @@ +package execuser + +import ( + "bytes" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestParseWhoOutputForDisplay(t *testing.T) { + testCases := []struct { + name string + output string + user string + expectedDisplay string + }{ + { + "Ubuntu 22.04.2 (X11)", + `foo :0 2024-05-14 17:34 (:0)`, + "foo", + ":0", + }, + { + "Ubuntu 22.04.2 (X11) - user not listed", + `foo :0 2024-05-14 17:34 (:0)`, + "bar", + "", + }, + { + "Ubuntu 24.04 (X11)", + `foo seat0 2024-05-14 17:42 (login screen) +foo :1 2024-05-14 17:42 (:1)`, + "foo", + ":1", + }, + { + "Ubuntu 24.04 (Wayland) - DISPLAY not found", + `foo seat0 2024-05-14 18:11 (login screen) +foo tty2 2024-05-14 18:11 (tty2)`, + "foo", + "", + }, + { + "Empty", + ``, + "foo", + "", + }, + } + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + display, err := parseWhoOutputForDisplay(bytes.NewReader([]byte(tc.output)), tc.user) + require.NoError(t, err) + require.Equal(t, tc.expectedDisplay, display) + }) + } +} diff --git a/server/datastore/mysql/hosts_test.go b/server/datastore/mysql/hosts_test.go index f6e5613b86..783fa156b7 100644 --- a/server/datastore/mysql/hosts_test.go +++ b/server/datastore/mysql/hosts_test.go @@ -8689,8 +8689,11 @@ func testHostHealth(t *testing.T, ds *Datastore) { require.NoError(t, ds.LoadHostSoftware(context.Background(), h, false)) soft1 := h.Software[0] - if soft1.Name != "bar" { - soft1 = h.Software[1] + for _, item := range h.Software { + if item.Name == "bar" { + soft1 = item + break + } } cpes := []fleet.SoftwareCPE{{SoftwareID: soft1.ID, CPE: "somecpe"}} @@ -8700,8 +8703,11 @@ func testHostHealth(t *testing.T, ds *Datastore) { // Reload software so that 'GeneratedCPEID is set. require.NoError(t, ds.LoadHostSoftware(context.Background(), h, false)) soft1 = h.Software[0] - if soft1.Name != "bar" { - soft1 = h.Software[1] + for _, item := range h.Software { + if item.Name == "bar" { + soft1 = item + break + } } inserted, err := ds.InsertSoftwareVulnerability( diff --git a/server/service/integration_core_test.go b/server/service/integration_core_test.go index 1830670a04..fae0a8cd98 100644 --- a/server/service/integration_core_test.go +++ b/server/service/integration_core_test.go @@ -725,8 +725,11 @@ func (s *integrationTestSuite) TestVulnerableSoftware() { require.NoError(t, s.ds.LoadHostSoftware(context.Background(), host, false)) soft1 := host.Software[0] - if soft1.Name != "bar" { - soft1 = host.Software[1] + for _, item := range host.Software { + if item.Name == "bar" { + soft1 = item + break + } } cpes := []fleet.SoftwareCPE{{SoftwareID: soft1.ID, CPE: "somecpe"}} @@ -736,8 +739,11 @@ func (s *integrationTestSuite) TestVulnerableSoftware() { // Reload software so that 'GeneratedCPEID is set. require.NoError(t, s.ds.LoadHostSoftware(context.Background(), host, false)) soft1 = host.Software[0] - if soft1.Name != "bar" { - soft1 = host.Software[1] + for _, item := range host.Software { + if item.Name == "bar" { + soft1 = item + break + } } inserted, err := s.ds.InsertSoftwareVulnerability( @@ -10862,8 +10868,11 @@ func (s *integrationTestSuite) TestHostHealth() { require.NoError(t, s.ds.LoadHostSoftware(context.Background(), host, false)) soft1 := host.Software[0] - if soft1.Name != "bar" { - soft1 = host.Software[1] + for _, item := range host.Software { + if item.Name == "bar" { + soft1 = item + break + } } cpes := []fleet.SoftwareCPE{{SoftwareID: soft1.ID, CPE: "somecpe"}} @@ -10873,8 +10882,11 @@ func (s *integrationTestSuite) TestHostHealth() { // Reload software so that 'GeneratedCPEID is set. require.NoError(t, s.ds.LoadHostSoftware(context.Background(), host, false)) soft1 = host.Software[0] - if soft1.Name != "bar" { - soft1 = host.Software[1] + for _, item := range host.Software { + if item.Name == "bar" { + soft1 = item + break + } } inserted, err := s.ds.InsertSoftwareVulnerability( diff --git a/server/service/integration_enterprise_test.go b/server/service/integration_enterprise_test.go index f9b2fb8195..623fd9d1bc 100644 --- a/server/service/integration_enterprise_test.go +++ b/server/service/integration_enterprise_test.go @@ -7081,8 +7081,11 @@ func (s *integrationEnterpriseTestSuite) TestAllSoftwareTitles() { require.NoError(t, s.ds.LoadHostSoftware(context.Background(), host, false)) soft1 := host.Software[0] - if soft1.Name != "bar" { - soft1 = host.Software[1] + for _, item := range host.Software { + if item.Name == "bar" { + soft1 = item + break + } } cpes := []fleet.SoftwareCPE{{SoftwareID: soft1.ID, CPE: "somecpe"}} @@ -7092,8 +7095,11 @@ func (s *integrationEnterpriseTestSuite) TestAllSoftwareTitles() { // Reload software so that 'GeneratedCPEID is set. require.NoError(t, s.ds.LoadHostSoftware(context.Background(), host, false)) soft1 = host.Software[0] - if soft1.Name != "bar" { - soft1 = host.Software[1] + for _, item := range host.Software { + if item.Name == "bar" { + soft1 = item + break + } } inserted, err := s.ds.InsertSoftwareVulnerability( diff --git a/tools/tuf/test/create_repository.sh b/tools/tuf/test/create_repository.sh index 9131852d5b..203eb791bc 100755 --- a/tools/tuf/test/create_repository.sh +++ b/tools/tuf/test/create_repository.sh @@ -29,7 +29,7 @@ SWIFT_DIALOG_MACOS_APP_VERSION=2.2.1 SWIFT_DIALOG_MACOS_APP_BUILD_VERSION=4591 if [[ -z "$OSQUERY_VERSION" ]]; then - OSQUERY_VERSION=5.11.0 + OSQUERY_VERSION=5.12.2 fi mkdir -p $TUF_PATH/tmp diff --git a/website/assets/images/docs-cta-stage-1-314x180@2x.png b/website/assets/images/docs-cta-stage-1-314x180@2x.png new file mode 100644 index 0000000000..ff4a3cdb72 Binary files /dev/null and b/website/assets/images/docs-cta-stage-1-314x180@2x.png differ diff --git a/website/assets/styles/mixins-and-variables/colors.less b/website/assets/styles/mixins-and-variables/colors.less index 4c39a6579c..f4a6e4c685 100644 --- a/website/assets/styles/mixins-and-variables/colors.less +++ b/website/assets/styles/mixins-and-variables/colors.less @@ -7,6 +7,7 @@ @core-fleet-black-50: #8b8fa2; @core-fleet-black-33: #B3B6C1; @core-fleet-black-25: #C5C7D1; +@core-fleet-black-10: #E2E4EA; @core-vibrant-red: #FF5C83; diff --git a/website/assets/styles/pages/docs/basic-documentation.less b/website/assets/styles/pages/docs/basic-documentation.less index fa770962ad..76de2bb4b8 100644 --- a/website/assets/styles/pages/docs/basic-documentation.less +++ b/website/assets/styles/pages/docs/basic-documentation.less @@ -573,6 +573,7 @@ font-style: normal; font-weight: 800; line-height: 19.2px; + margin-bottom: 8px; } span { font-size: 12px; @@ -580,6 +581,7 @@ font-weight: 400; line-height: 18px; display: block; + margin-bottom: 16px; } img { padding: 0; @@ -785,22 +787,30 @@ margin: 0px; } } - [purpose='slack-cta'] { - padding-top: 40px; - padding-bottom: 40px; - margin-bottom: 40px; - border-top: 1px solid @core-fleet-black-25; - border-bottom: 1px solid @core-fleet-black-25; - h3 { - margin-top: 0; - margin-bottom: 24px; - padding-bottom: 0; - border-bottom: 0; + + [purpose='docs-start-cta'] { + margin-top: 40px; + border: 1px solid @core-fleet-black-10; + border-radius: 16px; + overflow: hidden; + [purpose='docs-start-cta-image'] { + height: 180px; + background-image: url('/images/docs-cta-stage-1-314x180@2x.png'); + background-size: cover; + background-position-y: center; } - } - [purpose='improve-docs'] { - h3 { - border-bottom: 0; + [purpose='docs-start-cta-content'] { + h2 { + padding: 0; + margin-bottom: 4px; + } + p { + margin-bottom: 12px; + } + } + [purpose='continue-button'] { + font-size: 14px; + padding: 6px 12px; } } [purpose='checklist-item'] { diff --git a/website/config/routes.js b/website/config/routes.js index c7f446ab32..5cef225964 100644 --- a/website/config/routes.js +++ b/website/config/routes.js @@ -312,7 +312,6 @@ module.exports.routes = { 'GET /handbook/customer-experience': '/handbook/customers', 'GET /handbook/brand': '/handbook/digital-experience', 'GET /guides/deploying-fleet-on-aws-with-terraform': '/deploy/deploying-fleet-on-aws-with-terraform', - 'GET /guides/deploy-fleet-on-hetzner-cloud':'/deploy/deploy-fleet-on-hetzner-cloud', 'GET /guides/deploying-fleet-on-render': '/deploy/deploying-fleet-on-render', 'GET /use-cases/correlate-network-connections-with-community-id-in-osquery': '/guides/correlate-network-connections-with-community-id-in-osquery', 'GET /use-cases/converting-unix-timestamps-with-osquery': '/guides/converting-unix-timestamps-with-osquery', diff --git a/website/views/pages/docs/basic-documentation.ejs b/website/views/pages/docs/basic-documentation.ejs index 099001c3c3..1f965802fc 100644 --- a/website/views/pages/docs/basic-documentation.ejs +++ b/website/views/pages/docs/basic-documentation.ejs @@ -203,21 +203,14 @@ ) ) %> -
-

Need more help?

-
- - Slack logo - Ask the community on Slack - +
+
+
+

Try it out

+

See what Fleet can do

+ Start now
-
-

Did we miss anything?

-

- If you notice something we've missed or could be improved on, please follow this link and submit a pull request to the Fleet repo. -

-
diff --git a/website/views/pages/homepage.ejs b/website/views/pages/homepage.ejs index b9e567f0cc..7e66888d80 100644 --- a/website/views/pages/homepage.ejs +++ b/website/views/pages/homepage.ejs @@ -7,7 +7,7 @@ <%/* Hero text */%>

<%- partial('../partials/primary-tagline.partial.ejs') %>

-

Replace the sprawl with open-source code that works the way you want.

+

Replace the sprawl with open-source <%= primaryBuyingSituation === 'mdm'? 'MDM' : primaryBuyingSituation === 'vm'? 'reporting' : 'software' %> that works the way you want.