Commit Graph

276 Commits

Author SHA1 Message Date
nikstur 1b27ddd753
Merge pull request #159 from nix-community/renovate/all
fix(deps): update rust crate clap to 4.2.4
2023-04-24 14:03:04 +02:00
Janne Heß c22352ca20
tool: Use mtime of the symlink rather than the target
When using the target, this will always result in a timestamp from 1970
because the symlink points to the store.
2023-04-24 11:57:34 +02:00
Janne Heß 979d25ee13
Revert "Merge pull request #139 from adtya/built_on_date"
This reverts commit d751d13b0a, reversing
changes made to 7c55847aaf.
2023-04-24 11:48:46 +02:00
renovate[bot] 2ecd951de1
fix(deps): update rust crate clap to 4.2.4 2023-04-24 01:08:24 +00:00
nikstur 8efc061e1d tool: add comment for log level of malformed gens
The message about malformed generatiosn should semantically be a
warning. However, since users might have hundres of old and thus
malformed generations and can do little about it, this should remain a
debug message. This way the user is not spammed with no-op warnings
while still enabling debugging.
2023-04-23 23:28:21 +02:00
nikstur 68d1928e3d Revert "tool: don't silently ignore generations"
This reverts commit 4f182704e0.
2023-04-23 23:27:32 +02:00
Julian Stecklina 4f182704e0 tool: don't silently ignore generations 2023-04-23 15:20:49 +02:00
Julian Stecklina be458e3385 tool: avoid creating unbootable system
lzbt currently happily nukes all boot entries, if it can't parse any
bootspecs. With the upcoming incompatible bootspec change, this might
be a problem that's worth avoiding. :)

I changed lzbt to fail hard in case, it can't generate any boot
items.
2023-04-23 15:17:32 +02:00
Julian Stecklina ddd22a8f67
Merge branch 'master' into icache 2023-04-21 18:34:33 +02:00
Alois Wohlschlager ae401e4b18
stub: implement icache coherence on i686 and AArch64
People reportedly want to compile the stub on i686 and AArch64
platforms for testing. Make compilation possible by providing proper
`make_instruction_cache_coherent` implementations on these platforms.
For x86 (just as x86_64), this is a no-op, because Intel made the
instruction cache coherent for compatibility with code that was written
before caches existed.
For AArch64, adapt the procedure from their manual to multiple
instructions.
2023-04-21 18:00:14 +02:00
renovate[bot] b25e1b77d2
chore(deps): update all dependencies 2023-04-17 01:25:18 +00:00
Alois Wohlschlager 81e25ee5c3
stub: clarify instruction cache coherence 2023-04-16 16:17:50 +02:00
Raito Bezarius 666b5e7169 stub: make it compatible with a stable Rust compiler (≥ 1.68.x) 2023-04-14 16:48:30 +02:00
renovate[bot] 51017c0f40
fix(deps): update all dependencies 2023-04-09 20:27:07 +00:00
Julian Stecklina 5d3fbf10a6
Merge pull request #142 from Myaats/master
tool: drop buggy condition for when to sign
2023-04-09 22:20:46 +02:00
Mats 223ab53d55 tool: drop buggy condition for when to sign 2023-03-30 23:53:24 +02:00
Adithya Nair 97874a2002
propagate error instead of unwrapping in tests 2023-03-22 11:19:12 +05:30
Adithya Nair 6a342a49a9
propagate error instead of unwrapping 2023-03-22 01:25:17 +05:30
Adithya Nair e033a2fcaf
replace mtime with birth time 2023-03-21 23:47:33 +05:30
renovate[bot] 9bbbae3168
fix(deps): update all dependencies 2023-03-20 09:25:02 +00:00
Julian Stecklina 5a03bb751d stub: update dependencies
Update nightly toolchain and UEFI dependencies. The latest crane
version comes with a bug where it fails to compile UEFI binaries.
2023-03-20 09:51:30 +01:00
renovate[bot] dfa6c3db1f
chore(deps): lock file maintenance 2023-03-20 01:57:17 +00:00
Julian Stecklina 7060389698 stub: add safety comment for PE parsing 2023-03-15 21:53:19 +01:00
Julian Stecklina 9c128e9ef6 stub: do not read loaded image again from ESP
... because this might not work, if we were not loaded from a file
system. It also removes the issue where we might not load the signed
image that was actually loaded.

Fixes #123
2023-03-15 00:36:50 +01:00
renovate[bot] eed59b4d16
fix(deps): update all dependencies 2023-03-13 00:45:02 +00:00
nikstur 721b584940 tool: fine tune a few log messages 2023-03-06 00:52:46 +01:00
nikstur c8522e02b4 Merge pull request #122 from nix-community/renovate/all
fix(deps): update all dependencies
2023-02-28 22:20:33 +01:00
renovate[bot] e321ad1626
fix(deps): update all dependencies 2023-02-27 02:01:48 +00:00
Julian Stecklina cbccd64c57 tool: make file installation deterministic
Due to the use of hash maps, the order of file installation was not
deterministic. I've changed the code the use BTreeMaps instead, which
makes this deterministic. While I was here, I tried to simplify the
code a bit.
2023-02-25 20:42:08 +01:00
Julian Stecklina a5e283ca44
Merge pull request #112 from nix-community/log
Minimalistic Logging Support
2023-02-25 11:20:01 +01:00
nikstur 32950b7708 tool: fix typos 2023-02-24 01:29:15 +01:00
nikstur 75a19cd818 tool: correctly sort generation links
To correctly overwrite existing initrd with newer secrets (from newer
generations), the links need to be sorted from oldest generation to
newest.
2023-02-24 00:32:14 +01:00
nikstur 1d21d7bdd8 tool: add install tests
Add a few integration tests for installing files, e.g. overwriting
signed and unsigned files.
2023-02-24 00:04:00 +01:00
nikstur 362205c2ec tool: check file hashes before copying
To minimize writes to the ESP but still find necessary changes, compare
the hashes of the files on the ESP with the "expected" hashes. Only copy
and overwrite already existing files if the hashes don't match. This
ensures a working-as-expected state on the ESP as opposed to previously
where already existing files were just ignored.
2023-02-24 00:04:00 +01:00
nikstur 06b9cdc69e tool: move file_hash() to utils module 2023-02-24 00:04:00 +01:00
nikstur 3a3ad7c40d tool: write all generation artifacts at once
Previously, generations were installed one after another. Now all
artifacts (kernels, initrd etc.) are first collected and then installed.
This way the writes to the ESP are reduced as duplicate paths are
already removed in the collection phase.
2023-02-24 00:04:00 +01:00
Janne Heß de4c62a1bd stub: lanzatool -> lzbt 2023-02-23 09:32:28 +01:00
Julian Stecklina 90755b789f stub: use logger instead of printing manually 2023-02-21 01:32:29 +01:00
Julian Stecklina 7bde42f4a8 stub: enable logger in uefi-services 2023-02-21 01:32:29 +01:00
Julian Stecklina 3a9cd26c5e stub: update uefi dependency to 0.19.1 2023-02-21 00:50:34 +01:00
Julian Stecklina 697d0d1baa stub: drop unused ed25519-compact dependency 2023-02-21 00:35:00 +01:00
nikstur df6b1b07f7 tool: use random names for secure tempfiles
Using random names for tempfiles makes handling them easier. It reduces
the amount of noise in the code because no custom name needs to be
provided for each tempfile. The names were not really useful in any
case.

It also does not burden the developer with ensuring uniqueness of names.
This is relevant when files for multiple generations need to be stored
in the same directory (e.g. because they need to be accessed after
handling one generation).

Out of an abundance of caution, 32 random alphanumeric characters are
chosen for each filename. The tempfile crate, in comparison, only
chooses 8. 32 characters should be enough to avoid collisions, even
if the PRNG is not of cryptographic quality.
2023-02-21 00:13:40 +01:00
nikstur 4d2e67f799 tool: make some utility test functions reusable
Make them reusable by moving them to the common module.
2023-02-20 01:05:01 +01:00
nikstur a8d9ea128d tool: improve sd-boot generation display name
Leverage the bootspec `label` field in its intended way. The VERSION_ID
of the os-release in the stub now only contains the generation number
and the build time. This makes a correct PRETTY_NAME entirely dependent
on correct information in the bootspec `label` field.
2023-02-10 12:25:59 +01:00
nikstur 06f921ead0 tool: read build time from symlink
Read the build time from generation symlinks in /nix/var/nix/profiles
instead of from the underlying derivation. The derivation build time
will always be a UNIX epoch of 0 because of the `nix-build` sandbox,
which is useless for identifying when a generation was created.
2023-02-09 00:29:12 +01:00
Julian Stecklina 8b00b748f2 stub: add fall back for hash mismatches when Secure Boot is off 2023-02-02 18:03:54 +01:00
Julian Stecklina 8d2ebbc6a7 stub: move linux booting into its own function 2023-02-02 18:03:54 +01:00
Alois Wohlschlager 081714cab9
Pass the built-in cmdline to the kernel
Do not pass our own cmdline on to the kernel. It may have been set by a
malicious boot loader specification entry, and could instruct the
kernel to load an arbitrary unprotected initrd (or perform some other
fun stuff). Instead, always pass the command line built into the UKI,
which is properly authenticated.
2023-01-31 18:32:13 +01:00
Alois Wohlschlager 3885f114a8
Do not sign the kernel
Malicious boot loader specification entries could be used to make a
signed kernel load arbitrary unprotected initrds. Since we do not want
this, do not sign the kernel. This way, the only things allowed to boot
are our UKI stubs, which do verify the initrd.
2023-01-31 18:25:27 +01:00
Alois Wohlschlager 7387c6708d
Load the kernel image ourselves
When loading something with UEFI LoadImage, signature validation is
performed. However, we verify the kernel by its hash already, and don't
want to sign it. Hence, we have to load it on our own.
2023-01-31 18:25:14 +01:00
Janne Heß 96d52b215c
Make the os-release parser more precise
Closes #77
2023-01-30 11:46:48 +01:00
nikstur ce3b2c27b5 tool: write systemd-boot loader.conf
To minimize the number of arguments passed to `lzbt`, the loader config
is assembled outside `lzbt` and passed as a single argument.

Instead of reimplementing `consoleMode` under the `lanzaboote`
namespace, `config.loader.systemd-boot.consoleMode` is reused as is.
2023-01-29 16:19:14 +01:00
nikstur 5f28ae75ea tool: atomically write to ESP
To minimize the potential for irrecoverable errors, only atomic writes
to the ESP are performed. This is implemented by first copying the file
to the destination with a `.tmp` suffix and then renaming it to the
final desintation. This is atomic because the rename operation is atomic
on POSIX platforms.

Specifically, this means that even if the system crashes during the
operation, the final desintation path will most likely be intact if it
exists at all. There are some nuances to this however. It **cannot** be
actually guaranteed that the operation was performed on the filesystem
level. However, this is the best we can do for now.

For reference:
- POSIX rename(2): https://pubs.opengroup.org/onlinepubs/9699919799/
- Rust fs::rename corresponds to rename(2) on Unix: https://doc.rust-lang.org/std/fs/fn.rename.html
- Rust fs::rename is implemented using libc's rename: https://github.com/rust-lang/rust/blob/master/library/std/src/sys/unix/fs.rs#L1397
- Renaming in libc is atomic: https://www.gnu.org/software/libc/manual/html_node/Renaming-Files.html
2023-01-29 15:31:38 +01:00
nikstur 0ca25a9bf0
Merge pull request #78 from nix-community/robust-systemd-version-parsing
tool: make systemd version parsing robust
2023-01-26 21:46:03 +01:00
nikstur 247afb33a2 tool: make systemd version parsing robust
To make handling systemd versions more robust, they are parsed into a
u32 tuple instead of an f32. Additionally, some unit tests for correct
parsing and comparing of versions are added.
2023-01-26 21:30:44 +01:00
nikstur 1970b95b68 tool: remove bootspec.json
This fixture is not necessary anymore as we have enough integration
tests.
2023-01-26 01:16:09 +01:00
nikstur cc169689f3 tool: smarter systemd-boot install
The process of installing systemd-boot is "smarter" because it now
considers a a few conditions instead of doing nothing if there is a file
at the deistination path. systemd-boot is now forcibly installed (i.e.
overwriting any file at the destination) if (1) there is no file at the
destination, OR (2) a newer version of systemd-boot is available, OR (3)
the signature of the file at the destination could not be verified.
2023-01-25 22:21:14 +01:00
nikstur db75203e31 tool: split esp paths
To access paths on the ESP before or after installing generations, split
EspPaths into general EspPaths that only depend on the path to the ESP
and EspGenerationPaths which additionally depend on generation specific
information (e.g. version number and initrd filename).
2023-01-25 00:24:40 +01:00
nikstur 6e452b50df tool: add SecureTempDirExt
Add an extension to TempDir that allows to create secure tempfiles. This
way, everything related to creating secure tempfiles is bundled in a
single place and can easily be reused.
2023-01-21 16:26:17 +01:00
Julian Stecklina dd499f6642 treewide: fix typos 2023-01-21 10:27:34 +01:00
nikstur 5bb33f3389 treewide: simplify subproject names
Lanzatool is renamed to 'tool' and lanzaboote is renamed to 'stub'.
The name of the lanzatool binary is now 'lzbt' standing for
LanZaBooteTool.
2023-01-17 21:31:14 +01:00
nikstur 2fce3c0802 treewde: simplify subproject directory names
This commit only moves the directories instead of chaning any names
inside files.
2023-01-17 21:31:14 +01:00
nikstur 3db39f403b treewide: blake3 -> sha256
Using the sha2 crate instead of blake3 decreases the binary size of the
stub by around 50%.
2023-01-14 02:31:54 +01:00
nikstur 7f235ce004 lanzatool: spell specialised consistently 2023-01-06 23:20:31 +01:00
nikstur 4f44cb70a2 lanzatool: generate custom os-release 2023-01-06 21:27:51 +01:00
nikstur b6eb6c1e52 lanzatool: keep unrelated files when running gc 2023-01-04 22:29:09 +01:00
nikstur c4e5ec7008 lanzatool: add more assertions to gc integration test 2023-01-04 01:23:13 +01:00
nikstur 7afbc43195
Merge pull request #43 from nix-community/some-more-lanzatool-refactoring
lanzatool: some more refactoring
2023-01-02 00:41:13 +01:00
nikstur 1e632c0d1d lanzatool: add context to sbsing output failure 2023-01-02 00:34:01 +01:00
nikstur b592d92744 lanzatool: don't open file to read metadata 2023-01-02 00:34:01 +01:00
nikstur 1c0438a003 lanzatool: simplify uefi path code 2023-01-02 00:33:59 +01:00
nikstur d3a96b1c3c lanzatool: intgeration test infrastrucutre + gc tests 2023-01-02 00:05:32 +01:00
nikstur 676786f811 lanzatool: add rand dev dependency 2023-01-02 00:05:21 +01:00
nikstur 3c7c8340eb lanzatool: add assert_cmd dev dependency 2023-01-02 00:05:21 +01:00
nikstur 9daf9ae0a8 lanzatool: implement configuration limit 2023-01-02 00:05:17 +01:00
nikstur 4a8cfa7f7f lanzatool: add walkdir dependency 2022-12-31 02:10:36 +01:00
nikstur 0a58b290e2 lanzatool: clean up parse_version and add simple test 2022-12-30 23:43:19 +01:00
nikstur 463d9496bf lanzatool: write sbsign output to stdout 2022-12-30 23:43:19 +01:00
nikstur d4c5af23fe lanzatool: improve error msg for file_size 2022-12-30 23:43:19 +01:00
nikstur a341baa09a lanzatool: simplify nixos_path and add unit test 2022-12-30 23:43:18 +01:00
nikstur 781651b9e0 lanzatool: improve esp_relative_path_string error msg 2022-12-30 21:11:07 +01:00
Julian Stecklina f6ae373500 lanzatool: apply rustfmt to install.rs 2022-12-28 23:59:23 +01:00
Julian Stecklina f07618b64c lanzatool: remove unused utils module 2022-12-28 23:59:23 +01:00
Julian Stecklina b762de9fec lanzatool: remove Path -> String conversions in signature module 2022-12-28 23:59:23 +01:00
Julian Stecklina 74afcb1eea lanzatool: remove Path -> String conversion from pe module
... by using OsString, which can handle broken UTF-8 in file
names.
2022-12-28 23:59:23 +01:00
Raito Bezarius 0ad20b0d5a lanzatool: ignore malformed generations 2022-12-26 02:47:28 +01:00
nikstur 65f3c67357 lanzatool: appease clippy by removing borrow 2022-12-25 18:05:07 +01:00
nikstur 6e66c5f0ed Cargo.toml: update bootspec to upstream 2022-12-25 18:05:07 +01:00
Raito Bezarius e3f6029643 nixos/lanzaboote: use upstream bootspec for extension generation 2022-12-25 18:05:07 +01:00
Raito Bezarius 92e7e4f49a lanzatool(bootspec): introduce DetSys's bootspec library 2022-12-18 00:29:49 +01:00
nikstur 614131d648 lanzatool: remove placeholder code for auto enrolling uefi keys 2022-12-10 18:11:23 +01:00
nikstur 49a8ae8aec lanzatool: skip existing files in esp 2022-12-03 19:05:12 +01:00
Julian Stecklina 85b111aa17 initrd-stub: drop unused stub
This is not useful anymore, because we don't need to wrap the initrd
anymore.
2022-11-30 09:25:17 +01:00
Julian Stecklina 401c3b8c1c lanzatool, lanzaboote: don't wrap initrd as PE
... because we check its integrity using the embedded blake3 hash. So
there is no need for the LoadImage hack anymore.
2022-11-30 09:23:42 +01:00
Julian Stecklina 1739ffde26 lanzaboote: verify hash of kernel and initrd 2022-11-30 09:22:14 +01:00
Julian Stecklina 7a15bba50b lanzaboote: load kernel and initrd into memory only once 2022-11-30 09:22:14 +01:00
Julian Stecklina d754a87d5c lanzaboote: cleanup kernel/initrd opening 2022-11-30 09:22:14 +01:00
Julian Stecklina 3f78939d0a lanzatool: embed kernel and initrd hashes 2022-11-30 09:22:14 +01:00
Julian Stecklina ba119d398f lanzatool: add function documentation 2022-11-30 09:22:14 +01:00
Julian Stecklina 7926ab9e5e lanzaboote: fix clippy issues 2022-11-28 13:38:01 +01:00
nikstur 0a638970e7 lanzatool: enable specialisation 2022-11-27 12:01:53 +01:00
nikstur 98cf9e0978 lanzatool: improve --help output 2022-11-27 00:12:00 +01:00
nikstur fffa7d6bfa lanzatool: appease clippy 2022-11-26 23:19:08 +01:00
nikstur 0a96623461 lanzatool: bootspec from generation
The bootspec is now read from each generation so that more than one
entry can be generated when calling install
2022-11-26 22:27:44 +01:00
nikstur 967f78d374 lanzatool: hide sbsign output on happy path 2022-11-26 15:34:48 +01:00
nikstur c441f5157e lanzatool: sign and copy in one step) 2022-11-26 15:32:43 +01:00
nikstur 240c80368f lanzatool: make it more typedriven 2022-11-26 14:55:15 +01:00
Ryan Lahfa 95f596f4dc lanzatool: add support for generations and correct naming of kernels a… (#12)
* lanzatool: add support for generations and correct naming of kerels and initrds

* test: use convert_to_esp(extract_bspec_attr(⋅)) for unsigned tests

* lanzatool: ryan is a B class engineer

Co-authored-by: nikstur@outlook.com
2022-11-26 03:14:21 +01:00
Julian Stecklina 1f0f349559 lanzaboote: add error handling strings 2022-11-26 02:47:21 +01:00
Julian Stecklina 95a03d69bb lanzaboote: reorganize to avoid explicit drops 2022-11-26 02:31:01 +01:00
Raito Bezarius 9f65f75289 feature: support initrd secrets 2022-11-26 02:01:41 +01:00
Raito Bezarius a3150dca11 lanzatool: perform secure assembling for lanzaboote_image and PE wrapping 2022-11-26 01:24:33 +01:00
Raito Bezarius f6930955a3 lanzatool: sync for every sign operation 2022-11-25 23:58:06 +01:00
nikstur a3ec2cfc15 lanzatool: add error messages 2022-11-25 23:50:11 +01:00
Julian Stecklina c87b2a09dc nix: fix lanzatool integration/merge mixup 2022-11-25 23:46:19 +01:00
Julian Stecklina 3779e81b20 lanzaboote: handle errors in print_logo 2022-11-25 18:14:58 +01:00
Julian Stecklina 6bc66052c2 lanzaboote: add EmbeddedConfiguration docs 2022-11-25 18:14:58 +01:00
Julian Stecklina a9edb1488e lanzaboote: fix logo
Someone forget the E in the name.
2022-11-25 18:14:58 +01:00
nikstur 53c4e03619 merge this shit 2022-11-25 18:10:21 +01:00
Ryan Lahfa eda254b6cd nixpkgs: integrate the whole thing (#7)
* nixos: add a lanzaboote module

* nixos: add a lanzaboote module

- Wire up things with Bootspec & External bootloaders
- Introduce SecureBoot keys

* nixos: actually enable sb

* nixos: disable it and adapt it

* lanzatool: fix init

* nixos: secureboot reached

* nixos: enrollment is optional

Co-authored-by: nikstur@outlook.com
2022-11-25 17:59:15 +01:00
nikstur 3a093d85ab lanzatool: set permissons for all files in esp to 755 2022-11-25 17:47:24 +01:00
nikstur 7685ba088b lanzatool: reuse code for signer 2022-11-25 15:46:33 +01:00
nikstur c0391ce8d7 lanzatool: improve tempfiles and error handling in pe 2022-11-25 15:16:05 +01:00
nikstur ad3a8ec3e5 lanzatool: make --pki-bundle optional 2022-11-25 13:08:37 +01:00
nikstur cd2ef6181d lanzatool: improve signer code 2022-11-25 13:07:04 +01:00
Raito Bezarius 49519cb289 nixos: secureboot reached 2022-11-25 03:04:44 +01:00
nikstur efbb28dc99 lanzatool: fix init 2022-11-24 17:14:55 +01:00
Raito Bezarius ccdd02bf1c nixos: add a lanzaboote module
- Wire up things with Bootspec & External bootloaders
- Introduce SecureBoot keys
2022-11-24 17:07:05 +01:00
nikstur 858c0befb3 lanzaboot: include init in cmdline 2022-11-24 16:51:43 +01:00
nikstur aa86ae9e30 lanzatool: add cmdline args for keys 2022-11-24 14:12:00 +01:00
nikstur 587e388364 lanzatool: improve error handling 2022-11-24 13:33:01 +01:00
Julian Stecklina 417122e840 Merge remote-tracking branch 'origin/lanzatool-bootspec-funz' 2022-11-24 12:28:03 +01:00
nikstur d40b9f281c lanzatool: remove v1 key 2022-11-24 12:26:32 +01:00
Julian Stecklina df716e17d6 Add documentation to initrd loader 2022-11-24 12:18:23 +01:00
Julian Stecklina 30b61baf38 Add documentation to initrd loader 2022-11-24 12:11:17 +01:00
nikstur 3e7f5fa625 lanzatool: implement copying sdboot to esp 2022-11-24 11:10:19 +01:00
nikstur 73b1f7e2b5 lanzatool: readd efi relative file paths 2022-11-23 20:54:13 +01:00
nikstur 46f1e84a9d lanzatool: init wrapping initrd 2022-11-23 20:48:49 +01:00
nikstur a65998945d lanzatool: implement relative esp paths 2022-11-23 18:15:32 +01:00
Julian Stecklina dcca50d14f Refactor embedded config extraction 2022-11-23 17:57:43 +01:00
Julian Stecklina fa331d8b98 Fix section extraction 2022-11-23 17:57:23 +01:00
nikstur 24803a04a2 lanzatool: copy image to esp output dir 2022-11-23 17:26:56 +01:00
nikstur 5dbb8e7452 lanzatool: detrashify 2022-11-23 17:16:08 +01:00
Julian Stecklina de451fa5af Merge remote-tracking branch 'origin/lanzatool-install' 2022-11-23 15:49:38 +01:00
nikstur c4734d11fc lanzatool.crypto: remove 2022-11-23 15:49:02 +01:00
Julian Stecklina 4dab5f7b8f Extract Linux kernel and initrd filenames from PE binary 2022-11-23 15:46:25 +01:00
Julian Stecklina 788a112050 Merge pull request #6 from blitz/lanzatool-install
lanzatool.install: init
2022-11-23 15:46:12 +01:00
nikstur 27044f6bdf lanzatool.crypto: remove 2022-11-23 15:44:19 +01:00
nikstur 4356d342a2 lanzatool.install: init 2022-11-23 15:26:26 +01:00
Julian Stecklina 1ca83c25d5 Remove some unwraps 2022-11-23 14:11:54 +01:00