Skip to content

Commit 807a995

Browse files
committed
fix: use custom Talos/kernel version when generating UKI
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 <andrey.smirnov@siderolabs.com>
1 parent eb94468 commit 807a995

File tree

8 files changed

+135
-9
lines changed

8 files changed

+135
-9
lines changed

internal/pkg/secureboot/uki/generate.go

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import (
1919
)
2020

2121
func (builder *Builder) generateOSRel() error {
22-
osRelease, err := version.OSRelease()
22+
osRelease, err := version.OSReleaseFor(version.Name, builder.Version)
2323
if err != nil {
2424
return err
2525
}
@@ -94,9 +94,26 @@ func (builder *Builder) generateSplash() error {
9494
}
9595

9696
func (builder *Builder) generateUname() error {
97+
// it is not always possible to get the kernel version from the kernel image, so we
98+
// do a bit of pre-checks
99+
var kernelVersion string
100+
101+
if builder.Version == version.Tag {
102+
// if building from the same version of Talos, use default kernel version
103+
kernelVersion = constants.DefaultKernelVersion
104+
} else {
105+
// otherwise, try to get the kernel version from the kernel image
106+
kernelVersion, _ = DiscoverKernelVersion(builder.KernelPath) //nolint:errcheck
107+
}
108+
109+
if kernelVersion == "" {
110+
// we haven't got the kernel version, skip the uname section
111+
return nil
112+
}
113+
97114
path := filepath.Join(builder.scratchDir, "uname")
98115

99-
if err := os.WriteFile(path, []byte(constants.DefaultKernelVersion), 0o600); err != nil {
116+
if err := os.WriteFile(path, []byte(kernelVersion), 0o600); err != nil {
100117
return err
101118
}
102119

internal/pkg/secureboot/uki/kernel.go

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
// This Source Code Form is subject to the terms of the Mozilla Public
2+
// License, v. 2.0. If a copy of the MPL was not distributed with this
3+
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
4+
5+
package uki
6+
7+
import (
8+
"bytes"
9+
"encoding/binary"
10+
"fmt"
11+
"os"
12+
"strings"
13+
)
14+
15+
// DiscoverKernelVersion reads kernel version from the kernel image.
16+
//
17+
// This only works for x86 kernel images.
18+
//
19+
// Based on https://www.kernel.org/doc/html/v5.6/x86/boot.html.
20+
func DiscoverKernelVersion(kernelPath string) (string, error) {
21+
f, err := os.Open(kernelPath)
22+
if err != nil {
23+
return "", err
24+
}
25+
26+
defer f.Close() //nolint:errcheck
27+
28+
header := make([]byte, 1024)
29+
30+
_, err = f.Read(header)
31+
if err != nil {
32+
return "", err
33+
}
34+
35+
// check header magic
36+
if string(header[0x202:0x206]) != "HdrS" {
37+
return "", fmt.Errorf("invalid kernel image")
38+
}
39+
40+
setupSects := header[0x1f1]
41+
versionOffset := binary.LittleEndian.Uint16(header[0x20e:0x210])
42+
43+
if versionOffset == 0 {
44+
return "", fmt.Errorf("no kernel version")
45+
}
46+
47+
if versionOffset > uint16(setupSects)*0x200 {
48+
return "", fmt.Errorf("invalid kernel version offset")
49+
}
50+
51+
versionOffset += 0x200
52+
53+
version := make([]byte, 256)
54+
55+
_, err = f.ReadAt(version, int64(versionOffset))
56+
if err != nil {
57+
return "", err
58+
}
59+
60+
idx := bytes.IndexByte(version, 0)
61+
if idx == -1 {
62+
return "", fmt.Errorf("invalid kernel version")
63+
}
64+
65+
versionString := string(version[:idx])
66+
versionString, _, _ = strings.Cut(versionString, " ")
67+
68+
return versionString, nil
69+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// This Source Code Form is subject to the terms of the Mozilla Public
2+
// License, v. 2.0. If a copy of the MPL was not distributed with this
3+
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
4+
5+
package uki_test
6+
7+
import (
8+
"testing"
9+
10+
"github.com/stretchr/testify/assert"
11+
"github.com/stretchr/testify/require"
12+
13+
"github.com/siderolabs/talos/internal/pkg/secureboot/uki"
14+
)
15+
16+
func TestKernelVersion(t *testing.T) {
17+
version, err := uki.DiscoverKernelVersion("testdata/kernel")
18+
require.NoError(t, err)
19+
20+
assert.Equal(t, "6.1.58-talos", version)
21+
}
16 KB
Binary file not shown.

internal/pkg/secureboot/uki/uki.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ type Builder struct {
3535
//
3636
// Arch of the UKI file.
3737
Arch string
38+
// Version of Talos.
39+
Version string
3840
// Path to the sd-stub.
3941
SdStubPath string
4042
// Path to the sd-boot.

pkg/imager/imager.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,7 @@ func (i *Imager) buildUKI(report *reporter.Reporter) error {
302302

303303
builder := uki.Builder{
304304
Arch: i.prof.Arch,
305+
Version: i.prof.Version,
305306
SdStubPath: i.prof.Input.SDStub.Path,
306307
SdBootPath: i.prof.Input.SDBoot.Path,
307308
KernelPath: i.prof.Input.Kernel.Path,

pkg/version/osrelease.go

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,7 @@ import (
1414

1515
// OSRelease returns the contents of /etc/os-release.
1616
func OSRelease() ([]byte, error) {
17-
var (
18-
v string
19-
tmpl *template.Template
20-
)
17+
var v string
2118

2219
switch Tag {
2320
case "none":
@@ -26,14 +23,19 @@ func OSRelease() ([]byte, error) {
2623
v = Tag
2724
}
2825

26+
return OSReleaseFor(Name, v)
27+
}
28+
29+
// OSReleaseFor returns the contents of /etc/os-release for a given name and version.
30+
func OSReleaseFor(name, version string) ([]byte, error) {
2931
data := struct {
3032
Name string
3133
ID string
3234
Version string
3335
}{
34-
Name: Name,
35-
ID: strings.ToLower(Name),
36-
Version: v,
36+
Name: name,
37+
ID: strings.ToLower(name),
38+
Version: version,
3739
}
3840

3941
tmpl, err := template.New("").Parse(constants.OSReleaseTemplate)

pkg/version/osrelease_test.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ package version_test
77
import (
88
"testing"
99

10+
"github.com/stretchr/testify/assert"
1011
"github.com/stretchr/testify/require"
1112

1213
"github.com/siderolabs/talos/pkg/version"
@@ -19,3 +20,16 @@ func TestOSRelease(t *testing.T) {
1920
_, err := version.OSRelease()
2021
require.NoError(t, err)
2122
}
23+
24+
func TestOSReleaseFor(t *testing.T) {
25+
t.Parallel()
26+
27+
contents, err := version.OSReleaseFor("Talos", "v1.0.0")
28+
require.NoError(t, err)
29+
30+
assert.Equal(
31+
t,
32+
"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
33+
string(contents),
34+
)
35+
}

0 commit comments

Comments
 (0)