Improve "Add New Host" dialog (#8)

- Allow download of enroll secret, server certificate, and flagfile.
- Hosts can now be added following instructions directly.
This commit is contained in:
Zachary Wasserman
2020-11-04 17:07:58 -08:00
committed by GitHub
parent 7c923d9d19
commit a36bef6c16
7 changed files with 244 additions and 93 deletions
@@ -1,5 +1,6 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import FileSaver from 'file-saver';
import Button from 'components/buttons/Button';
import enrollSecretInterface from 'interfaces/enroll_secret';
@@ -35,6 +36,20 @@ class EnrollSecretRow extends Component {
return false;
}
onDownloadSecret = (evt) => {
evt.preventDefault();
const { secret } = this.props;
const filename = 'secret.txt';
const file = new global.window.File([secret], filename);
FileSaver.saveAs(file);
return false;
}
onToggleSecret = (evt) => {
evt.preventDefault();
@@ -47,7 +62,7 @@ class EnrollSecretRow extends Component {
renderLabel = () => {
const { name } = this.props;
const { showSecret, copyMessage } = this.state;
const { onCopySecret, onToggleSecret } = this;
const { onCopySecret, onDownloadSecret, onToggleSecret } = this;
return (
<span>
@@ -61,6 +76,16 @@ class EnrollSecretRow extends Component {
>
<Icon name="clipboard" />
</Button>
|
<a
href="#onDownloadSecret"
variant="unstyled"
className={`${baseClass}__secret-download-icon`}
onClick={onDownloadSecret}
>
Download
</a>
|
<a
href="#showSecret"
onClick={onToggleSecret}
@@ -1,70 +1,155 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import FileSaver from 'file-saver';
import Kolide from 'kolide';
import Button from 'components/buttons/Button';
import configInterface from 'interfaces/config';
import enrollSecretInterface from 'interfaces/enroll_secret';
import EnrollSecretTable from 'components/config/EnrollSecretTable';
import Icon from 'components/icons/Icon';
import certificate from '../../../../assets/images/osquery-certificate.svg';
const baseClass = 'add-host-modal';
class AddHostModal extends Component {
static propTypes = {
onFetchCertificate: PropTypes.func,
onReturnToApp: PropTypes.func,
enrollSecret: enrollSecretInterface,
config: configInterface,
};
constructor(props) {
super(props);
this.state = { fetchCertificateError: undefined };
}
componentDidMount() {
Kolide.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;
}
render() {
const {
onFetchCertificate,
config,
onReturnToApp,
enrollSecret,
} = this.props;
const { fetchCertificateError } = this.state;
let tlsHostname = config.kolide_server_url;
try {
const serverUrl = new URL(config.kolide_server_url);
tlsHostname = serverUrl.hostname;
if (serverUrl.port) {
tlsHostname += `:${serverUrl.port}`;
}
} catch (e) {
if (!(e instanceof TypeError)) {
throw e;
}
}
const flagfileContent = `--enroll_secret_path=secret.txt
--tls_server_certs=fleet.pem
--tls_hostname=${tlsHostname}
--host_identifier=uuid
--enroll_tls_endpoint=/api/v1/osquery/enroll
--config_plugin=tls
--config_tls_endpoint=/api/v1/osquery/config
--config_refresh=10
--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
--logger_plugin=tls
--logger_tls_endpoint=/api/v1/osquery/log
--logger_tls_period=10`;
const onDownloadFlagfile = (evt) => {
evt.preventDefault();
const filename = 'flagfile.txt';
const file = new global.window.File([flagfileContent], filename);
FileSaver.saveAs(file);
return false;
};
return (
<div className={baseClass}>
<p>
Follow the instructions below to add hosts to your Fleet Instance.
</p>
<div className={`${baseClass}__manual-install-content`}>
<div className={`${baseClass}__documentation-link`}>
<h4>
<a
href="https://github.com/kolide/fleet/blob/master/docs/infrastructure/adding-hosts-to-fleet.md"
target="_blank"
rel="noopener noreferrer"
>
Add Hosts Documentation <Icon name="external-link" />
</a>
</h4>
</div>
<ol className={`${baseClass}__install-steps`}>
<li>
<h4>
<a
href="https://github.com/kolide/fleet/blob/master/docs/infrastructure/adding-hosts-to-fleet.md"
target="_blank"
rel="noopener noreferrer"
>
Fleet / Osquery - Install Docs <Icon name="external-link" />
</a>
</h4>
</li>
<li>
<h4>Osquery Enroll Secret</h4>
<h4>1. Enroll Secret</h4>
<p>
Provide osquery with one of the following active enroll secrets:
Provide an active enroll secret to allow osquery to authenticate with the Fleet server:
</p>
<div className={`${baseClass}__secret-wrapper`}>
<EnrollSecretTable secrets={enrollSecret} />
</div>
</li>
<li>
<h4>Download Server Certificate (Optional)</h4>
<h4>2. Server Certificate</h4>
<p>
If you use the native osquery TLS plugins, Osquery requires the
same TLS certificate that Fleet is using in order to
authenticate. You can fetch the certificate below:
Provide the TLS certificate used by the Fleet server to enable secure connections from osquery:
</p>
<p className={`${baseClass}__download-cert`}>
<Button variant="unstyled" onClick={onFetchCertificate}>
<img src={certificate} alt="" />
<span>Fetch Fleet Certificate</span>
</Button>
<p>
{ fetchCertificateError
? <span className={`${baseClass}__error`}>{fetchCertificateError}</span>
: <a href="#downloadCertificate" onClick={this.onFetchCertificate}>Download Certificate</a>
}
</p>
</li>
<li>
<h4>3. Flagfile</h4>
<p>
If using the enroll secret and server certificate downloaded above, use the generated flagfile. In some configurations, modifications may need to be made:
</p>
<p>
<a href="#downloadFlagfile" onClick={onDownloadFlagfile}>Download Flagfile</a>
</p>
</li>
<li>
<h4>4. Run Osquery</h4>
<p>
Run osquery from the directory containing the above files (may require sudo or Run as Administrator privileges):
</p>
<pre>osqueryd --flagfile=flagfile.txt --verbose</pre>
</li>
</ol>
</div>
@@ -46,14 +46,14 @@
margin-left: 10px;
padding: 20px;
box-sizing: border-box;
h4 {
font-size: 16px;
font-weight: $bold;
line-height: 1.5;
letter-spacing: -0.5px;
color: rgba(32, 37, 50, 0.66);
margin: 40px 0 0;
margin: 10px 0 0;
.kolidecon {
margin-left: 5px;
@@ -75,6 +75,12 @@
}
}
&__documentation-link {
h4 {
margin: 0;
}
}
&__install-steps {
margin: 0;
padding: 0;
@@ -134,4 +140,14 @@
color: $link;
}
}
pre, code {
background-color: #f9f9f9;
}
&__error {
color: $alert;
}
}
@@ -2,14 +2,13 @@ import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';
import AceEditor from 'react-ace';
import { connect } from 'react-redux';
import FileSaver from 'file-saver';
import { push } from 'react-router-redux';
import { sortBy } from 'lodash';
import classNames from 'classnames';
import Kolide from 'kolide';
import AddHostModal from 'components/hosts/AddHostModal';
import Button from 'components/buttons/Button';
import configInterface from 'interfaces/config';
import HostContainer from 'components/hosts/HostContainer';
import HostPagination from 'components/hosts/HostPagination';
import HostSidePanel from 'components/side_panels/HostSidePanel';
@@ -45,6 +44,7 @@ const baseClass = 'manage-hosts';
export class ManageHostsPage extends PureComponent {
static propTypes = {
config: configInterface,
dispatch: PropTypes.func,
display: PropTypes.oneOf(['Grid', 'List']),
hosts: PropTypes.arrayOf(hostInterface),
@@ -155,18 +155,6 @@ export class ManageHostsPage extends PureComponent {
.catch(() => false);
}
onFetchCertificate = () => {
return Kolide.config.loadCertificate()
.then((certificate) => {
const filename = `${global.window.location.host}.pem`;
const file = new global.window.File([certificate], filename, { type: 'application/x-pem-file' });
FileSaver.saveAs(file);
return false;
});
}
onLabelClick = (selectedLabel) => {
return (evt) => {
evt.preventDefault();
@@ -304,9 +292,9 @@ export class ManageHostsPage extends PureComponent {
}
renderAddHostModal = () => {
const { onFetchCertificate, toggleAddHostModal } = this;
const { toggleAddHostModal } = this;
const { showAddHostModal } = this.state;
const { enrollSecret } = this.props;
const { enrollSecret, config } = this.props;
if (!showAddHostModal) {
return false;
@@ -319,9 +307,9 @@ export class ManageHostsPage extends PureComponent {
className={`${baseClass}__invite-modal`}
>
<AddHostModal
onFetchCertificate={onFetchCertificate}
onReturnToApp={toggleAddHostModal}
enrollSecret={enrollSecret}
config={config}
/>
</Modal>
);
@@ -668,6 +656,7 @@ const mapStateToProps = (state, { location, params }) => {
const { errors: labelErrors, loading: loadingLabels } = state.entities.labels;
const { loading: loadingHosts } = state.entities.hosts;
const enrollSecret = state.app.enrollSecret;
const config = state.app.config;
return {
selectedFilter,
@@ -684,6 +673,7 @@ const mapStateToProps = (state, { location, params }) => {
selectedLabel,
selectedOsqueryTable,
statusLabels,
config,
};
};
@@ -7,7 +7,7 @@ import hostActions from 'redux/nodes/entities/hosts/actions';
import labelActions from 'redux/nodes/entities/labels/actions';
import ConnectedManageHostsPage, { ManageHostsPage } from 'pages/hosts/ManageHostsPage/ManageHostsPage';
import { connectedComponent, createAceSpy, reduxMockStore, stubbedOsqueryTable } from 'test/helpers';
import { hostStub } from 'test/stubs';
import { hostStub, configStub } from 'test/stubs';
import * as manageHostsPageActions from 'redux/nodes/components/ManageHostsPage/actions';
const allHostsLabel = { id: 1, display_text: 'All Hosts', slug: 'all-hosts', type: 'all', count: 22 };
@@ -49,6 +49,7 @@ const mockStore = reduxMockStore({
describe('ManageHostsPage - component', () => {
const props = {
config: configStub,
dispatch: noop,
hosts: [],
labels: [],
+27 -17
View File
@@ -1,19 +1,29 @@
-----BEGIN CERTIFICATE-----
MIIDBjCCAe4CCQDjM7ghDw9OgDANBgkqhkiG9w0BAQsFADBFMQ4wDAYDVQQKDAVG
bGVldDEUMBIGA1UECwwLRGV2ZWxvcG1lbnQxHTAbBgNVBAMMFGhvc3QuZG9ja2Vy
LmludGVybmFsMB4XDTE5MDMyNjE5MTQyN1oXDTQ2MDgxMDE5MTQyN1owRTEOMAwG
A1UECgwFRmxlZXQxFDASBgNVBAsMC0RldmVsb3BtZW50MR0wGwYDVQQDDBRob3N0
LmRvY2tlci5pbnRlcm5hbDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
AOnDxrXtuB3VpmsExCVXboF0Llcfee4FF4Y0ifS75RjnLSXupZubQWHenBGmQ7qb
3eTBuw59HiTBkB5CZb79FpFyYrxMpXn+7dVJt2N9NnpKkVidqFXaVqTS9P84JQtt
LIPzyQlRFM482Y0RVfY1UnrWUTqze/wUpJP6xY+aPseIbnYvbsGtZ8PjrLMN+6z8
f2Ef8Hr6uIR2mQe+qu43HuEkjbTnRi1ORj2fr4cSgoVd7FMhIy1yGEaUP4aerB3X
k1h4eOJa/Vd2eE3b3vXg7awAtdPJDWz4LjQCkgzeI5tej+TooAHM0gyun7EPSy+9
2fgkAkEvY/dco6h+D0ULuNECAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAEhGq1t5G
ZyhXEYJrd92HHi6zyRQNlAoQ55utmE/tlNAq1e2k1uEKEeOmIFvaLpQ3ky2z1Aok
Qrn4+TYFcNN6vB99QxNVb3+hDm+hyKj0N7VvvYIX8ms/AUKZUaUahxM4gXDNky35
l33IYgGmdcSzBiTkTCGABmtHwJ/QAPI1v6iWGXLQ42HNfCCflFHt5AtOs7Nfv0u5
JsgkMVu+Vj9sOcdE+UtAJ/NQyV+kWFgzUtxdX8QX/NQVFqlNPVV4cRi8KMbBM1q+
xN2o1vC27vM+5VWTr5sddU6MVJJXIIP4wfe+qbCnD8GE5+7j8XiUrOMsauiafaW2
cdSv0nZ/WcEovw==
MIIE5jCCAs6gAwIBAgIJAKq0+FAVArUhMA0GCSqGSIb3DQEBCwUAMBUxEzARBgNV
BAMMCkZsZWV0IFRlc3QwHhcNMjAxMDE1MTg1ODE5WhcNMzAwNzE1MTg1ODE5WjAV
MRMwEQYDVQQDDApGbGVldCBUZXN0MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC
CgKCAgEAttQ62lpMq48/XjQFxYg47D2fgKgTBMjDNSfCt9VpqE3xPnybmWo8VZtk
jmrFM50IhagyYSjvl9iqdrnsl3ZV8KYbWEy6849zDYF1SudmC7/pJyH7QvpKgL7V
4McM62jM905hyFy9KZTAlFiaeezWSWJre7kHsK2u5tsqS6ElatEZmF59sInixaRw
RqVxOhtDm7Sl1c5xKWM6phWoJfykspFpu5J6N2jRQXCfzBYoQWN76OohGouit/BQ
C1xvm+f7wgGZgbbfDjUoHAe9Yhd3XzZsYTgMDt/SRJRDnxFZwo8BAkY9yJm7f3dQ
AEhgJ66KbyoxITdgma1hgmeWibZY4hVymcRxB1B3RNN2at19RNy2J+brMxlG0KZk
nD77EqidrwLAlYcdeU3yLjt0vYPxT+RW7l1jiZlVi/oaykAmVfOhWnTnTwbsYs7O
UMyMyYHQECEs98ex7wrjThIBJScqhsSN1ipAxr5RgaDr+U5IR+tLhMewBy8So+nf
2YuMhLfkCgoY80ELhz5F8avts5hksB0hqnNYr+Nlwm6eXqEPZSzFJmdc1IbmWzq2
7UH1OQmBFF2qr2j/8dcM+oPNgjrEEQjtyW0S4j2PhjSEbINgcwu0AaABssLI80Vm
Gp1TjUGA92rMwIjlAtcUUB5FOKSS8vAXb1VcDWMkybh9sHj4Z0ECAwEAAaM5MDcw
NQYDVR0RBC4wLIIJbG9jYWxob3N0ghRob3N0LmRvY2tlci5pbnRlcm5hbIIJMTI3
LjAuMC4xMA0GCSqGSIb3DQEBCwUAA4ICAQBZOY++LNRTVG8XlQGVlOloEKA2WY3P
gXKJLSM7xWSxj2co0v+noyhoInHT7ysub8en59Et+vN53/0OobxNIdEKDUdqs38R
F++Oy6s/MhFHSo87F06t+W91/60ub4iFRHfev1qeNFV6Yzv9sFJ5LpXLFk+qVDb8
pPyFFE1bXjctDPjD5gUj+Y34XikVKzMb7xddWCNs34v1KCaCBW7kkfefxiZiDR6g
lCEkDzp6xaLS898oCbfFakjr4bvOgBP1IqXLIDLPMhivaxNAooHTtu/3ezp7puix
TSDkjlkStDtEFw/wjyaMcEkk51Gs1ponBbADLRxQ50AHDWk/4vy8GcIVc6CdVEOA
Zw12FN06C4Jviiiv6uCXZ6iZ+V+pjGiGmSNYF+kruUs8BfrJIB89lqxpdQ4Kx01j
AuSFvjRRvIPmvApSdKEjLcY3AYRivXsB/hASMBbjh/p1f/JzSJdxoqSvONhNQJuh
+wcdNVQhGAv3kkLn/HMHTBl2Ur+9tQaJrnR1tWl1IzwLRJIi0Soyp/q5ZjQyFglj
32xW83DZhtpQ2SI1QGy4AvWIPnGHZhMfav02KnKRhZdOMW4oekXRMrwiyXCqIazc
xXzAlCq8dHdP2Y9uvfFxVFyE+uSfkcPxX+DG/ZnpgCS27oKA/qLCybJamlqtveNs
RSjNe5qwGi0ifA==
-----END CERTIFICATE-----
+50 -26
View File
@@ -1,28 +1,52 @@
-----BEGIN PRIVATE KEY-----
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDpw8a17bgd1aZr
BMQlV26BdC5XH3nuBReGNIn0u+UY5y0l7qWbm0Fh3pwRpkO6m93kwbsOfR4kwZAe
QmW+/RaRcmK8TKV5/u3VSbdjfTZ6SpFYnahV2lak0vT/OCULbSyD88kJURTOPNmN
EVX2NVJ61lE6s3v8FKST+sWPmj7HiG52L27BrWfD46yzDfus/H9hH/B6+riEdpkH
vqruNx7hJI2050YtTkY9n6+HEoKFXexTISMtchhGlD+Gnqwd15NYeHjiWv1XdnhN
29714O2sALXTyQ1s+C40ApIM3iObXo/k6KABzNIMrp+xD0svvdn4JAJBL2P3XKOo
fg9FC7jRAgMBAAECggEAPPViohqFgrIuHYY2qA4usO9dDjcVEq6dPdABkaJV0bKn
IVckNHm+CQmF5UxYKRdwO7ERWGvkKm2XaWyjH7Tb573OZZAPLsr9tjrs6bLDPAyp
7lPei9TN91lTriIz9tDXZOlzqUxNgqJ3kMPOiM/K3GF6+TXIxSmU6UnhrtroOBuq
K+o/qC5K8CnqpBhBQt6qtn/YdfbcABogRbwojnHRl068nMV9kJLp7/I5hgI8Vr+v
xMXI87wbL7kglcBZweG2EL//C31eSEVG2cW+0cIefQil0ZIPWgX0z5Oex2OD+cLL
E+FIp/waZ7plCiikNNMo+gm/4uy9iupxg+z5W71NyQKBgQD2OSTe8w2Ns9zzLIXC
5ZrFTa5LmTJ8bIv7/5kCS7fSGnoDPZvaRfnqzVEoCUp5D2N09NOJIsoNGrIGnMCc
q6HwGEzGwmOgtzYu1r2jC+6jlcJW4tMr8k52CRO3y3LvsYkmt6tYIBgMVoxIupce
QyBm5zVccyIvdhYhapgcSTJrtwKBgQDzC/30lTlBWsLiqLtfHAwM3YJgdVczJrbe
WDieUd8ywTVupDOhBSGwVGmXGLQE5G/eNfwlsBq2iij7H/WdkX6mD1YmnQJUit3x
0cPmDwWMo3vGLUUUI34WJ/2WSxdTtfaIcaFlV6S9Bp9B5K208kG/cZliqBWG1mtS
a/AdsdcPtwKBgFHan2pKzNet0qc4xuMK54/uCiJxtHnur/6/cwNzXpHHYYaQRa6j
kri/NtqjdBGYzwyDk4tEeH0wwbw3AkVTPYuO2H8/AlXccnPRyctZXSUe1TODRoaW
kATncZmpVfVfROQNLTYnvTbk0tAez7wsvqnW9UNdtyCmFidXw2er4cozAoGADuPK
MCTAODfGPrqVmBMQzez4Is6tg+24QFDpTxG2+dYKXvfiTdgRo0rYmTAjPzV7gQKP
qwNp74rxTck8c+XI+4VvAriVvvYu+LSgKsT60w3k9FQrqjsua08R4xZAnJlGPD+x
4pKG/imcsh2/YpmA26iq+/dOMk+KjacdM8SEZ2sCgYB/qypw7LpskkGA5ITODv5A
cAHVCCDMGNJiR4XpdOwHNAcd6EWjB/366/MwKfV24KnZH0RZe+pU2e4dP8miHNN0
dFOpAokCD3Eh7LGDVM6bYkQabG3XTmN8g9a4V2e63n5Bl2HZx0Qp1bHEtqsOujpj
5G5maHPlTRoNlvk4BiEZDA==
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
mWZen+G8ANnds10V7TmcpoaPSSoJMF0=
-----END PRIVATE KEY-----