{"id":50936946,"url":"https://github.com/anomixer/pve-cert","last_synced_at":"2026-06-17T10:02:39.887Z","repository":{"id":364989882,"uuid":"1270044831","full_name":"anomixer/pve-cert","owner":"anomixer","description":"Self-signed TLS certificate toolkit for Proxmox VE — no public domain, no internet required","archived":false,"fork":false,"pushed_at":"2026-06-15T11:44:42.000Z","size":30,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-15T12:24:12.492Z","etag":null,"topics":["bash","certificate","homelab","proxmox","proxmox-ve","self-signed","tls","windows"],"latest_commit_sha":null,"homepage":"","language":"Batchfile","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/anomixer.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-06-15T10:30:07.000Z","updated_at":"2026-06-15T11:51:14.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/anomixer/pve-cert","commit_stats":null,"previous_names":["anomixer/pve-cert"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/anomixer/pve-cert","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/anomixer%2Fpve-cert","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/anomixer%2Fpve-cert/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/anomixer%2Fpve-cert/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/anomixer%2Fpve-cert/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/anomixer","download_url":"https://codeload.github.com/anomixer/pve-cert/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/anomixer%2Fpve-cert/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34443239,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-17T02:00:05.408Z","response_time":127,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bash","certificate","homelab","proxmox","proxmox-ve","self-signed","tls","windows"],"created_at":"2026-06-17T10:02:39.231Z","updated_at":"2026-06-17T10:02:39.870Z","avatar_url":"https://github.com/anomixer.png","language":"Batchfile","funding_links":[],"categories":[],"sub_categories":[],"readme":"# pve-cert — Proxmox HTTPS, trusted on every client. One command. No domain. No internet. No annual renewal.\n\n**English** | [繁體中文](README_tw.md)\n\nYour Proxmox browser warning, permanently gone.\n\nOne command on the server, one command on each client —\nyour browser shows a padlock, stays that way for 10 years,\nand works completely offline with no public domain required.\n\n---\n\nEvery fresh Proxmox VE installation greets you with this:\n\n![](pic/1.png)\n![](pic/2.png)\n\n---\n\n## Files\n\n| File | Platform | Purpose |\n|------|----------|---------|\n| `pve-cert.sh` | Proxmox VE (Node/Cluster) | Generate Root CA + node cert, install into PVE |\n| `pve-cert-windows.bat` | Windows Client | Download CA cert, update hosts, import to Windows trust store |\n| `pve-cert-linux.sh` | Linux Client (Ubuntu/Debian) | Download CA cert, update hosts, import to system + browser trust stores |\n| `pve-cert-macos.sh` | macOS Client | Download CA cert, update hosts, import to macOS Keychain |\n\nAfter running `pve-cert.sh` on the PVE server and the appropriate client script on each machine, the warning is gone:\n\n![](pic/s1.png)\n![](pic/s2.png)\n\n---\n\n## Compatibility\n\n**Proxmox VE (Server)**\n- PVE 7.x or later\n\n**Client OS**\n\n| OS | Version |\n|----|---------|\n| Windows | 10, 11 (x86 / ARM) |\n| Linux | Ubuntu 20.04+, Debian 11+ (x86 / ARM) |\n| macOS | 12 Monterey+ (Intel / Apple Silicon) |\n\n**Browser**\n\n| Browser | Notes |\n|---------|-------|\n| Chrome / Chromium | ✅ Auto-imported on Linux; follows system trust on Windows / macOS |\n| Firefox | ✅ Auto-imported on Linux (incl. snap); follows system trust on Windows / macOS |\n| Edge | ✅ Follows Windows / macOS system trust store |\n| Safari | ✅ Follows macOS Keychain |\n\n---\n\n## Why Use This Script?\n\nThere are several ways to handle TLS certificates for a Proxmox VE Web UI. The table below compares the most common approaches.\n\n### Certificate Method Comparison\n\n| | **PVE Default Self-Signed** | **ACME / Let's Encrypt (HTTP-01)** | **Let's Encrypt + Cloudflare (DNS-01)** | **Commercial Wildcard Cert** | **This Script — Self-Signed CA + Client Trust** |\n|---|---|---|---|---|---|\n| **Browser warning** | ❌ Warning by default (can be suppressed by manually importing PVE Root CA on each client, but must be re-imported every year after renewal) | ✅ None | ✅ None | ✅ None | ✅ None (after client setup) |\n| **Public domain required** | ✅ No | ❌ Yes | ❌ Yes | ❌ Yes | ✅ No |\n| **Internet access required** | ✅ No | ❌ Yes (port 80/443) | ❌ Yes (DNS API) | ❌ Yes | ✅ No — fully air-gapped |\n| **Expiry / renewal** | 1 year, auto-renew | 90 days, auto-renew | 90 days, auto-renew | 1–2 years, manual | Configurable (default 10 yr) |\n| **Client re-setup on renewal** | ❌ Yes — re-import on every client | ✅ None | ✅ None | ✅ None | ✅ None — Root CA stays trusted |\n| **Setup complexity** | None (warning persists by default) | Medium | Medium–High | Low–Medium | Low |\n| **Extra services needed** | None | None | Cloudflare account + API token | None | None |\n| **Hostname exposed publicly** | ✅ No | ❌ Yes (CT logs) | ❌ Yes (CT logs) | ❌ Yes | ✅ No |\n| **Works on isolated LAN** | ✅ Yes | ❌ No | ❌ No | ❌ No | ✅ Yes |\n| **Multi-client trust** | Manual per client, repeated each renewal | Automatic | Automatic | Automatic | One-time per client |\n| **Cost** | Free | Free | Free | $100–300/yr | Free |\n| **Access by FQDN** | ✅ Yes (but browser warning still shown unless PVE Root CA is manually imported) | ✅ Yes | ✅ Yes | ✅ Yes | ✅ Yes |\n| **Access by IP** | ✅ Yes (but browser warning still shown) | ❌ No | ❌ No | ❌ No | ✅ Yes (SAN includes IP) |\n| **PVE Web UI cert management** | ✅ Yes (server-side only) | ✅ Yes (server-side only) | ✅ Yes (server-side only) | ❌ Manual | ❌ Manual (this script) |\n\n---\n\n### When to Use Each Method\n\n**PVE Default Self-Signed**\n\nThe out-of-the-box certificate. Fast to get started, but shows a browser warning by default. The warning can be suppressed by manually importing the cert on each client, but requires re-import every year when the cert renews. Acceptable for a quick lab test, not for daily use.\n\n**ACME / Let's Encrypt (HTTP-01)**\n\nBuilt into the Proxmox Web UI under Datacenter → pve → Certificates. Requires a publicly reachable domain and open ports 80/443. Not suitable for internal-only servers behind NAT or on air-gapped networks.\n\n**Let's Encrypt + Cloudflare DNS-01**\n\nValidation happens via DNS API, so port 80/443 does not need to be exposed. Requires a public domain managed through Cloudflare (or another supported DNS provider) and an API token. The hostname appears in public Certificate Transparency logs. Suitable for any environment — homelab or enterprise — that has a public domain managed through a supported DNS provider and is comfortable with hostnames appearing in CT logs.\n\n**Commercial Wildcard Certificate**\n\nCovers all subdomains of a public domain (`*.demo.local` is not valid — a real public TLD is required). High cost and typically manual renewal. Only worth it for production environments with existing public domain infrastructure.\n\n**This Script — Self-Signed CA + Client Trust**\n\n✅ Recommended for any internal infrastructure without a public domain\n\nGenerates a private Root CA and a node certificate with proper SAN entries (DNS + IP), and installs it into PVE. Each client machine then runs the client script once to import the CA and gain trusted access — no public domain, no internet access, no port forwarding, no subscription required.\n\nBest suited for:\n- Any internal infrastructure with no public domain — homelab, SMB, or enterprise private cloud\n- Air-gapped or NAT-only networks\n- Environments where hostnames must not appear in public CT logs\n- Multiple PVE nodes each needing their own trusted cert\n- Users who want a one-command setup on both server and client\n\nThe main trade-off is that every new client machine needs to run the client script once to import the CA. Unlike importing the PVE default cert directly, the Root CA does not change on renewal — so clients never need to re-import.\n\n---\n\n## How It Works\n\n```\n┌─────────────────────────────────────────────────┐\n│  Proxmox VE Server                              │\n│                                                 │\n│  pve-cert.sh                                    │\n│  ├── Auto-detect IP / FQDN                      │\n│  ├── Generate Root CA  (pve-local-ca.crt/.key)  │\n│  ├── Generate node cert signed by Root CA       │\n│  ├── Install to /etc/pve/local/                 │\n│  └── Restart pveproxy / pvedaemon               │\n└────────────────┬────────────────────────────────┘\n                 │  scp  pve-local-ca.crt\n                 ▼\n┌─────────────────────────────────────────────────┐\n│  Client Machine  ← repeat for each client       │\n│                                                 │\n│  pve-cert-windows.bat  /  pve-cert-linux.sh     │\n│  pve-cert-macos.sh                              │\n│  ├── Download CA cert via scp                   │\n│  ├── Auto-detect FQDN via ssh hostname -f       │\n│  ├── Add entry to hosts file                    │\n│  └── Import CA cert to system trust store       │\n└─────────────────────────────────────────────────┘\n                 │\n                 ▼\n     https://pve.demo.local:8006  🔒\n```\n\n---\n\n## Requirements\n\n### Proxmox VE Server\n- Run as `root`\n- `openssl` installed (included in PVE by default)\n\n### Windows Client\n- Run as **Administrator**\n- OpenSSH Client enabled (Windows 10 build 1809+)\n  - Settings → Apps → Optional Features → OpenSSH Client\n- `pve-cert.sh` must have been run on the PVE server first\n\n### Linux Client (Ubuntu / Debian)\n- Run with `sudo`\n- `openssh-client` and `openssl` installed\n  - `sudo apt install openssh-client openssl`\n- `ca-certificates` package installed (provides `update-ca-certificates`)\n  - `sudo apt install ca-certificates`\n- `libnss3-tools` installed (provides `certutil`, for Firefox / Chrome NSS store import)\n  - `sudo apt install libnss3-tools`\n  - The script will install this automatically if missing\n- `pve-cert.sh` must have been run on the PVE server first\n\n### macOS Client\n- Run with `sudo`\n- `ssh`, `scp`, `openssl`, and `security` are all included with macOS — no extra install needed\n- `pve-cert.sh` must have been run on the PVE server first\n\n---\n\n## Download\n\nClone this repository on the PVE server and on each client machine:\n\n**On Proxmox VE (SSH):**\n```bash\ngit clone https://github.com/anomixer/pve-cert.git\ncd pve-cert\n```\n\n**On Windows (Command Prompt or PowerShell):**\n```cmd\ngit clone https://github.com/anomixer/pve-cert.git\ncd pve-cert\n```\n\n**On Linux / macOS (Terminal):**\n```bash\ngit clone https://github.com/anomixer/pve-cert.git\ncd pve-cert\n```\n\n\u003e Or [download the ZIP directly](https://github.com/anomixer/pve-cert/archive/refs/heads/main.zip) and extract it.\n\n---\n\n## Installation\n\n### Step 1 — Run on the Proxmox VE server\n\n```bash\nsudo bash pve-cert.sh\n```\n\nThe script will:\n1. Auto-detect PVE IP and FQDN (`hostname -f`)\n2. Confirm the details with you before proceeding\n3. Generate `Proxmox VE Local Root CA` and a node certificate with SAN entries for both the DNS name and IP address\n4. Back up existing certs to `/etc/pve/local/pveproxy-ssl.pem.bak.\u003ctimestamp\u003e`\n5. Install the new certificate and restart `pveproxy` / `pvedaemon`\n6. Print the full certificate details and all output file locations\n\n**Output files on PVE:**\n\n| File | Description |\n|------|-------------|\n| `/root/pve-local-ca.crt` | Root CA certificate (downloaded by client scripts) |\n| `/root/pve-local-ca.key` | Root CA private key — keep on server, do not share |\n| `/root/pve-node.crt` | Node certificate |\n| `/root/pve-node.key` | Node private key |\n| `/etc/pve/local/pveproxy-ssl.pem` | Active certificate used by PVE Web UI |\n| `/etc/pve/local/pveproxy-ssl.key` | Active private key used by PVE Web UI |\n\n**Multiple PVE servers:** if you have more than one Proxmox VE server, run `pve-cert.sh` on each server individually. Every server generates its own independent Root CA and node certificate.\n\n---\n\n### Step 2 — Run on each client machine\n\nRun the script for your OS:\n\n**Windows** — right-click `pve-cert-windows.bat` → Run as administrator\n```bat\npve-cert-windows.bat\n```\n\n**Linux (Ubuntu / Debian)**\n```bash\nsudo bash pve-cert-linux.sh\n```\n\n**macOS**\n```bash\nsudo bash pve-cert-macos.sh\n```\n\nAll three scripts follow the same steps:\n1. Ask for the PVE IP address and SSH username\n2. Download `pve-local-ca.crt` from PVE via `scp` (prompts for SSH password once)\n3. Auto-detect the PVE FQDN via `ssh hostname -f`\n4. Add an entry to the system hosts file\n5. Import the CA cert into the OS trust store\n6. Optionally open the PVE Web UI in the default browser\n\n**Linux additionally** imports the CA cert into the NSS store used by Chrome, Chromium, and Firefox (including snap-packaged Firefox on Ubuntu 21.10+), so no manual browser steps are needed.\n\n**Multiple client machines:** run the appropriate script independently on every machine that needs access to the PVE Web UI without a certificate warning.\n\n**Multiple PVE nodes:** run the client script once per PVE node on the same machine. The script accumulates site entries without overwriting existing ones — each site is tracked by IP, FQDN, and cert fingerprint.\n\n---\n\n### Step 3 — Open the Web UI\n\nRestart your browser, then navigate to either the FQDN:\n\n```\nhttps://\u003cyour-pve-fqdn\u003e:8006\n```\n\nor directly by IP address:\n\n```\nhttps://\u003cyour-pve-ipaddr\u003e:8006\n```\n\nBoth work without a certificate warning — the certificate SAN includes both the FQDN and the IP address. The browser should show a padlock 🔒. Using the FQDN is recommended for day-to-day use (see [Notes](#notes) for details).\n\n---\n\n## Uninstall\n\n### PVE Server\n\n```bash\nsudo bash pve-cert.sh -u\n```\n\n- Finds the most recent backup and restores it automatically\n- Restarts `pveproxy` / `pvedaemon`\n\n### Client Machine\n\n**Windows** — run as Administrator:\n```bat\npve-cert-windows.bat -u\n```\n\n**Linux:**\n```bash\nsudo bash pve-cert-linux.sh -u\n```\n\n**macOS:**\n```bash\nsudo bash pve-cert-macos.sh -u\n```\n\nAll three present a numbered list of registered sites:\n\n```\n  Registered PVE sites:\n  -----------------------------------\n    [1]  192.168.1.111  \u003c\u003e  pve1.demo.local\n    [2]  192.168.1.112  \u003c\u003e  pve2.demo.local\n    [0]  Remove ALL\n\n  Select [1-2, 0=all]:\n```\n\nFor each selected site, the script will:\n- Remove the hosts entry\n- Remove the CA cert from the OS trust store (matched by fingerprint)\n- Remove the CA cert from browser NSS stores (Linux: Chrome/Chromium and Firefox including snap)\n- Delete the local cert file from the data directory\n\n---\n\n## Notes\n\n### Client Script Comparison\n\n| | **Windows** | **Linux** | **macOS** |\n|---|---|---|---|\n| **Script** | `pve-cert-windows.bat` | `pve-cert-linux.sh` | `pve-cert-macos.sh` |\n| **Run as** | Administrator | `sudo` | `sudo` |\n| **Hosts file** | `C:\\Windows\\System32\\drivers\\etc\\hosts` | `/etc/hosts` | `/etc/hosts` |\n| **Trust store** | Windows Root CA store (`certutil`) | System CA bundle (`update-ca-certificates` / `update-ca-trust`) + NSS store (Chrome/Firefox) | macOS Keychain (`security`) |\n| **Cert fingerprint** | SHA-1 thumbprint (PowerShell) | SHA-256 (openssl) | SHA-1 (openssl + Keychain) |\n| **Data directory** | `%ProgramData%\\pve-cert\\` | `~/.local/share/pve-cert/` | `~/Library/Application Support/pve-cert/` |\n| **Open browser** | `start` | `xdg-open` | `open` |\n| **Extra dependencies** | OpenSSH Client (built-in Win10+) | `openssh-client`, `openssl`, `ca-certificates`, `libnss3-tools` (auto-installed if missing) | None (all built-in) |\n\n---\n\n### Why FQDN instead of IP address?\n\nBrowsers validate TLS certificates using the **Subject Alternative Name (SAN)** field, not just the Common Name (CN). A SAN entry can be either a DNS name or an IP address, but the two are treated as completely separate identifiers.\n\nThis script includes **both** in the certificate:\n\n```\nSAN: DNS:pve92.demo.local\n     IP:192.168.21.92\n```\n\nSo `https://192.168.21.92:8006` **will** work without a warning. However, using the FQDN is strongly recommended because:\n\n- **IP addresses change.** If the PVE server is reassigned to a new IP, the cert SAN no longer matches and the warning returns. The FQDN stays stable as long as the `hosts` entry or DNS record is kept up to date.\n- **Browser behaviour.** Some browsers (notably older Chrome/Edge builds) do not honour IP SANs in private CA certificates and will show a warning regardless.\n- **Consistency.** Using the FQDN makes bookmarks, API calls, and scripts portable across environments without hardcoding IP addresses.\n\nThe `hosts` entry written by the client script maps the FQDN to the current IP, so the browser resolves correctly even without a local DNS server.\n\n---\n\n- **Certificate CN**: `Proxmox VE Local Root CA (\u003chostname\u003e)`\n\n- All persistent data is stored in a platform-specific directory:\n  - **Windows:** `%ProgramData%\\pve-cert\\`\n  - **Linux:** `~/.local/share/pve-cert/`\n  - **macOS:** `~/Library/Application Support/pve-cert/`\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fanomixer%2Fpve-cert","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fanomixer%2Fpve-cert","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fanomixer%2Fpve-cert/lists"}