Skip to content

Commit

Permalink
Merge branch 'master' into warn-on-apparmor-disabled
Browse files Browse the repository at this point in the history
  • Loading branch information
mardy committed Jun 13, 2022
2 parents 06e011b + 5d514ae commit 2014646
Show file tree
Hide file tree
Showing 15 changed files with 967 additions and 220 deletions.
12 changes: 9 additions & 3 deletions cmd/snap-fde-keymgr/export_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,15 @@ func MockRemoveRecoveryKeyFromLUKSUsingKey(f func(key keys.EncryptionKey, dev st
return restore
}

func MockChangeLUKSEncryptionKey(f func(newKey keys.EncryptionKey, dev string) error) (restore func()) {
restore = testutil.Backup(&keymgrChangeLUKSDeviceEncryptionKey)
keymgrChangeLUKSDeviceEncryptionKey = f
func MockStageLUKSEncryptionKeyChange(f func(newKey keys.EncryptionKey, dev string) error) (restore func()) {
restore = testutil.Backup(&keymgrStageLUKSDeviceEncryptionKeyChange)
keymgrStageLUKSDeviceEncryptionKeyChange = f
return restore
}

func MockTransitionLUKSEncryptionKeyChange(f func(newKey keys.EncryptionKey, dev string) error) (restore func()) {
restore = testutil.Backup(&keymgrTransitionLUKSDeviceEncryptionKeyChange)
keymgrTransitionLUKSDeviceEncryptionKeyChange = f
return restore
}

Expand Down
30 changes: 26 additions & 4 deletions cmd/snap-fde-keymgr/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,9 @@ type cmdRemoveRecoveryKey struct {
}

type cmdChangeEncryptionKey struct {
Device string `long:"device" description:"encrypted device" required:"yes"`
Device string `long:"device" description:"encrypted device" required:"yes"`
Stage bool `long:"stage" description:"stage the new key"`
Transition bool `long:"transition" description:"replace the old key, unstage the new"`
}

type options struct {
Expand All @@ -66,7 +68,8 @@ var (
keymgrAddRecoveryKeyToLUKSDeviceUsingKey = keymgr.AddRecoveryKeyToLUKSDeviceUsingKey
keymgrRemoveRecoveryKeyFromLUKSDevice = keymgr.RemoveRecoveryKeyFromLUKSDevice
keymgrRemoveRecoveryKeyFromLUKSDeviceUsingKey = keymgr.RemoveRecoveryKeyFromLUKSDeviceUsingKey
keymgrChangeLUKSDeviceEncryptionKey = keymgr.ChangeLUKSDeviceEncryptionKey
keymgrStageLUKSDeviceEncryptionKeyChange = keymgr.StageLUKSDeviceEncryptionKeyChange
keymgrTransitionLUKSDeviceEncryptionKeyChange = keymgr.TransitionLUKSDeviceEncryptionKeyChange
)

func validateAuthorizations(authorizations []string) error {
Expand Down Expand Up @@ -201,13 +204,32 @@ type newKey struct {
}

func (c *cmdChangeEncryptionKey) Execute(args []string) error {
if c.Stage && c.Transition {
return fmt.Errorf("cannot both stage and transition the encryption key change")
}
if !c.Stage && !c.Transition {
return fmt.Errorf("cannot change encryption key without stage or transition request")
}

var newEncryptionKeyData newKey
dec := json.NewDecoder(osStdin)
if err := dec.Decode(&newEncryptionKeyData); err != nil {
return fmt.Errorf("cannot obtain new encryption key: %v", err)
}
if err := keymgrChangeLUKSDeviceEncryptionKey(newEncryptionKeyData.Key, c.Device); err != nil {
return fmt.Errorf("cannot change LUKS device encryption key: %v", err)
switch {
case c.Stage:
// staging the key change authorizes the operation using a key
// from the keyring
if err := keymgrStageLUKSDeviceEncryptionKeyChange(newEncryptionKeyData.Key, c.Device); err != nil {
return fmt.Errorf("cannot stage LUKS device encryption key change: %v", err)
}
case c.Transition:
// transitioning the key change authorizes the operation using
// the currently provided key (which must have been staged
// before hence the op will be authorized successfully)
if err := keymgrTransitionLUKSDeviceEncryptionKeyChange(newEncryptionKeyData.Key, c.Device); err != nil {
return fmt.Errorf("cannot transition LUKS device encryption key change: %v", err)
}
}
return nil
}
Expand Down
99 changes: 93 additions & 6 deletions cmd/snap-fde-keymgr/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -338,28 +338,115 @@ func (s *mainSuite) TestRemoveKeyRequiresAuthz(c *C) {
c.Assert(err, ErrorMatches, `cannot remove recovery keys with invalid authorizations: authorization file .*/authz.key does not exist`)
}

// 1 in ASCII repeated 32 times
const all1sKey = `{"key":"MTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTE="}`

func (s *mainSuite) TestChangeEncryptionKey(c *C) {
const all1sKey = `{"key":"MTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTE="}`
b := bytes.NewBufferString(all1sKey)
restore := main.MockOsStdin(b)
defer restore()
unexpectedCall := func(newKey keys.EncryptionKey, luksDev string) error {
c.Errorf("unexpected call")
return fmt.Errorf("unexpected call")
}
defer main.MockStageLUKSEncryptionKeyChange(unexpectedCall)
defer main.MockTransitionLUKSEncryptionKeyChange(unexpectedCall)

err := main.Run([]string{
"change-encryption-key",
"--device", "/dev/vda4",
})
c.Assert(err, ErrorMatches, "cannot change encryption key without stage or transition request")

err = main.Run([]string{
"change-encryption-key",
"--device", "/dev/vda4",
"--stage", "--transition",
})
c.Assert(err, ErrorMatches, "cannot both stage and transition the encryption key change")
}

func (s *mainSuite) TestStageEncryptionKey(c *C) {
b := bytes.NewBufferString(all1sKey)
restore := main.MockOsStdin(b)
defer restore()
dev := ""
changeCalls := 0
stageCalls := 0
var key []byte
restore = main.MockChangeLUKSEncryptionKey(func(newKey keys.EncryptionKey, luksDev string) error {
changeCalls++
var stageErr error
restore = main.MockStageLUKSEncryptionKeyChange(func(newKey keys.EncryptionKey, luksDev string) error {
stageCalls++
dev = luksDev
key = newKey
return nil
return stageErr
})
defer restore()
restore = main.MockTransitionLUKSEncryptionKeyChange(func(newKey keys.EncryptionKey, luksDev string) error {
c.Errorf("unexpected call")
return fmt.Errorf("unexpected call")
})
defer restore()
err := main.Run([]string{
"change-encryption-key",
"--device", "/dev/vda4",
"--stage",
})
c.Assert(err, IsNil)
c.Check(changeCalls, Equals, 1)
c.Check(stageCalls, Equals, 1)
c.Check(dev, Equals, "/dev/vda4")
// secboot encryption key size
c.Check(key, DeepEquals, bytes.Repeat([]byte("1"), 32))

restore = main.MockOsStdin(bytes.NewBufferString(all1sKey))
defer restore()
stageErr = fmt.Errorf("mock stage error")
err = main.Run([]string{
"change-encryption-key",
"--device", "/dev/vda4",
"--stage",
})
c.Assert(err, ErrorMatches, "cannot stage LUKS device encryption key change: mock stage error")
}

func (s *mainSuite) TestTransitionEncryptionKey(c *C) {
b := bytes.NewBufferString(all1sKey)
restore := main.MockOsStdin(b)
defer restore()
dev := ""
transitionCalls := 0
var key []byte
var transitionErr error
restore = main.MockStageLUKSEncryptionKeyChange(func(newKey keys.EncryptionKey, luksDev string) error {
c.Errorf("unexpected call")
return fmt.Errorf("unexpected call")
})
defer restore()
restore = main.MockTransitionLUKSEncryptionKeyChange(func(newKey keys.EncryptionKey, luksDev string) error {
transitionCalls++
dev = luksDev
key = newKey
return transitionErr
})
defer restore()
defer restore()
err := main.Run([]string{
"change-encryption-key",
"--device", "/dev/vda4",
"--transition",
})
c.Assert(err, IsNil)
c.Check(transitionCalls, Equals, 1)
c.Check(dev, Equals, "/dev/vda4")
// secboot encryption key size
c.Check(key, DeepEquals, bytes.Repeat([]byte("1"), 32))

restore = main.MockOsStdin(bytes.NewBufferString(all1sKey))
defer restore()
transitionErr = fmt.Errorf("mock transition error")
err = main.Run([]string{
"change-encryption-key",
"--device", "/dev/vda4",
"--transition",
})
c.Assert(err, ErrorMatches, "cannot transition LUKS device encryption key change: mock transition error")
}
63 changes: 30 additions & 33 deletions cmd/snap/cmd_quota.go
Original file line number Diff line number Diff line change
Expand Up @@ -165,68 +165,65 @@ func parseCpuQuota(cpuMax string) (count int, percentage int, err error) {
return count, percentage, nil
}

func parseQuotas(maxMemory string, cpuMax string, cpuSet string, threadsMax string) (*client.QuotaValues, error) {
var mem int64
var cpuCount int
var cpuPercentage int
var cpus []int
var threads int

if maxMemory != "" {
value, err := strutil.ParseByteSize(maxMemory)
func (x *cmdSetQuota) parseQuotas() (*client.QuotaValues, error) {
var quotaValues client.QuotaValues

if x.MemoryMax != "" {
value, err := strutil.ParseByteSize(x.MemoryMax)
if err != nil {
return nil, err
}
mem = value
quotaValues.Memory = quantity.Size(value)
}

if cpuMax != "" {
countValue, percentageValue, err := parseCpuQuota(cpuMax)
if x.CPUMax != "" {
countValue, percentageValue, err := parseCpuQuota(x.CPUMax)
if err != nil {
return nil, err
}
if percentageValue > 100 || percentageValue <= 0 {
return nil, fmt.Errorf("cannot use value %v: cpu quota percentage must be between 1 and 100", percentageValue)
}

cpuCount = countValue
cpuPercentage = percentageValue
quotaValues.CPU = &client.QuotaCPUValues{
Count: countValue,
Percentage: percentageValue,
}
}

if cpuSet != "" {
cpuTokens := strutil.CommaSeparatedList(cpuSet)
if x.CPUSet != "" {
var cpus []int
cpuTokens := strutil.CommaSeparatedList(x.CPUSet)
for _, cpuToken := range cpuTokens {
cpu, err := strconv.ParseUint(cpuToken, 10, 32)
if err != nil {
return nil, fmt.Errorf("cannot parse CPU set value %q", cpuToken)
}
cpus = append(cpus, int(cpu))
}

quotaValues.CPUSet = &client.QuotaCPUSetValues{
CPUs: cpus,
}
}

if threadsMax != "" {
value, err := strconv.ParseUint(threadsMax, 10, 32)
if x.ThreadsMax != "" {
value, err := strconv.ParseUint(x.ThreadsMax, 10, 32)
if err != nil {
return nil, fmt.Errorf("cannot use threads value %q", threadsMax)
return nil, fmt.Errorf("cannot use threads value %q", x.ThreadsMax)
}
threads = int(value)
quotaValues.Threads = int(value)
}

return &client.QuotaValues{
Memory: quantity.Size(mem),
CPU: &client.QuotaCPUValues{
Count: cpuCount,
Percentage: cpuPercentage,
},
CPUSet: &client.QuotaCPUSetValues{
CPUs: cpus,
},
Threads: threads,
}, nil
return &quotaValues, nil
}

func (x *cmdSetQuota) hasQuotaSet() bool {
return x.MemoryMax != "" || x.CPUMax != "" || x.CPUSet != "" || x.ThreadsMax != ""
}

func (x *cmdSetQuota) Execute(args []string) (err error) {
quotaProvided := x.MemoryMax != "" || x.CPUMax != "" || x.CPUSet != "" || x.ThreadsMax != ""
quotaProvided := x.hasQuotaSet()

names := installedSnapNames(x.Positional.Snaps)

Expand Down Expand Up @@ -267,7 +264,7 @@ func (x *cmdSetQuota) Execute(args []string) (err error) {
// we have a limits to set for this group, so specify that along
// with whatever snaps may have been provided and whatever parent may
// have been specified
quotaValues, err := parseQuotas(x.MemoryMax, x.CPUMax, x.CPUSet, x.ThreadsMax)
quotaValues, err := x.parseQuotas()
if err != nil {
return err
}
Expand Down
12 changes: 6 additions & 6 deletions cmd/snap/cmd_quota_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -212,11 +212,11 @@ func (s *quotaSuite) TestParseQuotas(c *check.C) {
quotas string
err string
}{
{maxMemory: "12KB", quotas: `{"memory":12000,"cpu":{},"cpu-set":{}}`},
{cpuMax: "12x40%", quotas: `{"cpu":{"count":12,"percentage":40},"cpu-set":{}}`},
{cpuMax: "40%", quotas: `{"cpu":{"percentage":40},"cpu-set":{}}`},
{cpuSet: "1,3", quotas: `{"cpu":{},"cpu-set":{"cpus":[1,3]}}`},
{threadsMax: "2", quotas: `{"cpu":{},"cpu-set":{},"threads":2}`},
{maxMemory: "12KB", quotas: `{"memory":12000}`},
{cpuMax: "12x40%", quotas: `{"cpu":{"count":12,"percentage":40}}`},
{cpuMax: "40%", quotas: `{"cpu":{"percentage":40}}`},
{cpuSet: "1,3", quotas: `{"cpu-set":{"cpus":[1,3]}}`},
{threadsMax: "2", quotas: `{"threads":2}`},
// Error cases
{cpuMax: "ASD", err: `cannot parse cpu quota string "ASD"`},
{cpuMax: "0x100%", err: `cannot parse cpu quota string "0x100%"`},
Expand All @@ -230,7 +230,7 @@ func (s *quotaSuite) TestParseQuotas(c *check.C) {
{threadsMax: "xxx", err: `cannot use threads value "xxx"`},
{threadsMax: "-3", err: `cannot use threads value "-3"`},
} {
quotas, err := main.ParseQuotas(testData.maxMemory, testData.cpuMax, testData.cpuSet, testData.threadsMax)
quotas, err := main.ParseQuotaValues(testData.maxMemory, testData.cpuMax, testData.cpuSet, testData.threadsMax)
testLabel := check.Commentf("%v", testData)
if testData.err == "" {
c.Check(err, check.IsNil, testLabel)
Expand Down
13 changes: 11 additions & 2 deletions cmd/snap/export_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,6 @@ var (
IsStopping = isStopping

GetSnapDirOptions = getSnapDirOptions

ParseQuotas = parseQuotas
)

func HiddenCmd(descr string, completeHidden bool) *cmdInfo {
Expand Down Expand Up @@ -459,3 +457,14 @@ func MockAutostartSessionApps(f func(string) error) func() {
autostartSessionApps = old
}
}

func ParseQuotaValues(maxMemory, cpuMax, cpuSet, threadsMax string) (*client.QuotaValues, error) {
var quotas cmdSetQuota

quotas.MemoryMax = maxMemory
quotas.CPUMax = cpuMax
quotas.CPUSet = cpuSet
quotas.ThreadsMax = threadsMax

return quotas.parseQuotas()
}
1 change: 1 addition & 0 deletions overlord/ifacestate/export_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ var (

BatchConnectTasks = batchConnectTasks
FirstTaskAfterBootWhenPreseeding = firstTaskAfterBootWhenPreseeding
BuildConfinementOptions = buildConfinementOptions
)

type ConnectOpts = connectOpts
Expand Down
Loading

0 comments on commit 2014646

Please sign in to comment.