2016-09-26 18:48:55 +00:00
|
|
|
package service
|
2016-09-22 00:45:57 +00:00
|
|
|
|
|
|
|
import (
|
2017-03-15 15:55:30 +00:00
|
|
|
"context"
|
2017-01-11 02:00:46 +00:00
|
|
|
"fmt"
|
2019-08-02 21:08:42 +00:00
|
|
|
"html/template"
|
2017-03-22 19:40:01 +00:00
|
|
|
"strings"
|
2016-12-20 21:54:30 +00:00
|
|
|
|
2021-07-29 16:10:34 +00:00
|
|
|
"github.com/fleetdm/fleet/v4/server"
|
2022-05-19 21:28:49 +00:00
|
|
|
authz_ctx "github.com/fleetdm/fleet/v4/server/contexts/authz"
|
2021-11-22 14:13:26 +00:00
|
|
|
"github.com/fleetdm/fleet/v4/server/contexts/ctxerr"
|
2021-06-26 04:46:51 +00:00
|
|
|
"github.com/fleetdm/fleet/v4/server/contexts/viewer"
|
|
|
|
"github.com/fleetdm/fleet/v4/server/fleet"
|
|
|
|
"github.com/fleetdm/fleet/v4/server/mail"
|
2022-04-06 11:55:25 +00:00
|
|
|
"github.com/fleetdm/fleet/v4/server/service/externalsvc"
|
2016-09-22 00:45:57 +00:00
|
|
|
)
|
|
|
|
|
2017-01-11 02:00:46 +00:00
|
|
|
// mailError is set when an error performing mail operations
|
|
|
|
type mailError struct {
|
|
|
|
message string
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e mailError) Error() string {
|
|
|
|
return fmt.Sprintf("a mail error occurred: %s", e.message)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e mailError) MailError() []map[string]string {
|
|
|
|
return []map[string]string{
|
2021-06-03 23:24:15 +00:00
|
|
|
{
|
2017-01-11 02:00:46 +00:00
|
|
|
"name": "base",
|
|
|
|
"reason": e.message,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-08-20 15:27:41 +00:00
|
|
|
func (svc *Service) NewAppConfig(ctx context.Context, p fleet.AppConfig) (*fleet.AppConfig, error) {
|
2021-06-03 23:24:15 +00:00
|
|
|
// skipauth: No user context yet when the app config is first created.
|
|
|
|
svc.authz.SkipAuthorization(ctx)
|
|
|
|
|
2021-09-14 12:11:07 +00:00
|
|
|
newConfig, err := svc.ds.NewAppConfig(ctx, &p)
|
2016-09-22 00:45:57 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2020-05-29 16:12:39 +00:00
|
|
|
|
|
|
|
// Set up a default enroll secret
|
2021-07-19 18:08:41 +00:00
|
|
|
secret, err := server.GenerateRandomText(fleet.EnrollSecretDefaultLength)
|
2020-05-29 16:12:39 +00:00
|
|
|
if err != nil {
|
2021-11-22 14:13:26 +00:00
|
|
|
return nil, ctxerr.Wrap(ctx, err, "generate enroll secret string")
|
2020-05-29 16:12:39 +00:00
|
|
|
}
|
2021-06-06 22:07:29 +00:00
|
|
|
secrets := []*fleet.EnrollSecret{
|
2021-05-31 16:02:05 +00:00
|
|
|
{
|
|
|
|
Secret: secret,
|
2020-05-29 16:12:39 +00:00
|
|
|
},
|
|
|
|
}
|
2021-09-14 12:11:07 +00:00
|
|
|
err = svc.ds.ApplyEnrollSecrets(ctx, nil, secrets)
|
2020-05-29 16:12:39 +00:00
|
|
|
if err != nil {
|
2021-11-22 14:13:26 +00:00
|
|
|
return nil, ctxerr.Wrap(ctx, err, "save enroll secret")
|
2020-05-29 16:12:39 +00:00
|
|
|
}
|
|
|
|
|
2016-11-04 20:44:38 +00:00
|
|
|
return newConfig, nil
|
2016-09-22 00:45:57 +00:00
|
|
|
}
|
|
|
|
|
2021-06-06 22:07:29 +00:00
|
|
|
func (svc *Service) sendTestEmail(ctx context.Context, config *fleet.AppConfig) error {
|
2017-01-11 08:27:09 +00:00
|
|
|
vc, ok := viewer.FromContext(ctx)
|
|
|
|
if !ok {
|
2021-06-06 22:07:29 +00:00
|
|
|
return fleet.ErrNoContext
|
2016-09-22 00:45:57 +00:00
|
|
|
}
|
2017-01-11 04:41:58 +00:00
|
|
|
|
2021-06-06 22:07:29 +00:00
|
|
|
testMail := fleet.Email{
|
2019-01-24 17:39:32 +00:00
|
|
|
Subject: "Hello from Fleet",
|
2017-01-11 08:27:09 +00:00
|
|
|
To: []string{vc.User.Email},
|
2020-03-30 02:22:04 +00:00
|
|
|
Mailer: &mail.SMTPTestMailer{
|
2021-08-20 15:27:41 +00:00
|
|
|
BaseURL: template.URL(config.ServerSettings.ServerURL + svc.config.Server.URLPrefix),
|
2019-10-16 23:40:45 +00:00
|
|
|
AssetURL: getAssetURL(),
|
2017-01-11 08:27:09 +00:00
|
|
|
},
|
|
|
|
Config: config,
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := mail.Test(svc.mailService, testMail); err != nil {
|
|
|
|
return mailError{message: err.Error()}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
2016-12-22 14:12:34 +00:00
|
|
|
|
2022-04-06 11:55:25 +00:00
|
|
|
func (svc *Service) makeTestJiraRequest(ctx context.Context, jiraSettings *fleet.JiraIntegration) error {
|
2022-05-02 20:58:34 +00:00
|
|
|
if jiraSettings.APIToken == "" || jiraSettings.APIToken == "********" {
|
2022-05-31 10:19:57 +00:00
|
|
|
return &badRequestError{message: "jira integration request failed: missing or invalid API token"}
|
2022-05-02 20:58:34 +00:00
|
|
|
}
|
2022-04-06 11:55:25 +00:00
|
|
|
client, err := externalsvc.NewJiraClient(&externalsvc.JiraOptions{
|
|
|
|
BaseURL: jiraSettings.URL,
|
|
|
|
BasicAuthUsername: jiraSettings.Username,
|
2022-04-12 14:56:05 +00:00
|
|
|
BasicAuthPassword: jiraSettings.APIToken,
|
2022-04-06 11:55:25 +00:00
|
|
|
ProjectKey: jiraSettings.ProjectKey,
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return &badRequestError{message: fmt.Sprintf("jira integration request failed: %s", err.Error())}
|
|
|
|
}
|
|
|
|
if _, err := client.GetProject(ctx); err != nil {
|
|
|
|
return &badRequestError{message: fmt.Sprintf("jira integration request failed: %s", err.Error())}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2022-05-02 20:58:34 +00:00
|
|
|
func (svc *Service) makeTestZendeskRequest(ctx context.Context, zendeskSettings *fleet.ZendeskIntegration) error {
|
|
|
|
if zendeskSettings.APIToken == "" || zendeskSettings.APIToken == "********" {
|
2022-05-31 10:19:57 +00:00
|
|
|
return &badRequestError{message: "zendesk integration request failed: missing or invalid API token"}
|
2022-05-02 20:58:34 +00:00
|
|
|
}
|
|
|
|
client, err := externalsvc.NewZendeskClient(&externalsvc.ZendeskOptions{
|
|
|
|
URL: zendeskSettings.URL,
|
|
|
|
Email: zendeskSettings.Email,
|
|
|
|
APIToken: zendeskSettings.APIToken,
|
|
|
|
GroupID: zendeskSettings.GroupID,
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return &badRequestError{message: fmt.Sprintf("zendesk integration request failed: %s", err.Error())}
|
|
|
|
}
|
|
|
|
grp, err := client.GetGroup(ctx)
|
|
|
|
if err != nil {
|
|
|
|
return &badRequestError{message: fmt.Sprintf("zendesk integration request failed: %s", err.Error())}
|
|
|
|
}
|
|
|
|
if grp.ID != zendeskSettings.GroupID {
|
|
|
|
return &badRequestError{message: fmt.Sprint("zendesk integration request failed: no matching group id", grp.ID, zendeskSettings.GroupID)}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2017-03-22 19:40:01 +00:00
|
|
|
func cleanupURL(url string) string {
|
|
|
|
return strings.TrimRight(strings.Trim(url, " \t\n"), "/")
|
|
|
|
}
|
|
|
|
|
2021-06-06 22:07:29 +00:00
|
|
|
func (svc *Service) License(ctx context.Context) (*fleet.LicenseInfo, error) {
|
2022-05-19 21:28:49 +00:00
|
|
|
if !svc.authz.IsAuthenticatedWith(ctx, authz_ctx.AuthnDeviceToken) {
|
|
|
|
if err := svc.authz.Authorize(ctx, &fleet.AppConfig{}, fleet.ActionRead); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2021-06-03 23:24:15 +00:00
|
|
|
}
|
|
|
|
|
2021-05-20 00:29:38 +00:00
|
|
|
return &svc.license, nil
|
|
|
|
}
|
2021-06-03 23:24:15 +00:00
|
|
|
|
|
|
|
func (svc *Service) SetupRequired(ctx context.Context) (bool, error) {
|
2021-09-14 12:11:07 +00:00
|
|
|
users, err := svc.ds.ListUsers(ctx, fleet.UserListOptions{ListOptions: fleet.ListOptions{Page: 0, PerPage: 1}})
|
2021-06-03 23:24:15 +00:00
|
|
|
if err != nil {
|
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
if len(users) == 0 {
|
|
|
|
return true, nil
|
|
|
|
}
|
|
|
|
return false, nil
|
|
|
|
}
|
2021-07-30 15:45:49 +00:00
|
|
|
|
2021-08-24 20:24:52 +00:00
|
|
|
func (svc *Service) UpdateIntervalConfig(ctx context.Context) (*fleet.UpdateIntervalConfig, error) {
|
2021-09-27 19:27:38 +00:00
|
|
|
return &fleet.UpdateIntervalConfig{
|
|
|
|
OSQueryDetail: svc.config.Osquery.DetailUpdateInterval,
|
|
|
|
OSQueryPolicy: svc.config.Osquery.PolicyUpdateInterval,
|
|
|
|
}, nil
|
2021-08-24 20:24:52 +00:00
|
|
|
}
|
|
|
|
|
2021-10-07 13:19:10 +00:00
|
|
|
func (svc *Service) VulnerabilitiesConfig(ctx context.Context) (*fleet.VulnerabilitiesConfig, error) {
|
|
|
|
return &fleet.VulnerabilitiesConfig{
|
2022-04-13 16:05:57 +00:00
|
|
|
DatabasesPath: svc.config.Vulnerabilities.DatabasesPath,
|
|
|
|
Periodicity: svc.config.Vulnerabilities.Periodicity,
|
|
|
|
CPEDatabaseURL: svc.config.Vulnerabilities.CPEDatabaseURL,
|
|
|
|
CVEFeedPrefixURL: svc.config.Vulnerabilities.CVEFeedPrefixURL,
|
|
|
|
CurrentInstanceChecks: svc.config.Vulnerabilities.CurrentInstanceChecks,
|
|
|
|
DisableDataSync: svc.config.Vulnerabilities.DisableDataSync,
|
|
|
|
RecentVulnerabilityMaxAge: svc.config.Vulnerabilities.RecentVulnerabilityMaxAge,
|
2021-10-07 13:19:10 +00:00
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
2021-07-30 15:45:49 +00:00
|
|
|
func (svc *Service) LoggingConfig(ctx context.Context) (*fleet.Logging, error) {
|
|
|
|
if err := svc.authz.Authorize(ctx, &fleet.AppConfig{}, fleet.ActionRead); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
conf := svc.config
|
|
|
|
logging := &fleet.Logging{
|
|
|
|
Debug: conf.Logging.Debug,
|
|
|
|
Json: conf.Logging.JSON,
|
|
|
|
}
|
|
|
|
|
|
|
|
switch conf.Osquery.StatusLogPlugin {
|
|
|
|
case "", "filesystem":
|
|
|
|
logging.Status = fleet.LoggingPlugin{
|
|
|
|
Plugin: "filesystem",
|
|
|
|
Config: fleet.FilesystemConfig{FilesystemConfig: conf.Filesystem},
|
|
|
|
}
|
|
|
|
case "kinesis":
|
|
|
|
logging.Status = fleet.LoggingPlugin{
|
|
|
|
Plugin: "kinesis",
|
|
|
|
Config: fleet.KinesisConfig{
|
|
|
|
Region: conf.Kinesis.Region,
|
|
|
|
StatusStream: conf.Kinesis.StatusStream,
|
|
|
|
ResultStream: conf.Kinesis.ResultStream,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
case "firehose":
|
|
|
|
logging.Status = fleet.LoggingPlugin{
|
|
|
|
Plugin: "firehose",
|
|
|
|
Config: fleet.FirehoseConfig{
|
|
|
|
Region: conf.Firehose.Region,
|
|
|
|
StatusStream: conf.Firehose.StatusStream,
|
|
|
|
ResultStream: conf.Firehose.ResultStream,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
case "lambda":
|
|
|
|
logging.Status = fleet.LoggingPlugin{
|
|
|
|
Plugin: "lambda",
|
|
|
|
Config: fleet.LambdaConfig{
|
|
|
|
Region: conf.Lambda.Region,
|
|
|
|
StatusFunction: conf.Lambda.StatusFunction,
|
|
|
|
ResultFunction: conf.Lambda.ResultFunction,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
case "pubsub":
|
|
|
|
logging.Status = fleet.LoggingPlugin{
|
|
|
|
Plugin: "pubsub",
|
|
|
|
Config: fleet.PubSubConfig{PubSubConfig: conf.PubSub},
|
|
|
|
}
|
|
|
|
case "stdout":
|
|
|
|
logging.Status = fleet.LoggingPlugin{Plugin: "stdout"}
|
2021-10-28 04:51:17 +00:00
|
|
|
case "kafkarest":
|
|
|
|
logging.Status = fleet.LoggingPlugin{
|
|
|
|
Plugin: "kafkarest",
|
|
|
|
Config: fleet.KafkaRESTConfig{
|
|
|
|
StatusTopic: conf.KafkaREST.StatusTopic,
|
|
|
|
ProxyHost: conf.KafkaREST.ProxyHost,
|
|
|
|
},
|
|
|
|
}
|
2021-07-30 15:45:49 +00:00
|
|
|
default:
|
2021-11-22 14:13:26 +00:00
|
|
|
return nil, ctxerr.Errorf(ctx, "unrecognized logging plugin: %s", conf.Osquery.StatusLogPlugin)
|
2021-07-30 15:45:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
switch conf.Osquery.ResultLogPlugin {
|
|
|
|
case "", "filesystem":
|
|
|
|
logging.Result = fleet.LoggingPlugin{
|
|
|
|
Plugin: "filesystem",
|
|
|
|
Config: fleet.FilesystemConfig{FilesystemConfig: conf.Filesystem},
|
|
|
|
}
|
|
|
|
case "kinesis":
|
|
|
|
logging.Result = fleet.LoggingPlugin{
|
|
|
|
Plugin: "kinesis",
|
|
|
|
Config: fleet.KinesisConfig{
|
|
|
|
Region: conf.Kinesis.Region,
|
|
|
|
StatusStream: conf.Kinesis.StatusStream,
|
|
|
|
ResultStream: conf.Kinesis.ResultStream,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
case "firehose":
|
|
|
|
logging.Result = fleet.LoggingPlugin{
|
|
|
|
Plugin: "firehose",
|
|
|
|
Config: fleet.FirehoseConfig{
|
|
|
|
Region: conf.Firehose.Region,
|
|
|
|
StatusStream: conf.Firehose.StatusStream,
|
|
|
|
ResultStream: conf.Firehose.ResultStream,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
case "lambda":
|
|
|
|
logging.Result = fleet.LoggingPlugin{
|
|
|
|
Plugin: "lambda",
|
|
|
|
Config: fleet.LambdaConfig{
|
|
|
|
Region: conf.Lambda.Region,
|
|
|
|
StatusFunction: conf.Lambda.StatusFunction,
|
|
|
|
ResultFunction: conf.Lambda.ResultFunction,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
case "pubsub":
|
|
|
|
logging.Result = fleet.LoggingPlugin{
|
|
|
|
Plugin: "pubsub",
|
|
|
|
Config: fleet.PubSubConfig{PubSubConfig: conf.PubSub},
|
|
|
|
}
|
|
|
|
case "stdout":
|
|
|
|
logging.Result = fleet.LoggingPlugin{
|
|
|
|
Plugin: "stdout",
|
|
|
|
}
|
2021-10-28 04:51:17 +00:00
|
|
|
case "kafkarest":
|
|
|
|
logging.Result = fleet.LoggingPlugin{
|
|
|
|
Plugin: "kafkarest",
|
|
|
|
Config: fleet.KafkaRESTConfig{
|
|
|
|
ResultTopic: conf.KafkaREST.ResultTopic,
|
|
|
|
ProxyHost: conf.KafkaREST.ProxyHost,
|
|
|
|
},
|
|
|
|
}
|
2021-07-30 15:45:49 +00:00
|
|
|
default:
|
2021-11-22 14:13:26 +00:00
|
|
|
return nil, ctxerr.Errorf(ctx, "unrecognized logging plugin: %s", conf.Osquery.ResultLogPlugin)
|
2021-07-30 15:45:49 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
return logging, nil
|
|
|
|
}
|