bindcar/
cli.rs

1// Copyright (c) 2025 Erick Bourgeois, firestoned
2// SPDX-License-Identifier: MIT
3
4//! CLI argument parsing for the bindcar binary.
5//!
6//! bindcar supports two operating modes selected via subcommand:
7//!
8//! - **`run`** (default) — sidecar mode: runs alongside a local BIND9 instance inside a
9//!   Kubernetes pod, communicating over shared volumes and local RNDC.
10//!
11//! - **`drone`** — standalone mode: runs as an independent process on a bare-metal or VM
12//!   host, managing a remote BIND9 instance. Authentication is performed via the
13//!   Kubernetes TokenReview API using explicit credentials (`KUBE_API_SERVER`,
14//!   `KUBE_TOKEN_PATH`, `KUBE_CA_CERT_PATH`).
15//!
16//! When no subcommand is given, `run` is the default, preserving backwards compatibility
17//! for existing deployments and process supervisors that call `bindcar` directly.
18
19use clap::{Parser, Subcommand};
20
21/// bindcar — HTTP REST API for managing BIND9 zones via RNDC
22#[derive(Parser, Debug)]
23#[command(version, about, long_about = None)]
24pub struct Cli {
25    /// Enable debug-level logging (overrides RUST_LOG)
26    #[arg(long, short = 'd', global = true)]
27    pub debug: bool,
28
29    /// Acknowledge and allow starting with weak/disabled authentication on a
30    /// non-loopback interface. Without this (or a loopback bind / real auth),
31    /// bindcar refuses to start to avoid silently exposing an unauthenticated API.
32    #[arg(long, global = true)]
33    pub i_know_this_is_insecure: bool,
34
35    /// PEM certificate chain (leaf first) for the HTTPS listener.
36    ///
37    /// Must be given together with `--tls-key`. When neither is set bindcar
38    /// serves plaintext HTTP, preserving existing behaviour.
39    #[arg(long, env = "BIND_TLS_CERT", global = true)]
40    pub tls_cert: Option<String>,
41
42    /// PEM private key for the HTTPS listener (PKCS#8, PKCS#1 or SEC1).
43    ///
44    /// Must be given together with `--tls-cert`.
45    #[arg(long, env = "BIND_TLS_KEY", global = true)]
46    pub tls_key: Option<String>,
47
48    /// How often to re-check the TLS certificate files for a renewal, in seconds.
49    ///
50    /// Defaults to 60. Set to `0` to disable reloading entirely, restoring the
51    /// startup-only behaviour of earlier releases. A renewal that fails to load
52    /// never interrupts service: the previous certificate keeps serving and the
53    /// next poll retries.
54    #[arg(long, env = "BIND_TLS_RELOAD_INTERVAL", global = true)]
55    pub tls_reload_interval: Option<u64>,
56
57    /// PEM CA bundle used to verify client certificates.
58    ///
59    /// Setting this turns on mutual TLS: a client presenting no certificate, or
60    /// one not chaining to this bundle, is rejected at the TLS handshake. In the
61    /// bindy topology both ends have stable in-cluster identities, so mTLS is a
62    /// better fit than server-only TLS and keeps the bearer token off the wire.
63    #[arg(long, env = "BIND_TLS_CLIENT_CA", global = true)]
64    pub tls_client_ca: Option<String>,
65
66    #[command(subcommand)]
67    pub command: Option<Commands>,
68}
69
70/// bindcar operating modes
71#[derive(Subcommand, Debug, PartialEq, Clone)]
72pub enum Commands {
73    /// Run as a Kubernetes sidecar alongside a local BIND9 instance (default)
74    Run,
75    /// Run standalone, managing a remote BIND9 instance from outside the cluster
76    Drone,
77}
78
79impl Cli {
80    /// Return the resolved command, defaulting to [`Commands::Run`] when no subcommand
81    /// was given. This preserves backwards compatibility: `bindcar` with no args
82    /// behaves identically to `bindcar run`.
83    pub fn resolved_command(&self) -> &Commands {
84        self.command.as_ref().unwrap_or(&Commands::Run)
85    }
86}