Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add: Support LUKS encryption using IBM CEX secure keys on s390x #536

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions base/v0_6_exp/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@

package v0_6_exp

type Cex struct {
Enabled *bool `yaml:"enabled"`
}

type Clevis struct {
Custom ClevisCustom `yaml:"custom"`
Tang []Tang `yaml:"tang"`
Expand Down Expand Up @@ -119,6 +123,7 @@ type Link struct {
}

type Luks struct {
Cex Cex `yaml:"cex"`
Clevis Clevis `yaml:"clevis"`
Device *string `yaml:"device"`
Discard *bool `yaml:"discard"`
Expand Down
2 changes: 2 additions & 0 deletions config/common/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ var (
ErrNoLuksBootDevice = errors.New("device is required for layouts: s390x-eckd, s390x-zfcp")
ErrMirrorNotSupport = errors.New("mirroring not supported on layouts: s390x-eckd, s390x-zfcp, s390x-virt")
ErrLuksBootDeviceBadName = errors.New("device name must start with /dev/dasd on s390x-eckd layout or /dev/sd on s390x-zfcp layout")
ErrCexArchitectureMismatch = errors.New("when using cex the targeted architecture must match s390x")
ErrCexNotSupported = errors.New("cex is not currently supported on the target platform")

// partition
ErrReuseByLabel = errors.New("partitions cannot be reused by label; number must be specified except on boot disk (/dev/disk/by-id/coreos-boot-disk) or when wipe_table is true")
Expand Down
1 change: 1 addition & 0 deletions config/fcos/v1_6_exp/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ type BootDevice struct {
}

type BootDeviceLuks struct {
Cex base.Cex `yaml:"cex"`
Discard *bool `yaml:"discard"`
Device *string `yaml:"device"`
Tang []base.Tang `yaml:"tang"`
Expand Down
73 changes: 54 additions & 19 deletions config/fcos/v1_6_exp/translate.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ func (c Config) processBootDevice(config *types.Config, ts *translate.Translatio
var r report.Report

// check for high-level features
wantLuks := util.IsTrue(c.BootDevice.Luks.Tpm2) || len(c.BootDevice.Luks.Tang) > 0
wantLuks := util.IsTrue(c.BootDevice.Luks.Tpm2) || len(c.BootDevice.Luks.Tang) > 0 || util.IsTrue(c.BootDevice.Luks.Cex.Enabled)
Copy link
Contributor

Choose a reason for hiding this comment

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

This is getting long, not sure if there is anything to do to fix this.. but yeah.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added this feature similar to the other two luks method.

wantMirror := len(c.BootDevice.Mirror.Devices) > 0
if !wantLuks && !wantMirror {
return r
Expand Down Expand Up @@ -252,25 +252,47 @@ func (c Config) processBootDevice(config *types.Config, ts *translate.Translatio
default:
luksDevice = "/dev/disk/by-partlabel/root"
}
clevis, ts2, r2 := translateBootDeviceLuks(c.BootDevice.Luks, options)
rendered.Storage.Luks = []types.Luks{{
Clevis: clevis,
Device: &luksDevice,
Discard: c.BootDevice.Luks.Discard,
Label: util.StrToPtr("luks-root"),
Name: "root",
WipeVolume: util.BoolToPtr(true),
}}
lpath := path.New("yaml", "boot_device", "luks")
rpath := path.New("json", "storage", "luks", 0)
renderedTranslations.Merge(ts2.PrefixPaths(lpath, rpath.Append("clevis")))
renderedTranslations.AddTranslation(lpath.Append("discard"), rpath.Append("discard"))
for _, f := range []string{"device", "label", "name", "wipeVolume"} {
renderedTranslations.AddTranslation(lpath, rpath.Append(f))
if util.IsTrue(c.BootDevice.Luks.Cex.Enabled) {
cex, ts2, r2 := translateBootDeviceLuksCex(c.BootDevice.Luks, options)
rendered.Storage.Luks = []types.Luks{{
Cex: cex,
Device: &luksDevice,
Discard: c.BootDevice.Luks.Discard,
Label: util.StrToPtr("luks-root"),
Name: "root",
WipeVolume: util.BoolToPtr(true),
}}
lpath := path.New("yaml", "boot_device", "luks")
rpath := path.New("json", "storage", "luks", 0)
renderedTranslations.Merge(ts2.PrefixPaths(lpath, rpath.Append("cex")))
renderedTranslations.AddTranslation(lpath.Append("discard"), rpath.Append("discard"))
for _, f := range []string{"device", "label", "name", "wipeVolume"} {
renderedTranslations.AddTranslation(lpath, rpath.Append(f))
}
renderedTranslations.AddTranslation(lpath, rpath)
renderedTranslations.AddTranslation(lpath, path.New("json", "storage", "luks"))
r.Merge(r2)
} else {
clevis, ts2, r2 := translateBootDeviceLuks(c.BootDevice.Luks, options)
rendered.Storage.Luks = []types.Luks{{
Clevis: clevis,
Device: &luksDevice,
Discard: c.BootDevice.Luks.Discard,
Label: util.StrToPtr("luks-root"),
Name: "root",
WipeVolume: util.BoolToPtr(true),
}}
lpath := path.New("yaml", "boot_device", "luks")
rpath := path.New("json", "storage", "luks", 0)
renderedTranslations.Merge(ts2.PrefixPaths(lpath, rpath.Append("clevis")))
renderedTranslations.AddTranslation(lpath.Append("discard"), rpath.Append("discard"))
for _, f := range []string{"device", "label", "name", "wipeVolume"} {
renderedTranslations.AddTranslation(lpath, rpath.Append(f))
}
renderedTranslations.AddTranslation(lpath, rpath)
renderedTranslations.AddTranslation(lpath, path.New("json", "storage", "luks"))
r.Merge(r2)
}
renderedTranslations.AddTranslation(lpath, rpath)
renderedTranslations.AddTranslation(lpath, path.New("json", "storage", "luks"))
r.Merge(r2)
}

// create root filesystem
Expand Down Expand Up @@ -317,6 +339,19 @@ func translateBootDeviceLuks(from BootDeviceLuks, options common.TranslateOption
return
}

func translateBootDeviceLuksCex(from BootDeviceLuks, options common.TranslateOptions) (to types.Cex, tm translate.TranslationSet, r report.Report) {
tr := translate.NewTranslator("yaml", "json", options)
// Discard field is handled by the caller because it doesn't go
// into types.Cex
tm, r = translate.Prefixed(tr, "enabled", &from.Cex.Enabled, &to.Enabled)
translate.MergeP(tr, tm, &r, "enabled", &from.Cex.Enabled, &to.Enabled)
// we're being called manually, not via the translate package's
// custom translator mechanism, so we have to add the base
// translation ourselves
tm.AddTranslation(path.New("yaml"), path.New("json"))
return
}

func (c Config) handleUserGrubCfg(options common.TranslateOptions) (types.Config, translate.TranslationSet, report.Report) {
rendered := types.Config{}
ts := translate.NewTranslationSet("yaml", "json")
Expand Down
29 changes: 21 additions & 8 deletions config/fcos/v1_6_exp/validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"regexp"

"github.com/coreos/butane/config/common"
"github.com/coreos/ignition/v2/config/shared/errors"
"github.com/coreos/ignition/v2/config/util"

"github.com/coreos/vcontext/path"
Expand Down Expand Up @@ -51,31 +52,43 @@ func (conf Config) Validate(c path.ContextPath) (r report.Report) {
}

func (d BootDevice) Validate(c path.ContextPath) (r report.Report) {
if d.Layout != nil {
switch *d.Layout {
var layout = d.Layout
if layout != nil {
switch *layout {
case "aarch64", "ppc64le", "x86_64":
if util.IsTrue(d.Luks.Cex.Enabled) {
r.AddOnError(c.Append(*layout), common.ErrCexArchitectureMismatch)
}
case "s390x-eckd":
if util.NilOrEmpty(d.Luks.Device) {
r.AddOnError(c.Append(*d.Layout), common.ErrNoLuksBootDevice)
r.AddOnError(c.Append(*layout), common.ErrNoLuksBootDevice)
} else if !dasdRe.MatchString(*d.Luks.Device) {
r.AddOnError(c.Append(*d.Layout), common.ErrLuksBootDeviceBadName)
r.AddOnError(c.Append(*layout), common.ErrLuksBootDeviceBadName)
}
case "s390x-zfcp":
if util.NilOrEmpty(d.Luks.Device) {
r.AddOnError(c.Append(*d.Layout), common.ErrNoLuksBootDevice)
r.AddOnError(c.Append(*layout), common.ErrNoLuksBootDevice)
} else if !sdRe.MatchString(*d.Luks.Device) {
r.AddOnError(c.Append(*d.Layout), common.ErrLuksBootDeviceBadName)
r.AddOnError(c.Append(*layout), common.ErrLuksBootDeviceBadName)
}
case "s390x-virt":
default:
r.AddOnError(c.Append("layout"), common.ErrUnknownBootDeviceLayout)
}

if *d.Layout == "s390x-eckd" || *d.Layout == "s390x-zfcp" || *d.Layout == "s390x-virt" {
if *layout == "s390x-eckd" || *layout == "s390x-zfcp" || *layout == "s390x-virt" {
if len(d.Mirror.Devices) > 0 {
r.AddOnError(c.Append(*d.Layout), common.ErrMirrorNotSupport)
r.AddOnError(c.Append(*layout), common.ErrMirrorNotSupport)
}
}

if util.IsTrue(d.Luks.Cex.Enabled) && (len(d.Luks.Tang) > 0 || util.IsTrue(d.Luks.Tpm2)) {
r.AddOnError(c.Append("luks"), errors.ErrCexWithClevis)
}
}

if layout == nil && util.IsTrue(d.Luks.Cex.Enabled) {
r.AddOnError(c.Append("cex"), common.ErrCexArchitectureMismatch)
}
r.Merge(d.Mirror.Validate(c.Append("mirror")))
return
Expand Down
14 changes: 14 additions & 0 deletions config/fcos/v1_6_exp/validate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,20 @@ func TestValidateBootDevice(t *testing.T) {
nil,
path.New("yaml"),
},
// complete config with cex
{
BootDevice{
Layout: util.StrToPtr("s390x-eckd"),
Luks: BootDeviceLuks{
Device: util.StrToPtr("/dev/dasda"),
Cex: base.Cex{
Enabled: util.BoolToPtr(true),
},
},
},
nil,
path.New("yaml"),
},
// invalid layout
{
BootDevice{
Expand Down
8 changes: 7 additions & 1 deletion config/flatcar/v1_2_exp/translate.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,15 @@ import (
"github.com/coreos/vcontext/report"
)

var (
fieldFilters = cutil.NewFilters(types.Config{}, cutil.FilterMap{
prestist marked this conversation as resolved.
Show resolved Hide resolved
"storage.luks.cex": common.ErrCexNotSupported,
})
)

// Return FieldFilters for this spec.
func (c Config) FieldFilters() *cutil.FieldFilters {
return nil
return &fieldFilters
}

// ToIgn3_5 translates the config to an Ignition config. It returns a
Expand Down
4 changes: 4 additions & 0 deletions docs/config-fcos-v1_6-exp.md
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,8 @@ The Fedora CoreOS configuration is a YAML document conforming to the following s
* **pin** (string): the clevis pin.
* **config** (string): the clevis configuration JSON.
* **_needs_network_** (boolean): whether or not the device requires networking.
* **_cex_** (object): describes the IBM Crypto Express (CEX) card configuration for the luks device.
* **_enabled_** (boolean): whether or not to use a CEX secure key to encrypt the luks device.
* **_trees_** (list of objects): a list of local directory trees to be embedded in the config. Ownership is not preserved. File modes are set to 0755 if the local file is executable or 0644 otherwise. Attributes of files, directories, and symlinks can be overridden by creating a corresponding entry in the `files`, `directories`, or `links` section; such `files` entries must omit `contents` and such `links` entries must omit `target`.
* **local** (string): the base of the local directory tree, relative to the directory specified by the `--files-dir` command-line argument.
* **_path_** (string): the path of the tree within the target system. Defaults to `/`.
Expand Down Expand Up @@ -219,6 +221,8 @@ The Fedora CoreOS configuration is a YAML document conforming to the following s
* **_tpm2_** (boolean): whether or not to use a tpm2 device.
* **_threshold_** (integer): sets the minimum number of pieces required to decrypt the device. Default is 1.
* **_discard_** (boolean): whether to issue discard commands to the underlying block device when blocks are freed. Enabling this improves performance and device longevity on SSDs and space utilization on thinly provisioned SAN devices, but leaks information about which disk blocks contain data. If omitted, it defaults to false.
* **_cex_** (object): describes the IBM Crypto Express (CEX) card configuration for the luks device.
* **_enabled_** (boolean): whether or not to enable cex compatibility for luks. If omitted, defaults to false.
* **_mirror_** (object): describes mirroring of the boot disk for fault tolerance.
* **_devices_** (list of strings): the list of whole-disk devices (not partitions) to include in the disk array, referenced by their absolute path. At least two devices must be specified.
* **_grub_** (object): describes the desired GRUB bootloader configuration.
Expand Down
4 changes: 4 additions & 0 deletions docs/config-openshift-v4_17-exp.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,8 @@ The OpenShift configuration is a YAML document conforming to the following speci
* **pin** (string): the clevis pin.
* **config** (string): the clevis configuration JSON.
* **_needs_network_** (boolean): whether or not the device requires networking.
* **_cex_** (object): describes the IBM Crypto Express (CEX) card configuration for the luks device.
* **_enabled_** (boolean): whether or not to use a CEX secure key to encrypt the luks device.
* **_trees_** (list of objects): a list of local directory trees to be embedded in the config. Symlinks must not be present. Ownership is not preserved. File modes are set to 0755 if the local file is executable or 0644 otherwise. File attributes can be overridden by creating a corresponding entry in the `files` section; such entries must omit `contents`.
* **local** (string): the base of the local directory tree, relative to the directory specified by the `--files-dir` command-line argument.
* **_path_** (string): the path of the tree within the target system. Defaults to `/`.
Expand Down Expand Up @@ -168,6 +170,8 @@ The OpenShift configuration is a YAML document conforming to the following speci
* **_tpm2_** (boolean): whether or not to use a tpm2 device.
* **_threshold_** (integer): sets the minimum number of pieces required to decrypt the device. Default is 1.
* **_discard_** (boolean): whether to issue discard commands to the underlying block device when blocks are freed. Enabling this improves performance and device longevity on SSDs and space utilization on thinly provisioned SAN devices, but leaks information about which disk blocks contain data. If omitted, it defaults to false.
* **_cex_** (object): describes the IBM Crypto Express (CEX) card configuration for the luks device.
* **_enabled_** (boolean): whether or not to enable cex compatibility for luks. If omitted, defaults to false.
* **_mirror_** (object): describes mirroring of the boot disk for fault tolerance.
* **_devices_** (list of strings): the list of whole-disk devices (not partitions) to include in the disk array, referenced by their absolute path. At least two devices must be specified.
* **_grub_** (object): describes the desired GRUB bootloader configuration.
Expand Down
14 changes: 14 additions & 0 deletions docs/examples.md
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,20 @@ boot_device:
thumbprint: REPLACE-THIS-WITH-YOUR-TANG-THUMBPRINT
```

This example uses the shortcut `boot_device` syntax to configure an encrypted root filesystem in s390x on the `dasda` DASD device unlocked with a CEX card.

<!-- butane-config -->
```yaml
variant: fcos
version: 1.6.0-experimental
boot_device:
layout: s390x-eckd
luks:
device: /dev/dasda
cex:
Copy link
Contributor

@prestist prestist Jul 15, 2024

Choose a reason for hiding this comment

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

So we have a 3d object, with only two dementions ? (on and off)

Are there plans for adding other fields to the cex object?

if not we can we make it 2d? i.e cex: bool

Copy link
Contributor Author

Choose a reason for hiding this comment

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

There were few s390x specific parameter that can be selected under cex type. But later those parameter added as default and hardcoded in the ignition and decided that we can add in future if there is a requirement eg CIPHER. That's why the cex.enabled.

#536 (comment)

Copy link
Contributor

Choose a reason for hiding this comment

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

Ah, ok, nothing to push on if everyone else is okay with it.

enabled: true
```

### Mirrored boot disk

This example replicates all default partitions on the boot disk across multiple disks, allowing the system to survive disk failure.
Expand Down
17 changes: 17 additions & 0 deletions docs/release-notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,23 @@ nav_order: 9
## Upcoming Butane 0.22.0 (unreleased)


### Breaking changes


### Features

- Support LUKS encryption using IBM CEX secure keys on s390x

### Bug fixes


## Misc. changes


### Docs changes



## Butane 0.21.0 (2024-06-06)

Starting with this release, Butane binaries are signed with the [Fedora 40
Expand Down
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ require (
github.com/clarketm/json v1.17.1
github.com/coreos/go-semver v0.3.1
github.com/coreos/go-systemd/v22 v22.5.0
github.com/coreos/ignition/v2 v2.18.0
github.com/coreos/ignition/v2 v2.19.0
github.com/coreos/vcontext v0.0.0-20230201181013-d72178a18687
github.com/spf13/pflag v1.0.6-0.20210604193023-d5e0c0615ace
github.com/stretchr/testify v1.9.0
Expand All @@ -15,7 +15,7 @@ require (
)

require (
github.com/aws/aws-sdk-go v1.50.25 // indirect
github.com/aws/aws-sdk-go v1.53.5 // indirect
github.com/coreos/go-json v0.0.0-20230131223807-18775e0fb4fb // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/kr/pretty v0.3.1 // indirect
Expand Down
8 changes: 4 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
github.com/aws/aws-sdk-go v1.50.25 h1:vhiHtLYybv1Nhx3Kv18BBC6L0aPJHaG9aeEsr92W99c=
github.com/aws/aws-sdk-go v1.50.25/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk=
github.com/aws/aws-sdk-go v1.53.5 h1:1OcVWMjGlwt7EU5OWmmEEXqaYfmX581EK317QJZXItM=
github.com/aws/aws-sdk-go v1.53.5/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk=
github.com/clarketm/json v1.17.1 h1:U1IxjqJkJ7bRK4L6dyphmoO840P6bdhPdbbLySourqI=
github.com/clarketm/json v1.17.1/go.mod h1:ynr2LRfb0fQU34l07csRNBTcivjySLLiY1YzQqKVfdo=
github.com/coreos/go-json v0.0.0-20230131223807-18775e0fb4fb h1:rmqyI19j3Z/74bIRhuC59RB442rXUazKNueVpfJPxg4=
Expand All @@ -8,8 +8,8 @@ github.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr
github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03VsM8rvUec=
github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs=
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/coreos/ignition/v2 v2.18.0 h1:sPSGGsxaCuFMpKOMBQ71I9RIR20SIF4dWnoTomcPEYQ=
github.com/coreos/ignition/v2 v2.18.0/go.mod h1:TURPHDqWUWTmej8c+CEMBENMU3N/Lt6GfreHJuoDMbA=
github.com/coreos/ignition/v2 v2.19.0 h1:ek200E31M1NCVyvL22Bd40kOJp7yt1gdHAb3xwqTi8Y=
github.com/coreos/ignition/v2 v2.19.0/go.mod h1:ydb815SaH9A4304wIUoCS5IHyKRHWEp7dfJH8cQW2gA=
github.com/coreos/vcontext v0.0.0-20230201181013-d72178a18687 h1:uSmlDgJGbUB0bwQBcZomBTottKwEDF5fF8UjSwKSzWM=
github.com/coreos/vcontext v0.0.0-20230201181013-d72178a18687/go.mod h1:Salmysdw7DAVuobBW/LwsKKgpyCPHUhjyJoMJD+ZJiI=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
Expand Down
5 changes: 5 additions & 0 deletions internal/doc/butane.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,11 @@ root:
desc: sets the minimum number of pieces required to decrypt the device. Default is 1.
- name: discard
desc: whether to issue discard commands to the underlying block device when blocks are freed. Enabling this improves performance and device longevity on SSDs and space utilization on thinly provisioned SAN devices, but leaks information about which disk blocks contain data. If omitted, it defaults to false.
- name: cex
desc: describes the IBM Crypto Express (CEX) card configuration for the luks device.
children:
- name: enabled
Copy link
Member

Choose a reason for hiding this comment

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

Missing the description here

Copy link
Contributor Author

Choose a reason for hiding this comment

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

HI @travier , Oh, My Bad, I verified only from the docs side. Now i've added the description.

desc: whether or not to enable cex compatibility for luks. If omitted, defaults to false.
- name: mirror
desc: describes mirroring of the boot disk for fault tolerance.
children:
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading