mirror of
https://github.com/balena-io/balena-cli.git
synced 2025-04-09 04:14:15 +00:00
Deny preload for an image with secure boot enabled
Change-type: patch Signed-off-by: Ken Bannister <kb2ma@runbox.com>
This commit is contained in:
parent
813e9cb82e
commit
7f2daeebb0
@ -199,7 +199,7 @@
|
||||
"balena-config-json": "^4.2.2",
|
||||
"balena-device-init": "^8.1.3",
|
||||
"balena-errors": "^4.7.3",
|
||||
"balena-image-fs": "^7.3.0",
|
||||
"balena-image-fs": "^7.5.0",
|
||||
"balena-preload": "^18.0.1",
|
||||
"balena-sdk": "^21.3.0",
|
||||
"balena-semver": "^2.3.0",
|
||||
|
@ -37,6 +37,7 @@ import type {
|
||||
Release,
|
||||
} from 'balena-sdk';
|
||||
import type { Preloader } from 'balena-preload';
|
||||
import type * as Fs from 'fs';
|
||||
|
||||
export default class PreloadCmd extends Command {
|
||||
public static description = stripIndent`
|
||||
@ -161,6 +162,37 @@ Can be repeated to add multiple certificates.\
|
||||
);
|
||||
}
|
||||
|
||||
// Verify that image is not enabled for secure boot. First, confirm it
|
||||
// is a secure boot image with an /opt/*.sig file in the rootA partition.
|
||||
const { explorePartition, BalenaPartition } = await import(
|
||||
'../../utils/image-contents'
|
||||
);
|
||||
const isSecureBoot = await explorePartition<boolean>(
|
||||
params.image,
|
||||
BalenaPartition.ROOTA,
|
||||
async (fs: typeof Fs): Promise<boolean> => {
|
||||
try {
|
||||
const files = await fs.promises.readdir('/opt');
|
||||
return files.some((el) => el.endsWith('balenaos-img.sig'));
|
||||
} catch {
|
||||
// Typically one of:
|
||||
// - Error: No such file or directory
|
||||
// - Error: Unsupported filesystem.
|
||||
// - ErrnoException: node_ext2fs_open ENOENT (44) args: [5261576,5268064,"r",0]
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
);
|
||||
// Next verify that config.json enables secureboot.
|
||||
if (isSecureBoot) {
|
||||
const { read } = await import('balena-config-json');
|
||||
const config = await read(params.image, '');
|
||||
if (config.installer?.secureboot === true) {
|
||||
throw new ExpectedError("Can't preload image with secure boot enabled");
|
||||
}
|
||||
}
|
||||
|
||||
// balena-preload currently does not work with numerical app IDs
|
||||
// Load app here, and use app slug from hereon
|
||||
const fleetSlug: string | undefined = options.fleet
|
||||
|
69
src/utils/image-contents.ts
Normal file
69
src/utils/image-contents.ts
Normal file
@ -0,0 +1,69 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2025 Balena Ltd.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
// Utilities to explore the contents in a balenaOS image.
|
||||
|
||||
import * as imagefs from 'balena-image-fs';
|
||||
import * as filedisk from 'file-disk';
|
||||
import { getPartitions } from 'partitioninfo';
|
||||
import type * as Fs from 'fs';
|
||||
|
||||
/**
|
||||
* @summary IDs for the standard balenaOS partitions
|
||||
* @description Values are the base name for a partition on disk
|
||||
*/
|
||||
export enum BalenaPartition {
|
||||
BOOT = 'boot',
|
||||
ROOTA = 'rootA',
|
||||
ROOTB = 'rootB',
|
||||
STATE = 'state',
|
||||
DATA = 'data',
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary Allow a provided function to explore the contents of one of the well-known
|
||||
* partitions of a balenaOS image
|
||||
*
|
||||
* @param {string} imagePath - pathname of image for search
|
||||
* @param {BalenaPartition} partitionId - partition to find
|
||||
* @param {(fs) => Promise<T>} - function for exploration
|
||||
* @returns {T}
|
||||
*/
|
||||
export async function explorePartition<T>(
|
||||
imagePath: string,
|
||||
partitionId: BalenaPartition,
|
||||
exploreFn: (fs: typeof Fs) => Promise<T>,
|
||||
): Promise<T> {
|
||||
return await filedisk.withOpenFile(imagePath, 'r', async (handle) => {
|
||||
const disk = new filedisk.FileDisk(handle, true, false, false);
|
||||
const partitionInfo = await getPartitions(disk, {
|
||||
includeExtended: false,
|
||||
getLogical: true,
|
||||
});
|
||||
|
||||
const findResult = await imagefs.findPartition(disk, partitionInfo, [
|
||||
`resin-${partitionId}`,
|
||||
`flash-${partitionId}`,
|
||||
`balena-${partitionId}`,
|
||||
]);
|
||||
if (findResult == null) {
|
||||
throw new Error(`Can't find partition for ${partitionId}`);
|
||||
}
|
||||
|
||||
return await imagefs.interact<T>(disk, findResult.index, exploreFn);
|
||||
});
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user