mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-26 10:21:52 +00:00
feat: add Secure Boot MOK key enrollment and module signing
Generate DKMS MOK key pair during OS install, sign all unsigned kernel modules, and enroll the MOK certificate using the user's master password. On reboot, MokManager prompts the user to complete enrollment. Re-enrolls on every boot if the key exists but isn't enrolled yet. Adds setup wizard dialog to inform the user about the MokManager prompt.
This commit is contained in:
@@ -40,6 +40,7 @@ export class AppComponent {
|
||||
this.stateService.dataDriveGuid = status.guid
|
||||
}
|
||||
this.stateService.attach = status.attach
|
||||
this.stateService.mokEnrolled = status.mokEnrolled
|
||||
await this.router.navigate(['/language'])
|
||||
break
|
||||
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
import { Component } from '@angular/core'
|
||||
import { i18nPipe } from '@start9labs/shared'
|
||||
import { TuiButton, TuiDialogContext, TuiIcon } from '@taiga-ui/core'
|
||||
import { injectContext } from '@taiga-ui/polymorpheus'
|
||||
|
||||
@Component({
|
||||
standalone: true,
|
||||
imports: [TuiButton, TuiIcon, i18nPipe],
|
||||
template: `
|
||||
<div class="icon-container">
|
||||
<tui-icon icon="@tui.shield-check" class="mok-icon" />
|
||||
</div>
|
||||
<h3>{{ 'Secure Boot Key Enrollment' | i18n }}</h3>
|
||||
<p>
|
||||
{{
|
||||
'A signing key was enrolled for Secure Boot. On the next reboot, a blue screen (MokManager) will appear.'
|
||||
| i18n
|
||||
}}
|
||||
</p>
|
||||
<ol>
|
||||
<li>Select "Enroll MOK"</li>
|
||||
<li>Select "Continue"</li>
|
||||
<li>{{ 'Enter your StartOS master password when prompted' | i18n }}</li>
|
||||
<li>Select "Reboot"</li>
|
||||
</ol>
|
||||
<footer>
|
||||
<button tuiButton (click)="context.completeWith(true)">
|
||||
{{ 'Got it' | i18n }}
|
||||
</button>
|
||||
</footer>
|
||||
`,
|
||||
styles: `
|
||||
:host {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.icon-container {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.mok-icon {
|
||||
width: 3rem;
|
||||
height: 3rem;
|
||||
color: var(--tui-status-info);
|
||||
}
|
||||
|
||||
h3 {
|
||||
margin: 0 0 0.5rem;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0 0 1rem;
|
||||
color: var(--tui-text-secondary);
|
||||
}
|
||||
|
||||
ol {
|
||||
text-align: left;
|
||||
margin: 0 0 1.5rem;
|
||||
padding-left: 1.5rem;
|
||||
|
||||
li {
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
}
|
||||
|
||||
footer {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
`,
|
||||
})
|
||||
export class MokEnrollmentDialog {
|
||||
protected readonly context = injectContext<TuiDialogContext<boolean>>()
|
||||
}
|
||||
@@ -390,6 +390,7 @@ export default class DrivesPage {
|
||||
|
||||
this.stateService.dataDriveGuid = result.guid
|
||||
this.stateService.attach = result.attach
|
||||
this.stateService.mokEnrolled = result.mokEnrolled
|
||||
|
||||
loader.unsubscribe()
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ import { ApiService } from '../services/api.service'
|
||||
import { StateService } from '../services/state.service'
|
||||
import { DocumentationComponent } from '../components/documentation.component'
|
||||
import { MatrixComponent } from '../components/matrix.component'
|
||||
import { MokEnrollmentDialog } from '../components/mok-enrollment.dialog'
|
||||
import { RemoveMediaDialog } from '../components/remove-media.dialog'
|
||||
import { T } from '@start9labs/start-sdk'
|
||||
import { PolymorpheusComponent } from '@taiga-ui/polymorpheus'
|
||||
@@ -275,6 +276,20 @@ export default class SuccessPage implements AfterViewInit {
|
||||
await this.api.exit()
|
||||
}
|
||||
}
|
||||
|
||||
if (this.stateService.mokEnrolled && this.result.needsRestart) {
|
||||
this.dialogs
|
||||
.openComponent<boolean>(
|
||||
new PolymorpheusComponent(MokEnrollmentDialog),
|
||||
{
|
||||
label: 'Secure Boot',
|
||||
size: 's',
|
||||
dismissible: false,
|
||||
closeable: true,
|
||||
},
|
||||
)
|
||||
.subscribe()
|
||||
}
|
||||
} catch (e: any) {
|
||||
this.errorService.handleError(e)
|
||||
}
|
||||
|
||||
@@ -116,6 +116,7 @@ export class MockApiService extends ApiService {
|
||||
return {
|
||||
guid: 'mock-data-guid',
|
||||
attach: !params.dataDrive.wipe,
|
||||
mokEnrolled: false,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -42,6 +42,7 @@ export class StateService {
|
||||
// From install response or status response (incomplete)
|
||||
dataDriveGuid = ''
|
||||
attach = false
|
||||
mokEnrolled = false
|
||||
|
||||
// Set during setup flow
|
||||
setupType?: SetupType
|
||||
@@ -116,6 +117,7 @@ export class StateService {
|
||||
this.keyboard = ''
|
||||
this.dataDriveGuid = ''
|
||||
this.attach = false
|
||||
this.mokEnrolled = false
|
||||
this.setupType = undefined
|
||||
this.recoverySource = undefined
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ export interface InstallOsParams {
|
||||
export interface InstallOsRes {
|
||||
guid: string // data drive guid
|
||||
attach: boolean
|
||||
mokEnrolled: boolean
|
||||
}
|
||||
|
||||
// === Disk Info Helpers ===
|
||||
|
||||
@@ -709,4 +709,8 @@ export default {
|
||||
786: 'Automatisch',
|
||||
787: 'Ausgehender Datenverkehr',
|
||||
788: 'Gateway verwenden',
|
||||
789: 'Secure-Boot-Schlüsselregistrierung',
|
||||
790: 'Ein Signaturschlüssel wurde für Secure Boot registriert. Beim nächsten Neustart erscheint ein blauer Bildschirm (MokManager).',
|
||||
791: 'Geben Sie Ihr StartOS-Master-Passwort ein, wenn Sie dazu aufgefordert werden',
|
||||
792: 'Verstanden',
|
||||
} satisfies i18n
|
||||
|
||||
@@ -709,4 +709,9 @@ export const ENGLISH: Record<string, number> = {
|
||||
'Auto': 786,
|
||||
'Outbound Traffic': 787,
|
||||
'Use gateway': 788,
|
||||
// Secure Boot MOK enrollment
|
||||
'Secure Boot Key Enrollment': 789,
|
||||
'A signing key was enrolled for Secure Boot. On the next reboot, a blue screen (MokManager) will appear.': 790,
|
||||
'Enter your StartOS master password when prompted': 791,
|
||||
'Got it': 792,
|
||||
}
|
||||
|
||||
@@ -709,4 +709,8 @@ export default {
|
||||
786: 'Automático',
|
||||
787: 'Tráfico saliente',
|
||||
788: 'Usar gateway',
|
||||
789: 'Registro de clave de Secure Boot',
|
||||
790: 'Se registró una clave de firma para Secure Boot. En el próximo reinicio, aparecerá una pantalla azul (MokManager).',
|
||||
791: 'Ingrese su contraseña maestra de StartOS cuando se le solicite',
|
||||
792: 'Entendido',
|
||||
} satisfies i18n
|
||||
|
||||
@@ -709,4 +709,8 @@ export default {
|
||||
786: 'Automatique',
|
||||
787: 'Trafic sortant',
|
||||
788: 'Utiliser la passerelle',
|
||||
789: "Enregistrement de la clé Secure Boot",
|
||||
790: "Une clé de signature a été enregistrée pour Secure Boot. Au prochain redémarrage, un écran bleu (MokManager) apparaîtra.",
|
||||
791: 'Entrez votre mot de passe principal StartOS lorsque vous y êtes invité',
|
||||
792: 'Compris',
|
||||
} satisfies i18n
|
||||
|
||||
@@ -709,4 +709,8 @@ export default {
|
||||
786: 'Automatycznie',
|
||||
787: 'Ruch wychodzący',
|
||||
788: 'Użyj bramy',
|
||||
789: 'Rejestracja klucza Secure Boot',
|
||||
790: 'Klucz podpisu został zarejestrowany dla Secure Boot. Przy następnym uruchomieniu pojawi się niebieski ekran (MokManager).',
|
||||
791: 'Wprowadź swoje hasło główne StartOS po wyświetleniu monitu',
|
||||
792: 'Rozumiem',
|
||||
} satisfies i18n
|
||||
|
||||
Reference in New Issue
Block a user