Skip to content
This repository has been archived by the owner on Dec 7, 2023. It is now read-only.

Commit

Permalink
Implement insecure registry behavior for containerd client WRT HTTP a…
Browse files Browse the repository at this point in the history
…nd TLS-ignore-verify /w tests :)
  • Loading branch information
stealthybox committed May 24, 2021
1 parent 12e0919 commit 8db753a
Show file tree
Hide file tree
Showing 6 changed files with 222 additions and 134 deletions.
28 changes: 23 additions & 5 deletions docs/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,14 +64,17 @@ Now the `weaveworks/ignite-ubuntu` image is imported and ready for VM use.

### Configuring image registries

Ignite's runtime configuration for image registry uses the docker client
configuration. To add a new registry to docker client configuration, run
Ignite's runtime configuration for image registry uses the docker registry
configuration. To add a new registry to docker registry configuration, run
`docker login <registry-address>`. This will create `$HOME/.docker/config.json`
in the user's home directory. When ignite runs, it'll check the user's home
directory for docker client configuration file, load the registry configuration
directory for docker registry configuration file, load the registry configuration
if found and use it.

An example of a docker client configuration file:
!!! Note
On many systems, running `sudo ignite` will set the `$HOME` directory to `/root`.

An example of a docker registry configuration file:

```json

Expand All @@ -80,6 +83,9 @@ An example of a docker client configuration file:
"https://index.docker.io/v1/": {
"auth": "<token>"
},
"http://localhost:5000": {
"auth": "<token>"
},
"gcr.io": {
"auth": "<token>"
}
Expand All @@ -92,12 +98,24 @@ the token is a base64 encoded value of `<username>:<auth-token>`. For `gcr.io`,
it's a [json key][json-key] file. Using docker
[credential helpers][credential-helpers] also works but please ensure that the
required credential helper program is installed to handle the credentials. If
the docker client configuration contains `"credHelpers"` block, but the
the docker registry configuration contains `"credHelpers"` block, but the
associated helper program isn't installed or not configured properly, ignite
image pull will fail with errors related to the specific credential helper. In
presence of both auth tokens and credential helpers in a configuration file,
credential helper takes precedence.

The `--registry-config-dir` flag can be used to override the default directory(`$HOME/.docker/config.json`).
This can also be done from the ignite [Configuration](./ignite-configuration).

When using the `containerd` runtime to pull images, TLS verification can be disabled,
and `http://` protocols can be specified by using the client-side `IGNITE_CONTAINERD_INSECURE_REGISTRIES`
environment variable as a comma separate list.
In this list, the protocol is completely ignored, because it's specified by the registry-configuration:

```shell
IGNITE_CONTAINERD_INSECURE_REGISTRIES="localhost:5000,localhost:5001,example.com,http://example.com"
```

[json-key]: https://cloud.google.com/container-registry/docs/advanced-authentication#json-key
[credential-helpers]: https://docs.docker.com/engine/reference/commandline/login/#credential-helpers

Expand Down
155 changes: 83 additions & 72 deletions e2e/registry_auth_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,10 @@ import (
)

const (
testOSImage = "localhost:5000/weaveworks/ignite-ubuntu:test"
testKernelImage = "localhost:5000/weaveworks/ignite-kernel:test"
httpTestOSImage = "127.5.0.1:5080/weaveworks/ignite-ubuntu:test"
httpTestKernelImage = "127.5.0.1:5080/weaveworks/ignite-kernel:test"
httpsTestOSImage = "127.5.0.1:5443/weaveworks/ignite-ubuntu:test"
httpsTestKernelImage = "127.5.0.1:5443/weaveworks/ignite-kernel:test"
)

// client config with auth info for the registry setup in
Expand All @@ -24,22 +26,40 @@ const (
// is updated.
const clientConfigContent = `
{
"auths": {
"localhost:5000": {
"auth": "dGVzdHVzZXI6dGVzdHBhc3N3b3Jk"
}
}
"auths": {
"http://127.5.0.1:5080": {
"auth": "aHR0cF90ZXN0dXNlcjpodHRwX3Rlc3RwYXNzd29yZA=="
},
"https://127.5.0.1:5443": {
"auth": "aHR0cHNfdGVzdHVzZXI6aHR0cHNfdGVzdHBhc3N3b3Jk"
}
}
}
`

func TestPullFromAuthRegistry(t *testing.T) {
assert.Assert(t, e2eHome != "", "IGNITE_E2E_HOME should be set")

os.Setenv("IGNITE_CONTAINERD_INSECURE_REGISTRIES", "http://127.5.0.1:5080,https://127.5.0.1:5443")
defer os.Unsetenv("IGNITE_CONTAINERD_INSECURE_REGISTRIES")

// Create a client config directory to use in test.
emptyDir, err := ioutil.TempDir("", "ignite-test")
assert.NilError(t, err)
defer os.RemoveAll(emptyDir)

// Create a client config directory to use in test.
ccDir, err := ioutil.TempDir("", "ignite-test")
assert.NilError(t, err)
defer os.RemoveAll(ccDir)

// Ensure the directory exists and create a config file in the
// directory.
assert.NilError(t, os.MkdirAll(ccDir, 0755))
configPath := filepath.Join(ccDir, "config.json")
assert.NilError(t, os.WriteFile(configPath, []byte(clientConfigContent), 0600))
defer os.Remove(configPath)

templateConfig := `---
apiVersion: ignite.weave.works/v1alpha4
kind: Configuration
Expand All @@ -50,14 +70,14 @@ spec:
`
igniteConfigContent := fmt.Sprintf(templateConfig, ccDir)

cases := []struct {
name string
runtime runtime.Name
configWithAuthPath string
clientConfigFlag string
igniteConfig string
wantErr bool
}{
type testCase struct {
name string
runtime runtime.Name
clientConfigFlag string
igniteConfig string
wantErr bool
}
cases := []testCase{
{
name: "no auth info - containerd",
runtime: runtime.RuntimeContainerd,
Expand All @@ -69,90 +89,71 @@ spec:
wantErr: true,
},
{
name: "client config flag - containerd",
runtime: runtime.RuntimeContainerd,
configWithAuthPath: ccDir,
clientConfigFlag: ccDir,
name: "client config flag - containerd",
runtime: runtime.RuntimeContainerd,
clientConfigFlag: ccDir,
},
{
name: "client config flag - docker",
runtime: runtime.RuntimeDocker,
configWithAuthPath: ccDir,
clientConfigFlag: ccDir,
name: "client config flag - docker",
runtime: runtime.RuntimeDocker,
clientConfigFlag: ccDir,
},
{
name: "client config in ignite config - containerd",
runtime: runtime.RuntimeContainerd,
configWithAuthPath: ccDir,
igniteConfig: igniteConfigContent,
name: "client config in ignite config - containerd",
runtime: runtime.RuntimeContainerd,
igniteConfig: igniteConfigContent,
},
{
name: "client config in ignite config - docker",
runtime: runtime.RuntimeDocker,
configWithAuthPath: ccDir,
igniteConfig: igniteConfigContent,
name: "client config in ignite config - docker",
runtime: runtime.RuntimeDocker,
igniteConfig: igniteConfigContent,
},
// Following sets the client config dir to a location without a valid
// client config file, although the client config dir in the ignite
// config is correct, the import fails due to bad configuration by the
// flag override.
{
name: "flag override client config - containerd",
runtime: runtime.RuntimeContainerd,
configWithAuthPath: ccDir,
clientConfigFlag: "/tmp",
igniteConfig: igniteConfigContent,
wantErr: true,
name: "flag override client config - containerd",
runtime: runtime.RuntimeContainerd,
clientConfigFlag: emptyDir,
igniteConfig: igniteConfigContent,
wantErr: true,
},
{
name: "flag override client config - docker",
runtime: runtime.RuntimeDocker,
configWithAuthPath: ccDir,
clientConfigFlag: "/tmp",
igniteConfig: igniteConfigContent,
wantErr: true,
name: "flag override client config - docker",
runtime: runtime.RuntimeDocker,
clientConfigFlag: emptyDir,
igniteConfig: igniteConfigContent,
wantErr: true,
},
// Following set the client config dir via flag without any actual
// Following sets the client config dir via flag without any actual
// client config. Import fails due to missing auth info in the given
// client config dir.
{
name: "invalid client config - containerd",
runtime: runtime.RuntimeContainerd,
configWithAuthPath: "",
clientConfigFlag: ccDir,
wantErr: true,
name: "invalid client config - containerd",
runtime: runtime.RuntimeContainerd,
clientConfigFlag: emptyDir,
wantErr: true,
},
{
name: "invalid client config - docker",
runtime: runtime.RuntimeDocker,
configWithAuthPath: "",
clientConfigFlag: ccDir,
wantErr: true,
name: "invalid client config - docker",
runtime: runtime.RuntimeDocker,
clientConfigFlag: emptyDir,
wantErr: true,
},
}

for _, rt := range cases {
rt := rt
t.Run(rt.name, func(t *testing.T) {
testFunc := func(rt testCase, osImage, kernelImage string) func(t *testing.T) {
return func(t *testing.T) {
igniteCmd := util.NewCommand(t, igniteBin)

// Remove images from ignite store and runtime store. Remove
// individually because an error in deleting one image cancels the
// whole command.
// TODO: Improve image rm to not fail completely when there are
// multiple images and some are not found.
util.RmiCompletely(testOSImage, igniteCmd, rt.runtime)
util.RmiCompletely(testKernelImage, igniteCmd, rt.runtime)

// Write client config if given.
if len(rt.configWithAuthPath) > 0 {
// Ensure the directory exists and create a config file in the
// directory.
assert.NilError(t, os.MkdirAll(rt.configWithAuthPath, 0755))
configPath := filepath.Join(rt.configWithAuthPath, "config.json")
assert.NilError(t, os.WriteFile(configPath, []byte(clientConfigContent), 0600))
defer os.Remove(configPath)
}
util.RmiCompletely(osImage, igniteCmd, rt.runtime)
util.RmiCompletely(kernelImage, igniteCmd, rt.runtime)

// Write ignite config if provided.
var igniteConfigPath string
Expand Down Expand Up @@ -181,7 +182,7 @@ spec:

// Run image import.
_, importErr := igniteCmd.New().
With("image", "import", testOSImage).
With("image", "import", osImage).
With(imageImportCmdArgs...).
Cmd.CombinedOutput()
if (importErr != nil) != rt.wantErr {
Expand All @@ -190,12 +191,22 @@ spec:

// Run kernel import.
_, importErr = igniteCmd.New().
With("image", "import", testKernelImage).
With("image", "import", kernelImage).
With(imageImportCmdArgs...).
Cmd.CombinedOutput()
if (importErr != nil) != rt.wantErr {
t.Error("expected kernel image import to fail")
}
})
}
}

for _, rt := range cases {
rt := rt
t.Run("http_"+rt.name, testFunc(rt, httpTestOSImage, httpTestKernelImage))
}

for _, rt := range cases {
rt := rt
t.Run("https_"+rt.name, testFunc(rt, httpsTestOSImage, httpsTestKernelImage))
}
}
Loading

0 comments on commit 8db753a

Please sign in to comment.