Compare commits
34 Commits
travis-tes
...
azure-test
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
814223beaf | ||
|
|
136621ba40 | ||
|
|
495d7dd6b6 | ||
|
|
7fccf83ce1 | ||
|
|
7a6c04eb41 | ||
|
|
1443a723aa | ||
|
|
8110b8107a | ||
|
|
54064d6ee1 | ||
|
|
d299afa4ad | ||
|
|
689fc9909c | ||
|
|
067438f3c2 | ||
|
|
d395a569b7 | ||
|
|
d90b0c2b20 | ||
|
|
6e8e6e4d48 | ||
|
|
2dbb121e35 | ||
|
|
a37acc90eb | ||
|
|
46fa2620ee | ||
|
|
1879c32669 | ||
|
|
f5f5c9eba6 | ||
|
|
f86a1bde9d | ||
|
|
4583df2cd2 | ||
|
|
725eb7a2b8 | ||
|
|
b6300cbdbb | ||
|
|
2a9505927a | ||
|
|
1b16d64b9f | ||
|
|
1b55099059 | ||
|
|
dbc37ddac9 | ||
|
|
ec7ecd6b40 | ||
|
|
56ad2a3346 | ||
|
|
a896d16625 | ||
|
|
c02c14e18e | ||
|
|
b2e7219411 | ||
|
|
8f1380a90a | ||
|
|
7cc0a02a7c |
Binary file not shown.
@@ -3,57 +3,211 @@ import time
|
||||
import unittest
|
||||
import subprocess
|
||||
import re
|
||||
from http.server import BaseHTTPRequestHandler
|
||||
import threading
|
||||
import socketserver
|
||||
import json
|
||||
import tempfile
|
||||
import shutil
|
||||
import warnings
|
||||
|
||||
import pytest
|
||||
with warnings.catch_warnings():
|
||||
warnings.simplefilter("ignore")
|
||||
import pkg_resources
|
||||
|
||||
SCHEDULED_TASK_NAME = 'Certbot Renew and Auto-Update Task'
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def signing_cert():
|
||||
"""
|
||||
This fixture returns the path of a test signing certificate that is loaded into the
|
||||
Trusted Root Certification Authorities group of the Windows certificate store, in order
|
||||
to make Windows accept any executable signed with this certificate.
|
||||
Fixture cleanup is included.
|
||||
"""
|
||||
cert_thumbprint = None
|
||||
try:
|
||||
pfx_file = pkg_resources.resource_filename('windows_installer_integration_tests', 'assets/test-signing.pfx')
|
||||
output = _ps('(Import-PfxCertificate -FilePath {0} -CertStoreLocation Cert:\\LocalMachine\\Root).Thumbprint'
|
||||
.format(pfx_file), capture_stdout=True)
|
||||
cert_thumbprint = output.strip()
|
||||
if not cert_thumbprint:
|
||||
raise RuntimeError('Error, test signing certificate could not be installed.')
|
||||
|
||||
yield pfx_file
|
||||
finally:
|
||||
if cert_thumbprint:
|
||||
_ps('Get-ChildItem Cert:\\LocalMachine\\Root\\{0} | Remove-Item'.format(cert_thumbprint))
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def installer(request, signing_cert):
|
||||
"""
|
||||
This fixture returns the path of the Certbot Windows installer to use during the tests.
|
||||
It is signed with a test signing certificate that is accepted by the current system and
|
||||
thus the installer has a valid Authenticode status.
|
||||
Fixture cleanup is included.
|
||||
"""
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
shutil.copy(request.config.option.installer_path, temp_dir)
|
||||
installer_path = os.path.join(temp_dir, os.path.basename(request.config.option.installer_path))
|
||||
_ps('Set-AuthenticodeSignature -FilePath {0} -Certificate (Get-PfxCertificate -FilePath {1}) | Out-Null'
|
||||
.format(installer_path, signing_cert))
|
||||
|
||||
yield installer_path
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def github_mock(installer):
|
||||
"""
|
||||
This fixture starts a GitHub release API mock on localhost:9001. This mock returns a
|
||||
compliant GitHub release payload declaring that Certbot v99.9.9 is available.
|
||||
The assets path associated allows to download on localhost the signed Certbot installer
|
||||
used during the tests.
|
||||
"""
|
||||
server = None
|
||||
try:
|
||||
class ThreadedTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
|
||||
pass
|
||||
|
||||
class GitHubMock(BaseHTTPRequestHandler):
|
||||
def log_message(self, log_format, *args):
|
||||
pass
|
||||
|
||||
def do_GET(self):
|
||||
if re.match(r'^.*/releases/latest$', self.path):
|
||||
self.send_response(200)
|
||||
self.send_header('Content-type', 'application/json')
|
||||
self.end_headers()
|
||||
self.wfile.write(json.dumps(
|
||||
{
|
||||
'tag_name': 'v99.9.9',
|
||||
'assets': [{
|
||||
'name': os.path.basename(installer),
|
||||
'browser_download_url': 'http://localhost:8009/{0}'.format(os.path.basename(installer))
|
||||
}]
|
||||
}
|
||||
).encode())
|
||||
elif re.match(r'^.*certbot-.*installer-win32\.exe$', self.path):
|
||||
self.send_response(200)
|
||||
self.send_header('Content-type', 'application/octet-stream')
|
||||
self.end_headers()
|
||||
with open(installer, 'rb') as file_h:
|
||||
self.wfile.write(file_h.read())
|
||||
else:
|
||||
self.send_response(404)
|
||||
self.end_headers()
|
||||
|
||||
server_address = ('', 8009)
|
||||
server = ThreadedTCPServer(server_address, GitHubMock)
|
||||
thread = threading.Thread(target=server.serve_forever)
|
||||
thread.daemon = True
|
||||
thread.start()
|
||||
|
||||
yield 'http://localhost:8009/releases/latest'
|
||||
finally:
|
||||
if server:
|
||||
server.shutdown()
|
||||
server.server_close()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def upgrade_env(signing_cert, github_mock):
|
||||
"""
|
||||
This fixture prepares the current Windows system Registry for a proper blackbox testing
|
||||
of the auto-upgrade mechanism. GitHub release API is set to use the local GitHub release
|
||||
API mock. And the public key used to validate the installer is set to be the one from
|
||||
the test signing certificate used in the tests.
|
||||
Fixture cleanup is included.
|
||||
"""
|
||||
try:
|
||||
_ps('New-Item -Path HKLM:\\Software -Name Certbot -ErrorAction SilentlyContinue | Out-Null; exit 0')
|
||||
_ps('New-ItemProperty -Path HKLM:\\Software\\Certbot -Name CertbotUpgradeApiURL -Value {} '
|
||||
'| Out-Null'.format(github_mock))
|
||||
_ps('New-ItemProperty -Path HKLM:\\Software\\Certbot -Name CertbotSigningPubKey -Value '
|
||||
'([Convert]::ToBase64String((Get-PfxCertificate -FilePath {0}).GetPublicKey())) '
|
||||
'| Out-Null'.format(signing_cert))
|
||||
|
||||
yield True
|
||||
finally:
|
||||
_ps('Remove-ItemProperty -Path HKLM:\\Software\\Certbot -Name CertbotUpgradeApiURL')
|
||||
_ps('Remove-ItemProperty -Path HKLM:\\Software\\Certbot -Name CertbotSigningPubKey')
|
||||
|
||||
|
||||
@unittest.skipIf(os.name != 'nt', reason='Windows installer tests must be run on Windows.')
|
||||
def test_it(request):
|
||||
def test_base(installer):
|
||||
"""
|
||||
This test checks that the Certbot installer installs correctly Certbot, including a fully
|
||||
functional automated renewal mechanism through a Windows scheduled task.
|
||||
"""
|
||||
_assert_certbot_is_missing()
|
||||
|
||||
# Install certbot
|
||||
subprocess.check_output([installer, '/S'])
|
||||
|
||||
# Assert certbot is installed and runnable
|
||||
output = subprocess.check_output('certbot --version', shell=True, universal_newlines=True)
|
||||
assert re.match(r'^certbot \d+\.\d+\.\d+.*$', output), 'Flag --version does not output a version.'
|
||||
|
||||
# Assert the renew + auto-upgrade task is installed and ready
|
||||
output = _ps('(Get-ScheduledTask -TaskName "{}").State'.format(SCHEDULED_TASK_NAME), capture_stdout=True)
|
||||
assert output.strip() == 'Ready'
|
||||
|
||||
# Trigger the renew + auto-upgrade task, expecting Certbot to check for certificate renewals.
|
||||
now = time.time()
|
||||
_ps('Start-ScheduledTask -TaskName "{}"'.format(SCHEDULED_TASK_NAME))
|
||||
_wait_for_task_completion()
|
||||
|
||||
log_path = os.path.join('C:\\', 'Certbot', 'log', 'letsencrypt.log')
|
||||
|
||||
modification_time = os.path.getmtime(log_path)
|
||||
assert now < modification_time, 'Certbot log file has not been modified by the renew task.'
|
||||
|
||||
with open(log_path) as file_h:
|
||||
data = file_h.read()
|
||||
assert 'no renewal failures' in data, 'Renew task did not execute properly.'
|
||||
|
||||
|
||||
# NB: This test must be declared after test_base, and so will be started after test_base,
|
||||
# because it requires a working installation of Certbot, and test_base provides that.
|
||||
@unittest.skipIf(os.name != 'nt', reason='Windows installer tests must be run on Windows.')
|
||||
def test_upgrade(upgrade_env):
|
||||
"""
|
||||
This tests checks that Certbot installed with the current tested installer can upgrade
|
||||
or repair itself through a Windows scheduled task.
|
||||
"""
|
||||
assert upgrade_env
|
||||
subprocess.check_output(['certbot', '--version'])
|
||||
|
||||
# Break Certbot on purpose
|
||||
_ps('Remove-Item "${env:ProgramFiles(x86)}\\Certbot\\bin\\certbot.exe" -Confirm:$false')
|
||||
_assert_certbot_is_missing()
|
||||
|
||||
# Trigger the renew + auto-upgrade task, expecting Certbot to be reinstalled and functional again.
|
||||
now = time.time()
|
||||
_ps('Start-ScheduledTask -TaskName "{}"'.format(SCHEDULED_TASK_NAME))
|
||||
_wait_for_task_completion()
|
||||
|
||||
subprocess.check_output(['certbot', '--version'])
|
||||
|
||||
|
||||
def _assert_certbot_is_missing():
|
||||
try:
|
||||
subprocess.check_call(['certbot', '--version'])
|
||||
subprocess.check_output(['certbot', '--version'])
|
||||
except (subprocess.CalledProcessError, OSError):
|
||||
pass
|
||||
else:
|
||||
raise AssertionError('Expect certbot to not be available in the PATH.')
|
||||
|
||||
try:
|
||||
# Install certbot
|
||||
subprocess.check_call([request.config.option.installer_path, '/S'])
|
||||
|
||||
# Assert certbot is installed and runnable
|
||||
output = subprocess.check_output(['certbot', '--version'], universal_newlines=True)
|
||||
assert re.match(r'^certbot \d+\.\d+\.\d+.*$', output), 'Flag --version does not output a version.'
|
||||
|
||||
# Assert renew task is installed and ready
|
||||
output = _ps('(Get-ScheduledTask -TaskName "Certbot Renew Task").State', capture_stdout=True)
|
||||
assert output.strip() == 'Ready'
|
||||
|
||||
# Assert renew task is working
|
||||
now = time.time()
|
||||
_ps('Start-ScheduledTask -TaskName "Certbot Renew Task"')
|
||||
|
||||
status = 'Running'
|
||||
while status != 'Ready':
|
||||
status = _ps('(Get-ScheduledTask -TaskName "Certbot Renew Task").State', capture_stdout=True).strip()
|
||||
time.sleep(1)
|
||||
|
||||
log_path = os.path.join('C:\\', 'Certbot', 'log', 'letsencrypt.log')
|
||||
|
||||
modification_time = os.path.getmtime(log_path)
|
||||
assert now < modification_time, 'Certbot log file has not been modified by the renew task.'
|
||||
|
||||
with open(log_path) as file_h:
|
||||
data = file_h.read()
|
||||
assert 'no renewal failures' in data, 'Renew task did not execute properly.'
|
||||
|
||||
finally:
|
||||
# Sadly this command cannot work in non interactive mode: uninstaller will ask explicitly permission in an UAC prompt
|
||||
# print('Uninstalling Certbot ...')
|
||||
# uninstall_path = _ps('(gci "HKLM:\\SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall"'
|
||||
# ' | foreach { gp $_.PSPath }'
|
||||
# ' | ? { $_ -match "Certbot" }'
|
||||
# ' | select UninstallString)'
|
||||
# '.UninstallString', capture_stdout=True)
|
||||
# subprocess.check_call([uninstall_path, '/S'])
|
||||
pass
|
||||
def _wait_for_task_completion():
|
||||
status = 'Running'
|
||||
while status != 'Ready':
|
||||
status = _ps('(Get-ScheduledTask -TaskName "{}").State'
|
||||
.format(SCHEDULED_TASK_NAME), capture_stdout=True).strip()
|
||||
time.sleep(1)
|
||||
|
||||
|
||||
def _ps(powershell_str, capture_stdout=False):
|
||||
|
||||
139
windows-installer/auto-update.ps1
Normal file
139
windows-installer/auto-update.ps1
Normal file
@@ -0,0 +1,139 @@
|
||||
#Requires -Version 5.0
|
||||
[CmdletBinding()]
|
||||
param()
|
||||
begin {}
|
||||
process {
|
||||
$eventSource = "certbot/auto-update.ps1"
|
||||
$logName = "CertbotAutoUpdate"
|
||||
$eventID = 1
|
||||
|
||||
New-EventLog -Source $eventSource -LogName $logName -ErrorAction SilentlyContinue
|
||||
|
||||
function Write-Message($message, $level = "Information") {
|
||||
Write-EventLog -Source $eventSource -LogName $logName -EventID $eventID -EntryType $level -Message $message
|
||||
Write-Host $message
|
||||
}
|
||||
|
||||
function Throw-Error($message) {
|
||||
Write-EventLog -Source $eventSource -LogName $logName -EventID $eventID -EntryType Error -Message $message
|
||||
throw $message
|
||||
}
|
||||
|
||||
Write-Message "Starting auto-update workflow ..."
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
$installDir = $PSScriptRoot
|
||||
|
||||
if ((Test-Path HKLM:\Software\Certbot) -And ((Get-ItemProperty -Path HKLM:\Software\Certbot).PSObject.Properties.Name -Contains "CertbotSigningPubKey")) {
|
||||
$certbotSigningPubKey = (Get-ItemProperty -Path HKLM:\Software\Certbot).CertbotSigningPubKey
|
||||
} else {
|
||||
$certbotSigningPubKey = '
|
||||
-----BEGIN PUBLIC KEY-----
|
||||
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA6MR8W/galdxnpGqBsYbq
|
||||
OzQb2eyW15YFjDDEMI0ZOzt8f504obNs920lDnpPD2/KqgsfjOgw2K7xWDJIj/18
|
||||
xUvWPk3LDkrnokNiRkA3KOx3W6fHycKL+zID7zy+xZYBuh2fLyQtWV1VGQ45iNRp
|
||||
9+Zo7rH86cdfgkdnWTlNSHyTLW9NbXvyv/E12bppPcEvgCTAQXgnDVJ0/sqmeiij
|
||||
n9tTFh03aM+R2V/21h8aTraAS24qiPCz6gkmYGC8yr6mglcnNoYbsLNYZ69zF1XH
|
||||
cXPduCPdPdfLlzVlKK1/U7hkA28eG3BIAMh6uJYBRJTpiGgaGdPd7YekUB8S6cy+
|
||||
CQIDAQAB
|
||||
-----END PUBLIC KEY-----
|
||||
'
|
||||
}
|
||||
|
||||
if ((Test-Path HKLM:\Software\Certbot) -And ((Get-ItemProperty -Path HKLM:\Software\Certbot).PSObject.Properties.Name -Contains "CertbotUpgradeApiURL")) {
|
||||
$certbotUpgradeApiURL = (Get-ItemProperty -Path HKLM:\Software\Certbot).CertbotUpgradeApiURL
|
||||
} else {
|
||||
$certbotUpgradeApiURL = 'https://api.github.com/repos/certbot/certbot/releases/latest'
|
||||
}
|
||||
|
||||
# Get current local certbot version
|
||||
try {
|
||||
$currentVersion = certbot --version
|
||||
$currentVersion = $currentVersion -replace '^certbot (\d+\.\d+\.\d+).*$', '$1'
|
||||
$currentVersion = [System.Version]"$currentVersion"
|
||||
} catch {
|
||||
Write-Message @"
|
||||
An error occured while fetching the current local certbot version:
|
||||
$_
|
||||
Assuming Certbot is not up-to-date.
|
||||
"@ "Warning"
|
||||
$currentVersion = [System.Version]"0.0.0"
|
||||
}
|
||||
|
||||
# Get latest remote certbot version
|
||||
try {
|
||||
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
|
||||
$result = Invoke-RestMethod -Uri $certbotUpgradeApiURL
|
||||
$latestVersion = $result.tag_name -replace '^v(\d+\.\d+\.\d+).*$', '$1'
|
||||
$latestVersion = [System.Version]"$latestVersion"
|
||||
} catch {
|
||||
Throw-Error @"
|
||||
Could not get the latest remote certbot version. Error was:
|
||||
$_
|
||||
Aborting auto-upgrade process.
|
||||
"@
|
||||
}
|
||||
|
||||
if ($currentVersion -ge $latestVersion) {
|
||||
Write-Message "No upgrade is needed, Certbot is already at the latest version ($currentVersion)."
|
||||
} else {
|
||||
# Search for the Windows installer asset
|
||||
$installerUrl = $null
|
||||
foreach ($asset in $result.assets) {
|
||||
if ($asset.name -match '^certbot-.*installer-win32\.exe$') {
|
||||
$installerUrl = $asset.browser_download_url
|
||||
}
|
||||
}
|
||||
|
||||
if ($null -eq $installerUrl) {
|
||||
Throw-Error "Could not find the URL for the latest Certbot for Windows installer."
|
||||
}
|
||||
|
||||
Write-Message "Starting Certbot auto-upgrade from $currentVersion to $latestVersion ..."
|
||||
|
||||
$installerPath = "$env:TMP/certbot-installer-win32.exe"
|
||||
try {
|
||||
# Download the installer
|
||||
Write-Message "Downloading the installer ..."
|
||||
$webClient = New-Object System.Net.WebClient
|
||||
$webClient.DownloadFile($installerUrl, $installerPath)
|
||||
|
||||
# Check installer has a valid signature from the Certbot release team
|
||||
$signature = Get-AuthenticodeSignature $installerPath
|
||||
|
||||
if ($signature.Status -ne 'Valid') {
|
||||
throw "Downloaded installer has no or invalid Authenticode signature."
|
||||
}
|
||||
$publicKey = $certbotSigningPubKey -replace '-+.*-+' -replace "`n" -replace "`r"
|
||||
$refBinaryPublicKey = [System.Convert]::FromBase64String($publicKey)
|
||||
$curBinaryPublicKey = $signature.SignerCertificate.PublicKey.EncodedKeyValue.RawData
|
||||
$diff = Compare-Object -ReferenceObject $refBinaryPublicKey -DifferenceObject $curBinaryPublicKey
|
||||
if ($diff) {
|
||||
throw "Downloaded installer has not been signed by Certbot development team."
|
||||
}
|
||||
|
||||
if (Test-Path $installDir\uninstall.exe) {
|
||||
# Uninstall old Certbot first
|
||||
Write-Message "Running the uninstaller for old version (install dir: $installDir) ..."
|
||||
Start-Process -FilePath $installDir\uninstall.exe -ArgumentList "/S" -Wait
|
||||
}
|
||||
# Install new version of Certbot
|
||||
Write-Message "Running the installer for new version (install dir: $installDir) ..."
|
||||
Start-Process -FilePath $installerPath -ArgumentList "/S /D=$installDir" -Wait
|
||||
|
||||
Write-Message "Certbot $latestVersion is installed."
|
||||
} catch {
|
||||
Throw-Error @"
|
||||
Could not update to the latest remote certbot version. Error was:
|
||||
$_
|
||||
Aborting auto-upgrade process.
|
||||
"@
|
||||
} finally {
|
||||
Remove-Item $installerPath -ErrorAction 'Ignore'
|
||||
}
|
||||
}
|
||||
|
||||
Write-Message "Finished auto-update workflow."
|
||||
}
|
||||
end {}
|
||||
@@ -84,8 +84,9 @@ def _copy_assets(build_path, repo_path):
|
||||
shutil.copy(os.path.join(repo_path, 'windows-installer', 'certbot.ico'), build_path)
|
||||
shutil.copy(os.path.join(repo_path, 'windows-installer', 'run.bat'), build_path)
|
||||
shutil.copy(os.path.join(repo_path, 'windows-installer', 'template.nsi'), build_path)
|
||||
shutil.copy(os.path.join(repo_path, 'windows-installer', 'renew-up.ps1'), build_path)
|
||||
shutil.copy(os.path.join(repo_path, 'windows-installer', 'renew-down.ps1'), build_path)
|
||||
shutil.copy(os.path.join(repo_path, 'windows-installer', 'tasks-up.ps1'), build_path)
|
||||
shutil.copy(os.path.join(repo_path, 'windows-installer', 'tasks-down.ps1'), build_path)
|
||||
shutil.copy(os.path.join(repo_path, 'windows-installer', 'auto-update.ps1'), build_path)
|
||||
|
||||
|
||||
def _generate_pynsist_config(repo_path, build_path):
|
||||
@@ -144,8 +145,9 @@ bitness={python_bitness}
|
||||
[Include]
|
||||
local_wheels=wheels\\*.whl
|
||||
files=run.bat
|
||||
renew-up.ps1
|
||||
renew-down.ps1
|
||||
tasks-up.ps1
|
||||
tasks-down.ps1
|
||||
auto-update.ps1
|
||||
|
||||
[Command certbot]
|
||||
entry_point=certbot.main:main
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
$taskName = "Certbot Renew Task"
|
||||
|
||||
$exists = Get-ScheduledTask | Where-Object {$_.TaskName -like $taskName}
|
||||
if ($exists) {
|
||||
Unregister-ScheduledTask -TaskName $taskName -Confirm:$false
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
function Get-ScriptDirectory { Split-Path $MyInvocation.ScriptName }
|
||||
$down = Join-Path (Get-ScriptDirectory) 'renew-down.ps1'
|
||||
& $down
|
||||
|
||||
$taskName = "Certbot Renew Task"
|
||||
|
||||
$action = New-ScheduledTaskAction -Execute 'Powershell.exe' -Argument '-NoProfile -WindowStyle Hidden -Command "certbot renew"'
|
||||
$delay = New-TimeSpan -Hours 12
|
||||
$triggerAM = New-ScheduledTaskTrigger -Daily -At 12am -RandomDelay $delay
|
||||
$triggerPM = New-ScheduledTaskTrigger -Daily -At 12pm -RandomDelay $delay
|
||||
# NB: For now scheduled task is set up under Administrators group account because Certbot Installer installs Certbot for all users.
|
||||
# If in the future we allow the Installer to install Certbot for one specific user, the scheduled task will need to
|
||||
# switch to this user, since Certbot will be available only for him.
|
||||
$adminsSID = New-Object System.Security.Principal.SecurityIdentifier("S-1-5-32-544")
|
||||
$adminsGroupID = $adminsSID.Translate([System.Security.Principal.NTAccount]).Value
|
||||
$principal = New-ScheduledTaskPrincipal -GroupId $adminsGroupID -RunLevel Highest
|
||||
Register-ScheduledTask -Action $action -Trigger $triggerAM,$triggerPM -TaskName $taskName -Description "Execute twice a day the 'certbot renew' command, to renew managed certificates if needed." -Principal $principal
|
||||
10
windows-installer/tasks-down.ps1
Normal file
10
windows-installer/tasks-down.ps1
Normal file
@@ -0,0 +1,10 @@
|
||||
#Requires -Version 5.0
|
||||
$tasks = "Certbot Renew and Auto-Update Task", "Certbot Renew Task"
|
||||
|
||||
foreach ($task in $tasks) {
|
||||
$exists = Get-ScheduledTask | Where-Object { $_.TaskName -like $task }
|
||||
if ($exists)
|
||||
{
|
||||
Unregister-ScheduledTask -TaskName $task -Confirm:$false
|
||||
}
|
||||
}
|
||||
27
windows-installer/tasks-up.ps1
Normal file
27
windows-installer/tasks-up.ps1
Normal file
@@ -0,0 +1,27 @@
|
||||
#Requires -Version 5.0
|
||||
param(
|
||||
[Parameter(Mandatory=$true, ValueFromPipeline=$true)]
|
||||
[string]
|
||||
$InstallDir
|
||||
)
|
||||
|
||||
function Get-ScriptDirectory { Split-Path $MyInvocation.ScriptName }
|
||||
$down = Join-Path (Get-ScriptDirectory) 'tasks-down.ps1'
|
||||
& $down
|
||||
|
||||
$taskName = "Certbot Renew and Auto-Update Task"
|
||||
$taskDescription = "Execute twice a day the 'certbot renew' command, to renew managed certificates if needed, and upgrade Certbot if a new version is available."
|
||||
|
||||
$actionRenew = New-ScheduledTaskAction -Execute 'Powershell.exe' -Argument "-NoProfile -WindowStyle Hidden -Command ""& '$InstallDir\bin\certbot.exe' renew"""
|
||||
$actionUpgrade = New-ScheduledTaskAction -Execute 'Powershell.exe' -Argument "-NoProfile -WindowStyle Hidden -File ""$InstallDir\auto-update.ps1"""
|
||||
|
||||
$delay = New-TimeSpan -Hours 12
|
||||
$triggerAM = New-ScheduledTaskTrigger -Daily -At 12am -RandomDelay $delay
|
||||
$triggerPM = New-ScheduledTaskTrigger -Daily -At 12pm -RandomDelay $delay
|
||||
# NB: For now scheduled task is set up under Administrators account because Certbot Installer installs Certbot for all users.
|
||||
# If in the future we allow the Installer to install Certbot for one specific user, the scheduled task will need to
|
||||
# switch to this user, since Certbot will be available only for him.
|
||||
$adminSID = New-Object System.Security.Principal.SecurityIdentifier("S-1-5-32-544")
|
||||
$adminGroupID = $adminSID.Translate([System.Security.Principal.NTAccount]).Value
|
||||
$principal = New-ScheduledTaskPrincipal -GroupId $adminGroupID -RunLevel Highest
|
||||
Register-ScheduledTask -Action $actionRenew, $actionUpgrade -Trigger $triggerAM, $triggerPM -TaskName $taskName -Description $taskDescription -Principal $principal
|
||||
183
windows-installer/template-nsi.patch
Normal file
183
windows-installer/template-nsi.patch
Normal file
@@ -0,0 +1,183 @@
|
||||
--- pyapp.nsi 2020-03-31 01:46:13.118535700 +0200
|
||||
+++ template.nsi 2020-03-31 01:45:37.054503000 +0200
|
||||
@@ -1,3 +1,13 @@
|
||||
+; This NSIS template is based on the built-in one in pynsist 2.4.
|
||||
+; If pynsist is upgraded, this template may be updated if necessary using the new built-in one.
|
||||
+; Original file can be found here: https://github.com/takluyver/pynsist/blob/2.4/nsist/pyapp.nsi
|
||||
+; Diff file is located at .\template-nsi.patch
|
||||
+
|
||||
+; Require the installer do be installed with admin privileges
|
||||
+RequestExecutionLevel admin
|
||||
+; Set default installation path (overridable with /D= flag)
|
||||
+InstallDir "$PROGRAMFILES\Certbot"
|
||||
+
|
||||
!define PRODUCT_NAME "[[ib.appname]]"
|
||||
!define PRODUCT_VERSION "[[ib.version]]"
|
||||
!define PY_VERSION "[[ib.py_version]]"
|
||||
@@ -6,24 +16,22 @@
|
||||
!define ARCH_TAG "[[arch_tag]]"
|
||||
!define INSTALLER_NAME "[[ib.installer_name]]"
|
||||
!define PRODUCT_ICON "[[icon]]"
|
||||
-
|
||||
-; Marker file to tell the uninstaller that it's a user installation
|
||||
-!define USER_INSTALL_MARKER _user_install_marker
|
||||
-
|
||||
+
|
||||
SetCompressor lzma
|
||||
|
||||
-!define MULTIUSER_EXECUTIONLEVEL Highest
|
||||
-!define MULTIUSER_INSTALLMODE_DEFAULT_CURRENTUSER
|
||||
-!define MULTIUSER_MUI
|
||||
-!define MULTIUSER_INSTALLMODE_COMMANDLINE
|
||||
-!define MULTIUSER_INSTALLMODE_INSTDIR "[[ib.appname]]"
|
||||
-[% if ib.py_bitness == 64 %]
|
||||
-!define MULTIUSER_INSTALLMODE_FUNCTION correct_prog_files
|
||||
-[% endif %]
|
||||
-!include MultiUser.nsh
|
||||
+Function .onInit
|
||||
+ ; Check that Powershell is at least 5.0.
|
||||
+ nsExec::ExecToStack `powershell -ExecutionPolicy RemoteSigned -Command "Write-Host -NoNewline (Get-Host | Select-Object Version).Version.CompareTo((New-Object -TypeName System.Version -ArgumentList '5.0'))"`
|
||||
+ Pop $0
|
||||
+ Pop $1
|
||||
+ StrCmp $1 "-1" 0 powershellok
|
||||
+ MessageBox MB_OK|MB_ICONSTOP "Certbot requires Powershell 5.0+ to work.$\r$\nPlease check the following link to know how to upgrade your system:$\r$\nhttps://docs.microsoft.com/powershell/scripting/install/installing-powershell"
|
||||
+ Abort
|
||||
+ powershellok:
|
||||
+FunctionEnd
|
||||
|
||||
[% block modernui %]
|
||||
-; Modern UI installer stuff
|
||||
+; Modern UI installer stuff
|
||||
!include "MUI2.nsh"
|
||||
!define MUI_ABORTWARNING
|
||||
!define MUI_ICON "[[icon]]"
|
||||
@@ -35,7 +43,6 @@
|
||||
[% if license_file %]
|
||||
!insertmacro MUI_PAGE_LICENSE [[license_file]]
|
||||
[% endif %]
|
||||
-!insertmacro MULTIUSER_PAGE_INSTALLMODE
|
||||
!insertmacro MUI_PAGE_DIRECTORY
|
||||
!insertmacro MUI_PAGE_INSTFILES
|
||||
!insertmacro MUI_PAGE_FINISH
|
||||
@@ -43,7 +50,7 @@
|
||||
!insertmacro MUI_LANGUAGE "English"
|
||||
[% endblock modernui %]
|
||||
|
||||
-Name "${PRODUCT_NAME} ${PRODUCT_VERSION}"
|
||||
+Name "${PRODUCT_NAME} (beta) ${PRODUCT_VERSION}"
|
||||
OutFile "${INSTALLER_NAME}"
|
||||
ShowInstDetails show
|
||||
|
||||
@@ -56,18 +63,13 @@
|
||||
|
||||
Section "!${PRODUCT_NAME}" sec_app
|
||||
SetRegView [[ib.py_bitness]]
|
||||
+ SetShellVarContext all
|
||||
SectionIn RO
|
||||
File ${PRODUCT_ICON}
|
||||
SetOutPath "$INSTDIR\pkgs"
|
||||
File /r "pkgs\*.*"
|
||||
SetOutPath "$INSTDIR"
|
||||
|
||||
- ; Marker file for per-user install
|
||||
- StrCmp $MultiUser.InstallMode CurrentUser 0 +3
|
||||
- FileOpen $0 "$INSTDIR\${USER_INSTALL_MARKER}" w
|
||||
- FileClose $0
|
||||
- SetFileAttributes "$INSTDIR\${USER_INSTALL_MARKER}" HIDDEN
|
||||
-
|
||||
[% block install_files %]
|
||||
; Install files
|
||||
[% for destination, group in grouped_files %]
|
||||
@@ -76,14 +78,14 @@
|
||||
File "[[ file ]]"
|
||||
[% endfor %]
|
||||
[% endfor %]
|
||||
-
|
||||
+
|
||||
; Install directories
|
||||
[% for dir, destination in ib.install_dirs %]
|
||||
SetOutPath "[[ pjoin(destination, dir) ]]"
|
||||
File /r "[[dir]]\*.*"
|
||||
[% endfor %]
|
||||
[% endblock install_files %]
|
||||
-
|
||||
+
|
||||
[% block install_shortcuts %]
|
||||
; Install shortcuts
|
||||
; The output path becomes the working directory for shortcuts
|
||||
@@ -109,17 +111,11 @@
|
||||
DetailPrint "Setting up command-line launchers..."
|
||||
nsExec::ExecToLog '[[ python ]] -Es "$INSTDIR\_assemble_launchers.py" [[ python ]] "$INSTDIR\bin"'
|
||||
|
||||
- StrCmp $MultiUser.InstallMode CurrentUser 0 AddSysPathSystem
|
||||
- ; Add to PATH for current user
|
||||
- nsExec::ExecToLog '[[ python ]] -Es "$INSTDIR\_system_path.py" add_user "$INSTDIR\bin"'
|
||||
- GoTo AddedSysPath
|
||||
- AddSysPathSystem:
|
||||
- ; Add to PATH for all users
|
||||
- nsExec::ExecToLog '[[ python ]] -Es "$INSTDIR\_system_path.py" add "$INSTDIR\bin"'
|
||||
- AddedSysPath:
|
||||
+ ; Add to PATH for all users
|
||||
+ nsExec::ExecToLog '[[ python ]] -Es "$INSTDIR\_system_path.py" add "$INSTDIR\bin"'
|
||||
[% endif %]
|
||||
[% endblock install_commands %]
|
||||
-
|
||||
+
|
||||
; Byte-compile Python files.
|
||||
DetailPrint "Byte-compiling Python modules..."
|
||||
nsExec::ExecToLog '[[ python ]] -m compileall -q "$INSTDIR\pkgs"'
|
||||
@@ -144,6 +140,10 @@
|
||||
WriteRegDWORD SHCTX "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}" \
|
||||
"NoRepair" 1
|
||||
|
||||
+ ; Execute ps script to create the certbot renew & auto-update task
|
||||
+ DetailPrint "Setting up certbot renew & auto-update scheduled task"
|
||||
+ nsExec::ExecToLog 'powershell -inputformat none -ExecutionPolicy RemoteSigned -File "$INSTDIR\tasks-up.ps1" -InstallDir "$INSTDIR"'
|
||||
+
|
||||
; Check if we need to reboot
|
||||
IfRebootFlag 0 noreboot
|
||||
MessageBox MB_YESNO "A reboot is required to finish the installation. Do you wish to reboot now?" \
|
||||
@@ -155,11 +155,14 @@
|
||||
Section "Uninstall"
|
||||
SetRegView [[ib.py_bitness]]
|
||||
SetShellVarContext all
|
||||
- IfFileExists "$INSTDIR\${USER_INSTALL_MARKER}" 0 +3
|
||||
- SetShellVarContext current
|
||||
- Delete "$INSTDIR\${USER_INSTALL_MARKER}"
|
||||
|
||||
- Delete $INSTDIR\uninstall.exe
|
||||
+ ; Execute ps script to remove the certbot renew & auto-update task, then delete scripts
|
||||
+ nsExec::ExecToLog 'powershell -inputformat none -ExecutionPolicy RemoteSigned -File "$INSTDIR\tasks-down.ps1"'
|
||||
+ Delete "$INSTDIR\tasks-down.ps1"
|
||||
+ Delete "$INSTDIR\tasks-up.ps1"
|
||||
+ Delete "$INSTDIR\auto-update.ps1"
|
||||
+
|
||||
+ Delete "$INSTDIR\uninstall.exe"
|
||||
Delete "$INSTDIR\${PRODUCT_ICON}"
|
||||
RMDir /r "$INSTDIR\pkgs"
|
||||
|
||||
@@ -207,23 +210,6 @@
|
||||
[% block mouseover_messages %]
|
||||
StrCmp $0 ${sec_app} "" +2
|
||||
SendMessage $R0 ${WM_SETTEXT} 0 "STR:${PRODUCT_NAME}"
|
||||
-
|
||||
+
|
||||
[% endblock mouseover_messages %]
|
||||
FunctionEnd
|
||||
-
|
||||
-Function .onInit
|
||||
- !insertmacro MULTIUSER_INIT
|
||||
-FunctionEnd
|
||||
-
|
||||
-Function un.onInit
|
||||
- !insertmacro MULTIUSER_UNINIT
|
||||
-FunctionEnd
|
||||
-
|
||||
-[% if ib.py_bitness == 64 %]
|
||||
-Function correct_prog_files
|
||||
- ; The multiuser machinery doesn't know about the different Program files
|
||||
- ; folder for 64-bit applications. Override the install dir it set.
|
||||
- StrCmp $MultiUser.InstallMode AllUsers 0 +2
|
||||
- StrCpy $INSTDIR "$PROGRAMFILES64\${MULTIUSER_INSTALLMODE_INSTDIR}"
|
||||
-FunctionEnd
|
||||
-[% endif %]
|
||||
@@ -1,7 +1,12 @@
|
||||
; This NSIS template is based on the built-in one in pynsist 2.3.
|
||||
; Added lines are enclosed within "CERTBOT CUSTOM BEGIN/END" comments.
|
||||
; If pynsist is upgraded, this template must be updated if necessary using the new built-in one.
|
||||
; This NSIS template is based on the built-in one in pynsist 2.4.
|
||||
; If pynsist is upgraded, this template may be updated if necessary using the new built-in one.
|
||||
; Original file can be found here: https://github.com/takluyver/pynsist/blob/2.4/nsist/pyapp.nsi
|
||||
; Diff file is located at .\template-nsi.patch
|
||||
|
||||
; Require the installer do be installed with admin privileges
|
||||
RequestExecutionLevel admin
|
||||
; Set default installation path (overridable with /D= flag)
|
||||
InstallDir "$PROGRAMFILES\Certbot"
|
||||
|
||||
!define PRODUCT_NAME "[[ib.appname]]"
|
||||
!define PRODUCT_VERSION "[[ib.version]]"
|
||||
@@ -11,30 +16,19 @@
|
||||
!define ARCH_TAG "[[arch_tag]]"
|
||||
!define INSTALLER_NAME "[[ib.installer_name]]"
|
||||
!define PRODUCT_ICON "[[icon]]"
|
||||
|
||||
; Marker file to tell the uninstaller that it's a user installation
|
||||
!define USER_INSTALL_MARKER _user_install_marker
|
||||
|
||||
SetCompressor lzma
|
||||
|
||||
; CERTBOT CUSTOM BEGIN
|
||||
; Administrator privileges are required to insert a new task in Windows Scheduler.
|
||||
; Also comment out some options to disable ability to choose AllUsers/CurrentUser install mode.
|
||||
; As a result, installer run always with admin privileges (because of MULTIUSER_EXECUTIONLEVEL),
|
||||
; using the AllUsers installation mode by default (because of MULTIUSER_INSTALLMODE_DEFAULT_CURRENTUSER
|
||||
; not set), and this default behavior cannot be overridden (because of MULTIUSER_MUI not set).
|
||||
; See https://nsis.sourceforge.io/Docs/MultiUser/Readme.html
|
||||
!define MULTIUSER_EXECUTIONLEVEL Admin
|
||||
;!define MULTIUSER_EXECUTIONLEVEL Highest
|
||||
;!define MULTIUSER_INSTALLMODE_DEFAULT_CURRENTUSER
|
||||
;!define MULTIUSER_MUI
|
||||
;!define MULTIUSER_INSTALLMODE_COMMANDLINE
|
||||
; CERTBOT CUSTOM END
|
||||
!define MULTIUSER_INSTALLMODE_INSTDIR "[[ib.appname]]"
|
||||
[% if ib.py_bitness == 64 %]
|
||||
!define MULTIUSER_INSTALLMODE_FUNCTION correct_prog_files
|
||||
[% endif %]
|
||||
!include MultiUser.nsh
|
||||
Function .onInit
|
||||
; Check that Powershell is at least 5.0.
|
||||
nsExec::ExecToStack `powershell -ExecutionPolicy RemoteSigned -Command "Write-Host -NoNewline (Get-Host | Select-Object Version).Version.CompareTo([System.Version]'5.0')"`
|
||||
Pop $0
|
||||
Pop $1
|
||||
StrCmp $1 "-1" 0 powershellok
|
||||
MessageBox MB_OK|MB_ICONSTOP "Certbot requires Powershell 5.0+ to work.$\r$\nPlease check the following link to know how to upgrade your system:$\r$\nhttps://docs.microsoft.com/powershell/scripting/install/installing-powershell"
|
||||
Abort
|
||||
powershellok:
|
||||
FunctionEnd
|
||||
|
||||
[% block modernui %]
|
||||
; Modern UI installer stuff
|
||||
@@ -49,10 +43,6 @@ SetCompressor lzma
|
||||
[% if license_file %]
|
||||
!insertmacro MUI_PAGE_LICENSE [[license_file]]
|
||||
[% endif %]
|
||||
; CERTBOT CUSTOM BEGIN
|
||||
; Disable the installation mode page (AllUsers/CurrentUser)
|
||||
;!insertmacro MULTIUSER_PAGE_INSTALLMODE
|
||||
; CERTBOT CUSTOM END
|
||||
!insertmacro MUI_PAGE_DIRECTORY
|
||||
!insertmacro MUI_PAGE_INSTFILES
|
||||
!insertmacro MUI_PAGE_FINISH
|
||||
@@ -60,10 +50,7 @@ SetCompressor lzma
|
||||
!insertmacro MUI_LANGUAGE "English"
|
||||
[% endblock modernui %]
|
||||
|
||||
; CERTBOT CUSTOM BEGIN
|
||||
Name "${PRODUCT_NAME} (beta) ${PRODUCT_VERSION}"
|
||||
;Name "${PRODUCT_NAME} ${PRODUCT_VERSION}"
|
||||
; CERTBOT CUSTOM END
|
||||
OutFile "${INSTALLER_NAME}"
|
||||
ShowInstDetails show
|
||||
|
||||
@@ -76,18 +63,13 @@ SectionEnd
|
||||
|
||||
Section "!${PRODUCT_NAME}" sec_app
|
||||
SetRegView [[ib.py_bitness]]
|
||||
SetShellVarContext all
|
||||
SectionIn RO
|
||||
File ${PRODUCT_ICON}
|
||||
SetOutPath "$INSTDIR\pkgs"
|
||||
File /r "pkgs\*.*"
|
||||
SetOutPath "$INSTDIR"
|
||||
|
||||
; Marker file for per-user install
|
||||
StrCmp $MultiUser.InstallMode CurrentUser 0 +3
|
||||
FileOpen $0 "$INSTDIR\${USER_INSTALL_MARKER}" w
|
||||
FileClose $0
|
||||
SetFileAttributes "$INSTDIR\${USER_INSTALL_MARKER}" HIDDEN
|
||||
|
||||
[% block install_files %]
|
||||
; Install files
|
||||
[% for destination, group in grouped_files %]
|
||||
@@ -129,14 +111,8 @@ Section "!${PRODUCT_NAME}" sec_app
|
||||
DetailPrint "Setting up command-line launchers..."
|
||||
nsExec::ExecToLog '[[ python ]] -Es "$INSTDIR\_assemble_launchers.py" [[ python ]] "$INSTDIR\bin"'
|
||||
|
||||
StrCmp $MultiUser.InstallMode CurrentUser 0 AddSysPathSystem
|
||||
; Add to PATH for current user
|
||||
nsExec::ExecToLog '[[ python ]] -Es "$INSTDIR\_system_path.py" add_user "$INSTDIR\bin"'
|
||||
GoTo AddedSysPath
|
||||
AddSysPathSystem:
|
||||
; Add to PATH for all users
|
||||
nsExec::ExecToLog '[[ python ]] -Es "$INSTDIR\_system_path.py" add "$INSTDIR\bin"'
|
||||
AddedSysPath:
|
||||
; Add to PATH for all users
|
||||
nsExec::ExecToLog '[[ python ]] -Es "$INSTDIR\_system_path.py" add "$INSTDIR\bin"'
|
||||
[% endif %]
|
||||
[% endblock install_commands %]
|
||||
|
||||
@@ -164,11 +140,9 @@ Section "!${PRODUCT_NAME}" sec_app
|
||||
WriteRegDWORD SHCTX "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}" \
|
||||
"NoRepair" 1
|
||||
|
||||
; CERTBOT CUSTOM BEGIN
|
||||
; Execute ps script to create the certbot renew task
|
||||
DetailPrint "Setting up certbot renew scheduled task"
|
||||
nsExec::ExecToStack 'powershell -inputformat none -ExecutionPolicy RemoteSigned -File "$INSTDIR\renew-up.ps1"'
|
||||
; CERTBOT CUSTOM END
|
||||
; Execute ps script to create the certbot renew & auto-update task
|
||||
DetailPrint "Setting up certbot renew & auto-update scheduled task"
|
||||
nsExec::ExecToLog 'powershell -inputformat none -ExecutionPolicy RemoteSigned -File "$INSTDIR\tasks-up.ps1" -InstallDir "$INSTDIR"'
|
||||
|
||||
; Check if we need to reboot
|
||||
IfRebootFlag 0 noreboot
|
||||
@@ -179,18 +153,16 @@ Section "!${PRODUCT_NAME}" sec_app
|
||||
SectionEnd
|
||||
|
||||
Section "Uninstall"
|
||||
; CERTBOT CUSTOM BEGIN
|
||||
; Execute ps script to remove the certbot renew task
|
||||
nsExec::ExecToStack 'powershell -inputformat none -ExecutionPolicy RemoteSigned -File "$INSTDIR\renew-down.ps1"'
|
||||
; CERTBOT CUSTOM END
|
||||
|
||||
SetRegView [[ib.py_bitness]]
|
||||
SetShellVarContext all
|
||||
IfFileExists "$INSTDIR\${USER_INSTALL_MARKER}" 0 +3
|
||||
SetShellVarContext current
|
||||
Delete "$INSTDIR\${USER_INSTALL_MARKER}"
|
||||
|
||||
Delete $INSTDIR\uninstall.exe
|
||||
; Execute ps script to remove the certbot renew & auto-update task, then delete scripts
|
||||
nsExec::ExecToLog 'powershell -inputformat none -ExecutionPolicy RemoteSigned -File "$INSTDIR\tasks-down.ps1"'
|
||||
Delete "$INSTDIR\tasks-down.ps1"
|
||||
Delete "$INSTDIR\tasks-up.ps1"
|
||||
Delete "$INSTDIR\auto-update.ps1"
|
||||
|
||||
Delete "$INSTDIR\uninstall.exe"
|
||||
Delete "$INSTDIR\${PRODUCT_ICON}"
|
||||
RMDir /r "$INSTDIR\pkgs"
|
||||
|
||||
@@ -241,20 +213,3 @@ Function .onMouseOverSection
|
||||
|
||||
[% endblock mouseover_messages %]
|
||||
FunctionEnd
|
||||
|
||||
Function .onInit
|
||||
!insertmacro MULTIUSER_INIT
|
||||
FunctionEnd
|
||||
|
||||
Function un.onInit
|
||||
!insertmacro MULTIUSER_UNINIT
|
||||
FunctionEnd
|
||||
|
||||
[% if ib.py_bitness == 64 %]
|
||||
Function correct_prog_files
|
||||
; The multiuser machinery doesn't know about the different Program files
|
||||
; folder for 64-bit applications. Override the install dir it set.
|
||||
StrCmp $MultiUser.InstallMode AllUsers 0 +2
|
||||
StrCpy $INSTDIR "$PROGRAMFILES64\${MULTIUSER_INSTALLMODE_INSTDIR}"
|
||||
FunctionEnd
|
||||
[% endif %]
|
||||
Reference in New Issue
Block a user