Skip to content
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

RFC - Support safe subdirectory-based plugin conf loading #1052

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 8 additions & 7 deletions SPEC.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,14 +109,16 @@ A network configuration consists of a JSON object with the following keys:

- `cniVersion` (string): [Semantic Version 2.0](https://semver.org) of CNI specification to which this configuration list and all the individual configurations conform. Currently "1.1.0"
- `cniVersions` (string list): List of all CNI versions which this configuration supports. See [version selection](#version-selection) below.
- `name` (string): Network name. This should be unique across all network configurations on a host (or other administrative domain). Must start with an alphanumeric character, optionally followed by any combination of one or more alphanumeric characters, underscore, dot (.) or hyphen (-).
- `name` (string): Network name. This should be unique across all network configurations on a host (or other administrative domain). Must start with an alphanumeric character, optionally followed by any combination of one or more alphanumeric characters, underscore, dot (.) or hyphen (-). Must not contain characters disallowed in file paths.
- `disableCheck` (boolean): Either `true` or `false`. If `disableCheck` is `true`, runtimes must not call `CHECK` for this network configuration list. This allows an administrator to prevent `CHECK`ing where a combination of plugins is known to return spurious errors.
- `disableGC` (boolean): Either `true` or `false`. If `disableGC` is `true`, runtimes must not call `GC` for this network configuration list. This allows an administrator to prevent `GC`ing when it is known that garbage collection may have undesired effects (e.g. shared configuration between multiple runtimes).
- `plugins` (list): A list of CNI plugins and their configuration, which is a list of plugin configuration objects.
- `loadOnlyInlinedPlugins` (boolean): Either `true` or `false`. If `false` (default), indicates [plugin configuration objects](#plugin-configuration-objects) can be aggregated from multiple sources. Any valid plugin configuration objects aggregated from other sources must be appended to the final list of `plugins` for that network name. If set to `true`, indicates that valid plugin configuration objects aggregated from sources other than the main network configuration will be ignored. If `plugins` is not present in the network configuration, `loadOnlyInlinedPlugins` cannot be set to `true`.
- `plugins` (list): A list of inlined [plugin configuration objects](#plugin-configuration-objects). If this key is populated with inlined plugin objects, and `loadOnlyInlinedPlugins` is true, the final set of plugins for a network must consist of all the plugin objects in this list, merged with all the plugins loaded from the sibling folder with the same name as the network.

#### Plugin configuration objects:
Plugin configuration objects may contain additional fields than the ones defined here.
The runtime MUST pass through these fields, unchanged, to the plugin, as defined in section 3.
Runtimes may aggregate plugin configuration objects from multiple sources, and must unambiguously associate each loaded plugin configuration object with a single, valid network configuration. All aggregated plugin configuration objects must be validated, and each plugin with a valid configuration object must be invoked.

Plugin configuration objects may contain additional fields beyond the ones defined here. The runtime MUST pass through these fields, unchanged, to the invoked plugin, as defined in section 3.

**Required keys:**
- `type` (string): Matches the name of the CNI plugin binary on disk. Must not contain characters disallowed in file paths for the system (e.g. / or \\).
Expand Down Expand Up @@ -147,6 +149,7 @@ Plugins that consume any of these configuration keys should respect their intend
Plugins may define additional fields that they accept and may generate an error if called with unknown fields. Runtimes must preserve unknown fields in plugin configuration objects when transforming for execution.

#### Example configuration
The following is an example JSON representation of a network configuration `dbnet` with three plugin configurations (`bridge`, `tuning`, and `portmap`).
```jsonc
{
"cniVersion": "1.1.0",
Expand All @@ -158,7 +161,7 @@ Plugins may define additional fields that they accept and may generate an error
// plugin specific parameters
"bridge": "cni0",
"keyA": ["some more", "plugin specific", "configuration"],

"ipam": {
"type": "host-local",
// ipam specific
Expand Down Expand Up @@ -375,8 +378,6 @@ Resources may, for example, include:

A plugin SHOULD remove as many stale resources as possible. For example, a plugin should remove any IPAM reservations associated with attachments not in the provided list. The plugin MAY assume that the isolation domain (e.g. network namespace) has been deleted, and thus any resources (e.g. network interfaces) therein have been removed.

Garbage collection is a per-network operation. If a plugin manages resources shared across multiple networks, it must only remove stale resources known to belong to the network provided in the `GC `action.

Plugins should generally complete a `GC` action without error. If an error is encountered, a plugin should continue; removing as many resources as possible, and report the errors back to the runtime.

Plugins MUST, additionally, forward any GC calls to delegated plugins they are configured to use (see section 4).
Expand Down
2 changes: 1 addition & 1 deletion cnitool/cnitool.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ func main() {
if netdir == "" {
netdir = DefaultNetDir
}
netconf, err := libcni.LoadConfList(netdir, os.Args[2])
netconf, err := libcni.LoadNetworkConf(netdir, os.Args[2])
if err != nil {
exit(err)
}
Expand Down
61 changes: 33 additions & 28 deletions libcni/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,18 +67,23 @@ type RuntimeConf struct {
CacheDir string
}

type NetworkConfig struct {
Network *types.NetConf
// Deprecated: Use PluginConfig instead of NetworkConfig, the NetworkConfig
// backwards-compat alias will be removed in a future release.
type NetworkConfig = PluginConfig

type PluginConfig struct {
Network *types.PluginConf
Bytes []byte
}

type NetworkConfigList struct {
Name string
CNIVersion string
DisableCheck bool
DisableGC bool
Plugins []*NetworkConfig
Bytes []byte
Name string
CNIVersion string
DisableCheck bool
DisableGC bool
LoadOnlyInlinedPlugins bool
Plugins []*PluginConfig
Bytes []byte
}

type NetworkAttachment struct {
Expand All @@ -102,14 +107,14 @@ type CNI interface {
GetNetworkListCachedResult(net *NetworkConfigList, rt *RuntimeConf) (types.Result, error)
GetNetworkListCachedConfig(net *NetworkConfigList, rt *RuntimeConf) ([]byte, *RuntimeConf, error)

AddNetwork(ctx context.Context, net *NetworkConfig, rt *RuntimeConf) (types.Result, error)
CheckNetwork(ctx context.Context, net *NetworkConfig, rt *RuntimeConf) error
DelNetwork(ctx context.Context, net *NetworkConfig, rt *RuntimeConf) error
GetNetworkCachedResult(net *NetworkConfig, rt *RuntimeConf) (types.Result, error)
GetNetworkCachedConfig(net *NetworkConfig, rt *RuntimeConf) ([]byte, *RuntimeConf, error)
AddNetwork(ctx context.Context, net *PluginConfig, rt *RuntimeConf) (types.Result, error)
CheckNetwork(ctx context.Context, net *PluginConfig, rt *RuntimeConf) error
DelNetwork(ctx context.Context, net *PluginConfig, rt *RuntimeConf) error
GetNetworkCachedResult(net *PluginConfig, rt *RuntimeConf) (types.Result, error)
GetNetworkCachedConfig(net *PluginConfig, rt *RuntimeConf) ([]byte, *RuntimeConf, error)

ValidateNetworkList(ctx context.Context, net *NetworkConfigList) ([]string, error)
ValidateNetwork(ctx context.Context, net *NetworkConfig) ([]string, error)
ValidateNetwork(ctx context.Context, net *PluginConfig) ([]string, error)

GCNetworkList(ctx context.Context, net *NetworkConfigList, args *GCArgs) error
GetStatusNetworkList(ctx context.Context, net *NetworkConfigList) error
Expand Down Expand Up @@ -147,7 +152,7 @@ func NewCNIConfigWithCacheDir(path []string, cacheDir string, exec invoke.Exec)
}
}

func buildOneConfig(name, cniVersion string, orig *NetworkConfig, prevResult types.Result, rt *RuntimeConf) (*NetworkConfig, error) {
func buildOneConfig(name, cniVersion string, orig *PluginConfig, prevResult types.Result, rt *RuntimeConf) (*PluginConfig, error) {
var err error

inject := map[string]interface{}{
Expand Down Expand Up @@ -183,7 +188,7 @@ func buildOneConfig(name, cniVersion string, orig *NetworkConfig, prevResult typ
// capabilities include "portMappings", and the CapabilityArgs map includes a
// "portMappings" key, that key and its value are added to the "runtimeConfig"
// dictionary to be passed to the plugin's stdin.
func injectRuntimeConfig(orig *NetworkConfig, rt *RuntimeConf) (*NetworkConfig, error) {
func injectRuntimeConfig(orig *PluginConfig, rt *RuntimeConf) (*PluginConfig, error) {
var err error

rc := make(map[string]interface{})
Expand Down Expand Up @@ -404,7 +409,7 @@ func (c *CNIConfig) GetNetworkListCachedResult(list *NetworkConfigList, rt *Runt

// GetNetworkCachedResult returns the cached Result of the previous
// AddNetwork() operation for a network, or an error.
func (c *CNIConfig) GetNetworkCachedResult(net *NetworkConfig, rt *RuntimeConf) (types.Result, error) {
func (c *CNIConfig) GetNetworkCachedResult(net *PluginConfig, rt *RuntimeConf) (types.Result, error) {
return c.getCachedResult(net.Network.Name, net.Network.CNIVersion, rt)
}

Expand All @@ -416,7 +421,7 @@ func (c *CNIConfig) GetNetworkListCachedConfig(list *NetworkConfigList, rt *Runt

// GetNetworkCachedConfig copies the input RuntimeConf to output
// RuntimeConf with fields updated with info from the cached Config.
func (c *CNIConfig) GetNetworkCachedConfig(net *NetworkConfig, rt *RuntimeConf) ([]byte, *RuntimeConf, error) {
func (c *CNIConfig) GetNetworkCachedConfig(net *PluginConfig, rt *RuntimeConf) ([]byte, *RuntimeConf, error) {
return c.getCachedConfig(net.Network.Name, rt)
}

Expand Down Expand Up @@ -482,7 +487,7 @@ func (c *CNIConfig) GetCachedAttachments(containerID string) ([]*NetworkAttachme
return attachments, nil
}

func (c *CNIConfig) addNetwork(ctx context.Context, name, cniVersion string, net *NetworkConfig, prevResult types.Result, rt *RuntimeConf) (types.Result, error) {
func (c *CNIConfig) addNetwork(ctx context.Context, name, cniVersion string, net *PluginConfig, prevResult types.Result, rt *RuntimeConf) (types.Result, error) {
c.ensureExec()
pluginPath, err := c.exec.FindInPath(net.Network.Type, c.Path)
if err != nil {
Expand Down Expand Up @@ -524,7 +529,7 @@ func (c *CNIConfig) AddNetworkList(ctx context.Context, list *NetworkConfigList,
return result, nil
}

func (c *CNIConfig) checkNetwork(ctx context.Context, name, cniVersion string, net *NetworkConfig, prevResult types.Result, rt *RuntimeConf) error {
func (c *CNIConfig) checkNetwork(ctx context.Context, name, cniVersion string, net *PluginConfig, prevResult types.Result, rt *RuntimeConf) error {
c.ensureExec()
pluginPath, err := c.exec.FindInPath(net.Network.Type, c.Path)
if err != nil {
Expand Down Expand Up @@ -566,7 +571,7 @@ func (c *CNIConfig) CheckNetworkList(ctx context.Context, list *NetworkConfigLis
return nil
}

func (c *CNIConfig) delNetwork(ctx context.Context, name, cniVersion string, net *NetworkConfig, prevResult types.Result, rt *RuntimeConf) error {
func (c *CNIConfig) delNetwork(ctx context.Context, name, cniVersion string, net *PluginConfig, prevResult types.Result, rt *RuntimeConf) error {
c.ensureExec()
pluginPath, err := c.exec.FindInPath(net.Network.Type, c.Path)
if err != nil {
Expand Down Expand Up @@ -607,7 +612,7 @@ func (c *CNIConfig) DelNetworkList(ctx context.Context, list *NetworkConfigList,
return nil
}

func pluginDescription(net *types.NetConf) string {
func pluginDescription(net *types.PluginConf) string {
if net == nil {
return "<missing>"
}
Expand All @@ -621,7 +626,7 @@ func pluginDescription(net *types.NetConf) string {
}

// AddNetwork executes the plugin with the ADD command
func (c *CNIConfig) AddNetwork(ctx context.Context, net *NetworkConfig, rt *RuntimeConf) (types.Result, error) {
func (c *CNIConfig) AddNetwork(ctx context.Context, net *PluginConfig, rt *RuntimeConf) (types.Result, error) {
result, err := c.addNetwork(ctx, net.Network.Name, net.Network.CNIVersion, net, nil, rt)
if err != nil {
return nil, err
Expand All @@ -635,7 +640,7 @@ func (c *CNIConfig) AddNetwork(ctx context.Context, net *NetworkConfig, rt *Runt
}

// CheckNetwork executes the plugin with the CHECK command
func (c *CNIConfig) CheckNetwork(ctx context.Context, net *NetworkConfig, rt *RuntimeConf) error {
func (c *CNIConfig) CheckNetwork(ctx context.Context, net *PluginConfig, rt *RuntimeConf) error {
// CHECK was added in CNI spec version 0.4.0 and higher
if gtet, err := version.GreaterThanOrEqualTo(net.Network.CNIVersion, "0.4.0"); err != nil {
return err
Expand All @@ -651,7 +656,7 @@ func (c *CNIConfig) CheckNetwork(ctx context.Context, net *NetworkConfig, rt *Ru
}

// DelNetwork executes the plugin with the DEL command
func (c *CNIConfig) DelNetwork(ctx context.Context, net *NetworkConfig, rt *RuntimeConf) error {
func (c *CNIConfig) DelNetwork(ctx context.Context, net *PluginConfig, rt *RuntimeConf) error {
var cachedResult types.Result

// Cached result on DEL was added in CNI spec version 0.4.0 and higher
Expand Down Expand Up @@ -711,7 +716,7 @@ func (c *CNIConfig) ValidateNetworkList(ctx context.Context, list *NetworkConfig
// ValidateNetwork checks that a configuration is reasonably valid.
// It uses the same logic as ValidateNetworkList)
// Returns a list of capabilities
func (c *CNIConfig) ValidateNetwork(ctx context.Context, net *NetworkConfig) ([]string, error) {
func (c *CNIConfig) ValidateNetwork(ctx context.Context, net *PluginConfig) ([]string, error) {
caps := []string{}
for c, ok := range net.Network.Capabilities {
if ok {
Expand Down Expand Up @@ -834,7 +839,7 @@ func (c *CNIConfig) GCNetworkList(ctx context.Context, list *NetworkConfigList,
return errors.Join(errs...)
}

func (c *CNIConfig) gcNetwork(ctx context.Context, net *NetworkConfig) error {
func (c *CNIConfig) gcNetwork(ctx context.Context, net *PluginConfig) error {
c.ensureExec()
pluginPath, err := c.exec.FindInPath(net.Network.Type, c.Path)
if err != nil {
Expand Down Expand Up @@ -869,7 +874,7 @@ func (c *CNIConfig) GetStatusNetworkList(ctx context.Context, list *NetworkConfi
return nil
}

func (c *CNIConfig) getStatusNetwork(ctx context.Context, net *NetworkConfig) error {
func (c *CNIConfig) getStatusNetwork(ctx context.Context, net *PluginConfig) error {
c.ensureExec()
pluginPath, err := c.exec.FindInPath(net.Network.Type, c.Path)
if err != nil {
Expand Down
14 changes: 7 additions & 7 deletions libcni/api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ var _ = Describe("Invoking plugins", func() {
pluginConfig []byte
cniConfig *libcni.CNIConfig
runtimeConfig *libcni.RuntimeConf
netConfig *libcni.NetworkConfig
netConfig *libcni.PluginConfig
ctx context.Context
)

Expand Down Expand Up @@ -295,7 +295,7 @@ var _ = Describe("Invoking plugins", func() {
debug *noop_debug.Debug
pluginConfig string
cniConfig *libcni.CNIConfig
netConfig *libcni.NetworkConfig
netConfig *libcni.PluginConfig
runtimeConfig *libcni.RuntimeConf
ctx context.Context

Expand Down Expand Up @@ -1636,7 +1636,7 @@ var _ = Describe("Invoking plugins", func() {
cniBinPath string
pluginConfig string
cniConfig *libcni.CNIConfig
netConfig *libcni.NetworkConfig
netConfig *libcni.PluginConfig
runtimeConfig *libcni.RuntimeConf
netConfigList *libcni.NetworkConfigList
)
Expand Down Expand Up @@ -1809,7 +1809,7 @@ var _ = Describe("Invoking plugins", func() {
cniBinPath string
pluginConfig string
cniConfig *libcni.CNIConfig
netConfig *libcni.NetworkConfig
netConfig *libcni.PluginConfig
runtimeConfig *libcni.RuntimeConf

ctx context.Context
Expand Down Expand Up @@ -1931,14 +1931,14 @@ var _ = Describe("Invoking plugins", func() {
Context("when the RuntimeConf is incomplete", func() {
var (
testRt *libcni.RuntimeConf
testNetConf *libcni.NetworkConfig
testNetConf *libcni.PluginConfig
testNetConfList *libcni.NetworkConfigList
)

BeforeEach(func() {
testRt = &libcni.RuntimeConf{}
testNetConf = &libcni.NetworkConfig{
Network: &types.NetConf{},
testNetConf = &libcni.PluginConfig{
Network: &types.PluginConf{},
}
testNetConfList = &libcni.NetworkConfigList{}
})
Expand Down
Loading
Loading