Hello community, here is the log from the commit of package ignition for openSUSE:Factory checked in at 2019-12-23 22:47:40 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/ignition (Old) and /work/SRC/openSUSE:Factory/.ignition.new.6675 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "ignition" Mon Dec 23 22:47:40 2019 rev:8 rq:758999 version:2.1.1 Changes: -------- --- /work/SRC/openSUSE:Factory/ignition/ignition.changes 2019-11-20 12:38:23.037783832 +0100 +++ /work/SRC/openSUSE:Factory/.ignition.new.6675/ignition.changes 2019-12-23 22:49:13.370107054 +0100 @@ -1,0 +2,21 @@ +Mon Dec 23 09:16:22 UTC 2019 - [email protected] + +- Update to version 2.1.1: + * stages/files: don't relabel /home and /root symlinks + * tests/filesystems: fix error handling + * blackbox tests: don't swallow errors + * log: use os.ProcessState.ExitCode instead of unix + * travis: bump min go to 1.12 + * news: add notes for 2.1.1 + * readme: fix links, add validation container docs + * Dockerfile: add dockerfile for ignition-validate + * news: add news for v2.1.0 + * README.md: add details about dracut and branches here + * doc/operator-notes: simplify SELinux section + * mount: also relabel mount points + * util: factor out FindFirstMissingDirForFile function + * files: perform relabeling from initrd + * doc/examples: Make example file path valid + * Rework fetch/resource to automatically append required headers + +------------------------------------------------------------------- Old: ---- ignition-2.0.1+git20191112.a924dd7.tar.xz New: ---- ignition-2.1.1.tar.xz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ ignition.spec ++++++ --- /var/tmp/diff_new_pack.NcIjni/_old 2019-12-23 22:49:14.154107344 +0100 +++ /var/tmp/diff_new_pack.NcIjni/_new 2019-12-23 22:49:14.154107344 +0100 @@ -1,7 +1,7 @@ # # spec file for package ignition # -# Copyright (c) 2019 SUSE LLC. +# Copyright (c) 2019 SUSE LLC # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -17,7 +17,7 @@ Name: ignition -Version: 2.0.1+git20191112.a924dd7 +Version: 2.1.1 Release: 0 Summary: First boot installer and configuration tool License: Apache-2.0 ++++++ _service ++++++ --- /var/tmp/diff_new_pack.NcIjni/_old 2019-12-23 22:49:14.186107356 +0100 +++ /var/tmp/diff_new_pack.NcIjni/_new 2019-12-23 22:49:14.190107357 +0100 @@ -1,7 +1,7 @@ <services> <service name="tar_scm" mode="disabled"> - <param name="version">2.0.1</param> - <param name="versionformat">2.0.1+git%cd.%h</param> + <param name="version">2.1.1</param> + <param name="revision">v2.1.1</param> <param name="url">git://github.com/coreos/ignition.git</param> <param name="scm">git</param> <param name="changesgenerate">enable</param> ++++++ _servicedata ++++++ --- /var/tmp/diff_new_pack.NcIjni/_old 2019-12-23 22:49:14.202107361 +0100 +++ /var/tmp/diff_new_pack.NcIjni/_new 2019-12-23 22:49:14.206107363 +0100 @@ -1,6 +1,6 @@ <servicedata> <service name="tar_scm"> <param name="url">git://github.com/coreos/ignition.git</param> - <param name="changesrevision">a924dd7d11685a30bf79f20ce9fc3ca93860f032</param> + <param name="changesrevision">40c0b57b7606bd23210059c5554f151776a1d64b</param> </service> </servicedata> \ No newline at end of file ++++++ ignition-2.0.1+git20191112.a924dd7.tar.xz -> ignition-2.1.1.tar.xz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ignition-2.0.1+git20191112.a924dd7/.travis.yml new/ignition-2.1.1/.travis.yml --- old/ignition-2.0.1+git20191112.a924dd7/.travis.yml 2019-11-12 17:46:21.000000000 +0100 +++ new/ignition-2.1.1/.travis.yml 2019-12-13 23:06:16.000000000 +0100 @@ -3,7 +3,7 @@ go_import_path: github.com/coreos/ignition/v2 go: - - "1.11.x" + - "1.12.x" - "1.13.x" arch: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ignition-2.0.1+git20191112.a924dd7/Dockerfile.validate new/ignition-2.1.1/Dockerfile.validate --- old/ignition-2.0.1+git20191112.a924dd7/Dockerfile.validate 1970-01-01 01:00:00.000000000 +0100 +++ new/ignition-2.1.1/Dockerfile.validate 2019-12-13 23:06:16.000000000 +0100 @@ -0,0 +1,9 @@ +FROM golang:latest AS builder +RUN mkdir /ignition-validate +COPY . /ignition-validate +WORKDIR /ignition-validate +RUN ./build_releases + +FROM scratch +COPY --from=builder /ignition-validate/bin/releases/ignition-validate-x86_64-unknown-linux-gnu /usr/local/bin/ignition-validate +ENTRYPOINT ["/usr/local/bin/ignition-validate"] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ignition-2.0.1+git20191112.a924dd7/NEWS new/ignition-2.1.1/NEWS --- old/ignition-2.0.1+git20191112.a924dd7/NEWS 2019-11-12 17:46:21.000000000 +0100 +++ new/ignition-2.1.1/NEWS 2019-12-13 23:06:16.000000000 +0100 @@ -1,3 +1,45 @@ +13-Dec-2019 Ignition 2.1.1 + + Bug Fixes: + + - Fix panics when processes Ignition starts fail + + Features: + + - An ignition-validate container is now built and can be used instead of + the ignition-validate binaries + +12-Dec-2019 Ignition 2.1.0 + + Bug Fixes: + + - Do not panic when filesystem paths are unspecified + - Specify the correct config version HTTP Accept headers when fetching + configs + - Write the config cache file atomically + - Relabel symlinks for masking systemd units + - Fix bug where empty GPT labels were treated as errors + - Do not generate warnings if mode is unset for files with only an append + section + - Validate HTTP(S) proxy urls in spec 3.1.0-experimental + + Features: + + - Ignition now logs the name of the stage it is running + - Ignition now relabels files directly instead of writing systemd units to + do so. Requires Linux 5.4.0+ or a patch. See operator notes for more + details + - Add optional "fetch" stage to cache the rendered config, but not apply + any of it + - Add support for aliyun cloud + - Add support for zVM hypervisor + - Add support for specifying mount options for filesystems in spec + 3.1.0-experimental + + Dependency Changes: + + - Ignition no longer needs the chroot or id binaries in the initramfs + 24-Jul-2019 Ignition 2.0.1 Bug Fixes: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ignition-2.0.1+git20191112.a924dd7/README.md new/ignition-2.1.1/README.md --- old/ignition-2.0.1+git20191112.a924dd7/README.md 2019-11-12 17:46:21.000000000 +0100 +++ new/ignition-2.1.1/README.md 2019-12-13 23:06:16.000000000 +0100 @@ -1,8 +1,6 @@ # Ignition -Ignition is the utility used by CoreOS Container Linux to manipulate disks during the initramfs. This includes partitioning disks, formatting partitions, writing files (regular files, systemd units, etc.), and configuring users. On first boot, Ignition reads its configuration from a source of truth (remote URL, network metadata service, hypervisor bridge, etc.) and applies the configuration. - -Ignition has two main development branches: master and spec2x. This is the master branch which is for Ignition included in Red Hat CoreOS and Fedora CoreOS. For Ignition development for Container Linux see the [spec2x](https://github.com/coreos/ignition/tree/spec2x) branch. +Ignition is the utility used by CoreOS Container Linux, Fedora CoreOS, and RHEL CoreOS to manipulate disks during the initramfs. This includes partitioning disks, formatting partitions, writing files (regular files, systemd units, etc.), and configuring users. On first boot, Ignition reads its configuration from a source of truth (remote URL, network metadata service, hypervisor bridge, etc.) and applies the configuration. ## Usage @@ -10,8 +8,8 @@ ## Contact -- Mailing list: [coreos-dev](https://groups.google.com/forum/#!forum/coreos-dev) -- IRC: #[coreos](irc://irc.freenode.org:6697/#coreos) on freenode.org +- Mailing list: [[email protected]](https://lists.fedoraproject.org/archives/list/[email protected]/) +- IRC: #[fedora-coreos](irc://irc.freenode.org:6697/#fedora-coreos) on freenode.org - Bugs: [issues][issues] ## Contributing @@ -26,7 +24,32 @@ ## Config Validation -To validate a config for Ignition there are binaries for a cli tool called ignition-validate available [on the releases page][releases], and an online validator available [on the CoreOS website][online-validator]. +To validate a config for Ignition there are binaries for a cli tool called `ignition-validate` available [on the releases page][releases]. There is also an ignition-validate container: `quay.io/coreos/ignition-validate`. + +Example: +``` +# This example uses podman, but docker can be used too +podman run --rm -i quay.io/coreos/ignition-validate - < myconfig.ign +``` + +## Dracut + +For distributions that use dracut, there is an +[ignition-dracut](https://github.com/coreos/ignition-dracut) +repo which contains scripts and systemd units for boot-time +execution. But it's very likely that distributions will have +to do additional work in order to properly integrate with +Ignition. + +## Branches + +There are two branches: +- `master` works with the `master` branch of ignition-dracut + and is currently used by Fedora CoreOS, which targets + Ignition v2 (spec 3). +- `spec2x` works with the `spec2x` branch of ignition-dracut + and is currently used by CL and RHEL CoreOS, which (for + now) targets Ignition v0.x (spec 2). [getting started]: doc/getting-started.md [issues]: https://github.com/coreos/ignition/issues/new/choose diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ignition-2.0.1+git20191112.a924dd7/doc/examples.md new/ignition-2.1.1/doc/examples.md --- old/ignition-2.0.1+git20191112.a924dd7/doc/examples.md 2019-11-12 17:46:21.000000000 +0100 +++ new/ignition-2.1.1/doc/examples.md 2019-12-13 23:06:16.000000000 +0100 @@ -59,14 +59,14 @@ ``` ## Create Files on the Root Filesystem -In many cases it is useful to write files to the root filesystem. This example writes a single file to `/foo/bar` on the root filesystem. The contents of the file ("example file") are specified inline in the config using the [data URL scheme][rfc2397]. +In many cases it is useful to write files to the root filesystem. This example writes a single file to `/etc/someconfig` on the root filesystem. The contents of the file ("example file") are specified inline in the config using the [data URL scheme][rfc2397]. ```json ignition { "ignition": { "version": "3.0.0" }, "storage": { "files": [{ - "path": "/foo/bar", + "path": "/etc/someconfig", "mode": 420, "contents": { "source": "data:,example%20file%0A" } }] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ignition-2.0.1+git20191112.a924dd7/doc/operator-notes.md new/ignition-2.1.1/doc/operator-notes.md --- old/ignition-2.0.1+git20191112.a924dd7/doc/operator-notes.md 2019-11-12 17:46:21.000000000 +0100 +++ new/ignition-2.1.1/doc/operator-notes.md 2019-12-13 23:06:16.000000000 +0100 @@ -37,35 +37,10 @@ ## SELinux -When using Ignition with distributions which have [SELinux][selinux] enabled, extra care must be taken to prevent Ignition from creating files that lack SELinux labels. Unfortunately, distributions do not typically include SELinux policies in the initramfs where Ignition runs, so any files, directories, and links created by Ignition don't receive the proper default SELinux labels. - -A workaround for this issue is to use [`restorecon`][restorecon] in a oneshot systemd unit to relabel files that Ignition has touched. This unit can be set to run after the SELinux policies have loaded, but before services will try to use them. - -An example of this unit is as follows: - -``` -[Unit] -Requires=systemd-udevd.target -After=systemd-udevd.target - -Before=sssd.service -DefaultDependencies=no -ConditionFirstBoot=true - -[Service] -Type=oneshot -ExecStart=/usr/sbin/restorecon /foo/bar /etc/test /etc/systemd/system/example.service /etc/passwd /etc/group /etc/shadow - -[Install] -WantedBy=multi-user.target -``` - -This unit will vary based on the Ignition config it is being added to and the distribution that Ignition is running on. Notably the paths listed in the unit are all paths that Ignition caused to be modified or created, not just paths listed in `storage.files`. For example, if a new user is created then `/etc/passwd`, `/etc/shadow`, and `/etc/group` will all need to be relabeled. - -If tooling is being used to generate Ignition configs, the tooling _should_ generate such a unit when creating a config for distributions which rely on SELinux. +Ignition fully supports distributions which have [SELinux][selinux] enabled. It requires that the distribution ships the [`setfiles`][setfiles] utility. The kernel must be at least v5.5 or alternatively have [this patch](https://lore.kernel.org/selinux/[email protected]/T/#u) backported. [selinux]: https://selinuxproject.org/page/Main_Page -[restorecon]: https://linux.die.net/man/8/restorecon +[setfiles]: https://linux.die.net/man/8/setfiles ## Partition Reuse Semantics diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ignition-2.0.1+git20191112.a924dd7/internal/distro/distro.go new/ignition-2.1.1/internal/distro/distro.go --- old/ignition-2.0.1+git20191112.a924dd7/internal/distro/distro.go 2019-11-12 17:46:21.000000000 +0100 +++ new/ignition-2.1.1/internal/distro/distro.go 2019-12-13 23:06:16.000000000 +0100 @@ -41,10 +41,7 @@ udevadmCmd = "udevadm" usermodCmd = "usermod" useraddCmd = "useradd" - - // The restorecon tool is embedded inside of a systemd unit - // and as such requires the absolute path - restoreconCmd = "/usr/sbin/restorecon" + setfilesCmd = "setfiles" // Filesystem tools btrfsMkfsCmd = "mkfs.btrfs" @@ -75,15 +72,15 @@ func KernelCmdlinePath() string { return kernelCmdlinePath } func SystemConfigDir() string { return fromEnv("SYSTEM_CONFIG_DIR", systemConfigDir) } -func GroupaddCmd() string { return groupaddCmd } -func MdadmCmd() string { return mdadmCmd } -func MountCmd() string { return mountCmd } -func SgdiskCmd() string { return sgdiskCmd } -func ModprobeCmd() string { return modprobeCmd } -func UdevadmCmd() string { return udevadmCmd } -func UsermodCmd() string { return usermodCmd } -func UseraddCmd() string { return useraddCmd } -func RestoreconCmd() string { return restoreconCmd } +func GroupaddCmd() string { return groupaddCmd } +func MdadmCmd() string { return mdadmCmd } +func MountCmd() string { return mountCmd } +func SgdiskCmd() string { return sgdiskCmd } +func ModprobeCmd() string { return modprobeCmd } +func UdevadmCmd() string { return udevadmCmd } +func UsermodCmd() string { return usermodCmd } +func UseraddCmd() string { return useraddCmd } +func SetfilesCmd() string { return setfilesCmd } func BtrfsMkfsCmd() string { return btrfsMkfsCmd } func Ext4MkfsCmd() string { return ext4MkfsCmd } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ignition-2.0.1+git20191112.a924dd7/internal/exec/engine.go new/ignition-2.1.1/internal/exec/engine.go --- old/ignition-2.0.1+git20191112.a924dd7/internal/exec/engine.go 2019-11-12 17:46:21.000000000 +0100 +++ new/ignition-2.1.1/internal/exec/engine.go 2019-12-13 23:06:16.000000000 +0100 @@ -272,9 +272,7 @@ if err != nil { return types.Config{}, err } - rawCfg, err := e.Fetcher.FetchToBuffer(*u, resource.FetchOptions{ - Headers: resource.ConfigHeaders, - }) + rawCfg, err := e.Fetcher.FetchToBuffer(*u, resource.FetchOptions{}) if err != nil { return types.Config{}, err } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ignition-2.0.1+git20191112.a924dd7/internal/exec/stages/files/files.go new/ignition-2.1.1/internal/exec/stages/files/files.go --- old/ignition-2.0.1+git20191112.a924dd7/internal/exec/stages/files/files.go 2019-11-12 17:46:21.000000000 +0100 +++ new/ignition-2.1.1/internal/exec/stages/files/files.go 2019-12-13 23:06:16.000000000 +0100 @@ -17,10 +17,7 @@ import ( "errors" "fmt" - "io/ioutil" - "os" "path/filepath" - "strings" "github.com/coreos/ignition/v2/config/v3_1_experimental/types" "github.com/coreos/ignition/v2/internal/distro" @@ -32,9 +29,6 @@ const ( name = "files" - - // see https://github.com/systemd/systemd/commit/65e183d7899eb3725d3009196ac4decf1090b580 - relabelExtraDir = "/run/systemd/relabel-extra.d" ) var ( @@ -87,17 +81,8 @@ return fmt.Errorf("failed to create units: %v", err) } - // add systemd unit to relabel files - if err := s.addRelabelUnit(); err != nil { - return fmt.Errorf("failed to add relabel unit: %v", err) - } - - // Add a file in /run/systemd/relabel-extra.d/ with paths that need to be relabeled - // as early as possible (e.g. systemd units so systemd can read them while building its - // graph). These are relabeled very early (right after policy load) so it cannot relabel - // across mounts. Only relabel things in /etc here. - if err := s.addRelabelExtraFile(); err != nil { - return fmt.Errorf("failed to write systemd relabel file: %v", err) + if err := s.relabelFiles(); err != nil { + return fmt.Errorf("failed to handle relabeling: %v", err) } return nil @@ -106,23 +91,11 @@ // checkRelabeling determines whether relabeling is supported/requested so that // we only collect filenames if we need to. func (s *stage) checkRelabeling() error { - if !distro.SelinuxRelabel() || distro.RestoreconCmd() == "" { + if !distro.SelinuxRelabel() { s.Logger.Debug("compiled without relabeling support, skipping") return nil } - path, err := s.JoinPath(distro.RestoreconCmd()) - if err != nil { - return fmt.Errorf("error resolving path for %s: %v", distro.RestoreconCmd(), err) - } - - _, err = os.Lstat(path) - if err != nil && os.IsNotExist(err) { - return fmt.Errorf("targeting root without %s, cannot relabel", distro.RestoreconCmd()) - } else if err != nil { - return fmt.Errorf("error checking for %s in root: %v", distro.RestoreconCmd(), err) - } - // initialize to non-nil (whereas a nil slice means not to append, even // though they're functionally equivalent) s.toRelabel = []string{} @@ -137,82 +110,24 @@ // relabel adds one or more paths to the list of paths that need relabeling. func (s *stage) relabel(paths ...string) { if s.toRelabel != nil { - s.toRelabel = append(s.toRelabel, paths...) + for _, path := range paths { + s.toRelabel = append(s.toRelabel, filepath.Join(s.DestDir, path)) + } } } -// addRelabelUnit creates and enables a runtime systemd unit to run restorecon -// if there are files that need to be relabeled. -func (s *stage) addRelabelUnit() error { - if len(s.toRelabel) == 0 { +// relabelFiles relabels all the files that were marked for relabeling using +// the libselinux APIs. +func (s *stage) relabelFiles() error { + if s.toRelabel == nil || len(s.toRelabel) == 0 { return nil } - contents := `[Unit] -Description=Relabel files created by Ignition -DefaultDependencies=no -After=local-fs.target -Before=sysinit.target systemd-sysctl.service -ConditionSecurity=selinux -ConditionPathExists=/etc/selinux/ignition.relabel -OnFailure=emergency.target -OnFailureJobMode=replace-irreversibly - -[Service] -Type=oneshot -ExecStart=` + distro.RestoreconCmd() + ` -0vRif /etc/selinux/ignition.relabel -ExecStart=/usr/bin/rm /etc/selinux/ignition.relabel -RemainAfterExit=yes` - - // create the unit file itself - unit := types.Unit{ - Name: "ignition-relabel.service", - Contents: &contents, - } - - if err := s.writeSystemdUnit(unit, true); err != nil { - return err - } - - if err := s.EnableRuntimeUnit(unit, "sysinit.target"); err != nil { - return err - } - - // and now create the list of files to relabel - etcRelabelPath, err := s.JoinPath("etc/selinux/ignition.relabel") - if err != nil { - return err - } - f, err := os.Create(etcRelabelPath) - if err != nil { - return err - } - defer f.Close() - - // yes, apparently the final \0 is needed - _, err = f.WriteString(strings.Join(s.toRelabel, "\000") + "\000") - return err -} - -// addRelabelExtraFile writes a file to /run/systemd/relabel-extra.d/ with a list of files -// that should be relabeled immediately after policy load. In our case that's everything we -// wrote under /etc. This ensures systemd can access the files when building it's graph. -func (s stage) addRelabelExtraFile() error { - relabelFilePath := filepath.Join(relabelExtraDir, "ignition.relabel") - s.Logger.Info("adding relabel-extra.d/ file: %q", relabelFilePath) - defer s.Logger.Info("finished adding relabel file") - - relabelFileContents := "" - for _, file := range s.toRelabel { - if strings.HasPrefix(file, "/etc") { - relabelFileContents += file + "\n" - } - } - if relabelFileContents == "" { - return nil - } - if err := os.MkdirAll(relabelExtraDir, 0755); err != nil { - return err - } - return ioutil.WriteFile(relabelFilePath, []byte(relabelFileContents), 0644) + // We could go further here and use the `setfscreatecon` API so that we + // atomically create the files from the start with the right label, but (1) + // atomicity isn't really necessary here since there is not even a policy + // loaded and hence no MAC enforced, and (2) we'd still need after-the-fact + // labeling for files created by processes we call out to, like `useradd`. + + return s.RelabelFiles(s.toRelabel) } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ignition-2.0.1+git20191112.a924dd7/internal/exec/stages/files/filesystemEntries.go new/ignition-2.1.1/internal/exec/stages/files/filesystemEntries.go --- old/ignition-2.0.1+git20191112.a924dd7/internal/exec/stages/files/filesystemEntries.go 2019-11-12 17:46:21.000000000 +0100 +++ new/ignition-2.1.1/internal/exec/stages/files/filesystemEntries.go 2019-12-13 23:06:16.000000000 +0100 @@ -254,31 +254,14 @@ if !s.relabeling() { return nil } - // relabel from the first parent dir that we'll have to create -- - // alternatively, we could make `MkdirForFile` fancier instead of - // using `os.MkdirAll`, though that's quite a lot of levels to plumb - // through - relabelFrom := path - dir := filepath.Dir(path) - for { - exists := true - if _, err := os.Stat(dir); err != nil && os.IsNotExist(err) { - exists = false - } else if err != nil { - return err - } - // we're done on the first hit -- also sanity check we didn't - // somehow get all the way up to /sysroot - if exists || dir == s.DestDir { - break - } - relabelFrom = dir - dir = filepath.Dir(dir) + missing_dir, err := util.FindFirstMissingDirForFile(path) + if err != nil { + return err } - // trim off prefix since this needs to be relative to the sysroot - s.relabel(relabelFrom[len(s.DestDir):]) + // trim off prefix since this needs to be relative to the sysroot + s.relabel(missing_dir[len(s.DestDir):]) return nil } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ignition-2.0.1+git20191112.a924dd7/internal/exec/stages/mount/mount.go new/ignition-2.1.1/internal/exec/stages/mount/mount.go --- old/ignition-2.0.1+git20191112.a924dd7/internal/exec/stages/mount/mount.go 2019-11-12 17:46:21.000000000 +0100 +++ new/ignition-2.1.1/internal/exec/stages/mount/mount.go 2019-12-13 23:06:16.000000000 +0100 @@ -111,6 +111,15 @@ return err } + var firstMissing string + if distro.SelinuxRelabel() { + var err error + firstMissing, err = util.FindFirstMissingDirForFile(path) + if err != nil { + return err + } + } + if _, err := os.Stat(path); err != nil && os.IsNotExist(err) { if err := os.MkdirAll(path, 0755); err != nil { return err @@ -119,6 +128,12 @@ return err } + if distro.SelinuxRelabel() { + if err := s.RelabelFiles([]string{firstMissing}); err != nil { + return err + } + } + args := translateOptionSliceToString(fs.MountOptions, ",") cmd := exec.Command(distro.MountCmd(), "-o", args, "-t", *fs.Format, fs.Device, path) if _, err := s.Logger.LogCmd(cmd, @@ -126,6 +141,18 @@ ); err != nil { return err } + + if distro.SelinuxRelabel() { + // relabel the root of the disk if it's fresh + if isEmpty, err := util.DirIsEmpty(path); err != nil { + return fmt.Errorf("Checking if directory %s is empty: %v", path, err) + } else if isEmpty { + if err := s.RelabelFiles([]string{path}); err != nil { + return err + } + } + } + return nil } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ignition-2.0.1+git20191112.a924dd7/internal/exec/util/file.go new/ignition-2.1.1/internal/exec/util/file.go --- old/ignition-2.0.1+git20191112.a924dd7/internal/exec/util/file.go 2019-11-12 17:46:21.000000000 +0100 +++ new/ignition-2.1.1/internal/exec/util/file.go 2019-12-13 23:06:16.000000000 +0100 @@ -242,6 +242,47 @@ return os.MkdirAll(filepath.Dir(path), DefaultDirectoryPermissions) } +// FindFirstMissingDirForFile returns the first component which was found to be +// missing for the path. +func FindFirstMissingDirForFile(path string) (string, error) { + entry := path + dir := filepath.Dir(path) + for { + exists := true + if _, err := os.Stat(dir); err != nil && os.IsNotExist(err) { + exists = false + } else if err != nil { + return "", err + } + + // also sanity check we didn't somehow get all the way up to /sysroot + if dir == "/" { + return "", fmt.Errorf("/ doesn't seem to exist") + } + if exists { + return entry, nil + } + entry = dir + dir = filepath.Dir(dir) + } +} + +// DirIsEmpty checks whether a directory is empty. +// Adapted from https://stackoverflow.com/a/30708914 +func DirIsEmpty(dirpath string) (bool, error) { + dfd, err := os.Open(dirpath) + if err != nil { + return false, err + } + defer dfd.Close() + + _, err = dfd.Readdirnames(1) + if err == io.EOF { + return true, nil + } + return false, err +} + // getFileOwner will return the uid and gid for the file at a given path. If the // file doesn't exist, or some other error is encountered when running stat on // the path, 0, 0, and 0 will be returned. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ignition-2.0.1+git20191112.a924dd7/internal/exec/util/selinux.go new/ignition-2.1.1/internal/exec/util/selinux.go --- old/ignition-2.0.1+git20191112.a924dd7/internal/exec/util/selinux.go 1970-01-01 01:00:00.000000000 +0100 +++ new/ignition-2.1.1/internal/exec/util/selinux.go 2019-12-13 23:06:16.000000000 +0100 @@ -0,0 +1,86 @@ +// Copyright 2019 Red Hat, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package util + +import ( + "bufio" + "fmt" + "os" + "os/exec" + "strings" + + "github.com/coreos/ignition/v2/internal/distro" +) + +const ( + selinuxConfig = "/etc/selinux/config" + selinuxFileContexts = "contexts/files/file_contexts" +) + +var selinuxPolicy = "" + +func (ut Util) getSelinuxPolicy() (string, error) { + if selinuxPolicy == "" { + configPath, err := ut.JoinPath(selinuxConfig) + if err != nil { + return "", err + } + + file, err := os.Open(configPath) + if err != nil { + return "", fmt.Errorf("failed to open %v: %v", selinuxConfig, err) + } + defer file.Close() + + scanner := bufio.NewScanner(file) + for scanner.Scan() { + line := strings.TrimSpace(scanner.Text()) + if strings.HasPrefix(line, "SELINUXTYPE=") { + policy := line[len("SELINUXTYPE="):] + if len(policy) == 0 { + return "", fmt.Errorf("invalid SELINUXTYPE value in %v", selinuxConfig) + } + selinuxPolicy = policy + break + } + } + + if selinuxPolicy == "" { + return "", fmt.Errorf("didn't find SELINUXTYPE in %v", selinuxConfig) + } + } + + return selinuxPolicy, nil +} + +// RelabelFiles relabels all the files matching the globby patterns given. +func (ut Util) RelabelFiles(patterns []string) error { + policy, err := ut.getSelinuxPolicy() + if err != nil { + return err + } + + file_contexts, err := ut.JoinPath("/etc/selinux", policy, selinuxFileContexts) + if err != nil { + return err + } + + cmd := exec.Command(distro.SetfilesCmd(), "-vFi0", "-r", ut.DestDir, file_contexts, "-f", "-") + cmd.Stdin = strings.NewReader(strings.Join(patterns, "\000") + "\000") + if _, err := ut.Logger.LogCmd(cmd, "relabeling %d patterns", len(patterns)); err != nil { + return err + } + return nil +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ignition-2.0.1+git20191112.a924dd7/internal/log/log.go new/ignition-2.1.1/internal/log/log.go --- old/ignition-2.0.1+git20191112.a924dd7/internal/log/log.go 2019-11-12 17:46:21.000000000 +0100 +++ new/ignition-2.1.1/internal/log/log.go 2019-12-13 23:06:16.000000000 +0100 @@ -20,8 +20,6 @@ "log/syslog" "os/exec" "strings" - - "golang.org/x/sys/unix" ) type LoggerOps interface { @@ -150,7 +148,7 @@ cmd.Stderr = stderr if err := cmd.Run(); err != nil { if exitErr, ok := err.(*exec.ExitError); ok { - code = exitErr.Sys().(unix.WaitStatus).ExitStatus() + code = exitErr.ExitCode() } return fmt.Errorf("%v: Cmd: %s Stdout: %q Stderr: %q", err, cmdLine, stdout.Bytes(), stderr.Bytes()) } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ignition-2.0.1+git20191112.a924dd7/internal/providers/aliyun/aliyun.go new/ignition-2.1.1/internal/providers/aliyun/aliyun.go --- old/ignition-2.0.1+git20191112.a924dd7/internal/providers/aliyun/aliyun.go 2019-11-12 17:46:21.000000000 +0100 +++ new/ignition-2.1.1/internal/providers/aliyun/aliyun.go 2019-12-13 23:06:16.000000000 +0100 @@ -36,9 +36,7 @@ ) func FetchConfig(f *resource.Fetcher) (types.Config, report.Report, error) { - data, err := f.FetchToBuffer(userdataUrl, resource.FetchOptions{ - Headers: resource.ConfigHeaders, - }) + data, err := f.FetchToBuffer(userdataUrl, resource.FetchOptions{}) if err != nil && err != resource.ErrNotFound { return types.Config{}, report.Report{}, err } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ignition-2.0.1+git20191112.a924dd7/internal/providers/aws/aws.go new/ignition-2.1.1/internal/providers/aws/aws.go --- old/ignition-2.0.1+git20191112.a924dd7/internal/providers/aws/aws.go 2019-11-12 17:46:21.000000000 +0100 +++ new/ignition-2.1.1/internal/providers/aws/aws.go 2019-12-13 23:06:16.000000000 +0100 @@ -41,9 +41,7 @@ ) func FetchConfig(f *resource.Fetcher) (types.Config, report.Report, error) { - data, err := f.FetchToBuffer(userdataUrl, resource.FetchOptions{ - Headers: resource.ConfigHeaders, - }) + data, err := f.FetchToBuffer(userdataUrl, resource.FetchOptions{}) if err != nil && err != resource.ErrNotFound { return types.Config{}, report.Report{}, err } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ignition-2.0.1+git20191112.a924dd7/internal/providers/cloudstack/cloudstack.go new/ignition-2.1.1/internal/providers/cloudstack/cloudstack.go --- old/ignition-2.0.1+git20191112.a924dd7/internal/providers/cloudstack/cloudstack.go 2019-11-12 17:46:21.000000000 +0100 +++ new/ignition-2.1.1/internal/providers/cloudstack/cloudstack.go 2019-12-13 23:06:16.000000000 +0100 @@ -204,8 +204,6 @@ Path: "/latest/user-data", } - res, err := f.FetchToBuffer(metadataServiceUrl, resource.FetchOptions{ - Headers: resource.ConfigHeaders, - }) + res, err := f.FetchToBuffer(metadataServiceUrl, resource.FetchOptions{}) return res, err } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ignition-2.0.1+git20191112.a924dd7/internal/providers/cmdline/cmdline.go new/ignition-2.1.1/internal/providers/cmdline/cmdline.go --- old/ignition-2.0.1+git20191112.a924dd7/internal/providers/cmdline/cmdline.go 2019-11-12 17:46:21.000000000 +0100 +++ new/ignition-2.1.1/internal/providers/cmdline/cmdline.go 2019-12-13 23:06:16.000000000 +0100 @@ -46,9 +46,7 @@ return types.Config{}, report.Report{}, providers.ErrNoProvider } - data, err := f.FetchToBuffer(*url, resource.FetchOptions{ - Headers: resource.ConfigHeaders, - }) + data, err := f.FetchToBuffer(*url, resource.FetchOptions{}) if err != nil { return types.Config{}, report.Report{}, err } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ignition-2.0.1+git20191112.a924dd7/internal/providers/digitalocean/digitalocean.go new/ignition-2.1.1/internal/providers/digitalocean/digitalocean.go --- old/ignition-2.0.1+git20191112.a924dd7/internal/providers/digitalocean/digitalocean.go 2019-11-12 17:46:21.000000000 +0100 +++ new/ignition-2.1.1/internal/providers/digitalocean/digitalocean.go 2019-12-13 23:06:16.000000000 +0100 @@ -36,9 +36,7 @@ ) func FetchConfig(f *resource.Fetcher) (types.Config, report.Report, error) { - data, err := f.FetchToBuffer(userdataUrl, resource.FetchOptions{ - Headers: resource.ConfigHeaders, - }) + data, err := f.FetchToBuffer(userdataUrl, resource.FetchOptions{}) if err != nil { return types.Config{}, report.Report{}, err } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ignition-2.0.1+git20191112.a924dd7/internal/providers/gcp/gcp.go new/ignition-2.1.1/internal/providers/gcp/gcp.go --- old/ignition-2.0.1+git20191112.a924dd7/internal/providers/gcp/gcp.go 2019-11-12 17:46:21.000000000 +0100 +++ new/ignition-2.1.1/internal/providers/gcp/gcp.go 2019-12-13 23:06:16.000000000 +0100 @@ -18,6 +18,7 @@ package gcp import ( + "net/http" "net/url" "github.com/coreos/ignition/v2/config/v3_1_experimental/types" @@ -38,7 +39,7 @@ ) func FetchConfig(f *resource.Fetcher) (types.Config, report.Report, error) { - headers := resource.ConfigHeaders + headers := make(http.Header) headers.Set(metadataHeaderKey, metadataHeaderVal) data, err := f.FetchToBuffer(userdataUrl, resource.FetchOptions{ Headers: headers, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ignition-2.0.1+git20191112.a924dd7/internal/providers/openstack/openstack.go new/ignition-2.1.1/internal/providers/openstack/openstack.go --- old/ignition-2.0.1+git20191112.a924dd7/internal/providers/openstack/openstack.go 2019-11-12 17:46:21.000000000 +0100 +++ new/ignition-2.1.1/internal/providers/openstack/openstack.go 2019-12-13 23:06:16.000000000 +0100 @@ -131,8 +131,6 @@ } func fetchConfigFromMetadataService(f *resource.Fetcher) ([]byte, error) { - res, err := f.FetchToBuffer(metadataServiceUrl, resource.FetchOptions{ - Headers: resource.ConfigHeaders, - }) + res, err := f.FetchToBuffer(metadataServiceUrl, resource.FetchOptions{}) return res, err } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ignition-2.0.1+git20191112.a924dd7/internal/providers/packet/packet.go new/ignition-2.1.1/internal/providers/packet/packet.go --- old/ignition-2.0.1+git20191112.a924dd7/internal/providers/packet/packet.go 2019-11-12 17:46:21.000000000 +0100 +++ new/ignition-2.1.1/internal/providers/packet/packet.go 2019-12-13 23:06:16.000000000 +0100 @@ -55,7 +55,7 @@ func FetchConfig(f *resource.Fetcher) (types.Config, report.Report, error) { // Packet's metadata service returns "Not Acceptable" when queried // with the default Accept header. - headers := resource.ConfigHeaders + headers := make(http.Header) headers.Set("Accept", "*/*") data, err := f.FetchToBuffer(userdataUrl, resource.FetchOptions{ Headers: headers, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ignition-2.0.1+git20191112.a924dd7/internal/resource/url.go new/ignition-2.1.1/internal/resource/url.go --- old/ignition-2.0.1+git20191112.a924dd7/internal/resource/url.go 2019-11-12 17:46:21.000000000 +0100 +++ new/ignition-2.1.1/internal/resource/url.go 2019-12-13 23:06:16.000000000 +0100 @@ -52,7 +52,7 @@ // ConfigHeaders are the HTTP headers that should be used when the Ignition // config is being fetched - ConfigHeaders = http.Header{ + configHeaders = http.Header{ "Accept-Encoding": []string{"identity"}, "Accept": []string{"application/vnd.coreos.ignition+json;version=3.0.0, */*;q=0.1"}, } @@ -248,7 +248,21 @@ } } - dataReader, status, ctxCancel, err := f.client.getReaderWithHeader(u.String(), opts.Headers) + // TODO use .Clone() when we have a new enough golang + // (With Rust, we'd have immutability and wouldn't need to defensively clone) + headers := make(http.Header) + for k, va := range opts.Headers { + for _, v := range va { + headers.Add(k, v) + } + } + for k, va := range configHeaders { + for _, v := range va { + headers.Add(k, v) + } + } + + dataReader, status, ctxCancel, err := f.client.getReaderWithHeader(u.String(), headers) if ctxCancel != nil { // whatever context getReaderWithHeader created for the request should // be cancelled once we're done reading the response diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ignition-2.0.1+git20191112.a924dd7/tests/blackbox_test.go new/ignition-2.1.1/tests/blackbox_test.go --- old/ignition-2.0.1+git20191112.a924dd7/tests/blackbox_test.go 2019-11-12 17:46:21.000000000 +0100 +++ new/ignition-2.1.1/tests/blackbox_test.go 2019-12-13 23:06:16.000000000 +0100 @@ -332,7 +332,7 @@ return nil } if err := umountPartition(rootPartition); err != nil { - return nil + return err } if filesErr != nil { return nil // error is expected diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ignition-2.0.1+git20191112.a924dd7/tests/filesystem.go new/ignition-2.1.1/tests/filesystem.go --- old/ignition-2.0.1+git20191112.a924dd7/tests/filesystem.go 2019-11-12 17:46:21.000000000 +0100 +++ new/ignition-2.1.1/tests/filesystem.go 2019-12-13 23:06:16.000000000 +0100 @@ -49,11 +49,14 @@ return out, nil } -func prepareRootPartitionForPasswd(ctx context.Context, root *types.Partition) error { - if err := mountPartition(ctx, root); err != nil { +func prepareRootPartitionForPasswd(ctx context.Context, root *types.Partition) (err error) { + err = mountPartition(ctx, root) + if err != nil { return err } - defer umountPartition(root) + defer func() { + err = umountPartition(root) + }() mountPath := root.MountPath dirs := []string{ @@ -64,26 +67,25 @@ filepath.Join(mountPath, "etc"), } for _, dir := range dirs { - err := os.MkdirAll(dir, 0755) + err = os.MkdirAll(dir, 0755) if err != nil { - return err + return } } symlinks := []string{"lib64", "bin", "sbin"} for _, symlink := range symlinks { - err := os.Symlink( + err = os.Symlink( filepath.Join(mountPath, "usr", symlink), filepath.Join(mountPath, symlink)) if err != nil { - return err + return } } - // TODO: use the architecture, not hardcode amd64 // TODO: needed for user_group_lookup.c - _, err := run(ctx, "cp", "/lib64/libnss_files.so.2", filepath.Join(mountPath, "usr", "lib64")) - return err + _, err = run(ctx, "cp", "/lib64/libnss_files.so.2", filepath.Join(mountPath, "usr", "lib64")) + return } func getRootPartition(partitions []*types.Partition) *types.Partition { @@ -116,11 +118,8 @@ if !ok { return -1, logs, err } - status, ok2 := exitErr.Sys().(unix.WaitStatus) - if !ok2 { - return -1, logs, err - } - return status.ExitStatus(), logs, nil + status := exitErr.ExitCode() + return status, logs, nil } func umountPartition(p *types.Partition) error { @@ -132,24 +131,17 @@ // specific case. See https://github.com/coreos/bootengine/commit/8bf46fe78ec59bcd5148ce9ab8ec5fb805600151 // for more context. for i := 0; i < 3; i++ { - status, logs, err := runGetExit("umount", p.MountPath) - if status == 0 { - return nil - } - if err != nil { - return fmt.Errorf("exec'ing `umount %s` failed: %v", p.MountPath, err) - } - if status != 32 { - return fmt.Errorf("`umount %s` failed with exit status %d: %s", p.MountPath, status, logs) + if err := unix.Unmount(p.MountPath, unix.MNT_FORCE); err != nil { + time.Sleep(time.Second) + continue } - // wait a sec to see if things clear up - time.Sleep(time.Second) - if unmounted, _, err := runGetExit("mountpoint", "-q", p.MountPath); err != nil { return fmt.Errorf("exec'ing `mountpoint -q %s` failed: %v", p.MountPath, err) } else if unmounted == 1 { return nil } + // wait a sec to see if things clear up + time.Sleep(time.Second) } return fmt.Errorf("umount failed after 3 tries (exit status 32) for %s", p.MountPath) } @@ -399,26 +391,33 @@ return r } +func createFilesForPartition(ctx context.Context, partition *types.Partition) (err error) { + err = mountPartition(ctx, partition) + if err != nil { + return + } + defer func() { + err = umountPartition(partition) + }() + + err = createDirectoriesFromSlice(partition.MountPath, partition.Directories) + if err != nil { + return + } + err = createFilesFromSlice(partition.MountPath, partition.Files) + if err != nil { + return + } + err = createLinksFromSlice(partition.MountPath, partition.Links) + return +} + func createFilesForPartitions(ctx context.Context, partitions []*types.Partition) error { for _, partition := range partitions { if partition.FilesystemType == "swap" || partition.FilesystemType == "" || partition.FilesystemType == "blank" { continue } - if err := mountPartition(ctx, partition); err != nil { - return err - } - defer umountPartition(partition) - - err := createDirectoriesFromSlice(partition.MountPath, partition.Directories) - if err != nil { - return err - } - createFilesFromSlice(partition.MountPath, partition.Files) - if err != nil { - return err - } - createLinksFromSlice(partition.MountPath, partition.Links) - if err != nil { + if err := createFilesForPartition(ctx, partition); err != nil { return err } } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ignition-2.0.1+git20191112.a924dd7/tests/validator.go new/ignition-2.1.1/tests/validator.go --- old/ignition-2.0.1+git20191112.a924dd7/tests/validator.go 2019-11-12 17:46:21.000000000 +0100 +++ new/ignition-2.1.1/tests/validator.go 2019-12-13 23:06:16.000000000 +0100 @@ -183,7 +183,7 @@ defer func() { if err := umountPartition(partition); err != nil { // failing to unmount is not a validation failure - t.Logf("Failed to unmount %s: %v", partition.MountPath, err) + t.Fatalf("Failed to unmount %s: %v", partition.MountPath, err) } }() for _, file := range partition.Files {
