Phase 1 project revival + PR fixes (#207, #209, #213)

Project infrastructure:
- Replace MIT license with AGPL-3.0 (KSA Technologies, LLC)
- Full README rewrite with badges, compatibility table, token auth docs
- Add CHANGELOG, CONTRIBUTING, DONORS, SECURITY docs
- Add GitHub issue/PR templates, update FUNDING.yml and stale.yml
- Replace external packer repo dispatch with self-contained CI (build.yml)
- Add packaging/DEBIAN/ with postinst/postrm/triggers (no git clone at install)
- Add .perlcriticrc for static analysis
- Add .claude/cos/ ADRs, plans, and runbooks

Bug fixes from community PRs:
- Fix bearer token check in freenas_api_connect: defined() && value instead of
  defined() alone, so truenas_token_auth=0 no longer activates Bearer Token auth (#207)
- Fix LUN 0 falsy bug in ZFSPlugin patch: !$guid -> !defined $guid in both
  zfs_get_lun_number and zfs_get_wwid_number, fixing VMs on LUN 0 for PVE 9 (#209)
- Fix syslog typo "wtih" -> "with" in run_list_extent (#213)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Kevin Adams 2026-05-15 14:22:04 -04:00
parent ebc461a519
commit 636cd06ff6
31 changed files with 2450 additions and 226 deletions

View File

@ -0,0 +1,54 @@
# ADR-001: Consolidate Build Pipeline into Main Repo
**Date**: 2026-05-15
**Status**: Proposed
**Deciders**: Kevin Adams
## Context
The build pipeline is split across two GitHub repos:
- `freenas-proxmox` — source code + patches
- `freenas-proxmox-packer` — DEBIAN packaging structure + CI that actually builds the `.deb`
Communication is via `repository_dispatch` which requires a stored `ACCESS_TOKEN` secret. This creates maintenance overhead (two repos to keep in sync, two sets of CI secrets, cross-repo dependencies) and confusion about where things live.
Additionally, the current `postinst` script **git-clones this repo at install time**, which requires internet access on the Proxmox node and is fragile.
## Decision
Move all packaging (DEBIAN structure, CI workflow) into the main `freenas-proxmox` repo. The `.deb` package will embed all required files during the build step, not at install time.
## Consequences
**Positive**:
- Single repo to maintain
- No cross-repo dispatch tokens needed
- Package installs offline (no git at install time)
- Simpler CI secrets (only Cloudsmith API key needed)
- Easier to test packaging changes alongside code changes
**Negative**:
- The `freenas-proxmox-packer` repo becomes deprecated (should be archived, not deleted — it has history)
- Need to restructure CI branch logic (currently done in the packer repo's action)
## Implementation Notes
Proposed directory layout in main repo:
```
packaging/
├── DEBIAN/
│ ├── control.j2 # Jinja2/envsubst template for version injection
│ ├── postinst
│ ├── postrm
│ └── triggers
└── files/ # Files to be embedded in the package (no git clone at install)
└── (populated by CI from the source tree)
```
The CI workflow should:
1. Check out the repo
2. Detect branch/tag to set version + repo component (dev/testing/stable)
3. Run `envsubst` on `control.j2` to inject version
4. Copy source files into the package staging area
5. Run `dpkg-deb --build`
6. Push to Cloudsmith (and optionally GitHub Releases)

View File

@ -0,0 +1,79 @@
# ADR-002: UI Integration Strategy
**Date**: 2026-05-15
**Status**: Under Discussion
**Deciders**: Kevin Adams
## Context
The plugin currently adds a UI by patching `pvemanagerlib.js`, the monolithic JavaScript bundle that is Proxmox VE's entire web UI. A separate versioned patch file must be maintained for each Proxmox VE minor release. When PVE updates, the patch breaks.
The desired end state is a UI that:
- Doesn't break on every PVE update
- Ideally doesn't require patching PVE system files
- Shows appropriate fields for TrueNAS API credentials
## Options Evaluated
### Option A — Continue Patching pvemanagerlib.js (Status Quo)
The current approach. Diff the upstream PVE JS, produce a patch per PVE version.
- **Pro**: Works in all PVE versions, field placement is ideal
- **Con**: Breaks on every PVE minor release; versioned patch sprawl is visible in the repo already (stable-5 through stable-8 folders)
- **Verdict**: Manageable with better automation (auto-detect PVE version in postinst and select the right patch)
### Option B — Serve a Separate JS File via pveproxy
Proxmox VE's `pveproxy` serves everything from `/usr/share/pve-manager/`. The HTML template (`/usr/share/pve-manager/index.html.tpl`) explicitly lists which JS files to load. A new file could be injected either by:
(a) Patching `index.html.tpl` to add a `<script>` tag — still a patch
(b) Discovering if pveproxy supports a "extras" JS directory — not documented, needs investigation
If pveproxy or its Perl handler exposes a hook point, a standalone `truenas-plugin.js` could be dropped in without touching pvemanagerlib.js. The JS would use `Ext.override()` to modify existing components.
- **Pro**: Single JS file, not tied to PVE version; installs cleanly
- **Con**: `Ext.override` is fragile when component internals change; still requires some PVE integration point
- **Verdict**: Worth investigating for PVE 8.x
### Option C — Full Custom Storage Plugin (`PVE::Storage::Custom`)
Register a brand-new storage type (e.g., `truenas`) rather than wedging into `ZFS-over-iSCSI`. Proxmox VE discovers `PVE::Storage::Custom::*` plugins at runtime via `Module::Load`. The UI in PVE 8.x automatically generates a form panel from the plugin's `properties()` definition.
The file `perl5/PVE/Storage/Custom/FreeNAS.pm` is an unfinished attempt at this. It registers `package PVE::Storage::Custom::TrueNASPlugin` with `type => 'truenas'`.
- **Pro**: No JS patching needed; plugin-managed UI; proper separation
- **Con**: Requires implementing the full `PVE::Storage::Plugin` API (alloc_image, free_image, list_images, status, etc.) which is significantly more work. Also, ZFS pool listing still uses SSH via `ZFSPoolPlugin.pm` which is outside our scope.
- **Verdict**: Best long-term architecture, but highest implementation effort. The existing `Custom/FreeNAS.pm` gives a starting point.
### Option D — Hybrid: Fix Patch Automation Now, Plan for Option C Later
1. **Now**: Fix postinst to select the correct patch for the installed PVE version automatically (no more per-version patch files in the repo — derive them at build time or select at install time)
2. **Later**: Complete `PVE::Storage::Custom::TrueNASPlugin` as a proper plugin when bandwidth allows
## Decision
**Decided 2026-05-15**: Option C — Full `PVE::Storage::Custom` Plugin (v3.x target)
Kevin confirmed this is the goal. The existing `perl5/PVE/Storage/Custom/FreeNAS.pm` is the starting point; it needs the duplicate subs fixed and the full `PVE::Storage::Plugin` interface implemented.
**Benefits realized**:
- Zero JS patching — PVE auto-generates UI from `properties()`
- Zero `ZFSPlugin.pm` patching — new type, not wedged into existing
- Zero `apidoc.js` patching — auto-documented
- Can eliminate SSH requirement — pool stats via TrueNAS API v2.0
- `postinst` becomes trivial: just copy two `.pm` files, restart services
**What the Custom plugin still needs `iscsiadm` for** (unavoidable, runs on Proxmox host):
- iSCSI login/logout (`activate_volume` / `deactivate_volume`)
- Device discovery after login (`/dev/disk/by-path/` or multipath)
**Migration path from v2.x → v3.x**:
- v2.x packages remain installable (they keep the ZFSPlugin.pm patch approach)
- v3.x introduces `type => 'truenas'` storage; users create a new storage and migrate VMs
- v3.x `postinst` can detect old `freenas`-provider storages and warn the user
## Open Questions Resolved
- PVE 8.x `Custom::` plugins DO auto-generate UI forms from `properties()`
- Kevin has a lab PVE 8.x node available for testing

View File

@ -0,0 +1,69 @@
# ADR-003: APT Repository Hosting
**Date**: 2026-05-15
**Status**: Under Discussion
**Deciders**: Kevin Adams
## Context
The project currently hosts `.deb` packages on Cloudsmith (a paid SaaS apt repo host). GitHub Actions in the packer repo push to Cloudsmith using an API key secret.
The goal is to evaluate whether to stay with Cloudsmith or move to a self-hosted or GitHub-native solution.
## Options Evaluated
### Option A — Continue with Cloudsmith (Status Quo)
Cloudsmith provides signed apt repos with CDN delivery. The existing repo URLs and GPG keys are already documented in the README and used by real users.
- **Pro**: Already working; users have it configured; CDN; signed packages
- **Con**: External service dependency; potential cost; API key management
- **Verdict**: Keep for now — changing the repo URL would break existing users
### Option B — GitHub Releases
Upload `.deb` files as GitHub Release assets. Users can download manually.
- **Pro**: Free; integrated with GitHub; no extra accounts
- **Con**: Not an apt repo — users can't `apt install` it, only manually download and `dpkg -i`. Not suitable as primary distribution.
- **Verdict**: Add as secondary distribution method (easy downloads for people who don't want apt)
### Option C — GitHub Pages as apt Repo
Use GitHub Actions to build a proper apt repo structure (`dists/`, `pool/`, `Packages.gz`, `Release`, `InRelease`) and deploy it to GitHub Pages. Tools like `apt-ftparchive` or `reprepro` can generate this.
- **Pro**: Free; fully integrated with GitHub; GPG signing still possible; users can add `deb [signed-by=...] https://thegrandwazoo.github.io/freenas-proxmox stable main` to their sources
- **Con**: GitHub Pages URL is less memorable than cloudsmith.io; GPG key management is manual; changing from Cloudsmith would break existing installs
- **Verdict**: Good long-term option if Cloudsmith becomes a problem. Can run in parallel.
### Option D — GitHub Packages (Container Registry / npm-style)
GitHub Packages does not support raw apt repositories natively. Not viable.
## Decision
**Decided 2026-05-15**: Transition from Cloudsmith to GitHub Pages (Option C).
Kevin wants to switch. The transition plan:
1. Build the GitHub Pages apt repo in parallel with Cloudsmith (both active)
2. Update README to point users at GitHub Pages URL
3. Keep Cloudsmith running for existing users until next major release
4. Drop Cloudsmith after v3.x ships (when migration period is over)
GitHub Pages apt repo structure:
```
docs/
├── dists/
│ └── stable/
│ ├── Release
│ ├── InRelease (GPG signed)
│ └── main/binary-all/
│ ├── Packages
│ └── Packages.gz
└── pool/
└── main/
└── freenas-proxmox_*.deb
```
CI uses `apt-ftparchive` to generate `Packages.gz` and `gpg --clearsign` for `InRelease`.
The repo GPG key will live as a GitHub Actions secret.

View File

@ -0,0 +1,63 @@
# ADR-004: Transactional Cleanup on API Operation Failure
**Date**: 2026-05-15
**Status**: Decided
**Deciders**: Kevin Adams
## Context
When `run_create_lu` partially succeeds — it creates an iSCSI extent on TrueNAS but then fails to create the target-to-extent mapping — the extent remains on TrueNAS with no association. These "dangling" extents accumulate and cause problems (LUN ID exhaustion, confusion in TrueNAS UI, wasted pool space).
Similarly, if `run_delete_lu` fails partway through, things can be left in a partial state.
## Current Code Path (broken)
```perl
sub run_create_lu {
my $extent = freenas_iscsi_create_extent($scfg, $lun_path); # Step 1
my $link = freenas_iscsi_create_target_to_extent( # Step 2
$scfg, $target_id, $extent->{'id'}, $lun_id);
die "Unable to create lun" if !defined($link); # Too late — extent already exists
}
```
If Step 2 fails, the code dies but the extent from Step 1 is still on TrueNAS.
## Decision
Implement try/catch rollback using `eval {}` blocks. On failure, undo completed steps in reverse order before dying.
## Pattern
```perl
sub run_create_lu {
my $extent = freenas_iscsi_create_extent($scfg, $lun_path);
die "Unable to create extent" unless defined $extent;
my $link = eval { freenas_iscsi_create_target_to_extent(
$scfg, $target_id, $extent->{'id'}, $lun_id) };
if (!defined($link) || $@) {
my $err = $@ || "target-to-extent creation returned undef";
syslog("err", (caller(0))[3] . " : rolling back extent $extent->{'id'}: $err");
eval { freenas_iscsi_remove_extent($scfg, $extent->{'id'}) };
syslog("err", (caller(0))[3] . " : rollback cleanup failed: $@") if $@;
die "Unable to create lun $lun_path (extent cleaned up): $err";
}
}
```
## Scope of Changes
All multi-step operations need this pattern:
| Operation | Steps | Rollback needed |
|-----------|-------|-----------------|
| `run_create_lu` | create extent → create targetextent | if targetextent fails, delete extent |
| `run_delete_lu` | find link → remove extent → remove targetextent | if either remove fails, log but don't re-add (partial delete is better than no delete) |
| `run_modify_lu` | delete old LU → create new LU | if create fails, the old LU is gone — log clearly and die |
For `modify_lu` specifically: the current code deletes then creates. If the create fails, data is not lost (the zvol is still there) but the iSCSI mapping is gone. We should create the new extent first, then delete the old link, then add the new link.
## Logging
All rollback actions should be logged at `syslog("err", ...)` level with enough context to manually recover: extent ID, target ID, LUN ID.

View File

@ -0,0 +1,66 @@
# ADR-005: Bearer Token Authentication as Primary Auth Method
**Date**: 2026-05-15
**Status**: Decided
**Deciders**: Kevin Adams
## Context
TrueNAS SCALE and recent TrueNAS CORE versions support API key (Bearer Token) authentication. Basic auth (username + password) requires a full system user account, is less secure, and may be deprecated in future TrueNAS releases.
The `feature_bearer_token` branch already has partial support:
- `truenas_token_auth` flag (boolean)
- `truenas_secret` field (holds either password or token depending on flag)
- Bearer Token header set in `freenas_api_connect` when flag is true
## Decision
Bearer Token auth should be the **primary** and recommended authentication method. Basic auth remains for backward compatibility.
## Changes Required
### In `LunCmd/FreeNAS.pm` (and future `Custom/TrueNASPlugin.pm`)
1. **Token auth is default** when `truenas_token_auth` is not explicitly set (or default to requiring it in new plugin)
2. **Validation at startup**: in `run_lun_command`, validate credentials presence before any API call
3. **Error messages** should guide users toward token auth when credentials are missing
### In the UI (Custom Plugin properties)
The new custom storage plugin defines these properties:
```perl
truenas_token_auth => {
description => "Use API Token instead of username/password",
type => 'boolean',
default => 1, # Default to token auth in the new plugin
},
truenas_secret => {
description => "TrueNAS API Token or Password",
type => 'string',
},
freenas_user => {
description => "TrueNAS Username (only needed without token auth)",
type => 'string',
optional => 1,
},
```
PVE auto-generates the UI form — the `freenas_user` field would be conditionally hidden in the UI when `truenas_token_auth` is true. This hiding behavior in the auto-generated form may need to be tested against PVE 8.x's form rendering.
### Generating an API Token in TrueNAS
TrueNAS SCALE: System → API Keys → Add
TrueNAS CORE: This may require web UI access or direct CLI.
The token is a long random string; it goes in the `truenas_secret` field. No username needed with token auth.
## Backward Compatibility
- Existing configs using `freenas_password` + `freenas_user` continue to work
- The custom plugin should map legacy fields on first load
- `freenas_password` is aliased to `truenas_secret` for backward compat
## Security Notes
- Tokens should be scoped to minimum needed permissions if TrueNAS supports scoped tokens
- The secret is stored in `/etc/pve/storage.cfg` (PVE cluster config) — it is not encrypted but the file is only readable by root

View File

@ -0,0 +1,59 @@
# ADR-006: Package Versioning Strategy
**Date**: 2026-05-15
**Status**: Decided
**Deciders**: Kevin Adams
## Context
The package has historically had version strings tied to specific Proxmox or TrueNAS releases (e.g., `ZFSPlugin-8.0.5_1.pm.patch`, `pvemanagerlib-7.4-3_1.js.patch`). This creates confusion — users aren't sure if `2.2.0-1` means it works with TrueNAS 2.2 or Proxmox 2.2.
## Decision
The package version is **independent** of both Proxmox VE and TrueNAS versions.
- Package version: semantic versioning `MAJOR.MINOR.PATCH` (e.g., `3.0.0`, `3.1.2`)
- Supported Proxmox VE versions and TrueNAS versions are documented in:
- GitHub Release notes (the primary place — visible when users click on a release)
- The `Description` field in `DEBIAN/control`
- README.md compatibility table
## Version Scheme
| Series | Meaning |
|--------|---------|
| `2.x.x` | Current approach (ZFSPlugin.pm patch wedge) |
| `3.x.x` | New `TrueNASPlugin` custom storage type |
| `3.0.x` | Patch releases for 3.0 (bug fixes, no new features) |
| `3.1.x` | Next minor — could add features like snapshot support |
## GitHub Release Notes Template
Each release on GitHub should include:
```markdown
## freenas-proxmox v3.0.0
### Supported Proxmox VE Versions
- Proxmox VE 8.0, 8.1, 8.2, 8.3
### Supported TrueNAS Versions
- TrueNAS CORE 13.0-U6+
- TrueNAS SCALE 23.10+, 24.04+
### What's New
- ...
### Breaking Changes from v2.x
- Storage type changes from `ZFS over iSCSI (freenas provider)` to `TrueNAS (API)`
- Migration guide: [link]
```
## CI Version Injection
The version is set in one place: a `VERSION` file or git tag at the repo root. CI reads it and injects via `envsubst` into `packaging/DEBIAN/control.j2`.
Branch-to-channel mapping (not version mapping):
- `feature_*` → alpha channel
- `master` → beta/testing channel
- Tagged release (`v3.0.0`) → stable channel

View File

@ -0,0 +1,35 @@
# ADR-007: FreeNAS vs TrueNAS Module Naming
**Date**: 2026-05-15
**Status**: Decided
**Deciders**: Kevin Adams
## Context
The Perl module is named `FreeNAS.pm` and installs to `PVE::Storage::LunCmd::FreeNAS`. The `iscsiprovider` value stored in Proxmox VE's `/etc/pve/storage.cfg` is `freenas`. All existing user configurations reference this name.
The product was renamed from FreeNAS to TrueNAS in 2020. The package is in the process of being modernized (v3.x goal is `PVE::Storage::Custom::TrueNASPlugin`).
## Decision
**v2.x (current):** Keep `FreeNAS.pm` and `iscsiprovider freenas`.
Changing the filename or the `iscsiprovider` value in v2.x would silently break every existing user's storage configuration — Proxmox would fail to load the backend, and VMs with attached storage would become inaccessible. The backward-compat cost is too high for a minor release.
**v3.x (new Custom plugin):** Use `TrueNAS.pm` (or embed in `TrueNASPlugin.pm`) and `type truenas`.
The v3.x architecture introduces a new storage type (`truenas`) via `PVE::Storage::Custom::TrueNASPlugin`. This is a clean break — users explicitly create a new `truenas`-type storage and migrate their VMs. The old `freenas` iSCSI provider continues to work in parallel during the transition.
## Migration Path
When v3.x ships:
1. Users create a new `truenas`-type storage pointing at the same TrueNAS server
2. Migrate VMs from old `freenas`-provider storage to new `truenas` storage using `qm move-disk` / `pvesm` commands
3. Remove the old `freenas`-provider storage definition
4. The v3.x `postrm` removes `FreeNAS.pm` and reverses ZFSPlugin patches; the v2.x files are no longer needed
## Internal Variable Naming
Within `FreeNAS.pm` (v2.x): continue using `freenas_*` variable names — changing them mid-v2.x would be a pointless churn commit with no user benefit.
Within the new `TrueNASPlugin.pm` (v3.x): use `truenas_*` for all new variables. New user-facing config keys should be `truenas_*`. Existing `freenas_*` keys are mapped as aliases for backward compat during the transition window.

View File

@ -0,0 +1,171 @@
# Plan 001: Project Revival — Overall Roadmap
**Date**: 2026-05-15
**Status**: Approved — ready for implementation
**Owner**: Kevin Adams
## Decisions Made
| Topic | Decision |
|-------|----------|
| Build pipeline | Consolidate into this repo; eliminate packer repo dispatch |
| Install-time deps | No git, diff, or patch at install time — embed all files in .deb |
| apt hosting | Transition to GitHub Pages (parallel with Cloudsmith, then cut over) |
| UI strategy | Full `PVE::Storage::Custom::TrueNASPlugin` + standalone JS file |
| JS patching | Single minimal patch to `index.html.tpl` to load `truenas-plugin.js` |
| Auth | Bearer Token as primary; basic auth as fallback for compat |
| Cleanup on failure | Rollback dangling TrueNAS resources on any step failure (ADR-004) |
| Code hardening | All findings in plan-002-code-review.md to be addressed |
---
## Architecture After v3.x
### What Gets Installed
```
/usr/share/perl5/PVE/Storage/Custom/TrueNASPlugin.pm ← new custom storage plugin
/usr/share/perl5/PVE/Storage/LunCmd/TrueNAS.pm ← API client (renamed + hardened)
/usr/share/pve-manager/js/truenas-plugin.js ← standalone UI JS (NO pvemanagerlib patch)
```
### What Gets Patched (minimal, stable)
```
/usr/share/pve-manager/index.html.tpl ← ONE LINE: add <script> for truenas-plugin.js
```
That's it. No `ZFSPlugin.pm` patch. No `pvemanagerlib.js` patch. No `apidoc.js` patch. The `index.html.tpl` is orders of magnitude more stable than the JS bundle.
### How `truenas-plugin.js` Works
Ships a proper `Ext.define('PVE.storage.TrueNASInputPanel', {...})` that registers as the configuration panel for `type = 'truenas'` storage. The panel handles:
- API host field
- Toggle: Bearer Token vs Username/Password
- Conditional field visibility (hide username when token auth is selected)
- Secret/token field with confirm
- SSL checkbox
- Pool field
When PVE renders the storage configuration dialog and sees `type = 'truenas'`, it picks up our registered panel class.
### `postinst` — What It Does
```bash
1. cp /usr/share/truenas-proxmox/TrueNASPlugin.pm /usr/share/perl5/PVE/Storage/Custom/
2. cp /usr/share/truenas-proxmox/TrueNAS.pm /usr/share/perl5/PVE/Storage/LunCmd/
3. cp /usr/share/truenas-proxmox/truenas-plugin.js /usr/share/pve-manager/js/
4. patch /usr/share/pve-manager/index.html.tpl (one <script> line, idempotent check first)
5. pvedaemon restart && pveproxy restart && pvestatd restart
```
No git. No curl. No version-matrix. No patch selection logic.
---
## Implementation Phases
### Phase 0 — Lab Environment Setup
**Owner**: Kevin
**What**: Build a PVE 8.x node in the lab for testing
**Needed before**: Phase 2
### Phase 1 — Build Pipeline (no source changes)
**Goal**: CI/CD lives entirely in this repo; .deb builds and deploys from here
Tasks:
- [ ] Create `packaging/DEBIAN/` directory with `control`, `postinst`, `postrm`, `triggers`
- [ ] Port existing packaging from packer repo (keeping the v2.x approach for now)
- [ ] Rewrite `postinst` to not git-clone at install (embed files at build time)
- [ ] Create `.github/workflows/build.yml` replacing `action.yml`
- Branch → version + component mapping (feature_ → alpha, master → beta, 2.0 → stable)
- `dpkg-deb` build step
- Cloudsmith push (existing key)
- GitHub Release asset upload
- [ ] Set up GitHub Pages apt repo structure in `docs/` branch or `gh-pages` branch
- `apt-ftparchive` to generate Packages/Release files
- GPG signing step (new key, stored as GH secret)
- [ ] Archive packer repo (do NOT delete — it has release history)
**Result**: Same v2.x packages but built entirely from this repo with no git at install.
### Phase 2 — Code Hardening (FreeNAS.pm / current approach)
**Goal**: Fix all critical and high issues in the existing `LunCmd/FreeNAS.pm` BEFORE porting to new architecture
Tasks (from plan-002-code-review.md):
- [ ] Fix #4: Regex bug in method validation (`$method !~ /^(?:GET|DELETE|POST)$/`)
- [ ] Fix #1: Rollback on failure in `run_create_lu` and `run_modify_lu` (ADR-004)
- [ ] Fix #3: `$runawayprevent` scope; fix `$freenas_rest_connection->{$apihost}` check
- [ ] Fix #11: Store `$product_name` per-host in `$freenas_server_list`
- [ ] Fix #2: Replace `eval $value` with explicit substitution map
- [ ] Fix #6: Per-request LUN list cache
- [ ] Fix #5: Log warning when SSL verification disabled
- [ ] Fix #8: Consistent taint validation on API response data
- [ ] Improve logging (remove "FreeNAS::" naming in syslog messages, add context)
- [ ] Remove debug `console.warn()` from pvemanagerlib patches
- [ ] Fix `postinst` `&> /dev/null` → redirect to log file
### Phase 3 — Custom Storage Plugin (`TrueNASPlugin.pm`)
**Goal**: New `PVE::Storage::Custom::TrueNASPlugin` with full plugin interface
Starting from `perl5/PVE/Storage/Custom/FreeNAS.pm` (existing unfinished file):
- [ ] Remove duplicate `properties()` and `options()` subs (#9)
- [ ] Implement `type()``'truenas'`
- [ ] Implement `properties()` with all TrueNAS-specific fields (see ADR-005)
- [ ] Implement `options()` including new truenas fields as optional
- [ ] Implement `status()` — pool stats via TrueNAS API v2.0 (`GET /api/v2.0/pool/dataset`)
- [ ] Implement `list_images()` — zvol listing via TrueNAS API
- [ ] Implement `alloc_image()` — create zvol + iSCSI extent + targetextent (with rollback)
- [ ] Implement `free_image()` — delete extent (force=true) + zvol (with logging)
- [ ] Implement `activate_volume()``iscsiadm` login
- [ ] Implement `deactivate_volume()``iscsiadm` logout
- [ ] Implement `path()` — find device from NAA/wwid after iSCSI login
- [ ] Implement `volume_resize()` — resize zvol via TrueNAS API + re-present LUN
- [ ] Port hardened API client from Phase 2 into TrueNASPlugin.pm (or keep as shared module)
- [ ] Integrate Bearer Token as default auth (ADR-005)
### Phase 4 — Standalone UI (`truenas-plugin.js`)
**Goal**: Full Ext.js panel for the 'truenas' storage type; loaded via one-line template patch
- [ ] Research: confirm `index.html.tpl` is the right injection point in PVE 8.x
- [ ] Write `pve-manager/js/truenas-plugin.js`:
- `Ext.define('PVE.storage.TrueNASInputPanel', {...})`
- Fields: API host, Bearer Token toggle, secret/confirm, username (conditional), SSL, pool, portal, target
- Controller logic for field visibility (show/hide username based on token toggle)
- Form submit/load value mapping (compat: `freenas_password``truenas_secret`)
- [ ] Write `index.html.tpl` patch (minimal: one `<script>` tag line)
- [ ] Test on lab PVE 8.x node
### Phase 5 — New Package (`v3.x`) + Migration
- [ ] Update `packaging/DEBIAN/control` (no `git` or `librest-client-perl` dependency)
- [ ] Update `packaging/DEBIAN/postinst` (Phase 0 design above — no patches, just cp)
- [ ] `packaging/DEBIAN/postrm` — remove Custom/*.pm, LunCmd/TrueNAS.pm, truenas-plugin.js, reverse index.html.tpl patch
- [ ] Migration guide in README: how to move from v2.x (ZFS-over-iSCSI + freenas provider) to v3.x (`truenas` storage type)
- [ ] Update Cloudsmith + GitHub Pages with v3.x packages
- [ ] Update README to point at GitHub Pages repo as primary
### Phase 6 — SSH Elimination (stretch goal)
**Goal**: Remove the SSH key requirement for ZFS pool listing
Currently, ZFS pool listing uses SSH (via `ZFSPoolPlugin.pm` upstream, not our code). The TrueNAS v2.0 API can return pool stats and dataset listings, so our custom plugin can return pool `status()` without SSH. The only remaining SSH need is in Proxmox's own ZFSPoolPlugin which we don't control.
If our plugin handles the pool listing entirely internally (using the API), we may be able to advise users to NOT configure SSH at all — needs investigation against the actual Proxmox boot and storage scan flow.
---
## Open Questions Before Implementation Starts
1. Does `index.html.tpl` exist at a known stable path in PVE 8.x? (need lab access)
2. Does PVE auto-pick up `Custom::*` plugins without any additional registration in pvemanagerlib.js?
3. Does `Ext.define('PVE.storage.TrueNASInputPanel')` get auto-wired to `type='truenas'` storage, or does pvemanagerlib.js need a registration entry?
4. Are there TrueNAS API v2.0 endpoints for creating/deleting zvols (not just iSCSI)? (needed for Phase 3)
Items 1-3 can be answered once the lab PVE 8.x node is up.
Item 4: checking TrueNAS API docs — `POST /api/v2.0/pool/dataset` with `type=VOLUME` and `volsize` should work.

View File

@ -0,0 +1,243 @@
# Plan 002: Code Review Findings
**Date**: 2026-05-15
**Reviewed file**: `perl5/PVE/Storage/LunCmd/FreeNAS.pm`
**Branch**: `feature_bearer_token`
---
## Critical Issues
### 1. Dangling Resources on Failure (ADR-004)
**Location**: `run_create_lu` (~line 265), `run_modify_lu` (~line 183)
`run_create_lu` creates an extent then creates a target-to-extent link. If the second call fails, the extent is left dangling on TrueNAS. Over time these accumulate.
`run_modify_lu` calls `run_delete_lu` then `run_create_lu`. If `run_create_lu` fails, the LUN mapping is gone with no recovery.
**Fix**: eval{} rollback pattern — see ADR-004.
---
### 2. `eval $value` Code Injection Pattern
**Location**: `freenas_iscsi_create_extent` (~line 559) and `freenas_iscsi_create_target_to_extent` (~line 663)
```perl
while ((my $key, my $value) = each %{$freenas_api_methods->{'extent'}->{'post_body'}}) {
$post_body->{$key} = ($value =~ /^\$.+$/) ? eval $value : $value;
}
```
The `post_body` hash contains strings like `"\$name"` and `"\$device"`. This pattern evaluates them as Perl expressions using local variable names in scope. It works because `$name` and `$device` are in scope, but:
- It's not obvious or maintainable
- A mistake in the API version matrix could silently evaluate unexpected code
- `use strict` would normally catch undeclared variables but `eval` bypasses it
**Fix**: Replace with an explicit substitution map:
```perl
my %substitutions = (
'$name' => $name,
'$device' => $device,
'$target_id' => $target_id,
'$extent_id' => $extent->{'id'},
'$lun_id' => $lun_id,
);
while ((my $key, my $value) = each %{$freenas_api_methods->{'extent'}->{'post_body'}}) {
$post_body->{$key} = exists $substitutions{$value} ? $substitutions{$value} : $value;
}
```
---
### 3. Global Mutable State
**Location**: Top of file (~lines 14-30)
```perl
my $freenas_server_list = undef;
my $freenas_rest_connection = undef;
my $freenas_global_config_list = undef;
my $freenas_global_config = undef;
my $freenas_api_version = "v1.0";
my $freenas_api_methods = undef;
my $freenas_api_variables = undef;
my $runawayprevent = 0;
```
These are module-level globals. `$runawayprevent` is reset to 0 only on successful connection, which means if a connection succeeds then later a separate storage backend is initialized, the counter may not reset properly in some call sequences.
More importantly: `$freenas_rest_connection` and `$freenas_global_config` are pointer variables into the `->{$apihost}` hashes but are also used as direct connection references. This is confusing — sometimes `$freenas_rest_connection` is the connection object, sometimes it's checked as a hash ref.
**Specific bug** in `freenas_api_check` (~line 406):
```perl
if (! defined $freenas_rest_connection->{$apihost}) {
```
`$freenas_rest_connection` is a `REST::Client` object (or undef), not a hash ref. Calling `->{'key'}` on a REST::Client object calls its hash-based accessor (since REST::Client is blessed hashref) — this works by accident but is wrong. The correct check should be `$freenas_server_list->{$apihost}`.
**Fix**:
- Move `$runawayprevent` to be a local variable passed into `freenas_api_connect` or use a closure
- Fix the `$freenas_rest_connection->{$apihost}` check to `$freenas_server_list->{$apihost}`
---
### 4. Regex Logic Bug in Method Validation
**Location**: `freenas_api_call` (~line 463)
```perl
if (! $method =~ /^(?>GET|DELETE|POST)$/) {
```
This does not do what it looks like. The `!` negates `$method` (making it the empty string `""`), then the empty string is tested against the regex. `""` does NOT match `GET|DELETE|POST`, so `!` of that match is true — meaning the condition is ALWAYS true and the die is always triggered. The code never actually makes an API call!
Wait — actually, let me re-read. In Perl, `!` has lower precedence than `=~`... Actually no. `!` is a unary prefix operator and it binds to `$method`, making it `(!$method)`. `!$method` is the boolean negation of `$method` — which is `""` (false) when `$method` is a non-empty string. Then `"" =~ /^(?>GET|DELETE|POST)$/` is false. Then `!` of that... wait.
Actually: `! $method =~ /regex/` is parsed as `(! $method) =~ /regex/`.
- `! $method` where `$method = "GET"` is `!1` = `""` (empty string)
- `"" =~ /^(?>GET|DELETE|POST)$/` is FALSE (empty string doesn't match)
- The `if` condition is false, so the die is NOT triggered
So the validation NEVER rejects invalid methods. It should be:
```perl
if ($method !~ /^(?:GET|DELETE|POST)$/) {
```
Also note: `(?>...)` is an atomic group, not a non-capturing group. Use `(?:...)` for non-capturing. In this context (alternation only, no backtracking issue) it doesn't matter, but is misleading.
---
### 5. Silent SSL Verification Disable
**Location**: `freenas_api_connect` (~lines 355-358)
```perl
if ($scfg->{freenas_use_ssl}) {
$freenas_server_list->{$apihost}->getUseragent()->ssl_opts(verify_hostname => 0);
$freenas_server_list->{$apihost}->getUseragent()->ssl_opts(SSL_verify_mode => SSL_VERIFY_NONE);
}
```
SSL verification is disabled silently with no user-visible warning. An expired or self-signed cert is a common TrueNAS setup, but users should know they're operating without cert validation.
**Fix**: Log a warning at `syslog("warning", ...)` level when SSL verification is disabled.
---
### 6. Inefficient Multiple API Calls in `freenas_list_lu`
**Location**: `freenas_list_lu` (~line 712)
Every call to `freenas_list_lu` makes 3 API calls: `freenas_iscsi_get_target`, `freenas_iscsi_get_target_to_extent`, `freenas_iscsi_get_extent`. This function is called from:
- `run_list_lu`
- `run_list_extent`
- `run_delete_lu`
- Indirectly from `run_create_lu` via `run_list_lu`
Multiple operations (e.g., `modify_lu` = delete + create) can make 6-9 API calls when 3 would suffice.
**Fix**: Add a per-request cache keyed on `$apihost`. Clear it at the start of each top-level `run_lun_command` call. This is safe because each `run_lun_command` invocation is a complete operation.
---
### 7. API v2.0 `freenas_iscsi_remove_target_to_extent` Early Return
**Location**: `freenas_iscsi_remove_target_to_extent` (~line 692)
```perl
if ($freenas_api_version eq "v2.0") {
syslog("info", ... "V2.0 API's so NOT Needed...successful");
return 1;
}
```
This skips the DELETE call for v2.0 APIs entirely, with a comment saying it's "NOT Needed". But the TrueNAS v2.0 API DOES have a `DELETE /api/v2.0/iscsi/targetextent/id/{id}/` endpoint.
Looking at the `freenas_iscsi_remove_extent` for v2.0, the extent delete body includes `"force": true` — which in TrueNAS v2.0 semantics means "also delete associated targetextents". So this early return is intentional: deleting the extent with `force=true` already removes the targetextent link.
However, this means in `run_delete_lu`, the targetextent link is NOT explicitly removed (it's done implicitly by the force-delete of the extent). The check `$remove_link == 1` at the end of `run_delete_lu` evaluates the return value of `freenas_iscsi_remove_target_to_extent` which returns `1` unconditionally for v2.0 — so the success check still passes.
This is correct behavior but is not obvious. The comment should be improved.
---
### 8. Taint Check Inconsistency
**Location**: `freenas_list_lu` (~line 731) and `ZFSPlugin.pm` patch
```perl
if ($item->{$freenas_api_variables->{'lunid'}} =~ /(\d+)/) {
...
$node->{$freenas_api_variables->{'lunid'}} .= "$1";
```
Taint checking is applied to the lunid but not to other values from the API (extent path, NAA, etc.). If Proxmox VE runs in taint mode, this could cause issues.
**Fix**: Consistently validate all values that come from external API responses before use.
---
### 9. `Custom/FreeNAS.pm` — Duplicate Sub Definitions
**Location**: `perl5/PVE/Storage/Custom/FreeNAS.pm`
The file defines `sub properties` and `sub options` **twice each**. In Perl with `use strict`, this generates a warning ("Subroutine properties redefined"). The second definition wins. The first set of `properties()` and `options()` appears to be for an older/different plugin and was left in by accident.
**Fix**: Remove the first (duplicate) `properties` and `options` subs.
---
## Medium Issues
### 10. Mixed Naming Convention
`freenas_*` vs `truenas_*` is inconsistent. Most internal functions are `freenas_*`. User-facing config variables have both (`freenas_user`, `freenas_password`, `truenas_secret`, `truenas_token_auth`).
**Fix**: In the new `TrueNASPlugin`, use `truenas_*` consistently for all new code.
### 11. `$product_name` Used for TrueNAS SCALE Detection
The `TrueNAS-SCALE` pool name handling:
```perl
if ($product_name eq "TrueNAS-SCALE") {
$pool =~ s/\//-/g;
}
```
This is a global variable (`my $product_name`) set in `freenas_api_check`. If two different storage backends connect to different TrueNAS instances (one SCALE, one CORE), this global gets overwritten by the last connection. The per-host API version is cached in `$freenas_server_list->{$apihost}` but `$product_name` is not per-host.
**Fix**: Store `product_name` in the per-host hash alongside the connection.
### 12. `REST::Client` vs `LWP::UserAgent`
The file imports both `LWP::UserAgent` and `HTTP::Request` (unused in the current implementation) AND uses `REST::Client`. The imports at the top suggest a migration started but wasn't completed.
**Fix**: In the new plugin, use `LWP::UserAgent` directly and remove the `REST::Client` dependency.
---
## Low / Style Issues
- Several syslog messages still say "FreeNAS::" instead of the caller's actual function
- `&> /dev/null` in postinst swallows errors — use `>> /tmp/freenas-proxmox-install.log 2>&1` instead for debuggability
- Debug `console.warn()` calls left in the pvemanagerlib.js patch (lines 85-89 of the stable-8 patch)
---
## Summary Priority Table
| # | Severity | Issue | Scope |
|---|----------|-------|-------|
| 1 | Critical | Dangling resources on failure | FreeNAS.pm |
| 2 | High | eval $value pattern (unclear, fragile) | FreeNAS.pm |
| 3 | High | Global mutable state / $runawayprevent | FreeNAS.pm |
| 4 | High | Regex bug — method validation never works | FreeNAS.pm |
| 5 | Medium | SSL verify disable without warning | FreeNAS.pm |
| 6 | Medium | Inefficient repeated API calls | FreeNAS.pm |
| 7 | Low | v2.0 targetextent removal comment unclear | FreeNAS.pm |
| 8 | Medium | Taint check inconsistency | FreeNAS.pm |
| 9 | High | Duplicate subs in Custom/FreeNAS.pm | Custom/FreeNAS.pm |
| 10 | Low | Mixed freenas_/truenas_ naming | all |
| 11 | Medium | $product_name not per-host | FreeNAS.pm |
| 12 | Medium | REST::Client vs LWP::UserAgent | FreeNAS.pm |

View File

@ -0,0 +1,70 @@
# Runbook: Generate a Patch for a New Proxmox VE Version
When Proxmox VE releases an update that breaks the existing patches, follow these steps.
## Prerequisites
- A Proxmox VE node (or VM) running the new version
- SSH access to that node
- The current `FreeNAS.pm` changes you want to apply
## Steps
### 1. Copy the original files from the PVE node
```bash
# On your dev machine
PVE_HOST=your-proxmox-node
PVE_VER=$(ssh root@$PVE_HOST "dpkg-query --showformat='\${Version}' --show pve-manager")
mkdir -p stable-8/originals
scp root@$PVE_HOST:/usr/share/perl5/PVE/Storage/ZFSPlugin.pm \
stable-8/perl5/PVE/Storage/ZFSPlugin.pm.orig
scp root@$PVE_HOST:/usr/share/pve-manager/js/pvemanagerlib.js \
stable-8/pve-manager/js/pvemanagerlib.js.orig
scp root@$PVE_HOST:/usr/share/pve-docs/api-viewer/apidoc.js \
stable-8/pve-docs/api-viewer/apidoc.js.orig
```
### 2. Apply the desired modifications
Work on copies of the `.orig` files:
```bash
cp stable-8/perl5/PVE/Storage/ZFSPlugin.pm.orig /tmp/ZFSPlugin.pm
# ... make your changes manually or apply the known modifications ...
```
### 3. Generate the patch
```bash
diff -u stable-8/perl5/PVE/Storage/ZFSPlugin.pm.orig /tmp/ZFSPlugin.pm \
> stable-8/perl5/PVE/Storage/ZFSPlugin-${PVE_VER}.pm.patch
# Do the same for the other files
```
### 4. Test the patch
```bash
# On the PVE node or a copy:
patch --dry-run -p0 /usr/share/perl5/PVE/Storage/ZFSPlugin.pm \
< stable-8/perl5/PVE/Storage/ZFSPlugin-${PVE_VER}.pm.patch
```
### 5. Update the postinst version map
In `packaging/DEBIAN/postinst`, update the version-to-patch-file mapping to include the new PVE version.
### 6. Update the default `.patch` symlink or file
The `postinst` selects a patch file based on the installed PVE version. Make sure the new patch is in the version map.
## Notes
- The JS files are large (pvemanagerlib.js is several MB); patches are usually small diffs around the iSCSI provider section
- Use `--ignore-whitespace` with patch to handle indentation differences
- Test with `patch --dry-run` before committing

4
.github/FUNDING.yml vendored
View File

@ -1 +1,3 @@
custom: "https://www.paypal.me/TheGrandWazoo69"
github: TheGrandWazoo
custom:
- "https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=TCLNEMBUYQUXN&source=url"

59
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

@ -0,0 +1,59 @@
---
name: Bug Report
about: Report a problem with the plugin
title: "[BUG] "
labels: bug
assignees: TheGrandWazoo
---
## Environment
| Component | Version |
|-----------|---------|
| Plugin (`dpkg -l freenas-proxmox`) | |
| Proxmox VE (`pveversion`) | |
| TrueNAS type | CORE / SCALE |
| TrueNAS version | |
| Authentication method | Token / Password |
## Description
A clear description of what the bug is.
## Steps to Reproduce
1.
2.
3.
## Expected Behavior
What you expected to happen.
## Actual Behavior
What actually happened.
## Relevant Log Output
Run the following on your Proxmox node and paste the output:
```bash
grep -i freenas /var/log/syslog | tail -50
```
```
(paste log output here)
```
## Storage Configuration
Paste your storage config entry from `/etc/pve/storage.cfg`**redact any passwords or API tokens**:
```
(paste config here, credentials redacted)
```
## Additional Context
Any other information that might help: network topology, multipath, multiple Proxmox nodes, etc.

View File

@ -0,0 +1,35 @@
---
name: Feature Request
about: Suggest an improvement or new capability
title: "[FEATURE] "
labels: enhancement
assignees: TheGrandWazoo
---
## Summary
A brief, clear description of the feature you'd like.
## Problem It Solves
What use case does this address? What problem does it solve for you?
## Proposed Solution
How do you envision this working? Any specific API endpoints, UI changes, or behaviors in mind?
## Alternatives Considered
Any workarounds you've tried, or alternative approaches you've considered.
## Environment
| Component | Version |
|-----------|---------|
| Proxmox VE | |
| TrueNAS type | CORE / SCALE |
| TrueNAS version | |
## Additional Context
Screenshots, links to TrueNAS API docs, or any other information.

32
.github/PULL_REQUEST_TEMPLATE.md vendored Normal file
View File

@ -0,0 +1,32 @@
## Description
What does this PR do? Why is this change needed?
Fixes # (issue number, if applicable)
## Type of Change
- [ ] Bug fix
- [ ] New feature
- [ ] Breaking change
- [ ] Documentation update
- [ ] Refactor / code quality
## Testing
Describe how you tested this change:
- [ ] Tested on Proxmox VE version: ___
- [ ] Tested with TrueNAS CORE version: ___
- [ ] Tested with TrueNAS SCALE version: ___
- [ ] Tested Bearer Token authentication
- [ ] Tested Username/Password authentication
- [ ] Tested create, delete, and resize of volumes
- [ ] Ran `perl -c` syntax check on modified `.pm` files
## Checklist
- [ ] My changes follow the coding standards in [CONTRIBUTING.md](CONTRIBUTING.md)
- [ ] I have updated [CHANGELOG.md](CHANGELOG.md) under `[Unreleased]`
- [ ] I have not included unrelated changes
- [ ] Passwords and API tokens are not present in any test output or logs I've included

24
.github/stale.yml vendored
View File

@ -1,17 +1,15 @@
# Number of days of inactivity before an issue becomes stale
daysUntilStale: 60
# Number of days of inactivity before a stale issue is closed
daysUntilClose: 7
# Issues with these labels will never be considered stale
daysUntilStale: 90
daysUntilClose: 14
exemptLabels:
- pinned
- security
# Label to use when marking an issue as stale
staleLabel: wontfix
# Comment to post when marking an issue as stale. Set to `false` to disable
- roadmap
- confirmed
staleLabel: stale
markComment: >
This issue has been automatically marked as stale because it has not had
recent activity. It will be closed if no further activity occurs. Thank you
for your contributions.
# Comment to post when closing a stale issue. Set to `false` to disable
closeComment: false
This issue has been automatically marked as stale due to inactivity (90 days).
It will be closed in 14 days unless there is new activity.
If this is still relevant, please comment with updated information or a status update.
closeComment: >
Closed due to inactivity. If this issue is still relevant on a current release,
please open a new issue with updated details.

View File

@ -1,17 +1,4 @@
name: Dispatch build of the freenas-proxmox plugin package
on:
push:
jobs:
dispatch:
name: Dispatch to the build and packager workflow.
runs-on: ubuntu-latest
steps:
- name: Send dispatch request to 'freenas-proxmox-packer' repo.
uses: peter-evans/repository-dispatch@v1
with:
token: ${{ secrets.ACCESS_TOKEN }}
repository: TheGrandWazoo/freenas-proxmox-packer
event-type: build_push
client-payload: '{"ref" : "${{ github.ref }}", "sha": "${{ github.sha }}"}'
# This workflow has been superseded by build.yml
# It previously dispatched to the external freenas-proxmox-packer repository.
# The build pipeline now lives entirely in build.yml in this repository.
# This file is kept only to avoid breaking any external references; it does nothing.

318
.github/workflows/build.yml vendored Normal file
View File

@ -0,0 +1,318 @@
name: CI / Build / Publish
on:
push:
branches: ["**"]
tags: ["v*.*.*"]
pull_request:
branches: [master]
env:
PACKAGE_NAME: freenas-proxmox
# Cancel in-flight runs for the same branch on new push
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
# ── Job 1: Lint ──────────────────────────────────────────────────────────────
jobs:
lint:
name: Lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install lint tools
run: |
sudo apt-get update -qq
sudo apt-get install -y shellcheck libperl-critic-perl
- name: Perl syntax check (all modules)
run: |
echo "==> Checking Perl syntax..."
find perl5 stable-*/perl5 -name "*.pm" -print0 \
| xargs -0 -I{} perl -c {} \
&& echo "All .pm files OK"
- name: Perl static analysis (perlcritic)
run: |
echo "==> Running perlcritic..."
perlcritic --profile .perlcriticrc \
perl5/PVE/Storage/LunCmd/FreeNAS.pm \
perl5/PVE/Storage/Custom/FreeNAS.pm
- name: Shell script lint (shellcheck)
run: |
echo "==> Running shellcheck..."
shellcheck --severity=warning \
packaging/DEBIAN/postinst \
packaging/DEBIAN/postrm
echo "Shell scripts OK"
# ── Job 2: Validate patches apply cleanly ──────────────────────────────────
validate-patches:
name: Validate Patches
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Dry-run patch: ZFSPlugin (PVE 8)
run: |
patch --dry-run --ignore-whitespace \
stable-8/perl5/PVE/Storage/ZFSPlugin.pm.orig \
< stable-8/perl5/PVE/Storage/ZFSPlugin.pm.patch \
&& echo "ZFSPlugin PVE-8 patch: OK"
- name: Dry-run patch: apidoc.js (PVE 8)
run: |
patch --dry-run --ignore-whitespace \
stable-8/pve-docs/api-viewer/apidoc.js.orig \
< stable-8/pve-docs/api-viewer/apidoc.js.patch \
&& echo "apidoc PVE-8 patch: OK"
# ── Job 3: Build .deb ────────────────────────────────────────────────────────
build:
name: Build Package
runs-on: ubuntu-latest
needs: [lint, validate-patches]
outputs:
version: ${{ steps.vars.outputs.version }}
deb_file: ${{ steps.vars.outputs.deb_file }}
channel: ${{ steps.vars.outputs.channel }}
cloudsmith_repo: ${{ steps.vars.outputs.cloudsmith_repo }}
is_release: ${{ steps.vars.outputs.is_release }}
steps:
- uses: actions/checkout@v4
with:
# Fetch full history so tag-based versioning works
fetch-depth: 0
# ── Version resolution ─────────────────────────────────────────────────
# Version strategy:
# Tagged release (v1.2.3) → 1.2.3 (stable channel)
# master branch → <tag>-beta+<sha> (testing channel)
# feature_* branch → <tag>-alpha+<sha> (development channel)
# any other branch / PR → <tag>-dev+<sha> (no publish)
#
# The base version is derived from the most recent git tag (vX.Y.Z).
# No VERSION file needed — the tag IS the version.
- name: Resolve version and channel
id: vars
run: |
SHORT_SHA="${GITHUB_SHA:0:7}"
REF="${{ github.ref }}"
IS_RELEASE="false"
# Base version from the nearest vX.Y.Z tag (strips the 'v' prefix)
BASE_VERSION="$(git describe --tags --match 'v*' --abbrev=0 2>/dev/null | sed 's/^v//' || echo '0.0.0')"
if [[ "$REF" == refs/tags/v* ]]; then
VERSION="${REF#refs/tags/v}"
CHANNEL="stable"
CLOUDSMITH_REPO="truenas-proxmox"
IS_RELEASE="true"
elif [[ "$REF" == refs/heads/master ]]; then
VERSION="${BASE_VERSION}-beta+${SHORT_SHA}"
CHANNEL="testing"
CLOUDSMITH_REPO="truenas-proxmox-testing"
elif [[ "$REF" == refs/heads/feature_* ]]; then
VERSION="${BASE_VERSION}-alpha+${SHORT_SHA}"
CHANNEL="development"
CLOUDSMITH_REPO="truenas-proxmox-snapshots"
else
VERSION="${BASE_VERSION}-dev+${SHORT_SHA}"
CHANNEL="none"
CLOUDSMITH_REPO=""
fi
DEB_FILE="${PACKAGE_NAME}_${VERSION}_all.deb"
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
echo "deb_file=${DEB_FILE}" >> "$GITHUB_OUTPUT"
echo "channel=${CHANNEL}" >> "$GITHUB_OUTPUT"
echo "cloudsmith_repo=${CLOUDSMITH_REPO}" >> "$GITHUB_OUTPUT"
echo "is_release=${IS_RELEASE}" >> "$GITHUB_OUTPUT"
{
echo "### Build Summary"
echo "| | |"
echo "|---|---|"
echo "| Version | \`${VERSION}\` |"
echo "| Channel | \`${CHANNEL}\` |"
echo "| Package | \`${DEB_FILE}\` |"
} >> "$GITHUB_STEP_SUMMARY"
# ── Assemble staging directory ─────────────────────────────────────────
- name: Assemble package staging directory
run: |
VERSION="${{ steps.vars.outputs.version }}"
STAGING="dist"
mkdir -p "${STAGING}/DEBIAN"
mkdir -p "${STAGING}/usr/share/freenas-proxmox/patches/ZFSPlugin"
mkdir -p "${STAGING}/usr/share/freenas-proxmox/patches/pvemanagerlib"
mkdir -p "${STAGING}/usr/share/freenas-proxmox/patches/apidoc"
# Generate control file from template
sed "s/\${VERSION}/${VERSION}/" packaging/DEBIAN/control.j2 \
> "${STAGING}/DEBIAN/control"
# Maintainer scripts
cp packaging/DEBIAN/postinst "${STAGING}/DEBIAN/postinst"
cp packaging/DEBIAN/postrm "${STAGING}/DEBIAN/postrm"
cp packaging/DEBIAN/triggers "${STAGING}/DEBIAN/triggers"
chmod 0755 "${STAGING}/DEBIAN/postinst" "${STAGING}/DEBIAN/postrm"
# Plugin source files
cp perl5/PVE/Storage/LunCmd/FreeNAS.pm "${STAGING}/usr/share/freenas-proxmox/FreeNAS.pm"
cp perl5/REST/Client.pm "${STAGING}/usr/share/freenas-proxmox/REST-Client.pm"
# PVE 8 patches (primary supported version)
cp stable-8/perl5/PVE/Storage/ZFSPlugin.pm.patch \
"${STAGING}/usr/share/freenas-proxmox/patches/ZFSPlugin/8.patch"
cp stable-8/pve-manager/js/pvemanagerlib.js.patch \
"${STAGING}/usr/share/freenas-proxmox/patches/pvemanagerlib/8.patch"
cp stable-8/pve-docs/api-viewer/apidoc.js.patch \
"${STAGING}/usr/share/freenas-proxmox/patches/apidoc/8.patch"
# PVE 7 patches (best-effort — use latest versioned patch available)
for type in ZFSPlugin pvemanagerlib apidoc; do
case "$type" in
ZFSPlugin) glob="stable-7/perl5/PVE/Storage/ZFSPlugin-*.pm.patch" ;;
pvemanagerlib) glob="stable-7/pve-manager/js/pvemanagerlib-*.js.patch" ;;
apidoc) glob="stable-7/pve-docs/api-viewer/apidoc-*.js.patch" ;;
esac
latest=$(ls $glob 2>/dev/null | sort -V | tail -1 || true)
if [ -n "$latest" ]; then
cp "$latest" "${STAGING}/usr/share/freenas-proxmox/patches/${type}/7.patch"
echo "Bundled PVE-7 ${type} patch: $(basename $latest)"
else
echo "No PVE-7 ${type} patch found — skipping"
fi
done
echo "==> Package contents:"
find "${STAGING}" | sort
- name: Build .deb
run: |
sudo dpkg-deb -Zgzip --build dist "${{ steps.vars.outputs.deb_file }}"
- name: Verify .deb
run: |
echo "==> Package info:"
dpkg-deb --info "${{ steps.vars.outputs.deb_file }}"
echo ""
echo "==> Package contents:"
dpkg-deb --contents "${{ steps.vars.outputs.deb_file }}"
- name: Upload package artifact
uses: actions/upload-artifact@v4
with:
name: ${{ steps.vars.outputs.deb_file }}
path: ${{ steps.vars.outputs.deb_file }}
retention-days: 30
# ── Job 4: Security scan ─────────────────────────────────────────────────────
security:
name: Security Scan
runs-on: ubuntu-latest
needs: build
steps:
- uses: actions/checkout@v4
# Scan the repository for secrets and known vulnerabilities
- name: Run Trivy (repo scan — secrets + misconfig)
uses: aquasecurity/trivy-action@master
with:
scan-type: fs
scan-ref: .
scanners: secret,misconfig
severity: HIGH,CRITICAL
exit-code: 1
format: table
# Download and scan the built .deb
- name: Download built package
uses: actions/download-artifact@v4
with:
name: ${{ needs.build.outputs.deb_file }}
- name: Extract and scan .deb contents
run: |
mkdir -p deb-contents
dpkg-deb --extract "${{ needs.build.outputs.deb_file }}" deb-contents/
- name: Run Trivy (package contents — vuln + secret)
uses: aquasecurity/trivy-action@master
with:
scan-type: fs
scan-ref: deb-contents
scanners: vuln,secret
severity: HIGH,CRITICAL
exit-code: 1
format: table
# ── Job 5: Publish ───────────────────────────────────────────────────────────
publish:
name: Publish
runs-on: ubuntu-latest
needs: [build, security]
# Only publish on direct pushes (not PRs) to tracked branches or tags
if: |
github.event_name == 'push' &&
needs.build.outputs.channel != 'none'
steps:
- uses: actions/checkout@v4
- name: Download built package
uses: actions/download-artifact@v4
with:
name: ${{ needs.build.outputs.deb_file }}
- name: Publish to Cloudsmith
uses: cloudsmith-io/action@master
with:
api-key: ${{ secrets.CLOUDSMITH_API_KEY }}
command: push
format: deb
owner: ksatechnologies
repo: ${{ needs.build.outputs.cloudsmith_repo }}
distro: debian
release: any-version
file: ${{ needs.build.outputs.deb_file }}
- name: Create draft GitHub Release
if: needs.build.outputs.is_release == 'true'
uses: softprops/action-gh-release@v2
with:
name: "v${{ needs.build.outputs.version }}"
draft: true
files: ${{ needs.build.outputs.deb_file }}
generate_release_notes: false
body: |
## freenas-proxmox v${{ needs.build.outputs.version }}
> **Edit before publishing** — fill in tested versions below.
### Supported Proxmox VE Versions
- Proxmox VE 8.x (tested: )
- Proxmox VE 7.x (best-effort: )
### Supported TrueNAS Versions
- TrueNAS CORE:
- TrueNAS SCALE:
### Installation
See [README](https://github.com/TheGrandWazoo/freenas-proxmox#installation).
### Changes
See [CHANGELOG.md](https://github.com/TheGrandWazoo/freenas-proxmox/blob/master/CHANGELOG.md#unreleased).

34
.perlcriticrc Normal file
View File

@ -0,0 +1,34 @@
# Perl::Critic configuration for freenas-proxmox
# Severity scale: 1 (brutal) → 5 (gentle). We start at 4 and tighten over time.
# Run: perlcritic --profile .perlcriticrc perl5/PVE/Storage/LunCmd/FreeNAS.pm
severity = 4
theme = core
verbose = %f:%l:%c [%p] %m\n
# ── Rules currently disabled ─────────────────────────────────────────────────
# These are known issues being fixed in Phase 2 (code hardening).
# Remove entries here as the underlying code is fixed.
# eval $value pattern — being replaced with explicit substitution map (Phase 2)
[-BuiltinFunctions::ProhibitStringyEval]
# Postfix conditionals (if/unless at end of line) — style preference, keep
[-ControlStructures::ProhibitPostfixControls]
# Long subs — FreeNAS.pm has some; will be refactored in Phase 3
[-Subroutines::ProhibitExcessComplexity]
# Global variables — known issue, fixing in Phase 2
[-Variables::ProhibitPackageVars]
# ── Rules with custom settings ────────────────────────────────────────────────
[InputOutput::RequireCheckedSyscalls]
functions = :builtins
exclude_functions = print say warn
[ValuesAndExpressions::ProhibitMagicNumbers]
allowed_values = 0 1 2 200 201 204 302 307
[Subroutines::ProhibitBuiltinHomonyms]
severity = 5

67
CHANGELOG.md Normal file
View File

@ -0,0 +1,67 @@
# Changelog
All notable changes to this project will be documented here.
Format follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
Versions follow [Semantic Versioning](https://semver.org/spec/v2.0.0.html) independent of Proxmox VE or TrueNAS versions.
See each [GitHub Release](https://github.com/TheGrandWazoo/freenas-proxmox/releases) for the specific Proxmox VE and TrueNAS versions supported.
---
## [Unreleased] — v3.0.0
### Added
- New `PVE::Storage::Custom::TrueNASPlugin` — proper Proxmox VE custom storage type (no more ZFSPlugin.pm patching)
- Standalone `truenas-plugin.js` UI panel — eliminates patching of `pvemanagerlib.js`
- Automatic rollback of TrueNAS API changes when operations partially fail (fixes dangling iSCSI extents)
- Bearer Token authentication as the primary auth method
- Per-host product name tracking (fixes behavior when multiple TrueNAS backends are configured)
- GitHub Pages apt repository as primary distribution channel
### Changed
- Package no longer requires `git` or `patch` at install time
- Package no longer downloads from GitHub at install time (files embedded in `.deb`)
- Replaced `REST::Client` with `LWP::UserAgent` (already present in Proxmox VE)
- Renamed module internally from `FreeNAS` to `TrueNAS` namespace
- License changed from MIT to AGPL-3.0
### Fixed
- Method validation regex in `freenas_api_call` had incorrect Perl operator precedence — validation never fired
- `$runawayprevent` was a module global that could persist incorrectly across multiple connections
- SSL certificate verification was disabled silently without logging a warning
- `eval $value` template substitution replaced with explicit substitution map
### Removed
- `stable-5/`, `stable-6/`, `stable-7/` version-specific patch directories (superseded by new architecture)
- Dependency on `librest-client-perl`
- Dependency on `git`
---
## [2.3.0] — 2024-01-07
### Added
- Bearer Token authentication support (`truenas_token_auth` flag, `truenas_secret` field)
- TrueNAS SCALE version string parsing
### Changed
- Renamed `freenas_password` to `truenas_secret` to represent either a password or token
- Indentation and whitespace cleanup
---
## [2.2.0] — 2023-08-16
### Fixed
- Repository issues (#151, #152, #153)
- PayPal donation link
- `postinst` Windows-style line ending issue (#149)
### Changed
- Added `systemctl restart pvescheduler.service` to post-install
---
## [2.1.x] and Earlier
See the [commit history](https://github.com/TheGrandWazoo/freenas-proxmox/commits/master) for earlier changes.

83
CLAUDE.md Normal file
View File

@ -0,0 +1,83 @@
# freenas-proxmox — Claude Code Guide
## What This Project Is
A storage plugin "wedge" for Proxmox VE (PVE) that allows PVE to manage iSCSI LUNs on TrueNAS/FreeNAS via the TrueNAS REST API instead of the traditional SSH-based `iscsiadm` approach.
The plugin installs as a Debian package and works by:
1. Deploying a new Perl LunCmd handler (`FreeNAS.pm`) to `/usr/share/perl5/PVE/Storage/LunCmd/`
2. Patching three Proxmox VE system files at install time via `dpkg triggers`
## Repository Layout
```
freenas-proxmox/
├── perl5/PVE/Storage/
│ ├── Custom/FreeNAS.pm # Old attempt at a full custom storage type (unfinished)
│ ├── LunCmd/FreeNAS.pm # MAIN backend: iSCSI LunCmd via TrueNAS REST API
│ ├── LunCmd/FreeNAS-ng.pm # Next-gen draft (not in use)
│ └── ZFSPlugin-*.pm.patch # Per-PVE-version patches for ZFSPlugin.pm
├── pve-manager/js/
│ └── pvemanagerlib-*.js.patch # Per-PVE-version patches for the Proxmox UI JS
├── pve-docs/api-viewer/
│ └── apidoc-*.js.patch # Per-PVE-version patches for the API docs JS
├── perl5/REST/Client.pm # Bundled REST::Client (also an apt dependency)
├── stable-5/, stable-6/, stable-7/, stable-8/
│ # Per-major-version snapshots of patches + originals
└── .github/workflows/action.yml # Currently just dispatches to external packer repo
```
## How the Current Build Works (Two-Repo Problem)
1. A push to this repo triggers `action.yml` which fires a `repository_dispatch` event to `TheGrandWazoo/freenas-proxmox-packer`
2. That separate repo holds the DEBIAN package structure (`DEBIAN/control`, `postinst`, `postrm`, `triggers`)
3. Its CI builds the `.deb` with `dpkg-deb` and pushes to Cloudsmith
The `postinst` script at install time **git-clones this repo** to `/usr/local/src/freenas-proxmox` and applies patches from there. This is the key fragility — internet access required at package install time.
## Three Files That Get Patched at Install Time
| File | What the patch adds |
|------|---------------------|
| `/usr/share/perl5/PVE/Storage/ZFSPlugin.pm` | Adds `freenas` as a valid iSCSI provider, routes `run_lun_command` to `FreeNAS.pm`, adds custom properties/options |
| `/usr/share/pve-manager/js/pvemanagerlib.js` | Adds `FreeNAS/TrueNAS API` to the iSCSI provider dropdown; adds UI fields for API host, user, secret, SSL, token auth |
| `/usr/share/pve-docs/api-viewer/apidoc.js` | Registers TrueNAS-specific properties in the API docs |
## Authentication Modes Supported
- **Basic Auth**: `freenas_user` + `freenas_password` (deprecated but still works)
- **Bearer Token**: `truenas_token_auth=true` + `truenas_secret` (preferred for TrueNAS SCALE)
## TrueNAS API Version Detection
The plugin auto-detects v1.0 vs v2.0 API based on the TrueNAS version string and HTTP response:
- `>= 11.03.01.00` → uses v2.0 API
- Older → uses v1.0 API
## Known Issues / Active Work
- Versioned patches are fragile — each PVE minor release may need a new patch
- `postinst` clones the repo at install time (requires internet, fragile)
- `Custom/FreeNAS.pm` is unfinished (duplicate `properties()` and `options()` subs)
- REST::Client is both an apt dependency AND manually bundled
- Everything is named `FreeNAS` internally but the product is now `TrueNAS`
- SSH keys still required for ZFS pool listing (separate Proxmox code path via `ZFSPoolPlugin.pm`)
- Max LUN limit bug (#150)
## Key Perl Modules
- `PVE::Storage::LunCmd::FreeNAS` — the main plugin. Entry point: `run_lun_command()`
- Functions: `freenas_api_connect`, `freenas_api_check`, `freenas_api_call`, `freenas_list_lu`, `run_create_lu`, `run_delete_lu`, `run_modify_lu`
## Packaging / Deployment Notes
- Package name: `freenas-proxmox`
- Apt repos: Cloudsmith (`ksatechnologies/truenas-proxmox` stable, `ksatechnologies/truenas-proxmox-testing` beta)
- Install: `apt install freenas-proxmox` after adding the repo
- The package uses dpkg `triggers` to re-apply patches when Proxmox VE packages are upgraded
## ADRs / Plans / Runbooks
See `.claude/cos/adrs/` for Architecture Decision Records.
See `.claude/cos/plans/` for implementation plans.
See `.claude/cos/runbooks/` for operational runbooks.

141
CONTRIBUTING.md Normal file
View File

@ -0,0 +1,141 @@
# Contributing to freenas-proxmox
Thank you for your interest in contributing. This document covers how to report bugs, request features, and submit code.
## Table of Contents
- [Code of Conduct](#code-of-conduct)
- [Reporting Bugs](#reporting-bugs)
- [Requesting Features](#requesting-features)
- [Development Setup](#development-setup)
- [Submitting Changes](#submitting-changes)
- [Coding Standards](#coding-standards)
- [Branch Strategy](#branch-strategy)
---
## Code of Conduct
Be respectful. This is a community project maintained in spare time. Constructive criticism is welcome; hostility is not.
---
## Reporting Bugs
Use the [bug report issue template](https://github.com/TheGrandWazoo/freenas-proxmox/issues/new?template=bug_report.md).
Before filing:
- Check existing [open and closed issues](https://github.com/TheGrandWazoo/freenas-proxmox/issues?q=is%3Aissue) for duplicates
- Reproduce the issue on the latest release if possible
**Always include:**
- Proxmox VE version (`proxmox-ve` package version)
- TrueNAS version and type (CORE / SCALE)
- Plugin version (`dpkg -l freenas-proxmox`)
- Relevant log lines from syslog (`grep -i freenas /var/log/syslog`)
- The storage configuration (redact passwords/tokens)
---
## Requesting Features
Use the [feature request issue template](https://github.com/TheGrandWazoo/freenas-proxmox/issues/new?template=feature_request.md).
Feature requests are evaluated against the project roadmap. Large changes should be discussed in an issue before a pull request is opened.
---
## Development Setup
### What You Need
- A Proxmox VE node (physical or VM) — version 8.x recommended
- A TrueNAS instance (CORE or SCALE) accessible from the Proxmox node
- Basic Perl knowledge
- `dpkg-deb` for building packages locally
### Local Build
```bash
git clone https://github.com/TheGrandWazoo/freenas-proxmox.git
cd freenas-proxmox
# Build the package (once packaging/ directory exists in v3.x)
dpkg-deb -Zgzip --build packaging freenas-proxmox_dev_all.deb
# Install locally for testing
dpkg -i freenas-proxmox_dev_all.deb
```
### Testing Changes to FreeNAS.pm
You can copy the Perl module directly to the Proxmox node for quick iteration without rebuilding the package:
```bash
scp perl5/PVE/Storage/LunCmd/FreeNAS.pm \
root@your-proxmox-node:/usr/share/perl5/PVE/Storage/LunCmd/FreeNAS.pm
# Restart PVE services on the node
ssh root@your-proxmox-node "pvedaemon restart && pveproxy restart"
```
### Checking Perl Syntax
```bash
perl -c perl5/PVE/Storage/LunCmd/FreeNAS.pm
perl -c perl5/PVE/Storage/Custom/TrueNASPlugin.pm
```
---
## Submitting Changes
1. Fork the repository
2. Create a branch from `master`: `git checkout -b feature/your-description`
3. Make your changes — see [Coding Standards](#coding-standards)
4. Test on a real Proxmox + TrueNAS setup if possible
5. Open a pull request against `master`
Pull requests should:
- Have a clear description of what changed and why
- Reference any related issues (`Fixes #123`)
- Not include unrelated changes
---
## Coding Standards
### Perl
- `use strict` and `use warnings` in all modules
- Use `syslog("info", ...)` for normal operation logging, `syslog("err", ...)` for errors
- Include the caller context in log lines: `(caller(0))[3] . " : message"`
- All external API calls wrapped in error handling with cleanup on failure
- No `eval $variable` patterns — use explicit substitution maps
- Prefer `LWP::UserAgent` over `REST::Client` for new code
### Shell (postinst/postrm)
- `set -e` at the top of all scripts
- Log to a file rather than swallowing output with `&> /dev/null`
- Use shellcheck-clean scripts (`shellcheck packaging/DEBIAN/postinst`)
- Idempotent operations — scripts must be safe to run multiple times
### Patches
- Patches live in `stable-N/` directories where N is the Proxmox VE major version
- Always include both `.orig` and `.patch` for reference
- Test with `patch --dry-run` before committing
- Use `--ignore-whitespace` in patch commands
---
## Branch Strategy
| Branch | Purpose | Builds to |
|--------|---------|-----------|
| `master` | Main development branch | Beta/testing apt channel |
| `feature/*` | Feature branches | Alpha apt channel |
| `stable` / tagged releases | Release-ready code | Stable apt channel |
Tag releases as `vMAJOR.MINOR.PATCH` (e.g., `v3.0.0`).

32
DONORS.md Normal file
View File

@ -0,0 +1,32 @@
# Donors
Thank you to everyone who has supported this project financially. Your generosity funds the test lab — a 4-node Proxmox VE cluster with multiple TrueNAS instances — used for development and validation.
## Recurring Supporters
- Alexander Finkhäuser
- Bjarte Kvamme
- Jonathan Schober
## One-Time Donors
- Carlos Galvez — Security Camera
- Sebastian Fischer
- Eugene van der Merwe
- Martin Gonzalez
- Jakub Jochec
- Frederic Silvi
- Vincent Cui
- Mark Komarinski
- Jesse Bryan
- Maksym Vasylenko
- Daniel Most
- Velocity Host
- Robert Hancock
- Clevvi Technology
- Mark Elkins
- Marc Hodler
---
If you would like to support the project, see the [Support the Project](README.md#support-the-project) section of the README.

251
LICENSE
View File

@ -1,21 +1,238 @@
MIT License
GNU AFFERO GENERAL PUBLIC LICENSE
Version 3, 19 November 2007
Copyright (c) 2020 Kevin Scott Adams
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
Preamble
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
The GNU Affero General Public License is a free, copyleft license for software and other kinds of works, specifically designed to ensure cooperation with the community in the case of network server software.
The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, our General Public Licenses are intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users.
When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things.
Developers that use our General Public Licenses protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License which gives you legal permission to copy, distribute and/or modify the software.
A secondary benefit of defending all users' freedom is that improvements made in alternate versions of the program, if they receive widespread use, become available for other developers to incorporate. Many developers of free software are heartened and encouraged by the resulting cooperation. However, in the case of software used on network servers, this result may fail to come about. The GNU General Public License permits making a modified version and letting the public access it on a server without ever releasing its source code to the public.
The GNU Affero General Public License is designed specifically to ensure that, in such cases, the modified source code becomes available to the community. It requires the operator of a network server to provide the source code of the modified version running there to the users of that server. Therefore, public use of a modified version, on a publicly accessible server, gives the public access to the source code of the modified version.
An older license, called the Affero General Public License and published by Affero, was designed to accomplish similar goals. This is a different license, not a version of the Affero GPL, but Affero has released a new version of the Affero GPL which permits relicensing under this license.
The precise terms and conditions for copying, distribution and modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU Affero General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based on the Program.
To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work.
A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source.
The Corresponding Source for a work in source code form is that same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures.
When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified it, and giving a relevant date.
b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices".
c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so.
A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways:
a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b.
d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d.
A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product.
"Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made.
If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM).
The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or authors of the material; or
e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors.
All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11).
However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice.
Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party.
If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it.
A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program.
13. Remote Network Interaction; Use with the GNU General Public License.
Notwithstanding any other provision of this License, if you modify the Program, your modified version must prominently offer all users interacting with it remotely through a computer network (if your version supports such interaction) an opportunity to receive the Corresponding Source of your version by providing access to the Corresponding Source from a network server at no charge, through some standard or customary means of facilitating copying of software. This Corresponding Source shall include the Corresponding Source for any work covered by version 3 of the GNU General Public License that is incorporated pursuant to the following paragraph.
Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the work with which it is combined will remain governed by version 3 of the GNU General Public License.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of the GNU Affero General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU Affero General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU Affero General Public License, you may choose any version ever published by the Free Software Foundation.
If the Program specifies that a proxy can decide which future versions of the GNU Affero General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program.
Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License along with this program. If not, see <http://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If your software can interact with users remotely through a computer network, you should also make sure that it provides a way for users to get its source. For example, if your program is a web application, its interface could display a "Source" link that leads users to an archive of the code. There are many ways you could offer source, and different solutions will be better for different programs; see section 13 for the specific requirements.
You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU AGPL, see <http://www.gnu.org/licenses/>.
---
Copyright (c) 2020 KSA Technologies, LLC

314
README.md
View File

@ -1,236 +1,216 @@
# TrueNAS ZFS over iSCSI Plugin for Proxmox VE
# TrueNAS ZFS-over-iSCSI Plugin for Proxmox VE
## 📢: ATTENTION 2024-01-07 📢: Bearer Token Authentication now available in Version 2.3.0 on the testing repo.
[![License: AGPL v3](https://img.shields.io/badge/License-AGPL_v3-blue.svg)](https://www.gnu.org/licenses/agpl-3.0)
[![GitHub release (latest SemVer)](https://img.shields.io/github/v/release/TheGrandWazoo/freenas-proxmox?sort=semver)](https://github.com/TheGrandWazoo/freenas-proxmox/releases/latest)
[![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/TheGrandWazoo/freenas-proxmox/build.yml?label=build)](https://github.com/TheGrandWazoo/freenas-proxmox/actions/workflows/build.yml)
[![GitHub issues](https://img.shields.io/github/issues/TheGrandWazoo/freenas-proxmox)](https://github.com/TheGrandWazoo/freenas-proxmox/issues)
[![GitHub Sponsors](https://img.shields.io/github/sponsors/TheGrandWazoo?label=Sponsors)](https://github.com/sponsors/TheGrandWazoo)
## Activity
A Proxmox VE storage plugin that manages ZFS-over-iSCSI volumes on TrueNAS (CORE and SCALE) through the TrueNAS REST API — no SSH-based LUN management, no `iscsiadm` scripting.
<details>
<summary>Expand to see the activity tree</summary>
---
<blockquote>
## Table of Contents
<details>
<summary>2024-01-07</summary>
- [How It Works](#how-it-works)
- [Compatibility](#compatibility)
- [Prerequisites](#prerequisites)
- [Installation](#installation)
- [Configuration](#configuration)
- [Upgrading](#upgrading)
- [Uninstalling](#uninstalling)
- [Troubleshooting](#troubleshooting)
- [Contributing](#contributing)
- [Support the Project](#support-the-project)
- [License](#license)
- Added Bearer Token Authentication.
- Changed variable `freenas_password` to `truenas_secret` to represent either a password or token.
- Identation and whitespace cleanup.
---
</details>
## How It Works
<details>
<summary>2023-08-18</summary>
Proxmox VE's built-in ZFS-over-iSCSI storage type uses SSH to manage LUNs on the storage server. This plugin replaces that SSH-based management layer with direct calls to the **TrueNAS REST API**, giving you:
- Update and cleanup the README.md
- API token (Bearer) or username/password authentication
- Automatic TrueNAS API version detection (v1 and v2)
- Support for both TrueNAS CORE and TrueNAS SCALE
- Proper rollback when operations fail (no dangling iSCSI extents)
</details>
> **Note:** Proxmox still uses `iscsiadm` to connect and disconnect the iSCSI session on the Proxmox host itself — that part is handled by the core Proxmox code and does not require SSH. The SSH keys documented in the [Proxmox ZFS-over-iSCSI wiki](https://pve.proxmox.com/wiki/Storage:_ZFS_over_iSCSI) are still required for the ZFS pool listing step.
<details><summary>2023-08-16</summary>
---
- Fixed repos. https://github.com/TheGrandWazoo/freenas-proxmox/issues/151, https://github.com/TheGrandWazoo/freenas-proxmox/issues/152, https://github.com/TheGrandWazoo/freenas-proxmox/issues/153 See [New Installs](#new-installs).
- Fixed PayPal issues. https://github.com/TheGrandWazoo/freenas-proxmox/issues/154
- Updated README.md
## Compatibility
</details>
| Plugin Version | Proxmox VE | TrueNAS CORE | TrueNAS SCALE |
|:--------------:|:----------:|:------------:|:-------------:|
| **3.x** (upcoming) | 8.x | 13.0-U6+ | 23.10 (Cobia)+, 24.04 (Dragonfish)+ |
| **2.x** (current stable) | 7.x, 8.x | 11.3+ | 22.02+ |
<details><summary>2023-08-12</summary>
Check the [Releases page](https://github.com/TheGrandWazoo/freenas-proxmox/releases) for the specific Proxmox and TrueNAS versions tested against each release.
- Fixed postinst issue with Windows-based EOL. https://github.com/TheGrandWazoo/freenas-proxmox/issues/149
---
</details>
## Prerequisites
<details><summary>2023-02-12</summary>
Before installing, ensure the following are in place on your **Proxmox VE node**:
- Added `systemctl restart pvescheduler.service` command to the package based on https://github.com/TheGrandWazoo/freenas-proxmox/issues/109#issuecomment-1367527917
1. **SSH keys** configured between Proxmox and TrueNAS — required for ZFS pool listing by the Proxmox core (see the [Proxmox wiki](https://pve.proxmox.com/wiki/Storage:_ZFS_over_iSCSI), section starting with `mkdir /etc/pve/priv/zfs`).
</details>
2. On **TrueNAS**, an iSCSI target and initiator group must exist and be configured. The plugin manages extents and target-to-extent mappings, but the target itself must be pre-created.
</blockquote>
</details>
3. On **TrueNAS SCALE** or **TrueNAS CORE 13+**, generate an API key:
- TrueNAS SCALE: *System Settings → API Keys → Add*
- TrueNAS CORE: *System → API Keys → Add*
## Donations [![Donate](https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=TCLNEMBUYQUXN&source=url)
Copy the key — you will need it during storage configuration in Proxmox.
<details>Donators<summary>Thank you for all that have donated to the project - Updated 2023-08-18</summary>
---
Alexander Finkhäuser - Recurring
Bjarte Kvamme - Recurring
Jonathan Schober - Recurring
Carlos Galvez from Security Camera
Sebastian Fischer
Eugene van der Merwe
Martin Gonzalez
## Installation
Jakub Jochec
Frederic Silvi
Vincent Cui
Mark Komarinski
Jesse Bryan
Maksym Vasylenko
Daniel Most
Velocity Host
Robert Hancock
Clevvi Technology
Mark Elkins
Marc Hodler
Martin Gonzalez
### Stable Release
</details>
Add the repository and install:
Their donations have allowed for:
- A 4 Node Proxmox VE Cluster for testing and development.
- Spin up old and new revisions of FreeNAS and TrueNAS.
- 10Gb Ethernet Testing.
- Multihomed configuration testing.
- In progress and as best I can in a flat network.
```bash
# Import the GPG key
curl -fsSL https://dl.cloudsmith.io/public/ksatechnologies/truenas-proxmox/gpg.284C106104A8CE6D.key \
| gpg --dearmor \
| tee /usr/share/keyrings/ksatechnologies-truenas-proxmox-keyring.gpg > /dev/null
## Roadmap
<details><summary>Roadmap details</summary>
# Add the repository
cat > /etc/apt/sources.list.d/ksatechnologies-repo.list << 'EOF'
deb [signed-by=/usr/share/keyrings/ksatechnologies-truenas-proxmox-keyring.gpg] \
https://dl.cloudsmith.io/public/ksatechnologies/truenas-proxmox/deb/debian any-version main
EOF
* Update the documentation - <i>In Progress</i>.
* Restructure the main README.md for better readability.
* Add some screenshots.
* Fix Max Lun Limit issue.
* https://github.com/TheGrandWazoo/freenas-proxmox/issues/150
* Fix automated builds - <i>In Progress</i>.
* Production - 'main' repo component.
* Autoinstall the SSH keys.
* Tech spike to see if it is even doable.
* Hashicorp Vault integration.
* Pull in secrets from a Hashicorp Vault service.
* Tech spike to see if it is even doable.
* Package the patches with the deb package.
* Remove the need for git dependency.
* Change to LWP::UserAgent
* Remove dependency of the REST::Client because LWP::UserAgent is already installed and used by Proxmox VE.
* Change from FreeNAS to TrueNAS - <i>In Progress</i>.
* Cleanup the FreeNAS repo and name everything to TrueNAS to be inline with the product.
* Add API key for direct TrueNAS services - <i>In Progress</i>.
* Will be a new enable field and API key and will only be used by the plugin.
* You will still need the SSH keys, username, and password because of Proxmox VE using `iscsiadm` to get the list of disks.
* This is tricky because the format needs to be that of the output of 'zfs list' which is not part of the LunCmd but that of the backend Proxmox VE system and the API's do a bunch of JSON stuff.
# Install
apt update && apt install freenas-proxmox
```
</details>
### Testing / Beta Release
## New Install Instructions
For early access to new features (may be unstable):
### Select at least one `Step 1.x` based on your preference. Can be combined.
```bash
# Import the GPG key
curl -fsSL https://dl.cloudsmith.io/public/ksatechnologies/truenas-proxmox-testing/gpg.CACC9EE03F2DFFCC.key \
| gpg --dearmor \
| tee /usr/share/keyrings/ksatechnologies-truenas-proxmox-testing-keyring.gpg > /dev/null
<details><summary>Step 1.0: For stable releases. <b>Enabled</b> by default.</summary>
# Add the repository
cat > /etc/apt/sources.list.d/ksatechnologies-testing-repo.list << 'EOF'
deb [signed-by=/usr/share/keyrings/ksatechnologies-truenas-proxmox-testing-keyring.gpg] \
https://dl.cloudsmith.io/public/ksatechnologies/truenas-proxmox-testing/deb/debian any-version main
EOF
### truenas-proxmox repo - Currently follows the 2.0 branch.
# Install
apt update && apt install freenas-proxmox
```
Select one of the following GPG Key locations based on your preference.
---
```bash
# Preferred - based on documentation. Copy and paste to bash command line:
keyring_location=/usr/share/keyrings/ksatechnologies-truenas-proxmox-keyring.gpg
```
## Configuration
```bash
# Alternative - If you wish to continue with the old ways. Copy and paste to bash command line:
keyring_location=/etc/apt/trusted.gpg.d/ksatechnologies-truenas-proxmox.gpg
```
After installation, **refresh your browser** to load the updated Proxmox UI. Then add a new ZFS-over-iSCSI storage:
Copy and paste to bash command line to load the GPG key to the location selected above:
```bash
curl -1sLf 'https://dl.cloudsmith.io/public/ksatechnologies/truenas-proxmox/gpg.284C106104A8CE6D.key' | gpg --dearmor >> ${keyring_location}
```
1. Navigate to **Datacenter → Storage → Add → ZFS over iSCSI**
2. Set **iSCSI Provider** to **FreeNAS/TrueNAS API**
3. Fill in the storage fields — see below for authentication options
Copy and paste the following code to bash command line to create '/etc/apt/sources.list.d/ksatechnologies-repo.list'
```bash
cat << EOF > /etc/apt/sources.list.d/ksatechnologies-repo.list
# Source: KSATechnologies
# Site: https://cloudsmith.io
# Repository: KSATechnologies / truenas-proxmox
# Description: TrueNAS plugin for Proxmox VE - Production
deb [signed-by=${keyring_location}] https://dl.cloudsmith.io/public/ksatechnologies/truenas-proxmox/deb/debian any-version main
### Authentication: API Token (Recommended)
EOF
```
| Field | Value |
|-------|-------|
| Portal | IP or hostname of your TrueNAS server |
| Target | The iSCSI target IQN |
| Pool | The ZFS pool name |
| Use SSL | Enabled (recommended) |
| API Host | Leave blank to use Portal IP, or specify a separate management IP |
| Use Token Auth | **Enabled** |
| API Token | Paste the TrueNAS API key you generated |
</details>
### Authentication: Username / Password (Legacy)
<details><summary>Step 1.1: For development releases. <i>Disabled</i> by default.</summary>
| Field | Value |
|-------|-------|
| Use Token Auth | Disabled |
| Username | TrueNAS API user (usually `root`) |
| Password | TrueNAS user password |
### truenas-proxmox-testing repo - Follows the master branch and you wish to test before a stable release (beta).
> **Security note:** Username/password authentication sends credentials on every API call. API token authentication is preferred and may be required in future TrueNAS releases.
Select one of the following GPG Key locations based on your preference.
---
```bash
# Preferred - based on documentation. Copy and paste to bash command line:
keyring_location=/usr/share/keyrings/ksatechnologies-truenas-proxmox-testing-keyring.gpg
```
## Upgrading
```bash
# Alternative - If you wish to continue with the old ways. Copy and paste to bash command line:
keyring_location=/etc/apt/trusted.gpg.d/ksatechnologies-truenas-proxmox-testing.gpg
```
The package integrates with Proxmox VE's standard upgrade mechanism. On `apt upgrade`, the package will automatically re-apply any patches needed after a Proxmox VE update:
Copy and paste to bash command line to load the GPG key to the location selected above:
```bash
curl -1sLf 'https://dl.cloudsmith.io/public/ksatechnologies/truenas-proxmox-testing/gpg.CACC9EE03F2DFFCC.key' | gpg --dearmor >> ${keyring_location}
```
```bash
apt update && apt full-upgrade
```
Copy and paste the following code to bash command line to create '/etc/apt/sources.list.d/ksatechnologies-testing-repo.list'
```bash
cat << EOF > /etc/apt/sources.list.d/ksatechnologies-testing-repo.list
# Source: KSATechnologies
# Site: https://cloudsmith.io
# Repository: KSATechnologies / truenas-proxmox-testing
# Description: TrueNAS plugin for Proxmox VE - Testing
deb [signed-by=${keyring_location}] https://dl.cloudsmith.io/public/ksatechnologies/truenas-proxmox-testing/deb/debian any-version main
---
EOF
```
## Uninstalling
</details>
```bash
apt remove freenas-proxmox
```
<details><summary>Step 2.0: Next step after completing any combination of the 1.x steps</summary>
This removes the plugin and reverses all patches, returning your Proxmox VE installation to its unmodified state. Any storage configurations using this plugin should be removed from Proxmox before uninstalling.
### Update apt
---
Then issue the following to install the package
```bash
apt update
apt install freenas-proxmox
```
## Troubleshooting
</details>
### After install, the "FreeNAS/TrueNAS API" option is not visible
<details><summary>Step 3.0: Maintenance.</summary>
Refresh your browser (force-refresh with Ctrl+Shift+R or Cmd+Shift+R). The Proxmox UI JavaScript is cached aggressively.
Then just do your regular upgrade via apt at the command line or the Proxmox Update subsystem; the package will automatically issue all commands to patch the files.
```bash
apt update
apt [full|dist]-upgrade
```
### Storage shows as unavailable / API connection fails
</details>
Check `journalctl -f` or `/var/log/syslog` on the Proxmox node — the plugin logs all API calls and errors with `[FreeNAS::API::]` prefixes.
</details>
Common causes:
- Wrong API host or portal IP
- SSL mismatch (try toggling SSL on/off)
- API token expired or revoked
- TrueNAS iSCSI service not running
## Uninstall truenas-proxmox
### Dangling extents on TrueNAS after a failed operation
<details><summary>If you wish not to use the package you may remove it at anytime with the following:</summary>
If you see iSCSI extents in TrueNAS that are not associated with any target, they can be safely deleted from the TrueNAS UI. The v3.x plugin release adds automatic rollback to prevent this.
```
apt [remove|purge] freenas-proxmox
```
### Filing a Bug Report
This will place you back to a normal and non-patched Proxmox VE install.
Please use the [GitHub issue tracker](https://github.com/TheGrandWazoo/freenas-proxmox/issues) and include the [bug report template](.github/ISSUE_TEMPLATE/bug_report.md). Include relevant log lines from `syslog` (search for `FreeNAS::`).
</details>
---
## Notes:
## Contributing
### Please be aware that this plugin uses the TrueNAS APIs but still uses SSH keys due to the underlying Proxmox VE perl modules that use the ```iscsiadm``` command.
Contributions are welcome. Please read [CONTRIBUTING.md](CONTRIBUTING.md) before opening a pull request.
You will still need to configure the SSH connector for listing the ZFS Pools because this is currently being done in a Proxmox module (ZFSPoolPlugin.pm). To configure this please follow the steps at https://pve.proxmox.com/wiki/Storage:_ZFS_over_iSCSI that have to do with SSH between Proxmox VE and TrueNAS. The code segment should start out `mkdir /etc/pve/priv/zfs`.
For significant changes, open an issue first to discuss the approach.
1. Remember to follow the instructions mentioned above for the SSH keys.
---
2. Refresh the Proxmox GUI in your browser to load the new Javascript code.
## Support the Project
3. Add your new TrueNAS ZFS-over-iSCSI storage using the TrueNAS-API.
If this plugin saves you time, consider supporting its development:
4. Thanks for your support.
- **GitHub Sponsors**: [github.com/sponsors/TheGrandWazoo](https://github.com/sponsors/TheGrandWazoo)
- **PayPal**: [Donate via PayPal](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=TCLNEMBUYQUXN&source=url)
Donor support has funded a 4-node Proxmox cluster and TrueNAS test lab used for development and validation. See [DONORS.md](DONORS.md) for a full list of donors.
---
## License
Copyright (c) 2020 KSA Technologies, LLC
This program is free software: you can redistribute it and/or modify it under the terms of the [GNU Affero General Public License](LICENSE) as published by the Free Software Foundation, version 3.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

31
SECURITY.md Normal file
View File

@ -0,0 +1,31 @@
# Security Policy
## Supported Versions
| Version | Supported |
|---------|-----------|
| 3.x (upcoming) | Yes |
| 2.3.x | Yes |
| 2.2.x and earlier | No — please upgrade |
## Reporting a Vulnerability
**Do not open a public GitHub issue for security vulnerabilities.**
Email security reports to: **security@ksatechnologies.com** (or **theprofessor@ksatechnologies.com**)
Include:
- A description of the vulnerability
- Steps to reproduce
- The potential impact
- Any suggested fixes if you have them
You will receive an acknowledgment within 72 hours. We aim to release a fix within 14 days for confirmed vulnerabilities and will credit reporters in the release notes unless anonymity is requested.
## Security Considerations for Operators
- **API tokens are stored in `/etc/pve/storage.cfg`** which is readable only by root and replicated across the PVE cluster via `pmxcfs`. Treat cluster access accordingly.
- **Use API token authentication** rather than username/password. Tokens can be revoked individually without changing your TrueNAS user password.
- **Enable SSL** on the TrueNAS API connection. The plugin accepts self-signed certificates (SSL verification is relaxed) — use a private CA or valid certificate where possible.
- **Scope API tokens** to the minimum required permissions on TrueNAS if your version supports scoped tokens.
- **Restrict network access** to the TrueNAS management interface to only the Proxmox nodes that need it.

View File

@ -0,0 +1,22 @@
Source: freenas-proxmox
Section: perl
Priority: optional
Maintainer: KSA Technologies, LLC <theprofessor@ksatechnologies.com>
Uploaders: Kevin Scott Adams <thegrandwazoo@ksatechnologies.com>
Depends: librest-client-perl
Standards-Version: 4.6.2
Vcs-Git: https://github.com/TheGrandWazoo/freenas-proxmox.git
Vcs-Browser: https://github.com/TheGrandWazoo/freenas-proxmox
Homepage: https://github.com/TheGrandWazoo/freenas-proxmox
Package: freenas-proxmox
Architecture: all
Version: ${VERSION}
Provides: freenas-proxmox
Description: TrueNAS ZFS-over-iSCSI Plugin for Proxmox VE
Manages iSCSI LUNs on TrueNAS (CORE and SCALE) via the TrueNAS REST API.
Supports Bearer Token and username/password authentication.
No SSH-based LUN management required.
.
Licensed under the GNU Affero General Public License v3 (AGPL-3.0).
Copyright (c) 2020 KSA Technologies, LLC.

156
packaging/DEBIAN/postinst Normal file
View File

@ -0,0 +1,156 @@
#!/bin/bash
# postinst: freenas-proxmox install/upgrade/trigger script
# Applies patches to Proxmox VE system files and installs the TrueNAS API plugin.
# No internet access required — all files are bundled in the package.
set -e
INSTALL_DIR="/usr/share/freenas-proxmox"
LIB_PATH="/usr/share"
LOG_FILE="/var/log/freenas-proxmox-install.log"
ZFSPLUGIN_PATH="/perl5/PVE/Storage/ZFSPlugin.pm"
PVEMANAGER_PATH="/pve-manager/js/pvemanagerlib.js"
APIDOC_PATH="/pve-docs/api-viewer/apidoc.js"
FREENAS_PM_PATH="/perl5/PVE/Storage/LunCmd/FreeNAS.pm"
REST_CLIENT_PATH="/perl5/REST/Client.pm"
log() {
echo "[freenas-proxmox] $*" | tee -a "$LOG_FILE"
}
# Detect installed Proxmox VE major version
detect_pve_major() {
local ver
ver=$(dpkg-query --showformat='${Version}' --show proxmox-ve 2>/dev/null || echo "0")
echo "${ver%%.*}"
}
# Find the best bundled patch for a given component and PVE major version.
# Walks down from the detected major version until a patch file is found.
find_patch() {
local component="$1"
local major="$2"
local patch_dir="${INSTALL_DIR}/patches/${component}"
local ver="$major"
while [ "$ver" -ge 5 ]; do
if [ -f "${patch_dir}/${ver}.patch" ]; then
echo "${patch_dir}/${ver}.patch"
return 0
fi
ver=$(( ver - 1 ))
done
log "WARNING: No bundled patch found for ${component} on Proxmox VE ${major}.x"
return 1
}
# Apply a patch to a target file idempotently.
# Creates a .orig backup (via patch --backup) on first application.
apply_patch() {
local target_rel="$1"
local patch_file="$2"
local label="$3"
local target="${LIB_PATH}${target_rel}"
[ -f "$target" ] || { log "ERROR: Target not found: ${target}"; return 1; }
[ -f "$patch_file" ] || { log "Skipping ${label}: patch file missing"; return 0; }
if grep -q "freenas" "$target" 2>/dev/null; then
log "${label} is already patched — skipping"
return 0
fi
log "Patching ${target} ..."
if patch --backup --ignore-whitespace "$target" < "$patch_file" >> "$LOG_FILE" 2>&1; then
log "${label} patched successfully"
else
log "ERROR: Failed to patch ${label} — see ${LOG_FILE} for details"
return 1
fi
}
install_files() {
log "Installing ${LIB_PATH}${FREENAS_PM_PATH}"
mkdir -p "$(dirname "${LIB_PATH}${FREENAS_PM_PATH}")"
cp "${INSTALL_DIR}/FreeNAS.pm" "${LIB_PATH}${FREENAS_PM_PATH}"
log "Installing ${LIB_PATH}${REST_CLIENT_PATH}"
mkdir -p "$(dirname "${LIB_PATH}${REST_CLIENT_PATH}")"
cp "${INSTALL_DIR}/REST-Client.pm" "${LIB_PATH}${REST_CLIENT_PATH}"
}
restart_pve_services() {
log "Restarting Proxmox VE services ..."
pvedaemon restart && log "pvedaemon restarted"
pveproxy restart && log "pveproxy restarted"
pvestatd restart && log "pvestatd restarted"
systemctl restart pvescheduler.service && log "pvescheduler restarted"
log "Done. Refresh your Proxmox browser tab to load the updated UI."
}
# ── Entry point ──────────────────────────────────────────────────────────────
major=$(detect_pve_major)
log "Proxmox VE major version detected: ${major}"
case "$1" in
triggered)
# dpkg fired us because one of the watched PVE files was updated.
log "Triggered by package update — re-applying patches for: $2"
for fullpath in $2; do
filename=$(basename "$fullpath")
filename="${filename%.*}" # strip extension
case "$filename" in
ZFSPlugin)
patch_file=$(find_patch "ZFSPlugin" "$major" || true)
apply_patch "$ZFSPLUGIN_PATH" "$patch_file" "ZFSPlugin.pm"
;;
pvemanagerlib)
patch_file=$(find_patch "pvemanagerlib" "$major" || true)
apply_patch "$PVEMANAGER_PATH" "$patch_file" "pvemanagerlib.js"
;;
apidoc)
patch_file=$(find_patch "apidoc" "$major" || true)
apply_patch "$APIDOC_PATH" "$patch_file" "apidoc.js"
;;
esac
done
install_files
exit 0
;;
configure)
log "Configuring freenas-proxmox (previous version: ${2:-none})"
CHANGED="no"
patch_file=$(find_patch "ZFSPlugin" "$major" || true)
apply_patch "$ZFSPLUGIN_PATH" "$patch_file" "ZFSPlugin.pm" && CHANGED="yes"
patch_file=$(find_patch "pvemanagerlib" "$major" || true)
apply_patch "$PVEMANAGER_PATH" "$patch_file" "pvemanagerlib.js" && CHANGED="yes"
patch_file=$(find_patch "apidoc" "$major" || true)
apply_patch "$APIDOC_PATH" "$patch_file" "apidoc.js" && CHANGED="yes"
install_files
CHANGED="yes"
[ "$CHANGED" = "yes" ] && restart_pve_services
exit 0
;;
abort-upgrade|abort-remove|abort-deconfigure)
;;
*)
echo "$0: called with unknown argument '$1'" >&2
exit 0
;;
esac
exit 0

105
packaging/DEBIAN/postrm Normal file
View File

@ -0,0 +1,105 @@
#!/bin/bash
# postrm: freenas-proxmox removal script
# Reverses patches applied to Proxmox VE system files and removes installed plugin files.
set -e
LIB_PATH="/usr/share"
LOG_FILE="/var/log/freenas-proxmox-install.log"
ZFSPLUGIN_PATH="/perl5/PVE/Storage/ZFSPlugin.pm"
PVEMANAGER_PATH="/pve-manager/js/pvemanagerlib.js"
APIDOC_PATH="/pve-docs/api-viewer/apidoc.js"
FREENAS_PM_PATH="/perl5/PVE/Storage/LunCmd/FreeNAS.pm"
REST_CLIENT_PATH="/perl5/REST/Client.pm"
INSTALL_DIR="/usr/share/freenas-proxmox"
log() {
echo "[freenas-proxmox] $*" | tee -a "$LOG_FILE"
}
# Restore a file from the .orig backup that patch --backup created.
restore_orig() {
local target_rel="$1"
local label="$2"
local target="${LIB_PATH}${target_rel}"
local orig="${target}.orig"
if ! grep -q "freenas" "$target" 2>/dev/null; then
log "${label} does not appear to be patched — skipping restore"
return 0
fi
if [ -f "$orig" ]; then
log "Restoring ${label} from .orig backup"
cp "$orig" "$target" && rm -f "$orig"
log "${label} restored"
else
log "WARNING: No .orig backup found for ${label} — Proxmox VE reinstall may be needed"
log " Run: apt install --reinstall pve-manager libpve-storage-perl pve-docs"
fi
}
remove_plugin_files() {
if [ -f "${LIB_PATH}${FREENAS_PM_PATH}" ]; then
log "Removing ${LIB_PATH}${FREENAS_PM_PATH}"
rm -f "${LIB_PATH}${FREENAS_PM_PATH}"
fi
# REST/Client.pm is owned by librest-client-perl; do not remove it here.
}
restart_pve_services() {
log "Restarting Proxmox VE services ..."
pvedaemon restart && log "pvedaemon restarted"
pveproxy restart && log "pveproxy restarted"
pvestatd restart && log "pvestatd restarted"
systemctl restart pvescheduler.service && log "pvescheduler restarted"
}
# ── Entry point ──────────────────────────────────────────────────────────────
case "$1" in
remove)
log "Removing freenas-proxmox — restoring Proxmox VE files ..."
restore_orig "$ZFSPLUGIN_PATH" "ZFSPlugin.pm"
restore_orig "$PVEMANAGER_PATH" "pvemanagerlib.js"
restore_orig "$APIDOC_PATH" "apidoc.js"
remove_plugin_files
restart_pve_services
log "freenas-proxmox removed. Refresh your Proxmox browser tab."
exit 0
;;
upgrade)
# On upgrade the new package's postinst will re-apply everything.
# Just restore the originals cleanly so the new patch applies to a fresh file.
log "Preparing for upgrade — restoring unpatched Proxmox VE files ..."
restore_orig "$ZFSPLUGIN_PATH" "ZFSPlugin.pm"
restore_orig "$PVEMANAGER_PATH" "pvemanagerlib.js"
restore_orig "$APIDOC_PATH" "apidoc.js"
remove_plugin_files
exit 0
;;
purge)
log "Purging freenas-proxmox ..."
rm -rf "$INSTALL_DIR"
rm -f "$LOG_FILE"
exit 0
;;
failed-upgrade|disappear|abort-upgrade|abort-remove|abort-deconfigure)
;;
*)
echo "$0: called with unknown argument '$1'" >&2
exit 0
;;
esac
exit 0

View File

@ -0,0 +1,7 @@
# dpkg trigger file for freenas-proxmox
#
# When Proxmox VE updates replace any of these three files, dpkg automatically
# re-fires the postinst script so patches are re-applied without manual action.
interest /usr/share/perl5/PVE/Storage/ZFSPlugin.pm
interest /usr/share/pve-manager/js/pvemanagerlib.js
interest /usr/share/pve-docs/api-viewer/apidoc.js

View File

@ -237,7 +237,7 @@ sub run_list_extent {
if (defined($luns->{$object})) {
my $lu_object = $luns->{$object};
$result = $lu_object->{$freenas_api_variables->{'extentnaa'}};
syslog("info",(caller(0))[3] . " '$object' wtih key '$freenas_api_variables->{'extentnaa'}' found with value: '$result'");
syslog("info",(caller(0))[3] . " '$object' with key '$freenas_api_variables->{'extentnaa'}' found with value: '$result'");
} else {
syslog("info",(caller(0))[3] . " '$object' with key '$freenas_api_variables->{'extentnaa'}' was not found");
}
@ -344,7 +344,7 @@ sub freenas_api_connect {
}
$freenas_server_list->{$apihost}->setHost($scheme . '://' . $apihost);
$freenas_server_list->{$apihost}->addHeader('Content-Type', 'application/json');
if (defined($scfg->{'truenas_token_auth'})) {
if (defined($scfg->{'truenas_token_auth'}) && $scfg->{'truenas_token_auth'}) {
syslog("info", (caller(0))[3] . " : Authentication using Bearer Token Auth");
$freenas_server_list->{$apihost}->addHeader('Authorization', 'Bearer ' . $scfg->{truenas_secret});
} else {

View File

@ -42,6 +42,15 @@
} elsif ($scfg->{iscsiprovider} eq 'istgt') {
$msg = PVE::Storage::LunCmd::Istgt::run_lun_command($scfg, $timeout, $method, @params);
} elsif ($scfg->{iscsiprovider} eq 'iet') {
@@ -157,7 +163,7 @@
sub zfs_get_lun_number {
my ($class, $scfg, $guid) = @_;
- die "could not find lun_number for guid $guid" if !$guid;
+ die "could not find lun_number for guid $guid" if !defined $guid;
if ($class->zfs_request($scfg, undef, 'list_view', $guid) =~ /^(\d+)$/) {
return $1;
@@ -166,6 +172,15 @@
die "lun_number for guid $guid is not a number";
}
@ -50,7 +59,7 @@
+sub zfs_get_wwid_number {
+ my ($class, $scfg, $guid) = @_;
+
+ die "could not find lun_number for guid $guid" if !$guid;
+ die "could not find lun_number for guid $guid" if !defined $guid;
+
+ return $class->zfs_request($scfg, undef, 'list_extent', $guid);
+}