mirror of
https://github.com/openziti/desktop-edge-win.git
synced 2026-09-25 12:22:09 +00:00
remove implementation plan
This commit is contained in:
@@ -47,7 +47,6 @@ questionnaire, or filling in an audit response.
|
||||
| [build-fips-provider.md](doc/build-fips-provider.md) | Why the build is the way it is, and the publishing rules |
|
||||
| [verify-fips-provider.md](doc/verify-fips-provider.md) | Bench verification of a freshly built module, before it goes near the installer |
|
||||
| [zdew-integration.md](doc/zdew-integration.md) | How `ziti-edge-tunnel` finds and loads the provider, and what ZDEW has to place on disk |
|
||||
| [implementation-plan.md](doc/implementation-plan.md) | The work items to get this shipped, in order, with the known gaps |
|
||||
| [compliance-position.md](doc/compliance-position.md) | What we may and may not claim, the operational-environment scope, and the evidence we retain |
|
||||
| [test-plan.md](doc/test-plan.md) | How to prove, on a real machine, that validated cryptography is in use |
|
||||
|
||||
|
||||
@@ -1,230 +0,0 @@
|
||||
# Implementation plan
|
||||
|
||||
Ordered so that each phase de-risks the next. Do not start phase 3 before phase 2 passes -- the installer work is
|
||||
the expensive part and it is wasted if the module will not load inside the tunneler.
|
||||
|
||||
## Phase 1 -- reproducible module build
|
||||
|
||||
- [ ] Re-read the current security policy for CMVP certificate #4985 and reconcile it with
|
||||
[build-fips-provider.md](build-fips-provider.md). The procedure previously used was written against the
|
||||
2023-12-29 draft; the certificate was validated 11 March 2025 and updated 21 November 2025.
|
||||
- [ ] C runtime: **decided** -- keep the documented `/MD` build and solve loading in the installer (VC++
|
||||
redistributable prerequisite, or ship `vcruntime140.dll`). `/MT` modifies the security policy's build
|
||||
method and forfeits the MM §7.9.2 footnote 5 cover. Choose which of the two installer options to take.
|
||||
- [ ] Run `fips\scripts\Build-FipsProvider.ps1` end to end on a clean **Windows 10 x64** VM with **Visual Studio 2019**,
|
||||
matching §5.3 of the security policy. Keep the VM image reference. CI output is a test artifact.
|
||||
- [ ] Archive the evidence package produced by the script somewhere that outlives the VM.
|
||||
- [ ] Do **not** sign as part of the module build. ZDEW's installer build already signs every binary it
|
||||
packages, and `fips.dll` and `openssl.exe` go through that same pass. Keeping one signing process means
|
||||
a change to how signing works never leaves these two files behind.
|
||||
|
||||
The consequence for publishing: the release and `fips/provider.json` carry the **unsigned** hashes,
|
||||
which is what ties the shipped bytes back to this build. The installer verifies those hashes on
|
||||
download, then signs. `fipsinstall` runs at install time on the signed file, so the integrity HMAC
|
||||
still covers what actually ships.
|
||||
|
||||
The build machine from the previous attempt was an Azure VM that has been stopped for over a year. Treat it as
|
||||
gone. The script exists so the procedure no longer lives on one host.
|
||||
|
||||
## Phase 2 -- bench verification
|
||||
|
||||
- [x] **Stages 1 to 3 pass** (`fips/scripts/Test-FipsWithTunneler.ps1`, 2026-09-16). An MSVC-built 3.1.2 `fips.dll`
|
||||
loads into the mingw-built OpenSSL 3.6.3 core inside `ziti-edge-tunnel.exe`, and the tunneler reports
|
||||
`[FIPS]`. The negative control confirms the marker is meaningful. This was the single largest unknown in
|
||||
the plan; it is cleared, and none of the upstream fallbacks are needed.
|
||||
- [x] Module self-tests pass on a host matching neither the certificate's tested environment nor the build
|
||||
host: `Module_Integrity`, every algorithm KAT, all three DRBGs, every KDF, signatures and key agreement.
|
||||
- [ ] Stage 4: functional pass against a quickstart network in FIPS-only mode, with any algorithm limitations
|
||||
written down. X25519 and Ed25519 are absent from the 3.1.2 provider, so enrollment and TLS 1.3 group
|
||||
negotiation are the cases to watch.
|
||||
|
||||
## Phase 3 -- build pipeline
|
||||
|
||||
**Decided:** the artifacts are published as assets on a **public GitHub release** in this repository and pinned
|
||||
by SHA-256 in `fips/provider.json`. They are not committed. The earlier attempt committed both binaries
|
||||
(8.7 MB) into `Installer/openssl/`; a blob in git history is not an evidence chain, and a public release plus a
|
||||
hash lets a customer verify our claim without asking us for anything.
|
||||
|
||||
- [x] **First provider release published** (2026-09-16), tag `fips-provider-3.1.2-20260916`, marked not-latest
|
||||
so it cannot displace a ZDEW product release. All four artifacts plus `evidence.zip` attached.
|
||||
- [x] `fips/provider.json` generated and verified: every pinned hash matches a fresh download from the
|
||||
published URLs.
|
||||
- [x] Commit `fips/provider.json`.
|
||||
- [x] `Installer/build.ps1`: call `Installer/Get-FipsProvider.ps1` with `-Manifest "${checkoutRoot}\fips\provider.json"`
|
||||
and `-Destination "${buildPath}\service"`
|
||||
alongside the existing `ziti-edge-tunnel` fetch, so the FIPS files land next to `ziti-edge-tunnel.exe`
|
||||
exactly as they will on the target machine. It fails the build on a hash mismatch.
|
||||
- [x] **Decided:** always fetch. The files are inert unless `openssl.cnf` exists, and they are gated by the
|
||||
`EnableFIPS` feature, so a machine that does not ask for FIPS never receives them.
|
||||
- [ ] Record the FIPS provider version and `fips.dll` hash in `scripts/build-summary.txt` and in the build log
|
||||
next to the existing `ziti-edge-tunnel version -v` capture.
|
||||
|
||||
## Phase 4 -- installer
|
||||
|
||||
Every item here touches `Installer/ZitiDesktopEdge.aip`. **Do not edit the AIP without asking first** -- see the
|
||||
root `CLAUDE.md`. What follows is the specification to agree before any AIP work happens.
|
||||
|
||||
The earlier attempt already built most of this and it was reverted. Recovering it is useful; shipping it as it
|
||||
stood is not, because of the upgrade gap below.
|
||||
|
||||
- [x] Optional feature `EnableFIPS`, Installation Behavior "Not installed" with **Installed if**
|
||||
`ZITI_ENABLE_FIPS="1"`. The quotes matter: Advanced Installer accepts the unquoted `ZITI_ENABLE_FIPS=1`
|
||||
and it then never matches, because MSI evaluates a string property against an integer literal as false.
|
||||
- [x] `fips.dll`, `openssl.exe`, `libcrypto-3-x64.dll` and `libssl-3-x64.dll` as components in `APPDIR` with
|
||||
`DigSign="true"`; `vcruntime140.dll` alongside them without it, since Microsoft already signed it.
|
||||
- [x] A checkbox on `FolderDlg`, defaulted **off**, with text telling the reader to enable it only if they
|
||||
understand the implications. `FolderDlg` rather than `OptionalFeatsDlg` because it is the only page
|
||||
between Welcome and VerifyReady on a fresh install.
|
||||
- [x] Deferred, elevated `SetFipsConfig` custom action after `InstallFiles`: runs `openssl.exe fipsinstall
|
||||
-pedantic`, writes `openssl.cnf` with `APPDIR` substituted and forward slashes, confirms the provider
|
||||
activates, and fails the install on any error. Conditioned `&EnableFIPS=3`, so it also runs on upgrades.
|
||||
- [x] `RemoveFipsConfig` removes `openssl.cnf` and `fipsmodule.cnf`, conditioned `&EnableFIPS=2` so it covers
|
||||
both uninstall and a maintenance run that unticks the feature. Verified: neither file is left behind.
|
||||
|
||||
### The upgrade gap
|
||||
|
||||
The earlier attempt conditioned the generation action on `NOT Installed AND ZITI_ENABLE_FIPS`. ZDEW's
|
||||
auto-updater runs the new MSI **silently**, and in a silent major upgrade nothing sets `ZITI_ENABLE_FIPS`, so
|
||||
a machine that was in FIPS mode would quietly stop being in FIPS mode at its next automatic update.
|
||||
|
||||
What actually happens is the opposite, and it was observed rather than reasoned about. Every build gets a new
|
||||
`ProductCode`, so every install over an existing one is a major upgrade, which means `MigrateFeatureStates`
|
||||
runs. From an install log:
|
||||
|
||||
```
|
||||
PROPERTY CHANGE: Modifying ZITI_ENABLE_FIPS property. Its current value is '0'. Its new value: '1'.
|
||||
MigrateFeatureStates: based on existing product, setting feature 'EnableFIPS' to 'Absent' state.
|
||||
Feature: EnableFIPS; Installed: Absent; Request: Absent; Action: Absent
|
||||
```
|
||||
|
||||
The previous installation's feature selection wins over the current command line. That is the behaviour we
|
||||
want for the silent case -- a FIPS machine stays a FIPS machine, and `SetFipsConfig` re-runs because it is
|
||||
conditioned on `&EnableFIPS=3` rather than on `NOT Installed`, so a new `fips.dll` gets a matching
|
||||
`fipsmodule.cnf`.
|
||||
|
||||
It also means **FIPS cannot be turned on by upgrading**. Passing `ZITI_ENABLE_FIPS=1` to an upgrade of a
|
||||
non-FIPS installation does nothing. Enabling it on an existing install needs `ADDLOCAL=EnableFIPS` or a clean
|
||||
install. Document that; it will otherwise be reported as a bug.
|
||||
|
||||
Still outstanding:
|
||||
|
||||
- [ ] Persist the choice at install time: `HKLM\SOFTWARE\NetFoundry\Ziti Desktop Edge`, `FipsEnabled`
|
||||
(REG_DWORD), so the setting is legible outside MSI's feature state -- for support, for inventory, and
|
||||
for the tray UI.
|
||||
### Managed policy
|
||||
|
||||
Regulated fleets configure by policy, not by clicking a checkbox on 4,000 machines, so "require FIPS" has to
|
||||
be expressible through Group Policy and MDM. ZDEW already has that machinery --
|
||||
`ZitiUpdateService/windows/gpo/NetFoundry.ZitiMonitorService.admx`, documented in
|
||||
`ZitiUpdateService/POLICY-ADMIN-GUIDE.md` -- but FIPS does not fit its existing shape, and that needs
|
||||
deciding before any of it is built.
|
||||
|
||||
Every policy today (`UpdateTimer`, `InstallationCritical`, the maintenance window settings) is read **at
|
||||
runtime** by `ziti-monitor`, which can act on a changed value immediately. FIPS is not a runtime setting. It
|
||||
is MSI feature state fixed at install time, and a policy cannot switch on a feature whose files are not on
|
||||
disk. So there are two halves, and only one of them enforces anything:
|
||||
|
||||
- [ ] **Installer side.** `AppSearch` the policy value into `ZITI_ENABLE_FIPS` so a policied machine installs
|
||||
the feature without anyone passing a command line. On its own this only works for fresh installs:
|
||||
`MigrateFeatureStates` makes an existing installation's feature selection win on every upgrade, so
|
||||
turning FIPS *on* for a machine that lacks it also needs `ADDLOCAL=EnableFIPS`. Decide whether the
|
||||
installer forces that when policy requires FIPS, or whether policy-driven enablement is a documented
|
||||
reinstall.
|
||||
- [ ] **Runtime side.** `ziti-monitor` reads the policy and refuses to start the tunneler when policy requires
|
||||
FIPS and the machine is not in FIPS mode, logging why. This is the half that actually enforces, and it
|
||||
is also the answer to "what happens when `fipsinstall` fails on a machine whose administrator declared
|
||||
it must not run non-approved cryptography".
|
||||
- [ ] Decide what the policy is named and where it lives in the ADMX tree, alongside the existing update
|
||||
settings rather than in a category of its own.
|
||||
- [ ] `POLICY-ADMIN-GUIDE.md` carries compliance presets (CJIS, DISA STIG, PCI, NIST, NERC CIP, HITRUST).
|
||||
Whoever reads those is the exact audience for FIPS, so the install flag belongs next to the update
|
||||
cadence settings in the same presets.
|
||||
|
||||
### Automatic update URL
|
||||
|
||||
`AutomaticUpdateURL_Text` lets a fleet override the update stream URL entirely. Anything that depends on
|
||||
changing what a stream file advertises -- notably the `-win32crypto` migration in
|
||||
[../../doc/win32crypto-deprecation.md](../../doc/win32crypto-deprecation.md) -- does not reach a fleet pointing
|
||||
that policy at its own mirror. Those customers have to be told directly.
|
||||
|
||||
## Phase 5 -- surfacing real state
|
||||
|
||||
### Where it goes
|
||||
|
||||
The About screen already has the line. `DesktopEdge/Views/Screens/MainMenu.xaml.cs` renders:
|
||||
|
||||
```
|
||||
App: 2.11.7.0 Service: v1.19.0 openssl
|
||||
```
|
||||
|
||||
built as `$"App: {appVersion} Service: {version} {crypto}"`. That trailing word is the natural home for FIPS
|
||||
state, and it sits beside the service version, which is what someone reads when they want to know what is
|
||||
actually running.
|
||||
|
||||
### Why it cannot simply be extended
|
||||
|
||||
`crypto` is a compile-time constant:
|
||||
|
||||
```csharp
|
||||
#if WIN32CRYPTO
|
||||
string crypto = "win32crypto";
|
||||
#else
|
||||
string crypto = "openssl";
|
||||
#endif
|
||||
```
|
||||
|
||||
FIPS is neither a compile-time nor an install-time fact from the UI's point of view -- it is whether the
|
||||
*running* tunneler loaded the provider. Deriving it from the build, or from `File.Exists(fips.dll)` as the
|
||||
earlier attempt did, is the same mistake in a new place: it reports that a file was copied.
|
||||
|
||||
The data is not available to ask for. `TunnelStatus.ServiceVersion`
|
||||
(`ZitiDesktopEdge.Client/DataStructures/DataStructures.cs:412`) carries `Version`, `Revision` and `BuildDate`
|
||||
and nothing about the TLS backend, so the IPC payload has to grow before the UI can render anything truthful.
|
||||
|
||||
### Shipped as a stopgap in 2.12.0.0
|
||||
|
||||
The About line appends `(FIPS 140-3)` when `openssl.cnf` exists in the install directory. That file is written
|
||||
only after `fipsinstall` passes this machine's power-on self-tests and the provider is confirmed to activate,
|
||||
and it is deleted again on failure or on feature removal -- so it reports that the installer configured FIPS
|
||||
here, which is a real signal and not take-1's "a file was copied".
|
||||
|
||||
What it does not report is whether the tunneler running right now loaded it. Replace it with the work below
|
||||
rather than leaving it indefinitely.
|
||||
|
||||
### Work
|
||||
|
||||
- [ ] Decide where the truth comes from. In order of preference:
|
||||
1. the tunneler reports it in the status payload -- needs an upstream change, and is the only source
|
||||
that is authoritative by construction
|
||||
2. `ziti-monitor` parses the tunneler's own startup line, `- openssl config : configured using <path>
|
||||
found by <how>`, and forwards it -- no upstream change, and still describes the running process
|
||||
3. anything derived from files on disk -- rejected, see above
|
||||
- [ ] Carry it on `ServiceVersion` (or alongside it) so the UI has something to bind to.
|
||||
- [ ] Render it in the About line. Include the provider version, and link CMVP certificate #4985 rather than a
|
||||
Wikipedia article on FIPS 140-2: the module is validated under **140-3**, and 140-2 validations move to
|
||||
the CMVP historical list in September 2026.
|
||||
- [ ] Say something unambiguous when FIPS is **off**, so a customer who believes they enabled it finds out
|
||||
here rather than during an audit.
|
||||
- [ ] Log the state in the monitor service log and include it in support bundles, so the
|
||||
`debug-ziti-desktop-edge-win` workflow can tell whether a reporting machine was in FIPS mode.
|
||||
|
||||
## Phase 6 -- documentation and release
|
||||
|
||||
- [ ] Release-notes entry that states the certificate number, the provider version, the supported operational
|
||||
environment, and the known algorithm limitations.
|
||||
- [ ] A customer-facing page: how to enable it, how to verify it themselves, what is and is not covered. Point it
|
||||
at [compliance-position.md](compliance-position.md) rather than restating the claim in new words.
|
||||
- [ ] Add the FIPS cases to `manual-testing.md`.
|
||||
- [ ] Subscribe someone to OpenSSL's FIPS/CVE announcements and write down what happens when one lands. See
|
||||
[compliance-position.md](compliance-position.md#cves-in-the-validated-module).
|
||||
|
||||
## Open questions for the tunneler and SDK maintainers
|
||||
|
||||
1. Is the `-win32crypto` Windows build being retired? It is still published for v1.19.0, and ZDEW's paired
|
||||
release streams and promotion tooling exist only to serve it.
|
||||
2. Is an MSVC-built FIPS provider loading into the mingw-built static OpenSSL core a configuration upstream is
|
||||
willing to support, or should ZDEW expect a change in how OpenSSL is linked?
|
||||
3. How is the E2EE mode selected and can it be pinned to `tls` (or `aes-gcm`) from the client side? The FIPS
|
||||
claim covers the transport regardless; it covers the E2EE payload only in those modes.
|
||||
4. Does anything in ziti-edge-tunnel or ziti-sdk-c call OpenSSL outside the library context created by
|
||||
`tlsuv`? Anything on the default context is unaffected by `openssl.cnf` and sits outside the boundary.
|
||||
@@ -150,9 +150,13 @@ Run, and record the result of each:
|
||||
|
||||
## 7 -- the setting survives an upgrade
|
||||
|
||||
The failure this catches is described in
|
||||
[implementation-plan.md](implementation-plan.md#the-upgrade-gap----fix-this-or-the-feature-is-a-lie): an
|
||||
automatic silent upgrade that drops FIPS mode without saying so.
|
||||
The failure this catches is an automatic silent upgrade that drops FIPS mode without saying so. Verified
|
||||
working, but it depends on `SetFipsConfig` running before `StartServices` and on `MigrateFeatureStates`
|
||||
carrying the feature -- both easy to disturb. See
|
||||
[zdew-integration.md](zdew-integration.md#the-config-must-exist-before-the-service-starts).
|
||||
|
||||
Note also that FIPS cannot be *enabled* by upgrading: `ZITI_ENABLE_FIPS=1` passed to an upgrade is ignored,
|
||||
because the previous installation's feature selection wins.
|
||||
|
||||
1. Note the current state -- steps 4 and 5 above.
|
||||
2. Trigger an automatic update (`Restart-Service ziti-monitor` forces an immediate check).
|
||||
|
||||
@@ -202,7 +202,7 @@ with the peer:
|
||||
`e2ee_tls` was added in ziti-sdk-c 1.19.0. For a FIPS claim that covers E2EE payloads and not merely the
|
||||
transport, the mode has to be constrained to `tls` (or `aes-gcm`, with Microsoft's certificate cited instead of
|
||||
OpenSSL's) on both ends. That is a network-policy decision, not an installer setting, and it needs confirming
|
||||
with the SDK maintainers -- see the open questions in [implementation-plan.md](implementation-plan.md).
|
||||
with the SDK maintainers.
|
||||
|
||||
**Update verification.** `ziti-monitor` checks Authenticode signatures and talks HTTPS to the release stream
|
||||
using Windows CryptoAPI and .NET Framework, both of which use Microsoft's validated modules when the machine has
|
||||
|
||||
Reference in New Issue
Block a user