accessibility-insights-windows/docs/ChangingVersions.md

80 строки
6.7 KiB
Markdown
Исходник Постоянная ссылка Обычный вид История

feat(upgrader-security): Consume signed manifests, add telemetry and docs (#1493) #### Details This adds the client-side code to consume signed manifests (it temporarily also consumes unsigned manifests), as well as the code to use the data in the signed manifests to ensure that we only install non-tampered manifests. I tried to break this PR down into smaller pieces, but I couldn't get it any smaller than this. A reasonable overview of the process is added in `ChangingVersions.md`, but here's a (lengthy) breakdown of the code changes, in rough order of execution: ##### Fetching the manifest - We add 3 new fields to the `ChannelInfo` class. These fields contain the metadata about the signed MSI file. - We define a `StreamMetadata` class to provide more information about HTTP calls. - We define an `InterceptingWebClient` class that allows us to capture the fully resolved Uri that was the actual source of whatever we loaded. - In `GitHubClient.LoadUriContentsIntoStream`, we use the `InterceptingWebClient` to build a `StreamMetadata` that describes the web resource that we fetched. - In `ChannelIntoUtilities.GetChannelFromStream`, we first try to parse the stream as a signed manifest (including checking the signature chain). If that fails, we try to parse the stream as an unsigned manifest. The code to use the unsigned manifest will be removed after all release channels have migrated to signed manifests. - We define an `EnrichedContentInfo` class that derives from `ContentInfo`, adding the `StreamMetadata` as a member. This gives us a single object that contains the update information. - We synthesize `ChannelInfo.MinimumVersion` if we're reading an unsigned manifest (this data depends on the release channel and is not included in signed manifest files). - We add 3 manifest metadata fields to the `IAutoUpdate` interface. These exist for telemetry, since extensions can't publish telemetry events directly. - The `GithubAutoUpdate.AutoUpdate` class populates the metadata properties based on the data from the `EnrichedChannelnfo` object, and the manifest metadata fields get bundled with the `Upgrade_GetUpgradeOption` telemetry action. ##### Running the upgrade - We define the `ExecutionResult` enum to capture possible outcomes of running VersionSwitcher. - We define the `ResultBearingException` class, which is just a regular `Exception` with an `ExecutionResult` attached. - We define the `ResultExecutionWrapper` class, which allows us to have layers of failure capture, and the most granular failure will be preserved. - We define an `ExecutionHistory` class that will be used for telemetry from VersionSwitcher. It also replaces the existing EventLogger and ExceptionReporter classes that VersionSwitcher used for local debugging. - The VersionSwitcher's commands have been expanded to accept the MSI size and SHA, so VersionSwitcher can validate after downloading. For legacy manifests, these values are unknown. Since VersionSwitcher uses positional arguments, we need placeholders for the unknown versions. We use `0` for the unknown size and `none` for the unknown SHA. These new parameters are passed from `GitHubAutoUpdate.Update` into `VersionSwitcherWrapper.InstallUpgrade`. - VersionSwitcher uses any MSI metadata (size and/or SHA) to validate that the MSI installer is trustworthy before installing it. - Milestones during the execution of VersionSwitcher get saved in the `ExecutionHistory` object. - When VersionSwitcher exits, it serializes the `ExecutionHistory` data to a known file location. ##### Reporting VersionSwitcher results - When AIWin starts, it checks the disk for the serialized `ExecutionHistory`. - It takes selected data and publishes an `Upgrade_VersionSwitcherResults` event. This event includes rich metadata about the MSI file and should allow us to detect any tampering or corruption of the installer. There's some overlap with the existing `Upgrade_DoInstallation` telemetry point, so we might want to iterate a bit on this. - It renames the file with the serialized `ExecutionHistory`. This ensures that the telemetry gets updated only once, but keeps the file on disk for investigateion purposes. I've confirmed the core scenarios in the https://github.com/microsoft/accessibility-insights-windows/tree/DaveTryon/add-signed-manifest branch, but the real test will come when we get it into the Canary branch and enable the signed manifest. For testing, I'll manually copy the manifest into some web location and point the redirect to it. ##### Motivation Part of the upgrader security feature. ##### Context <!-- Are there any parts that you've intentionally left out-of-scope for a later PR to handle? --> - A future PR (after the client code changes are merged) will leverage the client code in the pipeline to run in-build tests that ensure that the client code and the manifest remain in sync. <!-- Were there any alternative approaches you considered? What tradeoffs did you consider? --> #### Pull request checklist <!-- If a checklist item is not applicable to this change, write "n/a" in the checkbox --> - [ ] Run through of all [test scenarios](https://github.com/Microsoft/accessibility-insights-windows/blob/main/docs/Scenarios.md) completed? - [n/a] Does this address an existing issue? If yes, Issue# - - [n/a] Includes UI changes? - [n/a] Run the production version of Accessibility Insights for Windows against a version with changes. - [n/a] Attach any screenshots / GIF's that are applicable. > Note: After the PR has been created, certain checks will be kicked off. All of these checks must pass before a merge.
2022-11-30 23:44:50 +03:00
## Changing version
This document provides a detailed description of the built-in mechanisms for changing versions. There are 2 scenarios that cause a version change:
1. [New versions being released](#new-versions-being-released)
2. [Changing channels](#changing-channels)
### New versions being released
This is the most common scenario that leads to a version change. The process works as follows:
- Accessibility Insights for Windows starts.
- It checks the result from [GitHubAutoUpdate](#githubautoupdate) to determines if an optional or a required update exists.
- It prompts the user to install any available updates.
- If the user chooses to install an update, it invokes the [VersionSwitcher](#versionswitcher) to install the new version.
- It exits the application after the VersionSwitcher has successfully launched with administrative privilege.
- It includes telemetry results written by the VersionSwitcher on its next boot.
Chore: Add fix & docs for mandatory prod update (#1740) #### Details This PR fixes a structural (but non-functional) bug associated with building mandatory prod updates. I also realized that this behavior wasn't documented anywhere, so I added an extra bit to the docs. The integration test that runs during the build now verifies that the generated value for production minimum version is correct based on the `IsMandatoryProdUpdate` environment variable. I ran 3 validation builds: 1. When `IsMandatoryProdUpdate` = "Dave", the build fails in the integration tests. Log output ([link](https://mseng.visualstudio.com/1ES/_build/results?buildId=24503522&view=logs&j=06f1c8b2-7bb0-5c49-9f03-01a8f16e24cf&t=acaf2a90-5d04-5dbb-0d6a-3dcb39127913&l=4438)): ``` Failed EmbeddedManifestMatchesRawManifest [172 ms] Error Message: Assert.IsTrue failed. The IsMandatoryProdUpdate environment variable must be 'true' or 'false' (case sensitive) if it is set. Stack Trace: at ManifestTests.IntegrationTests.EmbeddedManifestMatchesRawManifest() in D:\a\1\s\src\ManifestTests\IntegrationTests.cs:line 88 ``` 2. When `IsMandatoryProdUpdate` = "true", the build succeeds and uses current version as minimum prod version. Log output ([link](https://mseng.visualstudio.com/1ES/_build/results?buildId=24503524&view=logs&j=7c3ebc89-e7dd-5791-8c02-2ea1f3a27b50&t=9b81c1c0-976a-538f-a1e1-f4d170544876&l=3063)): ``` VERBOSE: Entering Get-MinimumProductionVersion VERBOSE: currentVersion = 1.1.2532.08 VERBOSE: octokitRelativePath = Octokit\9.0.0\lib\netstandard2.0\Octokit.dll VERBOSE: isMandatoryProdUpdate = true VERBOSE: Using currentVersion for minimumProductionVersion VERBOSE: Exiting Get-MinimumProductionVersion with value of 1.1.2532.08 ``` 3. When `IsMandatoryProdUpdate` = "false", the build succeeds and uses latest prod release as minimum prod version. Log output ([link](https://mseng.visualstudio.com/1ES/_build/results?buildId=24503525&view=logs&j=7c3ebc89-e7dd-5791-8c02-2ea1f3a27b50&t=9b81c1c0-976a-538f-a1e1-f4d170544876&l=3089)) ``` VERBOSE: Entering Get-MinimumProductionVersion VERBOSE: currentVersion = 1.1.2532.09 VERBOSE: octokitRelativePath = Octokit\9.0.0\lib\netstandard2.0\Octokit.dll VERBOSE: isMandatoryProdUpdate = false VERBOSE: Fetching version from 'https://github.com/microsoft/accessibility-insights-windows/releases/tag/latest' VERBOSE: Entering Get-Client VERBOSE: Relative path to OctoKit.dll = Octokit\9.0.0\lib\netstandard2.0\Octokit.dll VERBOSE: Full path to OctoKit.dll = C:\NuGetPackages\Octokit\9.0.0\lib\netstandard2.0\Octokit.dll VERBOSE: Exiting Get-Client VERBOSE: Exiting Get-MinimumProductionVersion with value of 1.1.2445.01 ``` ##### Motivation <!-- This can be as simple as "addresses issue #123" --> ##### Context <!-- Are there any parts that you've intentionally left out-of-scope for a later PR to handle? --> <!-- Were there any alternative approaches you considered? What tradeoffs did you consider? --> #### Pull request checklist <!-- If a checklist item is not applicable to this change, write "n/a" in the checkbox --> - [ ] Run through of all [test scenarios](https://github.com/Microsoft/accessibility-insights-windows/blob/main/docs/Scenarios.md) completed? - [n/a] Does this address an existing issue? If yes, Issue# - - [n/a] Includes UI changes? - [n/a] Run the production version of Accessibility Insights for Windows against a version with changes. - [n/a] Attach any screenshots / GIF's that are applicable. > Note: After the PR has been created, certain checks will be kicked off. All of these checks must pass before a merge.
2023-12-12 21:46:03 +03:00
#### Update policies
- In the **Canary** and **Insider** channels, users are _always_ required to update to the latest version if an update exists.
- In the **Production** channel, users are _generally_ allowed to use either of the 2 most recent **Production** releases. Once a third **Production** release is available, users will be required to update to the most recent version.
- There are _occasional_ cases where a **Production** release is required. This is done to ensure that all users are running a version that is supported by the Accessibility Insights for Windows team. This is done in the following cases:
- A security vulnerability is discovered in a previous version (and fixed in the most recent version).
- A critical bug is discovered in a previous version (and fixed in the most recent version).
To generate a Production release that creates a mandatory upgrade, run the build pipeline with the `IsMandatoryProdUpdate` variable set to `true`. This variable defaults to 'false' in the build pipeline and can be overridden when manually running a signed build.
feat(upgrader-security): Consume signed manifests, add telemetry and docs (#1493) #### Details This adds the client-side code to consume signed manifests (it temporarily also consumes unsigned manifests), as well as the code to use the data in the signed manifests to ensure that we only install non-tampered manifests. I tried to break this PR down into smaller pieces, but I couldn't get it any smaller than this. A reasonable overview of the process is added in `ChangingVersions.md`, but here's a (lengthy) breakdown of the code changes, in rough order of execution: ##### Fetching the manifest - We add 3 new fields to the `ChannelInfo` class. These fields contain the metadata about the signed MSI file. - We define a `StreamMetadata` class to provide more information about HTTP calls. - We define an `InterceptingWebClient` class that allows us to capture the fully resolved Uri that was the actual source of whatever we loaded. - In `GitHubClient.LoadUriContentsIntoStream`, we use the `InterceptingWebClient` to build a `StreamMetadata` that describes the web resource that we fetched. - In `ChannelIntoUtilities.GetChannelFromStream`, we first try to parse the stream as a signed manifest (including checking the signature chain). If that fails, we try to parse the stream as an unsigned manifest. The code to use the unsigned manifest will be removed after all release channels have migrated to signed manifests. - We define an `EnrichedContentInfo` class that derives from `ContentInfo`, adding the `StreamMetadata` as a member. This gives us a single object that contains the update information. - We synthesize `ChannelInfo.MinimumVersion` if we're reading an unsigned manifest (this data depends on the release channel and is not included in signed manifest files). - We add 3 manifest metadata fields to the `IAutoUpdate` interface. These exist for telemetry, since extensions can't publish telemetry events directly. - The `GithubAutoUpdate.AutoUpdate` class populates the metadata properties based on the data from the `EnrichedChannelnfo` object, and the manifest metadata fields get bundled with the `Upgrade_GetUpgradeOption` telemetry action. ##### Running the upgrade - We define the `ExecutionResult` enum to capture possible outcomes of running VersionSwitcher. - We define the `ResultBearingException` class, which is just a regular `Exception` with an `ExecutionResult` attached. - We define the `ResultExecutionWrapper` class, which allows us to have layers of failure capture, and the most granular failure will be preserved. - We define an `ExecutionHistory` class that will be used for telemetry from VersionSwitcher. It also replaces the existing EventLogger and ExceptionReporter classes that VersionSwitcher used for local debugging. - The VersionSwitcher's commands have been expanded to accept the MSI size and SHA, so VersionSwitcher can validate after downloading. For legacy manifests, these values are unknown. Since VersionSwitcher uses positional arguments, we need placeholders for the unknown versions. We use `0` for the unknown size and `none` for the unknown SHA. These new parameters are passed from `GitHubAutoUpdate.Update` into `VersionSwitcherWrapper.InstallUpgrade`. - VersionSwitcher uses any MSI metadata (size and/or SHA) to validate that the MSI installer is trustworthy before installing it. - Milestones during the execution of VersionSwitcher get saved in the `ExecutionHistory` object. - When VersionSwitcher exits, it serializes the `ExecutionHistory` data to a known file location. ##### Reporting VersionSwitcher results - When AIWin starts, it checks the disk for the serialized `ExecutionHistory`. - It takes selected data and publishes an `Upgrade_VersionSwitcherResults` event. This event includes rich metadata about the MSI file and should allow us to detect any tampering or corruption of the installer. There's some overlap with the existing `Upgrade_DoInstallation` telemetry point, so we might want to iterate a bit on this. - It renames the file with the serialized `ExecutionHistory`. This ensures that the telemetry gets updated only once, but keeps the file on disk for investigateion purposes. I've confirmed the core scenarios in the https://github.com/microsoft/accessibility-insights-windows/tree/DaveTryon/add-signed-manifest branch, but the real test will come when we get it into the Canary branch and enable the signed manifest. For testing, I'll manually copy the manifest into some web location and point the redirect to it. ##### Motivation Part of the upgrader security feature. ##### Context <!-- Are there any parts that you've intentionally left out-of-scope for a later PR to handle? --> - A future PR (after the client code changes are merged) will leverage the client code in the pipeline to run in-build tests that ensure that the client code and the manifest remain in sync. <!-- Were there any alternative approaches you considered? What tradeoffs did you consider? --> #### Pull request checklist <!-- If a checklist item is not applicable to this change, write "n/a" in the checkbox --> - [ ] Run through of all [test scenarios](https://github.com/Microsoft/accessibility-insights-windows/blob/main/docs/Scenarios.md) completed? - [n/a] Does this address an existing issue? If yes, Issue# - - [n/a] Includes UI changes? - [n/a] Run the production version of Accessibility Insights for Windows against a version with changes. - [n/a] Attach any screenshots / GIF's that are applicable. > Note: After the PR has been created, certain checks will be kicked off. All of these checks must pass before a merge.
2022-11-30 23:44:50 +03:00
### Changing channels
This scenario occurs after the user changes the release channel from the Settings tab. The application does the following:
- It makes a web call to retrieve the update manifest for the newly selected release channel.
- It checks the update manifest's digital signature to prevent tampering or corruption of the file.
- It invokes the [VersionSwitcher](#versionswitcher) to install the new version and change the release channel.
- It exits the application after the VersionSwitcher has successfully launched with administrative privilege.
- It includes telemetry results written by the VersionSwitcher on its next boot.
### Subsystems
#### GitHubAutoUpdate
`ApplicationInsights.Extensions.GitHubAutoUpdate.dll` does the following:
- It reads the registry to retrieve the currently installed version of Accessibility Insights for Windows.
- It makes a web call to retrieve the update manifest for the currently selected release channel.
- It checks the update manifest's digital signature to prevent tampering or corruption of the file.
- It extracts the update information from the manifest.
- It compares the installed application version against the update information. Depending on this comparison, one of the following statuses is returned:
- The installed version is the most recent version. No update exists.
- The installed version is supported but not the most recent version. An optional update exists.
- The installed version is no longer supported. A required update exists.
#### VersionSwitcher
`AccessibilityInsights.VersionSwitcher.exe` and its required assemblies are included in the installation of Accessibility Insights for Windows. It provides a secure mechanism to change application versions. Its operation consists of two steps:
1. The contents of the `VersionSwitcher` folder are securely copied from its location under `%ProgramFiles(x86)%` to a temporary location. This is necessary because the MSI installer will modify the files under `%ProgramFiles(x86)%`.
2. The copy of `AccessibilityInsights.VersionSwitcher.exe` is started with appropriate parameters.
3. `AccessibilityInsights.exe` waits for the copied binary to successfully initialize, then exits.
3. The copied binary does the following:
- It obtains permission to run with administrative privileges. On most Windows systems, this means that the LUA dialog is displayed.
- It reads the following command line parameters:
- Where to load the MSI installer for the new version.
- The expected size of the MSI installer.
- The expected SHA512 for the contents of the MSI installer.
- The newly selected release channel -- this is only specified in the [Changing channels](#changing-channels) scenario.
- It retrieves the new MSI installer from its specified location.
- It uses the provided MSI size and SHA512, along with a check of the MSI's digital signature, to prevent tampering or corruption of the MSI installer.
- It transactionally runs the MSI installer to change versions, reverting to the previously installed version on error.
- If a new release channel was specified, it updates the application's configuration file to reflect the new selection.
- It writes its execution history to disk (see [Telemetry from upgrades](#telemetry-from-upgrades) for more details.
- It re-launches the Accessibility Insights for Windows.
#### SetupLibrary
The `AccessibilityInsights.SetupLibrary` assembly contains most of the classes that are shared between `AccessibilityInsights.exe`, `AccessibilityInsights.Extensions.GitHubAutoUpdate.dll`, and `AccessibilityInsights.VersionSwitcher.exe`. This includes classes that:
- Retrieve files from the web.
- Validate digital signatures.
- Compute SHA512 values.
- Define data that gets shared between the different processes and workflows.
#### Telemetry from upgrades
`AccessibilityInsights.VersionSwitcher.exe` contains no direct telemetry signal. Its telemetry is captured as follows:
- During execution, it constructs an `ExecutionHistory` object.
- Before exiting, it serializes the `ExecutionHistory` object to disk, using `%temp%\AccessibilityInsights.VersionSwitcher.ExecutionHistory.json` as the output file. If the file already exists, it gets overwritten.
- When `AccessibilityInsights.exe` launches, it checks for the presence of this file. If it finds it, it uploads portions of the data as telemetry. It intentionally excludes the `LocalDetails` data, which is provided for local debugging and troubleshooting of upgrade failures.
- After the telemetry has been created, `%temp%\AccessibilityInsights.VersionSwitcher.ExecutionHistory.json` is deleted.
For more specific details of the generated telemetry, please refer to [Upgrade_VersionSwitcherResults](TelemetryDetails.md/#upgrade_versionswitcherresults).