Commit graph

160 commits

Author SHA1 Message Date
Tom Gundersen
72e1e7217b stages/rpm: allow check_gpg to be omitted
This brings it in line with the JSON schema. Also fix a bug where the
wrong root was used for checking the signatures.

Signed-off-by: Tom Gundersen <teg@jklm.no>
2020-06-10 15:10:27 +02:00
Lars Karlitski
d7d1d9dbbf Take care not to put large content on /tmp
Most stages and assemblers already use `/var/tmp` to store large
files or trees. Do this in the qemu assembler and copy stage as well.
2020-06-09 09:12:05 +02:00
Christian Kellner
7897e662fd stages/rpm-ostree: add initramfs-args
Add a new stage option `initramfs-args`, that will in turn be set
on the Treefile that is then passed to rpm-ostree. This will pass
the options on to dracut when building the initramfs module. NB:
the `ostree` dracut module will also always be automatically
included by rpm-ostree.
2020-06-04 10:25:39 +02:00
Christian Kellner
c91333aea8 stages: add org.osbuild.ignition stage
This stage will create a file '/boot/ignition.firstboot' that will,
with the help of support in grub, trigger ignition on the first
boot. The `network` option can be used to overwrite the default
network configuration set in grub2.
2020-06-04 10:25:39 +02:00
Christian Kellner
be6358d73f stages/grub2: support for ignition
Add support for ignition[1] via a new `ignition` stage option. If
enabled, a new section is added to the main grub.cfg that will
create a 'ignition_firstboot' variable meant to be included in the
kernel command line configuration.
The grub.cfg snippet was taken from 'src/grub.cfg' of Fedora CoreOS
Assembler[2] at ec05cde20d3449fab8e4c76493ffa1ebd9b0b626 but with
PR #1373 applied to not hard-code the dhcp options.

[1] https://github.com/coreos/ignition
[2] https://github.com/coreos/coreos-assembler/
2020-06-04 10:25:39 +02:00
Christian Kellner
3f14ace5c1 stages/users: support new users w/ duplicate uids
When adding a new user with a uid that already exists, the "-o",
option needs to be added. Always do so when the uid is specified.
A use case for this is e.g. an installer iso with a `install`
user that has 0 (like root) for its uid (and gid).
2020-06-03 17:09:55 +02:00
Christian Kellner
04d3c0fc17 stages/users: fix conditionals for zero & "" strs
Support setting uids, gids with values of `0` as well as passwords
and descriptions with the empty string, by explicitly checking the
value of each against `None`, because simple `if` conditionals are
false for those.
2020-06-03 17:09:55 +02:00
Tom Gundersen
fca588d4b5 stages/rpm: make GPG signature verification opt-in
The content hash of each RPM is already verified, so verifying
signatures again is not necessary if the manifest generation is trusted,
and verifying signatures does not help if the manifest generation is
not.

Let us follow what DNF does and default to not verify signatures, but in
order to preserve features already in use we still allow opting in to
verifying signatures as before on a per RPM basis.

This will make it possible to install unsigned RPMs, or a mixed of
signed and unsigned RPMs.

Signed-off-by: Tom Gundersen <teg@jklm.no>
2020-06-03 16:36:00 +02:00
Christian Kellner
f19effd70a stages/grub2: redirect cfg as template
Extract the grub.cfg redirect config as GRUB_REDIRECT_TEMPLATE,
meant to be used via python's string.Template class. Document
its intended use and also the template options.
2020-05-29 11:52:32 +02:00
Christian Kellner
06b2c8c99b stages/grub2: main configuration as a template
Extract the strings for grub.cfg into a GRUB_CFG_TEMPLATE multi-line
string and turn it into a template meant to be used via python's
string.Template class. Document it, especially the template options.
2020-05-29 11:52:32 +02:00
Christian Kellner
ff215aa77c stages/grub2: unify grub.cfg writing path
Instead of having two different places within conditionals where
the configuration is written, have only one common. In the case
of hybrid boot, in the end there will be two grub configuration
files: the canonical one in /boot/grub2/grub.cfg and a redirect
one, in the EFI directory that will redirect to the canonical.
In case of legacy only, only the canonical one in the default
location (/boot/grub2/grub.cfg) will be written.
For EFI only mode, only /boot/efi/EFI/<vendor>/grub.cfg will
be written with the main grub configuration data.
Thus the writing of the main grub configuration will now always
be written in exactly one place (now line 319).
2020-05-29 11:52:32 +02:00
Christian Kellner
823b8999a7 stages/grub2: write redirect cfg via the object
Move the write_grub_cfg_redirect to the new GrubConfig object as
write_redirect. Add a `separate_boot` property to be used by the
new write_redirect. Remove the corresponding variable and also
the `grub_fs` variable since that is now all handled by the
GrubConfig object.
2020-05-29 11:52:32 +02:00
Christian Kellner
5828729217 stages/grub2: turn write_grub_cfg into an object
The reason behind this is to combine all the necessary state in the
object instead of passing it all to the write_grub_cfg function.
The idea is that as more things will get configurable, say the
timeout or ignition support, more things need to be passed to it
and thus it is better to an object where these config options can
be set and then combined when writing the config.
2020-05-29 11:52:32 +02:00
Christian Kellner
dfd044a512 stages/ostree: 'rootfs' option is not required
Fedora CoreOS[1] uses a dracut module[2] together with a systemd
generator[3] to mount the file system, including the root one.
Thus neither '/etc/fstab' nor a `root=` kernel command line
option is needed. Support that use case by making the 'rootfs'
option optional.

[1] https://github.com/coreos/fedora-coreos-config/tree/testing-devel/
[2] overlay.d/05core/usr/lib/dracut/modules.d/40ignition-ostree
[3] overlay.d/05core/usr/lib/systemd/system-generators/coreos-boot-mount-generator
2020-05-29 11:51:57 +02:00
Christian Kellner
58db898790 stages/ostree: option to pre-populate /var
The ignition-dracut module for Fedora CoreOS and anaconda both have
code to populate '/var' via systemd-tmpfiles. In images that where
said dracut module is not used, but '/var' needs to be populate, it
can no be done by setting the `populate_var` option.
2020-05-29 11:51:57 +02:00
David Rheinsberg
faaa6c1a6b modules: fix format-strings without interpolation
Fix all occurrences of format-strings without any interpolation. pylint
warns about those (and for some reason did not do so for our modules).
A followup will fix the pylint tests, so make sure all the warnings are
resolved.
2020-05-29 11:07:44 +02:00
Christian Kellner
2a9cdde5ec osbuild: refactor stage information
For all currently supported modules, i.e. stages and assemblers,
convert the STAGE_DESC and STAGE_INFO into a proper doc-string.
Rename the STAGE_OPTS into SCHEMA.
Refactor meta.ModuleInfo loading accordingly.

The script to be used for the conversion is:

  --- 8< --- 8< --- 8< --- 8< --- 8< --- 8< --- 8< --- 8< ---

import os
import sys

import osbuild
import osbuild.meta

from osbuild.meta import ModuleInfo

def find_line(lines, start):
    for i, l in enumerate(lines):
        if l.startswith(start):
            return i
    return None

def del_block(lines, prefix):
    start = find_line(lines, prefix)
    end = find_line(lines[start:], '"""')
    print(start, end)
    del lines[start:start+end+1]

def main():
    index = osbuild.meta.Index(os.curdir)

    modules = []
    for klass in ("Stage", "Assembler"):
        mods = index.list_modules_for_class(klass)
        modules += [(klass, module) for module in mods]

    for m in modules:
        print(m)
        klass, name = m
        info = ModuleInfo.load(os.curdir, klass, name)

        module_path = ModuleInfo.module_class_to_directory(klass)
        path = os.path.join(os.curdir, module_path, name)
        with open(path, "r") as f:
            data = list(f.readlines())

            i = find_line(data, "STAGE_DESC")
            print(i)
            del data[i]

            del_block(data, "STAGE_INFO")

            i = find_line(data, "STAGE_OPTS")
            data[i] = 'SCHEMA = """\n'

        docstr = '"""\n' + info.desc + "\n" + info.info + '"""\n'
        doclst = docstr.split("\n")
        doclst = [l + "\n" for l in doclst]
        data = [data[0]] + doclst + data[1:]

        with open(path, "w") as f:
            f.writelines(data)

if __name__ == "__main__":
    main()
2020-05-29 08:37:47 +02:00
Christian Kellner
07c5fdb650 stages: add org.osbuild.copy stage
Copies files obtained via a `source` to the tree. Multiple files or
directories can be copied by specifying multiple entries in `paths`.
If no paths are specified the whole contents of `source` is copied.
The source and the target path for each individual path entry might
optionally be specified via `from` and `to`, respectively; if no
path is given for any of the two, the root `/` is assumed.

Currently only an 'archive' 'source' is supported that in turn uses
the existing 'org.osbuild.files' source to fetch an archive (tarball)
and extracts it to a temporary directory.
2020-05-28 10:42:18 +02:00
Christian Kellner
01ce01b1c7 stages & assemblers: don't allow additional props
Change all the schemata to not allow additional properties. This
should help with misspelled properties as well as missing schema
information in the stage itself.

Done via a small python3 script:

   --- 8< --- 8< --- 8< --- 8< --- 8< --- 8< --- 8< --- 8< ---

import os
import sys

def list_stages(base):
    return [(base, f) for f in os.listdir(base) if f.startswith("org.osbuild")]

stages = list_stages("stages")
stages += list_stages("assemblers")

def find_line(lines, start):
    for i, l in enumerate(lines):
        if l.startswith(start):
            return i
    return None

NOADD = '"additionalProperties": false'

for stage in stages:
    with open(f"{stage[0]}/{stage[1]}", "r") as f:
        print(f"{stage[0]}/{stage[1]}", file=sys.stderr)
        data = f.readlines()
        i = find_line(data, 'STAGE_OPTS = """')
        if i:
            data.insert(i+1, NOADD + ",\n")
        else:
            i = find_line(data, 'STAGE_OPTS = ""')
            if i:
                data[i] = f'STAGE_OPTS = """\n'
                data.insert(i+1, NOADD + "\n")
                data.insert(i+2, '"""\n')

    with open(f"{stage[0]}/{stage[1]}", "w") as f:
        f.writelines(data)
2020-05-06 15:42:23 +02:00
Christian Kellner
702e7c3423 stages/chrony: fix schema to require 'timeservers'
It is required in the code, that should be reflected in the schema
as well.
2020-05-06 15:42:23 +02:00
Christian Kellner
2383e08523 stages/zipl: add schema for timeout option
Add the missing schema information for the timeout option.
2020-05-06 15:42:23 +02:00
Christian Kellner
91a4810808 stages/ostree: add schema for osname, commit
Add missing schema information for the `osname` and `commit`
options.
2020-05-06 15:42:23 +02:00
Christian Kellner
685650cda2 stages/fix-bls: add missing schema for prefix
Add the missing schema information for the `prefix` option. While
there also add some description of it in the STAGE_INFO field.
2020-05-06 15:42:23 +02:00
Christian Kellner
2757125b60 stages/fstab: fix type 'freq', 'passno' defaults
They are integers according to the schema; make the default values
have the same type.
2020-05-06 15:42:23 +02:00
Christian Kellner
0c01a30657 stages/first-boot: fix schema
The correct type for the boolean wait_for_network option is
`boolean` not `bool`.
2020-05-06 15:42:23 +02:00
Christian Kellner
be68830c44 stages/ostree: fix schema for kernel_opts
The `kernel_opts` option is a array of strings not a single string.
2020-05-06 15:42:23 +02:00
Christian Kellner
6d2a3cbdea stages/rpm-ostree: fix stage schema
Remove the "enabled_services" requirement since that is not even
a valid option. Ops.
2020-05-06 15:42:23 +02:00
David Rheinsberg
4d2f15fb46 modules: drop osbuild symlink
Drop the `osbuild -> ../osbuild` symlink from all module directories.
We now properly initialize the PYTHONPATH to provide the imported
osbuild module from the host environment. Therefore, these links are no
longer needed.

The sources run from the host environment, so they should just pick them
up from the environment the same way osbuild itself does.
2020-05-04 12:32:25 +02:00
David Rheinsberg
2624be92dc osbuild: cleanup contextlib usage
Two cleanups for the context-managers we use:

  * Use `contextlib.AbstractContextManager` if possible. This class
    simply provides a default `__enter__` implementation which just
    returns `self`. So use it where applicable.
    Additionally, it provides an abstract `__exit__` method and thus
    allows static checks for an existance of `__exit__` in the dependent
    class. We might use that everywhere, but this is a separate
    decision, so not included here.

  * Explicitly return `None` from `__exit__`. The python docs state:

        If an exception is supplied, and the method wishes to suppress
        the exception (i.e., prevent it from being propagated), it
        should return a true value. Otherwise, the exception will be
        processed normally upon exit from this method.

    That is, unless we want exceptions to be suppressed, we should
    never return a `truthy` value. The python contextlib suggest using
    `None` as a default return value, so lets just do that.

    In particular, the explicit `return exc_type is None` that we use
    has no effect at all, since it only returns `True` if no exception
    was raised.

    This commit cleans this up and just follows what the `contextlib`
    module does and returns None everywhere (well, it returns nothing
    which apparently is the same as returning `None` in python). It is
    unlikely that we ever want to suppress any exceptions, anyway.
2020-04-21 16:02:20 +02:00
Christian Kellner
5224b4ad7f stages/grub2: ensure grubenv file has size of 1024
For historical and occult reasons the grubenv file is, according
to its documentation[1] a 'preallocated 1024-byte file'. The
unused space in the file needs to be filled with '#' as padding,
which tools will count as "free space"[2] and there must not be a
trailing new-line.
Fix our code to do as they say to make grub2-editenv work and in
turn greenboot.

[1] https://www.gnu.org/software/grub/manual/grub/html_node/Environment-block.html
[2] grub-core/lib/envblk.c#L105 (commit 0f102b9844f852d48501d231d32a17e1cc24062d)
2020-04-21 13:45:11 +02:00
Christian Kellner
9f9485e2a2 stages/fstab: support for using fs labels
Allow file systems to be identified via there label in addition to
their uuid; i.e. either `uuid` or `label` must be specified, which
results in either `UUID=<uuid>` or `LABEL=<label>` to end up in the
"fs_spec" field. See also fstab(5).
2020-04-20 18:59:27 +02:00
Christian Kellner
22d131a5d9 stages/grub2: support identifying fs via labels
In addition to support for identifying file-systems via their uuids,
they now can be identified via their label as well. Two new options
are introduce for this: `rootfs` and `bootfs` for the root and boot
file system. The latter is option in the case a separated partition
is used for /boot. Both options are an object that can either have
`uuid` or `label` set. The old uuid based options, `root_fs_uuid` &
`boot_fs_uuid` are still supported for now.
Additionally, remove the `GRUB2_ROOT_FS_UUID` option from the
grubenv file and directly write the root file system identifier into
the grub config file.
2020-04-20 18:59:27 +02:00
Christian Kellner
bc4fd565d6 stages/yum: drop stage
This stage has been replaced by the org.osbuild.rpm stage. The
latter does not need access to network due inside the container
due to its use of the osbuild sources API.
2020-04-16 11:08:25 +02:00
Tom Gundersen
c2243aee6a stage: add org.osbuild.first-boot
This stage runs a given command only on the first boot of the image,
useful for doing instantiation tasks that can only be done in the
target environment, or that should be done per-instance, rather
than per image.

Ideally we would use systemd's ConditionFirstBoot for this, but that
requires images to ship without an /etc/machine-id, and currently
we only support shipping images with an empty /etc/machine-id.
Changing this would mean dropping /etc/fstab in favor of mounting
the rootfs rw from the initrd. This is likely the right thing to
do regardless, but we would have to audit what other first-boot
services we would end up with pulling in in this case.

Instead we introduce our own flag file /etc/osbuild-first-boot,
and use ConditionPathExists.

Signed-off-by: Tom Gundersen <teg@jklm.no>
2020-04-15 16:07:34 +02:00
Christian Kellner
4d0e18eb24 stages/grub2: opt-out option for /etc/default/grub
The file `/etc/defaults/grub` sets the defaults that are used by
grub2-mkconfig to (re-)generate the grub config (grub.cfg). This
command is not run by any scripts but by the user directly. On
modern installations (without the grubby-deprecated package)
the kernel is configured via Bootloader Specification snippets
and thus the grub config should not need to be touched at all
under normal circumstances. In the new future the grub2-mkconfig
will be updated to not require GRUB_ENABLE_BLSCFG which should
make the existence `/etc/defaults/grub` even more superfluous.
Additionally, in the future, some images might not contain
the grub2 packages at all.
2020-04-15 15:39:45 +02:00
Christian Kellner
c15b3e6cf4 stages/grub2: option to install EFI data
Add support for copying EFI data from the build root. If
`uefi.install` is set to `true`, `BOOT` and `uefi.vendor`
directories will be copied from the build root. This is
useful for example on OSTree based systems where boot/efi/EFI
is not being populated by an RPM package; but it can be used
also on other systems where it is not desirable to deliver
the EFI data via packages.
2020-04-15 15:39:45 +02:00
Christian Kellner
fd5efd0d29 stages/ostree: fix SELinux labels after deployment
Deploying an OSTree commit leads to creation of hardlinks from
the repository to the check out. These will have the correct
SELinux labels, since the files in the repository will have the
correct SELinux labels[1]. But new files are generated in '/etc'
of the new deployment, due to the 3-way configuration merge[2].
Also a new kernel, initramfs and the corresponding Bootloader
Specification entries are created in the global '/boot' dir.

In theory, ostree will set the correct SELinux labels by loading
the SELinux config from the deployment and then setting the
correct security contexts via ostree_sepolicy_setfscreatecon().
But it does so conditionally on is_selinux_enabled(2)[3], which
in our container is FALSE Therefore we have to do the same dance
as ostree does, at least for now, and manually re-label the
affected paths.

[1] Assuming they had the correct label when the commit was made
[2] https://ostree.readthedocs.io/en/latest/manual/deployment/
[3] via ostree_sepolicy_setfscreatecon in ostree-sepolicy.c
    line 640 of commit 2c1658538f8fde5813e95e7408d65662a489be91
2020-04-15 15:39:45 +02:00
Christian Kellner
7be1fa7ac5 stages/ostree: option to specify remotes
Add the ability to specify one ore more remotes for the system
repository. The required options for a single remote are its
`name` and the `url`. Optionally one or more branch can be passed
via `branches`. GPG keys can be given via `gpgkeys`; if none are
specified, no gpg verification will be done.
2020-04-15 15:39:45 +02:00
Christian Kellner
4cfcd44480 stages/ostree: mode support for mount points
The list of mount points is changed from a list of strings to a
list of objects containing `path` and an optional `mode` value.
The latter can be used to set the mode of the mount point that
will be created in the file system tree. It defaults to 0755,
or 493 in decimal, because JSON does not support octal values.
2020-04-15 15:39:45 +02:00
Christian Kellner
4d9a99562e stages/ostree: uuid or label option for rootfs
Instead of taking a raw string for the root file system kernel
option, convert the `rootfs` option to an object that must have
either `uuid` or `label` set. This will then be translated into
the proper kernel command line argument.
2020-04-15 15:39:45 +02:00
Christian Kellner
3eea0668f9 stages/ostree: add ref support
Add a new optional `ref` option. When set, a references for the
commit that was pulled with the value of `ref` will be created
in the system ostree repo. It will also be used when deploying
the commit and thus will be set as the origin for it. This is
necessary for updates to work.
2020-04-15 15:39:45 +02:00
Christian Kellner
cc01eb3a0e stages: add org.osbuild.ostree stage
Initializes the filesystem via ostree and then pulls a specified
commit and deploys that. Options are `commit`, which is the id
of the commit to pull and deploy, `osname`, which is the name
to be used for the operating system root. The `roofs` together
with `kernel_opts` options are used to build the kernel command
line for the deployment.
Additionally, a `mounts` parameter can be supplied that indicates
file system boundaries. This is needed because ostree uses a hard
link farm, which must not link across said file system boundaries.
2020-04-15 15:39:45 +02:00
Christian Kellner
c05112ee94 stages: add new org.osbuild.rpm-ostree stage
A stage that uses rpm-ostree compose to do post-processing of the
tree. Among other things the main steps are:
  - moves /etc to /usr/etc
  - move /boot to /usr/lib/ostree-boot
  - potentially moving /var/lib/rpm
  - re-creates the initramfs (dracut)
  - stores the treefile at /usr/share/rpm-ostree/treefile.json
  - adds altfiles module to nss
  - Recompiles SELinux policy (semodule -nB)
  - Migrates /usr/etc/{passwd, group} to /usr/lib/
  - Postprocess SELinux policy
  - Convert /var to tmpfiles.d
  - Prepares symlinks
    - /usr/local -> /var/usrlocal
    - /var/lib/alternatives -> /usr/lib/alternatives
    - /var/lib/vagrant -> /usr/lib/vagrant
  - copies the rpmdb

Based on commit 1cf0d557ae8059e689b1fed670022727e9842288 (rpm-ostree)
2020-04-15 15:39:45 +02:00
Tom Gundersen
892342b978 stages/dnf: drop stage
This has now been entirely replaced by the rpm stage in all its
users. The dnf stage does not fit very nicely into the osbuild
module, in particular it requires direct network access, which
we would like to avoid.

Signed-off-by: Tom Gundersen <teg@jklm.no>
2020-04-15 15:29:52 +02:00
Tom Gundersen
9d79d5fcc3 stages/grub2: default to disabling legacy support
For the sake of backwards compatibility, legacy support was enabled
by default. Flip this around, so that leaving the parameter out
means disabling it.

This is more intuitive, and will pave the way for dropping support
for the value being a bool in the future.

`osbuild-composer` always passes the argumnet explicitly, though
still always as a boolean.

Signed-off-by: Tom Gundersen <teg@jklm.no>
2020-04-14 23:47:08 +02:00
Major Hayden
ddb652ecc7 dnf: enable fastestmirror by default
Enabling the fastestmirror plugin allows dnf to choose the fastest
(also usually the closest) mirror to the instance of osbuild. It
has no effect on builds that force the use of a specific server
or mirror.

Signed-off-by: Major Hayden <major@redhat.com>
2020-02-22 02:01:17 +01:00
Tom Gundersen
be663bd3d5 stages/rpm: be a bit more verbose to give some progress indication
The dnf stage was very verbose, probably overly so. Rather than
benig completely silent, let's print basic progress indication
so we have some idea what's going on.

Signed-off-by: Tom Gundersen <teg@jklm.no>
2020-02-06 19:01:12 +01:00
Tom Gundersen
1d588b8e86 stages/rpm: adapt to use the files source
Drop the rpm downloading and instead use the files source. This gives
us caching for free, and is the last missing step before we can
deprecate the dnf stage.

The main benefit of the rpm over the dnf stage is that we pin the package
versions rather than the repo metadata version. This will allow us to
support continuously changing repositories as individual packages are much
less likely to change than the repos iteself, and old packages are meant
to stay around for some time, unlike the repo metadata which is instantly
swapped out.

Depsolving is also slow on the first run, which we were always hitting as
the depsolving was always happening in a fresh container.

Based on a patch by Lars Karlitski.

Signed-off-by: Tom Gundersen <teg@jklm.no>
2020-02-06 19:01:12 +01:00
Christian Kellner
4f72036efa stages/grub2: fix schema entry for 'legacy'
The 'legacy' option can either be of type boolean (the old version
kept for compatibility reasons) or 'string'. Adjust the schema to
account for that.
2020-01-29 18:03:24 +01:00
Christian Kellner
a3b9b55682 stages/grub2: fix json for stage options
Extra data: line 49 column 1 (char 1350) caused by an extra "}" at
the end of the data.
2020-01-28 13:10:14 +01:00