Skip to content

Commit

Permalink
fix: use custom Talos/kernel version when generating UKI
Browse files Browse the repository at this point in the history
See siderolabs/image-factory#44

Instead of using constants, use proper Talos version and kernel version
discovered from the image.

Signed-off-by: Andrey Smirnov <[email protected]>
  • Loading branch information
smira committed Nov 3, 2023
1 parent eb94468 commit 807a995
Show file tree
Hide file tree
Showing 8 changed files with 135 additions and 9 deletions.
21 changes: 19 additions & 2 deletions internal/pkg/secureboot/uki/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import (
)

func (builder *Builder) generateOSRel() error {
osRelease, err := version.OSRelease()
osRelease, err := version.OSReleaseFor(version.Name, builder.Version)
if err != nil {
return err
}
Expand Down Expand Up @@ -94,9 +94,26 @@ func (builder *Builder) generateSplash() error {
}

func (builder *Builder) generateUname() error {
// it is not always possible to get the kernel version from the kernel image, so we
// do a bit of pre-checks
var kernelVersion string

if builder.Version == version.Tag {
// if building from the same version of Talos, use default kernel version
kernelVersion = constants.DefaultKernelVersion
} else {
// otherwise, try to get the kernel version from the kernel image
kernelVersion, _ = DiscoverKernelVersion(builder.KernelPath) //nolint:errcheck
}

if kernelVersion == "" {
// we haven't got the kernel version, skip the uname section
return nil
}

path := filepath.Join(builder.scratchDir, "uname")

if err := os.WriteFile(path, []byte(constants.DefaultKernelVersion), 0o600); err != nil {
if err := os.WriteFile(path, []byte(kernelVersion), 0o600); err != nil {
return err
}

Expand Down
69 changes: 69 additions & 0 deletions internal/pkg/secureboot/uki/kernel.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

package uki

import (
"bytes"
"encoding/binary"
"fmt"
"os"
"strings"
)

// DiscoverKernelVersion reads kernel version from the kernel image.
//
// This only works for x86 kernel images.
//
// Based on https://www.kernel.org/doc/html/v5.6/x86/boot.html.
func DiscoverKernelVersion(kernelPath string) (string, error) {
f, err := os.Open(kernelPath)
if err != nil {
return "", err
}

defer f.Close() //nolint:errcheck

header := make([]byte, 1024)

_, err = f.Read(header)
if err != nil {
return "", err
}

// check header magic
if string(header[0x202:0x206]) != "HdrS" {
return "", fmt.Errorf("invalid kernel image")
}

setupSects := header[0x1f1]
versionOffset := binary.LittleEndian.Uint16(header[0x20e:0x210])

if versionOffset == 0 {
return "", fmt.Errorf("no kernel version")
}

if versionOffset > uint16(setupSects)*0x200 {
return "", fmt.Errorf("invalid kernel version offset")
}

versionOffset += 0x200

version := make([]byte, 256)

_, err = f.ReadAt(version, int64(versionOffset))
if err != nil {
return "", err
}

idx := bytes.IndexByte(version, 0)
if idx == -1 {
return "", fmt.Errorf("invalid kernel version")
}

versionString := string(version[:idx])
versionString, _, _ = strings.Cut(versionString, " ")

return versionString, nil
}
21 changes: 21 additions & 0 deletions internal/pkg/secureboot/uki/kernel_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

package uki_test

import (
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"github.com/siderolabs/talos/internal/pkg/secureboot/uki"
)

func TestKernelVersion(t *testing.T) {
version, err := uki.DiscoverKernelVersion("testdata/kernel")
require.NoError(t, err)

assert.Equal(t, "6.1.58-talos", version)
}
Binary file added internal/pkg/secureboot/uki/testdata/kernel
Binary file not shown.
2 changes: 2 additions & 0 deletions internal/pkg/secureboot/uki/uki.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ type Builder struct {
//
// Arch of the UKI file.
Arch string
// Version of Talos.
Version string
// Path to the sd-stub.
SdStubPath string
// Path to the sd-boot.
Expand Down
1 change: 1 addition & 0 deletions pkg/imager/imager.go
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,7 @@ func (i *Imager) buildUKI(report *reporter.Reporter) error {

builder := uki.Builder{
Arch: i.prof.Arch,
Version: i.prof.Version,
SdStubPath: i.prof.Input.SDStub.Path,
SdBootPath: i.prof.Input.SDBoot.Path,
KernelPath: i.prof.Input.Kernel.Path,
Expand Down
16 changes: 9 additions & 7 deletions pkg/version/osrelease.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,7 @@ import (

// OSRelease returns the contents of /etc/os-release.
func OSRelease() ([]byte, error) {
var (
v string
tmpl *template.Template
)
var v string

switch Tag {
case "none":
Expand All @@ -26,14 +23,19 @@ func OSRelease() ([]byte, error) {
v = Tag
}

return OSReleaseFor(Name, v)
}

// OSReleaseFor returns the contents of /etc/os-release for a given name and version.
func OSReleaseFor(name, version string) ([]byte, error) {
data := struct {
Name string
ID string
Version string
}{
Name: Name,
ID: strings.ToLower(Name),
Version: v,
Name: name,
ID: strings.ToLower(name),
Version: version,
}

tmpl, err := template.New("").Parse(constants.OSReleaseTemplate)
Expand Down
14 changes: 14 additions & 0 deletions pkg/version/osrelease_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ package version_test
import (
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"github.com/siderolabs/talos/pkg/version"
Expand All @@ -19,3 +20,16 @@ func TestOSRelease(t *testing.T) {
_, err := version.OSRelease()
require.NoError(t, err)
}

func TestOSReleaseFor(t *testing.T) {
t.Parallel()

contents, err := version.OSReleaseFor("Talos", "v1.0.0")
require.NoError(t, err)

assert.Equal(
t,
"NAME=\"Talos\"\nID=talos\nVERSION_ID=v1.0.0\nPRETTY_NAME=\"Talos (v1.0.0)\"\nHOME_URL=\"https://www.talos.dev/\"\nBUG_REPORT_URL=\"https://github.com/siderolabs/talos/issues\"\nVENDOR_NAME=\"Sidero Labs\"\nVENDOR_URL=\"https://www.siderolabs.com/\"\n", //nolint:lll
string(contents),
)
}

0 comments on commit 807a995

Please sign in to comment.