Are you looking to run your Azure DevOps agent behind an unauthenticated/authenticated web proxy for traffic destined to the internet?
Then hopefully this post will help you straighten out those question marks that might come up during the process. There's a bit of a difference between the Windows and Linux agent, this post will only cover the Linux agent running on Ubuntu 20.04.
We'll start first with a summary of how the installation and deployment of the Azure DevOps VM Extension works:
- VM/VMSS has the Azure DevOps Extension deployed to it
- The VM/VMSS will download the extension from a public Azure Storage Account
- The Extension will run "Handler.sh -enable" and AzureRM.py to install the DevOps Agent
- AzureRM.py will read the settings file, decrypt the protected settings with the computer certificate
- AzureRM.py will download the Azure DevOps agent zip file and EnableAgent script
- InstallDependencies.sh will use APT to install missing dependencies
- The Azure DevOps agent installation will start and configure itself
Read more about the communication and setup of Azure DevOps Agent
As part of our network design, no web traffic may go directly to the internet, it will always go through a proxy. If the traffic tries to reach its destination without going through the proxy, the network security group (NSG) will stop it.
The first issue we encounter is that the Azure DevOps Extension itself cannot be downloaded as it is hosted on Microsoft generated storage accounts. You need to ensure this traffic can reach the storage account endpoint.
Once you have allowed the extension to download, the next problem is that the extension is not proxy aware. Looking at the code:
def url_retrieve(download_url, target):
if ('ProxyUrl' in proxy_config):
proxy_url = proxy_config['ProxyUrl']
proxy_handler = urllib.request.ProxyHandler({'https': proxy_url})
opener = urllib.request.build_opener(proxy_handler)
urllib.request.install_opener(opener)
urllib.request.urlretrieve(download_url, target)
The proxy_config is imported from GlobalSettings.py which just contains an empty dict:
proxy_config = {}
Looking at the urllib documentation we can see it supports "request.getproxies()", but this is not implemented in the extension code.
The workaround: host the agent zip and enable script on an Azure Storage Account with a private endpoint in the same virtual network as the VMSS, then update the extension settings to point there instead.
Once the agent is installed, configure the proxy file at /agent/.proxy:
[2022-11-04 08:23:39Z INFO VstsAgentWebProxy] Config proxy at: http://10.2.0.7:3128.
[2022-11-04 08:23:39Z INFO VstsAgentWebProxy] Config proxy use DefaultNetworkCredentials.
Don't forget to also set the APT proxy:
Acquire::http::Proxy "http://10.2.0.7:3128";
I hope this helps cast some clarity on how the Azure DevOps agent extension works behind a proxy. Thank you for reading!
Shout out to my colleagues who helped figure this out: Björn Sundling & Simon Wåhlin