certmate-cli 0.1.5__tar.gz → 0.1.6__tar.gz
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.
- {certmate_cli-0.1.5 → certmate_cli-0.1.6}/.gitignore +2 -0
- {certmate_cli-0.1.5 → certmate_cli-0.1.6}/PKG-INFO +10 -3
- {certmate_cli-0.1.5 → certmate_cli-0.1.6}/README.md +8 -1
- {certmate_cli-0.1.5 → certmate_cli-0.1.6}/certmate_cli/__init__.py +1 -1
- {certmate_cli-0.1.5 → certmate_cli-0.1.6}/certmate_cli/main.py +28 -6
- {certmate_cli-0.1.5 → certmate_cli-0.1.6}/pyproject.toml +4 -4
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: certmate-cli
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.6
|
|
4
4
|
Summary: CertMate command-line interface — the SSL certificate lifecycle from your terminal
|
|
5
5
|
Project-URL: Homepage, https://github.com/fabriziosalmi/certmate
|
|
6
6
|
Project-URL: Source, https://github.com/fabriziosalmi/certmate/tree/main/clients/certmate-cli
|
|
@@ -23,7 +23,7 @@ Classifier: Topic :: Security
|
|
|
23
23
|
Classifier: Topic :: System :: Systems Administration
|
|
24
24
|
Classifier: Topic :: Utilities
|
|
25
25
|
Requires-Python: >=3.9
|
|
26
|
-
Requires-Dist: certmate-sdk>=0.1.
|
|
26
|
+
Requires-Dist: certmate-sdk>=0.1.6
|
|
27
27
|
Requires-Dist: rich>=13
|
|
28
28
|
Requires-Dist: typer>=0.9
|
|
29
29
|
Description-Content-Type: text/markdown
|
|
@@ -34,7 +34,7 @@ The [CertMate](https://github.com/fabriziosalmi/certmate) SSL certificate
|
|
|
34
34
|
lifecycle from your terminal — built on `certmate-sdk`.
|
|
35
35
|
|
|
36
36
|
```bash
|
|
37
|
-
pip install certmate-cli
|
|
37
|
+
pip install certmate-cli # or, on macOS/Linux: brew install fabriziosalmi/certmate/certmate-cli
|
|
38
38
|
export CERTMATE_URL=http://localhost:8000
|
|
39
39
|
export CERTMATE_TOKEN=...
|
|
40
40
|
|
|
@@ -43,9 +43,16 @@ certmate cert ls
|
|
|
43
43
|
certmate cert info app.example.com
|
|
44
44
|
certmate cert renew app.example.com --force
|
|
45
45
|
certmate cert create app.example.com --dns cloudflare --dry-run
|
|
46
|
+
certmate cert create app.example.com --name internal.example.com --dns cloudflare
|
|
46
47
|
certmate audit verify
|
|
47
48
|
```
|
|
48
49
|
|
|
50
|
+
`--name` names a second certificate for a domain apart from it: the certificate
|
|
51
|
+
is issued for `app.example.com`, and every later command takes
|
|
52
|
+
`internal.example.com`. The server must be on API contract 2.61 or later; an
|
|
53
|
+
older one would ignore the name, so the CLI asks first and refuses, and
|
|
54
|
+
`--dry-run` says whether the server takes it.
|
|
55
|
+
|
|
49
56
|
## Pulling certificates onto a host
|
|
50
57
|
|
|
51
58
|
`cert download` fetches one file at a time, so a target server can pull
|
|
@@ -4,7 +4,7 @@ The [CertMate](https://github.com/fabriziosalmi/certmate) SSL certificate
|
|
|
4
4
|
lifecycle from your terminal — built on `certmate-sdk`.
|
|
5
5
|
|
|
6
6
|
```bash
|
|
7
|
-
pip install certmate-cli
|
|
7
|
+
pip install certmate-cli # or, on macOS/Linux: brew install fabriziosalmi/certmate/certmate-cli
|
|
8
8
|
export CERTMATE_URL=http://localhost:8000
|
|
9
9
|
export CERTMATE_TOKEN=...
|
|
10
10
|
|
|
@@ -13,9 +13,16 @@ certmate cert ls
|
|
|
13
13
|
certmate cert info app.example.com
|
|
14
14
|
certmate cert renew app.example.com --force
|
|
15
15
|
certmate cert create app.example.com --dns cloudflare --dry-run
|
|
16
|
+
certmate cert create app.example.com --name internal.example.com --dns cloudflare
|
|
16
17
|
certmate audit verify
|
|
17
18
|
```
|
|
18
19
|
|
|
20
|
+
`--name` names a second certificate for a domain apart from it: the certificate
|
|
21
|
+
is issued for `app.example.com`, and every later command takes
|
|
22
|
+
`internal.example.com`. The server must be on API contract 2.61 or later; an
|
|
23
|
+
older one would ignore the name, so the CLI asks first and refuses, and
|
|
24
|
+
`--dry-run` says whether the server takes it.
|
|
25
|
+
|
|
19
26
|
## Pulling certificates onto a host
|
|
20
27
|
|
|
21
28
|
`cert download` fetches one file at a time, so a target server can pull
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
"""certmate-cli — the CertMate SSL lifecycle from your terminal (built on certmate-sdk)."""
|
|
2
|
-
__version__ = "0.1.
|
|
2
|
+
__version__ = "0.1.6"
|
|
@@ -21,7 +21,7 @@ import typer
|
|
|
21
21
|
from rich.console import Console
|
|
22
22
|
from rich.table import Table
|
|
23
23
|
|
|
24
|
-
from certmate import Client,
|
|
24
|
+
from certmate import CertMateError, Client, Job, UnsupportedByServer
|
|
25
25
|
|
|
26
26
|
app = typer.Typer(no_args_is_help=True, add_completion=False,
|
|
27
27
|
help="CertMate — the SSL certificate lifecycle from your terminal.")
|
|
@@ -207,6 +207,10 @@ def cert_info(ctx: typer.Context, domain: str):
|
|
|
207
207
|
"""Show a certificate's details."""
|
|
208
208
|
c = _run(lambda: _client(ctx).get_certificate(domain))
|
|
209
209
|
out.print(f"[bold]{c.domain}[/]")
|
|
210
|
+
if c.host and c.host != c.domain:
|
|
211
|
+
# Where it is served, when that is not its name: an IP certificate's
|
|
212
|
+
# address, or the host a certificate named apart from it is for.
|
|
213
|
+
out.print(f" host: {c.host}")
|
|
210
214
|
if c.has_expired() is True:
|
|
211
215
|
remaining = "[red]expired[/]"
|
|
212
216
|
elif c.days_until_expiry is None:
|
|
@@ -233,6 +237,12 @@ def cert_create(
|
|
|
233
237
|
dns: Optional[str] = typer.Option(None, "--dns", help="DNS provider (e.g. cloudflare)."),
|
|
234
238
|
ca: Optional[str] = typer.Option(None, "--ca", help="CA provider (e.g. letsencrypt)."),
|
|
235
239
|
san: Optional[str] = typer.Option(None, "--san", help="Comma-separated SAN domains."),
|
|
240
|
+
# A str with an empty default, not Optional: typer reads the annotation at
|
|
241
|
+
# runtime, and `str | None` is not one on Python 3.9.
|
|
242
|
+
name: str = typer.Option(
|
|
243
|
+
"", "--name",
|
|
244
|
+
help="Name the certificate apart from its domain, for a second certificate for it "
|
|
245
|
+
"(server API contract 2.61 or later)."),
|
|
236
246
|
wait: bool = typer.Option(True, "--wait/--no-wait", help="Wait for issuance to finish."),
|
|
237
247
|
dry_run: bool = typer.Option(False, "--dry-run",
|
|
238
248
|
help="Validate inputs and preflight the DNS provider WITHOUT issuing."),
|
|
@@ -242,6 +252,8 @@ def cert_create(
|
|
|
242
252
|
# bogus "" SAN entry — mirrors cert_reissue.
|
|
243
253
|
sans: List[str] = [s.strip() for s in san.split(",") if s.strip()] if san else []
|
|
244
254
|
client = _client(ctx)
|
|
255
|
+
# What it is issued as: the domain, and the name it is given apart from it.
|
|
256
|
+
issued_as = f"{domain} as {name}" if name else domain
|
|
245
257
|
|
|
246
258
|
if dry_run:
|
|
247
259
|
problems = []
|
|
@@ -250,6 +262,16 @@ def cert_create(
|
|
|
250
262
|
for s in sans:
|
|
251
263
|
if not _DOMAIN_RE.match(s):
|
|
252
264
|
problems.append(f"SAN {s!r} does not look valid")
|
|
265
|
+
if name:
|
|
266
|
+
# The server's rule (#1149): the shape of a DNS name, never a wildcard.
|
|
267
|
+
if not _DOMAIN_RE.match(name) or name.startswith("*."):
|
|
268
|
+
problems.append(f"name {name!r} is not a DNS name")
|
|
269
|
+
try:
|
|
270
|
+
client.require_contract("2.61", "name")
|
|
271
|
+
except UnsupportedByServer as e:
|
|
272
|
+
problems.append(str(e))
|
|
273
|
+
except CertMateError as e:
|
|
274
|
+
_die(str(e))
|
|
253
275
|
if dns:
|
|
254
276
|
res = _run(lambda: client.test_dns_provider(dns))
|
|
255
277
|
ok = bool(res.get("success", res.get("ok", True)))
|
|
@@ -261,23 +283,23 @@ def cert_create(
|
|
|
261
283
|
out.print("[dim]No --dns given; skipping provider preflight.[/]")
|
|
262
284
|
if problems:
|
|
263
285
|
_die("dry-run found issues:\n - " + "\n - ".join(problems))
|
|
264
|
-
out.print(f"[green]dry-run OK[/] — would issue [bold]{
|
|
286
|
+
out.print(f"[green]dry-run OK[/] — would issue [bold]{issued_as}[/]"
|
|
265
287
|
f"{(' (+' + str(len(sans)) + ' SAN)') if sans else ''}"
|
|
266
288
|
f"{(' via ' + dns) if dns else ''}{(' on ' + ca) if ca else ''}. Nothing issued.")
|
|
267
289
|
return
|
|
268
290
|
|
|
269
291
|
def _issue():
|
|
270
|
-
with out.status(f"Issuing [bold]{
|
|
292
|
+
with out.status(f"Issuing [bold]{issued_as}[/] …") as status:
|
|
271
293
|
def _progress(j: Job):
|
|
272
294
|
if j.status:
|
|
273
|
-
status.update(f"Issuing [bold]{
|
|
295
|
+
status.update(f"Issuing [bold]{issued_as}[/] … [{j.status}]")
|
|
274
296
|
return client.create_certificate(
|
|
275
297
|
domain, dns_provider=dns, ca_provider=ca, san_domains=sans or None,
|
|
276
|
-
wait=wait, on_progress=_progress)
|
|
298
|
+
name=name or None, wait=wait, on_progress=_progress)
|
|
277
299
|
|
|
278
300
|
job = _run(_issue)
|
|
279
301
|
if wait:
|
|
280
|
-
out.print(f"[green]issued[/] [bold]{
|
|
302
|
+
out.print(f"[green]issued[/] [bold]{issued_as}[/]"
|
|
281
303
|
f"{(' (' + job.status + ')') if job.status else ''}.")
|
|
282
304
|
else:
|
|
283
305
|
out.print(f"[yellow]accepted[/] — job [bold]{job.job_id}[/] "
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "certmate-cli"
|
|
7
|
-
version = "0.1.
|
|
7
|
+
version = "0.1.6"
|
|
8
8
|
description = "CertMate command-line interface — the SSL certificate lifecycle from your terminal"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.9"
|
|
@@ -33,10 +33,10 @@ classifiers = [
|
|
|
33
33
|
# The SDK floor tracks the CLI release, and now something enforces that:
|
|
34
34
|
# tests/test_version_consistency.py fails if this floor falls behind the SDK
|
|
35
35
|
# in the tree. It had already drifted once, sitting at 0.1.3 while both
|
|
36
|
-
# packages were 0.1.4. 0.1.
|
|
37
|
-
# `cert
|
|
36
|
+
# packages were 0.1.4. 0.1.6 is needed for Client.require_contract(), which
|
|
37
|
+
# `cert create --name --dry-run` calls, and create_certificate(name=...).
|
|
38
38
|
dependencies = [
|
|
39
|
-
"certmate-sdk>=0.1.
|
|
39
|
+
"certmate-sdk>=0.1.6",
|
|
40
40
|
"typer>=0.9",
|
|
41
41
|
"rich>=13",
|
|
42
42
|
]
|