Compare commits
1 Commits
main
...
fix/symlin
Author | SHA1 | Date | |
---|---|---|---|
|
6f965012fe |
@ -31,6 +31,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- [#535](https://github.com/spegel-org/spegel/pull/535) Fix Docker build casing checks.
|
- [#535](https://github.com/spegel-org/spegel/pull/535) Fix Docker build casing checks.
|
||||||
|
- [#571](https://github.com/spegel-org/spegel/pull/571) Walk config path symlinks when verifying Containerd configuration.
|
||||||
|
|
||||||
### Security
|
### Security
|
||||||
|
|
||||||
|
@ -126,16 +126,41 @@ func verifyStatusResponse(resp *runtimeapi.StatusResponse, configPath string) er
|
|||||||
if cfg.Registry.ConfigPath == "" {
|
if cfg.Registry.ConfigPath == "" {
|
||||||
return errors.New("Containerd registry config path needs to be set for mirror configuration to take effect")
|
return errors.New("Containerd registry config path needs to be set for mirror configuration to take effect")
|
||||||
}
|
}
|
||||||
|
linkPaths, err := walkSymbolicLinks(configPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
paths := filepath.SplitList(cfg.Registry.ConfigPath)
|
paths := filepath.SplitList(cfg.Registry.ConfigPath)
|
||||||
for _, path := range paths {
|
for _, path := range paths {
|
||||||
if path != configPath {
|
for _, linkedPath := range linkPaths {
|
||||||
continue
|
if linkedPath == path {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
return fmt.Errorf("Containerd registry config path is %s but needs to contain path %s for mirror configuration to take effect", cfg.Registry.ConfigPath, configPath)
|
return fmt.Errorf("Containerd registry config path is %s but needs to contain path %s for mirror configuration to take effect", cfg.Registry.ConfigPath, configPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func walkSymbolicLinks(path string) ([]string, error) {
|
||||||
|
fi, err := os.Lstat(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if fi.Mode()&os.ModeSymlink != os.ModeSymlink {
|
||||||
|
return []string{path}, nil
|
||||||
|
}
|
||||||
|
linkPath, err := os.Readlink(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
paths, err := walkSymbolicLinks(linkPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
paths = append(paths, path)
|
||||||
|
return paths, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Containerd) Subscribe(ctx context.Context) (<-chan ImageEvent, <-chan error, error) {
|
func (c *Containerd) Subscribe(ctx context.Context) (<-chan ImageEvent, <-chan error, error) {
|
||||||
imgCh := make(chan ImageEvent)
|
imgCh := make(chan ImageEvent)
|
||||||
errCh := make(chan error)
|
errCh := make(chan error)
|
||||||
|
@ -5,6 +5,9 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
iofs "io/fs"
|
iofs "io/fs"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
eventtypes "github.com/containerd/containerd/api/events"
|
eventtypes "github.com/containerd/containerd/api/events"
|
||||||
@ -31,40 +34,54 @@ func TestNewContainerd(t *testing.T) {
|
|||||||
func TestVerifyStatusResponse(t *testing.T) {
|
func TestVerifyStatusResponse(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
|
tmpDir := t.TempDir()
|
||||||
|
|
||||||
|
err := os.MkdirAll(filepath.Join(tmpDir, "etc", "target", "certs.d"), 0o777)
|
||||||
|
require.NoError(t, err)
|
||||||
|
err = os.MkdirAll(filepath.Join(tmpDir, "etc", "symlink"), 0o777)
|
||||||
|
require.NoError(t, err)
|
||||||
|
err = os.Symlink(filepath.Join(tmpDir, "etc", "target", "certs.d"), filepath.Join(tmpDir, "etc", "symlink", "certs.d"))
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
configPath string
|
|
||||||
requiredConfigPath string
|
requiredConfigPath string
|
||||||
expectedErrMsg string
|
expectedErrMsg string
|
||||||
|
configPaths []string
|
||||||
discardUnpackedLayers bool
|
discardUnpackedLayers bool
|
||||||
}{
|
}{
|
||||||
|
{
|
||||||
|
name: "single config path",
|
||||||
|
configPaths: []string{"/etc/containerd/certs.d"},
|
||||||
|
requiredConfigPath: "/etc/containerd/certs.d",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "multiple config paths",
|
||||||
|
configPaths: []string{"/etc/containerd/certs.d", "/etc/docker/certs.d"},
|
||||||
|
requiredConfigPath: "/etc/containerd/certs.d",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "symlinked config path",
|
||||||
|
configPaths: []string{"/etc/target/certs.d"},
|
||||||
|
requiredConfigPath: "/etc/symlink/certs.d",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "empty config path",
|
name: "empty config path",
|
||||||
configPath: "",
|
configPaths: nil,
|
||||||
requiredConfigPath: "/etc/containerd/certs.d",
|
requiredConfigPath: "/etc/containerd/certs.d",
|
||||||
expectedErrMsg: "Containerd registry config path needs to be set for mirror configuration to take effect",
|
expectedErrMsg: "Containerd registry config path needs to be set for mirror configuration to take effect",
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: "single config path",
|
|
||||||
configPath: "/etc/containerd/certs.d",
|
|
||||||
requiredConfigPath: "/etc/containerd/certs.d",
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: "missing single config path",
|
name: "missing single config path",
|
||||||
configPath: "/etc/containerd/certs.d",
|
configPaths: []string{"/etc/containerd/certs.d"},
|
||||||
requiredConfigPath: "/var/lib/containerd/certs.d",
|
requiredConfigPath: "/var/lib/containerd/certs.d",
|
||||||
expectedErrMsg: "Containerd registry config path is /etc/containerd/certs.d but needs to contain path /var/lib/containerd/certs.d for mirror configuration to take effect",
|
expectedErrMsg: fmt.Sprintf("Containerd registry config path is %[1]s/etc/containerd/certs.d but needs to contain path %[1]s/var/lib/containerd/certs.d for mirror configuration to take effect", tmpDir),
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "multiple config paths",
|
|
||||||
configPath: "/etc/containerd/certs.d:/etc/docker/certs.d",
|
|
||||||
requiredConfigPath: "/etc/containerd/certs.d",
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "missing multiple config paths",
|
name: "missing multiple config paths",
|
||||||
configPath: "/etc/containerd/certs.d:/etc/docker/certs.d",
|
configPaths: []string{"/etc/containerd/certs.d", "/etc/docker/certs.d"},
|
||||||
requiredConfigPath: "/var/lib/containerd/certs.d",
|
requiredConfigPath: "/var/lib/containerd/certs.d",
|
||||||
expectedErrMsg: "Containerd registry config path is /etc/containerd/certs.d:/etc/docker/certs.d but needs to contain path /var/lib/containerd/certs.d for mirror configuration to take effect",
|
expectedErrMsg: fmt.Sprintf("Containerd registry config path is %[1]s/etc/containerd/certs.d:%[1]s/etc/docker/certs.d but needs to contain path %[1]s/var/lib/containerd/certs.d for mirror configuration to take effect", tmpDir),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "discard unpacked layers enabled",
|
name: "discard unpacked layers enabled",
|
||||||
@ -76,12 +93,21 @@ func TestVerifyStatusResponse(t *testing.T) {
|
|||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
|
tmpConfigPaths := []string{}
|
||||||
|
for _, configPath := range tt.configPaths {
|
||||||
|
tmpConfigPaths = append(tmpConfigPaths, filepath.Join(tmpDir, configPath))
|
||||||
|
}
|
||||||
|
tmpConfigPath := strings.Join(tmpConfigPaths, string(os.PathListSeparator))
|
||||||
|
tmpRequiredPath := filepath.Join(tmpDir, tt.requiredConfigPath)
|
||||||
|
err := os.MkdirAll(tmpRequiredPath, 0o777)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
resp := &runtimeapi.StatusResponse{
|
resp := &runtimeapi.StatusResponse{
|
||||||
Info: map[string]string{
|
Info: map[string]string{
|
||||||
"config": fmt.Sprintf(`{"registry": {"configPath": %q}, "containerd": {"runtimes":{"discardUnpackedLayers": %v}}}`, tt.configPath, tt.discardUnpackedLayers),
|
"config": fmt.Sprintf(`{"registry": {"configPath": %q}, "containerd": {"runtimes":{"discardUnpackedLayers": %v}}}`, tmpConfigPath, tt.discardUnpackedLayers),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
err := verifyStatusResponse(resp, tt.requiredConfigPath)
|
err = verifyStatusResponse(resp, tmpRequiredPath)
|
||||||
if tt.expectedErrMsg != "" {
|
if tt.expectedErrMsg != "" {
|
||||||
require.EqualError(t, err, tt.expectedErrMsg)
|
require.EqualError(t, err, tt.expectedErrMsg)
|
||||||
return
|
return
|
||||||
@ -91,6 +117,36 @@ func TestVerifyStatusResponse(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestWalkSymbolicLinks(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
tmpDir := t.TempDir()
|
||||||
|
targetPath := filepath.Join(tmpDir, "data.txt")
|
||||||
|
err := os.WriteFile(targetPath, []byte("hello world"), 0o777)
|
||||||
|
require.NoError(t, err)
|
||||||
|
firstOrderPath := filepath.Join(tmpDir, "first.txt")
|
||||||
|
err = os.Symlink(targetPath, firstOrderPath)
|
||||||
|
require.NoError(t, err)
|
||||||
|
secondOrderPath := filepath.Join(tmpDir, "second.txt")
|
||||||
|
err = os.Symlink(firstOrderPath, secondOrderPath)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Second order symlink
|
||||||
|
paths, err := walkSymbolicLinks(secondOrderPath)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, []string{targetPath, firstOrderPath, secondOrderPath}, paths)
|
||||||
|
|
||||||
|
// First order symlink
|
||||||
|
paths, err = walkSymbolicLinks(firstOrderPath)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, []string{targetPath, firstOrderPath}, paths)
|
||||||
|
|
||||||
|
// No symnlink
|
||||||
|
paths, err = walkSymbolicLinks(targetPath)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, []string{targetPath}, paths)
|
||||||
|
}
|
||||||
|
|
||||||
func TestCreateFilter(t *testing.T) {
|
func TestCreateFilter(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user