From 5c65915211b9379d33ea40c44625a4246dfdb80d Mon Sep 17 00:00:00 2001 From: Stephen Lowrie Date: Thu, 16 Jul 2020 00:30:49 -0500 Subject: [PATCH 1/3] config: add custom options for clevis devices Adds new custom options that allow for the direct specification of the clevis pin & configuration JSON that will be passed to `clevis luks bind`. --- config/shared/errors/errors.go | 3 ++ config/v3_2_experimental/schema/ignition.json | 18 ++++++++ config/v3_2_experimental/types/custom.go | 41 +++++++++++++++++++ config/v3_2_experimental/types/luks.go | 6 +++ config/v3_2_experimental/types/schema.go | 13 ++++-- 5 files changed, 78 insertions(+), 3 deletions(-) create mode 100644 config/v3_2_experimental/types/custom.go diff --git a/config/shared/errors/errors.go b/config/shared/errors/errors.go index 33dcd5bd0..577ddf935 100644 --- a/config/shared/errors/errors.go +++ b/config/shared/errors/errors.go @@ -55,6 +55,9 @@ var ( ErrLuksLabelTooLong = errors.New("luks device labels cannot be longer than 47 characters") ErrLuksNameContainsSlash = errors.New("device names cannot contain slashes") ErrInvalidLuksKeyFile = errors.New("invalid key-file source") + ErrUnknownClevisPin = errors.New("unsupported clevis pin") + ErrClevisConfigRequired = errors.New("missing required custom clevis config") + ErrClevisCustomWithOthers = errors.New("cannot use custom clevis config with tpm2, tang, or threshold") ErrTangThumbprintRequired = errors.New("thumbprint is required") ErrFileIllegalMode = errors.New("illegal file mode") ErrBothIDAndNameSet = errors.New("cannot set both id and name") diff --git a/config/v3_2_experimental/schema/ignition.json b/config/v3_2_experimental/schema/ignition.json index ca0834043..c15d40dbf 100644 --- a/config/v3_2_experimental/schema/ignition.json +++ b/config/v3_2_experimental/schema/ignition.json @@ -262,6 +262,24 @@ "clevis": { "type": ["object", "null"], "properties": { + "custom": { + "type": ["object", "null"], + "properties": { + "pin": { + "type": "string" + }, + "config": { + "type": "string" + }, + "needsNetwork": { + "type": ["boolean", "null"] + } + }, + "required": [ + "pin", + "config" + ] + }, "tpm2": { "type": ["boolean", "null"] }, diff --git a/config/v3_2_experimental/types/custom.go b/config/v3_2_experimental/types/custom.go new file mode 100644 index 000000000..1b0c77b8b --- /dev/null +++ b/config/v3_2_experimental/types/custom.go @@ -0,0 +1,41 @@ +// Copyright 2020 Red Hat, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package types + +import ( + "github.com/coreos/ignition/v2/config/shared/errors" + + "github.com/coreos/vcontext/path" + "github.com/coreos/vcontext/report" +) + +func (cu Custom) Key() string { + return cu.Pin +} + +func (cu Custom) Validate(c path.ContextPath) (r report.Report) { + if cu.Pin == "" && cu.Config == "" { + return + } + switch cu.Pin { + case "tpm2", "tang", "sss": + default: + r.AddOnError(c.Append("pin"), errors.ErrUnknownClevisPin) + } + if cu.Config == "" { + r.AddOnError(c.Append("config"), errors.ErrClevisConfigRequired) + } + return +} diff --git a/config/v3_2_experimental/types/luks.go b/config/v3_2_experimental/types/luks.go index ffa8e7f55..d7cd29bac 100644 --- a/config/v3_2_experimental/types/luks.go +++ b/config/v3_2_experimental/types/luks.go @@ -45,6 +45,12 @@ func (l Luks) Validate(c path.ContextPath) (r report.Report) { r.AddOnError(c.Append("device"), validatePath(*l.Device)) } + if l.Clevis != nil { + if l.Clevis.Custom != nil && (len(l.Clevis.Tang) > 0 || (l.Clevis.Tpm2 != nil && *l.Clevis.Tpm2) || (l.Clevis.Threshold != nil && *l.Clevis.Threshold != 0)) { + r.AddOnError(c.Append("clevis"), errors.ErrClevisCustomWithOthers) + } + } + // fail if a key file is provided and is not valid if err := validateURLNilOK(l.KeyFile.Source); err != nil { r.AddOnError(c.Append("keys"), errors.ErrInvalidLuksKeyFile) diff --git a/config/v3_2_experimental/types/schema.go b/config/v3_2_experimental/types/schema.go index 01dc9c52f..60c4bac59 100644 --- a/config/v3_2_experimental/types/schema.go +++ b/config/v3_2_experimental/types/schema.go @@ -3,9 +3,10 @@ package types // generated by "schematyper --package=types config/v3_2_experimental/schema/ignition.json -o config/v3_2_experimental/types/schema.go --root-type=Config" -- DO NOT EDIT type Clevis struct { - Tang []Tang `json:"tang,omitempty"` - Threshold *int `json:"threshold,omitempty"` - Tpm2 *bool `json:"tpm2,omitempty"` + Custom *Custom `json:"custom,omitempty"` + Tang []Tang `json:"tang,omitempty"` + Threshold *int `json:"threshold,omitempty"` + Tpm2 *bool `json:"tpm2,omitempty"` } type Config struct { @@ -15,6 +16,12 @@ type Config struct { Systemd Systemd `json:"systemd,omitempty"` } +type Custom struct { + Config string `json:"config"` + NeedsNetwork *bool `json:"needsNetwork,omitempty"` + Pin string `json:"pin"` +} + type Device string type Directory struct { From 1ad13eb18322fabd1236ef060c13a4b4f07b0531 Mon Sep 17 00:00:00 2001 From: Stephen Lowrie Date: Thu, 16 Jul 2020 00:32:27 -0500 Subject: [PATCH 2/3] internal/exec/stages: add clevis custom support --- internal/exec/stages/disks/luks.go | 50 ++++++++++++------- .../exec/stages/files/filesystemEntries.go | 2 +- 2 files changed, 33 insertions(+), 19 deletions(-) diff --git a/internal/exec/stages/disks/luks.go b/internal/exec/stages/disks/luks.go index 97877e779..671926ff4 100644 --- a/internal/exec/stages/disks/luks.go +++ b/internal/exec/stages/disks/luks.go @@ -247,27 +247,41 @@ func (s *stage) createLuks(config types.Config) error { } if luks.Clevis != nil { - c := Clevis{ - Threshold: 1, - } - if luks.Clevis.Threshold != nil { - c.Threshold = *luks.Clevis.Threshold - } - for _, tang := range luks.Clevis.Tang { - c.Pins.Tang = append(c.Pins.Tang, Tang{ - URL: tang.URL, - Thumbprint: *tang.Thumbprint, - }) - } - if luks.Clevis.Tpm2 != nil { - c.Pins.Tpm = *luks.Clevis.Tpm2 + var pin string + var config string + + if luks.Clevis.Custom != nil { + pin = luks.Clevis.Custom.Pin + config = luks.Clevis.Custom.Config } - clevisJson, err := json.Marshal(c) - if err != nil { - return fmt.Errorf("creating clevis json: %v", err) + + // if the override pin is empty the config must also be empty + if pin == "" { + pin = "sss" + c := Clevis{ + Threshold: 1, + } + if luks.Clevis.Threshold != nil { + c.Threshold = *luks.Clevis.Threshold + } + for _, tang := range luks.Clevis.Tang { + c.Pins.Tang = append(c.Pins.Tang, Tang{ + URL: tang.URL, + Thumbprint: *tang.Thumbprint, + }) + } + if luks.Clevis.Tpm2 != nil { + c.Pins.Tpm = *luks.Clevis.Tpm2 + } + clevisJson, err := json.Marshal(c) + if err != nil { + return fmt.Errorf("creating clevis json: %v", err) + } + config = string(clevisJson) } + if _, err := s.Logger.LogCmd( - exec.Command(distro.ClevisCmd(), "luks", "bind", "-f", "-k", keyFilePath, "-d", devAlias, "sss", string(clevisJson)), "Clevis bind", + exec.Command(distro.ClevisCmd(), "luks", "bind", "-f", "-k", keyFilePath, "-d", devAlias, pin, config), "Clevis bind", ); err != nil { return fmt.Errorf("binding clevis device: %v", err) } diff --git a/internal/exec/stages/files/filesystemEntries.go b/internal/exec/stages/files/filesystemEntries.go index 5752470a8..ed443666e 100644 --- a/internal/exec/stages/files/filesystemEntries.go +++ b/internal/exec/stages/files/filesystemEntries.go @@ -62,7 +62,7 @@ func (s *stage) createCrypttabEntries(config types.Config) error { } uuid := strings.TrimSpace(string(out)) netdev := "" - if luks.Clevis != nil && len(luks.Clevis.Tang) > 0 { + if luks.Clevis != nil && (len(luks.Clevis.Tang) > 0 || (luks.Clevis.Custom != nil && luks.Clevis.Custom.NeedsNetwork != nil && *luks.Clevis.Custom.NeedsNetwork)) { netdev = ",_netdev" } keyfile := "none" From 776f321e165ac072f9c2d34f3b7b5550e302ba71 Mon Sep 17 00:00:00 2001 From: Stephen Lowrie Date: Thu, 16 Jul 2020 00:32:46 -0500 Subject: [PATCH 3/3] doc: add clevis custom documentation --- docs/configuration-v3_2_experimental.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/configuration-v3_2_experimental.md b/docs/configuration-v3_2_experimental.md index 93a4bf286..6dd4c35c6 100644 --- a/docs/configuration-v3_2_experimental.md +++ b/docs/configuration-v3_2_experimental.md @@ -144,6 +144,10 @@ The Ignition configuration is a JSON document conforming to the following specif * **thumbprint** (string): thumbprint of a trusted signing key. * **_tpm2_** (bool): whether or not to use a tpm2 device. * **_threshold_** (int): sets the minimum number of pieces required to decrypt the device. + * **_custom_** (object): overrides the clevis configuration. The `pin` & `config` will be passed directly to `clevis luks bind`. If specified, all other clevis options must be omitted. + * **pin** (string): the clevis pin. + * **config** (string): the clevis configuration JSON. + * **_needsNetwork_** (bool): whether or not the device requires networking. * **_systemd_** (object): describes the desired state of the systemd units. * **_units_** (list of objects): the list of systemd units. * **name** (string): the name of the unit. This must be suffixed with a valid unit type (e.g. "thing.service"). Every unit must have a unique `name`.