Skip to content
This repository has been archived by the owner on Sep 19, 2024. It is now read-only.

Commit

Permalink
add attestation cache
Browse files Browse the repository at this point in the history
with init of current processes at startup
  • Loading branch information
waynz0r committed Nov 13, 2023
1 parent 4114d5f commit b798850
Show file tree
Hide file tree
Showing 7 changed files with 156 additions and 293 deletions.
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ require (
github.com/google/uuid v1.3.0
github.com/iancoleman/strcase v0.3.0
github.com/iand/logfmtr v0.2.2
github.com/jellydator/ttlcache/v3 v3.1.0
github.com/mattn/go-isatty v0.0.19
github.com/shirou/gopsutil/v3 v3.23.8
github.com/spf13/cobra v1.7.0
Expand Down Expand Up @@ -68,6 +69,7 @@ require (
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/mod v0.10.0 // indirect
golang.org/x/net v0.17.0 // indirect
golang.org/x/sync v0.3.0 // indirect
golang.org/x/sys v0.13.0 // indirect
golang.org/x/term v0.13.0 // indirect
golang.org/x/text v0.13.0 // indirect
Expand Down
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,8 @@ github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/jellydator/ttlcache/v3 v3.1.0 h1:0gPFG0IHHP6xyUyXq+JaD8fwkDCqgqwohXNJBcYE71g=
github.com/jellydator/ttlcache/v3 v3.1.0/go.mod h1:hi7MGFdMAwZna5n2tuvh63DvFLzVKySzCVW6+0gA2n4=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
Expand Down Expand Up @@ -285,6 +287,8 @@ go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A=
go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4=
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
Expand Down
275 changes: 1 addition & 274 deletions go.work.sum

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion internal/cli/cmd/agent/agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ func (c *agentCommand) runCommander(ctx context.Context) error {
h.AddHandler("csr_sign", csrSign)

attestor := attest.GetWorkloadAttestor(c.cli.Configuration(), c.cli.Logger())
h.AddHandler("attest", commands.Attest(attestor))
h.AddHandler("attest", commands.Attest(ctx, attestor, c.cli.Logger()))

if err := h.Run(ctx); err != nil {
return err
Expand Down
148 changes: 131 additions & 17 deletions pkg/agent/commands/attest.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,32 +22,61 @@ package commands
import (
"context"
"encoding/json"
"errors"
"fmt"
"os"
"path/filepath"
"strconv"
"strings"
"time"

"github.com/go-logr/logr"
"github.com/jellydator/ttlcache/v3"
"github.com/shirou/gopsutil/v3/process"

"github.com/cisco-open/nasp/pkg/agent/messenger"
"github.com/cisco-open/nasp/pkg/plugin/workloadattestor"
"github.com/cisco-open/nasp/pkg/plugin/workloadattestor/docker"
"github.com/cisco-open/nasp/pkg/plugin/workloadattestor/linux"
)

type attestCommand struct {
attestor workloadattestor.WorkloadAttestors
logger logr.Logger
cache *ttlcache.Cache[string, string]
}

func Attest(attestor workloadattestor.WorkloadAttestors) CommandHandler {
return &attestCommand{
func Attest(ctx context.Context, attestor workloadattestor.WorkloadAttestors, logger logr.Logger) CommandHandler {
cmd := &attestCommand{
attestor: attestor,
logger: logger,
cache: ttlcache.New[string, string](
ttlcache.WithTTL[string, string](5 * time.Minute),
),
}

go func() {
cmd.cache.Start()
<-ctx.Done()
cmd.cache.Stop()
}()

cmd.init()

return cmd
}

func (c *attestCommand) HandleCommand(cmd messenger.Command) (string, error) {
var data struct {
SourceIP string `json:"source_ip,omitempty"`
SourcePort int32 `json:"source_port,omitempty"`
DestinationIP string `json:"destination_ip,omitempty"`
DestinationPort int32 `json:"destination_port,omitempty"`
}
c.cache.DeleteExpired()

if err := json.Unmarshal([]byte(cmd.Data), &data); err != nil {
return "error", err
cacheKey := cmd.Context.UniqueString()

logger := c.logger.WithValues("pid", cmd.Context.PID, "path", cmd.Context.CommandPath, "cacheKey", cacheKey)

if item := c.cache.Get(cacheKey); item != nil {
logger.V(1).Info("attest response cache hit")

return item.Value(), nil
}

tags, err := c.attestor.Attest(context.Background(), int32(cmd.Context.PID))
Expand All @@ -58,14 +87,8 @@ func (c *attestCommand) HandleCommand(cmd messenger.Command) (string, error) {
var response struct {
Selectors map[string]bool `json:"selectors"`
}

response.Selectors = make(map[string]bool)

response.Selectors[fmt.Sprintf("source:ip:%s", data.SourceIP)] = true
response.Selectors[fmt.Sprintf("source:port:%d", data.SourcePort)] = true
response.Selectors[fmt.Sprintf("destination:ip:%s", data.DestinationIP)] = true
response.Selectors[fmt.Sprintf("destination:port:%d", data.DestinationPort)] = true

for _, v := range tags.Entries {
response.Selectors[fmt.Sprintf("%s:%s", v.Key, v.Value)] = true
}
Expand All @@ -75,5 +98,96 @@ func (c *attestCommand) HandleCommand(cmd messenger.Command) (string, error) {
return "error", err
}

return string(j), nil
js := string(j)
c.cache.Set(cmd.Context.UniqueString(), js, ttlcache.DefaultTTL)
logger.V(2).Info("attest response cached")

return js, nil
}

func (c *attestCommand) init() {
pids, err := process.Pids()
if err != nil {
c.logger.Error(err, "could not get pids")
}

for _, pid := range pids {
logger := c.logger.WithValues("pid", pid)
cmdCtx, err := getCMDContextFromPID(pid)
if err != nil && !errors.Is(err, os.ErrNotExist) && !errors.Is(err, os.ErrPermission) {
logger.Error(err, "could not get command context for pid")
continue
}

c.HandleCommand(messenger.Command{
Context: cmdCtx,
})
}
}

func getCMDContextFromPID(pid int32) (ctx messenger.CommandContext, err error) {
p, err := process.NewProcess(pid)
if err != nil {
return ctx, err
}

ctx.PID = int(pid)

if cgroups, err := docker.GetCgroups(pid); err != nil {
return ctx, err
} else if len(cgroups) > 0 {
ctx.CGroupPath = cgroups[0].CGroupPath
}

ctx.CommandName, err = p.Name()
if err != nil {
return ctx, err
}

ctx.CommandPath, err = p.Exe()
if err != nil {
return ctx, err
}

if uids, err := p.Uids(); err != nil {
return ctx, err
} else if len(uids) > 0 {
ctx.UID = int(uids[0])
}

if gids, err := p.Gids(); err != nil {
return ctx, err
} else if len(gids) > 0 {
ctx.GID = int(gids[0])
}

base := linux.GetProcPath(pid, "ns")

entries, err := os.ReadDir(base)
if err != nil {
return ctx, err
}

namespaceIDs := map[string]int{}

for _, entry := range entries {
s, err := os.Readlink(filepath.Join(base, entry.Name()))
if err != nil {
return ctx, err
}

if id, err := strconv.Atoi(s[strings.IndexByte(s, '[')+1 : strings.IndexByte(s, ']')]); err == nil {
namespaceIDs[entry.Name()] = id
}
}

ctx.NamespaceIDs.CGroup = int64(namespaceIDs["cgroup"])
ctx.NamespaceIDs.IPC = int64(namespaceIDs["ipc"])
ctx.NamespaceIDs.Mount = int64(namespaceIDs["mnt"])
ctx.NamespaceIDs.Network = int64(namespaceIDs["net"])
ctx.NamespaceIDs.PID = int64(namespaceIDs["pid"])
ctx.NamespaceIDs.Time = int64(namespaceIDs["time"])
ctx.NamespaceIDs.UTS = int64(namespaceIDs["uts"])

return ctx, nil
}
1 change: 1 addition & 0 deletions pkg/agent/commands/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ func (h *handler) handleCommand(topic string, msg messenger.Message) {
cmd, err := messenger.GetCommand(msg)
if err != nil {
h.logger.Error(err, "could not get command from incoming message")
return
}

h.logger.Info("incoming command", "type", cmd.Command, "id", cmd.ID, "context", cmd.Context)
Expand Down
17 changes: 16 additions & 1 deletion pkg/agent/messenger/command_msg.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,11 @@

package messenger

import "emperror.dev/errors"
import (
"fmt"

"emperror.dev/errors"
)

const CommandMessageType MessageType = "command"

Expand Down Expand Up @@ -53,6 +57,17 @@ type CommandContext struct {
CommandName string `json:"command_name,omitempty"`
CommandPath string `json:"command_path,omitempty"`
NamespaceIDs NamespaceIDs `json:"namespace_ids,omitempty"`
CGroupPath string `json:"cgroup_path,omitempty"`
}

func (c CommandContext) UniqueString() string {
return fmt.Sprintf("%s-%d-%d-%s-%s",
c.CGroupPath,
c.UID,
c.GID,
c.CommandName,
c.CommandPath,
)
}

type NamespaceIDs struct {
Expand Down

0 comments on commit b798850

Please sign in to comment.