zscripts-token-savers/zsetup_mail.ps1
kellymichels 91b638ac31
feat: checksums, toolkit versioning (v{major}.{rc}.{beta}.{alpha}.{build}), and downloadable release zips (#35)
* feat(zchecksums): SHA-256 manifest so a download can be verified before it's run

CHECKSUMS.txt lists a SHA-256 for every top-level .ps1 and .cmd - the files a
user actually executes. zchecksums verifies them; zchecksums -Update
regenerates after an intentional edit.

The manifest is sha256sum format, so 'sha256sum -c CHECKSUMS.txt' works on
Linux/macOS/WSL as well as the PowerShell path on Windows. Hashes are identical
on every platform because .gitattributes pins .ps1/.cmd to CRLF everywhere -
that pin is now load-bearing, so it is commented as such.

Beyond changed and missing files it also reports a script that is on disk but
NOT in the manifest, so something added outside a commit still gets noticed.
Exits non-zero on any of the three.

Honest about its limits, in the header and the README: the manifest lives in
the same repo as the code, so it is an integrity check rather than a signature.
It catches a truncated clone, a forgotten local edit, or an unlisted file - not
a compromised repo.

CHECKSUMS.txt is pinned to LF: sha256sum treats a trailing CR as part of the
filename and would report every entry as missing on Linux.

tests/Checksums.Tests.ps1 keeps it from rotting - a stale manifest is worse
than none, since it either cries wolf until people ignore it or quietly stops
covering a new script. The tests assert the format, LF endings, sort order,
full coverage of on-disk scripts, current hashes, and that zchecksums itself
exits 1 on a tampered file (proved by appending a byte and restoring it).

* feat(zversion, zrelease): toolkit versioning + downloadable release zips

Implements the versioning rule (SmartPlant's 5-segment scheme, now the global
standard; currently only sp and zscripts are on it at v1.x):

    v{major}.{rc}.{beta}.{alpha}.{build}

zversion: get / bump / bump-stage / set. A stage bump zeroes every lower
segment including build. 'bump' is one per PR and one per defect fix, not per
file. Any write rewrites three things together, because they are only useful
when they agree: build-version.json (source of truth), a '# Version:' line in
all 42 script headers (a lone copied script still says which release it came
from), and CHECKSUMS.txt (stamping changes every file).

zrelease: packages the current version as releases/zscripts-<version>.zip with
a sibling .sha256, for people who want the toolkit without cloning. One hash
verifies the download; the bundled CHECKSUMS.txt verifies the extracted
contents. Refuses to overwrite an existing version's zip (released = immutable;
bump instead), and refuses to package when zchecksums fails. tests/ excluded
from the zip; releases/ never packages itself.

First release included: releases/zscripts-v1.0.0.0.0.zip (42 scripts + 7
support files) and its .sha256.

.gitattributes: releases/*.sha256 pinned LF (sha256sum treats a trailing CR as
part of the filename), releases/*.zip marked binary.

Verified end-to-end as a downloader would experience it, in WSL: sha256sum -c
on the zip passes, unzip, sha256sum -c CHECKSUMS.txt inside gives 42 OK / 0
FAILED, and the extracted zdeploy.ps1 header and build-version.json both read
v1.0.0.0.0. Double-release guard and -Verify mode exercised. Full Pester suite
219/219 (the checksum tests absorb the new files automatically).
2026-07-28 13:47:25 -05:00

119 lines
5.5 KiB
PowerShell

# Evomedia.net Token Savers — https://github.com/kellymichels/zscripts-token-savers
# Created by Kelly Michels · dev@evomedia.net
# Licensed under the MIT License. See LICENSE.
# Version: v1.0.0.0.0
# zsetup_mail.ps1 — create admin@ and noreply@ mailboxes in a docker-mailserver
# container on the server, and print the DNS records + SMTP/IMAP settings to use.
#
# Usage:
# zsetup_mail.ps1 -domain yourdomain.com [-mailHost mail.yourdomain.com] [-ec2Host <ip>] [-pemKey <path>]
#
param (
[string]$domain = "",
[string]$mailHost = "",
[string]$ec2Host = "",
[string]$pemKey = ""
)
$ErrorActionPreference = "Stop"
. (Join-Path $PSScriptRoot "ZHelpers.ps1")
Start-ZTracking
$cfg = Get-ZConfig
if (-not $ec2Host) { $ec2Host = $cfg.ec2.ip }
if (-not $pemKey) { $pemKey = $cfg.ec2.pemKey }
if ([string]::IsNullOrWhiteSpace($domain)) {
Write-Host ""
Write-Host "Usage: zsetup_mail.ps1 -domain yourdomain.com [-mailHost mail.yourdomain.com]" -ForegroundColor Yellow
Write-Host " Creates admin@ and noreply@ mailboxes on the server's mailserver container." -ForegroundColor Gray
Stop-ZTracking; exit 1
}
if (-not $mailHost) { $mailHost = "mail.$domain" }
Write-Host "=== Mail Setup Utility ===" -ForegroundColor Cyan
if (-not (Test-Path $pemKey)) {
Write-Error "PEM key not found: $pemKey"
exit 1
}
Write-Host "Using PEM Key: $pemKey" -ForegroundColor Gray
Write-Host "Target Host : $ec2Host" -ForegroundColor Gray
Write-Host "Domain Name : $domain" -ForegroundColor Gray
function Generate-Password {
$chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#%^&*"
$random = New-Object System.Random
$password = ""
for ($i = 0; $i -lt 16; $i++) {
$password += $chars[$random.Next(0, $chars.Length)]
}
return $password
}
$adminPassword = Generate-Password
$noreplyPassword = Generate-Password
Write-Host "`n[1/3] Connecting to the mail server to provision mailboxes..." -ForegroundColor Yellow
$SshOpts = @("-o", "ConnectTimeout=15", "-o", "StrictHostKeyChecking=no", "-i", $pemKey, "$($cfg.ec2.user)@$ec2Host")
function Exec-Remote ([string]$cmd) { & ssh @SshOpts $cmd }
Write-Host "Adding email account: admin@$domain..." -ForegroundColor Gray
Exec-Remote "sudo docker exec mailserver setup email add admin@$domain '$adminPassword'"
if ($LASTEXITCODE -eq 0) {
Write-Host "Account admin@$domain added successfully." -ForegroundColor Green
} else {
Write-Warning "Could not add admin account (it might already exist or docker setup failed)."
}
Write-Host "Adding email account: noreply@$domain..." -ForegroundColor Gray
Exec-Remote "sudo docker exec mailserver setup email add noreply@$domain '$noreplyPassword'"
if ($LASTEXITCODE -eq 0) {
Write-Host "Account noreply@$domain added successfully." -ForegroundColor Green
} else {
Write-Warning "Could not add noreply account (it might already exist or docker setup failed)."
}
Write-Host "`n[2/3] DNS Configuration Requirements" -ForegroundColor Yellow
Write-Host "Add or update the following records in your DNS zone for ${domain}:" -ForegroundColor Gray
Write-Host "--------------------------------------------------------------------------------" -ForegroundColor Cyan
Write-Host "1. MX Record (Inbound mail routing):" -ForegroundColor White
Write-Host " - Name : (leave blank or @)" -ForegroundColor Green
Write-Host " - Type : MX" -ForegroundColor Green
Write-Host " - Value : 10 $mailHost" -ForegroundColor Yellow
Write-Host "2. TXT Record (SPF authorization):" -ForegroundColor White
Write-Host " - Name : (leave blank or @)" -ForegroundColor Green
Write-Host " - Type : TXT" -ForegroundColor Green
Write-Host " - Value : `"v=spf1 mx ~all`"" -ForegroundColor Yellow
Write-Host "3. A Record for Webmail:" -ForegroundColor White
Write-Host " - Name : webmail" -ForegroundColor Green
Write-Host " - Type : A" -ForegroundColor Green
Write-Host " - Value : $ec2Host" -ForegroundColor Yellow
Write-Host "4. A Record for Mail Admin:" -ForegroundColor White
Write-Host " - Name : mail-admin" -ForegroundColor Green
Write-Host " - Type : A" -ForegroundColor Green
Write-Host " - Value : $ec2Host" -ForegroundColor Yellow
Write-Host "--------------------------------------------------------------------------------" -ForegroundColor Cyan
Write-Host "`n[3/3] Application Connection Credentials" -ForegroundColor Yellow
Write-Host "Use these connection settings in your app config or .env:" -ForegroundColor Gray
Write-Host "--------------------------------------------------------------------------------" -ForegroundColor Cyan
Write-Host "SMTP Hostname : $mailHost" -ForegroundColor White
Write-Host "SMTP Port (STARTTLS) : 587" -ForegroundColor White
Write-Host "SMTP Port (SSL/TLS) : 465" -ForegroundColor White
Write-Host "IMAP Hostname : $mailHost" -ForegroundColor White
Write-Host "IMAP Port (SSL/TLS) : 993" -ForegroundColor White
Write-Host "--------------------------------------------------------------------------------" -ForegroundColor Cyan
Write-Host "Email User 1 : admin@$domain" -ForegroundColor Green
Write-Host "Password : $adminPassword" -ForegroundColor Yellow
Write-Host ""
Write-Host "Email User 2 : noreply@$domain" -ForegroundColor Green
Write-Host "Password : $noreplyPassword" -ForegroundColor Yellow
Write-Host "--------------------------------------------------------------------------------" -ForegroundColor Cyan
Write-Host "Completed successfully!" -ForegroundColor Green
Stop-ZTracking