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 the ability to view and list of leases metadata #2650

Merged
merged 18 commits into from
May 4, 2017
Merged

Conversation

chrishoffman
Copy link
Contributor

@chrishoffman chrishoffman commented Apr 27, 2017

This adds the ability to view and list lease metadata. There are currently no methods to view lease metadata or to list leases by a given prefix. Since there are methods that exist to revoke individual or a prefix of leases and renew individual leases, it makes sense to have endpoints that allow you to view this data as well. The data that will be returned will only be metadata about the lease and will not return any internal lease data.

@jefferai jefferai added this to the 0.7.1 milestone Apr 27, 2017
return handleError(err)
}

return &logical.Response{
Copy link
Member

Choose a reason for hiding this comment

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

Check logical.ListResponse, which does this for you!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Magic :).

func (b *SystemBackend) handleLease(
req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
leaseID := data.Get("lease_id").(string)

Copy link
Member

Choose a reason for hiding this comment

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

Can we have a check for leaseID == "" here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Will do.

Data: map[string]interface{}{
"lease_id": leaseID,
"issue_time": leaseTimes.IssueTime,
"expire_time": nil,
Copy link
Member

Choose a reason for hiding this comment

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

Is there a reason for setting these 2 fields explicitly to nil?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This was really just to serialize to null in the serializer. I guess omitting would probably be fine.

}

// Read a key with a LeaseID
req = logical.TestRequest(t, logical.ReadOperation, "secret/foo")
Copy link
Member

Choose a reason for hiding this comment

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

Generic secret backend does not attach a lease to secrets. I'm not sure if this test would work.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It does and I was surprised too and this how all the renew and revoke functions are written. I think it might be something in the test harness. Otherwise it isn't straightforward creating a non-token lease without some backing dynamic backend. Maybe pki with generate_lease set true.

Copy link
Member

Choose a reason for hiding this comment

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

When we removed leases from the generic backend in the normal case we still kept the ability to have it issue leases specifically to make testing easier!

}

// Read a key with a LeaseID
req = logical.TestRequest(t, logical.ReadOperation, "secret/foo")
Copy link
Member

Choose a reason for hiding this comment

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

Same with this test as well.

},

Callbacks: map[logical.Operation]framework.OperationFunc{
logical.UpdateOperation: b.handleLease,
Copy link
Member

Choose a reason for hiding this comment

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

Should this be Read operation?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Since lease_id is sensitive, we determined it made sense to pass lease_id as a parameter with an update operation. This is currently the preferred method to use in /sys/renew and /sys/revoke.

},

&framework.Path{
Pattern: "lease" + framework.OptionalParamRegex("prefix"),
Copy link
Member

Choose a reason for hiding this comment

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

Instead of making this optional what if the two framework.Paths were merged with both a read op and a list op?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

My only concern here was some arbitrary path would not be an "unsupported operation" on update operations because of the optional prefix.


func (b *SystemBackend) handleLeaseList(
req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
prefix := data.Get("prefix").(string)
Copy link
Member

Choose a reason for hiding this comment

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

Can you add a check to make sure prefix is not ""

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Empty prefix is valid and will list the top level keys.

@@ -63,6 +63,7 @@ func NewSystemBackend(core *Core, config *logical.BackendConfig) (logical.Backen
"replication/reindex",
"rotate",
"config/auditing/*",
"lease*",
Copy link
Member

Choose a reason for hiding this comment

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

Also you'll need to update the root paths test after changing this list

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks for the head up. I wasn't aware of that test.

@@ -347,7 +371,7 @@ func NewSystemBackend(core *Core, config *logical.BackendConfig) (logical.Backen
},

&framework.Path{
Pattern: "revoke-force/(?P<prefix>.+)",
Pattern: "(lease/)?revoke-force" + framework.OptionalParamRegex("prefix"),
Copy link
Member

Choose a reason for hiding this comment

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

Before this change, I think the prefix parameter was mandatory and would always be set. If it was not supplied, parsing the path pattern would error out. Now that it is an optional param, missing prefix I think will not error out. We might want to check explicitly that the required parameters (but optionally set in the URL) are in fact set. This comment applies to all the similar changes below.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You are right. I got a little ambitious in my refactoring. I'll revert out these changes.

"audit",
"audit/*",
"raw/*",
"replication/primary/secondary-token",
"replication/reindex",
"rotate",
"config/auditing/*",
"lease/lookup*",
"lease/revoke-prefix/*",
Copy link
Member

Choose a reason for hiding this comment

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

Can we also add revoke-force/* and lease/revoke-force/* to this list?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It is probably the right thing to do but this would be a change in behavior. I just want to make sure that what we want to do. I will note it in the changelog if we decide to go that route.

Copy link
Member

Choose a reason for hiding this comment

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

If revoke-prefix is a privileged operation, revoke-force must be I suppose. If we decide to do this, we should document it in CL as you said, in the docs and as a breaking behavior in the upgrade notes.

Copy link
Member

Choose a reason for hiding this comment

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

Generally we reserve root paths for functions that can be super destructive or super sensitive from a security standpoint. revoke-force does fit that bill and probably should be privileged, but I wonder if we should reserve that change for 0.8; people will be more likely to expect breaking changes for that release and I don't think this is super high priority.

@@ -1274,6 +1300,56 @@ func (b *SystemBackend) handleTuneWriteCommon(
return nil, nil
}

// handleLeasse is use to view the metadata for a given LeaseID
Copy link
Member

Choose a reason for hiding this comment

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

s/handleLeasse/handleLeaseLookup

"renewable": leaseTimes.renewable(),
},
}
if !leaseTimes.LastRenewalTime.IsZero() {
Copy link
Member

Choose a reason for hiding this comment

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

Sorry for circling around on this. On a second thought, the idea of having fields with default values (like you were doing earlier) does seem like a sensible thing to do too. What might be of concern is that this API will not have deterministic number of response fields. That is okay I guess but I am not sure if that is a problem. Bringing this up so we can all agree on this and move forward.

@@ -2429,4 +2505,19 @@ This path responds to the following HTTP methods.
"Lists the headers configured to be audited.",
`Returns a list of headers that have been configured to be audited.`,
},

"lease": {
``,
Copy link
Member

Choose a reason for hiding this comment

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

Reminder to fill these out.

"audit",
"audit/*",
"raw/*",
"replication/primary/secondary-token",
"replication/reindex",
"rotate",
"config/auditing/*",
"lease/lookup*",
Copy link
Member

Choose a reason for hiding this comment

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

Can this be leases not lease? It's a collection so plural makes sense. (As opposed to a singular system like replication, e.g. if it was lease-management, but that's too wordy.)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I debated this but wanted to match the other paths that had similar functions, like token and policy. I am not strongly either way and was just trying for consistency.

Copy link
Member

Choose a reason for hiding this comment

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

The difference with policy is that it's supposed to be you acting on a single one, e.g. PUT (write) a policy, GET a policy, and so on. If there were a collection of policy-related APIs under a prefix, having that prefix be policies instead of policy would be the right move IMHO.

Copy link
Member

Choose a reason for hiding this comment

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

Also, lease lookup should definitely not be a root path.

return false
}

func (le *leaseEntry) renewableErr() error {
Copy link
Member

Choose a reason for hiding this comment

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

What is the point of doing this?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I thought this was a bit confusing and was just consolidating the logic for dealing with the error, which the expiration manager uses, and returning the value of renewable.

Copy link
Member

Choose a reason for hiding this comment

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

I'm concerned about people defaulting to use this function when in fact the exact error returned can be useful, e.g. in a debugging/logs context.

resp := &logical.Response{
Data: map[string]interface{}{
"id": leaseID,
"creation_time": leaseTimes.IssueTime,
Copy link
Member

Choose a reason for hiding this comment

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

This has a different type than creation_time in the token store; there, you set this to issue_time.

Copy link
Member

Choose a reason for hiding this comment

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

There's a type discrepancy with last_renewal_time too.

@chrishoffman
Copy link
Contributor Author

This should be ready for review again.

Notable changes since last full review:

  • Added documentation
  • Nested "lease" related paths under /sys/leases. Backwards compatibility is maintained.
  • Token lookup and lease lookup fields merged

I ended up moving a lot of documentation around. Should we provide landing pages for the old docs locations that point to the new locations?

@jefferai jefferai modified the milestones: 0.7.1, 0.7.2 May 2, 2017
Copy link
Member

@jefferai jefferai left a comment

Choose a reason for hiding this comment

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

Looks good! Please just make sys/leases/revoke-force a sudo operation (and document that) and it's good to go IMHO.

@@ -325,7 +363,7 @@ func NewSystemBackend(core *Core, config *logical.BackendConfig) (logical.Backen
},

&framework.Path{
Pattern: "revoke" + framework.OptionalParamRegex("url_lease_id"),
Pattern: "(leases/)?revoke" + framework.OptionalParamRegex("url_lease_id"),
Copy link
Member

Choose a reason for hiding this comment

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

👍

@@ -1274,6 +1314,61 @@ func (b *SystemBackend) handleTuneWriteCommon(
return nil, nil
}

// handleLease is use to view the metadata for a given LeaseID
Copy link
Member

Choose a reason for hiding this comment

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

s/handleLease/handleLeaseLookup

if leaseTimes == nil {
return logical.ErrorResponse("invalid lease"), logical.ErrInvalidRequest
}

Copy link
Member

Choose a reason for hiding this comment

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

Can we add a comment here explaining why we are setting values to nil beforehand. This will avoid people removing it in future.

},

&framework.Path{
Pattern: "leases/lookup",
Copy link
Member

Choose a reason for hiding this comment

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

Do we want this to be a root protected path?

Copy link
Member

Choose a reason for hiding this comment

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

@briankassouf Since lease/lookup doesn't return sensitive information (e.g. the contents of internal data) and you have to know the lease ID ahead of time I don't think it needs to be root. In fact, I am actually thinking we may want to add it to the default policy. Listing on the other hand does divulge lease IDs which can be used to e.g. renew them, so that should be root protected.

Copy link
Member

@briankassouf briankassouf left a comment

Choose a reason for hiding this comment

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

Just that one comment about leases/lookup being a root path but other that that LGTM!!

Copy link
Member

@vishalnayak vishalnayak left a comment

Choose a reason for hiding this comment

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

LGTM! You might want to update the upgrade notes with the revoke-force change. I think its okay to have a breaking change in 0.7.1 considering that its a very low impact break.

@jefferai
Copy link
Member

jefferai commented May 4, 2017

@vishalnayak it's not a breaking change; sys/revoke-force remains non-root (for now), but sys/leases/revoke-force is root. Since that's what documentation will focus on we'll get people used to using that, then eventually can change over the older path.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants