mirror of
				https://github.com/siderolabs/talos.git
				synced 2025-11-04 02:11:12 +01:00 
			
		
		
		
	feat: make ISO builds reproducible
This relies on changes in GRUB and other utilities to respect `SOURCE_DATE_EPOCH`. Variable `SOURCE_DATE_EPOCH` is set to the timestamp of the last git commit which makes it deterministic, but still changes for each release/commit. Signed-off-by: Andrey Smirnov <andrey.smirnov@talos-systems.com>
This commit is contained in:
		
							parent
							
								
									887c2326a4
								
							
						
					
					
						commit
						faecae44fd
					
				@ -126,7 +126,8 @@ ARG CGO_ENABLED
 | 
			
		||||
ENV CGO_ENABLED ${CGO_ENABLED}
 | 
			
		||||
ENV GOCACHE /.cache/go-build
 | 
			
		||||
ENV GOMODCACHE /.cache/mod
 | 
			
		||||
ENV SOURCE_DATE_EPOCH=0
 | 
			
		||||
ARG SOURCE_DATE_EPOCH
 | 
			
		||||
ENV SOURCE_DATE_EPOCH ${SOURCE_DATE_EPOCH}
 | 
			
		||||
WORKDIR /src
 | 
			
		||||
 | 
			
		||||
# The build-go target creates a container to build Go code with Go modules downloaded and verified.
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										6
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										6
									
								
								Makefile
									
									
									
									
									
								
							@ -2,6 +2,7 @@ REGISTRY ?= ghcr.io
 | 
			
		||||
USERNAME ?= talos-systems
 | 
			
		||||
SHA ?= $(shell git describe --match=none --always --abbrev=8 --dirty)
 | 
			
		||||
TAG ?= $(shell git describe --tag --always --dirty --match v[0-9]*)
 | 
			
		||||
SOURCE_DATE_EPOCH ?= $(shell git log -1 --pretty=%ct)
 | 
			
		||||
IMAGE_REGISTRY ?= $(REGISTRY)
 | 
			
		||||
IMAGE_TAG ?= $(TAG)
 | 
			
		||||
BRANCH ?= $(shell git rev-parse --abbrev-ref HEAD)
 | 
			
		||||
@ -11,7 +12,7 @@ NAME = Talos
 | 
			
		||||
 | 
			
		||||
ARTIFACTS := _out
 | 
			
		||||
TOOLS ?= ghcr.io/talos-systems/tools:v0.7.0-alpha.0-2-g7172a5d
 | 
			
		||||
PKGS ?= v0.7.0-alpha.0-13-g12856ce
 | 
			
		||||
PKGS ?= v0.7.0-alpha.0-14-g875c7ec
 | 
			
		||||
EXTRAS ?= v0.5.0-alpha.0-1-g4957f3c
 | 
			
		||||
GO_VERSION ?= 1.16
 | 
			
		||||
GOFUMPT_VERSION ?= v0.1.0
 | 
			
		||||
@ -84,6 +85,7 @@ COMMON_ARGS += --build-arg=STRINGER_VERSION=$(STRINGER_VERSION)
 | 
			
		||||
COMMON_ARGS += --build-arg=DEEPCOPY_GEN_VERSION=$(DEEPCOPY_GEN_VERSION)
 | 
			
		||||
COMMON_ARGS += --build-arg=VTPROTOBUF_VERSION=$(VTPROTOBUF_VERSION)
 | 
			
		||||
COMMON_ARGS += --build-arg=TAG=$(TAG)
 | 
			
		||||
COMMON_ARGS += --build-arg=SOURCE_DATE_EPOCH=$(SOURCE_DATE_EPOCH)
 | 
			
		||||
COMMON_ARGS += --build-arg=ARTIFACTS=$(ARTIFACTS)
 | 
			
		||||
COMMON_ARGS += --build-arg=IMPORTVET=$(IMPORTVET)
 | 
			
		||||
COMMON_ARGS += --build-arg=TESTPKGS=$(TESTPKGS)
 | 
			
		||||
@ -230,7 +232,7 @@ iso: ## Builds the ISO and outputs it to the artifact directory.
 | 
			
		||||
	@docker pull $(REGISTRY_AND_USERNAME)/installer:$(TAG)
 | 
			
		||||
	@for platform in $(subst $(,),$(space),$(PLATFORM)); do \
 | 
			
		||||
		arch=`basename "$${platform}"` ; \
 | 
			
		||||
		docker run --rm -i $(REGISTRY_AND_USERNAME)/installer:$(TAG) iso --arch $$arch --tar-to-stdout | tar xz -C $(ARTIFACTS)  ; \
 | 
			
		||||
		docker run --rm -e SOURCE_DATE_EPOCH=$(SOURCE_DATE_EPOCH) -i $(REGISTRY_AND_USERNAME)/installer:$(TAG) iso --arch $$arch --tar-to-stdout | tar xz -C $(ARTIFACTS)  ; \
 | 
			
		||||
	done
 | 
			
		||||
 | 
			
		||||
.PHONY: boot
 | 
			
		||||
 | 
			
		||||
@ -100,6 +100,10 @@ func runISOCmd() error {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := pkg.TouchFiles("/mnt"); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	log.Println("creating ISO")
 | 
			
		||||
 | 
			
		||||
	out := fmt.Sprintf("/tmp/talos-%s.iso", options.Arch)
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										25
									
								
								cmd/installer/pkg/epoch.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								cmd/installer/pkg/epoch.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,25 @@
 | 
			
		||||
// 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 pkg
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"os"
 | 
			
		||||
	"strconv"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// SourceDateEpoch returns parsed value of SOURCE_DATE_EPOCH.
 | 
			
		||||
func SourceDateEpoch() (int64, bool, error) {
 | 
			
		||||
	epoch, ok := os.LookupEnv("SOURCE_DATE_EPOCH")
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return 0, false, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	epochInt, err := strconv.ParseInt(epoch, 10, 64)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return 0, false, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return epochInt, true, nil
 | 
			
		||||
}
 | 
			
		||||
@ -6,19 +6,36 @@ package pkg
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"os"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/talos-systems/go-cmd/pkg/cmd"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// CreateISO creates an iso by invoking the `grub-mkrescue` command.
 | 
			
		||||
func CreateISO(iso, dir string) (err error) {
 | 
			
		||||
	_, err = cmd.Run(
 | 
			
		||||
		"grub-mkrescue",
 | 
			
		||||
func CreateISO(iso, dir string) error {
 | 
			
		||||
	args := []string{
 | 
			
		||||
		"--compress=xz",
 | 
			
		||||
		"--output="+iso,
 | 
			
		||||
		"--output=" + iso,
 | 
			
		||||
		dir,
 | 
			
		||||
	)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if epoch, ok, err := SourceDateEpoch(); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	} else if ok {
 | 
			
		||||
		// set EFI FAT image serial number
 | 
			
		||||
		if err := os.Setenv("GRUB_FAT_SERIAL_NUMBER", fmt.Sprintf("%x", uint32(epoch))); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		args = append(args,
 | 
			
		||||
			"--",
 | 
			
		||||
			"-volume_date", "all_file_dates", fmt.Sprintf("=%d", epoch),
 | 
			
		||||
			"-volume_date", "uuid", time.Unix(epoch, 0).Format("2006010215040500"),
 | 
			
		||||
		)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	_, err := cmd.Run("grub-mkrescue", args...)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fmt.Errorf("failed to create ISO: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										37
									
								
								cmd/installer/pkg/touch.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								cmd/installer/pkg/touch.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,37 @@
 | 
			
		||||
// 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 pkg
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"io/fs"
 | 
			
		||||
	"log"
 | 
			
		||||
	"os"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// TouchFiles updates mtime for all the files under root if SOURCE_DATE_EPOCH is set.
 | 
			
		||||
func TouchFiles(root string) error {
 | 
			
		||||
	epochInt, ok, err := SourceDateEpoch()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	timestamp := time.Unix(epochInt, 0)
 | 
			
		||||
 | 
			
		||||
	log.Printf("changing timestamps under %q to %s", root, timestamp)
 | 
			
		||||
 | 
			
		||||
	return filepath.WalkDir(root, func(path string, d fs.DirEntry, err error) error {
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return os.Chtimes(path, timestamp, timestamp)
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user