Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 91 additions & 0 deletions classes/grub-uefi-sign.bbclass
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
# Class for signing GRUB EFI binaries for UEFI Secure Boot
# Supports both local signing (with sbsign) and remote signing via secure server

# Sign GRUB EFI binaries after they're installed
do_sign_grub() {
if [ "${@bb.utils.contains('DISTRO_FEATURES', 'uefi-secure-boot', 'true', 'false', d)}" = "true" ]; then

# Sign both the default GRUB image and the NILRT-specific image
for GRUB_UNSIGNED in "${D}/boot/efi/EFI/BOOT/${GRUB_IMAGE}" "${D}/boot/efi/nilrt/${GRUB_IMAGE}"; do
if [ ! -f "${GRUB_UNSIGNED}" ]; then
bbnote "GRUB image not found: ${GRUB_UNSIGNED}, skipping"
continue
fi

GRUB_SIGNED="${GRUB_UNSIGNED}.signed"

# Check if remote signing is enabled
if [ "${UEFI_REMOTE_SIGNING}" = "1" ]; then
bbnote "Using remote signing server for GRUB image: ${GRUB_UNSIGNED}"

if [ -z "${UEFI_SIGNING_SERVER_URL}" ]; then
bbfatal "UEFI_REMOTE_SIGNING is enabled but UEFI_SIGNING_SERVER_URL is not set"
fi

# Build remote-sign-efi command
REMOTE_SIGN_CMD="remote-sign-efi"
REMOTE_SIGN_CMD="$REMOTE_SIGN_CMD '${GRUB_UNSIGNED}' '${GRUB_SIGNED}'"
REMOTE_SIGN_CMD="$REMOTE_SIGN_CMD --server-url '${UEFI_SIGNING_SERVER_URL}'"
REMOTE_SIGN_CMD="$REMOTE_SIGN_CMD --auth-method '${UEFI_SIGNING_AUTH_METHOD}'"
REMOTE_SIGN_CMD="$REMOTE_SIGN_CMD --component-type 'grub'"
REMOTE_SIGN_CMD="$REMOTE_SIGN_CMD --timeout '${UEFI_SIGNING_TIMEOUT}'"
REMOTE_SIGN_CMD="$REMOTE_SIGN_CMD --retries '${UEFI_SIGNING_RETRIES}'"
REMOTE_SIGN_CMD="$REMOTE_SIGN_CMD --retry-delay '${UEFI_SIGNING_RETRY_DELAY}'"
REMOTE_SIGN_CMD="$REMOTE_SIGN_CMD --verify '${UEFI_VERIFY_SIGNED}'"

if [ "${UEFI_SIGNING_AUTH_METHOD}" = "token" ] && [ -n "${UEFI_SIGNING_AUTH_TOKEN}" ]; then
REMOTE_SIGN_CMD="$REMOTE_SIGN_CMD --auth-token '${UEFI_SIGNING_AUTH_TOKEN}'"
fi

if [ "${UEFI_SIGNING_AUTH_METHOD}" = "cert" ]; then
if [ -n "${UEFI_SIGNING_CLIENT_CERT}" ]; then
REMOTE_SIGN_CMD="$REMOTE_SIGN_CMD --client-cert '${UEFI_SIGNING_CLIENT_CERT}'"
fi
if [ -n "${UEFI_SIGNING_CLIENT_KEY}" ]; then
REMOTE_SIGN_CMD="$REMOTE_SIGN_CMD --client-key '${UEFI_SIGNING_CLIENT_KEY}'"
fi
fi

bbnote "Signing GRUB remotely: $(basename ${GRUB_UNSIGNED})"
eval $REMOTE_SIGN_CMD

if [ $? -eq 0 ]; then
bbnote "GRUB signed successfully via remote server: $(basename ${GRUB_SIGNED})"
# Replace unsigned with signed
mv "${GRUB_SIGNED}" "${GRUB_UNSIGNED}"
else
bbfatal "Failed to sign GRUB image via remote server: ${GRUB_UNSIGNED}"
fi

else
# Local signing with sbsign
bbnote "Using local signing with sbsign for GRUB image"

if [ ! -f "${UEFI_SB_DB_KEY}" ] || [ ! -f "${UEFI_SB_DB_CERT}" ]; then
bbwarn "UEFI Secure Boot is enabled but keys are not found:"
bbwarn " Key: ${UEFI_SB_DB_KEY}"
bbwarn " Cert: ${UEFI_SB_DB_CERT}"
bbwarn "GRUB will not be signed. Set UEFI_REMOTE_SIGNING=1 to use remote signing."
return
fi

bbnote "Signing GRUB image ${GRUB_UNSIGNED} for UEFI Secure Boot"
sbsign --key "${UEFI_SB_DB_KEY}" \
--cert "${UEFI_SB_DB_CERT}" \
--output "${GRUB_SIGNED}" \
"${GRUB_UNSIGNED}"

if [ $? -eq 0 ]; then
bbnote "GRUB signed successfully: $(basename ${GRUB_SIGNED})"
# Replace unsigned with signed
mv "${GRUB_SIGNED}" "${GRUB_UNSIGNED}"
else
bbfatal "Failed to sign GRUB image with sbsign: ${GRUB_UNSIGNED}"
fi
fi
done
fi
}

do_sign_grub[depends] += "${@bb.utils.contains('UEFI_REMOTE_SIGNING', '1', 'uefi-remote-signing-native:do_populate_sysroot', '', d)}"
addtask sign_grub after do_install before do_populate_sysroot do_package
91 changes: 91 additions & 0 deletions classes/kernel-uefi-sign.bbclass
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
# Class for signing kernel images for UEFI Secure Boot
# Supports both local signing (with sbsign) and remote signing via secure server

# Sign the kernel image after it's built
do_sign_kernel() {
if [ "${@bb.utils.contains('DISTRO_FEATURES', 'uefi-secure-boot', 'true', 'false', d)}" = "true" ]; then

UNSIGNED_KERNEL="${KERNEL_OUTPUT_DIR}/${KERNEL_IMAGETYPE}"
SIGNED_KERNEL="${KERNEL_OUTPUT_DIR}/${KERNEL_IMAGETYPE}.signed"

# Check if remote signing is enabled
if [ "${UEFI_REMOTE_SIGNING}" = "1" ]; then
bbnote "Using remote signing server for kernel image"

if [ -z "${UEFI_SIGNING_SERVER_URL}" ]; then
bbfatal "UEFI_REMOTE_SIGNING is enabled but UEFI_SIGNING_SERVER_URL is not set"
fi

# Build remote-sign-efi command
REMOTE_SIGN_CMD="remote-sign-efi"
REMOTE_SIGN_CMD="$REMOTE_SIGN_CMD '${UNSIGNED_KERNEL}' '${SIGNED_KERNEL}'"
REMOTE_SIGN_CMD="$REMOTE_SIGN_CMD --server-url '${UEFI_SIGNING_SERVER_URL}'"
REMOTE_SIGN_CMD="$REMOTE_SIGN_CMD --auth-method '${UEFI_SIGNING_AUTH_METHOD}'"
REMOTE_SIGN_CMD="$REMOTE_SIGN_CMD --component-type '${UEFI_COMPONENT_TYPE}'"
REMOTE_SIGN_CMD="$REMOTE_SIGN_CMD --timeout '${UEFI_SIGNING_TIMEOUT}'"
REMOTE_SIGN_CMD="$REMOTE_SIGN_CMD --retries '${UEFI_SIGNING_RETRIES}'"
REMOTE_SIGN_CMD="$REMOTE_SIGN_CMD --retry-delay '${UEFI_SIGNING_RETRY_DELAY}'"
REMOTE_SIGN_CMD="$REMOTE_SIGN_CMD --verify '${UEFI_VERIFY_SIGNED}'"

if [ "${UEFI_SIGNING_AUTH_METHOD}" = "token" ] && [ -n "${UEFI_SIGNING_AUTH_TOKEN}" ]; then
REMOTE_SIGN_CMD="$REMOTE_SIGN_CMD --auth-token '${UEFI_SIGNING_AUTH_TOKEN}'"
fi

if [ "${UEFI_SIGNING_AUTH_METHOD}" = "cert" ]; then
if [ -n "${UEFI_SIGNING_CLIENT_CERT}" ]; then
REMOTE_SIGN_CMD="$REMOTE_SIGN_CMD --client-cert '${UEFI_SIGNING_CLIENT_CERT}'"
fi
if [ -n "${UEFI_SIGNING_CLIENT_KEY}" ]; then
REMOTE_SIGN_CMD="$REMOTE_SIGN_CMD --client-key '${UEFI_SIGNING_CLIENT_KEY}'"
fi
fi

bbnote "Signing kernel remotely: ${KERNEL_IMAGETYPE}"
eval $REMOTE_SIGN_CMD

if [ $? -eq 0 ]; then
bbnote "Kernel signed successfully via remote server: ${KERNEL_IMAGETYPE}.signed"
else
bbfatal "Failed to sign kernel image via remote server"
fi

else
# Local signing with sbsign
bbnote "Using local signing with sbsign for kernel image"

if ! PSEUDO_UNLOAD=1 test -f "${UEFI_SB_DB_KEY}" || ! PSEUDO_UNLOAD=1 test -f "${UEFI_SB_DB_CERT}"; then
bbwarn "UEFI Secure Boot is enabled but keys are not found:"
bbwarn " Key: ${UEFI_SB_DB_KEY}"
bbwarn " Cert: ${UEFI_SB_DB_CERT}"
bbwarn "Kernel will not be signed. Set UEFI_REMOTE_SIGNING=1 to use remote signing."
return
fi

bbnote "Signing kernel image ${UNSIGNED_KERNEL} for UEFI Secure Boot"
PSEUDO_UNLOAD=1 sbsign --key "${UEFI_SB_DB_KEY}" \
--cert "${UEFI_SB_DB_CERT}" \
--output "${SIGNED_KERNEL}" \
"${UNSIGNED_KERNEL}"

if [ $? -eq 0 ]; then
bbnote "Kernel signed successfully: ${KERNEL_IMAGETYPE}.signed"
else
bbfatal "Failed to sign kernel image with sbsign"
fi
fi
fi
}

do_sign_kernel[depends] += "${@bb.utils.contains('UEFI_REMOTE_SIGNING', '1', 'uefi-remote-signing-native:do_populate_sysroot', '', d)}"
addtask sign_kernel after do_bundle_initramfs before do_deploy

# Deploy the signed kernel
do_deploy:append() {
if [ "${@bb.utils.contains('DISTRO_FEATURES', 'uefi-secure-boot', 'true', 'false', d)}" = "true" ]; then
if [ -f "${KERNEL_OUTPUT_DIR}/${KERNEL_IMAGETYPE}.signed" ]; then
install -m 0644 "${KERNEL_OUTPUT_DIR}/${KERNEL_IMAGETYPE}.signed" \
"${DEPLOYDIR}/${KERNEL_IMAGETYPE}.signed"
bbnote "Deployed signed kernel: ${DEPLOYDIR}/${KERNEL_IMAGETYPE}.signed"
fi
fi
}
197 changes: 197 additions & 0 deletions conf/distro/REMOTE-SIGNING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
# Remote Signing for UEFI Secure Boot

This document describes how to configure NILRT builds to use a remote signing server for UEFI Secure Boot components.

## Overview

Remote signing is the **recommended approach for production** because:
- Private keys never leave the secure signing server
- Reduces attack surface on build infrastructure
- Enables centralized key management and auditing
- Supports HSM (Hardware Security Module) backed signing

## Architecture

```
┌─────────────┐ ┌──────────────────┐
│ Bitbake │ │ Signing Server │
│ Build │ │ (Secure HSM) │
├─────────────┤ ├──────────────────┤
│ │ │ │
│ 1. Build │ │ - Private Keys │
│ bzImage │ │ - HSM Storage │
│ │ │ - Audit Logs │
│ 2. Send ──────────────────────> 3. Sign with │
│ unsigned │ HTTPS/TLS │ UEFI keys │
│ │ │ │
│ 4. Receive <────────────────── 5. Return │
│ signed │ │ signed │
│ │ │ │
│ 6. Deploy │ │ │
│ to image │ │ │
└─────────────┘ └──────────────────┘
```

## Configuration

### Step 1: Enable Remote Signing in Distro Config

Add to `conf/distro/nilrt.conf`:

```bitbake
require nilrt.inc
require secureboot.inc
require remote-signing.inc
```

### Step 2: Configure in local.conf

Add to `build/conf/local.conf`:

```bitbake
# Enable remote signing
UEFI_REMOTE_SIGNING = "1"

# Signing server URL
UEFI_SIGNING_SERVER_URL = "https://signing-server.example.com/api/v1/sign"

# Authentication
UEFI_SIGNING_AUTH_METHOD = "token"
UEFI_SIGNING_AUTH_TOKEN = "${@os.getenv('SIGNING_TOKEN', '')}"

# Note: UEFI_COMPONENT_TYPE is automatically set by signing classes:
# - kernel-uefi-sign.bbclass sets it to "kernel"
# - grub-uefi-sign.bbclass sets it to "grub"
# You typically don't need to override this manually.
```

### Step 3: Set Authentication Token

**Important:** Never commit tokens to version control!

```bash
# Set via environment variable
export SIGNING_TOKEN="your-secret-token-here"

# Or store in a separate file
echo "your-secret-token" > ~/.signing-token
chmod 600 ~/.signing-token

# Reference in local.conf
UEFI_SIGNING_AUTH_TOKEN = "${@open(os.path.expanduser('~/.signing-token')).read().strip()}"
```

### Step 4: Build

```bash
bitbake nilrt-safemode-rootfs
```

The kernel will automatically be sent to the signing server and the signed version used in the image.

## Signing Server API

The signing server must implement this API:

### Endpoint

```
POST /api/v1/sign
```

### Request

Multipart form data with:
- `file`: Binary file to sign (bzImage, grubx64.efi, etc.)
- `metadata`: JSON with:
```json
{
"component_type": "kernel", # or "grub" for bootloader
"filename": "bzImage",
"hash_algorithm": "sha256",
"unsigned_hash": "abc123..."
}
```

### Authentication

**Token-based:**
```
Authorization: Bearer <token>
```

**Certificate-based:**
- Client certificate verification via TLS

### Response

- **Success (200 OK)**: Binary signed file
- **Error (4xx/5xx)**: JSON error message

## Configuration Variables

| Variable | Default | Description |
|----------|---------|-------------|
| `UEFI_REMOTE_SIGNING` | `0` | Enable remote signing (1=yes, 0=no) |
| `UEFI_SIGNING_SERVER_URL` | `""` | Signing server endpoint URL |
| `UEFI_SIGNING_AUTH_METHOD` | `token` | Auth method: token, cert, or none |
| `UEFI_SIGNING_AUTH_TOKEN` | `""` | Bearer token for authentication |
| `UEFI_SIGNING_CLIENT_CERT` | `""` | Client certificate for cert auth |
| `UEFI_SIGNING_CLIENT_KEY` | `""` | Client key for cert auth |
| `UEFI_COMPONENT_TYPE` | `kernel` | Component identifier (auto-set by bbclass: "kernel" or "grub") |
| `UEFI_SIGNING_TIMEOUT` | `300` | Request timeout (seconds) |
| `UEFI_SIGNING_RETRIES` | `3` | Number of retry attempts |
| `UEFI_SIGNING_RETRY_DELAY` | `5` | Delay between retries (seconds) |
| `UEFI_VERIFY_SIGNED` | `1` | Verify signature after signing |

## Security Best Practices

1. **Use HTTPS**: Always use TLS for signing server communication
2. **Rotate Tokens**: Regularly rotate authentication tokens
3. **Audit Logs**: Enable server-side audit logging of all signing requests
4. **Network Isolation**: Place signing server in isolated network segment
5. **Rate Limiting**: Implement rate limiting on signing endpoint
6. **HSM Storage**: Store private keys in Hardware Security Module
7. **Access Control**: Restrict signing server access by IP/certificate

## Troubleshooting

### Build fails with "signing server timeout"
- Check network connectivity to signing server
- Increase `UEFI_SIGNING_TIMEOUT`
- Check signing server logs

### Build fails with "authentication failed"
- Verify `UEFI_SIGNING_AUTH_TOKEN` is set correctly
- Check token hasn't expired
- Verify token has permissions for signing

### Signed binary verification fails
- Check server is using correct signing key
- Verify certificate chain is valid
- Check `UEFI_SB_DB_CERT` matches server's signing certificate

## Local Signing (Development Only)

For development/testing, you can use local signing:

```bitbake
# Disable remote signing
UEFI_REMOTE_SIGNING = "0"

# Set local keys
UEFI_SB_DB_KEY = "/path/to/db.key"
UEFI_SB_DB_CERT = "/path/to/db.crt"
```

**Warning:** Local signing requires private keys on the build server. Only use for development!

## Example Signing Server Implementation

See `scripts/signing-server-example/` for a reference Flask-based signing server implementation.

## See Also

- [secureboot.inc](secureboot.inc) - Main secure boot configuration
- [remote-signing.inc](remote-signing.inc) - Remote signing variables
- [kernel-uefi-sign.bbclass](../../classes/kernel-uefi-sign.bbclass) - Kernel signing class
3 changes: 3 additions & 0 deletions conf/distro/nilrt.inc
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ DISTRO_FEATURES += "\
ptest \
selinux \
virtualization \
uefi-secure-boot \
"

# Enable TPM2 features for x64 architectures
Expand Down Expand Up @@ -171,3 +172,5 @@ IPK_FEED_URIS += "\
NIOE-${TUNE_PKGARCH}##${NILRT_LOCAL_FEED_URI}/${TUNE_PKGARCH} \
NI-main-software##${NILRT_MACHINE_FEED_URI_nimain} \
"

include ${@bb.utils.contains('DISTRO_FEATURES', 'uefi-secure-boot', 'secureboot.inc', '', d)}
Loading