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

docs,client: improve documentation on access permissions #100

Merged
merged 3 commits into from
Feb 19, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
27 changes: 20 additions & 7 deletions acl/acl.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,29 @@ import (
type Action string

const (
ActionGet = Action("get")
ActionInfo = Action("info")
ActionPut = Action("put")
// ActionGet ("get" in the API) denotes permission to fetch the contents of a secret.
ActionGet = Action("get")

// ActionInfo ("info" in the API) denotes permission to read the metadata
// for a secret, including available and active version numbers, but not the
// secret values.
ActionInfo = Action("info")

// ActionPut ("put" in the API) denotes permission to put a new value of a
// secret.
ActionPut = Action("put")

// ActionActivate ("activate" in the API) denotes permission to set one one
// of of the available versions of a secret as the active one.
ActionActivate = Action("activate")
ActionDelete = Action("delete")

// ActionDelete ("delete" in the API) denotes permission to delete secret
// versions, either individually or entirely.
ActionDelete = Action("delete")
)

// Secret is a secret name pattern that can optionally contain '*'
// wildcard characters. The wildcard means "zero or more of any
// character here."
// Secret is a secret name pattern that can optionally contain '*' wildcard
// characters. The wildcard means "zero or more of any character here."
type Secret string

// Match reports whether the Secret name pattern matches val.
Expand Down
33 changes: 28 additions & 5 deletions client/setec/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,13 +84,17 @@ func do[RESP, REQ any](ctx context.Context, c Client, path string, req REQ) (RES
return resp, nil
}

// List fetches a list of secret names and associated metadata (but
// not the secret values themselves).
// List fetches a list of secret names and associated metadata for all those
// secrets on which the caller has "info" access. List does not report the
// secret values themselves. If the caller does not have "info" access to any
// secrets, List reports zero values without error.
func (c Client) List(ctx context.Context) ([]*api.SecretInfo, error) {
return do[[]*api.SecretInfo](ctx, c, "/api/list", api.ListRequest{})
}

// Get fetches the current active secret value for name.
//
// Access requirement: "get"
func (c Client) Get(ctx context.Context, name string) (*api.SecretValue, error) {
return do[*api.SecretValue](ctx, c, "/api/get", api.GetRequest{
Name: name,
Expand All @@ -103,6 +107,8 @@ func (c Client) Get(ctx context.Context, name string) (*api.SecretValue, error)
// the same as oldVersion, it reports api.ErrValueNotChanged without returning
// a secret. As a special case, if oldVersion == 0 then GetIfVersion behaves as
// Get and retrieves the current active version.
//
// Access requirement: "get"
func (c Client) GetIfChanged(ctx context.Context, name string, oldVersion api.SecretVersion) (*api.SecretValue, error) {
if oldVersion == api.SecretVersionDefault {
return c.Get(ctx, name)
Expand All @@ -116,6 +122,8 @@ func (c Client) GetIfChanged(ctx context.Context, name string, oldVersion api.Se

// Get fetches a secret value by name and version. If version == 0, GetVersion
// retrieves the current active version.
//
// Access requirement: "get"
func (c Client) GetVersion(ctx context.Context, name string, version api.SecretVersion) (*api.SecretValue, error) {
return do[*api.SecretValue](ctx, c, "/api/get", api.GetRequest{
Name: name,
Expand All @@ -124,15 +132,18 @@ func (c Client) GetVersion(ctx context.Context, name string, version api.SecretV
}

// Info fetches metadata for a given secret name.
//
// Access requirement: "info"
func (c Client) Info(ctx context.Context, name string) (*api.SecretInfo, error) {
return do[*api.SecretInfo](ctx, c, "/api/info", api.InfoRequest{
Name: name,
})
}

// Put creates a secret called name, with the given value. If a secret
// called name already exist, the value is saved as a new inactive
// version.
// Put creates a secret called name, with the given value. If a secret called
// name already exist, the value is saved as a new inactive version.
//
// Access requirement: "put"
func (c Client) Put(ctx context.Context, name string, value []byte) (version api.SecretVersion, err error) {
return do[api.SecretVersion](ctx, c, "/api/put", api.PutRequest{
Name: name,
Expand All @@ -141,6 +152,8 @@ func (c Client) Put(ctx context.Context, name string, value []byte) (version api
}

// Activate changes the active version of the secret called name to version.
//
// Access requirement: "activate"
func (c Client) Activate(ctx context.Context, name string, version api.SecretVersion) error {
_, err := do[struct{}](ctx, c, "/api/activate", api.ActivateRequest{
Name: name,
Expand All @@ -150,6 +163,11 @@ func (c Client) Activate(ctx context.Context, name string, version api.SecretVer
}

// DeleteVersion deletes the specified version of the named secret.
//
// Note: DeleteVersion will report an error if the caller attempts to delete
// the active version, even if they have permission to do so.
//
// Access requirement: "delete"
func (c Client) DeleteVersion(ctx context.Context, name string, version api.SecretVersion) error {
_, err := do[struct{}](ctx, c, "/api/delete-version", api.DeleteVersionRequest{
Name: name,
Expand All @@ -159,6 +177,11 @@ func (c Client) DeleteVersion(ctx context.Context, name string, version api.Secr
}

// Delete deletes all versions of the named secret.
//
// Note: Delete will delete all versions of the secret, including the active
// one, if the caller has permission to do so.
//
// Access requirement: "delete"
func (c Client) Delete(ctx context.Context, name string) error {
_, err := do[struct{}](ctx, c, "/api/delete", api.DeleteRequest{
Name: name,
Expand Down
19 changes: 19 additions & 0 deletions docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,32 @@ The peer capability label is `tailscale.com/cap/secrets`.
Calls to the API must include a header `Sec-X-Tailscale-No-Browsers: setec`.
This prevents browser scripts from initiating calls to the service.


## HTTP Status

- Invalid request parameters report 400 Invalid request.
- Access permission errors report 403 Forbidden.
- Requests for unknown values report 404 Not found.
- All other errors report 500 Internal server error.


## Permissions

The service defines named _actions_ that are subject to access control:

- `get`: Denotes permission to fetch the contents of a secret. |
Copy link
Member

Choose a reason for hiding this comment

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

With only "get", can I get version numbers?

Copy link
Member Author

Choose a reason for hiding this comment

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

You cannot list version numbers, but you can request a specific version.

- `info`: Denotes permission to read the metadata for a secret, including
available and active version numbers, but not the secret values

- `put`: Denotes permission to put a new value of a secret.

- `activate`: Denotes permission to set one one of of the available versions of
a secret as the active one.

- `delete`: Denotes permission to delete secret versions, either individually
or entirely.


## Methods

- `/api/list`: List metadata for all secrets to which the caller has `info`
Expand Down