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

Commit

Permalink
Manage config files hostname, hosts, resolv.conf outside of container…
Browse files Browse the repository at this point in the history
…'s rootfs and bind mount them read-only into the container.

(to prevent those runtime-related changes from being included into a container image on commit)

Signed-off-by: Max Goltzsche <[email protected]>
  • Loading branch information
mgoltzsche committed Oct 28, 2018
1 parent 5f5cb3c commit ad34d7a
Show file tree
Hide file tree
Showing 7 changed files with 64 additions and 27 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ to either use an external runc binary or use libcontainer (no runtime dependenci
- health check
- improved Docker Compose support
- service discovery integration (hook / DNS; consul, etcd)
- daemon mode
- detached mode
- systemd integration (cgroup, startup notification)
- **1.0 release**
- advanced logging
Expand Down
50 changes: 41 additions & 9 deletions bundle/bundlebuilder.go
Original file line number Diff line number Diff line change
@@ -1,21 +1,26 @@
package bundle

import (
"os"
"path/filepath"

"github.com/cyphar/filepath-securejoin"
"github.com/mgoltzsche/ctnr/pkg/generate"
"github.com/openSUSE/umoci/pkg/fseval"
"github.com/pkg/errors"
)

type BundleBuilder struct {
id string
*generate.SpecBuilder
image BundleImage
image BundleImage
managedFiles map[string]bool
}

func Builder(id string) *BundleBuilder {
specgen := generate.NewSpecBuilder()
specgen.SetRootPath("rootfs")
b := &BundleBuilder{"", &specgen, nil}
b := &BundleBuilder{"", &specgen, nil, map[string]bool{}}
b.SetID(id)
return b
}
Expand All @@ -29,28 +34,55 @@ func (b *BundleBuilder) SetID(id string) {
b.AddAnnotation(ANNOTATION_BUNDLE_ID, id)
}

func (b *BundleBuilder) GetID() string {
return b.id
}

func (b *BundleBuilder) SetImage(image BundleImage) {
b.ApplyImage(image.Config())
b.image = image
}

// Overlays the provided file path with a bind mounted read-only copy.
// The file's content is supposed to be managed by an OCI hook.
func (b *BundleBuilder) AddBindMountConfig(path string) {
path = filepath.Clean(path)
opts := []string{"bind", "mode=0444", "nosuid", "noexec", "nodev", "ro"}
b.managedFiles[path] = true
b.AddBindMount(filepath.Join("mount", path), path, opts)
}

func (b *BundleBuilder) Build(bundle *LockedBundle) (err error) {
// Prepare rootfs
if err = bundle.UpdateRootfs(b.image); err != nil {
return
return errors.Wrap(err, "build bundle")
}

// Generate managed config files
for path := range b.managedFiles {
if err = b.touchManagedFile(bundle.Dir(), path); err != nil {
return errors.Wrap(err, "build bundle")
}
}

// Resolve user/group names
rootfs := filepath.Join(bundle.Dir(), b.Generator.Spec().Root.Path)
spec, err := b.Spec(rootfs)
if err != nil {
return
return errors.Wrap(err, "build bundle")
}

// Apply spec
return bundle.SetSpec(spec)
return errors.Wrap(bundle.SetSpec(spec), "build bundle")
}

func (b *BundleBuilder) touchManagedFile(bundleDir, path string) (err error) {
file, err := securejoin.SecureJoinVFS(filepath.Join(bundleDir, "mount"), path, fseval.RootlessFsEval)
if err != nil {
return
}
if err = os.MkdirAll(filepath.Dir(file), 0755); err != nil {
return
}
f, err := os.OpenFile(file, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644)
if err != nil {
return
}
return f.Close()
}
2 changes: 1 addition & 1 deletion bundle/store/bundlestore.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ func (s *BundleStore) Bundle(id string) (r bundle.Bundle, err error) {
func (s *BundleStore) CreateBundle(id string, update bool) (b *bundle.LockedBundle, err error) {
dir := filepath.Join(s.dir, id)
if id == "" {
if err = os.MkdirAll(s.dir, 0770); err != nil {
if err = os.MkdirAll(s.dir, 0750); err != nil {
return nil, errors.Wrap(err, "create bundle")
}
if dir, err = ioutil.TempDir(s.dir, ""); err != nil {
Expand Down
4 changes: 3 additions & 1 deletion cmd/net.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,9 @@ func runNetInit(cmd *cobra.Command, args []string) (err error) {
// Generate hostname, hosts, resolv.conf files
cfg.SetHostname(spec.Hostname)
applyArgs(&cfg)
return cfg.WriteConfigFiles(filepath.Join(state.Bundle, spec.Root.Path))
rootfs := filepath.Join(state.Bundle, spec.Root.Path)
mounts := filepath.Join(state.Bundle, "mount")
return cfg.WriteConfigFiles(rootfs, mounts)
}

func runNetRemove(cmd *cobra.Command, args []string) (err error) {
Expand Down
9 changes: 6 additions & 3 deletions model/oci/ocitransform.go
Original file line number Diff line number Diff line change
Expand Up @@ -136,20 +136,23 @@ func ToSpec(service *model.Service, res model.ResourceResolver, rootless bool, p
return errors.New("transform: no networks supported in rootless mode")
}

// Use host networks by removing 'network' namespace
// Use host network by removing 'network' namespace
if useHostNetwork {
spec.UseHostNetwork()
} else {
spec.AddOrReplaceLinuxNamespace(specs.NetworkNamespace, "")
}

// Add hostname. Empty string results in host's hostname
if service.Hostname != "" || useHostNetwork {
// Add hostname
if service.Hostname != "" {
spec.SetHostname(service.Hostname)
}

// Add network hook
if len(networks) > 0 {
spec.AddBindMountConfig("/etc/hostname")
spec.AddBindMountConfig("/etc/hosts")
spec.AddBindMountConfig("/etc/resolv.conf")
hook, err := generate.NewHookBuilderFromSpec(sp)
if err != nil {
return err
Expand Down
23 changes: 11 additions & 12 deletions net/configbuilder.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,17 +81,10 @@ func (b *ConfigFileGenerator) AddDnsOptions(opts []string) {
}
}

func (b *ConfigFileGenerator) WriteConfigFiles(rootfs string) error {
// Create /etc dir in bundle's rootfs
etcDir := filepath.Join(rootfs, "etc")
if _, err := os.Stat(etcDir); os.IsNotExist(err) {
if err = os.Mkdir(etcDir, 0755); err != nil {
return err
}
}
hostnameFile := filepath.Join(etcDir, "hostname")
hostsFile := filepath.Join(etcDir, "hosts")
resolvConfFile := filepath.Join(etcDir, "resolv.conf")
func (b *ConfigFileGenerator) WriteConfigFiles(rootfs, overlay string) error {
hostnameFile := filepath.Join(overlay, "etc", "hostname")
hostsFile := filepath.Join(overlay, "etc", "hosts")
resolvConfFile := filepath.Join(overlay, "etc", "resolv.conf")

// Write /etc/hostname
hostname := b.hostname
Expand All @@ -102,11 +95,13 @@ func (b *ConfigFileGenerator) WriteConfigFiles(rootfs string) error {
}

// Write /etc/resolv.conf if value set
// TODO: apply existing resolv.conf first
if err := b.writeResolvConf(resolvConfFile); err != nil {
return err
}

// Write /etc/hosts if not empty
// TODO: apply existing hosts first
return b.writeHosts(hostsFile)
}

Expand Down Expand Up @@ -181,7 +176,11 @@ func (b *ConfigFileGenerator) writeHosts(dest string) error {
func writeFile(dest, content string) error {
f, err := os.OpenFile(dest, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644)
if err != nil {
return err
if os.IsNotExist(err) {
errors.Errorf("file %s does not exist. needs to exist and bind mounted into the containers's rootfs", dest)
} else {
return err
}
}
if _, err := f.Write([]byte(content)); err != nil {
f.Close()
Expand Down
1 change: 1 addition & 0 deletions pkg/generate/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ func (b *SpecBuilder) ToRootless() {

func (b *SpecBuilder) UseHostNetwork() {
b.RemoveLinuxNamespace(rspecs.NetworkNamespace)
b.SetHostname("") // empty hostname results in host's hostname
opts := []string{"bind", "mode=0444", "nosuid", "noexec", "nodev", "ro"}
b.AddBindMount("/etc/hosts", "/etc/hosts", opts)
b.AddBindMount("/etc/resolv.conf", "/etc/resolv.conf", opts)
Expand Down

0 comments on commit ad34d7a

Please sign in to comment.