Configure VS Code to install extensions through the Varnish Virtual Registry

This tutorial points VS Code at a Virtual Registry that caches extensions from the Visual Studio Marketplace, and then puts the Artifact Firewall in front of them.

Prerequisites

The Extensions view only works over HTTPS. VS Code’s window allows its own requests to https: addresses only, so the view cannot reach a registry served over plain http, and it reports Failed to fetch without sending a request. The code command line installs extensions over http too, so a setup that passes a check with code --install-extension can still leave the Extensions view empty.

Configure the registry

virtual_registry:
  registries:
  - name: vscode
    remotes:
    - url: https://marketplace.visualstudio.com

The examples below reach the registry as https://vscode.example.com, a host that the registry named vscode answers (see name).

The registry rewrites the download links in the Marketplace’s listings so that VS Code downloads each extension’s VSIX through it. Each VSIX is cached once per publisher, extension, version and platform. Icons, READMEs and the signature VS Code checks each extension against stay on the Marketplace’s CDN, so the editor still needs to reach the Marketplace. Package types describes how each request is cached.

Point VS Code at the registry

VS Code has no setting for the Marketplace address. It reads it from the extensionsGallery section of product.json, in the resources/app directory of the installation:

  • Linux: /usr/share/code/resources/app/product.json
  • macOS: /Applications/Visual Studio Code.app/Contents/Resources/app/product.json

Change three keys in that section:

product=/usr/share/code/resources/app/product.json
jq '.extensionsGallery.serviceUrl = "https://vscode.example.com/_apis/public/gallery"
  | .extensionsGallery.itemUrl = "https://vscode.example.com/items"
  | .extensionsGallery.resourceUrlTemplate = ""' \
  "$product" > /tmp/product.json
sudo cp /tmp/product.json "$product"

resourceUrlTemplate has to be empty. While it is set, VS Code builds the download URLs itself and ignores the ones the registry rewrote, so extensions download straight from the Marketplace. Leave the other keys in extensionsGallery as they are.

An update of VS Code replaces product.json, so make the change again after each one. On a managed fleet, the ExtensionGalleryServiceUrl policy sets the service URL instead.

Restart VS Code. Extensions now install through the registry, from the Extensions view and from the command line:

code --install-extension EditorConfig.EditorConfig

Enable the Artifact Firewall

The firewall is licensed separately, see enable_firewall.

virtual_registry:
  registries:
  - name: vscode
    enable_firewall: true
    remotes:
    - url: https://marketplace.visualstudio.com

firewall:
  max_manifest_size: 268435456
  redirects:
    signing:
      enabled: true
      key_env: FIREWALL_SIGNING_KEY
  rulesets:
  - path: /etc/varnish-supervisor/rulesets

max_manifest_size has to be raised from its 64 MiB default. The Extensions view asks for the full version history of many extensions at once, and such a listing runs past 100 MiB once decompressed. The firewall refuses a listing larger than the limit, and the Extensions view stays empty. 256 MiB is a good start. Each listing is held in memory while it is filtered, so the limit also bounds the memory a single request can take.

redirects.signing protects the quarantine window. The firewall enforces it on the download, and stamps the publish time onto each download link it hands VS Code. Signed, the link cannot be edited to backdate that time, and it expires after an hour. The key is hex-encoded and read from the Supervisor’s environment:

FIREWALL_SIGNING_KEY=$(openssl rand -hex 32)

A rule names an extension as pkg:vscode-extension/<publisher>/<extension>, in lowercase whatever case the Marketplace shows. This one, in /etc/varnish-supervisor/rulesets/vscode.yaml, denies ESLint:

id: vscode-policy
rules:
- id: deny-eslint
  action: deny
  reason: ESLint is not approved
  match:
  - purl: pkg:vscode-extension/dbaeumer/vscode-eslint

A rule naming pkg:vscode-extension/DBaeumer/vscode-eslint matches nothing.

code --install-extension dbaeumer.vscode-eslint then fails. The extension stays listed in the Extensions view, and installing it fails on the download, so that the attempt is recorded in the audit log. VS Code reports the refusal as a VSIX that is not a valid zip file and does not show the rule’s reason, so the audit log is the place to look for it.

To remove an extension from the Extensions view instead, use hide. Use hide too for an extension that is part of an extension pack, such as ms-python.vscode-pylance in ms-python.python. Denying one member makes VS Code roll back the whole pack. VS Code enforcement describes what each action and the quarantine window do.


®Varnish Software, Wallingatan 12, 111 60 Stockholm, Organization nr. 556805-6203