Skip to content

Commit

Permalink
Merge pull request #960 from arithx/luks
Browse files Browse the repository at this point in the history
*: add LUKS
  • Loading branch information
Stephen Lowrie authored Jul 10, 2020
2 parents b025750 + b866d91 commit ff94f87
Show file tree
Hide file tree
Showing 12 changed files with 581 additions and 1 deletion.
4 changes: 4 additions & 0 deletions config/shared/errors/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,10 @@ var (
ErrXfsLabelTooLong = errors.New("filesystem labels cannot be longer than 12 characters when using xfs")
ErrSwapLabelTooLong = errors.New("filesystem labels cannot be longer than 15 characters when using swap")
ErrVfatLabelTooLong = errors.New("filesystem labels cannot be longer than 11 characters when using vfat")
ErrLuksLabelTooLong = errors.New("luks device labels cannot be longer than 47 characters")
ErrLuksNameContainsSlash = errors.New("device names cannot contain slashes")
ErrInvalidLuksVolume = errors.New("a key-file or clevis configuration must be specified")
ErrTangThumbprintRequired = errors.New("thumbprint is required")
ErrFileIllegalMode = errors.New("illegal file mode")
ErrBothIDAndNameSet = errors.New("cannot set both id and name")
ErrLabelTooLong = errors.New("partition labels may not exceed 36 characters")
Expand Down
63 changes: 63 additions & 0 deletions config/v3_2_experimental/schema/ignition.json
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,12 @@
"$ref": "#/definitions/storage/definitions/raid"
}
},
"luks": {
"type": "array",
"items": {
"$ref": "#/definitions/storage/definitions/luks"
}
},
"filesystems": {
"type": "array",
"items": {
Expand Down Expand Up @@ -232,6 +238,63 @@
"devices"
]
},
"luks": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"label": {
"type": ["string", "null"]
},
"uuid": {
"type": ["string", "null"]
},
"device": {
"type": ["string", "null"]
},
"keyFile": {
"$ref": "#/definitions/resource"
},
"clevis": {
"type": ["object", "null"],
"properties": {
"tpm2": {
"type": ["boolean", "null"]
},
"tang": {
"type": "array",
"items": {
"$ref": "#/definitions/storage/definitions/tang"
}
},
"threshold": {
"type": ["integer", "null"]
}
}
},
"options": {
"type": "array",
"items": {
"type": "string"
}
}
},
"required": [
"name"
]
},
"tang": {
"type": "object",
"properties": {
"url": {
"type": "string"
},
"thumbprint": {
"type": ["string", "null"]
}
}
},
"filesystem": {
"type": "object",
"properties": {
Expand Down
12 changes: 12 additions & 0 deletions config/v3_2_experimental/translate/translate.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,21 @@ func translateIgnition(old old_types.Ignition) (ret types.Ignition) {
return
}

func translateStorage(old old_types.Storage) (ret types.Storage) {
tr := translate.NewTranslator()
tr.Translate(&old.Directories, &ret.Directories)
tr.Translate(&old.Disks, &ret.Disks)
tr.Translate(&old.Files, &ret.Files)
tr.Translate(&old.Filesystems, &ret.Filesystems)
tr.Translate(&old.Links, &ret.Links)
tr.Translate(&old.Raid, &ret.Raid)
return
}

func Translate(old old_types.Config) (ret types.Config) {
tr := translate.NewTranslator()
tr.AddCustomTranslator(translateIgnition)
tr.AddCustomTranslator(translateStorage)
tr.Translate(&old, &ret)
return
}
75 changes: 75 additions & 0 deletions config/v3_2_experimental/types/luks.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
// 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 (
"strings"

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

"github.com/coreos/vcontext/path"
"github.com/coreos/vcontext/report"
)

func (l Luks) Key() string {
return l.Name
}

func (l Luks) IgnoreDuplicates() map[string]struct{} {
return map[string]struct{}{
"Options": {},
}
}

func (l Luks) Validate(c path.ContextPath) (r report.Report) {
if strings.Contains(l.Name, "/") {
r.AddOnError(c.Append("name"), errors.ErrLuksNameContainsSlash)
}
r.AddOnError(c.Append("label"), l.validateLabel())
if util.NilOrEmpty(l.Device) {
r.AddOnError(c.Append("device"), errors.ErrDiskDeviceRequired)
} else {
r.AddOnError(c.Append("device"), validatePath(*l.Device))
}

// fail if there is no valid keyfile & no clevis entries
if err := l.KeyFile.validateRequiredSource(); err != nil && l.emptyClevis() {
r.AddOnError(c.Append("keys"), errors.ErrInvalidLuksVolume)
}
return
}

func (l Luks) emptyClevis() bool {
if l.Clevis == nil {
return true
}

return len(l.Clevis.Tang) == 0 && (l.Clevis.Tpm2 == nil || !*l.Clevis.Tpm2)
}

func (l Luks) validateLabel() error {
if util.NilOrEmpty(l.Label) {
return nil
}

if len(*l.Label) > 47 {
// LUKS2_LABEL_L has a maximum length of 48 (including the null terminator)
// https://gitlab.com/cryptsetup/cryptsetup/-/blob/1633f030e89ad2f11ae649ba9600997a41abd3fc/lib/luks2/luks2.h#L86
return errors.ErrLuksLabelTooLong
}

return nil
}
24 changes: 24 additions & 0 deletions config/v3_2_experimental/types/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@ 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"`
}

type Config struct {
Ignition Ignition `json:"ignition"`
Passwd Passwd `json:"passwd,omitempty"`
Expand Down Expand Up @@ -87,6 +93,18 @@ type LinkEmbedded1 struct {
Target string `json:"target"`
}

type Luks struct {
Clevis *Clevis `json:"clevis,omitempty"`
Device *string `json:"device,omitempty"`
KeyFile Resource `json:"keyFile,omitempty"`
Label *string `json:"label,omitempty"`
Name string `json:"name"`
Options []LuksOption `json:"options,omitempty"`
UUID *string `json:"uuid,omitempty"`
}

type LuksOption string

type MountOption string

type NoProxyItem string
Expand Down Expand Up @@ -182,6 +200,7 @@ type Storage struct {
Files []File `json:"files,omitempty"`
Filesystems []Filesystem `json:"filesystems,omitempty"`
Links []Link `json:"links,omitempty"`
Luks []Luks `json:"luks,omitempty"`
Raid []Raid `json:"raid,omitempty"`
}

Expand All @@ -193,6 +212,11 @@ type TLS struct {
CertificateAuthorities []Resource `json:"certificateAuthorities,omitempty"`
}

type Tang struct {
Thumbprint *string `json:"thumbprint,omitempty"`
URL string `json:"url,omitempty"`
}

type Timeouts struct {
HTTPResponseHeaders *int `json:"httpResponseHeaders,omitempty"`
HTTPTotal *int `json:"httpTotal,omitempty"`
Expand Down
51 changes: 51 additions & 0 deletions config/v3_2_experimental/types/tang.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// 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 (
"net/url"

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

"github.com/coreos/vcontext/path"
"github.com/coreos/vcontext/report"
)

func (t Tang) Key() string {
return t.URL
}

func (t Tang) Validate(c path.ContextPath) (r report.Report) {
r.AddOnError(c.Append("url"), validateTangURL(t.URL))
if util.NilOrEmpty(t.Thumbprint) {
r.AddOnError(c.Append("thumbprint"), errors.ErrTangThumbprintRequired)
}
return
}

func validateTangURL(s string) error {
u, err := url.Parse(s)
if err != nil {
return errors.ErrInvalidUrl
}

switch u.Scheme {
case "http", "https":
return nil
default:
return errors.ErrInvalidScheme
}
}
20 changes: 20 additions & 0 deletions doc/configuration-v3_2_experimental.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,26 @@ The Ignition configuration is a JSON document conforming to the following specif
* **_name_** (string): the group name of the owner.
* **target** (string): the target path of the link
* **_hard_** (boolean): a symbolic link is created if this is false, a hard one if this is true.
* **_luks_** (list of objects): the list of luks devices to be created. Every device must have a unique `name`.
* **name** (string): the name of the luks device.
* **device** (string): the absolute path to the device. Devices are typically referenced by the `/dev/disk/by-*` symlinks.
* **_keyFile_** (string): options related to the contents of the key file.
* **_compression_** (string): the type of compression used on the contents (null or gzip). Compression cannot be used with S3.
* **_source_** (string): the URL of the contents to append. Supported schemes are `http`, `https`, `tftp`, `s3`, `gs`, and [`data`][rfc2397]. When using `http`, it is advisable to use the verification option to ensure the contents haven't been modified.
* **_httpHeaders_** (list of objects): a list of HTTP headers to be added to the request. Available for `http` and `https` source schemes only.
* **name** (string): the header name.
* **_value_** (string): the header contents.
* **_verification_** (object): options related to the verification of the key file.
* **_hash_** (string): the hash of the contents, in the form `<type>-<value>` where type is either `sha512` or `sha256`.
* **_label_** (string): the label of the luks device.
* **_uuid_** (string): the uuid of the luks device.
* **_options_** (list of strings): any additional options to be passed to the cryptsetup utility.
* **_clevis_** (object): describes the clevis configuration for the luks device.
* **_tang_** (list of objects): describes a tang server. Every server must have a unique `url`.
* **url** (string): url of the tang server.
* **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.
* **_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`.
Expand Down
13 changes: 13 additions & 0 deletions internal/distro/distro.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@ var (
chccwdevCmd = "chccwdev"
cioIgnoreCmd = "cio_ignore"

// LUKS programs
clevisCmd = "clevis"
cryptsetupCmd = "cryptsetup"

// Flags
selinuxRelabel = "true"
blackboxTesting = "false"
Expand All @@ -64,6 +68,9 @@ var (
// ".ssh/authorized_keys.d/ignition" ("true"), or to
// ".ssh/authorized_keys" ("false").
writeAuthorizedKeysFragment = "true"

luksInitramfsKeyFilePath = "/run/ignition/luks-keyfiles/"
luksRealRootKeyFilePath = "/etc/luks/"
)

func DiskByIDDir() string { return diskByIDDir }
Expand Down Expand Up @@ -94,6 +101,12 @@ func VmurCmd() string { return vmurCmd }
func ChccwdevCmd() string { return chccwdevCmd }
func CioIgnoreCmd() string { return cioIgnoreCmd }

func ClevisCmd() string { return clevisCmd }
func CryptsetupCmd() string { return cryptsetupCmd }

func LuksInitramfsKeyFilePath() string { return luksInitramfsKeyFilePath }
func LuksRealRootKeyFilePath() string { return luksRealRootKeyFilePath }

func SelinuxRelabel() bool { return bakedStringToBool(selinuxRelabel) && !BlackboxTesting() }
func BlackboxTesting() bool { return bakedStringToBool(blackboxTesting) }
func WriteAuthorizedKeysFragment() bool {
Expand Down
7 changes: 6 additions & 1 deletion internal/exec/stages/disks/disks.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,8 @@ func (s stage) Run(config types.Config) error {
// do the udevadm settle and can just return here.
if len(config.Storage.Disks) == 0 &&
len(config.Storage.Raid) == 0 &&
len(config.Storage.Filesystems) == 0 {
len(config.Storage.Filesystems) == 0 &&
len(config.Storage.Luks) == 0 {
return nil
}

Expand All @@ -83,6 +84,10 @@ func (s stage) Run(config types.Config) error {
return fmt.Errorf("failed to create raids: %v", err)
}

if err := s.createLuks(config); err != nil {
return fmt.Errorf("failed to create luks: %v", err)
}

if err := s.createFilesystems(config); err != nil {
return fmt.Errorf("failed to create filesystems: %v", err)
}
Expand Down
Loading

0 comments on commit ff94f87

Please sign in to comment.