zetcert 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (4) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +175 -0
  3. package/dist/zetcert.cjs +36039 -0
  4. package/package.json +43 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 ArmCyber
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,175 @@
1
+ # zetcert
2
+
3
+ zetcert manages the Let's Encrypt certificates of an nginx server, on top of certbot.
4
+
5
+ - **nginx decides which certificate a site uses.** A server block includes `/etc/nginx/zetcert/<cert>.conf`, and the certificate gets the `server_name`s of every block that includes it.
6
+ - **certbot does the ACME work and the renewals**, on its own timer. zetcert decides which certificates exist and which names they hold.
7
+ - **zetcert never edits your nginx files.** It writes only its own files in `/etc/nginx/zetcert/`.
8
+ - **After changing nginx, run `sudo zetcert sync`.** It shows the changes, issues what is needed, updates the snippets, runs `nginx -t` and reloads nginx.
9
+
10
+ Every command, setting and file is described in [docs/reference.md](docs/reference.md).
11
+
12
+ ## Requirements
13
+
14
+ Linux with systemd (Debian and Ubuntu are tested), nginx, certbot (`apt install certbot` or the snap, no plugins needed), openssl, and Node.js 20 or newer.
15
+
16
+ ## Install
17
+
18
+ ```sh
19
+ npm install -g zetcert # with sudo when Node is installed system-wide (apt, NodeSource)
20
+ zetcert init # as your normal user, without sudo: it asks for sudo itself
21
+ ```
22
+
23
+ `init` asks for an email, the webroot, the server's public IPs and an optional [alert command](#alerts-and-monitoring), and offers to import the certificates certbot already has. From then on, `sudo zetcert …` works however Node was installed. With nvm or volta, `sudo zetcert` says "command not found" until `init` has run.
24
+
25
+ ## nginx setup
26
+
27
+ **Include a certificate's snippet in each HTTPS server block.** Blocks that include the same snippet share one certificate. Never include the whole directory (`zetcert/*.conf`).
28
+
29
+ ```nginx
30
+ server {
31
+ listen 443 ssl;
32
+ server_name shop.example.com www.shop.example.com;
33
+ include /etc/nginx/zetcert/shop.conf;
34
+ }
35
+ ```
36
+
37
+ **Port 80: answer Let's Encrypt's checks.** Open `/etc/nginx/sites-available/default` and replace everything in it with this. It serves the validation path for every domain and sends all other HTTP traffic to HTTPS. If you keep your own port-80 server instead, add just the `include` line to it.
38
+
39
+ ```nginx
40
+ server {
41
+ listen 80 default_server;
42
+ listen [::]:80 default_server;
43
+ server_name _;
44
+ include /etc/nginx/zetcert/_acme.conf;
45
+ location / { return 301 https://$host$request_uri; }
46
+ }
47
+ ```
48
+
49
+ **Optional, TLS settings** (Mozilla's "intermediate" profile, as certbot's nginx plugin writes them). Open `/etc/nginx/nginx.conf`:
50
+
51
+ - delete the `ssl_protocols` and `ssl_prefer_server_ciphers` lines;
52
+ - inside the `http {` block, right after the `http {` line, add `include /etc/nginx/zetcert/_tls.conf;`.
53
+
54
+ After changing nginx's files: `sudo nginx -t`, then `sudo systemctl reload nginx`.
55
+
56
+ ## Daily use
57
+
58
+ ```sh
59
+ sudo zetcert status # certificates, names, expiry, pending changes and problems
60
+ sudo zetcert sync --dry-run # try the changes against Let's Encrypt's staging server
61
+ sudo zetcert sync # apply them: issue, update the snippets, nginx -t, reload
62
+ ```
63
+
64
+ **sudo without a password:** run `sudo visudo -f /etc/sudoers.d/zetcert` and add this line with your user name. A file of its own leaves your other sudo rules as they are. It amounts to password-free root for that user, since zetcert can be told to run any command as root.
65
+
66
+ ```
67
+ <user> ALL=(root) NOPASSWD: /usr/local/sbin/zetcert
68
+ ```
69
+
70
+ A name that fails the pre-checks (its DNS doesn't point here, nginx doesn't serve it the ACME path, or CAA forbids Let's Encrypt) is left out with a warning, and the rest of its certificate is issued. Fix it and run `sync` again.
71
+
72
+ Names that don't come from nginx, and other options:
73
+
74
+ ```sh
75
+ sudo zetcert update shop --exclude old.shop.example.com
76
+ sudo zetcert create mail --add mail.example.com --deploy 'systemctl reload postfix dovecot'
77
+ ```
78
+
79
+ ## Wildcard certificates
80
+
81
+ A snippet named `wildcard.<domain>.conf` gets a certificate for `<domain>` and `*.<domain>`, validated through a [DNS account](#dns-accounts):
82
+
83
+ ```nginx
84
+ server { server_name app.example.net; include /etc/nginx/zetcert/wildcard.example.net.conf; }
85
+ server { server_name api.eu.example.net; include /etc/nginx/zetcert/wildcard.example.net.conf; }
86
+ ```
87
+
88
+ This gives `example.net`, `*.example.net` and `*.eu.example.net`. A new `x.example.net` server block never needs a new certificate.
89
+
90
+ ## DNS accounts
91
+
92
+ For DNS validation, zetcert creates the `_acme-challenge` TXT records itself, through a DNS account:
93
+
94
+ ```sh
95
+ sudo zetcert dns add cf --driver cloudflare # asks for the API token
96
+ sudo zetcert dns add aws --driver route53 # asks for the access key id and secret
97
+ sudo zetcert dns test cf app.example.net # creates and deletes a test record
98
+ ```
99
+
100
+ - **Cloudflare:** an API token with **Zone → DNS → Edit**, limited to the zones needed.
101
+ - **Route 53:** an access key (or none, to use the instance role) with [this policy](docs/reference.md#route-53-policy).
102
+ - **Behind Cloudflare's proxy,** HTTP validation depends on Cloudflare's settings; DNS validation doesn't: `sudo zetcert update <cert> --challenge dns`.
103
+
104
+ DNS-validated certificates renew through zetcert, so keep it installed.
105
+
106
+ ## Alerts and monitoring
107
+
108
+ Let's Encrypt no longer sends expiry emails: **without `notify`, nothing tells you when renewals fail.**
109
+
110
+ `notify` in `/etc/zetcert/config.yml` is a command that gets the alert on stdin: when a certificate isn't renewing or is about to expire, when nginx couldn't be reloaded after a renewal, or when a deploy command failed. For Telegram:
111
+
112
+ ```yaml
113
+ notify: curl -sS --fail-with-body --max-time 30 -K /etc/zetcert/telegram.curl --data-urlencode text@-
114
+ ```
115
+
116
+ with the token in `/etc/zetcert/telegram.curl` (mode 0600, so it stays out of `ps`):
117
+
118
+ ```
119
+ url = "https://api.telegram.org/bot<TOKEN>/sendMessage"
120
+ data = "chat_id=<CHAT_ID>"
121
+ ```
122
+
123
+ For ntfy: `notify: curl -sS --fail-with-body --max-time 30 -K /etc/zetcert/ntfy.curl --data-binary @-`, with `url = "https://ntfy.sh/<random-topic>"` in that file (topics are public, so pick a random name). By email, if the server can send mail: `notify: mail -s "zetcert alert" you@example.com`. `sudo zetcert notify --test` sends a test alert.
124
+
125
+ - `sudo zetcert status --check` exits with 0 (healthy), 1 (warning) or 2 (critical), for monitoring.
126
+ - `sudo zetcert doctor` checks everything: the install, certbot's timer, nginx, DNS accounts, the certificates and what nginx serves.
127
+ - A renewal failed? Fix the cause, then `sudo zetcert sync` renews the certificate.
128
+
129
+ ## Upgrade
130
+
131
+ ```sh
132
+ npm install -g zetcert@latest # with sudo when Node is installed system-wide
133
+ sudo zetcert init # installs the new version
134
+ ```
135
+
136
+ `npm update -g` isn't enough: on 0.x it doesn't even cross a minor version. With nvm, if you installed the new version under another Node version, run `zetcert init` without sudo instead.
137
+
138
+ ## Uninstall
139
+
140
+ ```sh
141
+ sudo zetcert uninstall # removes zetcert and its certbot hooks; the config, snippets and certificates stay
142
+ npm uninstall -g zetcert # with sudo when Node is installed system-wide
143
+ ```
144
+
145
+ nginx keeps working, and certbot keeps renewing the HTTP-validated certificates and reloading nginx. `uninstall` lists the deploy commands that stop running, and refuses while DNS-validated certificates need zetcert (`--force` goes on; their renewals then fail).
146
+
147
+ If `sudo zetcert uninstall` says "command not found", `init` never ran: only the npm package is there.
148
+
149
+ **To remove every trace:**
150
+
151
+ 1. In your nginx config, replace each `include /etc/nginx/zetcert/….conf;` with that file's lines, except the ones pointing into `/etc/nginx/zetcert` or `/var/lib/zetcert` (the `ssl_dhparam` of `_tls.conf`, a placeholder certificate). Keep the ACME `location`: certbot renews through it.
152
+ 2. `sudo grep -rnE '/etc/nginx/zetcert|/var/lib/zetcert' /etc/nginx --exclude-dir=zetcert` must print nothing.
153
+ 3. `sudo rm -rf /etc/zetcert /etc/nginx/zetcert /var/lib/zetcert`, then `sudo nginx -t && sudo systemctl reload nginx`.
154
+
155
+ ## Commands
156
+
157
+ | Command | |
158
+ |---|---|
159
+ | `status [cert]` | certificates, names, expiry, pending changes and problems; `--check` for monitoring, `--json` |
160
+ | `sync [cert…]` | apply nginx changes: issue, update the snippets, `nginx -t`, reload; `--dry-run`, `--no-precheck`, `--strict` |
161
+ | `init` | install or upgrade zetcert, create the config, write zetcert's files and hooks |
162
+ | `import <name…>`, `import --all` | take over certificates certbot already has |
163
+ | `create <cert>` | register a certificate before any nginx include exists |
164
+ | `update <cert>` | change a certificate's options (`--add`, `--exclude`, `--challenge`, `--dns`, `--key-type`, `--deploy`) |
165
+ | `delete <cert>` | delete a certificate nginx no longer includes |
166
+ | `dns add`, `dns list`, `dns test`, `dns remove` | manage DNS accounts |
167
+ | `doctor` | full health check |
168
+ | `notify --test` | send a test alert |
169
+ | `uninstall` | remove zetcert, keep the certificates |
170
+
171
+ Global options: `-y` (don't ask), `--json`, `-v` (certbot's output and skipped names), `-q`, `--config <path>`, `--no-color`, `--version`.
172
+
173
+ ## License
174
+
175
+ MIT