Packaging Cisco AnyConnect

The Problem

Just like many corporations, my corporation recently had to scramble for solutions to move our workforce to their homes for COVID-19. For our emergency work from home solution, we used VMWare’s Horizon software for the majority of our workers. This worked great, but there was a small subset that had specialized software on their laptops that needed true VPN access. Cisco AnyConnect has been our choice for VPN access, but we suddenly had a lot of new AnyConnect users that had already left the building. Unfortunately, with browsers disabling Active-X and Java the web installer is basically just a glorified downloader now and it ends up confusing many users.

This led us to a need to package Cisco AnyConnect for distribution from our colleague support website. We wanted to have three components (AnyConnect, DART, and SBL) installed, but we didn’t want colleagues having to download and install the three separate MSI files.

Solution Part 1

More on why there is a part two later, but first part one. To combine the MSIs into one package and install them in the order that I wanted, I needed a packaging tool. Since the budget for the project was $0 and my roots are in OSS, I sought out a free solution. Inno Setup has been around since 1997 and is free so it has a good track record and meets my budget.

Inno Setup is a compiler that takes a plain text file along with the source files like the MSIs and puts them into an EXE setup file. At it’s simplest I could just write the text file and run the program, but I needed something fast and I didn’t want to spend time troubleshooting my lack of understanding of the Inno Setup configuration. That’s where Inno Script Studio, also free, comes into the picture. Inno Script Studio gave me a graphical IDE to design my installer.

Using the tools I created a Inno Setup script that included the three MSIs and then the required msiexec commands to run the installers in order. With a few edits for eliminating my employer, here is the script that I used.

; Script generated by the Inno Setup Script Wizard.
; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES!

#define MyAppName "PackIT Forwarding Cisco AnyConnect"
#define MyAppVersion "1.0"
#define MyAppPublisher "PackIT Forwarding"
#define MyAppURL "https://packitforwarding.com"

[Setup]
; NOTE: The value of AppId uniquely identifies this application.
; Do not use the same AppId value in installers for other applications.
; (To generate a new GUID, click Tools | Generate GUID inside the IDE.)
AppId=
AppName={#MyAppName}
AppVersion={#MyAppVersion}
;AppVerName={#MyAppName} {#MyAppVersion}
AppPublisher={#MyAppPublisher}
AppPublisherURL={#MyAppURL}
AppSupportURL={#MyAppURL}
AppUpdatesURL={#MyAppURL}
DefaultDirName={pf}\{#MyAppName}
DefaultGroupName={#MyAppName}
DisableProgramGroupPage=yes
OutputDir=C:\Users\packitforwarding\Desktop\AnyConnect-Installer
OutputBaseFilename=pifanyconnect.exe
Compression=lzma
SolidCompression=yes
PrivilegesRequired=admin
DisableReadyPage=True
DisableReadyMemo=True
DisableFinishedPage=True
AlwaysShowGroupOnReadyPage=True
AlwaysShowDirOnReadyPage=True
VersionInfoVersion=1.01
VersionInfoCompany=PackIT Forwarding
VersionInfoProductName=PackIT Forwarding Cisco AnyConnect
VersionInfoProductVersion=1.01
VersionInfoProductTextVersion=1.01
MinVersion=0,6.1
SignTool=signtool

[Messages]

AdminPrivilegesRequired=This install requires administrative privileges.

[Languages]
Name: "english"; MessagesFile: "compiler:Default.isl"

[Files]
Source: "anyconnect-win-4.8.02045-core-vpn-predeploy-k9.msi"; DestDir: "{app}"; Flags: ignoreversion
Source: "anyconnect-win-4.8.02045-dart-predeploy-k9.msi"; DestDir: "{app}"; Flags: ignoreversion
Source: "anyconnect-win-4.8.02045-gina-predeploy-k9.msi"; DestDir: "{app}"; Flags: ignoreversion
; NOTE: Don't use "Flags: ignoreversion" on any shared system files

[Run]
Filename: "msiexec"; Parameters: "/package ""{app}\anyconnect-win-4.8.02045-core-vpn-predeploy-k9.msi"" /norestart /passive /lvx* C:\anyconnectinstall.log"
Filename: "msiexec"; Parameters: "/package ""{app}\anyconnect-win-4.8.02045-dart-predeploy-k9.msi"" /norestart /passive /lvx* C:\anyconnectdartinstall.log"
Filename: "msiexec"; Parameters: "/package ""{app}\anyconnect-win-4.8.02045-gina-predeploy-k9.msi"" /norestart /passive /lvx* C:\anyconnectginainstall.log"

At this point, I had a working installer that did everything I wanted. But as usual, it can’t be THAT easy.

The Problem TNG

I took the newly minted installer and ran it through some testing. Everything worked great. Unfortunately, when I uploaded the file to our website for colleagues to download, things went off course a bit. The new problem was Microsoft Windows Defender SmartScreen. Every time a colleague downloaded the installer and ran it, they were given a big warning that was not readily clear on how to say run anyway. Spoiler, click “more info” to get the ability to run it anyway.

The Bandaid (aka Solution Part 2)

Unfortunately, I can’t fully call this a solution, but it did help. Microsoft Windows Defender SmartScreen wants to see two things to not put up the big warning. They are a signed application and an application that it has seen on a large number of computers.

To sign applications you need two things, the signtool.exe and a certificate. To get that exe you have to download and install part of the Windows 10 SDK. When doing the install you only need to install “Windows SDK Signing Tools for Desktop Apps”. This will install signtool.exe into C:\Program Files (x86)\Windows Kits\10\x86\ .

Getting the certificate signed by our CA ended up being the most difficult part of the project. Mainly this was due to layers 8 and 9 of the OSI model getting gummed up with everyone out of the office for COVID-19. Once I had the certificate though, it was fairly easy to sign the EXE.

"C:\Program Files (x86)\Windows Kits\10\bin\x86\signtool.exe" sign /f "C:\users\packitforward\Desktop\Certs\PIF_CodeSigning.pfx" /t http://timestamp.comodoca.com/rfc3161 /p "Sup3rS3cre7" pifanyconnect.exe

After manually signing the package, I also setup Inno Setup Studio so that it could do it as part of the compilation in the future. It’s fairly simple to do as shown below.

As I alluded to in the header for this section, the code signing wasn’t a perfect solution. Microsoft still requires the application to be downloaded a lot to be trusted. If anyone knows of a way to speed that up, or submit an exe to Microsoft for validation, please comment below.

Final Thoughts

All told, this project was a quick win in the midst of the chaos of trying to move our work force to work from home. It also had the benefit of allowing me to help our telecommunications team when they needed to push out Jabber with some custom post-install powershell scripts. I hope it is of value to someone else.