feat: allow domain suffix to be locked down#8
Merged
Conversation
Contributor
There was a problem hiding this comment.
Pull request overview
Adds an optional CA “name suffix” policy that locks certificate issuance (CN + DNS SANs) to a configured suffix (e.g. .local), persisted in the CA store and enforced centrally during issuance. The TUI/API/dashboard surface the active policy and the test suite is expanded to cover policy normalization and enforcement.
Changes:
- Add
name_suffixpersistence (meta.name_suffix) and enforcement inca.issue_certagainst CN + DNS SANs (IP SANs exempt). - Expose policy in the TUI init flow and in issuance/status UI, plus API dashboard/designer warning.
- Add tests and update docs/CI to cover the new feature.
Reviewed changes
Copilot reviewed 13 out of 13 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
tests/test_2_store.py |
Adds tests for suffix persistence, normalization/matching helpers, and CA enforcement behavior. |
ssltui/tui.py |
Adds init input for suffix and displays active policy during issue/status/serve views. |
ssltui/store.py |
Persists name_suffix in the meta table with get/set helpers. |
ssltui/config.py |
Introduces normalization and suffix-matching helpers used by CA enforcement and tests. |
ssltui/ca.py |
Enforces suffix policy during issuance and stores policy during init. |
ssltui/api.py |
Displays policy on dashboard and warns in API Designer UI when names violate policy. |
README.md |
Documents the new optional suffix restriction during init. |
docs/TUI.md |
Documents the init/issue UI behavior for the suffix policy. |
docs/TODO.md |
Removes outdated test-harness TODO section. |
docs/API.md |
Documents the new 400 error case for policy violations. |
CLAUDE.md |
Updates architecture notes to include the suffix policy and sqlite persistence wording. |
.github/workflows/pr.yaml |
Runs pytest with --all-extras to include optional dependencies in CI. |
.gitattributes |
Sets * text=auto to normalize text handling. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
1085
to
+1088
| try: | ||
| subj = ca_subject(root) | ||
| expiry = ca_expiry(root) | ||
| fp = ca_fingerprint(root) | ||
| info = f"Subject: {subj} | Expires: {expiry} | SHA256: {fp}" | ||
| info = f"Subject: {subj} | Expires: {expiry}" |
Comment on lines
973
to
983
| try: | ||
| subj = ca_subject(root) | ||
| expiry = ca_expiry(root) | ||
| fp = ca_fingerprint(root) | ||
| suffix = store.get_name_suffix(root) | ||
| suffix_part = ( | ||
| f" names [b].{suffix}[/b]" if suffix else " names [dim]any[/dim]" | ||
| ) | ||
| status.update( | ||
| f"[green]CA ready[/green] [b]{subj}[/b] " | ||
| f"expires [b]{expiry}[/b] SHA256: {fp[:29]}…" | ||
| f"expires [b]{expiry}[/b]{suffix_part}" | ||
| ) |
Comment on lines
+531
to
+549
| def _enforce_name_suffix(root: Path, cn: str, san_list: list[str]) -> None: | ||
| """Reject the request if the CN or any DNS SAN escapes the CA's policy. | ||
|
|
||
| IP SANs are exempt — a name-suffix policy only constrains DNS hostnames. | ||
| No-op when the CA was initialised without a suffix restriction. | ||
| """ | ||
| from ssltui.store import get_name_suffix | ||
|
|
||
| suffix = get_name_suffix(root) | ||
| if not suffix: | ||
| return | ||
|
|
||
| names = [cn] + [e[4:] for e in san_list if e.startswith("DNS:")] | ||
| offenders = [n for n in names if not config.name_matches_suffix(n, suffix)] | ||
| if offenders: | ||
| uniq = ", ".join(dict.fromkeys(offenders)) | ||
| raise CAError( | ||
| f"name(s) not permitted by CA policy (must be under .{suffix}): {uniq}" | ||
| ) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Changes