import React, { Component } from "react"; import PropTypes from "prop-types"; import FileSaver from "file-saver"; import Fleet from "fleet"; import Button from "components/buttons/Button"; import configInterface from "interfaces/config"; import teamInterface from "interfaces/team"; import userInterface from "interfaces/user"; import permissionUtils from "utilities/permissions"; import EnrollSecretTable from "components/config/EnrollSecretTable"; import FleetIcon from "components/icons/FleetIcon"; import Dropdown from "components/forms/fields/Dropdown"; import InputField from "components/forms/fields/InputField"; import { stringToClipboard } from "utilities/copy_text"; import DownloadIcon from "../../../../../../assets/images/icon-download-12x12@2x.png"; const baseClass = "add-host-modal"; const NO_TEAM_OPTION = { value: "no-team", label: "No team", }; class AddHostModal extends Component { static propTypes = { teams: PropTypes.arrayOf(teamInterface), onReturnToApp: PropTypes.func, config: configInterface, currentUser: userInterface, }; constructor(props) { super(props); this.userRole = { isAnyTeamMaintainer: permissionUtils.isAnyTeamMaintainer( this.props.currentUser ), isGlobalAdmin: permissionUtils.isGlobalAdmin(this.props.currentUser), isGlobalMaintainer: permissionUtils.isGlobalMaintainer( this.props.currentUser ), }; this.currentUserTeams = this.userRole.isAnyTeamMaintainer ? Object.values(this.props.currentUser.teams).filter( (team) => team.role === "maintainer" ) : this.props.teams; this.teamSecrets = this.props.teams ? Object.values(this.props.teams).map((team) => { return { id: team.id, name: team.name, secrets: team.secrets }; }) : []; this.state = { fetchCertificateError: undefined, selectedTeam: null, globalSecrets: [], selectedEnrollSecrets: [], copyMessage: "", }; } componentDidMount() { const { isGlobalAdmin, isGlobalMaintainer } = this.userRole; (() => { if (isGlobalAdmin || isGlobalMaintainer) { Fleet.config .loadEnrollSecret() .then((response) => { this.setState({ globalSecrets: response.spec.secrets, selectedTeam: { id: NO_TEAM_OPTION.value }, // Reset initial selectedTeam value to "no-team" in the case of global users }); }) .catch((err) => { console.log(err); }); } else { this.setState({ selectedTeam: this.currentUserTeams[0] }); } })(); Fleet.config .loadCertificate() .then((certificate) => { this.setState({ certificate }); }) .catch(() => { this.setState({ fetchCertificateError: "Failed to load certificate. Is Fleet app URL configured properly?", }); }); } onFetchCertificate = (evt) => { evt.preventDefault(); const { certificate } = this.state; const filename = "fleet.pem"; const file = new global.window.File([certificate], filename, { type: "application/x-pem-file", }); FileSaver.saveAs(file); return false; }; onCopyRunCommand = (evt) => { evt.preventDefault(); stringToClipboard("osqueryd --flagfile=flagfile.txt --verbose") .then(() => this.setState({ copyMessage: "Copied!" })) .catch(() => this.setState({ copyMessage: "Copy failed" })); // Clear message after 1 second setTimeout(() => this.setState({ copyMessage: "" }), 1000); return false; }; // if isGlobalAdmin or isGlobalMaintainer, we include a "No team" option and reveal globalSecrets // if not, we pull secrets for the user's teams from the teamsSecrets onChangeSelectTeam = (teamId) => { const { globalSecrets } = this.state; const { currentUserTeams, teamSecrets } = this; if (teamId === "no-team") { this.setState({ selectedTeam: { id: NO_TEAM_OPTION.value } }); this.setState({ selectedEnrollSecrets: globalSecrets || [] }); } else { const selectedTeam = currentUserTeams.find((team) => team.id === teamId); const selectedEnrollSecrets = teamSecrets.find((e) => e.id === selectedTeam.id)?.secrets || ""; this.setState({ selectedTeam }); this.setState({ selectedEnrollSecrets, }); } }; getSelectedEnrollSecrets = (selectedTeam) => { if (selectedTeam.id === NO_TEAM_OPTION.value) { return this.state.globalSecrets; } return ( this.teamSecrets.find((e) => e.id === selectedTeam.id)?.secrets || "" ); }; createTeamDropdownOptions = (currentUserTeams = []) => { const teamOptions = currentUserTeams.map((team) => { return { value: team.id, label: team.name, }; }); return this.userRole.isAnyTeamMaintainer ? teamOptions : [NO_TEAM_OPTION, ...teamOptions]; }; renderRunCommandLabel = () => { const { copyMessage } = this.state; const { onCopyRunCommand } = this; return ( {copyMessage && {`${copyMessage} `}} ); }; render() { const { config, onReturnToApp } = this.props; const { fetchCertificateError, selectedTeam, globalSecrets } = this.state; const { createTeamDropdownOptions, currentUserTeams, getSelectedEnrollSecrets, onChangeSelectTeam, renderRunCommandLabel, } = this; const isPremiumTier = permissionUtils.isPremiumTier(config); let tlsHostname = config.server_url; try { const serverUrl = new URL(config.server_url); tlsHostname = serverUrl.hostname; if (serverUrl.port) { tlsHostname += `:${serverUrl.port}`; } } catch (e) { if (!(e instanceof TypeError)) { throw e; } } const flagfileContent = `# Server --tls_hostname=${tlsHostname} --tls_server_certs=fleet.pem # Enrollment --host_identifier=instance --enroll_secret_path=secret.txt --enroll_tls_endpoint=/api/v1/osquery/enroll # Configuration --config_plugin=tls --config_tls_endpoint=/api/v1/osquery/config --config_refresh=10 # Live query --disable_distributed=false --distributed_plugin=tls --distributed_interval=10 --distributed_tls_max_attempts=3 --distributed_tls_read_endpoint=/api/v1/osquery/distributed/read --distributed_tls_write_endpoint=/api/v1/osquery/distributed/write # Logging --logger_plugin=tls --logger_tls_endpoint=/api/v1/osquery/log --logger_tls_period=10 # File carving --disable_carver=false --carver_start_endpoint=/api/v1/osquery/carve/begin --carver_continue_endpoint=/api/v1/osquery/carve/block --carver_block_size=2000000`; const onDownloadFlagfile = (evt) => { evt.preventDefault(); const filename = "flagfile.txt"; const file = new global.window.File([flagfileContent], filename); FileSaver.saveAs(file); return false; }; return (

Add Hosts Documentation

  1. 1Enroll secret

    Osquery uses an enroll secret to authenticate with the Fleet server.

    {isPremiumTier && currentUserTeams ? ( ) : null} {isPremiumTier && selectedTeam && ( )} {!isPremiumTier && ( )}
  2. 2Server certificate

    Provide the TLS certificate used by the Fleet server to enable secure connections from osquery:

    {fetchCertificateError ? ( {fetchCertificateError} ) : ( Download download icon )}

  3. 3Flagfile

    If using the enroll secret and server certificate downloaded above, use the generated flagfile. In some configurations, modifications may need to be made:

    Download download icon

  4. 4Run osquery

    Run osquery from the directory containing the above files (may require sudo or Run as Administrator privileges):

); } } export default AddHostModal;