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

Issue #2700: Adds support for X509 and JWT specific SVID TTLs #3445

Merged
merged 6 commits into from
Oct 26, 2022
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
53 changes: 44 additions & 9 deletions cmd/spire-server/cli/entry/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,17 @@ type createCommand struct {
// Workload spiffeID
spiffeID string

// TTL for certificates issued to this workload
// TTL for x509 and JWT SVIDs issued to this workload, unless type specific TTLs are set.
// This field is deprecated in favor of the x509SVIDTTL and jwtSVIDTTL fields and will be
// removed in a future release.
ttl int

// TTL for x509 SVIDs issued to this workload
x509SVIDTTL int

// TTL for JWT SVIDs issued to this workload
jwtSVIDTTL int

// List of SPIFFE IDs of trust domains the registration entry is federated with
federatesWith StringsFlag

Expand Down Expand Up @@ -75,7 +83,9 @@ func (*createCommand) Synopsis() string {
func (c *createCommand) AppendFlags(f *flag.FlagSet) {
f.StringVar(&c.parentID, "parentID", "", "The SPIFFE ID of this record's parent")
f.StringVar(&c.spiffeID, "spiffeID", "", "The SPIFFE ID that this record represents")
f.IntVar(&c.ttl, "ttl", 0, "The lifetime, in seconds, for SVIDs issued based on this registration entry")
f.IntVar(&c.ttl, "ttl", 0, "The lifetime, in seconds, for SVIDs issued based on this registration entry. This flag is deprecated in favor of x509SVIDTTL and jwtSVIDTTL and will be removed in a future version")
f.IntVar(&c.x509SVIDTTL, "x509SVIDTTL", 0, "The lifetime, in seconds, for x509-SVIDs issued based on this registration entry. Overrides ttl flag")
f.IntVar(&c.jwtSVIDTTL, "jwtSVIDTTL", 0, "The lifetime, in seconds, for JWT-SVIDs issued based on this registration entry. Overrides ttl flag")
f.StringVar(&c.path, "data", "", "Path to a file containing registration JSON (optional). If set to '-', read the JSON from stdin.")
f.Var(&c.selectors, "selector", "A colon-delimited type:value selector. Can be used more than once")
f.Var(&c.federatesWith, "federatesWith", "SPIFFE ID of a trust domain to federate with. Can be used more than once")
Expand Down Expand Up @@ -156,6 +166,18 @@ func (c *createCommand) validate() (err error) {
return errors.New("a positive TTL is required")
}

if c.x509SVIDTTL < 0 {
return errors.New("a positive x509-SVID TTL is required")
}

if c.jwtSVIDTTL < 0 {
return errors.New("a positive JWT-SVID TTL is required")
}

if c.ttl > 0 && (c.x509SVIDTTL > 0 || c.jwtSVIDTTL > 0) {
return errors.New("use x509SVIDTTL and jwtSVIDTTL flags or the deprecated ttl flag")
}

return nil
}

Expand All @@ -172,13 +194,26 @@ func (c *createCommand) parseConfig() ([]*types.Entry, error) {
}

e := &types.Entry{
ParentId: parentID,
SpiffeId: spiffeID,
Ttl: int32(c.ttl),
Downstream: c.downstream,
ExpiresAt: c.entryExpiry,
DnsNames: c.dnsNames,
StoreSvid: c.storeSVID,
ParentId: parentID,
SpiffeId: spiffeID,
Downstream: c.downstream,
ExpiresAt: c.entryExpiry,
DnsNames: c.dnsNames,
StoreSvid: c.storeSVID,
X509SvidTtl: int32(c.x509SVIDTTL),
JwtSvidTtl: int32(c.jwtSVIDTTL),
}

// c.ttl is deprecated but usable if the new c.x509Svid field is not used.
// c.ttl should not be used to set the jwtSVIDTTL value because the previous
// behavior was to have a hard-coded 5 minute JWT TTL no matter what the value
// of ttl was set to.
// validate(...) ensures that either the new fields or the deprecated field is
// used, but never a mixture.
//
// https://github.com/spiffe/spire/issues/2700
if e.X509SvidTtl == 0 {
e.X509SvidTtl = int32(c.ttl)
}

selectors := []*types.Selector{}
Expand Down
176 changes: 144 additions & 32 deletions cmd/spire-server/cli/entry/create_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,35 @@ func TestCreate(t *testing.T) {
{Type: "zebra", Value: "zebra:2000"},
{Type: "alpha", Value: "alpha:2000"},
},
Ttl: 60,
X509SvidTtl: 60,
JwtSvidTtl: 30,
FederatesWith: []string{"spiffe://domaina.test", "spiffe://domainb.test"},
Admin: true,
ExpiresAt: 1552410266,
DnsNames: []string{"unu1000", "ung1000"},
Downstream: true,
StoreSvid: true,
},
Status: &types.Status{
Code: int32(codes.OK),
Message: "OK",
},
},
},
}

fakeRespOKFromCmd2 := &entryv1.BatchCreateEntryResponse{
Results: []*entryv1.BatchCreateEntryResponse_Result{
{
Entry: &types.Entry{
Id: "entry-id",
SpiffeId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/workload"},
ParentId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/parent"},
Selectors: []*types.Selector{
{Type: "zebra", Value: "zebra:2000"},
{Type: "alpha", Value: "alpha:2000"},
},
X509SvidTtl: 60,
FederatesWith: []string{"spiffe://domaina.test", "spiffe://domainb.test"},
Admin: true,
ExpiresAt: 1552410266,
Expand All @@ -56,12 +84,13 @@ func TestCreate(t *testing.T) {
Results: []*entryv1.BatchCreateEntryResponse_Result{
{
Entry: &types.Entry{
Id: "entry-id-1",
SpiffeId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/Blog"},
ParentId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/spire/agent/join_token/TokenBlog"},
Selectors: []*types.Selector{{Type: "unix", Value: "uid:1111"}},
Ttl: 200,
Admin: true,
Id: "entry-id-1",
SpiffeId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/Blog"},
ParentId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/spire/agent/join_token/TokenBlog"},
Selectors: []*types.Selector{{Type: "unix", Value: "uid:1111"}},
X509SvidTtl: 200,
JwtSvidTtl: 30,
Admin: true,
},
Status: &types.Status{
Code: int32(codes.OK),
Expand All @@ -70,11 +99,12 @@ func TestCreate(t *testing.T) {
},
{
Entry: &types.Entry{
Id: "entry-id-2",
SpiffeId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/Database"},
ParentId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/spire/agent/join_token/TokenDatabase"},
Selectors: []*types.Selector{{Type: "unix", Value: "uid:1111"}},
Ttl: 200,
Id: "entry-id-2",
SpiffeId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/Database"},
ParentId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/spire/agent/join_token/TokenDatabase"},
Selectors: []*types.Selector{{Type: "unix", Value: "uid:1111"}},
X509SvidTtl: 200,
JwtSvidTtl: 30,
},
Status: &types.Status{
Code: int32(codes.OK),
Expand All @@ -90,8 +120,9 @@ func TestCreate(t *testing.T) {
{Type: "type", Value: "key1:value"},
{Type: "type", Value: "key2:value"},
},
StoreSvid: true,
Ttl: 200,
StoreSvid: true,
X509SvidTtl: 200,
JwtSvidTtl: 30,
},
Status: &types.Status{
Code: int32(codes.OK),
Expand Down Expand Up @@ -147,6 +178,21 @@ func TestCreate(t *testing.T) {
args: []string{"-selector", "unix", "-parentID", "spiffe://example.org/parent", "-spiffeID", "spiffe://example.org/workload", "-ttl", "-10"},
expErr: "Error: a positive TTL is required\n",
},
{
name: "Invalid TTL and X509SvidTtl",
args: []string{"-selector", "unix", "-parentID", "spiffe://example.org/parent", "-spiffeID", "spiffe://example.org/workload", "-ttl", "10", "-x509SVIDTTL", "20"},
expErr: "Error: use x509SVIDTTL and jwtSVIDTTL flags or the deprecated ttl flag\n",
},
{
name: "Invalid TTL and JwtSvidTtl",
args: []string{"-selector", "unix", "-parentID", "spiffe://example.org/parent", "-spiffeID", "spiffe://example.org/workload", "-ttl", "10", "-jwtSVIDTTL", "20"},
expErr: "Error: use x509SVIDTTL and jwtSVIDTTL flags or the deprecated ttl flag\n",
},
{
name: "Invalid TTL and both X509SvidTtl and JwtSvidTtl",
args: []string{"-selector", "unix", "-parentID", "spiffe://example.org/parent", "-spiffeID", "spiffe://example.org/workload", "-ttl", "10", "-x509SVIDTTL", "20", "-jwtSVIDTTL", "30"},
expErr: "Error: use x509SVIDTTL and jwtSVIDTTL flags or the deprecated ttl flag\n",
},
{
name: "Federated node entries",
args: []string{"-selector", "unix", "-spiffeID", "spiffe://example.org/workload", "-node", "-federatesWith", "spiffe://another.org"},
Expand All @@ -172,7 +218,8 @@ func TestCreate(t *testing.T) {
"-parentID", "spiffe://example.org/parent",
"-selector", "zebra:zebra:2000",
"-selector", "alpha:alpha:2000",
"-ttl", "60",
"-x509SVIDTTL", "60",
"-jwtSVIDTTL", "30",
"-federatesWith", "spiffe://domaina.test",
"-federatesWith", "spiffe://domainb.test",
"-admin",
Expand All @@ -191,7 +238,8 @@ func TestCreate(t *testing.T) {
{Type: "zebra", Value: "zebra:2000"},
{Type: "alpha", Value: "alpha:2000"},
},
Ttl: 60,
X509SvidTtl: 60,
JwtSvidTtl: 30,
FederatesWith: []string{"spiffe://domaina.test", "spiffe://domainb.test"},
Admin: true,
ExpiresAt: 1552410266,
Expand All @@ -207,7 +255,64 @@ SPIFFE ID : spiffe://example.org/workload
Parent ID : spiffe://example.org/parent
Revision : 0
Downstream : true
TTL : 60
X509-SVID TTL : 60
JWT-SVID TTL : 30
Expiration time : %s
Selector : zebra:zebra:2000
Selector : alpha:alpha:2000
FederatesWith : spiffe://domaina.test
FederatesWith : spiffe://domainb.test
DNS name : unu1000
DNS name : ung1000
Admin : true
StoreSvid : true

`, time.Unix(1552410266, 0).UTC()),
},
{
name: "Create succeeds using deprecated command line arguments",
args: []string{
"-spiffeID", "spiffe://example.org/workload",
"-parentID", "spiffe://example.org/parent",
"-selector", "zebra:zebra:2000",
"-selector", "alpha:alpha:2000",
"-ttl", "60",
"-federatesWith", "spiffe://domaina.test",
"-federatesWith", "spiffe://domainb.test",
"-admin",
"-entryExpiry", "1552410266",
"-dns", "unu1000",
"-dns", "ung1000",
"-downstream",
"-storeSVID",
},
expReq: &entryv1.BatchCreateEntryRequest{
Entries: []*types.Entry{
{
SpiffeId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/workload"},
ParentId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/parent"},
Selectors: []*types.Selector{
{Type: "zebra", Value: "zebra:2000"},
{Type: "alpha", Value: "alpha:2000"},
},
X509SvidTtl: 60,
FederatesWith: []string{"spiffe://domaina.test", "spiffe://domainb.test"},
Admin: true,
ExpiresAt: 1552410266,
DnsNames: []string{"unu1000", "ung1000"},
Downstream: true,
StoreSvid: true,
},
},
},
fakeResp: fakeRespOKFromCmd2,
expOut: fmt.Sprintf(`Entry ID : entry-id
SPIFFE ID : spiffe://example.org/workload
Parent ID : spiffe://example.org/parent
Revision : 0
Downstream : true
X509-SVID TTL : 60
JWT-SVID TTL : default
Expiration time : %s
Selector : zebra:zebra:2000
Selector : alpha:alpha:2000
Expand All @@ -228,17 +333,19 @@ StoreSvid : true
expReq: &entryv1.BatchCreateEntryRequest{
Entries: []*types.Entry{
{
SpiffeId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/Blog"},
ParentId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/spire/agent/join_token/TokenBlog"},
Selectors: []*types.Selector{{Type: "unix", Value: "uid:1111"}},
Ttl: 200,
Admin: true,
SpiffeId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/Blog"},
ParentId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/spire/agent/join_token/TokenBlog"},
Selectors: []*types.Selector{{Type: "unix", Value: "uid:1111"}},
X509SvidTtl: 200,
JwtSvidTtl: 30,
Admin: true,
},
{
SpiffeId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/Database"},
ParentId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/spire/agent/join_token/TokenDatabase"},
Selectors: []*types.Selector{{Type: "unix", Value: "uid:1111"}},
Ttl: 200,
SpiffeId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/Database"},
ParentId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/spire/agent/join_token/TokenDatabase"},
Selectors: []*types.Selector{{Type: "unix", Value: "uid:1111"}},
X509SvidTtl: 200,
JwtSvidTtl: 30,
},
{
SpiffeId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/storesvid"},
Expand All @@ -247,8 +354,9 @@ StoreSvid : true
{Type: "type", Value: "key1:value"},
{Type: "type", Value: "key2:value"},
},
Ttl: 200,
StoreSvid: true,
X509SvidTtl: 200,
JwtSvidTtl: 30,
StoreSvid: true,
},
},
},
Expand All @@ -257,22 +365,25 @@ StoreSvid : true
SPIFFE ID : spiffe://example.org/Blog
Parent ID : spiffe://example.org/spire/agent/join_token/TokenBlog
Revision : 0
TTL : 200
X509-SVID TTL : 200
JWT-SVID TTL : 30
Selector : unix:uid:1111
Admin : true

Entry ID : entry-id-2
SPIFFE ID : spiffe://example.org/Database
Parent ID : spiffe://example.org/spire/agent/join_token/TokenDatabase
Revision : 0
TTL : 200
X509-SVID TTL : 200
JWT-SVID TTL : 30
Selector : unix:uid:1111

Entry ID : entry-id-3
SPIFFE ID : spiffe://example.org/storesvid
Parent ID : spiffe://example.org/spire/agent/join_token/TokenDatabase
Revision : 0
TTL : 200
X509-SVID TTL : 200
JWT-SVID TTL : 30
Selector : type:key1:value
Selector : type:key2:value
StoreSvid : true
Expand All @@ -295,7 +406,8 @@ Entry ID : (none)
SPIFFE ID : spiffe://example.org/already-exist
Parent ID : spiffe://example.org/spire/server
Revision : 0
TTL : default
X509-SVID TTL : default
JWT-SVID TTL : default
Selector : unix:uid:1

Error: failed to create one or more entries
Expand Down
12 changes: 8 additions & 4 deletions cmd/spire-server/cli/entry/show_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -415,7 +415,8 @@ func getPrintedEntry(idx int) string {
SPIFFE ID : spiffe://example.org/son
Parent ID : spiffe://example.org/father
Revision : 0
TTL : default
X509-SVID TTL : default
JWT-SVID TTL : default
Selector : foo:bar

`
Expand All @@ -424,7 +425,8 @@ Selector : foo:bar
SPIFFE ID : spiffe://example.org/daughter
Parent ID : spiffe://example.org/father
Revision : 0
TTL : default
X509-SVID TTL : default
JWT-SVID TTL : default
Selector : bar:baz
Selector : foo:bar

Expand All @@ -434,7 +436,8 @@ Selector : foo:bar
SPIFFE ID : spiffe://example.org/daughter
Parent ID : spiffe://example.org/mother
Revision : 0
TTL : default
X509-SVID TTL : default
JWT-SVID TTL : default
Selector : bar:baz
Selector : baz:bat
FederatesWith : spiffe://domain.test
Expand All @@ -445,7 +448,8 @@ FederatesWith : spiffe://domain.test
SPIFFE ID : spiffe://example.org/son
Parent ID : spiffe://example.org/mother
Revision : 0
TTL : default
X509-SVID TTL : default
JWT-SVID TTL : default
Expiration time : %s
Selector : baz:bat

Expand Down
Loading