kata-containers/src/runtime/virtcontainers/mock_agent.go
Dan Mihai ab829d1038 agent: runtime: add the Agent Policy feature
Fixes: #7573

To enable this feature, build your rootfs using AGENT_POLICY=yes. The
default is AGENT_POLICY=no.

Building rootfs using AGENT_POLICY=yes has the following effects:

1. The kata-opa service gets included in the Guest image.

2. The agent gets built using AGENT_POLICY=yes.

After this patch, the shim calls SetPolicy if and only if a Policy
annotation is attached to the sandbox/pod. When creating a sandbox/pod
that doesn't have an attached Policy annotation:

1. If the agent was built using AGENT_POLICY=yes, the new sandbox uses
   the default agent settings, that might include a default Policy too.

2. If the agent was built using AGENT_POLICY=no, the new sandbox is
   executed the same way as before this patch.

Any SetPolicy calls from the shim to the agent fail if the agent was
built using AGENT_POLICY=no.

If the agent was built using AGENT_POLICY=yes:

1. The agent reads the contents of a default policy file during sandbox
   start-up.

2. The agent then connects to the OPA service on localhost and sends
   the default policy to OPA.

3. If the shim calls SetPolicy:

   a. The agent checks if SetPolicy is allowed by the current
      policy (the current policy is typically the default policy
      mentioned above).

   b. If SetPolicy is allowed, the agent deletes the current policy
      from OPA and replaces it with the new policy it received from
      the shim.

   A typical new policy from the shim doesn't allow any future SetPolicy
   calls.

4. For every agent rpc API call, the agent asks OPA if that call
   should be allowed. OPA allows or not a call based on the current
   policy, the name of the agent API, and the API call's inputs. The
   agent rejects any calls that are rejected by OPA.

When building using AGENT_POLICY_DEBUG=yes, additional Policy logging
gets enabled in the agent. In particular, information about the inputs
for agent rpc API calls is logged in /tmp/policy.txt, on the Guest VM.
These inputs can be useful for investigating API calls that might have
been rejected by the Policy. Examples:

1. Load a failing policy file test1.rego on a different machine:

opa run --server --addr 127.0.0.1:8181 test1.rego

2. Collect the API inputs from Guest's /tmp/policy.txt and test on the
   machine where the failing policy has been loaded:

curl -X POST http://localhost:8181/v1/data/agent_policy/CreateContainerRequest \
--data-binary @test1-inputs.json

Signed-off-by: Dan Mihai <dmihai@microsoft.com>
2023-08-14 17:07:35 +00:00

274 lines
9.2 KiB
Go

// Copyright (c) 2016 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
//
package virtcontainers
import (
"syscall"
"time"
"context"
persistapi "github.com/kata-containers/kata-containers/src/runtime/virtcontainers/persist/api"
pbTypes "github.com/kata-containers/kata-containers/src/runtime/virtcontainers/pkg/agent/protocols"
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/pkg/agent/protocols/grpc"
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/types"
specs "github.com/opencontainers/runtime-spec/specs-go"
)
// mockAgent is an empty Agent implementation, for testing and
// mocking purposes.
type mockAgent struct {
}
// nolint:golint
func NewMockAgent() agent {
return &mockAgent{}
}
// init initializes the Noop agent, i.e. it does nothing.
func (n *mockAgent) init(ctx context.Context, sandbox *Sandbox, config KataAgentConfig) (bool, error) {
return false, nil
}
func (n *mockAgent) longLiveConn() bool {
return false
}
// createSandbox is the Noop agent sandbox creation implementation. It does nothing.
func (n *mockAgent) createSandbox(ctx context.Context, sandbox *Sandbox) error {
return nil
}
// capabilities returns empty capabilities, i.e no capabilties are supported.
func (n *mockAgent) capabilities() types.Capabilities {
return types.Capabilities{}
}
// disconnect is the Noop agent connection closer. It does nothing.
func (n *mockAgent) disconnect(ctx context.Context) error {
return nil
}
// exec is the Noop agent command execution implementation. It does nothing.
func (n *mockAgent) exec(ctx context.Context, sandbox *Sandbox, c Container, cmd types.Cmd) (*Process, error) {
return nil, nil
}
// startSandbox is the Noop agent Sandbox starting implementation. It does nothing.
func (n *mockAgent) startSandbox(ctx context.Context, sandbox *Sandbox) error {
return nil
}
// stopSandbox is the Noop agent Sandbox stopping implementation. It does nothing.
func (n *mockAgent) stopSandbox(ctx context.Context, sandbox *Sandbox) error {
return nil
}
// createContainer is the Noop agent Container creation implementation. It does nothing.
func (n *mockAgent) createContainer(ctx context.Context, sandbox *Sandbox, c *Container) (*Process, error) {
return &Process{}, nil
}
// startContainer is the Noop agent Container starting implementation. It does nothing.
func (n *mockAgent) startContainer(ctx context.Context, sandbox *Sandbox, c *Container) error {
return nil
}
// stopContainer is the Noop agent Container stopping implementation. It does nothing.
func (n *mockAgent) stopContainer(ctx context.Context, sandbox *Sandbox, c Container) error {
return nil
}
// signalProcess is the Noop agent Container signaling implementation. It does nothing.
func (n *mockAgent) signalProcess(ctx context.Context, c *Container, processID string, signal syscall.Signal, all bool) error {
return nil
}
// updateContainer is the Noop agent Container update implementation. It does nothing.
func (n *mockAgent) updateContainer(ctx context.Context, sandbox *Sandbox, c Container, resources specs.LinuxResources) error {
return nil
}
// memHotplugByProbe is the Noop agent notify meomory hotplug event via probe interface implementation. It does nothing.
func (n *mockAgent) memHotplugByProbe(ctx context.Context, addr uint64, sizeMB uint32, memorySectionSizeMB uint32) error {
return nil
}
// onlineCPUMem is the Noop agent Container online CPU and Memory implementation. It does nothing.
func (n *mockAgent) onlineCPUMem(ctx context.Context, cpus uint32, cpuOnly bool) error {
return nil
}
// updateInterface is the Noop agent Interface update implementation. It does nothing.
func (n *mockAgent) updateInterface(ctx context.Context, inf *pbTypes.Interface) (*pbTypes.Interface, error) {
return nil, nil
}
// listInterfaces is the Noop agent Interfaces list implementation. It does nothing.
func (n *mockAgent) listInterfaces(ctx context.Context) ([]*pbTypes.Interface, error) {
return nil, nil
}
// updateRoutes is the Noop agent Routes update implementation. It does nothing.
func (n *mockAgent) updateRoutes(ctx context.Context, routes []*pbTypes.Route) ([]*pbTypes.Route, error) {
return nil, nil
}
// listRoutes is the Noop agent Routes list implementation. It does nothing.
func (n *mockAgent) listRoutes(ctx context.Context) ([]*pbTypes.Route, error) {
return nil, nil
}
// updateEphemeralMounts is the Noop agent updateEphemeralMounts implementation. It does nothing.
func (n *mockAgent) updateEphemeralMounts(ctx context.Context, storages []*grpc.Storage) error {
return nil
}
// check is the Noop agent health checker. It does nothing.
func (n *mockAgent) check(ctx context.Context) error {
return nil
}
// statsContainer is the Noop agent Container stats implementation. It does nothing.
func (n *mockAgent) statsContainer(ctx context.Context, sandbox *Sandbox, c Container) (*ContainerStats, error) {
return &ContainerStats{}, nil
}
// waitProcess is the Noop agent process waiter. It does nothing.
func (n *mockAgent) waitProcess(ctx context.Context, c *Container, processID string) (int32, error) {
return 0, nil
}
// removeStaleVirtiofsShareMounts is the Noop agent removeStaleVirtiofsShareMounts implementation. It does nothing.
func (n *mockAgent) removeStaleVirtiofsShareMounts(ctx context.Context) error {
return nil
}
// winsizeProcess is the Noop agent process tty resizer. It does nothing.
func (n *mockAgent) winsizeProcess(ctx context.Context, c *Container, processID string, height, width uint32) error {
return nil
}
// writeProcessStdin is the Noop agent process stdin writer. It does nothing.
func (n *mockAgent) writeProcessStdin(ctx context.Context, c *Container, ProcessID string, data []byte) (int, error) {
return 0, nil
}
// closeProcessStdin is the Noop agent process stdin closer. It does nothing.
func (n *mockAgent) closeProcessStdin(ctx context.Context, c *Container, ProcessID string) error {
return nil
}
// readProcessStdout is the Noop agent process stdout reader. It does nothing.
func (n *mockAgent) readProcessStdout(ctx context.Context, c *Container, processID string, data []byte) (int, error) {
return 0, nil
}
// readProcessStderr is the Noop agent process stderr reader. It does nothing.
func (n *mockAgent) readProcessStderr(ctx context.Context, c *Container, processID string, data []byte) (int, error) {
return 0, nil
}
// pauseContainer is the Noop agent Container pause implementation. It does nothing.
func (n *mockAgent) pauseContainer(ctx context.Context, sandbox *Sandbox, c Container) error {
return nil
}
// resumeContainer is the Noop agent Container resume implementation. It does nothing.
func (n *mockAgent) resumeContainer(ctx context.Context, sandbox *Sandbox, c Container) error {
return nil
}
// configure is the Noop agent configuration implementation. It does nothing.
func (n *mockAgent) configure(ctx context.Context, h Hypervisor, id, sharePath string, config KataAgentConfig) error {
return nil
}
func (n *mockAgent) configureFromGrpc(ctx context.Context, h Hypervisor, id string, config KataAgentConfig) error {
return nil
}
// reseedRNG is the Noop agent RND reseeder. It does nothing.
func (n *mockAgent) reseedRNG(ctx context.Context, data []byte) error {
return nil
}
// reuseAgent is the Noop agent reuser. It does nothing.
func (n *mockAgent) reuseAgent(agent agent) error {
return nil
}
// getAgentURL is the Noop agent url getter. It returns nothing.
func (n *mockAgent) getAgentURL() (string, error) {
return "", nil
}
// setAgentURL is the Noop agent url setter. It does nothing.
func (n *mockAgent) setAgentURL() error {
return nil
}
// getGuestDetails is the Noop agent GuestDetails queryer. It does nothing.
func (n *mockAgent) getGuestDetails(context.Context, *grpc.GuestDetailsRequest) (*grpc.GuestDetailsResponse, error) {
return nil, nil
}
// setGuestDateTime is the Noop agent guest time setter. It does nothing.
func (n *mockAgent) setGuestDateTime(context.Context, time.Time) error {
return nil
}
// copyFile is the Noop agent copy file. It does nothing.
func (n *mockAgent) copyFile(ctx context.Context, src, dst string) error {
return nil
}
// addSwap is the Noop agent setup swap. It does nothing.
func (n *mockAgent) addSwap(ctx context.Context, PCIPath types.PciPath) error {
return nil
}
func (n *mockAgent) markDead(ctx context.Context) {
}
func (n *mockAgent) cleanup(ctx context.Context) {
}
// save is the Noop agent state saver. It does nothing.
func (n *mockAgent) save() (s persistapi.AgentState) {
return
}
// load is the Noop agent state loader. It does nothing.
func (n *mockAgent) load(s persistapi.AgentState) {}
func (n *mockAgent) getOOMEvent(ctx context.Context) (string, error) {
return "", nil
}
func (n *mockAgent) getAgentMetrics(ctx context.Context, req *grpc.GetMetricsRequest) (*grpc.Metrics, error) {
return nil, nil
}
func (n *mockAgent) getGuestVolumeStats(ctx context.Context, volumeGuestPath string) ([]byte, error) {
return nil, nil
}
func (n *mockAgent) resizeGuestVolume(ctx context.Context, volumeGuestPath string, size uint64) error {
return nil
}
func (k *mockAgent) getIPTables(ctx context.Context, isIPv6 bool) ([]byte, error) {
return nil, nil
}
func (k *mockAgent) setIPTables(ctx context.Context, isIPv6 bool, data []byte) error {
return nil
}
func (k *mockAgent) setPolicy(ctx context.Context, policy string) error {
return nil
}