Skip to content

Add STACKIT KMS integration - #5

Open
Gottox wants to merge 5 commits into
mainfrom
feature/kms-integration
Open

Gottox wants to merge 5 commits into
mainfrom
feature/kms-integration

Conversation

@Gottox

@Gottox Gottox commented Oct 19, 2026

Copy link
Copy Markdown

There are a few commits missing in main. These are included here too.

@kbudde

kbudde commented Oct 21, 2026

Copy link
Copy Markdown
Collaborator

Hey @Gottox we fixed the build on main. Can you solve the conflicts?

The overrideSourceImageRegistry and skipVersionfilter options were added
without rerunning the docs generator. Bring docs/configuration.md back in
sync with options.go before adding new options on top.
… KMS

STACKIT KMS offers no RSA signing, so it cannot act as the signer the way
Azure Key Vault and AWS KMS do. Instead the file-based private keys are
stored as ciphertext of a symmetric KMS key. At startup they are decrypted
and served to the imager over its signer sockets, so no plaintext key ever
touches the filesystem.

In memory the keys live in memguard enclaves: encrypted, locked against
swapping and excluded from core dumps. They are opened for the duration of a
single signing operation only. Importing memguard disables core dumps for
the whole process.

The new secureBoot.stackitKMS options select the KMS key. They are wired in
through a small hook in buildSecureBootService; everything else lives in
new files so the upstream code stays easy to rebase.
The dev config carried the hostname of one developer machine. Compose
publishes the factory on localhost:8080, so use that for everyone.
Enable SecureBoot in the dev config with the key files decrypted by STACKIT
KMS, and add a small script that exchanges a service account key for a
short-lived access token. The token is for the curl calls around the setup,
for example creating the key ring and encrypting the key files; the factory
itself authenticates through the STACKIT SDK.
@Gottox
Gottox force-pushed the feature/kms-integration branch from d4ea2b4 to 146cc35 Compare October 22, 2026 17:42
@Gottox

Gottox commented Oct 22, 2026

Copy link
Copy Markdown
Author

rebased!

Comment thread docs/configuration.md
IF_ARTIFACTS_INSTALLER_INTERNAL_NAMESPACE=siderolabs
IF_ARTIFACTS_INSTALLER_INTERNAL_REGISTRY=ghcr.io
IF_ARTIFACTS_INSTALLER_INTERNAL_REPOSITORY=
IF_ARTIFACTS_OVERRIDESOURCEIMAGEREGISTRY=

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could you use some underscores pls to myke it more readable :)

Comment thread docs/configuration.md
IF_ARTIFACTS_SCHEMATIC_NAMESPACE=siderolabs/image-factory
IF_ARTIFACTS_SCHEMATIC_REGISTRY=ghcr.io
IF_ARTIFACTS_SCHEMATIC_REPOSITORY=schematics
IF_ARTIFACTS_SKIPVERSIONFILTER=false

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same ⏫

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven't find a nice way to do that. But I'll look into it!

return nil, status.Errorf(codes.InvalidArgument, "unsupported hash %s", req.GetHash())
}

key, buf, err := s.key.open()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do you return the buffer when you don't use it?
can you defer & destroy in open() instead?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, that's an tinkering artifact. Thanks for noticing.

//
// The path is fixed so that it does not change the profile hash between restarts.
func serveSigner(ctx context.Context, name string, srv *signerServer) (string, error) {
path := filepath.Join(os.TempDir(), "image-factory-signer", name+".sock")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could you use a const please?

server := grpc.NewServer()
signer.RegisterSignerServiceServer(server, srv)

go server.Serve(lis) //nolint:errcheck

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i'd rather log the error here tbh

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

right! - on it!

signingKeyPath, signingCertPath, pcrKeyPath string,
kmsOpts ...config.ConfigurationOption,
) (*Service, error) {
ctx, cancel := context.WithTimeout(ctx, time.Minute)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make this timeout duration as an const

func sealKey(der []byte) (*sealedKey, error) {
key, err := x509.ParsePKCS1PrivateKey(der)
if err != nil {
memguard.WipeBytes(der)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We must discuss if we want to add this sensitive package from some person from github.

We can try out the go stdlib runtime/secret, but this is unfortunately behind the experimental flag.
Haven't taken a deeper look if it has the same features as with this memguard lib.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants