56 Commits

Author SHA1 Message Date
Jacky
69cfa82b1d feat: self-signed certificate support (#1655) (#1688)
* feat(cert): add self-signed certificate type and config to model

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(cert): generate self-signed leaf certificates

Add GenerateSelfSigned / SelfSignedOptions plus five new error codes
(50032-50036) and a full TDD test suite covering valid cert output,
multiple key types, empty-SAN rejection, and invalid-IP rejection.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(cert): regenerate self-signed certificates with key reuse

Add RegenerateSelfSigned, SelfSignedOptionsFromModel, deriveSelfSignedCommonName,
loadSelfSignedKey, and parsePrivateKeyPEM to support re-issuing self-signed
certificates for the auto-renewal job, reusing the on-disk private key when possible.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(cert): add self-signed certificate renewal worker

Add auto-renewal worker for self-signed certificates that mirrors the
ACME renewal logic, using a dedicated shouldRenewSelfSignedCert threshold
function verified with TDD.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(cron): schedule self-signed certificate renewal

Register setupSelfSignedCertRenewalJob as a periodic cron job (every
30 minutes) in InitCronJobs, mirroring the existing setupAutoCertJob
pattern.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(api): add self-signed certificate generation endpoints

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(cert): add self-signed certificate frontend API

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(cert): add shared self-signed certificate fields component

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(cert): add self-signed certificate generation modal and list entry

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(cert): support self-signed certificates in the editor

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(site): generate self-signed certificates from the site editor

Extract hasTLSListen/ensureDirective/ensureTLSDirectives into a shared
useTLSDirectives composable, refactor ObtainCert.vue to use it, and add
SelfSignedCert.vue to the site cert tab so users can generate and apply
a self-signed certificate directly from the site editor.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(cert): validate self-signed key type and name IP-only renewals

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* refactor(cert): apply code-review cleanup

- reuse certcrypto.ParsePEMPrivateKey instead of a hand-rolled PEM
  private-key parser
- stop exporting the unused ensureDirective from useTLSDirectives
- use the AutoCertState enum instead of integer literals in certColumns
- allocate the renewal Logger only when renewal is attempted, avoiding a
  per-tick goroutine and empty-log database write for non-due certificates

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(cert): address PR #1688 review feedback

- clean up the partial certificate directory when the initial write
  fails, not just the database row
- log a warning when the existing self-signed private key cannot be
  reused so operators notice the public-key fingerprint has changed
- defensively copy the model's Domains and IPAddresses slices in
  SelfSignedOptionsFromModel
- require an explicit "Save now" confirmation after generating from the
  site editor, and write the directives into the editor first so the
  user can review the diff before saving

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(cert): harden self-signed certificate lifecycle

Reuse private keys on manual self-signed edits, make certificate writes safer, clean managed self-signed files on delete, and guard renewal against missing config.

* fix(cert): harden self-signed frontend handling

Avoid undefined certificate redirects, rely on payload defaults for self-signed fields, and parse TLS listen directives precisely.

* fix(site): satisfy strict listen regex lint

Escape the IPv6 listen closing bracket explicitly so the strict regexp lint rule accepts TLS listen parsing.

* fix(cert): harden self-signed key handling

Co-authored-by: Jacky <me@jackyu.cn>

* docs(cert): design merging self-signed entry into issue dialog

Spec for collapsing the Certificate list header from three actions to
two by adding a Self-signed option inside the existing Issue Certificate
dialog.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* docs(cert): plan merging self-signed into issue dialog

Step-by-step plan that turns the spec into two scoped commits:
extend DNSIssueCertificate with a self-signed type, then drop the
standalone header button from the certificate list view.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(cert): add self-signed option in issue certificate dialog

Extend the Issue Certificate dialog's Certificate Type select with a
"Self-signed" option that swaps the form body to SelfSignedCertFields
and routes submission through cert.generate_self_signed(). ACME paths
(Wildcard / Custom Domains) are unchanged.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* refactor(cert): drop standalone self-signed button from list header

Certificate creation is now consolidated under the Issue Certificate
dialog (which exposes Self-signed as a Certificate Type option), so
the duplicate header entry, its ref, handler, and modal mount are
removed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* docs(cert): design self-signed UX enhancements

Adds a reusable StringListInput, renewal-policy hint in the self-signed
form, and a required Name field (frontend + backend). Builds on the
prior merge spec.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* docs(cert): plan self-signed UX enhancements

Six-task plan: extract StringListInput, require Name backend + test,
refactor SelfSignedCertFields with renewal hint, hide duplicate alert
in editor, seed/filter payloads with Name validation, and adopt
StringListInput in the ACME Custom Domains branch.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(ui): add StringListInput component

Reusable multi-row text input with Add/Remove buttons. Used in the
upcoming refactor of Custom Domains and self-signed Domains / IP
Addresses editors so all three share a single editor pattern.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(ui): simplify StringListInput model write and add a11y label

Replace the captured-index update closure with v-model:value on
items[index] so input events are guaranteed to write to the array
slot currently bound to the DOM input. Add an aria-label suffix
on the Remove button so screen readers can distinguish rows.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(cert): require Name when generating self-signed certificates

Adds binding:"required" to SelfSignedCertRequest.Name so an empty name
is rejected at the request boundary, and covers the contract with a
new API-level test.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(cert): unify self-signed editor and surface renewal hint

Switch Domains and IP Addresses to the shared StringListInput so all
self-signed field editors match the Custom Domains pattern. Add an
auto-renewal hint (suppressible via hideRenewalNote) and mark Name as
required to match the new backend contract.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* chore(cert): suppress duplicate renewal alert in cert editor

SelfSignedCertManagement already has its own renewal-status alert;
pass hide-renewal-note to SelfSignedCertFields to avoid showing two
adjacent alerts saying the same thing.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(cert): seed and filter self-signed payloads, validate Name

StringListInput preserves empty placeholder rows for editing; seed
arrays with [''] in toSelfSignedPayload / emptySelfSignedPayload /
emptyForm so the editor always renders an empty row to type into.

Each submit/save path trims and filters the arrays before sending and
now rejects an empty Name client-side to match the new server contract.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* refactor(cert): make SelfSignedCertPayload.name required

Every factory already seeds name as ''; the optional marker forced
defensive (name ?? '').trim() at three call sites. Align the type
with reality.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* refactor(cert): use StringListInput for Custom Domains

Drop the inline multi-row template + add/remove helpers in favour of
the shared StringListInput component, matching the editor used by the
self-signed branch.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* chore(ui): regenerate components.d.ts for StringListInput

Auto-generated by unplugin-vue-components after the new component
was added under app/src/components/StringListInput/.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(cert): render key_type for both legacy and canonical forms

The backend's helper.GetKeyType normalizes key_type to its canonical
form (EC256, RSA2048…) on every write — self-signed generation as well
as the ModifyCert BeforeExecuteHook. The frontend PrivateKeyTypeMask
was keyed only by the legacy form (P256, 2048…), so maskRender returned
"/" for every cert that took a write path through normalization.

Two reported symptoms with the same root cause:
- New self-signed cert always shows "/" in the Key Type column
- Editing any ACME cert (issue #1697) flips its column to "/" after save

Add formatPrivateKeyType / normalizePrivateKeyType helpers that map both
forms to the frontend's legacy key. Use them in the list column renderer
and when loading certs into the self-signed and ACME editor forms so the
ASelect highlights the correct option.

Fixes #1697.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* style(cert): cap self-signed fields width at 600px

The fields stretched full-width inside the certificate editor page; cap
the form at 600px to match AutoCertManagement and keep the editing area
readable. Modal consumers were already bounded by their own width, so
the change is invisible there.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* chore: update translations

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Hintay <hintay@me.com>
2026-05-24 09:22:21 +08:00
Hintay
013634e8ca feat(cert): preserve config and add retry on issuance failure (#1694)
* feat(cert): add Status, LastError, LastAttemptAt fields

* feat(cert): sweep stale pending certs at startup

* feat(cert): invoke SweepStalePending at cron startup

* feat(cert): skip non-success status in auto-renew worker

* feat(cert): persist draft on issuance entry, status transitions on completion

* feat(cert): expose status, last_error, last_attempt_at on Cert type

* feat(cert): show Pending/Failed status badges in cert list

* feat(cert): add RetryCert component and wire into list actions

* feat(cert): inline Retry button on issuance error in wildcard modal

* chore(cert): minor cleanups after retry-on-failure review

- Remove unused model.FirstOrInit helper (last caller was rewritten in the issuance handler change).
- Normalize cleanup_test setupTestDB DSN to ":memory:" for per-test isolation, matching issue_test.go.
- Reset errored state in DNSIssueCertificate.open() as a defensive guard against stale state on modal reopen.

* refactor(cert): extract IssueCertModal wrapper shared by Renew and Retry

Both RenewCert.vue and RetryCert.vue carried near-identical AModal +
ObtainCertLive scaffolding (modalVisible/modalClosable refs, template ref,
modal props). Lift the shared shell into IssueCertModal.vue and expose a
single start() method returning Promise<CertificateResult>. The trigger
components now own only the parts that actually differ: button styling,
emit name, pre-issuance hook (certStore.save for Renew), and success toast.

* chore(cert): fix small bugs with review

- shortError now truncates by rune count instead of bytes, so non-ASCII
  error messages (e.g. localized ACME / DNS provider errors) cannot be
  split mid-rune. TestShortError gains a CJK case asserting valid UTF-8.
- Cert.last_attempt_at is typed string | null on the frontend to reflect
  that the *time.Time pointer serializes as null for legacy / pre-attempt
  rows.
- Drop redundant ?. on refModal / refObtainCertLive in the three click
  handlers. The refs are bound to components rendered alongside their
  trigger button, so they are guaranteed to be mounted by the time the
  handler fires.

* fix(cert): guard certificate issuance ref before retry

Co-authored-by: Jacky <me@jackyu.cn>

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Jacky <me@jackyu.cn>
2026-05-23 20:34:52 +08:00
0xJacky
3f04b3e776 fix(cert): normalize legacy key types 2026-05-12 14:52:25 +00:00
0xJacky
9f1b9bbbba refactor(ws): implement SafeWebSocketWriter for serialized access
- Introduced SafeWebSocketWriter to ensure thread-safe writes to WebSocket connections.
- Updated WebSocket handling in certificate issuance, revocation, Nginx log tailing, and system upgrades to use the new writer.
- Enhanced WebSocket client management in the site navigation module for improved message handling and connection stability.
2026-04-04 02:01:20 +00:00
0xJacky
cb92cc4da8 feat: add ACMEUserID to certificate issuance and auto-certification requests 2026-03-16 00:52:12 +00:00
0xJacky
413dc631ee feat: add WebSocketTrustedOrigins configuration and implement origin validation for WebSocket connections
- Introduced `WebSocketTrustedOrigins` setting in `app.example.ini` and corresponding documentation.
- Refactored WebSocket origin checks across multiple API endpoints to utilize the new middleware for improved security.
- Added tests for the new origin validation logic to ensure proper handling of trusted origins and node secret requests.
2026-03-14 23:37:10 +08:00
0xJacky
cb1fb691af refactor: improve provider selection logic in DNSChallenge component and update column definition in ACMEUser view 2025-12-09 17:35:47 +08:00
0xJacky
b8cfd93b60 fix: logging middleware context 2025-09-11 22:50:19 +08:00
0xJacky
38ee12f587 feat: add EAB supports for ACME user register #1255 2025-08-02 21:18:32 +08:00
0xJacky
16aed0c1fe enhance(dns_credential): add provider and name filtering options 2025-07-29 10:16:12 +08:00
Jacky
f91e551066 fix(cert): update revoke logic #1179 2025-06-26 10:53:44 +08:00
Jacky
55d54766f8 refactor: auto-cert handling logic #1095, #1104 2025-06-05 19:09:31 +08:00
Jacky
c241122fde fix(cert): maybe panic when add or modify #1034 2025-05-09 20:40:40 +08:00
Jacky
e8f1321e0c enhance: translations 2025-04-24 22:47:27 +08:00
Jacky
b63dbe1e50 refactor: refresh 25.04 2025-04-19 17:58:36 +08:00
Jacky
c073801794 feat: revoke certificate #293 2025-04-10 16:16:06 +08:00
Jacky
771859d3b8 feat(certificate): add certificate type detection and update logic in AddCert and ModifyCert functions 2025-04-06 01:08:14 +00:00
Jacky
b469ca7961 refactor: replace api error handler with cosy err handler 2025-03-29 20:33:48 +08:00
Jacky
2026f0d3ac feat(cert): improve certificate renewal and logging #896 2025-03-03 17:03:08 +08:00
Jacky
6c7edf5c25 enhance(cert): do not create cert in db if issue failed 2025-03-03 16:26:15 +08:00
Jacky
ab46d1de7e refactor: notification module 2025-02-25 19:06:39 +08:00
Jacky
bc70567dc1 feat(wip): node selector supports sse 2024-11-16 10:58:02 +08:00
Jacky
f4c17f0c65 enhance: save certificate before renew #723 2024-11-16 10:51:35 +08:00
Jacky
36221937bf fix: some certificate routes are incorrect #669 2024-10-28 17:03:59 +08:00
Jacky
7ad5cac3b8 feat(wip): site category 2024-10-26 10:39:31 +08:00
Jacky
c5f5038d35 style(go): sort imports 2024-10-24 18:22:45 +08:00
Jacky
33a996e777 refactor: migrate to new cosy 2024-10-22 16:38:38 +08:00
Jacky
598d91a417 style: format go code with tab indent #605 2024-10-14 10:14:43 +08:00
Jacky
46a9ccbdf1 feat: add proxy option for acme user 2024-10-07 12:19:20 +08:00
Jacky
013d810678 fix: uncontrolled data used in path expression 2024-07-30 15:07:14 +08:00
Jacky
4660a46a7e refactor: auto certificate options
1. Add OCSP Must Staple options #292
2. Add LEGO_DISABLE_CNAME_SUPPORT options #407
2024-07-24 22:53:22 +08:00
Jacky
ff1399e044 enhance: handle websocket error 2024-07-20 18:35:06 +08:00
Jacky
65befe282b fix: update cert model bug 2024-06-18 18:40:49 +08:00
Jacky
b3486a42a5 feat: sync certificates to remote server #293, #363, #411 2024-06-18 17:39:05 +08:00
Jacky
11e460765a fix: certificate dir cannot be created in some windows-docker setup #403 2024-06-15 12:26:12 +08:00
Jacky
805934567a fix: maybe fail to get get default ca dir #417 2024-06-15 11:24:37 +08:00
Jacky
80a8b1de97 fix: missing provider in dns credential viewer 2024-06-10 20:34:55 +08:00
Jacky
538be53e9c fix: renewed certificate and private key not save #391 2024-05-21 14:05:27 +08:00
Jacky
be5eae0df9 fix: obtain certificate issue #368 2024-05-02 20:43:53 +08:00
Jacky
e16b077d20 feat: using renewal api to renew certificate #319 2024-04-30 19:48:48 +08:00
Jacky
3e90b838fd feat: SSL management support different types of certificates of a same doamin name #309 2024-04-30 16:09:40 +08:00
Jacky
f0dcd67004 feat: add a option to select a acme user when obtaining a certificate. 2024-04-30 11:16:30 +08:00
Jacky
4c7e037b76 refactor: refresh webui 2024-04-30 10:11:51 +08:00
Jacky
97ec26331b fix: wildcard certificate challenge method not present 2024-02-14 17:40:04 +08:00
Jacky
22d62e420b feat: configurable cert key type #264 2024-02-13 14:15:02 +08:00
0xJacky
2526d71c0e fix: unable to create a certificate from a second environment #273 2024-02-06 16:20:46 +08:00
0xJacky
371472e67b fix: parse ssl certificate error #270 2024-02-06 15:47:38 +08:00
0xJacky
398eea2159 enhance: show the form error details of Preference 2024-01-28 15:01:46 +08:00
0xJacky
b0a3989ef4 enhance: show the form error details of CertificateEditor 2024-01-28 14:33:21 +08:00
0xJacky
8581bdd3c6 enhance: validate certificate content before save 2024-01-28 14:00:47 +08:00