Skip to content

Commit

Permalink
incus-osd: Some initial logic
Browse files Browse the repository at this point in the history
Signed-off-by: Stéphane Graber <[email protected]>
  • Loading branch information
stgraber committed Jan 16, 2025
1 parent da15bcb commit cc3a72e
Show file tree
Hide file tree
Showing 8 changed files with 284 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,5 @@ mkosi.crt
mkosi.key

mkosi.images/base/mkosi.extra/boot/EFI/

incus-osd/incus-osd
142 changes: 142 additions & 0 deletions incus-osd/cmd/incus-osd/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
package main

import (
"compress/gzip"
"context"
"fmt"
"io"
"log/slog"
"net/http"
"os"
"path/filepath"
"slices"
"strings"

"github.com/google/go-github/v68/github"

"github.com/lxc/incus-os/incus-osd/internal/keyring"
"github.com/lxc/incus-os/incus-osd/internal/systemd"
)

var (
ghOrganization = "lxc"
ghRepository = "incus-os"
osExtensions = []string{"debug.raw.gz", "incus.raw.gz"}
osExtensionsPath = "/var/lib/extensions"
)

func main() {
err := run()
if err != nil {
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
}
}

func run() error {
ctx := context.TODO()

// Prepare a logger.
logger := slog.New(slog.NewTextHandler(os.Stdout, nil))
slog.SetDefault(logger)

// Check kernel keyring.
slog.Info("Getting trusted system keys")
keys, err := keyring.GetKeys(ctx, keyring.PlatformKeyring)
if err != nil {
return err
}

// Determine runtime mode.
mode := "unsafe"

for _, key := range keys {
if key.Fingerprint == "7d4dc2ac7ad1ef27365ff599612e07e2312adf79" {
mode = "release"
}

if mode == "unsafe" && strings.HasPrefix(key.Description, "mkosi of ") {
mode = "dev"
}

slog.Info("Platform keyring entry", "name", key.Description, "key", key.Fingerprint)
}

slog.Info("Starting up", "mode", mode, "app", "incus")

// Fetch the system extensions.
gh := github.NewClient(nil)

release, _, err := gh.Repositories.GetLatestRelease(ctx, ghOrganization, ghRepository)
if err != nil {
return err
}

slog.Info(fmt.Sprintf("Found latest %s/%s release", ghOrganization, ghRepository), "tag", release.GetTagName())

assets, _, err := gh.Repositories.ListReleaseAssets(ctx, ghOrganization, ghRepository, release.GetID(), nil)
if err != nil {
return err
}

err = os.MkdirAll(osExtensionsPath, 0700)
if err != nil {
return err
}

for _, asset := range assets {
if !slices.Contains(osExtensions, asset.GetName()) {
continue
}

slog.Info("Downloading OS extension", "file", asset.GetName(), "url", asset.GetBrowserDownloadURL())

rc, _, err := gh.Repositories.DownloadReleaseAsset(ctx, ghOrganization, ghRepository, asset.GetID(), http.DefaultClient)
if err != nil {
return err
}

defer rc.Close()

body, err := gzip.NewReader(rc)
if err != nil {
return err
}

defer body.Close()

fd, err := os.Create(filepath.Join(osExtensionsPath, strings.TrimSuffix(asset.GetName(), ".gz")))
if err != nil {
return err
}

defer fd.Close()

_, err = io.Copy(fd, body)
if err != nil {
return err
}
}

// Apply the system extensions.
slog.Info("Refreshing system extensions")
err = systemd.RefreshExtensions(ctx)
if err != nil {
return err
}

// Apply the system users.
slog.Info("Refreshing users")
err = systemd.RefreshUsers(ctx)
if err != nil {
return err
}

// Enable and start Incus.
slog.Info("Starting Incus")
err = systemd.EnableUnit(ctx, true, "incus.socket", "incus-lxcfs.service", "incus-startup.service", "incus.service")
if err != nil {
return err
}

return nil
}
11 changes: 11 additions & 0 deletions incus-osd/go.mod
Original file line number Diff line number Diff line change
@@ -1,3 +1,14 @@
module github.com/lxc/incus-os/incus-osd

go 1.23.4

require (
github.com/google/go-github/v68 v68.0.0
github.com/lxc/incus/v6 v6.8.0
)

require (
github.com/google/go-querystring v1.1.0 // indirect
golang.org/x/sys v0.28.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
)
16 changes: 16 additions & 0 deletions incus-osd/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-github/v68 v68.0.0 h1:ZW57zeNZiXTdQ16qrDiZ0k6XucrxZ2CGmoTvcCyQG6s=
github.com/google/go-github/v68 v68.0.0/go.mod h1:K9HAUBovM2sLwM408A18h+wd9vqdLOEqTUCbnRIcx68=
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
github.com/lxc/incus/v6 v6.8.0 h1:SgAvmsRfzRYuwTsJbaLeX9xsJfAb6nckGOu1nXI8dcQ=
github.com/lxc/incus/v6 v6.8.0/go.mod h1:dVwGmKWdDJvFK4MzJsZGqFKUKalAKwPwc5q126VyMIQ=
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
57 changes: 57 additions & 0 deletions incus-osd/internal/keyring/keys.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package keyring

import (
"bufio"
"context"
"os"
"strings"
)

// PlatformKeyring is the SecureBoot platform keyring.
var PlatformKeyring = "1f010000"

// Key represents a key in the Linux kernel keyring.
type Key struct {
Description string
Fingerprint string
Type string
}

// GetKeys returns a list of keys in the requested keyring.
func GetKeys(ctx context.Context, keyring string) ([]Key, error) {
keys := []Key{}

// Read the key list.
fd, err := os.Open("/proc/keys")
if err != nil {
return nil, err
}

defer fd.Close()

// Iterate over the entries..
fdScan := bufio.NewScanner(fd)
for fdScan.Scan() {
fields := strings.Fields(fdScan.Text())

if len(fields) < 10 {
// Skipping invalid entries.
continue
}

if fields[4] != keyring {
// Skipping entries outside of the platform (SecureBoot) keyring.
continue
}

keyFields := strings.Split(strings.Join(fields[8:], " "), ": ")

keys = append(keys, Key{
Description: strings.Join(keyFields[0:len(keyFields)-2], ": "),
Fingerprint: keyFields[len(keyFields)-2],
Type: strings.Fields(keyFields[len(keyFields)-1])[0],
})
}

return keys, nil
}
16 changes: 16 additions & 0 deletions incus-osd/internal/systemd/sysext.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package systemd

import (
"context"

"github.com/lxc/incus/v6/shared/subprocess"
)

func RefreshExtensions(ctx context.Context) error {
_, err := subprocess.RunCommandContext(ctx, "systemd-sysext", "refresh")
if err != nil {
return err
}

return nil
}
24 changes: 24 additions & 0 deletions incus-osd/internal/systemd/systemctl.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package systemd

import (
"context"

"github.com/lxc/incus/v6/shared/subprocess"
)

func EnableUnit(ctx context.Context, now bool, units ...string) error {
args := []string{"enable"}

if now {
args = append(args, "--now")
}

args = append(args, units...)

_, err := subprocess.RunCommandContext(ctx, "systemctl", args...)
if err != nil {
return err
}

return nil
}
16 changes: 16 additions & 0 deletions incus-osd/internal/systemd/sysusers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package systemd

import (
"context"

"github.com/lxc/incus/v6/shared/subprocess"
)

func RefreshUsers(ctx context.Context) error {
_, err := subprocess.RunCommandContext(ctx, "systemd-sysusers")
if err != nil {
return err
}

return nil
}

0 comments on commit cc3a72e

Please sign in to comment.