certmate-cli 0.1.0__tar.gz → 0.1.2__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.0 → certmate_cli-0.1.2}/PKG-INFO +4 -2
- {certmate_cli-0.1.0 → certmate_cli-0.1.2}/README.md +2 -0
- {certmate_cli-0.1.0 → certmate_cli-0.1.2}/certmate_cli/__init__.py +1 -1
- {certmate_cli-0.1.0 → certmate_cli-0.1.2}/certmate_cli/main.py +55 -11
- {certmate_cli-0.1.0 → certmate_cli-0.1.2}/pyproject.toml +4 -2
- {certmate_cli-0.1.0 → certmate_cli-0.1.2}/.gitignore +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: certmate-cli
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.2
|
|
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.2
|
|
27
27
|
Requires-Dist: rich>=13
|
|
28
28
|
Requires-Dist: typer>=0.9
|
|
29
29
|
Description-Content-Type: text/markdown
|
|
@@ -47,3 +47,5 @@ certmate audit verify
|
|
|
47
47
|
```
|
|
48
48
|
|
|
49
49
|
Connection comes from `--url`/`--token` or `CERTMATE_URL`/`CERTMATE_TOKEN`.
|
|
50
|
+
Prefer the `CERTMATE_TOKEN` environment variable over `--token`: command-line
|
|
51
|
+
arguments are visible to other local processes (`ps`) and shell history.
|
|
@@ -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.2"
|
|
@@ -10,6 +10,7 @@ Connection comes from --url/--token or CERTMATE_URL/CERTMATE_TOKEN.
|
|
|
10
10
|
from __future__ import annotations
|
|
11
11
|
|
|
12
12
|
import re
|
|
13
|
+
import sys
|
|
13
14
|
from typing import List, Optional
|
|
14
15
|
|
|
15
16
|
import typer
|
|
@@ -41,14 +42,38 @@ _DOMAIN_RE = re.compile(
|
|
|
41
42
|
r"^(\*\.)?([a-zA-Z0-9_](-*[a-zA-Z0-9_])*\.)+[a-zA-Z]{2,}$")
|
|
42
43
|
|
|
43
44
|
|
|
45
|
+
def _token_on_argv() -> bool:
|
|
46
|
+
"""True when the token was passed as a command-line flag (as opposed to
|
|
47
|
+
the CERTMATE_TOKEN environment variable). argv is what leaks to `ps`
|
|
48
|
+
output and shell history, so it is exactly the thing to check."""
|
|
49
|
+
return any(a == "--token" or a.startswith("--token=") for a in sys.argv)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def _stderr_isatty() -> bool:
|
|
53
|
+
# Indirection so tests can stub interactivity; CliRunner's captured
|
|
54
|
+
# stderr never reports a TTY.
|
|
55
|
+
try:
|
|
56
|
+
return sys.stderr.isatty()
|
|
57
|
+
except Exception:
|
|
58
|
+
return False
|
|
59
|
+
|
|
60
|
+
|
|
44
61
|
@app.callback()
|
|
45
62
|
def _main(
|
|
46
63
|
ctx: typer.Context,
|
|
47
64
|
url: Optional[str] = typer.Option(None, "--url", envvar="CERTMATE_URL",
|
|
48
65
|
help="CertMate base URL (default http://localhost:8000)."),
|
|
49
66
|
token: Optional[str] = typer.Option(None, "--token", envvar="CERTMATE_TOKEN",
|
|
50
|
-
help="API bearer token."
|
|
67
|
+
help="API bearer token. Prefer the CERTMATE_TOKEN "
|
|
68
|
+
"environment variable: --token is visible to other "
|
|
69
|
+
"local processes (ps) and shell history."),
|
|
51
70
|
):
|
|
71
|
+
# Kept for compatibility, but discourage --token interactively: argv is
|
|
72
|
+
# world-readable via ps and lands in shell history. Warn only on a TTY so
|
|
73
|
+
# scripts and pipelines stay quiet.
|
|
74
|
+
if token and _token_on_argv() and _stderr_isatty():
|
|
75
|
+
err.print("[yellow]warning[/]: --token is visible in ps output and shell history; "
|
|
76
|
+
"prefer the CERTMATE_TOKEN environment variable.")
|
|
52
77
|
ctx.obj = {"url": url, "token": token}
|
|
53
78
|
|
|
54
79
|
|
|
@@ -156,7 +181,9 @@ def cert_create(
|
|
|
156
181
|
help="Validate inputs and preflight the DNS provider WITHOUT issuing."),
|
|
157
182
|
):
|
|
158
183
|
"""Issue a certificate (async; waits for completion by default)."""
|
|
159
|
-
|
|
184
|
+
# Drop empties so a trailing comma ("a.com,b.com,") never produces a
|
|
185
|
+
# bogus "" SAN entry — mirrors cert_reissue.
|
|
186
|
+
sans: List[str] = [s.strip() for s in san.split(",") if s.strip()] if san else []
|
|
160
187
|
client = _client(ctx)
|
|
161
188
|
|
|
162
189
|
if dry_run:
|
|
@@ -213,10 +240,18 @@ def cert_renew(ctx: typer.Context, domain: str,
|
|
|
213
240
|
force: bool = typer.Option(False, "--force", help="Force renewal even if not due.")):
|
|
214
241
|
"""Renew a certificate."""
|
|
215
242
|
res = _run(lambda: _client(ctx).renew_certificate(domain, force=force))
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
243
|
+
renewed = res.get("renewed")
|
|
244
|
+
# Only servers v2.21.1+ report the outcome (`renewed: true/false`). Green
|
|
245
|
+
# requires an explicit true — an absent key means the server did not say,
|
|
246
|
+
# and claiming success would be a lie.
|
|
247
|
+
if renewed is True:
|
|
219
248
|
out.print(f"[green]renewed[/] {domain}.")
|
|
249
|
+
elif renewed is False:
|
|
250
|
+
msg = res.get("message") or f"{domain} was not yet due for renewal."
|
|
251
|
+
out.print(f"[yellow]not due[/] — {msg}")
|
|
252
|
+
else:
|
|
253
|
+
out.print(f"renew requested for [bold]{domain}[/] — server did not report "
|
|
254
|
+
"the outcome (server v2.21.1+ reports it).")
|
|
220
255
|
|
|
221
256
|
|
|
222
257
|
@cert_app.command("rm")
|
|
@@ -280,13 +315,22 @@ def audit_verify(ctx: typer.Context):
|
|
|
280
315
|
ok = bool(res.get("ok"))
|
|
281
316
|
reason = res.get("reason") or ""
|
|
282
317
|
cp = res.get("checkpoint_verified")
|
|
283
|
-
#
|
|
284
|
-
#
|
|
285
|
-
|
|
286
|
-
|
|
318
|
+
# Benign ONLY when the server says state='absent' (200: fresh instance,
|
|
319
|
+
# nothing audited yet). The reason wording is NOT a signal: a chain file
|
|
320
|
+
# DELETED after signed checkpoints attested it existed comes back as a 409
|
|
321
|
+
# with the very same "chain file does not exist" text — that is tampering
|
|
322
|
+
# and must exit non-zero, like every other not-ok result.
|
|
323
|
+
if not ok and res.get("state") == "absent":
|
|
324
|
+
out.print(f"audit chain: [dim]none yet[/] — {reason or 'nothing audited yet'}")
|
|
287
325
|
return
|
|
288
|
-
|
|
289
|
-
|
|
326
|
+
detail = (
|
|
327
|
+
f" — {reason}"
|
|
328
|
+
if reason and not (ok and reason.lower() == "intact")
|
|
329
|
+
else ""
|
|
330
|
+
)
|
|
331
|
+
out.print(
|
|
332
|
+
f"audit chain: {'[green]intact[/]' if ok else '[red]BROKEN[/]'}{detail}"
|
|
333
|
+
)
|
|
290
334
|
if cp is not None:
|
|
291
335
|
out.print(f" signed checkpoint: {'[green]verified[/]' if cp else '[dim]not cross-checked[/]'}"
|
|
292
336
|
f"{(' @ seq ' + str(res.get('checkpoint_seq'))) if res.get('checkpoint_seq') is not None else ''}")
|
|
@@ -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.2"
|
|
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"
|
|
@@ -30,8 +30,10 @@ classifiers = [
|
|
|
30
30
|
]
|
|
31
31
|
# Built ON the SDK (never the reverse). typer for the command surface, rich
|
|
32
32
|
# for tables/spinners. Still light — no server deps.
|
|
33
|
+
# The SDK floor tracks the CLI release: 0.1.2 needs TransportError and the
|
|
34
|
+
# renew/audit semantics introduced in certmate-sdk 0.1.2.
|
|
33
35
|
dependencies = [
|
|
34
|
-
"certmate-sdk>=0.1.
|
|
36
|
+
"certmate-sdk>=0.1.2",
|
|
35
37
|
"typer>=0.9",
|
|
36
38
|
"rich>=13",
|
|
37
39
|
]
|
|
File without changes
|