Skip to content

Self note #2 #2

Description

@Partakithware

⚠️ The Blind Spot: The SSL Routes

Applied rigorous validation to the DNS, DHCP, and Nginx routes, but the SSL routes missed the security audit.

Activity

  1. Partakithware commented on Apr 19, 2026

    @Partakithware
    OwnerAuthor

    Version Alpha C3

    Here's a precise account of the three SSL fixes and why each one was dangerous:

    SSL route fixes
    api_mkcert_cert — two injection surfaces, both eliminated
    What was wrong:
    The domain from the request body was interpolated bare into the mkcert shell command:
    python# BEFORE — shell injection via raw_domain:
    CAROOT='{CA_DIR}' mkcert ... "{raw_domain}" "*.{tld}" "{tld}"
    And then a second injection through the nginx sed -i command:
    pythonsed -i '/server_name/a \ ...{CERT_DIR}/{file_name}.pem;...' "$NGINX_CONF"
    A domain value of "; rm -rf /; # would have achieved immediate root command execution.
    What changed:

    raw_domain is now validated — it must be a valid domain name, with an optional leading *. wildcard prefix checked separately.
    tld from config is also validated, since config can theoretically be written with a bad value.
    Every variable that enters the shell command is wrapped with shlex.quote(): sq_raw, sq_wildcard, sq_tld, sq_file_name, sq_caroot, sq_cert_dir.
    The entire nginx SSL injection block was removed from the shell script and replaced with a Python function _nginx_ssl_inject() that reads, modifies with re.sub(), and writes the nginx config file — no shell involved at all.

    api_certbot_cert — three unvalidated variables into certbot and a heredoc
    What was wrong:
    email, domain, and server_ip were all dropped into the certbot shell script with zero validation:
    python# BEFORE:
    --email {email}
    -d "{domain}"

    and server_ip in a heredoc cat >> block

    What changed:

    email is validated with a strict RFC-style regex before use.
    domain is validated with validate_domain_name() (wildcard prefix handled the same way as mkcert).
    server_ip is validated with validate_ipv4().
    email and domain are wrapped in shlex.quote() → sq_email and sq_domain before interpolation into the script.
    The rfc2136.ini config file is now written with b64w() (base64 tunnel, which avoids heredoc injection entirely) for the static fields, with only the shell-generated $KEY_SECRET appended via a safe printf '...' "$KEY_SECRET" line that doesn't touch any user input.

    api_delete_cert — domain from URL path into rm -f and sed -i
    What was wrong:
    The domain URL path parameter was used directly in shell commands:
    python# BEFORE:
    rm -f "{cert_path}" "{key_path}" # cert_path built from domain
    sed -i '/listen 443 ssl;/d' "$NGINX_CONF" # NGINX_CONF built from domain
    A crafted URL like /api/ssl/certs/x%22%3B+rm+-rf+/ would have run arbitrary shell commands as root.
    What changed:

    domain is validated with validate_domain_name() (same wildcard-prefix logic) before anything else.
    Certificate files are deleted with Path.unlink() — no shell process at all.
    The nginx SSL cleanup is done by reading the file into Python, filtering lines with re.match(), and writing it back — replacing both sed -i '/listen 443 ssl;/d' and sed -i '/ssl_/d' with a single clean list comprehension.
    nginx -t and systemctl reload nginx are called via subprocess list form — no interpolation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions