Skip to content

Commit

Permalink
add: Support LUKS encryption using IBM CEX secure keys on s390x
Browse files Browse the repository at this point in the history
  • Loading branch information
madhu-pillai committed May 29, 2024
1 parent 97b9123 commit 23b740a
Show file tree
Hide file tree
Showing 12 changed files with 127 additions and 27 deletions.
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
1 change: 1 addition & 0 deletions config/common/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ 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")
ErrCexUnSupportArch = errors.New("cex does not supported architectures other than s390x")

// 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 @@ -33,6 +33,7 @@ type BootDevice struct {
type BootDeviceLuks struct {
Discard *bool `yaml:"discard"`
Device *string `yaml:"device"`
Enabled *bool `yaml:"enabled"`
Tang []base.Tang `yaml:"tang"`
Threshold *int `yaml:"threshold"`
Tpm2 *bool `yaml:"tpm2"`
Expand Down
74 changes: 55 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.Enabled)
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.Enabled) {
cex, ts2, r2 := translateBootDeviceLuksCex(c.BootDevice.Luks, options)
rendered.Storage.Luks = []types.Luks{{
Cex: cex,

Check failure on line 258 in config/fcos/v1_6_exp/translate.go

View workflow job for this annotation

GitHub Actions / Regenerate

unknown field Cex in struct literal of type struct{Clevis types.Clevis "json:\"clevis,omitempty\""; Device *string "json:\"device,omitempty\""; Discard *bool "json:\"discard,omitempty\""; KeyFile types.Resource "json:\"keyFile,omitempty\""; Label *string "json:\"label,omitempty\""; Name string "json:\"name\""; OpenOptions []types.OpenOption "json:\"openOptions,omitempty\""; Options []types.LuksOption "json:\"options,omitempty\""; UUID *string "json:\"uuid,omitempty\""; WipeVolume *bool "json:\"wipeVolume,omitempty\""}

Check failure on line 258 in config/fcos/v1_6_exp/translate.go

View workflow job for this annotation

GitHub Actions / Test (1.20.x, ubuntu-latest)

unknown field Cex in struct literal of type struct{Clevis types.Clevis "json:\"clevis,omitempty\""; Device *string "json:\"device,omitempty\""; Discard *bool "json:\"discard,omitempty\""; KeyFile types.Resource "json:\"keyFile,omitempty\""; Label *string "json:\"label,omitempty\""; Name string "json:\"name\""; OpenOptions []types.OpenOption "json:\"openOptions,omitempty\""; Options []types.LuksOption "json:\"options,omitempty\""; UUID *string "json:\"uuid,omitempty\""; WipeVolume *bool "json:\"wipeVolume,omitempty\""}

Check failure on line 258 in config/fcos/v1_6_exp/translate.go

View workflow job for this annotation

GitHub Actions / Test (1.21.x, ubuntu-latest)

unknown field Cex in struct literal of type struct{Clevis types.Clevis "json:\"clevis,omitempty\""; Device *string "json:\"device,omitempty\""; Discard *bool "json:\"discard,omitempty\""; KeyFile types.Resource "json:\"keyFile,omitempty\""; Label *string "json:\"label,omitempty\""; Name string "json:\"name\""; OpenOptions []types.OpenOption "json:\"openOptions,omitempty\""; Options []types.LuksOption "json:\"options,omitempty\""; UUID *string "json:\"uuid,omitempty\""; WipeVolume *bool "json:\"wipeVolume,omitempty\""}

Check failure on line 258 in config/fcos/v1_6_exp/translate.go

View workflow job for this annotation

GitHub Actions / Test (1.21.x, macos-latest)

unknown field Cex in struct literal of type struct{Clevis types.Clevis "json:\"clevis,omitempty\""; Device *string "json:\"device,omitempty\""; Discard *bool "json:\"discard,omitempty\""; KeyFile types.Resource "json:\"keyFile,omitempty\""; Label *string "json:\"label,omitempty\""; Name string "json:\"name\""; OpenOptions []types.OpenOption "json:\"openOptions,omitempty\""; Options []types.LuksOption "json:\"options,omitempty\""; UUID *string "json:\"uuid,omitempty\""; WipeVolume *bool "json:\"wipeVolume,omitempty\""}
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,20 @@ func translateBootDeviceLuks(from BootDeviceLuks, options common.TranslateOption
return
}

func translateBootDeviceLuksCex(from BootDeviceLuks, options common.TranslateOptions) (to types.Cex, tm translate.TranslationSet, r report.Report) {

Check failure on line 342 in config/fcos/v1_6_exp/translate.go

View workflow job for this annotation

GitHub Actions / Regenerate

undefined: types.Cex

Check failure on line 342 in config/fcos/v1_6_exp/translate.go

View workflow job for this annotation

GitHub Actions / Test (1.20.x, ubuntu-latest)

undefined: types.Cex

Check failure on line 342 in config/fcos/v1_6_exp/translate.go

View workflow job for this annotation

GitHub Actions / Test (1.21.x, ubuntu-latest)

undefined: types.Cex

Check failure on line 342 in config/fcos/v1_6_exp/translate.go

View workflow job for this annotation

GitHub Actions / Test (1.21.x, macos-latest)

undefined: types.Cex
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.Enabled, &to.Enabled)
//translate.MergeP(tr, tm, &r, "threshold", &from.Threshold, &to.Threshold)
translate.MergeP(tr, tm, &r, "enabled", &from.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.Enabled) {
r.AddOnError(c.Append(*layout), common.ErrCexUnSupportArch)
}
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.Enabled) && (len(d.Luks.Tang) > 0 || util.IsTrue(d.Luks.Tpm2)) {
r.AddOnError(c.Append("luks"), errors.ErrCexWithClevis)

Check failure on line 86 in config/fcos/v1_6_exp/validate.go

View workflow job for this annotation

GitHub Actions / Regenerate

undefined: errors.ErrCexWithClevis

Check failure on line 86 in config/fcos/v1_6_exp/validate.go

View workflow job for this annotation

GitHub Actions / Test (1.20.x, ubuntu-latest)

undefined: errors.ErrCexWithClevis

Check failure on line 86 in config/fcos/v1_6_exp/validate.go

View workflow job for this annotation

GitHub Actions / Test (1.21.x, ubuntu-latest)

undefined: errors.ErrCexWithClevis

Check failure on line 86 in config/fcos/v1_6_exp/validate.go

View workflow job for this annotation

GitHub Actions / Test (1.21.x, macos-latest)

undefined: errors.ErrCexWithClevis
}
}

if layout == nil && util.IsTrue(d.Luks.Enabled) {
r.AddOnError(c.Append("cex"), common.ErrCexUnSupportArch)
}
r.Merge(d.Mirror.Validate(c.Append("mirror")))
return
Expand Down
12 changes: 12 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,18 @@ 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"),
Enabled: util.BoolToPtr(true),
},
},
nil,
path.New("yaml"),
},
// invalid layout
{
BootDevice{
Expand Down
3 changes: 3 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,7 @@ 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.
* **_enabled_** (boolean): UnSupported
* **_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
2 changes: 2 additions & 0 deletions docs/config-flatcar-v1_2-exp.md
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,8 @@ The Flatcar configuration is a YAML document conforming to the following specifi
* **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
3 changes: 3 additions & 0 deletions docs/config-openshift-v4_16-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,7 @@ 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.
* **_enabled_** (boolean): UnSupported
* **_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
13 changes: 13 additions & 0 deletions docs/examples.md
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,19 @@ 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
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
1 change: 1 addition & 0 deletions docs/release-notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ nav_order: 9
### Features

- Support `storage.luks.clevis` (flatcar 1.2.0-exp)
- Support LUKS encryption using IBM CEX secure keys on s390x

### Bug fixes

Expand Down
10 changes: 10 additions & 0 deletions internal/doc/butane.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,16 @@ 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: enabled
desc: whether or not to use a cex device.
transforms:
- regex: ".*"
replacement: "UnSupported"
if:
- variant: fcos
min: 1.6.0-experimental
- variant: openshift
min: 4.16.0-experimental
- name: mirror
desc: describes mirroring of the boot disk for fault tolerance.
children:
Expand Down

0 comments on commit 23b740a

Please sign in to comment.