Commit graph

112 commits

Author SHA1 Message Date
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
Christian Kellner
c084d51e3e stages/error: fix json for stage options
Expecting ',' delimiter: line 5 column 5 (char 88) caused by a
missing ','.
2020-01-28 13:10:14 +01:00
Christian Kellner
b261cb071e stages/dnf: fix json for stage options
Expecting property name enclosed in double quotes: line 81
column 1 (char 2272), caused by a trailing ",".
2020-01-28 13:10:14 +01:00
Christian Kellner
abfc4d6b5a stages/fix-bls: support for different prefix
The paths for the kernel and the initrd in the BLS snippets are
meant to be relative to the root of the filesystem they are on.
The current code assumes that kernel and initrd are installed
under '/boot' and that '/boot' is on the root file system and
thus all paths get fixed up to start with '/boot/…'. But the
'/boot' directory can be on a separate partition and thus file
system, and then paths need to be relative to that and should
be fixed up with '/…'. Introduce a new option 'prefix' that
can be used to manually specify the prefix after the fixup,
defaulting to '/boot' for backwards compatibility.
NB: The canonical Boot Loader Specification[1] requires that
a separate partition is used boot related files and it will
be mounted at '/boot' (or '/efi').

[1] https://systemd.io/BOOT_LOADER_SPECIFICATION/
2020-01-19 23:01:36 +01:00
Christian Kellner
b3ae34b07e stages/grub2: make $boot just an alias for $root
Up until now the grub config theoretically supported having different
values for 'root' (via grubenv's $GRUB2_ROOT_FS_UUID) and 'boot' (via
grubenv's $GRUB2_BOOT_FS_UUID). 'boot' is a leftover from the initial
implementation when grub was looking for BLS snippets also in the ESP,
and will be removed in the future.
In our and also the canonical Fedora/RHEL grub configurations the BLS
are in the very same location for UEFI and legacy and thus 'boot' is
always 'root'.
Therefore we get rid of the extra grubenv variable refering to 'boot'
and just set 'boot' to 'root' after that was discovered.
2020-01-19 23:01:36 +01:00
Christian Kellner
92e2961090 stages/grub2: support for separate boot partition
Add a new `boot_fs_uuid` option for when a separate partition is
being used for '/boot' with the indicated uuid. This will then be
used for the grub2 "root" and "boot" variables. Additionally, in
the redirect config, need to refer to files and directories paths
relative to the partition they are contained, i.e. /boot/grub2 if
/boot is on root ('/') or /grub2 if /boot is on a extra dedicated
partition.
2020-01-19 23:01:36 +01:00
Christian Kellner
7a8c76cb1c stages/kopts: new stage to set the kernel cmdline
The canonical way to set the kernel commandline, which is used by
the kernel post install scripts, see kernel-install(8), is the
file /etc/kernel/cmdline, or in the case this does not exist,
/proc/cmdline. The new stages offers a way to write this file in a
more "type-safe" way, by providing explicit options for certain
well known params (for now only `root_fs_uuid`). Additional params
are specified via `kernel_opts`. This follows the grub2 stage name
convention.
2020-01-13 20:05:10 +01:00
Christian Kellner
fc9108e16e stage/zipl: new stage to configure zipl
Write a default config for the z Initial Program Loader to work
correctly. Parameters taken from anaconda[1].

[1] 2e793483b4/pyanaconda/bootloader/zipl.py (L132)
2020-01-13 20:05:10 +01:00
Christian Kellner
4804632268 stages/grub2: support for hybrid booting
In the case that the image should support booting via EFI *and*
legacy grub, i.e. hybrid booting, the canonical grub config is
stored in /boot/grub2 just as for normal legacy booting. The
config file for efi grub is a very small one that will just look
for the partition containing the /boot/grub2/grub.cfg file and use
then set the prefix accordingly and load that file. In the hybrid
case grubenv file also will just be located in /boot/grub2 and
not in the ESP therefore the symlink that was created by the
package needs to be removed.
2020-01-10 18:25:35 +01:00
Christian Kellner
42a4176a9f stages/grub2: fix auto bool to platform conversion
In the case that legacy is of type bool it is automatically converted
to the platform string ("i386-legacy"). This is mainly done to keep
backwards comparability as it was just a boolean before. But the auto
conversion did not take the actual *value* of the boolean into account
meaning "legacy: False" would be turned into "legacy: i386-pc" and
thus effectively changing the value from False to True.
2020-01-10 15:48:27 +01:00
Lars Karlitski
3a73d589d2 stages/dnf: don't ignore unavailable repositories
dnf skips unavailable repositories by default, which only leads to
harder understand errors later. Configure it to fail when any of the
passed repositories cannot be reached.
2020-01-09 23:55:43 +01:00
Lars Karlitski
84a948bcd7 stages/dnf: don't print repository data
It may contain secrets now, which we don't want in the logs.
2020-01-09 23:55:43 +01:00
Lars Karlitski
59ffebaff0 stages,sources/dnf: allow passing certificate data
Add support for dnf's sslcacert, sslclientcert, and sslclientkey
options. The latter two are passed as secrets (clientcert as well
because it might be a pem file that also includes the private key).

Sources run on the host, so their options may contain paths to the host
file system. Make use of that by accepting only paths in those options,
because it allows using tools to deal with certificate files.

Also make sure that the dnf source only returns options it knows about.
2020-01-09 23:55:43 +01:00
Christian Kellner
828b568734 stage/grub2: support for non i386-pc platforms
Change the `legacy` option (keeping compatibility) from a boolean
to a string, specifying the target platform the legacy modules
are being installed for.
2019-12-24 15:42:24 +01:00
Lars Karlitski
08b7a1a6b5 stages/dnf: don't show repo id in checksum error
repoid was based on index in the "repos" array from options. Now that we
fetch repository configuration from a source, the order might be
different.
2019-12-23 01:12:38 +01:00
Lars Karlitski
510e2b1e94 osbuild: introduce sources
Pipelines encode which source content they need in the form of
repository metadata checksums (or rpm checksums). In addition, they
encode where they fetch that source content from in the form of URLs.
This is overly specific and doesn't have to be in the pipeline's hash:
the checksum is enough to specify an image.

In practice, this precluded using alternative ways of getting at source
packages, such as local mirrors, which could speed up development.

Introduce a new osbuild API: sources. With it, a stage can query for a
way to fetch source content based on checksums.

The first such source is `org.osbuild.dnf`, which returns repository
configuration for a metadata checksum. Note that the dnf stage continues
to verify that the content it received matches the checksum it expects.

Sources are implemented as programs, living in a `sources` directory.
They are run on the host (i.e., uncontained) right now. Each source gets
passed options, which are taken from a new command line argument to
osbuild, and an array of checksums for which to return content.

This API is only available to stages right now.
2019-12-23 01:12:38 +01:00
Tom Gundersen
1f7766596f stages/dnf: log more clearly in case the repo metadata changes
This is an expected error case, so we should not assert, but log
and return failure. In the future we should probably also return
the error as structured data.

Signed-off-by: Tom Gundersen <teg@jklm.no>
2019-12-18 22:18:33 +01:00
Christian Kellner
6333d1c3bd stage/error: new simple stage always erroring out
A simple stage like 'noop' that will return with `returncode` or
255 if nothing is specified. Like 'noop' it might be useful for
testing, debugging, and wasting time.
2019-12-18 12:45:59 +01:00
Christian Kellner
ce5f3c4331 stages/grub: add uefi support (optional)
Introduce two new configuration options: `legacy` and `uefi`. The
first one being a boolean (default: True) that controls if GRUB
modules, fonts and the configuration is installed in the right
locations to support legacy boot mode.
The `uefi` option (of type object with a single `vendor` property)
enables UEFI support by writing the configuration into the correct
EFI directory, "/boot/efi/EFI/<vendor>/grub.cfg", where vendor is
taken from said `vendor` property.
2019-12-12 17:25:47 +01:00
Christian Kellner
1f5853a27f stages/grub: extract code to write grub config
Part of refactoring the grub2 stage to be able the handle UEFI. No
semantic change.
2019-12-12 17:25:47 +01:00
Christian Kellner
b6da6418c1 stages/grub: extract font copying code
Part of refactoring the grub2 stage to be able the handle UEFI. No
semantic change.
2019-12-12 17:25:47 +01:00
Christian Kellner
1c0f00e37c stages/grub: extract module copying code
Part of refactoring the grub2 module so it can also handle UEFI.
No semantic change.
2019-12-12 17:25:47 +01:00
Lars Karlitski
f64a6aaa64 stages/dnf: disable all dnf plugins
osbuild does not use any plugins. When running without a build root, dnf
shouldn't pull in anything from the host.
2019-11-29 00:45:14 +01:00
Lars Karlitski
d2dbdcf5ef stages/systemd: add default_target
Allow setting the default to boot into.
2019-11-27 22:22:13 +01:00
Lars Karlitski
aece548644 stages/dnf: add module_platform_id option
This is needed by modular repositories.
2019-11-18 19:11:21 +01:00
Will Woods
6164b38fb9 Add STAGE_DESC, STAGE_INFO, and STAGE_OPTS to stages
This commit adds semi-structured documentation to all osbuild stages and
assemblers. The variables added work like this:

* STAGE_DESC: Short description of the stage.
* STAGE_INFO: Longer documentation of the stage, including expected
              behavior, required binaries, etc.
* STAGE_OPTS: A JSON Schema describing the stage's expected/allowed
              options. (see https://json-schema.org/ for details)

It also has a little unittest to check stageinfo - specifically:

1. All (executable) stages in stages/* and assemblers/ must define strings named
   STAGE_DESC, STAGE_INFO, and STAGE_OPTS
2. The contents of STAGE_OPTS must be valid JSON (if you put '{' '}'
   around it)
3. STAGE_OPTS, if non-empty, should have a "properties" object
4. if STAGE_OPTS lists "required" properties, those need to be present
   in the "properties" object.

The test is *not* included in .travis.yml because I'm not sure we want
to fail the build for this, but it's still helpful as a lint-style
check.
2019-11-13 21:47:03 +01:00
Will Woods
9d4b526a25 org.osbuild.rpm: make rpm --install check signatures
It turns out that rpm will happily check signatures on `--install`,
that's just not the default behavior, because of Historical Reasons.

This commit enables RPM's signature checking and drops our manual check,
which will probably speed up the RPM stage a little bit. Fun!

Oh, also there's two bonus code cleanups: one to use f-strings harder,
and one to make sure we ignore whitespace in package checksum strings.
2019-11-13 21:11:39 +01:00
Will Woods
d6ce127a8e org.osbuild.rpm: safe tempfiles
This makes the org.osbuild.rpm stage use safe temporary files for
handling keys and the package manifest.
2019-11-13 21:11:39 +01:00
Will Woods
8b8493cf04 org.osbuild.{dnf,yum}: use safe tempfiles
As a general rule, using temporary files with predictable names is a
security risk. It probably isn't _actually_ a security risk inside
osbuild stages, since they're usually running in some kind of isolated
container environment, but it's still a better idea to use tempfiles.

This makes the dnf and yum stages put their temporary files into a
temporary directory that gets deleted after dnf/yum finishes.
2019-11-13 21:11:39 +01:00
Ondřej Budai
ce6f77cbdc stages: add org.osbuild.chrony stage
lorax-composer supports modifying timeservers, this stage implements it.

I was concerned if I should name this stage timeservers or chrony, but
I've decided to go with chrony. If some day in future Fedora/RHEL
changes the ntp client, we can easily introduce new stage named after
the new ntp client. Additionally, this solution enables us to create
systemd-timesyncd stage, which can change timeservers when chrony is not
installed (in that case systemd-timesyncd takes over the ntp
synchronization).
2019-10-19 18:38:17 +02:00
Ondřej Budai
21d91fd6df stages: add org.osbuild.groups stage
lorax-composer supports adding groups, therefore we need it as well.
2019-10-17 16:14:20 +01:00
Ondřej Budai
d0a3f99342 stages/users: set authorized_keys file permissions to 600
Otherwise user may be unable to login. More information:
https://stackoverflow.com/questions/6377009/adding-public-key-to-ssh-authorized-keys-does-not-log-me-in-automatically
2019-10-17 16:11:01 +01:00
Lars Karlitski
26d29b646b stages/rpm: whitelist checksum algorithms 2019-10-15 22:53:53 +01:00
Lars Karlitski
2b872bbbfb stages: add org.osbuild.rpm
A new stage that downloads a list of packages and installs them using
`rpm`.
2019-10-15 00:00:13 +02:00
Tom Gundersen
21df63ba31 stages/dnf: embed the gpgkey in the pipeline
Downloading the gpg key is fragile and kept causing our tests to fail.
In general, we want to limit the network access, so let's just embed
the gpg keys directly in the pipeline.

Fixes #133.

Signed-off-by: Tom Gundersen <teg@jklm.no>
2019-10-12 14:59:01 +02:00
Lars Karlitski
3e57f13380 stages/dnf: exclude-packages → exclude_packages 2019-10-03 12:53:01 +02:00
Tom Gundersen
72c3157162 assemblers/qemu: replace grub2-install
Background:

grub2 works in three stages:
 - The first stage is found in the first 440 bytes of the master
   boot record, and its only purpose is to load and execute the
   second stage. This stage is static, and just copied from the rpm
   without modification.
 - The second stage is found in the gap between the MBR and the
   first partition, and may be up to 31kB in size. This stage is
   specific to the host and must contain the instructions for
   finding the right file system and subdirectory for the grub2
   config and modules on the host, as well as the modules needed
   to do this.
 - The third stage is found in the `normal` module, which loads
   grub2.conf, which in turn may load more modules and perform
   arbitrary instructions.

Problem:

grub2-install is responsible for installing all these stages on the
target image. This goes against our design, as modifications outside
the filesystem should happen in the assembler, but modifications to
the filesystem should happen in a stage. In particular, we don't
want the contents of the image to differ in any way from the output
tree that is stored in our content store (the output of our last
stage). This causes a practical problem at the moment, as our
selinux stage is ran before the assembler, and as such the grub
modules do not get selinux labels applied.

It turns out that we could split grub2-install in two as we want,
by passing `--no-bootsector` to it to install only the modules,
and copy/genereta the two first stages as files under /boot and
then run `grub2-bios-setup` to write the stages from /boot into
the image where they belong.

Regrettably, this does not work as both `grub2-install` and
`grub2-bios-setup` introspect the system and block devices they
are being run on to generate the right configuration. This is not
what we want, as we would like to specifcy the config explicitly
and run them independently of the target image. The specific bug
we get in both cases is that the canonical path containing our
object store cannot be found.

Before osbuild this was not a problem, as other installers would
instal and assemble everything directly in the target image as a
loopback device. Something we explicitly do not want to do.

Solution:

This patch essentially reimplements grub2-install, or rather the
parts of it that we need. One change in behavior from the upstream
tool is that we no longer write the level one and level two boot
loaders to /boot before moving them into place, but just write them
directly where they belong (so they do not end up on the
filesystem).

The parts that copy files into /boot are now in the grub2 installer
and the parts that write the level one/two bootloaders are in the
qemu assembler.

This achieves a few principles I think we should always adher to:
 - never run tools from the target image (no chroot)
 - don't read/copy files from the target image that was written
   by other stages. We already try to avoid sharing state, and
   by treating the image as write-only, we avoid accidentally
   sharing state through the target tree.

Based-on-suggestions-from: Javier Martinez Canillas <javierm@redhat.com>
With-god-like-debugging-and-fixes-by: Lars Karlitski <lubreni@redhat.com>
Signed-off-by: Tom Gundersen <teg@jklm.no>
2019-10-02 15:10:37 +02:00
Tom Gundersen
8f9dd5ec7d stages/dnf: support --exclude
This allows given packages to be excluded from the transaction. This
is useful if you want to install a group with certain exceptions.

A common thing to do in kicktstart files is:
```
rm -f /boot/*-rescue*
```

By instead excluding the dracut-rescue-config package we end up
with:
```
"deleted_files": [
  "/etc/kernel/postinst.d",
  "/usr/lib/dracut/dracut.conf.d/02-rescue.conf",
  "/usr/lib/kernel/install.d/51-dracut-rescue.install",
  "/boot/initramfs-0-rescue-ffffffffffffffffffffffffffffffff.img",
  "/boot/vmlinuz-0-rescue-ffffffffffffffffffffffffffffffff"
],
```

Signed-off-by: Tom Gundersen <teg@jklm.no>
2019-10-02 13:34:14 +02:00
Tom Gundersen
6ed426773f stages/yum: don't name the repositories
See 840bfd580c.

Signed-off-by: Tom Gundersen <teg@jklm.no>
2019-09-30 23:48:23 +02:00
Tom Gundersen
840bfd580c stages/dnf: don't name the repositories
The names carry no information, and do not affect the produced image.
Generate them instead.

Signed-off-by: Tom Gundersen <teg@jklm.no>
2019-09-29 19:04:39 +02:00
Ondřej Budai
3a0a480792 stages: Drop the remove-uniqueness stage
We don't need this stage anymore, random-seed has never been in
the tree and machine-id is now handled by dnf stage.
2019-09-26 23:47:33 +02:00
Ondřej Budai
fd8eb9492f stages/dnf: Remove random seed after dnf run
Some dnf packages introduce random seed file. If we leave in the tree
it would mean all systems running from the created image would use
the same random seed. This can be potentially dangerous, therefore we
just remove the generated random seed from our images.
2019-09-26 23:47:33 +02:00
Ondřej Budai
cc73fa5d10 stages/dnf: Improve dnf stage reproducibility
Normally, machine ID is generated randomly when dnf installs @Core
group. Unfortunately this isn't helping us with reproducibility
of images.

This commit introduces the concept of fake machine ID. Before dnf
command is run in dnf stage, we set the machine ID to a fake one.
This ensures all the scriptlets requiring machine ID have predictable
outputs.

For example GRUB uses machine-id in file names inside
/boot/loader/entries. With fixed machine ID the file names are always
the same and totally predictable.
2019-09-26 23:47:33 +02:00
Lars Karlitski
3009b9255f stages: remove org.osbuild.anaconda
We haven't used or tested it.
2019-09-24 20:17:04 +02:00
Lars Karlitski
bbe4129f36 stages/dnf: remove dnf cache directory
The repository used to install the image might be unrelated to any
repository that's ever used on a system running that image.
2019-09-24 20:17:04 +02:00
Lars Karlitski
57c82a00d0 stages/dnf: verify repository checksum
Require "checksum" option for each repository, which contains the
checksum of the `repodata/repomd.xml` file. This file (indirectly)
contains checksums for all packages.

Verify that the metadata dnf downloaded to install packages matches that
checksum. This way, this stage will give an error when a reposiory
changed between putting together the pipeline and running it.
2019-09-24 20:17:04 +02:00
Lars Karlitski
e23b5a32a2 stages/yum: only write known options to repo file
This is similar to the previous commit for the dnf stage.

Don't pass through arbitrary options. This means that pipeline repo
objects don't have the same options as yum repo files anymore:

1. Hard code repo name to repo id. The name has no influence on the
resulting image and should thus not appear in a pipeline.

2. Set gpgcheck=1 when gpgkey is given. It defaults to false, which
means that all sample and test pipelines didn't verify packages. It
would have failed anyway, because the container doesn't have the key
referenced in /etc. Change all gpgkeys to refer to the key id and import
them manually.

3. Don't allow lists for baseurl and gpgkey. We can add that if we need
it at some point.

Also be less verbose.
2019-09-24 20:17:04 +02:00
Lars Karlitski
0dd939b658 stages/dnf: only write known options to repo file
Don't pass through arbitrary options. This means that pipeline repo
objects don't have the same options as dnf repo files anymore:

1. Hard code repo name to repo id. The name has no influence on the
resulting image and should thus not appear in a pipeline.

2. Set gpgcheck=1 when gpgkey is given. It defaults to false, which
means that all sample and test pipelines didn't verify packages. It
would have failed anyway, because the container doesn't have the key
referenced in /etc. Change all gpgkeys to refer to the key id and import
them manually.

3. Don't allow lists for baseurl and gpgkey. We can add that if we need
it at some point.
2019-09-24 20:17:04 +02:00
Lars Karlitski
93da5caa69 stages/dnf: add mandatory basearch argument
We've been effectively using the basearch of the host, making the stage
non-reproducible: if the same pipeline was run on machines with
different architectures, it would produce different results. However,
pipelines producing different outputs must be different. Thus, this
patch includes the basearch in the pipeline.

In principle, this allows cross-arch builds. dnf should be the only
stage running binaries from the target tree. This is not yet tested.
2019-09-24 20:17:04 +02:00