generate-client bundles one client's zip with a prefix glob, so a client whose name is a prefix of another client's name packages that other client's private keys.
_bundle_client_zip (scripts/generate-client.py) globs {client_name}-*. Client names allow - (validate_client_name permits [a-zA-Z0-9_-]+), so hyperi matches every file belonging to hyperi-laptop.
Proved it:
hyperi.zip would contain: ['hyperi-laptop-udp-split.ovpn', 'hyperi-laptop-wg-split.conf', 'hyperi-udp-split.ovpn', 'hyperi-wg-split.conf']
The .ovpn files carry the embedded client key and the tls-crypt-v2 key. The .conf files carry the WireGuard private key. So whoever gets hyperi.zip gets hyperi-laptop's full credentials, and the docstring on that function says the per-client bundling exists precisely so "one client's private keys are never packaged alongside another's".
It only runs one way -- the SHORTER name swallows the longer one, never the reverse.
Same glob class bites revocation. revoke_client globs {client_name}-*.ovpn, so revoking hyperi deletes hyperi-laptop's .ovpn files out of the output dir without revoking that client's certificate. Less serious (the cert stays valid, the files regenerate) but it is the same mistake. The WireGuard removal glob {client_name}-wg*.conf is safe, because the literal -wg anchors it.
Nothing on devex hits this today -- the client names there are hyperi and devex-hyperi-io, and hyperi-* does not match devex-hyperi-io-*. It is latent, not live. But "do not name a client a prefix of another client" is not a rule anyone knows about, and the failure is silent.
The fix is to match on the actual filename boundary rather than a bare prefix, on both globs.
Done when a client named foo and a client named foo-bar can coexist, and foo.zip contains only foo's files.
generate-clientbundles one client's zip with a prefix glob, so a client whose name is a prefix of another client's name packages that other client's private keys._bundle_client_zip(scripts/generate-client.py) globs{client_name}-*. Client names allow-(validate_client_namepermits[a-zA-Z0-9_-]+), sohyperimatches every file belonging tohyperi-laptop.Proved it:
The
.ovpnfiles carry the embedded client key and the tls-crypt-v2 key. The.conffiles carry the WireGuard private key. So whoever getshyperi.zipgetshyperi-laptop's full credentials, and the docstring on that function says the per-client bundling exists precisely so "one client's private keys are never packaged alongside another's".It only runs one way -- the SHORTER name swallows the longer one, never the reverse.
Same glob class bites revocation.
revoke_clientglobs{client_name}-*.ovpn, so revokinghyperideleteshyperi-laptop's.ovpnfiles out of the output dir without revoking that client's certificate. Less serious (the cert stays valid, the files regenerate) but it is the same mistake. The WireGuard removal glob{client_name}-wg*.confis safe, because the literal-wganchors it.Nothing on devex hits this today -- the client names there are
hyperianddevex-hyperi-io, andhyperi-*does not matchdevex-hyperi-io-*. It is latent, not live. But "do not name a client a prefix of another client" is not a rule anyone knows about, and the failure is silent.The fix is to match on the actual filename boundary rather than a bare prefix, on both globs.
Done when a client named
fooand a client namedfoo-barcan coexist, andfoo.zipcontains onlyfoo's files.