diff --git a/changes/14899-yara-rules b/changes/14899-yara-rules new file mode 100644 index 000000000000..eeea8751359f --- /dev/null +++ b/changes/14899-yara-rules @@ -0,0 +1 @@ +* Add capability for Fleet to serve yara rules to agents over HTTPS authenticated via node key. \ No newline at end of file diff --git a/server/datastore/mysql/app_configs.go b/server/datastore/mysql/app_configs.go index be9d0c427a47..8f8d708eb908 100644 --- a/server/datastore/mysql/app_configs.go +++ b/server/datastore/mysql/app_configs.go @@ -288,3 +288,52 @@ func (ds *Datastore) getConfigEnableDiskEncryption(ctx context.Context, teamID * } return ac.MDM.EnableDiskEncryption.Value, nil } + +func (ds *Datastore) ApplyYaraRules(ctx context.Context, rules []fleet.YaraRule) error { + return ds.withRetryTxx(ctx, func(tx sqlx.ExtContext) error { + return applyYaraRulesDB(ctx, tx, rules) + }) +} + +func applyYaraRulesDB(ctx context.Context, q sqlx.ExtContext, rules []fleet.YaraRule) error { + const delStmt = "DELETE FROM yara_rules" + if _, err := q.ExecContext(ctx, delStmt); err != nil { + return ctxerr.Wrap(ctx, err, "clear before insert") + } + + if len(rules) > 0 { + const insStmt = `INSERT INTO yara_rules (name, contents) VALUES %s` + var args []interface{} + sql := fmt.Sprintf(insStmt, strings.TrimSuffix(strings.Repeat(`(?, ?),`, len(rules)), ",")) + for _, r := range rules { + args = append(args, r.Name, r.Contents) + } + + if _, err := q.ExecContext(ctx, sql, args...); err != nil { + return ctxerr.Wrap(ctx, err, "insert yara rules") + } + } + + return nil +} + +func (ds *Datastore) GetYaraRules(ctx context.Context) ([]fleet.YaraRule, error) { + sql := "SELECT name, contents FROM yara_rules" + rules := []fleet.YaraRule{} + if err := sqlx.SelectContext(ctx, ds.reader(ctx), &rules, sql); err != nil { + return nil, ctxerr.Wrap(ctx, err, "get yara rules") + } + return rules, nil +} + +func (ds *Datastore) YaraRuleByName(ctx context.Context, name string) (*fleet.YaraRule, error) { + query := "SELECT name, contents FROM yara_rules WHERE name = ?" + rule := fleet.YaraRule{} + if err := sqlx.GetContext(ctx, ds.reader(ctx), &rule, query, name); err != nil { + if errors.Is(err, sql.ErrNoRows) { + return nil, ctxerr.Wrap(ctx, notFound("YaraRule"), "no yara rule with provided name") + } + return nil, ctxerr.Wrap(ctx, err, "get yara rule by name") + } + return &rule, nil +} diff --git a/server/datastore/mysql/app_configs_test.go b/server/datastore/mysql/app_configs_test.go index 0e8afb39ded7..30fb5cf7cb54 100644 --- a/server/datastore/mysql/app_configs_test.go +++ b/server/datastore/mysql/app_configs_test.go @@ -37,6 +37,7 @@ func TestAppConfig(t *testing.T) { {"GetConfigEnableDiskEncryption", testGetConfigEnableDiskEncryption}, {"IsEnrollSecretAvailable", testIsEnrollSecretAvailable}, {"NDESSCEPProxyPassword", testNDESSCEPProxyPassword}, + {"YaraRulesRoundtrip", testYaraRulesRoundtrip}, } for _, c := range cases { t.Run(c.name, func(t *testing.T) { @@ -533,7 +534,6 @@ func testIsEnrollSecretAvailable(t *testing.T, ds *Datastore) { }, ) } - } func testNDESSCEPProxyPassword(t *testing.T, ds *Datastore) { @@ -606,5 +606,105 @@ func testNDESSCEPProxyPassword(t *testing.T, ds *Datastore) { require.NoError(t, err) checkProxyConfig() checkPassword() +} + +func testYaraRulesRoundtrip(t *testing.T, ds *Datastore) { + ctx := context.Background() + defer TruncateTables(t, ds) + + // Empty insert + expectedRules := []fleet.YaraRule{} + err := ds.ApplyYaraRules(ctx, expectedRules) + require.NoError(t, err) + rules, err := ds.GetYaraRules(ctx) + require.NoError(t, err) + assert.Equal(t, expectedRules, rules) + + // Insert values + expectedRules = []fleet.YaraRule{ + { + Name: "wildcard.yar", + Contents: `rule WildcardExample +{ + strings: + $hex_string = { E2 34 ?? C8 A? FB } + + condition: + $hex_string +}`, + }, + { + Name: "jump.yar", + Contents: `rule JumpExample +{ + strings: + $hex_string = { F4 23 [4-6] 62 B4 } + + condition: + $hex_string +}`, + }, + } + err = ds.ApplyYaraRules(ctx, expectedRules) + require.NoError(t, err) + rules, err = ds.GetYaraRules(ctx) + require.NoError(t, err) + assert.Equal(t, expectedRules, rules) + + rule, err := ds.YaraRuleByName(ctx, expectedRules[0].Name) + require.NoError(t, err) + assert.Equal(t, &expectedRules[0], rule) + rule, err = ds.YaraRuleByName(ctx, expectedRules[1].Name) + require.NoError(t, err) + assert.Equal(t, &expectedRules[1], rule) + + // Update rules + expectedRules = []fleet.YaraRule{ + { + Name: "wildcard.yar", + Contents: `rule WildcardExample +{ + strings: + $hex_string = { E2 34 ?? C8 A? FB } + + condition: + $hex_string +}`, + }, + { + Name: "jump-modified.yar", + Contents: `rule JumpExample +{ + strings: + $hex_string = true + condition: + $hex_string +}`, + }, + } + err = ds.ApplyYaraRules(ctx, expectedRules) + require.NoError(t, err) + rules, err = ds.GetYaraRules(ctx) + require.NoError(t, err) + assert.Equal(t, expectedRules, rules) + + rule, err = ds.YaraRuleByName(ctx, expectedRules[0].Name) + require.NoError(t, err) + assert.Equal(t, &expectedRules[0], rule) + rule, err = ds.YaraRuleByName(ctx, expectedRules[1].Name) + require.NoError(t, err) + assert.Equal(t, &expectedRules[1], rule) + + // Clear rules + expectedRules = []fleet.YaraRule{} + err = ds.ApplyYaraRules(ctx, expectedRules) + require.NoError(t, err) + rules, err = ds.GetYaraRules(ctx) + require.NoError(t, err) + assert.Equal(t, expectedRules, rules) + + // Get rule that doesn't exist + rule, err = ds.YaraRuleByName(ctx, "wildcard.yar") + require.Error(t, err) } diff --git a/server/datastore/mysql/migrations/tables/20241025141856_AddYaraRulesTable.go b/server/datastore/mysql/migrations/tables/20241025141856_AddYaraRulesTable.go new file mode 100644 index 000000000000..8f31d05e5384 --- /dev/null +++ b/server/datastore/mysql/migrations/tables/20241025141856_AddYaraRulesTable.go @@ -0,0 +1,29 @@ +package tables + +import ( + "database/sql" + "fmt" +) + +func init() { + MigrationClient.AddMigration(Up_20241016155452, Down_20241016155452) +} + +func Up_20241016155452(tx *sql.Tx) error { + _, err := tx.Exec(` +CREATE TABLE yara_rules ( + id INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, + name VARCHAR(255) NOT NULL, + contents TEXT NOT NULL, + PRIMARY KEY (id), + UNIQUE KEY idx_yara_rules_name (name) +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_unicode_ci;`) + if err != nil { + return fmt.Errorf("failed to create yara_rules table: %w", err) + } + return nil +} + +func Down_20241016155452(tx *sql.Tx) error { + return nil +} diff --git a/server/datastore/mysql/migrations/tables/20241025141856_AddYaraRulesTable_test.go b/server/datastore/mysql/migrations/tables/20241025141856_AddYaraRulesTable_test.go new file mode 100644 index 000000000000..299faafa46de --- /dev/null +++ b/server/datastore/mysql/migrations/tables/20241025141856_AddYaraRulesTable_test.go @@ -0,0 +1,20 @@ +package tables + +import "testing" + +func TestUp_20241025141856(t *testing.T) { + db := applyUpToPrev(t) + + // + // Insert data to test the migration + // + // ... + + // Apply current migration. + applyNext(t, db) + + // + // Check data, insert new entries, e.g. to verify migration is safe. + // + // ... +} diff --git a/server/datastore/mysql/schema.sql b/server/datastore/mysql/schema.sql index 3fa9bfa65040..5fd0f8acfa74 100644 --- a/server/datastore/mysql/schema.sql +++ b/server/datastore/mysql/schema.sql @@ -1099,9 +1099,9 @@ CREATE TABLE `migration_status_tables` ( `tstamp` timestamp NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `id` (`id`) -) /*!50100 TABLESPACE `innodb_system` */ ENGINE=InnoDB AUTO_INCREMENT=327 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +) /*!50100 TABLESPACE `innodb_system` */ ENGINE=InnoDB AUTO_INCREMENT=328 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -INSERT INTO `migration_status_tables` VALUES (1,0,1,'2020-01-01 01:01:01'),(2,20161118193812,1,'2020-01-01 01:01:01'),(3,20161118211713,1,'2020-01-01 01:01:01'),(4,20161118212436,1,'2020-01-01 01:01:01'),(5,20161118212515,1,'2020-01-01 01:01:01'),(6,20161118212528,1,'2020-01-01 01:01:01'),(7,20161118212538,1,'2020-01-01 01:01:01'),(8,20161118212549,1,'2020-01-01 01:01:01'),(9,20161118212557,1,'2020-01-01 01:01:01'),(10,20161118212604,1,'2020-01-01 01:01:01'),(11,20161118212613,1,'2020-01-01 01:01:01'),(12,20161118212621,1,'2020-01-01 01:01:01'),(13,20161118212630,1,'2020-01-01 01:01:01'),(14,20161118212641,1,'2020-01-01 01:01:01'),(15,20161118212649,1,'2020-01-01 01:01:01'),(16,20161118212656,1,'2020-01-01 01:01:01'),(17,20161118212758,1,'2020-01-01 01:01:01'),(18,20161128234849,1,'2020-01-01 01:01:01'),(19,20161230162221,1,'2020-01-01 01:01:01'),(20,20170104113816,1,'2020-01-01 01:01:01'),(21,20170105151732,1,'2020-01-01 01:01:01'),(22,20170108191242,1,'2020-01-01 01:01:01'),(23,20170109094020,1,'2020-01-01 01:01:01'),(24,20170109130438,1,'2020-01-01 01:01:01'),(25,20170110202752,1,'2020-01-01 01:01:01'),(26,20170111133013,1,'2020-01-01 01:01:01'),(27,20170117025759,1,'2020-01-01 01:01:01'),(28,20170118191001,1,'2020-01-01 01:01:01'),(29,20170119234632,1,'2020-01-01 01:01:01'),(30,20170124230432,1,'2020-01-01 01:01:01'),(31,20170127014618,1,'2020-01-01 01:01:01'),(32,20170131232841,1,'2020-01-01 01:01:01'),(33,20170223094154,1,'2020-01-01 01:01:01'),(34,20170306075207,1,'2020-01-01 01:01:01'),(35,20170309100733,1,'2020-01-01 01:01:01'),(36,20170331111922,1,'2020-01-01 01:01:01'),(37,20170502143928,1,'2020-01-01 01:01:01'),(38,20170504130602,1,'2020-01-01 01:01:01'),(39,20170509132100,1,'2020-01-01 01:01:01'),(40,20170519105647,1,'2020-01-01 01:01:01'),(41,20170519105648,1,'2020-01-01 01:01:01'),(42,20170831234300,1,'2020-01-01 01:01:01'),(43,20170831234301,1,'2020-01-01 01:01:01'),(44,20170831234303,1,'2020-01-01 01:01:01'),(45,20171116163618,1,'2020-01-01 01:01:01'),(46,20171219164727,1,'2020-01-01 01:01:01'),(47,20180620164811,1,'2020-01-01 01:01:01'),(48,20180620175054,1,'2020-01-01 01:01:01'),(49,20180620175055,1,'2020-01-01 01:01:01'),(50,20191010101639,1,'2020-01-01 01:01:01'),(51,20191010155147,1,'2020-01-01 01:01:01'),(52,20191220130734,1,'2020-01-01 01:01:01'),(53,20200311140000,1,'2020-01-01 01:01:01'),(54,20200405120000,1,'2020-01-01 01:01:01'),(55,20200407120000,1,'2020-01-01 01:01:01'),(56,20200420120000,1,'2020-01-01 01:01:01'),(57,20200504120000,1,'2020-01-01 01:01:01'),(58,20200512120000,1,'2020-01-01 01:01:01'),(59,20200707120000,1,'2020-01-01 01:01:01'),(60,20201011162341,1,'2020-01-01 01:01:01'),(61,20201021104586,1,'2020-01-01 01:01:01'),(62,20201102112520,1,'2020-01-01 01:01:01'),(63,20201208121729,1,'2020-01-01 01:01:01'),(64,20201215091637,1,'2020-01-01 01:01:01'),(65,20210119174155,1,'2020-01-01 01:01:01'),(66,20210326182902,1,'2020-01-01 01:01:01'),(67,20210421112652,1,'2020-01-01 01:01:01'),(68,20210506095025,1,'2020-01-01 01:01:01'),(69,20210513115729,1,'2020-01-01 01:01:01'),(70,20210526113559,1,'2020-01-01 01:01:01'),(71,20210601000001,1,'2020-01-01 01:01:01'),(72,20210601000002,1,'2020-01-01 01:01:01'),(73,20210601000003,1,'2020-01-01 01:01:01'),(74,20210601000004,1,'2020-01-01 01:01:01'),(75,20210601000005,1,'2020-01-01 01:01:01'),(76,20210601000006,1,'2020-01-01 01:01:01'),(77,20210601000007,1,'2020-01-01 01:01:01'),(78,20210601000008,1,'2020-01-01 01:01:01'),(79,20210606151329,1,'2020-01-01 01:01:01'),(80,20210616163757,1,'2020-01-01 01:01:01'),(81,20210617174723,1,'2020-01-01 01:01:01'),(82,20210622160235,1,'2020-01-01 01:01:01'),(83,20210623100031,1,'2020-01-01 01:01:01'),(84,20210623133615,1,'2020-01-01 01:01:01'),(85,20210708143152,1,'2020-01-01 01:01:01'),(86,20210709124443,1,'2020-01-01 01:01:01'),(87,20210712155608,1,'2020-01-01 01:01:01'),(88,20210714102108,1,'2020-01-01 01:01:01'),(89,20210719153709,1,'2020-01-01 01:01:01'),(90,20210721171531,1,'2020-01-01 01:01:01'),(91,20210723135713,1,'2020-01-01 01:01:01'),(92,20210802135933,1,'2020-01-01 01:01:01'),(93,20210806112844,1,'2020-01-01 01:01:01'),(94,20210810095603,1,'2020-01-01 01:01:01'),(95,20210811150223,1,'2020-01-01 01:01:01'),(96,20210818151827,1,'2020-01-01 01:01:01'),(97,20210818151828,1,'2020-01-01 01:01:01'),(98,20210818182258,1,'2020-01-01 01:01:01'),(99,20210819131107,1,'2020-01-01 01:01:01'),(100,20210819143446,1,'2020-01-01 01:01:01'),(101,20210903132338,1,'2020-01-01 01:01:01'),(102,20210915144307,1,'2020-01-01 01:01:01'),(103,20210920155130,1,'2020-01-01 01:01:01'),(104,20210927143115,1,'2020-01-01 01:01:01'),(105,20210927143116,1,'2020-01-01 01:01:01'),(106,20211013133706,1,'2020-01-01 01:01:01'),(107,20211013133707,1,'2020-01-01 01:01:01'),(108,20211102135149,1,'2020-01-01 01:01:01'),(109,20211109121546,1,'2020-01-01 01:01:01'),(110,20211110163320,1,'2020-01-01 01:01:01'),(111,20211116184029,1,'2020-01-01 01:01:01'),(112,20211116184030,1,'2020-01-01 01:01:01'),(113,20211202092042,1,'2020-01-01 01:01:01'),(114,20211202181033,1,'2020-01-01 01:01:01'),(115,20211207161856,1,'2020-01-01 01:01:01'),(116,20211216131203,1,'2020-01-01 01:01:01'),(117,20211221110132,1,'2020-01-01 01:01:01'),(118,20220107155700,1,'2020-01-01 01:01:01'),(119,20220125105650,1,'2020-01-01 01:01:01'),(120,20220201084510,1,'2020-01-01 01:01:01'),(121,20220208144830,1,'2020-01-01 01:01:01'),(122,20220208144831,1,'2020-01-01 01:01:01'),(123,20220215152203,1,'2020-01-01 01:01:01'),(124,20220223113157,1,'2020-01-01 01:01:01'),(125,20220307104655,1,'2020-01-01 01:01:01'),(126,20220309133956,1,'2020-01-01 01:01:01'),(127,20220316155700,1,'2020-01-01 01:01:01'),(128,20220323152301,1,'2020-01-01 01:01:01'),(129,20220330100659,1,'2020-01-01 01:01:01'),(130,20220404091216,1,'2020-01-01 01:01:01'),(131,20220419140750,1,'2020-01-01 01:01:01'),(132,20220428140039,1,'2020-01-01 01:01:01'),(133,20220503134048,1,'2020-01-01 01:01:01'),(134,20220524102918,1,'2020-01-01 01:01:01'),(135,20220526123327,1,'2020-01-01 01:01:01'),(136,20220526123328,1,'2020-01-01 01:01:01'),(137,20220526123329,1,'2020-01-01 01:01:01'),(138,20220608113128,1,'2020-01-01 01:01:01'),(139,20220627104817,1,'2020-01-01 01:01:01'),(140,20220704101843,1,'2020-01-01 01:01:01'),(141,20220708095046,1,'2020-01-01 01:01:01'),(142,20220713091130,1,'2020-01-01 01:01:01'),(143,20220802135510,1,'2020-01-01 01:01:01'),(144,20220818101352,1,'2020-01-01 01:01:01'),(145,20220822161445,1,'2020-01-01 01:01:01'),(146,20220831100036,1,'2020-01-01 01:01:01'),(147,20220831100151,1,'2020-01-01 01:01:01'),(148,20220908181826,1,'2020-01-01 01:01:01'),(149,20220914154915,1,'2020-01-01 01:01:01'),(150,20220915165115,1,'2020-01-01 01:01:01'),(151,20220915165116,1,'2020-01-01 01:01:01'),(152,20220928100158,1,'2020-01-01 01:01:01'),(153,20221014084130,1,'2020-01-01 01:01:01'),(154,20221027085019,1,'2020-01-01 01:01:01'),(155,20221101103952,1,'2020-01-01 01:01:01'),(156,20221104144401,1,'2020-01-01 01:01:01'),(157,20221109100749,1,'2020-01-01 01:01:01'),(158,20221115104546,1,'2020-01-01 01:01:01'),(159,20221130114928,1,'2020-01-01 01:01:01'),(160,20221205112142,1,'2020-01-01 01:01:01'),(161,20221216115820,1,'2020-01-01 01:01:01'),(162,20221220195934,1,'2020-01-01 01:01:01'),(163,20221220195935,1,'2020-01-01 01:01:01'),(164,20221223174807,1,'2020-01-01 01:01:01'),(165,20221227163855,1,'2020-01-01 01:01:01'),(166,20221227163856,1,'2020-01-01 01:01:01'),(167,20230202224725,1,'2020-01-01 01:01:01'),(168,20230206163608,1,'2020-01-01 01:01:01'),(169,20230214131519,1,'2020-01-01 01:01:01'),(170,20230303135738,1,'2020-01-01 01:01:01'),(171,20230313135301,1,'2020-01-01 01:01:01'),(172,20230313141819,1,'2020-01-01 01:01:01'),(173,20230315104937,1,'2020-01-01 01:01:01'),(174,20230317173844,1,'2020-01-01 01:01:01'),(175,20230320133602,1,'2020-01-01 01:01:01'),(176,20230330100011,1,'2020-01-01 01:01:01'),(177,20230330134823,1,'2020-01-01 01:01:01'),(178,20230405232025,1,'2020-01-01 01:01:01'),(179,20230408084104,1,'2020-01-01 01:01:01'),(180,20230411102858,1,'2020-01-01 01:01:01'),(181,20230421155932,1,'2020-01-01 01:01:01'),(182,20230425082126,1,'2020-01-01 01:01:01'),(183,20230425105727,1,'2020-01-01 01:01:01'),(184,20230501154913,1,'2020-01-01 01:01:01'),(185,20230503101418,1,'2020-01-01 01:01:01'),(186,20230515144206,1,'2020-01-01 01:01:01'),(187,20230517140952,1,'2020-01-01 01:01:01'),(188,20230517152807,1,'2020-01-01 01:01:01'),(189,20230518114155,1,'2020-01-01 01:01:01'),(190,20230520153236,1,'2020-01-01 01:01:01'),(191,20230525151159,1,'2020-01-01 01:01:01'),(192,20230530122103,1,'2020-01-01 01:01:01'),(193,20230602111827,1,'2020-01-01 01:01:01'),(194,20230608103123,1,'2020-01-01 01:01:01'),(195,20230629140529,1,'2020-01-01 01:01:01'),(196,20230629140530,1,'2020-01-01 01:01:01'),(197,20230711144622,1,'2020-01-01 01:01:01'),(198,20230721135421,1,'2020-01-01 01:01:01'),(199,20230721161508,1,'2020-01-01 01:01:01'),(200,20230726115701,1,'2020-01-01 01:01:01'),(201,20230807100822,1,'2020-01-01 01:01:01'),(202,20230814150442,1,'2020-01-01 01:01:01'),(203,20230823122728,1,'2020-01-01 01:01:01'),(204,20230906152143,1,'2020-01-01 01:01:01'),(205,20230911163618,1,'2020-01-01 01:01:01'),(206,20230912101759,1,'2020-01-01 01:01:01'),(207,20230915101341,1,'2020-01-01 01:01:01'),(208,20230918132351,1,'2020-01-01 01:01:01'),(209,20231004144339,1,'2020-01-01 01:01:01'),(210,20231009094541,1,'2020-01-01 01:01:01'),(211,20231009094542,1,'2020-01-01 01:01:01'),(212,20231009094543,1,'2020-01-01 01:01:01'),(213,20231009094544,1,'2020-01-01 01:01:01'),(214,20231016091915,1,'2020-01-01 01:01:01'),(215,20231024174135,1,'2020-01-01 01:01:01'),(216,20231025120016,1,'2020-01-01 01:01:01'),(217,20231025160156,1,'2020-01-01 01:01:01'),(218,20231031165350,1,'2020-01-01 01:01:01'),(219,20231106144110,1,'2020-01-01 01:01:01'),(220,20231107130934,1,'2020-01-01 01:01:01'),(221,20231109115838,1,'2020-01-01 01:01:01'),(222,20231121054530,1,'2020-01-01 01:01:01'),(223,20231122101320,1,'2020-01-01 01:01:01'),(224,20231130132828,1,'2020-01-01 01:01:01'),(225,20231130132931,1,'2020-01-01 01:01:01'),(226,20231204155427,1,'2020-01-01 01:01:01'),(227,20231206142340,1,'2020-01-01 01:01:01'),(228,20231207102320,1,'2020-01-01 01:01:01'),(229,20231207102321,1,'2020-01-01 01:01:01'),(230,20231207133731,1,'2020-01-01 01:01:01'),(231,20231212094238,1,'2020-01-01 01:01:01'),(232,20231212095734,1,'2020-01-01 01:01:01'),(233,20231212161121,1,'2020-01-01 01:01:01'),(234,20231215122713,1,'2020-01-01 01:01:01'),(235,20231219143041,1,'2020-01-01 01:01:01'),(236,20231224070653,1,'2020-01-01 01:01:01'),(237,20240110134315,1,'2020-01-01 01:01:01'),(238,20240119091637,1,'2020-01-01 01:01:01'),(239,20240126020642,1,'2020-01-01 01:01:01'),(240,20240126020643,1,'2020-01-01 01:01:01'),(241,20240129162819,1,'2020-01-01 01:01:01'),(242,20240130115133,1,'2020-01-01 01:01:01'),(243,20240131083822,1,'2020-01-01 01:01:01'),(244,20240205095928,1,'2020-01-01 01:01:01'),(245,20240205121956,1,'2020-01-01 01:01:01'),(246,20240209110212,1,'2020-01-01 01:01:01'),(247,20240212111533,1,'2020-01-01 01:01:01'),(248,20240221112844,1,'2020-01-01 01:01:01'),(249,20240222073518,1,'2020-01-01 01:01:01'),(250,20240222135115,1,'2020-01-01 01:01:01'),(251,20240226082255,1,'2020-01-01 01:01:01'),(252,20240228082706,1,'2020-01-01 01:01:01'),(253,20240301173035,1,'2020-01-01 01:01:01'),(254,20240302111134,1,'2020-01-01 01:01:01'),(255,20240312103753,1,'2020-01-01 01:01:01'),(256,20240313143416,1,'2020-01-01 01:01:01'),(257,20240314085226,1,'2020-01-01 01:01:01'),(258,20240314151747,1,'2020-01-01 01:01:01'),(259,20240320145650,1,'2020-01-01 01:01:01'),(260,20240327115530,1,'2020-01-01 01:01:01'),(261,20240327115617,1,'2020-01-01 01:01:01'),(262,20240408085837,1,'2020-01-01 01:01:01'),(263,20240415104633,1,'2020-01-01 01:01:01'),(264,20240430111727,1,'2020-01-01 01:01:01'),(265,20240515200020,1,'2020-01-01 01:01:01'),(266,20240521143023,1,'2020-01-01 01:01:01'),(267,20240521143024,1,'2020-01-01 01:01:01'),(268,20240601174138,1,'2020-01-01 01:01:01'),(269,20240607133721,1,'2020-01-01 01:01:01'),(270,20240612150059,1,'2020-01-01 01:01:01'),(271,20240613162201,1,'2020-01-01 01:01:01'),(272,20240613172616,1,'2020-01-01 01:01:01'),(273,20240618142419,1,'2020-01-01 01:01:01'),(274,20240625093543,1,'2020-01-01 01:01:01'),(275,20240626195531,1,'2020-01-01 01:01:01'),(276,20240702123921,1,'2020-01-01 01:01:01'),(277,20240703154849,1,'2020-01-01 01:01:01'),(278,20240707134035,1,'2020-01-01 01:01:01'),(279,20240707134036,1,'2020-01-01 01:01:01'),(280,20240709124958,1,'2020-01-01 01:01:01'),(281,20240709132642,1,'2020-01-01 01:01:01'),(282,20240709183940,1,'2020-01-01 01:01:01'),(283,20240710155623,1,'2020-01-01 01:01:01'),(284,20240723102712,1,'2020-01-01 01:01:01'),(285,20240725152735,1,'2020-01-01 01:01:01'),(286,20240725182118,1,'2020-01-01 01:01:01'),(287,20240726100517,1,'2020-01-01 01:01:01'),(288,20240730171504,1,'2020-01-01 01:01:01'),(289,20240730174056,1,'2020-01-01 01:01:01'),(290,20240730215453,1,'2020-01-01 01:01:01'),(291,20240730374423,1,'2020-01-01 01:01:01'),(292,20240801115359,1,'2020-01-01 01:01:01'),(293,20240802101043,1,'2020-01-01 01:01:01'),(294,20240802113716,1,'2020-01-01 01:01:01'),(295,20240814135330,1,'2020-01-01 01:01:01'),(296,20240815000000,1,'2020-01-01 01:01:01'),(297,20240815000001,1,'2020-01-01 01:01:01'),(298,20240816103247,1,'2020-01-01 01:01:01'),(299,20240820091218,1,'2020-01-01 01:01:01'),(300,20240826111228,1,'2020-01-01 01:01:01'),(301,20240826160025,1,'2020-01-01 01:01:01'),(302,20240829165448,1,'2020-01-01 01:01:01'),(303,20240829165605,1,'2020-01-01 01:01:01'),(304,20240829165715,1,'2020-01-01 01:01:01'),(305,20240829165930,1,'2020-01-01 01:01:01'),(306,20240829170023,1,'2020-01-01 01:01:01'),(307,20240829170033,1,'2020-01-01 01:01:01'),(308,20240829170044,1,'2020-01-01 01:01:01'),(309,20240905105135,1,'2020-01-01 01:01:01'),(310,20240905140514,1,'2020-01-01 01:01:01'),(311,20240905200000,1,'2020-01-01 01:01:01'),(312,20240905200001,1,'2020-01-01 01:01:01'),(313,20241002104104,1,'2020-01-01 01:01:01'),(314,20241002104105,1,'2020-01-01 01:01:01'),(315,20241002104106,1,'2020-01-01 01:01:01'),(316,20241002210000,1,'2020-01-01 01:01:01'),(317,20241003145349,1,'2020-01-01 01:01:01'),(318,20241004005000,1,'2020-01-01 01:01:01'),(319,20241008083925,1,'2020-01-01 01:01:01'),(320,20241009090010,1,'2020-01-01 01:01:01'),(321,20241017163402,1,'2020-01-01 01:01:01'),(322,20241021224359,1,'2020-01-01 01:01:01'),(323,20241022140321,1,'2020-01-01 01:01:01'),(324,20241025111236,1,'2020-01-01 01:01:01'),(325,20241025112748,1,'2020-01-01 01:01:01'),(326,20241025141855,1,'2020-01-01 01:01:01'); +INSERT INTO `migration_status_tables` VALUES (1,0,1,'2020-01-01 01:01:01'),(2,20161118193812,1,'2020-01-01 01:01:01'),(3,20161118211713,1,'2020-01-01 01:01:01'),(4,20161118212436,1,'2020-01-01 01:01:01'),(5,20161118212515,1,'2020-01-01 01:01:01'),(6,20161118212528,1,'2020-01-01 01:01:01'),(7,20161118212538,1,'2020-01-01 01:01:01'),(8,20161118212549,1,'2020-01-01 01:01:01'),(9,20161118212557,1,'2020-01-01 01:01:01'),(10,20161118212604,1,'2020-01-01 01:01:01'),(11,20161118212613,1,'2020-01-01 01:01:01'),(12,20161118212621,1,'2020-01-01 01:01:01'),(13,20161118212630,1,'2020-01-01 01:01:01'),(14,20161118212641,1,'2020-01-01 01:01:01'),(15,20161118212649,1,'2020-01-01 01:01:01'),(16,20161118212656,1,'2020-01-01 01:01:01'),(17,20161118212758,1,'2020-01-01 01:01:01'),(18,20161128234849,1,'2020-01-01 01:01:01'),(19,20161230162221,1,'2020-01-01 01:01:01'),(20,20170104113816,1,'2020-01-01 01:01:01'),(21,20170105151732,1,'2020-01-01 01:01:01'),(22,20170108191242,1,'2020-01-01 01:01:01'),(23,20170109094020,1,'2020-01-01 01:01:01'),(24,20170109130438,1,'2020-01-01 01:01:01'),(25,20170110202752,1,'2020-01-01 01:01:01'),(26,20170111133013,1,'2020-01-01 01:01:01'),(27,20170117025759,1,'2020-01-01 01:01:01'),(28,20170118191001,1,'2020-01-01 01:01:01'),(29,20170119234632,1,'2020-01-01 01:01:01'),(30,20170124230432,1,'2020-01-01 01:01:01'),(31,20170127014618,1,'2020-01-01 01:01:01'),(32,20170131232841,1,'2020-01-01 01:01:01'),(33,20170223094154,1,'2020-01-01 01:01:01'),(34,20170306075207,1,'2020-01-01 01:01:01'),(35,20170309100733,1,'2020-01-01 01:01:01'),(36,20170331111922,1,'2020-01-01 01:01:01'),(37,20170502143928,1,'2020-01-01 01:01:01'),(38,20170504130602,1,'2020-01-01 01:01:01'),(39,20170509132100,1,'2020-01-01 01:01:01'),(40,20170519105647,1,'2020-01-01 01:01:01'),(41,20170519105648,1,'2020-01-01 01:01:01'),(42,20170831234300,1,'2020-01-01 01:01:01'),(43,20170831234301,1,'2020-01-01 01:01:01'),(44,20170831234303,1,'2020-01-01 01:01:01'),(45,20171116163618,1,'2020-01-01 01:01:01'),(46,20171219164727,1,'2020-01-01 01:01:01'),(47,20180620164811,1,'2020-01-01 01:01:01'),(48,20180620175054,1,'2020-01-01 01:01:01'),(49,20180620175055,1,'2020-01-01 01:01:01'),(50,20191010101639,1,'2020-01-01 01:01:01'),(51,20191010155147,1,'2020-01-01 01:01:01'),(52,20191220130734,1,'2020-01-01 01:01:01'),(53,20200311140000,1,'2020-01-01 01:01:01'),(54,20200405120000,1,'2020-01-01 01:01:01'),(55,20200407120000,1,'2020-01-01 01:01:01'),(56,20200420120000,1,'2020-01-01 01:01:01'),(57,20200504120000,1,'2020-01-01 01:01:01'),(58,20200512120000,1,'2020-01-01 01:01:01'),(59,20200707120000,1,'2020-01-01 01:01:01'),(60,20201011162341,1,'2020-01-01 01:01:01'),(61,20201021104586,1,'2020-01-01 01:01:01'),(62,20201102112520,1,'2020-01-01 01:01:01'),(63,20201208121729,1,'2020-01-01 01:01:01'),(64,20201215091637,1,'2020-01-01 01:01:01'),(65,20210119174155,1,'2020-01-01 01:01:01'),(66,20210326182902,1,'2020-01-01 01:01:01'),(67,20210421112652,1,'2020-01-01 01:01:01'),(68,20210506095025,1,'2020-01-01 01:01:01'),(69,20210513115729,1,'2020-01-01 01:01:01'),(70,20210526113559,1,'2020-01-01 01:01:01'),(71,20210601000001,1,'2020-01-01 01:01:01'),(72,20210601000002,1,'2020-01-01 01:01:01'),(73,20210601000003,1,'2020-01-01 01:01:01'),(74,20210601000004,1,'2020-01-01 01:01:01'),(75,20210601000005,1,'2020-01-01 01:01:01'),(76,20210601000006,1,'2020-01-01 01:01:01'),(77,20210601000007,1,'2020-01-01 01:01:01'),(78,20210601000008,1,'2020-01-01 01:01:01'),(79,20210606151329,1,'2020-01-01 01:01:01'),(80,20210616163757,1,'2020-01-01 01:01:01'),(81,20210617174723,1,'2020-01-01 01:01:01'),(82,20210622160235,1,'2020-01-01 01:01:01'),(83,20210623100031,1,'2020-01-01 01:01:01'),(84,20210623133615,1,'2020-01-01 01:01:01'),(85,20210708143152,1,'2020-01-01 01:01:01'),(86,20210709124443,1,'2020-01-01 01:01:01'),(87,20210712155608,1,'2020-01-01 01:01:01'),(88,20210714102108,1,'2020-01-01 01:01:01'),(89,20210719153709,1,'2020-01-01 01:01:01'),(90,20210721171531,1,'2020-01-01 01:01:01'),(91,20210723135713,1,'2020-01-01 01:01:01'),(92,20210802135933,1,'2020-01-01 01:01:01'),(93,20210806112844,1,'2020-01-01 01:01:01'),(94,20210810095603,1,'2020-01-01 01:01:01'),(95,20210811150223,1,'2020-01-01 01:01:01'),(96,20210818151827,1,'2020-01-01 01:01:01'),(97,20210818151828,1,'2020-01-01 01:01:01'),(98,20210818182258,1,'2020-01-01 01:01:01'),(99,20210819131107,1,'2020-01-01 01:01:01'),(100,20210819143446,1,'2020-01-01 01:01:01'),(101,20210903132338,1,'2020-01-01 01:01:01'),(102,20210915144307,1,'2020-01-01 01:01:01'),(103,20210920155130,1,'2020-01-01 01:01:01'),(104,20210927143115,1,'2020-01-01 01:01:01'),(105,20210927143116,1,'2020-01-01 01:01:01'),(106,20211013133706,1,'2020-01-01 01:01:01'),(107,20211013133707,1,'2020-01-01 01:01:01'),(108,20211102135149,1,'2020-01-01 01:01:01'),(109,20211109121546,1,'2020-01-01 01:01:01'),(110,20211110163320,1,'2020-01-01 01:01:01'),(111,20211116184029,1,'2020-01-01 01:01:01'),(112,20211116184030,1,'2020-01-01 01:01:01'),(113,20211202092042,1,'2020-01-01 01:01:01'),(114,20211202181033,1,'2020-01-01 01:01:01'),(115,20211207161856,1,'2020-01-01 01:01:01'),(116,20211216131203,1,'2020-01-01 01:01:01'),(117,20211221110132,1,'2020-01-01 01:01:01'),(118,20220107155700,1,'2020-01-01 01:01:01'),(119,20220125105650,1,'2020-01-01 01:01:01'),(120,20220201084510,1,'2020-01-01 01:01:01'),(121,20220208144830,1,'2020-01-01 01:01:01'),(122,20220208144831,1,'2020-01-01 01:01:01'),(123,20220215152203,1,'2020-01-01 01:01:01'),(124,20220223113157,1,'2020-01-01 01:01:01'),(125,20220307104655,1,'2020-01-01 01:01:01'),(126,20220309133956,1,'2020-01-01 01:01:01'),(127,20220316155700,1,'2020-01-01 01:01:01'),(128,20220323152301,1,'2020-01-01 01:01:01'),(129,20220330100659,1,'2020-01-01 01:01:01'),(130,20220404091216,1,'2020-01-01 01:01:01'),(131,20220419140750,1,'2020-01-01 01:01:01'),(132,20220428140039,1,'2020-01-01 01:01:01'),(133,20220503134048,1,'2020-01-01 01:01:01'),(134,20220524102918,1,'2020-01-01 01:01:01'),(135,20220526123327,1,'2020-01-01 01:01:01'),(136,20220526123328,1,'2020-01-01 01:01:01'),(137,20220526123329,1,'2020-01-01 01:01:01'),(138,20220608113128,1,'2020-01-01 01:01:01'),(139,20220627104817,1,'2020-01-01 01:01:01'),(140,20220704101843,1,'2020-01-01 01:01:01'),(141,20220708095046,1,'2020-01-01 01:01:01'),(142,20220713091130,1,'2020-01-01 01:01:01'),(143,20220802135510,1,'2020-01-01 01:01:01'),(144,20220818101352,1,'2020-01-01 01:01:01'),(145,20220822161445,1,'2020-01-01 01:01:01'),(146,20220831100036,1,'2020-01-01 01:01:01'),(147,20220831100151,1,'2020-01-01 01:01:01'),(148,20220908181826,1,'2020-01-01 01:01:01'),(149,20220914154915,1,'2020-01-01 01:01:01'),(150,20220915165115,1,'2020-01-01 01:01:01'),(151,20220915165116,1,'2020-01-01 01:01:01'),(152,20220928100158,1,'2020-01-01 01:01:01'),(153,20221014084130,1,'2020-01-01 01:01:01'),(154,20221027085019,1,'2020-01-01 01:01:01'),(155,20221101103952,1,'2020-01-01 01:01:01'),(156,20221104144401,1,'2020-01-01 01:01:01'),(157,20221109100749,1,'2020-01-01 01:01:01'),(158,20221115104546,1,'2020-01-01 01:01:01'),(159,20221130114928,1,'2020-01-01 01:01:01'),(160,20221205112142,1,'2020-01-01 01:01:01'),(161,20221216115820,1,'2020-01-01 01:01:01'),(162,20221220195934,1,'2020-01-01 01:01:01'),(163,20221220195935,1,'2020-01-01 01:01:01'),(164,20221223174807,1,'2020-01-01 01:01:01'),(165,20221227163855,1,'2020-01-01 01:01:01'),(166,20221227163856,1,'2020-01-01 01:01:01'),(167,20230202224725,1,'2020-01-01 01:01:01'),(168,20230206163608,1,'2020-01-01 01:01:01'),(169,20230214131519,1,'2020-01-01 01:01:01'),(170,20230303135738,1,'2020-01-01 01:01:01'),(171,20230313135301,1,'2020-01-01 01:01:01'),(172,20230313141819,1,'2020-01-01 01:01:01'),(173,20230315104937,1,'2020-01-01 01:01:01'),(174,20230317173844,1,'2020-01-01 01:01:01'),(175,20230320133602,1,'2020-01-01 01:01:01'),(176,20230330100011,1,'2020-01-01 01:01:01'),(177,20230330134823,1,'2020-01-01 01:01:01'),(178,20230405232025,1,'2020-01-01 01:01:01'),(179,20230408084104,1,'2020-01-01 01:01:01'),(180,20230411102858,1,'2020-01-01 01:01:01'),(181,20230421155932,1,'2020-01-01 01:01:01'),(182,20230425082126,1,'2020-01-01 01:01:01'),(183,20230425105727,1,'2020-01-01 01:01:01'),(184,20230501154913,1,'2020-01-01 01:01:01'),(185,20230503101418,1,'2020-01-01 01:01:01'),(186,20230515144206,1,'2020-01-01 01:01:01'),(187,20230517140952,1,'2020-01-01 01:01:01'),(188,20230517152807,1,'2020-01-01 01:01:01'),(189,20230518114155,1,'2020-01-01 01:01:01'),(190,20230520153236,1,'2020-01-01 01:01:01'),(191,20230525151159,1,'2020-01-01 01:01:01'),(192,20230530122103,1,'2020-01-01 01:01:01'),(193,20230602111827,1,'2020-01-01 01:01:01'),(194,20230608103123,1,'2020-01-01 01:01:01'),(195,20230629140529,1,'2020-01-01 01:01:01'),(196,20230629140530,1,'2020-01-01 01:01:01'),(197,20230711144622,1,'2020-01-01 01:01:01'),(198,20230721135421,1,'2020-01-01 01:01:01'),(199,20230721161508,1,'2020-01-01 01:01:01'),(200,20230726115701,1,'2020-01-01 01:01:01'),(201,20230807100822,1,'2020-01-01 01:01:01'),(202,20230814150442,1,'2020-01-01 01:01:01'),(203,20230823122728,1,'2020-01-01 01:01:01'),(204,20230906152143,1,'2020-01-01 01:01:01'),(205,20230911163618,1,'2020-01-01 01:01:01'),(206,20230912101759,1,'2020-01-01 01:01:01'),(207,20230915101341,1,'2020-01-01 01:01:01'),(208,20230918132351,1,'2020-01-01 01:01:01'),(209,20231004144339,1,'2020-01-01 01:01:01'),(210,20231009094541,1,'2020-01-01 01:01:01'),(211,20231009094542,1,'2020-01-01 01:01:01'),(212,20231009094543,1,'2020-01-01 01:01:01'),(213,20231009094544,1,'2020-01-01 01:01:01'),(214,20231016091915,1,'2020-01-01 01:01:01'),(215,20231024174135,1,'2020-01-01 01:01:01'),(216,20231025120016,1,'2020-01-01 01:01:01'),(217,20231025160156,1,'2020-01-01 01:01:01'),(218,20231031165350,1,'2020-01-01 01:01:01'),(219,20231106144110,1,'2020-01-01 01:01:01'),(220,20231107130934,1,'2020-01-01 01:01:01'),(221,20231109115838,1,'2020-01-01 01:01:01'),(222,20231121054530,1,'2020-01-01 01:01:01'),(223,20231122101320,1,'2020-01-01 01:01:01'),(224,20231130132828,1,'2020-01-01 01:01:01'),(225,20231130132931,1,'2020-01-01 01:01:01'),(226,20231204155427,1,'2020-01-01 01:01:01'),(227,20231206142340,1,'2020-01-01 01:01:01'),(228,20231207102320,1,'2020-01-01 01:01:01'),(229,20231207102321,1,'2020-01-01 01:01:01'),(230,20231207133731,1,'2020-01-01 01:01:01'),(231,20231212094238,1,'2020-01-01 01:01:01'),(232,20231212095734,1,'2020-01-01 01:01:01'),(233,20231212161121,1,'2020-01-01 01:01:01'),(234,20231215122713,1,'2020-01-01 01:01:01'),(235,20231219143041,1,'2020-01-01 01:01:01'),(236,20231224070653,1,'2020-01-01 01:01:01'),(237,20240110134315,1,'2020-01-01 01:01:01'),(238,20240119091637,1,'2020-01-01 01:01:01'),(239,20240126020642,1,'2020-01-01 01:01:01'),(240,20240126020643,1,'2020-01-01 01:01:01'),(241,20240129162819,1,'2020-01-01 01:01:01'),(242,20240130115133,1,'2020-01-01 01:01:01'),(243,20240131083822,1,'2020-01-01 01:01:01'),(244,20240205095928,1,'2020-01-01 01:01:01'),(245,20240205121956,1,'2020-01-01 01:01:01'),(246,20240209110212,1,'2020-01-01 01:01:01'),(247,20240212111533,1,'2020-01-01 01:01:01'),(248,20240221112844,1,'2020-01-01 01:01:01'),(249,20240222073518,1,'2020-01-01 01:01:01'),(250,20240222135115,1,'2020-01-01 01:01:01'),(251,20240226082255,1,'2020-01-01 01:01:01'),(252,20240228082706,1,'2020-01-01 01:01:01'),(253,20240301173035,1,'2020-01-01 01:01:01'),(254,20240302111134,1,'2020-01-01 01:01:01'),(255,20240312103753,1,'2020-01-01 01:01:01'),(256,20240313143416,1,'2020-01-01 01:01:01'),(257,20240314085226,1,'2020-01-01 01:01:01'),(258,20240314151747,1,'2020-01-01 01:01:01'),(259,20240320145650,1,'2020-01-01 01:01:01'),(260,20240327115530,1,'2020-01-01 01:01:01'),(261,20240327115617,1,'2020-01-01 01:01:01'),(262,20240408085837,1,'2020-01-01 01:01:01'),(263,20240415104633,1,'2020-01-01 01:01:01'),(264,20240430111727,1,'2020-01-01 01:01:01'),(265,20240515200020,1,'2020-01-01 01:01:01'),(266,20240521143023,1,'2020-01-01 01:01:01'),(267,20240521143024,1,'2020-01-01 01:01:01'),(268,20240601174138,1,'2020-01-01 01:01:01'),(269,20240607133721,1,'2020-01-01 01:01:01'),(270,20240612150059,1,'2020-01-01 01:01:01'),(271,20240613162201,1,'2020-01-01 01:01:01'),(272,20240613172616,1,'2020-01-01 01:01:01'),(273,20240618142419,1,'2020-01-01 01:01:01'),(274,20240625093543,1,'2020-01-01 01:01:01'),(275,20240626195531,1,'2020-01-01 01:01:01'),(276,20240702123921,1,'2020-01-01 01:01:01'),(277,20240703154849,1,'2020-01-01 01:01:01'),(278,20240707134035,1,'2020-01-01 01:01:01'),(279,20240707134036,1,'2020-01-01 01:01:01'),(280,20240709124958,1,'2020-01-01 01:01:01'),(281,20240709132642,1,'2020-01-01 01:01:01'),(282,20240709183940,1,'2020-01-01 01:01:01'),(283,20240710155623,1,'2020-01-01 01:01:01'),(284,20240723102712,1,'2020-01-01 01:01:01'),(285,20240725152735,1,'2020-01-01 01:01:01'),(286,20240725182118,1,'2020-01-01 01:01:01'),(287,20240726100517,1,'2020-01-01 01:01:01'),(288,20240730171504,1,'2020-01-01 01:01:01'),(289,20240730174056,1,'2020-01-01 01:01:01'),(290,20240730215453,1,'2020-01-01 01:01:01'),(291,20240730374423,1,'2020-01-01 01:01:01'),(292,20240801115359,1,'2020-01-01 01:01:01'),(293,20240802101043,1,'2020-01-01 01:01:01'),(294,20240802113716,1,'2020-01-01 01:01:01'),(295,20240814135330,1,'2020-01-01 01:01:01'),(296,20240815000000,1,'2020-01-01 01:01:01'),(297,20240815000001,1,'2020-01-01 01:01:01'),(298,20240816103247,1,'2020-01-01 01:01:01'),(299,20240820091218,1,'2020-01-01 01:01:01'),(300,20240826111228,1,'2020-01-01 01:01:01'),(301,20240826160025,1,'2020-01-01 01:01:01'),(302,20240829165448,1,'2020-01-01 01:01:01'),(303,20240829165605,1,'2020-01-01 01:01:01'),(304,20240829165715,1,'2020-01-01 01:01:01'),(305,20240829165930,1,'2020-01-01 01:01:01'),(306,20240829170023,1,'2020-01-01 01:01:01'),(307,20240829170033,1,'2020-01-01 01:01:01'),(308,20240829170044,1,'2020-01-01 01:01:01'),(309,20240905105135,1,'2020-01-01 01:01:01'),(310,20240905140514,1,'2020-01-01 01:01:01'),(311,20240905200000,1,'2020-01-01 01:01:01'),(312,20240905200001,1,'2020-01-01 01:01:01'),(313,20241002104104,1,'2020-01-01 01:01:01'),(314,20241002104105,1,'2020-01-01 01:01:01'),(315,20241002104106,1,'2020-01-01 01:01:01'),(316,20241002210000,1,'2020-01-01 01:01:01'),(317,20241003145349,1,'2020-01-01 01:01:01'),(318,20241004005000,1,'2020-01-01 01:01:01'),(319,20241008083925,1,'2020-01-01 01:01:01'),(320,20241009090010,1,'2020-01-01 01:01:01'),(321,20241016155452,1,'2020-01-01 01:01:01'),(322,20241017163402,1,'2020-01-01 01:01:01'),(323,20241021224359,1,'2020-01-01 01:01:01'),(324,20241022140321,1,'2020-01-01 01:01:01'),(325,20241025111236,1,'2020-01-01 01:01:01'),(326,20241025112748,1,'2020-01-01 01:01:01'),(327,20241025141855,1,'2020-01-01 01:01:01'); /*!40101 SET @saved_cs_client = @@character_set_client */; /*!50503 SET character_set_client = utf8mb4 */; CREATE TABLE `mobile_device_management_solutions` ( @@ -2081,6 +2081,16 @@ CREATE TABLE `wstep_serials` ( PRIMARY KEY (`serial`) ) /*!50100 TABLESPACE `innodb_system` */ ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `yara_rules` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL, + `contents` text COLLATE utf8mb4_unicode_ci NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `idx_yara_rules_name` (`name`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; /*!50001 DROP VIEW IF EXISTS `nano_view_queue`*/; /*!50001 SET @saved_cs_client = @@character_set_client */; /*!50001 SET @saved_cs_results = @@character_set_results */; diff --git a/server/fleet/agent_options_generated.go b/server/fleet/agent_options_generated.go index 427c3901886c..860b054fdaa9 100644 --- a/server/fleet/agent_options_generated.go +++ b/server/fleet/agent_options_generated.go @@ -115,6 +115,7 @@ type osqueryOptions struct { TlsDisableStatusLog bool `json:"tls_disable_status_log"` Verbose bool `json:"verbose"` YaraDelay uint32 `json:"yara_delay"` + YaraSigurlAuthenticate bool `json:"yara_sigurl_authenticate"` // embed the os-specific structs OsqueryCommandLineFlagsLinux diff --git a/server/fleet/app.go b/server/fleet/app.go index 483dd5fe796a..cf2a9924793d 100644 --- a/server/fleet/app.go +++ b/server/fleet/app.go @@ -518,6 +518,8 @@ type AppConfig struct { // (The source of truth for scripts is in MySQL.) Scripts optjson.Slice[string] `json:"scripts"` + YaraRules []YaraRule `json:"yara_rules,omitempty"` + // when true, strictDecoding causes the UnmarshalJSON method to return an // error if there are unknown fields in the raw JSON. strictDecoding bool @@ -688,6 +690,12 @@ func (c *AppConfig) Copy() *AppConfig { clone.MDM.MacOSSetup.Software = optjson.SetSlice(sw) } + if c.YaraRules != nil { + rules := make([]YaraRule, len(c.YaraRules)) + copy(rules, c.YaraRules) + clone.YaraRules = rules + } + return &clone } @@ -1383,3 +1391,12 @@ type WindowsSettings struct { // (The source of truth for profiles is in MySQL.) CustomSettings optjson.Slice[MDMProfileSpec] `json:"custom_settings"` } + +type YaraRuleSpec struct { + Path string `json:"path"` +} + +type YaraRule struct { + Name string `json:"name"` + Contents string `json:"contents"` +} diff --git a/server/fleet/datastore.go b/server/fleet/datastore.go index 93a4d6ff2207..b8eae3ff4f24 100644 --- a/server/fleet/datastore.go +++ b/server/fleet/datastore.go @@ -439,6 +439,11 @@ type Datastore interface { // value. AggregateEnrollSecretPerTeam(ctx context.Context) ([]*EnrollSecret, error) + // Methods for getting and applying the stored yara rules. + GetYaraRules(ctx context.Context) ([]YaraRule, error) + ApplyYaraRules(ctx context.Context, rules []YaraRule) error + YaraRuleByName(ctx context.Context, name string) (*YaraRule, error) + /////////////////////////////////////////////////////////////////////////////// // InviteStore contains the methods for managing user invites in a datastore. diff --git a/server/fleet/service.go b/server/fleet/service.go index e7f70e807260..7bdcdc11c3dc 100644 --- a/server/fleet/service.go +++ b/server/fleet/service.go @@ -66,6 +66,7 @@ type OsqueryService interface { ) (err error) SubmitStatusLogs(ctx context.Context, logs []json.RawMessage) (err error) SubmitResultLogs(ctx context.Context, logs []json.RawMessage) (err error) + YaraRuleByName(ctx context.Context, name string) (*YaraRule, error) } type Service interface { diff --git a/server/mock/datastore_mock.go b/server/mock/datastore_mock.go index 93b125a007ad..3ac2ac33d531 100644 --- a/server/mock/datastore_mock.go +++ b/server/mock/datastore_mock.go @@ -327,6 +327,12 @@ type ApplyEnrollSecretsFunc func(ctx context.Context, teamID *uint, secrets []*f type AggregateEnrollSecretPerTeamFunc func(ctx context.Context) ([]*fleet.EnrollSecret, error) +type GetYaraRulesFunc func(ctx context.Context) ([]fleet.YaraRule, error) + +type ApplyYaraRulesFunc func(ctx context.Context, rules []fleet.YaraRule) error + +type YaraRuleByNameFunc func(ctx context.Context, name string) (*fleet.YaraRule, error) + type NewInviteFunc func(ctx context.Context, i *fleet.Invite) (*fleet.Invite, error) type ListInvitesFunc func(ctx context.Context, opt fleet.ListOptions) ([]*fleet.Invite, error) @@ -1599,6 +1605,15 @@ type DataStore struct { AggregateEnrollSecretPerTeamFunc AggregateEnrollSecretPerTeamFunc AggregateEnrollSecretPerTeamFuncInvoked bool + GetYaraRulesFunc GetYaraRulesFunc + GetYaraRulesFuncInvoked bool + + ApplyYaraRulesFunc ApplyYaraRulesFunc + ApplyYaraRulesFuncInvoked bool + + YaraRuleByNameFunc YaraRuleByNameFunc + YaraRuleByNameFuncInvoked bool + NewInviteFunc NewInviteFunc NewInviteFuncInvoked bool @@ -3891,6 +3906,27 @@ func (s *DataStore) AggregateEnrollSecretPerTeam(ctx context.Context) ([]*fleet. return s.AggregateEnrollSecretPerTeamFunc(ctx) } +func (s *DataStore) GetYaraRules(ctx context.Context) ([]fleet.YaraRule, error) { + s.mu.Lock() + s.GetYaraRulesFuncInvoked = true + s.mu.Unlock() + return s.GetYaraRulesFunc(ctx) +} + +func (s *DataStore) ApplyYaraRules(ctx context.Context, rules []fleet.YaraRule) error { + s.mu.Lock() + s.ApplyYaraRulesFuncInvoked = true + s.mu.Unlock() + return s.ApplyYaraRulesFunc(ctx, rules) +} + +func (s *DataStore) YaraRuleByName(ctx context.Context, name string) (*fleet.YaraRule, error) { + s.mu.Lock() + s.YaraRuleByNameFuncInvoked = true + s.mu.Unlock() + return s.YaraRuleByNameFunc(ctx, name) +} + func (s *DataStore) NewInvite(ctx context.Context, i *fleet.Invite) (*fleet.Invite, error) { s.mu.Lock() s.NewInviteFuncInvoked = true diff --git a/server/service/appconfig.go b/server/service/appconfig.go index 420a49ddb8b0..e28af4ab27af 100644 --- a/server/service/appconfig.go +++ b/server/service/appconfig.go @@ -749,6 +749,12 @@ func (svc *Service) ModifyAppConfig(ctx context.Context, p []byte, applyOpts fle return nil, ctxerr.Wrap(ctx, err, "process iPadOS OS updates config change") } + if appConfig.YaraRules != nil { + if err := svc.ds.ApplyYaraRules(ctx, appConfig.YaraRules); err != nil { + return nil, ctxerr.Wrap(ctx, err, "save yara rules for app config") + } + } + // if the Windows updates requirements changed, create the corresponding // activity. if !oldAppConfig.MDM.WindowsUpdates.Equal(appConfig.MDM.WindowsUpdates) { diff --git a/server/service/client.go b/server/service/client.go index 1e2c0179bbbd..2878084b5e63 100644 --- a/server/service/client.go +++ b/server/service/client.go @@ -524,7 +524,7 @@ func (c *Client) ApplyGroup( for i, f := range scripts { b, err := os.ReadFile(f) if err != nil { - return nil, nil, nil, fmt.Errorf("applying fleet config: %w", err) + return nil, nil, nil, fmt.Errorf("applying no-team scripts: %w", err) } scriptPayloads[i] = fleet.ScriptPayload{ ScriptContents: b, @@ -537,6 +537,27 @@ func (c *Client) ApplyGroup( } teamsScripts["No team"] = noTeamScripts } + + rules, err := extractAppCfgYaraRules(specs.AppConfig) + if err != nil { + return nil, nil, nil, fmt.Errorf("applying yara rules: %w", err) + } + if rules != nil { + rulePayloads := make([]fleet.YaraRule, len(rules)) + for i, f := range rules { + path := resolveApplyRelativePath(baseDir, f.Path) + b, err := os.ReadFile(path) + if err != nil { + return nil, nil, nil, fmt.Errorf("applying yara rules: %w", err) + } + rulePayloads[i] = fleet.YaraRule{ + Contents: string(b), + Name: filepath.Base(f.Path), + } + } + specs.AppConfig.(map[string]interface{})["yara_rules"] = rulePayloads + } + if err := c.ApplyAppConfig(specs.AppConfig, opts.ApplySpecOptions); err != nil { return nil, nil, nil, fmt.Errorf("applying fleet config: %w", err) } @@ -1137,6 +1158,47 @@ func extractAppCfgScripts(appCfg interface{}) []string { return scriptsStrings } +func extractAppCfgYaraRules(appCfg interface{}) ([]fleet.YaraRuleSpec, error) { + asMap, ok := appCfg.(map[string]interface{}) + if !ok { + return nil, errors.New("extract yara rules: app config is not a map") + } + + rules, ok := asMap["yara_rules"] + if !ok { + // yara_rules is not present + return nil, nil + } + + rulesAny, ok := rules.([]interface{}) + if !ok || rulesAny == nil { + // return a non-nil, empty slice instead, so the caller knows that the + // rules key was actually provided. + return []fleet.YaraRuleSpec{}, nil + } + + ruleSpecs := make([]fleet.YaraRuleSpec, 0, len(rulesAny)) + for _, v := range rulesAny { + smap, ok := v.(map[string]interface{}) + if !ok { + return nil, errors.New("extract yara rules: rule entry is not a map") + } + + pathEntry, ok := smap["path"] + if !ok { + return nil, errors.New("extract yara rules: rule entry missing path") + } + + path, ok := pathEntry.(string) + if !ok { + return nil, errors.New("extract yara rules: rule entry path is not string") + } + + ruleSpecs = append(ruleSpecs, fleet.YaraRuleSpec{Path: path}) + } + return ruleSpecs, nil +} + type profileSpecsByPlatform struct { macos []fleet.MDMProfileSpec windows []fleet.MDMProfileSpec diff --git a/server/service/handler.go b/server/service/handler.go index 56cc734eca0f..183fed9d9db6 100644 --- a/server/service/handler.go +++ b/server/service/handler.go @@ -855,6 +855,8 @@ func attachFleetAPIRoutes(r *mux.Router, svc fleet.Service, config config.FleetC POST("/api/osquery/carve/begin", carveBeginEndpoint, carveBeginRequest{}) he.WithAltPaths("/api/v1/osquery/log"). POST("/api/osquery/log", submitLogsEndpoint, submitLogsRequest{}) + he.WithAltPaths("/api/v1/osquery/yara/{name}"). + POST("/api/osquery/yara/{name}", getYaraEndpoint, getYaraRequest{}) // orbit authenticated endpoints oe := newOrbitAuthenticatedEndpointer(svc, logger, opts, r, apiVersions...) diff --git a/server/service/mock/service_osquery.go b/server/service/mock/service_osquery.go index a25eb3a8269e..736354074c96 100644 --- a/server/service/mock/service_osquery.go +++ b/server/service/mock/service_osquery.go @@ -26,6 +26,8 @@ type SubmitStatusLogsFunc func(ctx context.Context, logs []json.RawMessage) (err type SubmitResultLogsFunc func(ctx context.Context, logs []json.RawMessage) (err error) +type YaraRuleByNameFunc func(ctx context.Context, name string) (*fleet.YaraRule, error) + type TLSService struct { EnrollAgentFunc EnrollAgentFunc EnrollAgentFuncInvoked bool @@ -48,6 +50,9 @@ type TLSService struct { SubmitResultLogsFunc SubmitResultLogsFunc SubmitResultLogsFuncInvoked bool + YaraRuleByNameFunc YaraRuleByNameFunc + YaraRuleByNameFuncInvoked bool + mu sync.Mutex } @@ -99,3 +104,10 @@ func (s *TLSService) SubmitResultLogs(ctx context.Context, logs []json.RawMessag s.mu.Unlock() return s.SubmitResultLogsFunc(ctx, logs) } + +func (s *TLSService) YaraRuleByName(ctx context.Context, name string) (*fleet.YaraRule, error) { + s.mu.Lock() + s.YaraRuleByNameFuncInvoked = true + s.mu.Unlock() + return s.YaraRuleByNameFunc(ctx, name) +} diff --git a/server/service/osquery.go b/server/service/osquery.go index c0f79bddcc3b..62199deb2ca1 100644 --- a/server/service/osquery.go +++ b/server/service/osquery.go @@ -2488,3 +2488,39 @@ func getQueryNameAndTeamIDFromResult(path string) (*uint, string, error) { // If none of the above patterns match, return error return nil, "", fmt.Errorf("unknown format: %q", path) } + +// Yara rules + +func (svc *Service) YaraRuleByName(ctx context.Context, name string) (*fleet.YaraRule, error) { + return svc.ds.YaraRuleByName(ctx, name) +} + +type getYaraRequest struct { + NodeKey string `json:"node_key"` + Name string `url:"name"` +} + +func (r *getYaraRequest) hostNodeKey() string { + return r.NodeKey +} + +type getYaraResponse struct { + Err error `json:"error,omitempty"` + Content string +} + +func (r getYaraResponse) error() error { return r.Err } + +func (r getYaraResponse) hijackRender(ctx context.Context, w http.ResponseWriter) { + w.Header().Set("Content-Type", "text/plain; charset=utf-8") + w.Write([]byte(r.Content)) +} + +func getYaraEndpoint(ctx context.Context, request interface{}, svc fleet.Service) (errorer, error) { + r := request.(*getYaraRequest) + rule, err := svc.YaraRuleByName(ctx, r.Name) + if err != nil { + return getYaraResponse{Err: err}, nil + } + return getYaraResponse{Content: rule.Contents}, nil +}