Compare commits

...

8 Commits

Author SHA1 Message Date
Itxaka
6d6dfd00a1 🌱 Make some functions public
So they can be imported from different places as lib and re-used

Signed-off-by: Itxaka <itxaka.garcia@spectrocloud.com>
2023-04-19 14:44:33 +02:00
Mauro Morales
92c79f4e75 Merge pull request #15 from kairos-io/bump-go-version-to-1.20.2
⬆️ bump go version to 1.20.2
2023-03-30 09:10:18 +02:00
Mauro Morales
a254871c22 Remove something left from the merge conflict
Signed-off-by: Mauro Morales <mauro.morales@spectrocloud.com>
2023-03-30 09:06:11 +02:00
Mauro Morales
7e6a31f38b Add lint workflow
Signed-off-by: Mauro Morales <mauro.morales@spectrocloud.com>
2023-03-29 17:05:53 +02:00
Mauro Morales
0f3eee7851 Add golint
Signed-off-by: Mauro Morales <mauro.morales@spectrocloud.com>
2023-03-29 17:05:51 +02:00
Mauro Morales
15cc284978 Part of previous commit
Signed-off-by: Mauro Morales <mauro.morales@spectrocloud.com>
2023-03-29 17:00:06 +02:00
Mauro Morales
b7352829ff Add yamllint
Signed-off-by: Mauro Morales <mauro.morales@spectrocloud.com>
2023-03-29 17:00:06 +02:00
Mauro Morales
57aef89b02 ⬆️ bump go version to 1.20.2
Signed-off-by: Mauro Morales <mauro.morales@spectrocloud.com>
2023-03-29 16:59:56 +02:00
9 changed files with 202 additions and 114 deletions

28
.github/workflows/lint.yaml vendored Normal file
View File

@@ -0,0 +1,28 @@
name: Lint
on:
push:
branches:
- main
pull_request:
paths:
- '**'
env:
FORCE_COLOR: 1
jobs:
lint:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Install Go
uses: actions/setup-go@v4
- name: Install earthly
uses: Luet-lab/luet-install-action@v1
with:
repository: quay.io/kairos/packages
packages: utils/earthly
- name: Run Lint checks
run: |
earthly +lint

View File

@@ -1,9 +1,9 @@
name: Unit tests
on:
push:
branches:
- master
pull_request:
push:
branches:
- master
pull_request:
jobs:
unit-tests:
@@ -16,7 +16,7 @@ jobs:
- name: Install Go
uses: actions/setup-go@v3
with:
go-version: '^1.18'
go-version: '^1.20'
- name: Install Ginkgo
run: go install github.com/onsi/ginkgo/v2/ginkgo@v2.5.0

21
.yamllint Normal file
View File

@@ -0,0 +1,21 @@
extends: default
rules:
# 80 chars should be enough, but don't fail if a line is longer
line-length:
max: 150
level: warning
# accept both key:
# - item
#
# and key:
# - item
indentation:
indent-sequences: whatever
truthy:
check-keys: false
document-start:
present: false

View File

@@ -3,6 +3,9 @@ VERSION 0.6
# TODO: This needs to come from pre-built kernels in c3os repos, kcrypt included.
# Framework images should use our initrd
ARG BASE_IMAGE=quay.io/kairos/core-opensuse
# renovate: datasource=docker depName=golang
ARG GO_VERSION=1.20.2
ARG GOLINT_VERSION=1.52.2
build-kcrypt:
FROM golang:alpine
@@ -63,3 +66,21 @@ iso:
RUN sha256sum $ISO_NAME.iso > $ISO_NAME.iso.sha256
SAVE ARTIFACT /build/$ISO_NAME.iso iso AS LOCAL build/$ISO_NAME.iso
SAVE ARTIFACT /build/$ISO_NAME.iso.sha256 sha256 AS LOCAL build/$ISO_NAME.iso.sha256
lint:
BUILD +golint
BUILD +yamllint
golint:
ARG GO_VERSION
FROM golang:$GO_VERSION
ARG GOLINT_VERSION
RUN wget -O- -nv https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s v$GOLINT_VERSION
WORKDIR /build
COPY . .
RUN golangci-lint run
yamllint:
FROM cytopia/yamllint
COPY . .
RUN yamllint .github/workflows/

2
go.mod
View File

@@ -1,6 +1,6 @@
module github.com/kairos-io/kcrypt
go 1.18
go 1.20
require (
github.com/anatol/luks.go v0.0.0-20230125211543-ada2562d4206

113
main.go
View File

@@ -2,20 +2,16 @@ package main
import (
"fmt"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"strings"
"time"
luks "github.com/anatol/luks.go"
multierror "github.com/hashicorp/go-multierror"
"github.com/jaypipes/ghw"
"github.com/jaypipes/ghw/pkg/block"
"github.com/kairos-io/kcrypt/pkg/bus"
configpkg "github.com/kairos-io/kcrypt/pkg/config"
"github.com/mudler/go-pluggable"
"github.com/kairos-io/kcrypt/pkg/lib"
cp "github.com/otiai10/copy"
"github.com/urfave/cli"
)
@@ -24,8 +20,11 @@ var Version = "v0.0.0-dev"
func waitdevice(device string, attempts int) error {
for tries := 0; tries < attempts; tries++ {
sh("udevadm settle")
_, err := os.Lstat(device)
_, err := sh("udevadm settle")
if err != nil {
return err
}
_, err = os.Lstat(device)
if !os.IsNotExist(err) {
return nil
}
@@ -34,49 +33,6 @@ func waitdevice(device string, attempts int) error {
return fmt.Errorf("no device found")
}
// TODO: Ask to discovery a pass to unlock. keep waiting until we get it and a timeout is exhausted with retrials (exp backoff)
func getPassword(b *block.Partition) (password string, err error) {
bus.Reload()
bus.Manager.Response(bus.EventDiscoveryPassword, func(p *pluggable.Plugin, r *pluggable.EventResponse) {
password = r.Data
if r.Errored() {
err = fmt.Errorf("failed discovery: %s", r.Error)
}
})
bus.Manager.Publish(bus.EventDiscoveryPassword, b)
if password == "" {
return password, fmt.Errorf("received empty password")
}
return
}
func luksUnlock(device, mapper, password string) error {
dev, err := luks.Open(device)
if err != nil {
// handle error
return err
}
defer dev.Close()
err = dev.Unlock(0, []byte(password), mapper)
if err != nil {
return err
}
return nil
}
func unlockDisk(b *block.Partition) error {
pass, err := getPassword(b)
if err != nil {
return fmt.Errorf("error retreiving password remotely: %w", err)
}
return luksUnlock(fmt.Sprintf("/dev/%s", b.Name), b.Name, pass)
}
func createLuks(dev, password, version string, cryptsetupArgs ...string) error {
if version == "" {
version = "luks2"
@@ -95,19 +51,6 @@ func createLuks(dev, password, version string, cryptsetupArgs ...string) error {
return nil
}
func createDiskImage() (*os.File, error) {
disk, err := ioutil.TempFile("", "luksv2.go.disk")
if err != nil {
return nil, err
}
if err := disk.Truncate(24 * 1024 * 1024); err != nil {
return nil, err
}
return disk, err
}
// TODO: A crypt disk utility to call after install, that with discovery discoveries the password that should be used
// this function should delete COS_PERSISTENT. delete the partition and create a luks+type in place.
@@ -124,7 +67,7 @@ func luksify(label string) (string, error) {
return "", err
}
pass, err := getPassword(b)
pass, err := lib.GetPassword(b)
if err != nil {
return "", err
}
@@ -136,7 +79,7 @@ func luksify(label string) (string, error) {
return "", err
}
if err := luksUnlock(persistent, b.Name, pass); err != nil {
if err := lib.LuksUnlock(persistent, b.Name, pass); err != nil {
return "", err
}
@@ -210,9 +153,13 @@ func detect(archive string) (string, error) {
// TODO: replace with golang native code
func extractInitrd(initrd string, dst string) error {
os.MkdirAll(dst, os.ModePerm)
var out string
var err error
err = os.MkdirAll(dst, os.ModePerm)
if err != nil {
return err
}
format, err := detect(initrd)
if err != nil {
return err
@@ -256,7 +203,7 @@ func injectInitrd(initrd string, file, dst string) error {
if err != nil {
return err
}
tmp, err := ioutil.TempDir("", "kcrypt")
tmp, err := os.MkdirTemp("", "kcrypt")
if err != nil {
return fmt.Errorf("cannot create tempdir, %s", err)
}
@@ -277,36 +224,6 @@ func injectInitrd(initrd string, file, dst string) error {
}
// TODO: a custom toolkit version, to build out initrd pre-built with this component
func unlockAll() error {
bus.Manager.Initialize()
config, err := configpkg.GetConfiguration(configpkg.ConfigScanDirs)
if err != nil {
fmt.Printf("Warning: Could not read kcrypt configuration '%s'\n", err.Error())
}
block, err := ghw.Block()
if err != nil {
fmt.Printf("Warning: Error reading partitions '%s \n", err.Error())
return nil
}
for _, disk := range block.Disks {
for _, p := range disk.Partitions {
if p.Type == "crypto_LUKS" {
p.Label = config.LookupLabelForUUID(p.UUID)
fmt.Printf("Unmounted Luks found at '%s' LABEL '%s' \n", p.Name, p.Label)
err = multierror.Append(err, unlockDisk(p))
if err != nil {
fmt.Printf("Unlocking failed: '%s'\n", err.Error())
}
time.Sleep(10 * time.Second)
}
}
}
return nil
}
func main() {
app := &cli.App{
@@ -367,7 +284,7 @@ Typically run during initrd to unlock all the LUKS partitions found
&cli.StringFlag{},
},
Action: func(c *cli.Context) error {
return unlockAll()
return lib.UnlockAll()
},
},
},

View File

@@ -4,7 +4,7 @@ package config
import (
"fmt"
"io/ioutil"
"os"
"strings"
"github.com/jaypipes/ghw/pkg/block"
@@ -97,7 +97,7 @@ func (c *Config) WriteMappings(fileName string) error {
data = append([]byte(collector.DefaultHeader+"\n"), data...)
err = ioutil.WriteFile(fileName, data, 0744)
err = os.WriteFile(fileName, data, 0744)
if err != nil {
return errors.Wrap(err, "writing the kcrypt configuration file")
}

View File

@@ -124,8 +124,10 @@ kcrypt:
})
It("replaces the file contents", func() {
c.SetMapping("COS_PERSISTENT:the_new_name:the_new_uuid")
c.WriteMappings(tmpFile.Name())
err := c.SetMapping("COS_PERSISTENT:the_new_name:the_new_uuid")
Expect(err).ToNot(HaveOccurred())
err = c.WriteMappings(tmpFile.Name())
Expect(err).ToNot(HaveOccurred())
data, err := os.ReadFile(tmpFile.Name())
Expect(err).ToNot(HaveOccurred())
Expect(collector.HasValidHeader(string(data))).To(BeTrue())
@@ -145,8 +147,9 @@ kcrypt:
})
It("creates the file with the given mappings", func() {
c.SetMapping("COS_PERSISTENT:the_new_name:the_new_uuid")
err := c.WriteMappings(tmpFile.Name())
err := c.SetMapping("COS_PERSISTENT:the_new_name:the_new_uuid")
Expect(err).ToNot(HaveOccurred())
err = c.WriteMappings(tmpFile.Name())
Expect(err).ToNot(HaveOccurred())
newConfig, err := configpkg.GetConfiguration([]string{tmpDir})
@@ -164,8 +167,10 @@ kcrypt:
tmpFile, err = os.CreateTemp(tmpDir, "config-*.yaml")
Expect(err).ToNot(HaveOccurred())
// Should trim the whitespace
c.SetMapping("COS_PERSISTENT:the_new_name:some_uuid_1\n")
c.WriteMappings(tmpFile.Name())
err = c.SetMapping("COS_PERSISTENT:the_new_name:some_uuid_1\n")
Expect(err).ToNot(HaveOccurred())
err = c.WriteMappings(tmpFile.Name())
Expect(err).ToNot(HaveOccurred())
})
It("returns the correct UUID", func() {
@@ -192,8 +197,10 @@ kcrypt:
BeforeEach(func() {
tmpFile, err = os.CreateTemp(tmpDir, "config-*.yaml")
Expect(err).ToNot(HaveOccurred())
c.SetMapping("COS_PERSISTENT:the_new_name:some_uuid_1")
c.WriteMappings(tmpFile.Name())
err = c.SetMapping("COS_PERSISTENT:the_new_name:some_uuid_1")
Expect(err).ToNot(HaveOccurred())
err = c.WriteMappings(tmpFile.Name())
Expect(err).ToNot(HaveOccurred())
})
It("returns the correct label", func() {

94
pkg/lib/unlock.go Normal file
View File

@@ -0,0 +1,94 @@
package lib
import (
"fmt"
"time"
"github.com/anatol/luks.go"
"github.com/hashicorp/go-multierror"
"github.com/jaypipes/ghw"
"github.com/jaypipes/ghw/pkg/block"
"github.com/kairos-io/kcrypt/pkg/bus"
configpkg "github.com/kairos-io/kcrypt/pkg/config"
"github.com/mudler/go-pluggable"
)
// UnlockAll Unlocks all encrypted devices found in the system
func UnlockAll() error {
bus.Manager.Initialize()
config, err := configpkg.GetConfiguration(configpkg.ConfigScanDirs)
if err != nil {
fmt.Printf("Warning: Could not read kcrypt configuration '%s'\n", err.Error())
}
blk, err := ghw.Block()
if err != nil {
fmt.Printf("Warning: Error reading partitions '%s \n", err.Error())
return nil
}
for _, disk := range blk.Disks {
for _, p := range disk.Partitions {
if p.Type == "crypto_LUKS" {
p.Label = config.LookupLabelForUUID(p.UUID)
fmt.Printf("Unmounted Luks found at '%s' LABEL '%s' \n", p.Name, p.Label)
multiError := multierror.Append(err, UnlockDisk(p))
if multiError.ErrorOrNil() != nil {
fmt.Printf("Unlocking failed: '%s'\n", err.Error())
}
time.Sleep(10 * time.Second)
}
}
}
return nil
}
// UnlockDisk unlocks a single block.Partition
func UnlockDisk(b *block.Partition) error {
pass, err := GetPassword(b)
if err != nil {
return fmt.Errorf("error retreiving password remotely: %w", err)
}
return LuksUnlock(fmt.Sprintf("/dev/%s", b.Name), b.Name, pass)
}
// GetPassword gets the password for a block.Partition
// TODO: Ask to discovery a pass to unlock. keep waiting until we get it and a timeout is exhausted with retrials (exp backoff)
func GetPassword(b *block.Partition) (password string, err error) {
bus.Reload()
bus.Manager.Response(bus.EventDiscoveryPassword, func(p *pluggable.Plugin, r *pluggable.EventResponse) {
password = r.Data
if r.Errored() {
err = fmt.Errorf("failed discovery: %s", r.Error)
}
})
_, err = bus.Manager.Publish(bus.EventDiscoveryPassword, b)
if err != nil {
return password, err
}
if password == "" {
return password, fmt.Errorf("received empty password")
}
return
}
func LuksUnlock(device, mapper, password string) error {
dev, err := luks.Open(device)
if err != nil {
// handle error
return err
}
defer dev.Close()
err = dev.Unlock(0, []byte(password), mapper)
if err != nil {
return err
}
return nil
}