-
Notifications
You must be signed in to change notification settings - Fork 84
Description
Background
An increasing number of PRs or feature requests from the community propose adding new container mutation capabilities with direct security implications to NRI. See #123, #124, and #135 for some examples.
Based on the review discussions of these PRs it looks like
- there is a desire to extend NRI with such capabilities
- the ability to administratively lock down such capabilities would help accepting them
- some existing capabilities in NRI could be put under such control (OCI hook injection)
Proposed Enhancement
Extend the current implementation of NRI with the notion of restrictions.
Restrictions allow one to disable a subset of the container mutation capabilities present in NRI. Restrictions are defined in the configuration. Initially a single global set of restrictions are defined and they apply to all plugins. This can later be updated to a more fine-grained model with further improvements, if desired.
Restrictions are communicated to an NRI plugin during registration. The plugin can then report and choose not to start up if the configured restrictions prevented it from functioning properly. Initially OCI hook injection would be subject to restrictions. For backward compatibility with the current implementation, OCI hooks would default to an unrestricted configuration.
NRI request/response processing is updated to check a plugin's response for potential restriction violations before applying the requested mutations to containers. If restrictions are violated, the plugin is forcibly disconnected with an error message.
Proposed Implementation
Protocol Additions
Define the set of possible restrictions. Initially this only consist of OCI hook injection.
message Restrictions {
// If set to true, adjustment of OCI hooks is disabled.
bool oci_hooks = 1;
}Include configured restrictions also in the plugin configuration request.
message ConfigureRequest {
// Any plugin-specific data, if present among the NRI configuration.
string config = 1;
// Name of the runtime NRI is running in.
string runtime_name = 2;
// Version of the runtime NRI is running in.
string runtime_version = 3;
// Configured registration timeout in milliseconds.
int64 registration_timeout = 4;
// Configured request processing timeout in milliseconds.
int64 request_timeout = 5;
// Extra restrictions imposed by the runtime.
Restrictions restrictions = 6;
}Add convenience functions for runtime restriction checking.
func (r *Restrictions) CheckAdjustment(a *ContainerAdjustment) error {
if a == nil || r == nil {
return nil
}
if err := r.checkOciHookAdjustment(a); err != nil {
return err
}
return nil
}
func (r *Restrictions) checkOciHookAdjustment(a *ContainerAdjustment) error {
if !r.OciHooks || a.Hooks == nil {
return nil
}
switch {
case a.Hooks.Prestart != nil:
return OciHooksRestricted
case a.Hooks.CreateRuntime != nil:
return OciHooksRestricted
case a.Hooks.CreateContainer != nil:
return OciHooksRestricted
case a.Hooks.StartContainer != nil:
return OciHooksRestricted
case a.Hooks.Poststart != nil:
return OciHooksRestricted
case a.Hooks.Poststop != nil:
return OciHooksRestricted
}
return nil
}Add convenience functions for plugin side permission checks.
func (r *Restrictions) AllowOciHooks() bool {
return r == nil || !r.OciHooks
}Runtime Adaptation Changes
Add a new programmatic option for the NRI runtime adaptation interface to set the configured restrictions. Update the plugin response processing logic to first check a response for any potential violation of configured restrictions. If a violation is found, log an error and disconnect the plugin.
Plugin Stub Changes
Extract restrictions from the configuration message and make it available to the plugin.
Sample Plugin Changes
In the OCI hook injector plugin, check restrictions during plugin configuration logging an error and exiting if OCI hook injection is disallowed.
Changes for Pending PRs
Add restrictions for seccomp policy adjustment.
message Restrictions {
// If set to true, adjustment of OCI hooks is disabled.
bool oci_hooks = 1;
// If set to true, adjustment of seccpomp policy is disabled.
bool seccomp_policy = 2;
}Implement/update restriction checking functions for seccomp policy.
var (
RestrictionError = fmt.Errorf("restriction violation")
OciHooksRestricted = fmt.Errorf("%w: OCI hook adjustment disabled", RestrictionError)
SeccompPolicyRestricted = fmt.Errorf("%w: seccomp policy adjustment disabled, RestrictionError)
)
func (r *Restrictions) CheckAdjustment(a *ContainerAdjustment) error {
if a == nil || r == nil {
return nil
}
if err := r.checkOciHookAdjustment(a); err != nil {
return err
}
if err := r.checkSeccompPolicyAdjustment(a); err != nil {
return err
}
return nil
}
func (r *Restrictions) checkSeccompPolicyAdjustment(a *ContainerAdjustment) error {
if !r.SeccompPolicy || a.Linux == nil || a.Linux.SeccompPolicy == nil {
return nil
}
return SeccompPolicyRestricted
}Add convenience functions for plugin side permissions checks.
func (r *Restrictions) AllowSeccompPolicy() bool {
return r == nil || !r.SeccompPolicy
}Add restrictions for linux namespace adjustment.
message Restrictions {
// If set to true, adjustment of OCI hooks is disabled.
bool oci_hooks = 1;
// If set to true, adjustment of seccpomp policy is disabled.
bool seccomp_policy = 2;
// If set to true, adjustment of linux namespaces is disabled.
bool namespaces = 3;
}Implement/update violation checking for namespaces.
var (
RestrictionError = fmt.Errorf("restriction violation")
OciHooksRestricted = fmt.Errorf("%w: OCI hook adjustment disabled", RestrictionError)
SeccompPolicyRestricted = fmt.Errorf("%w: seccomp policy adjustment disabled, RestrictionError)
NamespacesRestricted = fmt.Errorf("%w: namespace adjustment disabled, RestrictionError)
)
func (r *Restrictions) CheckAdjustment(a *ContainerAdjustment) error {
if a == nil || r == nil {
return nil
}
if err := r.checkOciHookAdjustment(a); err != nil {
return err
}
if err := r.checkSeccompPolicyAdjustment(a); err != nil {
return err
}
if err := r.checkNamespaceAdjustment(a); err != nil {
return err
}
return nil
}
func (r *Restrictions) checkNamespaceAdjustment(a *ContainerAdjustment) error {
if !r.Namespaces || a.Linux == nil || len(a.Linux.Namespaces) == 0 {
return nil
}
return NamespacesRestricted
}Add convenience functions for plugins to check permissions.
func (r *Restrictions) AllowNamespaces() bool {
return r == nil || !r.Namespaces
}Potential Future Improvements
If we implement a mechanism to securely authenticate and identify plugins, this scheme can be updated to allow additional, more fine-grained per plugin (or per authorization level) restrictions.
TODO: Provide a reasonably detailed description of how that would work.