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 support for GMSA CredentialSpecs from Swarmkit configs #38632

Merged
merged 3 commits into from
Feb 21, 2019

Conversation

dperny
Copy link
Contributor

@dperny dperny commented Jan 24, 2019

- What I did

Adds the ability to specify a swarmkit Config as a CredentialSpec, as an alternative to the existing registry and file options. Adds a new CredentialSpec URI specifier: config://. This is used to specify the ID of a swarmkit config to be used as the CredentialSpec.

- How I did it

Altered swarmkit to support distributing a config that is not mounted into a container directly. Then, altered the oci_windows.go code to get that config from the dependency store, and use the data as the GMSA config.

- How to verify it

This will need to be manually tested, as I do not have access to a Windows environment on which to test.

- Description for the changelog

Add support for using swarmkit Configs as GMSA CredentialSpecs.

@GordonTheTurtle GordonTheTurtle added the dco/no Automatically set by a bot when one of the commits lacks proper signature label Jan 24, 2019
@GordonTheTurtle
Copy link

Please sign your commits following these rules:
https://github.com/moby/moby/blob/master/CONTRIBUTING.md#sign-your-work
The easiest way to do this is to amend the last commit:

$ git clone -b "gmsa-support" git@github.com:dperny/docker.git somewhere
$ cd somewhere
$ git rebase -i HEAD~842357812584
editor opens
change each 'pick' to 'edit'
save the file and quit
$ git commit --amend -s --no-edit
$ git rebase --continue # and repeat the amend for each commit
$ git push -f

Amending updates the existing PR. You DO NOT need to open a new one.

@GordonTheTurtle GordonTheTurtle removed the dco/no Automatically set by a bot when one of the commits lacks proper signature label Jan 24, 2019
@dperny
Copy link
Contributor Author

dperny commented Jan 24, 2019

Interestingly, this requires no CLI or API changes, because CredentialSpecs are specified using that weird URI format.

@cpuguy83
Copy link
Member

I assume this has that change for async delete in it? We need to resolve that before we can re-vendor swarmkit.

@dperny
Copy link
Contributor Author

dperny commented Jan 24, 2019

@cpuguy83 yes, sorry, I should have mentioned that. This is blocked on the async delete changes.

@codecov
Copy link

codecov bot commented Jan 31, 2019

Codecov Report

❗ No coverage uploaded for pull request base (master@7403497). Click here to learn what that means.
The diff coverage is 89.77%.

@@            Coverage Diff            @@
##             master   #38632   +/-   ##
=========================================
  Coverage          ?   36.41%           
=========================================
  Files             ?      613           
  Lines             ?    45837           
  Branches          ?        0           
=========================================
  Hits              ?    16693           
  Misses            ?    26851           
  Partials          ?     2293

@dperny
Copy link
Contributor Author

dperny commented Jan 31, 2019

Ok, the async delete change has been reverted, and I have revendored swarmkit, so this commit no longer includes that change. This is fully ready for review.

@cpuguy83
Copy link
Member

CI is all failing on the same test

14:23:57 --- FAIL: TestServiceUpdateLabel (2.05s)
14:23:57     daemon.go:296: [d5e1505aff338] waiting for daemon to start
14:23:57     daemon.go:328: [d5e1505aff338] daemon started
14:23:57     update_test.go:64: assertion failed: error is not nil: Error response from daemon: rpc error: code = Unknown desc = update out of sequence
14:23:57     daemon.go:283: [d5e1505aff338] exiting daemon

@thaJeztah
Copy link
Member

oh, wha, missed this one; guess this will have to be combined with #38672 ?

@dperny
Copy link
Contributor Author

dperny commented Feb 4, 2019

@thaJeztah yes, affirmative.

dperny and others added 2 commits February 4, 2019 14:52
Signed-off-by: Drew Erny <drew.erny@docker.com>
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
@dperny
Copy link
Contributor Author

dperny commented Feb 5, 2019

This PR is updated

Copy link
Member

@thaJeztah thaJeztah left a comment

Choose a reason for hiding this comment

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

Just some quick comments from my phone 😅

}
csConfig, err := c.DependencyStore.Configs().Get(csValue)
if err != nil {
return fmt.Errorf("error getting value from config store: %v", err)
Copy link
Member

Choose a reason for hiding this comment

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

This should probably use errors.Wrap to preserve the original error; something like;

return errors.Wrap(err, "failed to get value from config store")

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good catch.


// if the container does not have a DependencyStore, then we
// return an error
if c.DependencyStore == nil {
Copy link
Member

Choose a reason for hiding this comment

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

Wondering (reading from my phone so have limited sight in the whole code path) if we can catch this situation during create of the container instead of here (as this is (I think) only ran at the last point, at container start)

eg somewhere in this code path (would have to check if that's the right place though)

func (daemon *Daemon) CreateManagedContainer(params types.ContainerCreateConfig) (containertypes.ContainerCreateCreatedBody, error) {

/cc @cpuguy83

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'm not sure that a nil dependency store is even a possibility here, but golang has no way to express that a value cannot be nil, so you end up having to check it everywhere to be defensive.

@dperny
Copy link
Contributor Author

dperny commented Feb 5, 2019

@thaJeztah are you going to veto this if I push any code-review-stage changes to a new commit, instead of reordering the two commits i have or altering your commit (which is at the head of the branch)?

@cpuguy83
Copy link
Member

cpuguy83 commented Feb 5, 2019

Is it not odd that we are pulling in swarm data at the oci config level?

@dperny
Copy link
Contributor Author

dperny commented Feb 5, 2019

It is a bit odd, but where else would we do it? This is the part of the code where we get the actual CredentialSpec data. Not a rhetorical question, by the way. Legitimately unsure where else to put it.

@cpuguy83
Copy link
Member

cpuguy83 commented Feb 5, 2019

Can we create a credential file from the config and use that in the host config?
Potentially could be done after create if we allow the swarm agent to update the container config after create.

@dperny
Copy link
Contributor Author

dperny commented Feb 5, 2019

Seems like an awful lot of runaround, especially if it involves a temporary file. The DependencyStore is part of the container.Container type, so why not use it? Plus, it's indirected through an interface, so it's plenty decoupled from swarm.

@thaJeztah
Copy link
Member

thaJeztah commented Feb 5, 2019 via email

@dperny
Copy link
Contributor Author

dperny commented Feb 6, 2019

Ugh I realize I've bungled some things up.

In Swarmkit, the Config referenced in a CredentialSpec needs to be included in the ContainerSpec's ConfigReferences, with a RuntimeTarget instead of a FileTarget. This tells Swarmkit, "This config is going to be used somewhere, but not as a file in the container"

Then, the CredentialSpec is set to use a Config and references the ConfigID. This tells the daemon, "when you want a CredentialSpec, use this config".

The reason for this being in two places is that the ConfigReferences tell you what configs you're using, and the CredentialSpec tells you what CredentialSpec you're using. This means any existing logic for Configs is contained totally to checking ConfigReferences, and does not need to shop around for the other places Configs could be hiding. The biggest benefit for this is in the validation logic, and in the dispatcher logic.

My biggest mistake so far is that I forgot to write a validation check that ensures the config referenced as a CredentialSpec is actually included in the ConfigReferences, so nothing stops you from putting a Config in the CredentialSpec that can't be used.

I've been assuming we'll take the same pattern with the Docker API, but I neglected to take into account the fact that the RuntimeTarget object is empty -- it doesn't need any data (yet). I'm unsure if JSON would allow us to specify an empty object that is semantically different from a nil object...

The ultimate intention at the CLI level was for the user to simply pass --credential-spec="configid", and then have the CLI go and get the Config and add it to both the ConfigReferences and the CredentialSpec field.

@dperny
Copy link
Contributor Author

dperny commented Feb 7, 2019

Updated the API to reflect the Runtime target.

// ConfigReference is a reference to a config in swarm
type ConfigReference struct {
File *ConfigReferenceFileTarget
File *ConfigReferenceFileTarget `json:",omitempty"`
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Does adding json:",omitempty" alter the API in an incompatible way?

Copy link
Member

Choose a reason for hiding this comment

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

Hm, don't think it's strictly incompatible (although the swagger does not include x-nullable: true). Thinking of situations where someone would expect the File field to always be there, but those situations would likely already break, because in the new situation, File would be nil, instead of {"Name": ...., .....}, so I think changing this is OK

We should update the swagger though to indicate the fields can be empty (adding that to my other comment/proposed change)

I guess strictly they're different types, but (see my other comment) that may become complicated

wk8 added a commit to wk8/moby that referenced this pull request Mar 6, 2019
…ngine

Instead of having to go through files or registry values as is currently the
case.

While adding GMSA support to Kubernetes (kubernetes/kubernetes#73726)
I stumbled upon the fact that Docker currently only allows passing Windows
credential specs through files or registry values, forcing the Kubelet
to perform a rather awkward dance of writing-then-deleting to either the
disk or the registry to be able to create a Windows container with cred
specs.

This patch solves this problem by making it possible to directly pass
whole base64-encoded cred specs to the engine's API. I took the opportunity
to slightly refactor the method responsible for Windows cred spec as it
seemed hard to read to me.

Added some unit tests on Windows credential specs handling, as there were
previously none.

I have also tested it manually: given a Windows container using a cred spec
that you would normally start with e.g.
```powershell
docker run --rm --security-opt "credentialspec=file://win.json" mcr.microsoft.com/windows/servercore:ltsc2019 nltest /parentdomain
# output:
# my.ad.domain.com. (1)
# The command completed successfully
```
can now equivalently be started with
```powershell
$b64CredSpec = [System.Convert]::ToBase64String([System.IO.File]::ReadAllBytes('C:\ProgramData\docker\credentialspecs\win.json'))
docker run --rm --security-opt "credentialspec=base64://$b64CredSpec" mcr.microsoft.com/windows/servercore:ltsc2019 nltest /parentdomain
# same output!
```

I'll do another PR on Swarmkit after this is merged to allow services to use
the same option.

(It's worth noting that @dperny faced the same problem adding GMSA support
to Swarmkit, to which he came up with an interesting solution - see
moby#38632 - but alas these tricks are not
available to the Kubelet.)

Signed-off-by: Jean Rouge <rougej+github@gmail.com>
wk8 added a commit to wk8/moby that referenced this pull request Mar 16, 2019
…ngine

Instead of having to go through files or registry values as is currently the
case.

While adding GMSA support to Kubernetes (kubernetes/kubernetes#73726)
I stumbled upon the fact that Docker currently only allows passing Windows
credential specs through files or registry values, forcing the Kubelet
to perform a rather awkward dance of writing-then-deleting to either the
disk or the registry to be able to create a Windows container with cred
specs.

This patch solves this problem by making it possible to directly pass
whole base64-encoded cred specs to the engine's API. I took the opportunity
to slightly refactor the method responsible for Windows cred spec as it
seemed hard to read to me.

Added some unit tests on Windows credential specs handling, as there were
previously none.

Added/amended the relevant integration tests.

I have also tested it manually: given a Windows container using a cred spec
that you would normally start with e.g.
```powershell
docker run --rm --security-opt "credentialspec=file://win.json" mcr.microsoft.com/windows/servercore:ltsc2019 nltest /parentdomain
# output:
# my.ad.domain.com. (1)
# The command completed successfully
```
can now equivalently be started with
```powershell
$rawCredSpec = & cat 'C:\ProgramData\docker\credentialspecs\win.json'
$escaped = $rawCredSpec.Replace('"', '\"')
docker run --rm --security-opt "credentialspec=raw://$escaped" mcr.microsoft.com/windows/servercore:ltsc2019 nltest /parentdomain
# same output!
```

I'll do another PR on Swarmkit after this is merged to allow services to use
the same option.

(It's worth noting that @dperny faced the same problem adding GMSA support
to Swarmkit, to which he came up with an interesting solution - see
moby#38632 - but alas these tricks are not
available to the Kubelet.)

Signed-off-by: Jean Rouge <rougej+github@gmail.com>
docker-jenkins pushed a commit to docker-archive/docker-ce that referenced this pull request Mar 16, 2019
…ngine

Instead of having to go through files or registry values as is currently the
case.

While adding GMSA support to Kubernetes (kubernetes/kubernetes#73726)
I stumbled upon the fact that Docker currently only allows passing Windows
credential specs through files or registry values, forcing the Kubelet
to perform a rather awkward dance of writing-then-deleting to either the
disk or the registry to be able to create a Windows container with cred
specs.

This patch solves this problem by making it possible to directly pass
whole base64-encoded cred specs to the engine's API. I took the opportunity
to slightly refactor the method responsible for Windows cred spec as it
seemed hard to read to me.

Added some unit tests on Windows credential specs handling, as there were
previously none.

Added/amended the relevant integration tests.

I have also tested it manually: given a Windows container using a cred spec
that you would normally start with e.g.
```powershell
docker run --rm --security-opt "credentialspec=file://win.json" mcr.microsoft.com/windows/servercore:ltsc2019 nltest /parentdomain
# output:
# my.ad.domain.com. (1)
# The command completed successfully
```
can now equivalently be started with
```powershell
$rawCredSpec = & cat 'C:\ProgramData\docker\credentialspecs\win.json'
$escaped = $rawCredSpec.Replace('"', '\"')
docker run --rm --security-opt "credentialspec=raw://$escaped" mcr.microsoft.com/windows/servercore:ltsc2019 nltest /parentdomain
# same output!
```

I'll do another PR on Swarmkit after this is merged to allow services to use
the same option.

(It's worth noting that @dperny faced the same problem adding GMSA support
to Swarmkit, to which he came up with an interesting solution - see
moby/moby#38632 - but alas these tricks are not
available to the Kubelet.)

Signed-off-by: Jean Rouge <rougej+github@gmail.com>
Upstream-commit: 7fdac7eb0ff836633c0a08c430b9472a3bfd3e20
Component: engine
adhulipa pushed a commit to adhulipa/docker that referenced this pull request Apr 11, 2019
…ngine

Instead of having to go through files or registry values as is currently the
case.

While adding GMSA support to Kubernetes (kubernetes/kubernetes#73726)
I stumbled upon the fact that Docker currently only allows passing Windows
credential specs through files or registry values, forcing the Kubelet
to perform a rather awkward dance of writing-then-deleting to either the
disk or the registry to be able to create a Windows container with cred
specs.

This patch solves this problem by making it possible to directly pass
whole base64-encoded cred specs to the engine's API. I took the opportunity
to slightly refactor the method responsible for Windows cred spec as it
seemed hard to read to me.

Added some unit tests on Windows credential specs handling, as there were
previously none.

Added/amended the relevant integration tests.

I have also tested it manually: given a Windows container using a cred spec
that you would normally start with e.g.
```powershell
docker run --rm --security-opt "credentialspec=file://win.json" mcr.microsoft.com/windows/servercore:ltsc2019 nltest /parentdomain
# output:
# my.ad.domain.com. (1)
# The command completed successfully
```
can now equivalently be started with
```powershell
$rawCredSpec = & cat 'C:\ProgramData\docker\credentialspecs\win.json'
$escaped = $rawCredSpec.Replace('"', '\"')
docker run --rm --security-opt "credentialspec=raw://$escaped" mcr.microsoft.com/windows/servercore:ltsc2019 nltest /parentdomain
# same output!
```

I'll do another PR on Swarmkit after this is merged to allow services to use
the same option.

(It's worth noting that @dperny faced the same problem adding GMSA support
to Swarmkit, to which he came up with an interesting solution - see
moby#38632 - but alas these tricks are not
available to the Kubelet.)

Signed-off-by: Jean Rouge <rougej+github@gmail.com>
kiku-jw pushed a commit to kiku-jw/moby that referenced this pull request May 16, 2019
…ngine

Instead of having to go through files or registry values as is currently the
case.

While adding GMSA support to Kubernetes (kubernetes/kubernetes#73726)
I stumbled upon the fact that Docker currently only allows passing Windows
credential specs through files or registry values, forcing the Kubelet
to perform a rather awkward dance of writing-then-deleting to either the
disk or the registry to be able to create a Windows container with cred
specs.

This patch solves this problem by making it possible to directly pass
whole base64-encoded cred specs to the engine's API. I took the opportunity
to slightly refactor the method responsible for Windows cred spec as it
seemed hard to read to me.

Added some unit tests on Windows credential specs handling, as there were
previously none.

Added/amended the relevant integration tests.

I have also tested it manually: given a Windows container using a cred spec
that you would normally start with e.g.
```powershell
docker run --rm --security-opt "credentialspec=file://win.json" mcr.microsoft.com/windows/servercore:ltsc2019 nltest /parentdomain
# output:
# my.ad.domain.com. (1)
# The command completed successfully
```
can now equivalently be started with
```powershell
$rawCredSpec = & cat 'C:\ProgramData\docker\credentialspecs\win.json'
$escaped = $rawCredSpec.Replace('"', '\"')
docker run --rm --security-opt "credentialspec=raw://$escaped" mcr.microsoft.com/windows/servercore:ltsc2019 nltest /parentdomain
# same output!
```

I'll do another PR on Swarmkit after this is merged to allow services to use
the same option.

(It's worth noting that @dperny faced the same problem adding GMSA support
to Swarmkit, to which he came up with an interesting solution - see
moby#38632 - but alas these tricks are not
available to the Kubelet.)

Signed-off-by: Jean Rouge <rougej+github@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

6 participants