fix(vpn): purging a host no longer leaves a phantom access behind
The FK only nulled host_id, so the adopted peer stayed enabled-but-absent: shown as "wird angewendet" forever, and re-enabling it would have put the deleted host's key and address back on the hub. PurgeHost now tombstones that row, which hands it to the same revocation machinery as everything else. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>feat/portal-design
parent
c54b718566
commit
d1302e17bc
|
|
@ -3,6 +3,7 @@
|
||||||
namespace App\Provisioning\Jobs;
|
namespace App\Provisioning\Jobs;
|
||||||
|
|
||||||
use App\Models\Host;
|
use App\Models\Host;
|
||||||
|
use App\Models\VpnPeer;
|
||||||
use Illuminate\Bus\Queueable;
|
use Illuminate\Bus\Queueable;
|
||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
use Illuminate\Foundation\Bus\Dispatchable;
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
|
|
@ -44,6 +45,14 @@ class PurgeHost implements ShouldQueue
|
||||||
}
|
}
|
||||||
|
|
||||||
if (filled($host->wg_pubkey)) {
|
if (filled($host->wg_pubkey)) {
|
||||||
|
// Tombstone the console's view of this peer as well. The FK only
|
||||||
|
// nulls host_id, which would leave an enabled-but-absent row behind:
|
||||||
|
// shown forever as "wird angewendet", and re-enabling it would put a
|
||||||
|
// deleted host's key back on the hub. Soft-deleting hands it to the
|
||||||
|
// same machinery every other revocation uses — the removal below
|
||||||
|
// clears the tombstone, and a sync retries if that removal is lost.
|
||||||
|
VpnPeer::query()->where('public_key', $host->wg_pubkey)->delete();
|
||||||
|
|
||||||
RemoveWireguardPeer::dispatch($host->wg_pubkey);
|
RemoveWireguardPeer::dispatch($host->wg_pubkey);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -430,3 +430,24 @@ it('does not rename an access an operator named themselves', function () {
|
||||||
|
|
||||||
expect($peer->fresh()->name)->toBe('Notebook Boban');
|
expect($peer->fresh()->name)->toBe('Notebook Boban');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('does not leave a phantom access behind when a host is purged', function () {
|
||||||
|
$hub = vpnHub();
|
||||||
|
$key = Keypair::generate()->publicKey;
|
||||||
|
$host = Host::factory()->active()->create(['name' => 'pve-fsn1', 'wg_pubkey' => $key]);
|
||||||
|
$hub->addPeer($key, '10.66.0.7');
|
||||||
|
|
||||||
|
(new SyncVpnPeers)->handle($hub); // console adopts the host peer
|
||||||
|
expect(VpnPeer::query()->count())->toBe(1);
|
||||||
|
|
||||||
|
(new App\Provisioning\Jobs\PurgeHost($host->uuid))->handle();
|
||||||
|
|
||||||
|
// No enabled-but-absent leftover the operator could toggle back on.
|
||||||
|
expect(VpnPeer::query()->count())->toBe(0)
|
||||||
|
->and(VpnPeer::onlyTrashed()->count())->toBe(1);
|
||||||
|
|
||||||
|
// The queued removal clears both the hub peer and the tombstone.
|
||||||
|
(new ApplyVpnPeer($key, null, false))->handle($hub);
|
||||||
|
expect($hub->peerIps())->toBe([])
|
||||||
|
->and(VpnPeer::withTrashed()->count())->toBe(0);
|
||||||
|
});
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue