-
Notifications
You must be signed in to change notification settings - Fork 8.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Security Solution][Detections] Allows rules to be updated from EPR #91949
Conversation
* Enable retrieval of rules from the Elastic Package Registry. | ||
*/ | ||
export const enablePackageRegistryRules = () => { | ||
usePackageRegistryRules = true; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would do a return process.env.ENABLE_REGISTRY_RULES || false
here and then we will have it by default turned off. In your local .bashrc/.zshrc we can flip it to true.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh wait, never mind, I see what you're doing here. You're using the config globally and then flipping it here from the plugin. You can do this for now, I'm fine with it. Usually we push the config object with the feature flags in the REST routes. I will see if I can give a pointer there for you, but this is fine right now as written. It will work out.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
so entirely remove it from the config then?
followed by a change from L31-L39, removing this function and doing this instead?
const usePackageRegistryRules = process.env.ENABLE_REGISTRY_RULES || false;
edit -- oh i was delayed. okay I'll leave it as is unless you have an update or stronger suggestion
const rulePromises = rulePaths.map(async (path) => { | ||
const content = JSON.parse(getAsset(path).toString('utf8')); | ||
return content as AddPrepackagedRulesSchema; | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you can just do this:
const rulePromises = rulePaths.map<AddPrepackagedRulesSchema>((path) => {
return JSON.parse(getAsset(path).toString('utf8'));
});
return validateAllPrepackagedRules(rulePromises);
Not seeing anything with Promises here going on.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
import { rawRules } from './prepackaged_rules'; | ||
|
||
const DetectionRulesPackageName = 'detection_rules'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
usually constants are capital letters screaming such as DETECTION_RULES_PACKAGE_NAME = 'detection_rules'
as a convention or they're just lowercase like detectionRulesPackageName
.
We have a global constants file that is used front and backend here:
x-pack/plugins/security_solution/common/constants.ts
But some people prefer to put their constants in a file called constants.ts
that is local and next to their file if they don't think it will be shared outside of their code. I'm fine with constants local to a file like you have it or any of the above. Putting it in the common section will add more bytes to the plugin downloaded to the front end so it might be better to keep it local to the file or just in another constants.ts
next to this one.
Context fwiw.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
doh! yeah, I had a moment from Go, which yells at you if you have a global constant that doesn't follow the same naming convention as as a variable (it's dumb).
I added a constants.ts
next to this file and moved this there
b301d4a
latestRulesPackageVersion = registryPackage.version; // eslint-disable-line require-atomic-updates | ||
} | ||
} catch (error) { | ||
// console.warn(error); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would remove this commented out line and put in its place:
TODO: Push down the logger to give a warning that this should warn about errors seen during an attempted download
@@ -36,7 +36,7 @@ export type EpmPackageInstallStatus = 'installed' | 'installing'; | |||
export type DetailViewPanelName = 'overview' | 'policies' | 'settings' | 'custom'; | |||
export type ServiceName = 'kibana' | 'elasticsearch'; | |||
export type AgentAssetType = typeof agentAssetTypes; | |||
export type AssetType = KibanaAssetType | ElasticsearchAssetType | ValueOf<AgentAssetType>; | |||
export type AssetType = KibanaAssetType | ElasticsearchAssetType | ValueOf<AgentAssetType> | 'rules'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You'll probably have to get someone from the epm
team to also give you a LGTM, but looking below I think they might want it like so (from their patterns):
export type AssetType =
| KibanaAssetType
| ElasticsearchAssetType
| ValueOf<AgentAssetType>
| RuleAssetType;
/*
Enum mapping of a rules saved object asset type
*/
export enum RuleAssetType {
rules = 'rules',
}
(caveat, I haven't tested that, just coming off the top of my head)
typeof installPrepackagedRulesSchema, | ||
InstallPrepackagedRulesSchema | ||
>(installPrepackagedRulesSchema), | ||
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You'll have to mess with these types and figure out how to make the body optional. I am pretty sure the way to do that is to make the type basically a union with null so that when you have request.body
the body value can be a null value.
💚 Build SucceededMetrics [docs]Async chunks
Page load bundle
History
To update your PR or re-run it, just comment with: |
Pinging @elastic/security-detections-response (Team:Detections and Resp) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We did a joint review and this is experimental and behind a feature flag to get the code in quick enough.
There will be more than likely a few followups from detections team members and/or others for additions such as:
- FTR e2e structural tests to test the new version that gets pushes down in the REST routes so that it cannot be broken by others doing refactoring like the other REST endpoints have those tests already.
- jest unit tests for the globals and possibly slight tweaks to the functions to make unit testing those parts simple.
Longer term tests might involve fleet and EPM within the e2e FTR's at a later time. Last note is that when we remove the feature flag to prefer/always use online rules we might have to tweak/change the existing tests as they are written within the pre-packaged rules and the cypress.
None of this feels like big issues. Just minor stuff.
Pinging @elastic/fleet (Team:Fleet) |
@rw-access Could we convert this PR to a draft to make sure it does not get merged before all the changes are made in the package-spec? Spec should always go in first to make sure all parts in the stack are aligned. |
I have some concerns on how the installation is working here, here the security plugin is going to use directly the package from the registry and installing the package will not really install anything, I am wondering if we should have a mechanism where the security plugin do not has to deal with the package. If the security rules are a saved object for example, the fleet plugin could create the rules during install and the security plugin will consume that saved object. @jfsiii I would love to have your thought on that |
A more generic question here: How will this work if lets say I add 2 rules to the nginx package. Will this all still work? |
Summary
Allow the rules to be updated from EPR, or from the file system if EPR can't be reached.
The general workflow for the best-effort rules update mechanism:
checkAndStageUpdate()
is run in the background, non-blocking, to check if EPR is reachable and there are any updates available. It will return the rules compiled in from the filesystem (rawRules
)checkAndStageUpdate
) and decoded rules (latestRulesDownload
) are stored in memorycheckAndStageUpdate()
in the background. We could add a simple TTL in a global var to throttle the call to fleet'sfetchFindLatestPackage
. Or we could encourage fleet to do the same_status
, a newrules_package_version
field is added which returns the package version to the front-end, if it is being used for reference. This is to make sure that if a user consents to one package update (you have X rules to update or install), we don't install a newer package when they click the button.Not done yet:
I'm using elastic/package-storage#843 to test out EPR.
Checklist
Delete any items that are not applicable to this PR.
For maintainers