You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Findings: import_local_package_mtls_bundle() unconditionally calls store_pki_bundle() every time it finds certs in a package-managed directory, with no comparison against existing user-config certs. The OIDC code path returns early before reaching the mTLS branch, acting as an accidental guard.
Remaining reason for filing: No guard exists in import_local_package_mtls_bundle to skip the import when a valid bundle already exists.
Description
Actual behavior: Running openshell gateway add --local without --oidc-issuer atomically replaces the entire ~/.config/openshell/<gateway>/mtls/ directory, regardless of whether the existing certificates are valid or identical to the source.
import_local_package_mtls_bundle() (crates/openshell-cli/src/commands/gateway.rs:634) searches package-managed TLS directories and calls store_pki_bundle() (crates/openshell-bootstrap/src/mtls.rs:15) without checking whether a valid bundle already exists in the user config directory. store_pki_bundle() writes to a temp directory, backs up the existing mtls/ directory, renames the temp into place, then deletes the backup — an unconditional atomic replace.
When --oidc-issuer IS specified, the OIDC branch returns early (gateway.rs:984) before reaching the mTLS import code, so certificates are never touched. The OIDC flag acts as an accidental guard against the overwrite.
Additionally, the gateway server's certgen subcommand (crates/openshell-server/src/certgen.rs:515) also calls store_pki_bundle("openshell", &bundle) unconditionally in local mode, so server restarts that regenerate PKI will also overwrite the CLI's client certs.
Expected behavior: If a valid mTLS bundle already exists in the user config directory and matches the package-managed source (or is otherwise still valid), gateway add --local should skip the import rather than silently replacing it.
Reproduction Steps
Manually place valid mTLS certificates (ca.crt, tls.crt, tls.key) into ~/.config/openshell/gateways/<gateway>/mtls/
Run openshell gateway add https://localhost:8443 --local — the existing certificates are silently replaced with certs imported from the package-managed TLS directory
Environment
OS: macOS (Darwin 25.5.0), also affects Linux
OpenShell: v0.0.91
Latest release checked: yes — no related fixes found
Add a comparison in import_local_package_mtls_bundle() that reads the existing user-config bundle (if present), compares it byte-for-byte (or by cert fingerprint) against the package-managed source, and skips the store_pki_bundle() call when they match. This keeps store_pki_bundle itself unconditional — it is a low-level storage primitive — and puts the "should we reimport?" decision where the context exists.
Agent Diagnostic
create-github-issuemain)git log --grepforstore_pki_bundle,import_local_package_mtls_bundle,mtls.*overwritefound no relevant patchesstore_pki_bundleoverwrite. This issue covers a different trigger: repeated--localinvocations without--oidc-issuer, where no system gateway is involved.import_local_package_mtls_bundle()unconditionally callsstore_pki_bundle()every time it finds certs in a package-managed directory, with no comparison against existing user-config certs. The OIDC code path returns early before reaching the mTLS branch, acting as an accidental guard.import_local_package_mtls_bundleto skip the import when a valid bundle already exists.Description
Actual behavior: Running
openshell gateway add --localwithout--oidc-issueratomically replaces the entire~/.config/openshell/<gateway>/mtls/directory, regardless of whether the existing certificates are valid or identical to the source.import_local_package_mtls_bundle()(crates/openshell-cli/src/commands/gateway.rs:634) searches package-managed TLS directories and callsstore_pki_bundle()(crates/openshell-bootstrap/src/mtls.rs:15) without checking whether a valid bundle already exists in the user config directory.store_pki_bundle()writes to a temp directory, backs up the existingmtls/directory, renames the temp into place, then deletes the backup — an unconditional atomic replace.When
--oidc-issuerIS specified, the OIDC branch returns early (gateway.rs:984) before reaching the mTLS import code, so certificates are never touched. The OIDC flag acts as an accidental guard against the overwrite.Additionally, the gateway server's
certgensubcommand (crates/openshell-server/src/certgen.rs:515) also callsstore_pki_bundle("openshell", &bundle)unconditionally in local mode, so server restarts that regenerate PKI will also overwrite the CLI's client certs.Expected behavior: If a valid mTLS bundle already exists in the user config directory and matches the package-managed source (or is otherwise still valid),
gateway add --localshould skip the import rather than silently replacing it.Reproduction Steps
ca.crt,tls.crt,tls.key) into~/.config/openshell/gateways/<gateway>/mtls/openshell gateway add https://localhost:8443 --local— the existing certificates are silently replaced with certs imported from the package-managed TLS directoryEnvironment
import_local_package_mtls_bundlepathRelated
store_pki_bundleoverwrite. The fix proposed there (CLI-layer guard for system gateways) would not address this issue, since this path does not involve a system gateway.Relevant Code Paths
crates/openshell-cli/src/commands/gateway.rs:634—import_local_package_mtls_bundle()crates/openshell-cli/src/commands/gateway.rs:987-993— mTLS branch calling the importcrates/openshell-bootstrap/src/mtls.rs:15—store_pki_bundle()atomic replacecrates/openshell-server/src/certgen.rs:515— server-sidestore_pki_bundle()call (also overwrites unconditionally)Proposed Fix
Add a comparison in
import_local_package_mtls_bundle()that reads the existing user-config bundle (if present), compares it byte-for-byte (or by cert fingerprint) against the package-managed source, and skips thestore_pki_bundle()call when they match. This keepsstore_pki_bundleitself unconditional — it is a low-level storage primitive — and puts the "should we reimport?" decision where the context exists.