feat: EasyRSA subprocess wrappers (revoke, build, gen-crl, copy-crl)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Vlad Doloman
2026-06-23 23:50:59 +03:00
parent 941b3d3f5b
commit c93a7b0c00
2 changed files with 125 additions and 0 deletions

View File

@@ -149,6 +149,64 @@ def generate_password() -> str:
return "".join(pw)
# ============================================================
# === EASYRSA ===
# ============================================================
class EasyRSAError(Exception):
pass
def _run_easyrsa(cmd: list[str], cwd: str) -> None:
result = subprocess.run(cmd, cwd=cwd, capture_output=True, text=True)
if result.returncode != 0:
# Find the first non-flag argument after the binary (the actual easyrsa verb)
verb = next((c for c in cmd[1:] if not c.startswith("-")), "unknown")
raise EasyRSAError(
f"{verb} failed (exit {result.returncode}): {result.stderr.strip()}"
)
def _base_cmd(easyrsa_dir: str, pki_dir: str, ca_passphrase: str) -> list[str]:
cmd = [f"{easyrsa_dir}/easyrsa", "--batch", f"--pki={pki_dir}"]
if ca_passphrase:
cmd.append(f"--passin=pass:{ca_passphrase}")
return cmd
def revoke_issued(
easyrsa_dir: str, pki_dir: str, cn: str, ca_passphrase: str
) -> None:
_run_easyrsa(
_base_cmd(easyrsa_dir, pki_dir, ca_passphrase) + ["revoke-issued", cn],
cwd=easyrsa_dir,
)
def build_client_full(
easyrsa_dir: str, pki_dir: str, cn: str,
key_passphrase: str, ca_passphrase: str,
) -> None:
_run_easyrsa(
_base_cmd(easyrsa_dir, pki_dir, ca_passphrase)
+ [f"--passout=pass:{key_passphrase}", "build-client-full", cn],
cwd=easyrsa_dir,
)
def gen_crl(easyrsa_dir: str, pki_dir: str, ca_passphrase: str) -> None:
_run_easyrsa(
_base_cmd(easyrsa_dir, pki_dir, ca_passphrase) + ["gen-crl"],
cwd=easyrsa_dir,
)
def copy_crl(pki_dir: str, dest_path: str) -> None:
shutil.copy2(f"{pki_dir}/crl.pem", dest_path)
os.chmod(dest_path, 0o644)
# (remaining sections added in later tasks)
# ============================================================