Risk Engine
The Risk Engine empowers security teams to create policies that automatically identify when applications exceed your organization's risk tolerance. You can configure these policies to flag applications for various reasons, such as known malware detection or organization restrictions on virtualization software.
The system uses a flexible plugin architechture, allowing multiple plugins to participate in the process of deciding whether a given application is above or below the line of risk.
When the Risk Engine is enabled, every time Santa uploads an event to Workshop
the binary / application inside the events are passed to it. The Risk Engine
then generates an authorization request for each of its plugins in parallel,
setting a deadline. If all plugins return an ALLOW
decision within the
deadline then the event is considered safe. If any plugins returns DENY
or
DENY_MALWARE
then the event is considered dangerous. If any plugins return
errors or respond after the deadline they are treated as if the plugin returned
a DENY
decision.
Configuration
UI
The Risk Engine can be configured in the Settings page.
API Methods
The Risk Engine can be configured using the UpdateRiskEngineSettings method.
Internal vs. Remote Plugins
Risk Engine plugins come in two flavors - Internal, which are included as part of Workshop and Remote, which are extensions that can be written by customers or North Pole Security.
Secrets URL
Some options in Risk Engine plugins can be configured to use AWS and GCP secret stores.
AWS
-
Give the Workshop service account read access to the secret The Workshop service account is displayed at the top of the Settings page
-
Pass the ARN to the secret prefixed with
aws://
e.g.aws://arn:aws:secretsmanager:us-east-1:940000000003:secret:Secret-YYLN9X
GCP
-
Give the Workshop service account read access to the secret The Workshop service account is displayed at the top of the Settings page
-
Specify the path to the secret as
gcp://projects/<projectID>/secrets/<secretID>/versions/latest
Included Plugins
Workshop's internal plugins include:
VirusTotal
The VirusTotal plugin will check the SHA-256 of the binary against VirusTotal using the file report API.
The VirusTotal plugin will cache results per user defined parameters. This ensures that results are timely and saves expensive API calls.
Options
- API Key - Your VirusTotal Key; this is either the raw string or a secrets URL
- Cache Time - How long in seconds the cache entries should be kept alive for in seconds
- Cache Entries - How many entries to cache
- Excluded Engines - A list of engines to exclude results from
Reversing Labs
The Reversing Labs plugin will check the SHA-256 hash of a binary against
ReversingLabs Spectra file reputation API. If the API deems it malicious it
returns a DENY_MALWARE
response.
The ReversingLabs plugin will cache results per user defined parameters. This ensures that results are timely and saves expensive API calls.
Options
- Username - your reversing labs username
- Password - your reversing labs username or a secret URL
- Cache Entries - How many entries to cache
- Cache Time - How long in seconds the cache entries should be kept alive for
- Cache Entries - How many entries should be cached (up to 50,000)
Blockable Rules
The Blockable Rules plugin allows you to write rules using the Common
Expression Language to match properties of a blockable. A
blockable is a collection of attributes from the binary that Santa or Workshop
policy can be matched on. All matchable attributes will be populated into the
blockable
object.
If the CEL expression returns true then this plugin will return a DENY
decision, otherwise it will return an ALLOW
. You may also use CEL
macros
for working with nested structures such as entitlements.
This is an extremely powerful feature that allows you to flag entire classes of software. Furthermore it allows you to tailor an extremely granular policy.
For example to have the risk engine return a DENY
decision for any
virtualization software you can use the following rule:
has(blockable.entitlements) &&
blockable.entitlements.exists(e, e.key == "com.apple.security.hypervisor") ||
blockable.entitlements.exists(e, e.key == "com.apple.security.virtualization")
You can use all of the attributes of a blockable in rules.
This includes:
sha256
- The SHA-256 of the binarycdhash
- The CDHash of the binarysigning_id
- The signing ID, prefixed with either the team ID or platformteam_id
- The 10-digit alphanumeric Team ID of the binary that uniquely identifies the publishersigned_by
- The certificate chainentitlements
- The array of entitlements provided
Remote Risk Engine Plugins
In addition to the included plugins the Risk Engine can be extended via
Writing Your Own Remote Risk Engine Plugins
To write your own remote risk engine plugin you need to simply create a server
that takes an HTTP POST with JSON consisting of the PluginAuthzRequest
and that
returns an RemoteRiskEnginePluginServiceAuthorizeRequest
serialized to JSON.
The interaction is essentially as follows:
Plugin authors are responsible for TLS and authorization.
Handling Requests
The first step is to make a web service that can receive and unmarshal a
PluginAuthzRequest
.
// A RemoteRiskEnginePluginServiceAuthorizeRequest is a request made by Workshop to
// a plugin to authorize a binary / blockable.
message RemoteRiskEnginePluginServiceAuthorizeRequest {
string tx_id = 1; // The transaction ID of the request.
BinaryBlockable blockable = 2; // The binary to authorize with all blockable attributes.
google.protobuf.Timestamp timestamp = 3; // The timestamp of the request.
google.protobuf.Timestamp deadline = 4; // The deadline for the plugin to return a decision before it is automatically considered a denial.
}
After unmarshaling the RemoteRiskEnginePluginServiceAuthorizeRequest
you can
find all of the details about the binary in the blockable
field. This contains
a subset of the attributes Santa has recorded at the time of execution,
including signing information.
Each request has a transaction ID (tx_id
) field and all responses are expected
to have the same value in their transaction ID field.
Each RemoteRiskEnginePluginServiceAuthorizeRequest
also contains a deadline
that the plugin must respond with a PluginAuthzResponse
before to be
considered. Failure to respond within the deadline will be treated as a if the
plugin had responded with a deny decision.
Once the data from the request has been processed a
RemoteRiskEnginePluginServiceAuthorizeResponse
must be send back to Workshop
with a decision and and explanation for the decision.
The structure of the RemoteRiskEnginePluginServiceAuthorizeResponse
is as
follows:
// This message is used by a remote risk engine plugin to represent the decision
// for a blockable. All errors and timeouts are treated as denials.
message RemoteRiskEnginePluginServiceAuthorizeResponse {
string tx_id = 1; // The transaction ID of the request this response is for.
Decision decision = 2; // The decision for the blockable.
Explanation explanation = 3; // An explanation for the decision.
string error = 4; // An error message containing any errors the plugin encountered.
string plugin_uuid = 5; // The UUID of the plugin that made the decision.
google.protobuf.Timestamp good_until = 6; // The time the decision is considered valid until for caching.
}
Decisions can be one of the following:
Decision | Meaning |
---|---|
UNKNOWN | This is a programming error and should not be used |
DENY | The plugin has determined the binary should be blocked by policy. |
DENY_MALWARE | The plugin has determined the binary is malware and should be blocked |
ALLOW | The plugin believes this binary is safe. |
TIMEOUT | The plugin or something it depends on has timed out |
ERROR | The plugin has encountered an error |
All decisions except for allow are considered a denial.
See the Decision proto for more details
Additionally plugin authors are expected to provide an explanation for the decision and optionally a URL for getting more information. Workshop presents this information to to users and also helps with debugging.
See the Explanation proto for more details
Exceptions
Risk Engine plugin decisions can be overridden using Exceptions. These are created through the UI or API and grant users in a targeted tag an exception to specific Risk Engine plugin decisions. All exceptions include an expiration date after which they no longer apply.
For example, if the Risk Engine's Blockable Rules plugin has a rule banning VPN software e.g.
has(blockable.entitlements) &&
blockable.entitlements.exists(e, e.key == "com.apple.developer.networking.networkextension" && e.value.contains("packet-tunnel-provider-systemextension"))
But you wanted to let members of the tag vpn-access
approve their own
VPN software then you could accomplish this by granting the
exception to tag for the specific Blockable Rule.
Expiry
Exceptions all have an expiration date built into them after which point they will not longer be considered.
This can be updated via both the UI and API.
Configuration
UI
Exceptions can be configured via the UI under the Exceptions tab on the Risk Engine Settings portion of the main Settings page.
API
Exceptions can also be mangaged via the CreateException,UpdateException, ListExceptions, and DeleteException methods in API