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

agent/workloadattestor: Add docker plugin #687

Merged
merged 5 commits into from
Jan 24, 2019

Conversation

APTy
Copy link
Contributor

@APTy APTy commented Jan 23, 2019

Pull Request check list

  • Commit conforms to CONTRIBUTING.md?
  • Proper tests/regressions included?
  • Documentation updated?

Affected functionality
Adds a docker-based workload attestor that enables selectors based on docker labels and the container's image id.

TODO

  • add docs for image id selector
  • add any other selectors wanted, e.g. image sha

@APTy APTy force-pushed the tj/docker-attestor branch 2 times, most recently from 3c9bc8f to ea48a32 Compare January 23, 2019 00:18
Copy link
Member

@evan2645 evan2645 left a comment

Choose a reason for hiding this comment

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

This is awesome!! Thank you greatly for the contrib @APTy!

Left a few small comments and nits. Curious to see if @azdagron has anything else to say... looks good to me otherwise!

parts := strings.Split(cgroup.GroupPath, "/")

if len(parts) <= p.cgroupContainerIndex+1 {
log.Printf("docker entry found, but without container id: %v", cgroup.GroupPath)
Copy link
Member

Choose a reason for hiding this comment

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

nit: Log messages should be capitalized. Also, might be a bit clearer s/without container id/is missing the container id/

}

if !hasDockerEntries {
return nil, fmt.Errorf("workloadattestor/docker: no cgroup entries found with prefix %q", p.cgroupPrefix)
Copy link
Member

Choose a reason for hiding this comment

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

We used to have logging like this in the k8s attestor, but ended up removing it because it would cause the agent to log errors when trying to attest non-k8s workloads. Generally speaking, I think we should support the mix-and-match case well and expect that maybe some dockerized workloads will call us, and some others will call us too... in that vein, perhaps we should not log or return an error here? Curious to hear thoughts on this point

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah okay, I noticed that but wasn't sure of the reason. Agree with this reasoning and updated to reflect

return &spi.ConfigureResponse{ErrorList: []string{err.Error()}}, err
}
p.cgroupPrefix = config.CgroupPrefix
p.cgroupContainerIndex = config.CgroupContainerIndex
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 to set the defaults on these here?

| docker_socket_path | The location of the docker daemon socket (default: "unix:///var/run/docker.sock" on unix). |
| docker_version | The API version of the docker daemon (default: "1.25").
| cgroup_prefix | The cgroup prefix to look for in the cgroup entries (e.g. "/docker"). |
| cgroup_container_index | The index within the cgroup path where the container ID should be found (default: 0). |
Copy link
Member

Choose a reason for hiding this comment

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

IMO it is desirable to limit the number of knobs available to users wherever possible... otherwise, they tend to tweak things inappropriately and end up with either misconfigured or broken software, sometimes without even knowing. In that vein, I'd vote to leave cgroup_prefix and cgroup_container_index undocumented... I don't think they are very likely to change... If someone comes to us with a broken attestor we can ask them to tweak these undocumented values, or if an operator finds them in the code, then the assumption is that they know what they are doing

If we remove these configurables from the documentation, then we can also remove the Cgroup entry section below

| cgroup_container_index | The index within the cgroup path where the container ID should be found (default: 0). |

Since selectors are created dynamically based on the container's docker labels, there isn't a list of known selectors.
Instead, all of the container's labels are used in creating the list of selectors.
Copy link
Member

Choose a reason for hiding this comment

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

I don't think this applies any longer?

| Selector | Value |
| -------- | ----- |
| docker:label:abc | The value of the docker label `abc` |
| docker:label:xyz | The value of the docker label `xyz` |
Copy link
Member

Choose a reason for hiding this comment

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

nit: I know that this is taken from the k8s workload attestor docs, but I think separating by selector and value is confusing to users. Even though the difference between type and value is distinguished internally, we don't really surface that very strongly in the CLI tools. I'd suggest a table like this one instead: https://github.com/spiffe/spire/blob/master/doc/plugin_server_nodeattestor_k8s_sat.md

If a workload container is started with `docker run --label com.example.name=foo [...]`, then its workload selector will be:
```
type: "docker:label"
value: "com.example.name=foo"
Copy link
Member

Choose a reason for hiding this comment

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

= or :?

Similar nit here on selector type/value

plugin_data {
cgroup_container_index = 3
cgroup_prefix = ""
labels = [""]
Copy link
Member

Choose a reason for hiding this comment

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

I think we can remove labels and possibly the other two as well if we decide to leave them undocumented

Copy link
Member

@azdagron azdagron left a comment

Choose a reason for hiding this comment

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

Looking great :) Just a couple of small nits...

)

const (
_selectorType = "docker"
Copy link
Member

Choose a reason for hiding this comment

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

nit: package consts should be camelCase w/o the leading underscore.


// Parse HCL config payload into config struct
config := &dockerPluginConfig{}
hclTree, err := hcl.Parse(req.Configuration)
Copy link
Member

Choose a reason for hiding this comment

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

nit: hcl can be parsed and decoded in a single step via hcl.Decode(config, req.Configuration)

config := &dockerPluginConfig{}
hclTree, err := hcl.Parse(req.Configuration)
if err != nil {
return &spi.ConfigureResponse{ErrorList: []string{err.Error()}}, err
Copy link
Member

Choose a reason for hiding this comment

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

i know this was likely a pattern followed from other plugins, but we should just return nil, err here. The ErrorList stuff isn't used and gRPC ignores the returned response anyway if an error is returned. I've been meaning to clean up the plugins.... :)

Signed-off-by: R. Tyler Julian <tjulian@uber.com>
@APTy APTy force-pushed the tj/docker-attestor branch from ea48a32 to 3fca261 Compare January 23, 2019 15:39
@APTy
Copy link
Contributor Author

APTy commented Jan 23, 2019

Okay I've made the recommended changes. Sorry I had to force push to rebase onto master and signoff correctly, so some of the history is lost. Anyway, one more thing I wanted to discuss:

Regarding the docker dependency, the library has a weird versioning/support story. I tried including the latest version, v17.05.0-ce, which results in:

go test -race -timeout 8m github.com/spiffe/spire/...
go: finding github.com/docker/docker v17.05.0-ce
go: finding github.com/docker/libtrust latest
go: finding github.com/docker/distribution/reference latest
go: finding github.com/docker/go-connections/nat latest
go: finding github.com/docker/go-connections/tlsconfig latest
go: finding github.com/docker/go-connections/sockets latest
go: finding github.com/docker/distribution v2.7.1+incompatible
go: downloading github.com/docker/distribution v2.7.1+incompatible
go: github.com/Sirupsen/logrus@v1.3.0: parsing go.mod: unexpected module path "github.com/sirupsen/logrus"
go: error loading module requirements
make: *** [test] Error 1

I've run into logrus import errors too many times to care to debug this lol. The current state of this PR pins to master, which is less than ideal IMO. My preference here is to just write our own docker client, but we could also vendor the library. Thoughts?

Signed-off-by: R. Tyler Julian <tjulian@uber.com>
@azdagron
Copy link
Member

Hmm. I pulled down your branch and ran go mod tidy and it complained about the checksum in go.sum for docker (travis had the same problem). I removed the docker entries from go.sum and ran go mod tidy and then was able to build and run tests....

@APTy
Copy link
Contributor Author

APTy commented Jan 23, 2019

Ah okay yeah I'm new to go mod stuff, should be able to fix that. But I think we should first answer the question of "do we (1) pin to master, (2) vendor master, (3) pin to the last stable release 1.5 years ago and try to debug the logrus nightmare, or (4) build our own docker client with limited functionality"

@APTy
Copy link
Contributor Author

APTy commented Jan 23, 2019

Here's an example of what (4) would look like: APTy/spire@tj/docker-attestor...APTy:tj/docker-attestor-2. It's probably missing some things like client version negotiation, but it's not too complex to create the core functionality ourselves.

Signed-off-by: R. Tyler Julian <tjulian@uber.com>
@azdagron
Copy link
Member

My opinion is that since github.com/docker/docker/client is from the official docker repo, we should probably just pin to latest master (assuming that it works). I think enough has changed in 1.5 years that using the old release will be problematic from a dependency standpoint (as you have discovered). I'd be totally for vendoring or writing our own if this was some problematic one-off library, but since it is the official repo, I'm pretty comfortable just using it. Dunno if @evan2645 has other thoughts...

Signed-off-by: R. Tyler Julian <tjulian@uber.com>
Copy link
Member

@azdagron azdagron left a comment

Choose a reason for hiding this comment

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

Awesome. Thanks @APTy!

@azdagron azdagron merged commit fcce613 into spiffe:master Jan 24, 2019
@evan2645 evan2645 mentioned this pull request Feb 4, 2019
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.

3 participants