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

Add more filtering options to entry count/show and agent count/list #4714

Merged
merged 19 commits into from
Mar 27, 2024
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
20 changes: 20 additions & 0 deletions cmd/spire-server/cli/agent/agent_posix_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,14 @@ var (
Path to the SPIRE Server API socket (default "/tmp/spire-server/private/api.sock")
`
listUsage = `Usage of agent list:
-attestationType string
Filter by attestation type, like join_token or x509pop.
-banned value
Filter based on string received, 'true': banned agents, 'false': not banned agents, other value will return all.
-canReattest value
Filter based on string received, 'true': agents that can reattest, 'false': agents that can't reattest, other value will return all.
-expiresBefore string
Filter by expiration time (format: "2006-01-02 15:04:05 -0700 -07")
-matchSelectorsOn string
The match mode used when filtering by selectors. Options: exact, any, superset and subset (default "superset")
-output value
Expand All @@ -40,8 +48,20 @@ var (
The SPIFFE ID of the agent to evict (agent identity)
`
countUsage = `Usage of agent count:
-attestationType string
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The description doesn't match here.

Filter by attestation type, like join_token or x509pop.
-banned value
Filter based on string received, 'true': banned agents, 'false': not banned agents, other value will return all.
-canReattest value
Filter based on string received, 'true': agents that can reattest, 'false': agents that can't reattest, other value will return all.
-expiresBefore string
Filter by expiration time (format: "2006-01-02 15:04:05 -0700 -07")
-matchSelectorsOn string
The match mode used when filtering by selectors. Options: exact, any, superset and subset (default "superset")
-output value
Desired output format (pretty, json); default: pretty.
-selector value
A colon-delimited type:value selector. Can be used more than once
-socketPath string
Path to the SPIRE Server API socket (default "/tmp/spire-server/private/api.sock")
`
Expand Down
51 changes: 51 additions & 0 deletions cmd/spire-server/cli/agent/agent_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,12 @@ func TestCount(t *testing.T) {
expectedReturnCode: 1,
expectedStderr: common.AddrError,
},
{
name: "Count by expiresBefore: month out of range",
args: []string{"-expiresBefore", "2001-13-05"},
expectedReturnCode: 1,
expectedStderr: "Error: date is not valid: parsing time \"2001-13-05\": month out of range\n",
},
} {
for _, format := range availableFormats {
t.Run(fmt.Sprintf("%s using %s format", tt.name, format), func(t *testing.T) {
Expand Down Expand Up @@ -389,6 +395,45 @@ func TestList(t *testing.T) {
expectedStdoutPretty: "Found 1 attested agent:\n\nSPIFFE ID : spiffe://example.org/spire/agent/agent1",
expectedStdoutJSON: `{"agents":[{"id":{"trust_domain":"example.org","path":"/spire/agent/agent1"},"attestation_type":"","x509svid_serial_number":"","x509svid_expires_at":"0","selectors":[],"banned":false,"can_reattest":true}],"next_page_token":""}`,
},
{
name: "by expiresBefore",
args: []string{"-expiresBefore", "2000-01-01 15:04:05 -0700 -07"},
expectReq: &agentv1.ListAgentsRequest{
Filter: &agentv1.ListAgentsRequest_Filter{
ByExpiresBefore: "2000-01-01 15:04:05 -0700 -07",
},
PageSize: 1000,
},
existentAgents: testAgents,
expectedStdoutPretty: "Found 1 attested agent:\n\nSPIFFE ID : spiffe://example.org/spire/agent/agent1",
expectedStdoutJSON: `{"agents":[{"id":{"trust_domain":"example.org","path":"/spire/agent/agent1"},"attestation_type":"","x509svid_serial_number":"","x509svid_expires_at":"0","selectors":[],"banned":false,"can_reattest":true}],"next_page_token":""}`,
},
{
name: "by banned",
args: []string{"-banned", "true"},
expectReq: &agentv1.ListAgentsRequest{
Filter: &agentv1.ListAgentsRequest_Filter{
ByBanned: wrapperspb.Bool(true),
},
PageSize: 1000,
},
existentAgents: testAgentsWithBanned,
expectedStdoutPretty: "Found 1 attested agent:\n\nSPIFFE ID : spiffe://example.org/spire/agent/banned",
expectedStdoutJSON: `{"agents":[{"id":{"trust_domain":"example.org","path":"/spire/agent/banned"},"attestation_type":"","x509svid_serial_number":"","x509svid_expires_at":"0","selectors":[],"banned":true,"can_reattest":false}],"next_page_token":""}`,
},
{
name: "by canReattest",
args: []string{"-canReattest", "true"},
expectReq: &agentv1.ListAgentsRequest{
Filter: &agentv1.ListAgentsRequest_Filter{
ByCanReattest: wrapperspb.Bool(true),
},
PageSize: 1000,
},
existentAgents: testAgents,
expectedStdoutPretty: "Found 1 attested agent:\n\nSPIFFE ID : spiffe://example.org/spire/agent/agent1",
expectedStdoutJSON: `{"agents":[{"id":{"trust_domain":"example.org","path":"/spire/agent/agent1"},"attestation_type":"","x509svid_serial_number":"","x509svid_expires_at":"0","selectors":[],"banned":false,"can_reattest":true}],"next_page_token":""}`,
},
{
name: "List by selectors: Invalid matcher",
args: []string{"-selector", "foo:bar", "-selector", "bar:baz", "-matchSelectorsOn", "NO-MATCHER"},
Expand All @@ -407,6 +452,12 @@ func TestList(t *testing.T) {
expectedReturnCode: 1,
expectedStderr: common.AddrError,
},
{
name: "List by expiresBefore: month out of range",
args: []string{"-expiresBefore", "2001-13-05"},
expectedReturnCode: 1,
expectedStderr: "Error: date is not valid: parsing time \"2001-13-05\": month out of range\n",
},
} {
for _, format := range availableFormats {
t.Run(fmt.Sprintf("%s using %s format", tt.name, format), func(t *testing.T) {
Expand Down
20 changes: 20 additions & 0 deletions cmd/spire-server/cli/agent/agent_windows_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,14 @@ var (
Desired output format (pretty, json); default: pretty.
`
listUsage = `Usage of agent list:
-attestationType string
Filter by attestation type, like join_token or x509pop.
-banned value
Filter based on string received, 'true': banned agents, 'false': not banned agents, other value will return all.
-canReattest value
Filter based on string received, 'true': agents that can reattest, 'false': agents that can't reattest, other value will return all.
-expiresBefore string
Filter by expiration time (format: "2006-01-02 15:04:05 -0700 -07")
-matchSelectorsOn string
The match mode used when filtering by selectors. Options: exact, any, superset and subset (default "superset")
-namedPipeName string
Expand All @@ -40,10 +48,22 @@ var (
The SPIFFE ID of the agent to evict (agent identity)
`
countUsage = `Usage of agent count:
-attestationType string
Filter by attestation type, like join_token or x509pop.
-banned value
Filter based on string received, 'true': banned agents, 'false': not banned agents, other value will return all.
-canReattest value
Filter based on string received, 'true': agents that can reattest, 'false': agents that can't reattest, other value will return all.
-expiresBefore string
Filter by expiration time (format: "2006-01-02 15:04:05 -0700 -07")
-matchSelectorsOn string
The match mode used when filtering by selectors. Options: exact, any, superset and subset (default "superset")
-namedPipeName string
Pipe name of the SPIRE Server API named pipe (default "\\spire-server\\private\\api")
-output value
Desired output format (pretty, json); default: pretty.
-selector value
A colon-delimited type:value selector. Can be used more than once
`
showUsage = `Usage of agent show:
-namedPipeName string
Expand Down
86 changes: 84 additions & 2 deletions cmd/spire-server/cli/agent/count.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,39 @@ import (
"errors"
"flag"
"fmt"
"time"

"github.com/mitchellh/cli"
agentv1 "github.com/spiffe/spire-api-sdk/proto/spire/api/server/agent/v1"
"github.com/spiffe/spire-api-sdk/proto/spire/api/types"
"github.com/spiffe/spire/cmd/spire-server/util"
commoncli "github.com/spiffe/spire/pkg/common/cli"
"github.com/spiffe/spire/pkg/common/cliprinter"
"google.golang.org/protobuf/types/known/wrapperspb"
)

type countCommand struct {
env *commoncli.Env
// Type and value are delimited by a colon (:)
// ex. "unix:uid:1000" or "spiffe_id:spiffe://example.org/foo"
selectors commoncli.StringsFlag

// Match used when filtering by selectors
matchSelectorsOn string

// Filters agents to those that are banned.
banned commoncli.BoolFlag

// Filters agents by those expires before.
expiresBefore string

// Filters agents to those matching the attestation type.
attestationType string

// Filters agents that can re-attest.
canReattest commoncli.BoolFlag

env *commoncli.Env

printer cliprinter.Printer
}

Expand All @@ -39,8 +62,61 @@ func (*countCommand) Synopsis() string {

// Run counts attested agents
func (c *countCommand) Run(ctx context.Context, _ *commoncli.Env, serverClient util.ServerClient) error {
filter := &agentv1.CountAgentsRequest_Filter{}
if len(c.selectors) > 0 {
matchBehavior, err := parseToSelectorMatch(c.matchSelectorsOn)
if err != nil {
return err
}

selectors := make([]*types.Selector, len(c.selectors))
for i, sel := range c.selectors {
selector, err := util.ParseSelector(sel)
if err != nil {
return fmt.Errorf("error parsing selector %q: %w", sel, err)
}
selectors[i] = selector
}
filter.BySelectorMatch = &types.SelectorMatch{
Selectors: selectors,
Match: matchBehavior,
}
}

if c.expiresBefore != "" {
// Parse the time string into a time.Time object
_, err := time.Parse("2006-01-02 15:04:05 -0700 -07", c.expiresBefore)
if err != nil {
return fmt.Errorf("date is not valid: %w", err)
}
filter.ByExpiresBefore = c.expiresBefore
}

if c.attestationType != "" {
filter.ByAttestationType = c.attestationType
}

// 0: all, 1: can't reattest, 2: can reattest
if c.canReattest == 1 {
filter.ByCanReattest = wrapperspb.Bool(false)
}
if c.canReattest == 2 {
filter.ByCanReattest = wrapperspb.Bool(true)
}

// 0: all, 1: no-banned, 2: banned
if c.banned == 1 {
filter.ByBanned = wrapperspb.Bool(false)
}
if c.banned == 2 {
filter.ByBanned = wrapperspb.Bool(true)
}

agentClient := serverClient.NewAgentClient()
countResponse, err := agentClient.CountAgents(ctx, &agentv1.CountAgentsRequest{})

countResponse, err := agentClient.CountAgents(ctx, &agentv1.CountAgentsRequest{
Filter: filter,
})
if err != nil {
return err
}
Expand All @@ -49,6 +125,12 @@ func (c *countCommand) Run(ctx context.Context, _ *commoncli.Env, serverClient u
}

func (c *countCommand) AppendFlags(fs *flag.FlagSet) {
fs.Var(&c.selectors, "selector", "A colon-delimited type:value selector. Can be used more than once")
fs.StringVar(&c.attestationType, "attestationType", "", "Filter by attestation type, like join_token or x509pop.")
fs.Var(&c.canReattest, "canReattest", "Filter based on string received, 'true': agents that can reattest, 'false': agents that can't reattest, other value will return all.")
fs.Var(&c.banned, "banned", "Filter based on string received, 'true': banned agents, 'false': not banned agents, other value will return all.")
fs.StringVar(&c.expiresBefore, "expiresBefore", "", "Filter by expiration time (format: \"2006-01-02 15:04:05 -0700 -07\")")
fs.StringVar(&c.matchSelectorsOn, "matchSelectorsOn", "superset", "The match mode used when filtering by selectors. Options: exact, any, superset and subset")
cliprinter.AppendFlagWithCustomPretty(&c.printer, fs, c.env, prettyPrintCount)
}

Expand Down
57 changes: 53 additions & 4 deletions cmd/spire-server/cli/agent/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,32 @@ import (
commoncli "github.com/spiffe/spire/pkg/common/cli"
"github.com/spiffe/spire/pkg/common/cliprinter"
"github.com/spiffe/spire/pkg/common/idutil"
"google.golang.org/protobuf/types/known/wrapperspb"
)

type listCommand struct {
env *commoncli.Env
// Type and value are delimited by a colon (:)
// ex. "unix:uid:1000" or "spiffe_id:spiffe://example.org/foo"
selectors commoncli.StringsFlag
// Match used when filtering agents by selectors

// Match used when filtering by selectors
matchSelectorsOn string
printer cliprinter.Printer

// Filters agents to those that are banned.
banned commoncli.BoolFlag

// Filters agents by those expires before.
expiresBefore string

// Filters agents to those matching the attestation type.
attestationType string

// Filters agents that can re-attest.
canReattest commoncli.BoolFlag

env *commoncli.Env

printer cliprinter.Printer
}

// NewListCommand creates a new "list" subcommand for "agent" command.
Expand Down Expand Up @@ -68,6 +84,35 @@ func (c *listCommand) Run(ctx context.Context, _ *commoncli.Env, serverClient ut
}
}

if c.expiresBefore != "" {
// Parse the time string into a time.Time object
_, err := time.Parse("2006-01-02 15:04:05 -0700 -07", c.expiresBefore)
if err != nil {
return fmt.Errorf("date is not valid: %w", err)
}
filter.ByExpiresBefore = c.expiresBefore
}

if c.attestationType != "" {
filter.ByAttestationType = c.attestationType
}

// 0: all, 1: can't reattest, 2: can reattest
if c.canReattest == 1 {
filter.ByCanReattest = wrapperspb.Bool(false)
}
if c.canReattest == 2 {
filter.ByCanReattest = wrapperspb.Bool(true)
}

// 0: all, 1: no-banned, 2: banned
if c.banned == 1 {
filter.ByBanned = wrapperspb.Bool(false)
}
if c.banned == 2 {
filter.ByBanned = wrapperspb.Bool(true)
}

agentClient := serverClient.NewAgentClient()

pageToken := ""
Expand All @@ -91,8 +136,12 @@ func (c *listCommand) Run(ctx context.Context, _ *commoncli.Env, serverClient ut
}

func (c *listCommand) AppendFlags(fs *flag.FlagSet) {
fs.StringVar(&c.matchSelectorsOn, "matchSelectorsOn", "superset", "The match mode used when filtering by selectors. Options: exact, any, superset and subset")
fs.Var(&c.selectors, "selector", "A colon-delimited type:value selector. Can be used more than once")
fs.StringVar(&c.attestationType, "attestationType", "", "Filter by attestation type, like join_token or x509pop.")
fs.Var(&c.canReattest, "canReattest", "Filter based on string received, 'true': agents that can reattest, 'false': agents that can't reattest, other value will return all.")
fs.Var(&c.banned, "banned", "Filter based on string received, 'true': banned agents, 'false': not banned agents, other value will return all.")
fs.StringVar(&c.expiresBefore, "expiresBefore", "", "Filter by expiration time (format: \"2006-01-02 15:04:05 -0700 -07\")")
fs.StringVar(&c.matchSelectorsOn, "matchSelectorsOn", "superset", "The match mode used when filtering by selectors. Options: exact, any, superset and subset")
cliprinter.AppendFlagWithCustomPretty(&c.printer, fs, c.env, prettyPrintAgents)
}

Expand Down
Loading
Loading