Implement a two-level key hierarchy: the MEK now wraps per-engine DEKs stored in a new barrier_keys table, rather than encrypting all barrier entries directly. A v2 ciphertext format (0x02) embeds the key ID so the barrier can resolve which DEK to use on decryption. v1 ciphertext remains supported for backward compatibility. Key changes: - crypto: EncryptV2/DecryptV2/ExtractKeyID for v2 ciphertext with key IDs - barrier: key registry (CreateKey, RotateKey, ListKeys, MigrateToV2, ReWrapKeys) - seal: RotateMEK re-wraps DEKs without re-encrypting data - engine: Mount auto-creates per-engine DEK - REST + gRPC: barrier/keys, barrier/rotate-mek, barrier/rotate-key, barrier/migrate - proto: BarrierService (v1 + v2) with ListKeys, RotateMEK, RotateKey, Migrate - db: migration v2 adds barrier_keys table Also includes: security audit report, CSRF protection, engine design specs (sshca, transit, user), path-bound AAD migration tool, policy engine enhancements, and ARCHITECTURE.md updates. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
111 lines
4.9 KiB
HTML
111 lines
4.9 KiB
HTML
{{define "title"}} - Policy{{end}}
|
|
{{define "content"}}
|
|
<div class="page-header">
|
|
<h2>Policy Rules</h2>
|
|
<div class="page-meta">
|
|
<a href="/dashboard" class="btn btn-ghost btn-sm">← Dashboard</a>
|
|
</div>
|
|
</div>
|
|
|
|
{{if .Error}}<div class="error">{{.Error}}</div>{{end}}
|
|
|
|
<div class="card">
|
|
<div class="card-title">Active Rules</div>
|
|
{{if .Rules}}
|
|
<div class="table-wrapper">
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>ID</th>
|
|
<th>Priority</th>
|
|
<th>Effect</th>
|
|
<th>Usernames</th>
|
|
<th>Roles</th>
|
|
<th>Resources</th>
|
|
<th>Actions</th>
|
|
<th></th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{{range .Rules}}
|
|
<tr>
|
|
<td><code>{{.ID}}</code></td>
|
|
<td>{{.Priority}}</td>
|
|
<td><span class="badge {{if eq .Effect "allow"}}badge-ok{{else}}badge-warn{{end}}">{{.Effect}}</span></td>
|
|
<td>{{if .Usernames}}{{range $i, $u := .Usernames}}{{if $i}}, {{end}}{{$u}}{{end}}{{else}}<em>any</em>{{end}}</td>
|
|
<td>{{if .Roles}}{{range $i, $r := .Roles}}{{if $i}}, {{end}}{{$r}}{{end}}{{else}}<em>any</em>{{end}}</td>
|
|
<td>{{if .Resources}}{{range $i, $r := .Resources}}{{if $i}}, {{end}}<code>{{$r}}</code>{{end}}{{else}}<em>any</em>{{end}}</td>
|
|
<td>{{if .Actions}}{{range $i, $a := .Actions}}{{if $i}}, {{end}}{{$a}}{{end}}{{else}}<em>any</em>{{end}}</td>
|
|
<td>
|
|
<form method="post" action="/policy/delete" style="display:inline">
|
|
{{csrfField}}
|
|
<input type="hidden" name="id" value="{{.ID}}">
|
|
<button type="submit" class="btn-danger btn-sm" onclick="return confirm('Delete rule {{.ID}}?')">Delete</button>
|
|
</form>
|
|
</td>
|
|
</tr>
|
|
{{end}}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
{{else}}
|
|
<p>No policy rules defined. Admins always have full access. Non-admin users are denied by default unless a matching allow rule exists.</p>
|
|
{{end}}
|
|
</div>
|
|
|
|
<div class="card">
|
|
<div class="card-title">Create Rule</div>
|
|
<details>
|
|
<summary>Add a new policy rule</summary>
|
|
<form method="post" action="/policy">
|
|
{{csrfField}}
|
|
<div class="form-row">
|
|
<div class="form-group">
|
|
<label for="rule_id">Rule ID <span class="required">*</span></label>
|
|
<input type="text" id="rule_id" name="id" placeholder="allow-users-read" required>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="rule_priority">Priority</label>
|
|
<input type="number" id="rule_priority" name="priority" value="50" min="0" max="9999">
|
|
<small>Lower number = higher priority. Default: 50.</small>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="rule_effect">Effect <span class="required">*</span></label>
|
|
<select id="rule_effect" name="effect" required>
|
|
<option value="allow">allow</option>
|
|
<option value="deny">deny</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
<div class="form-row">
|
|
<div class="form-group">
|
|
<label for="rule_usernames">Usernames</label>
|
|
<input type="text" id="rule_usernames" name="usernames" placeholder="alice, bob">
|
|
<small>Comma-separated. Leave blank to match any user.</small>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="rule_roles">Roles</label>
|
|
<input type="text" id="rule_roles" name="roles" placeholder="user, guest">
|
|
<small>Comma-separated. Leave blank to match any role.</small>
|
|
</div>
|
|
</div>
|
|
<div class="form-row">
|
|
<div class="form-group">
|
|
<label for="rule_resources">Resources</label>
|
|
<input type="text" id="rule_resources" name="resources" placeholder="engine/pki/*, engine/pki/list-certs">
|
|
<small>Comma-separated glob patterns. Leave blank to match any resource.</small>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="rule_actions">Actions</label>
|
|
<input type="text" id="rule_actions" name="actions" placeholder="read, write">
|
|
<small>Comma-separated: <code>read</code> or <code>write</code>. Leave blank to match any action.</small>
|
|
</div>
|
|
</div>
|
|
<div class="form-actions">
|
|
<button type="submit">Create Rule</button>
|
|
</div>
|
|
</form>
|
|
</details>
|
|
</div>
|
|
{{end}}
|