Files
MoShiro/packaging/install/install.ps1
T

134 lines
6.7 KiB
PowerShell
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# MoShiro 节点一键安装(Windows,Agent 和 Probe 共用同一份)。面板在 /install.ps1?code=… 生成这份脚本,把下面四个值填好:
# irm 'https://panel/install.ps1?code=jt_…' | iex
# 镜像:向面板取 CI 签名的发布清单,用内置的发布公钥验签,再按清单里的 digest 拉取。面板还没有签名的清单时,设 $env:MOSHIRO_ALLOW_UNVERIFIED='1' 才会按标签安装。
# 做的事:用 Docker Desktop 起一个容器,身份和配置存在命名卷里(重启、换新镜像重建都不用再给参数),节点名字默认取计算机名。
# 可选环境变量:MOSHIRO_NAME=节点名字(名字被占用时用)。
function Test-MoShiroSignature([byte[]]$data, [byte[]]$derSignature, [string]$pem) {
# 发布公钥是 PEM 的 SPKI(P-256):最后 64 字节是 X、Y;签名是 DER,.NET 要 r||s
$b64 = ($pem -split "`n" | Where-Object { $_ -notmatch '^-----' }) -join ''
$spki = [Convert]::FromBase64String($b64.Trim())
$xy = $spki[($spki.Length - 64)..($spki.Length - 1)]
$blob = [byte[]]@(0x45, 0x43, 0x53, 0x31, 0x20, 0, 0, 0) + $xy
$i = 2
if ($derSignature[1] -band 0x80) { $i = 2 + ($derSignature[1] -band 0x7f) }
$parts = @()
for ($n = 0; $n -lt 2; $n++) {
$len = $derSignature[$i + 1]
$part = [byte[]]$derSignature[($i + 2)..($i + 1 + $len)]
while ($part.Length -gt 32 -and $part[0] -eq 0) { $part = [byte[]]$part[1..($part.Length - 1)] }
if ($part.Length -gt 32) { return $false }
$parts += , ([byte[]](New-Object byte[] (32 - $part.Length)) + $part)
$i += 2 + $len
}
$key = [Security.Cryptography.CngKey]::Import($blob, [Security.Cryptography.CngKeyBlobFormat]::EccPublicBlob)
try {
$ecdsa = New-Object Security.Cryptography.ECDsaCng($key)
return $ecdsa.VerifyData($data, [byte[]]($parts[0] + $parts[1]), [Security.Cryptography.HashAlgorithmName]::SHA256)
} finally { $key.Dispose() }
}
function Install-MoShiro {
$ErrorActionPreference = 'Stop'
$role = '@@ROLE@@'
$server = '@@SERVER@@'
$code = '@@CODE@@'
$image = '@@IMAGE@@'
$releaseKey = @'
@@RELEASE_KEY@@
'@
$name = if ($env:MOSHIRO_NAME) { $env:MOSHIRO_NAME } else { $env:COMPUTERNAME }
$name = $name -replace '[^A-Za-z0-9._-]', '-'
if ($name.Length -gt 64) { $name = $name.Substring(0, 64) }
if (-not $name) { $name = 'node' }
if (-not (Get-Command docker -ErrorAction SilentlyContinue)) {
Write-Error 'Docker Desktop is required: https://docs.docker.com/desktop/setup/install/windows-install/'
return
}
docker info *> $null
if ($LASTEXITCODE -ne 0) {
Write-Error 'Docker is installed but not running: start Docker Desktop and run this again'
return
}
$container = "moshiro-$role"
$volume = "moshiro-$role-config"
docker inspect $container *> $null
if ($LASTEXITCODE -eq 0) {
docker start $container | Out-Null
Write-Host "$container is already installed and running."
Write-Host "reinstall: docker rm -f $container; docker volume rm $volume (then run the install command again)"
return
}
# 验发布清单:签名对不上、组件不对、digest 格式不对都直接拒绝,不装
$client = New-Object System.Net.WebClient
$manifest = $null
try {
$manifest = $client.DownloadData("$server/install/manifest?code=$code")
$signature = [Convert]::FromBase64String(($client.DownloadString("$server/install/manifest.sig?code=$code")).Trim())
} catch {
$manifest = $null
}
if ($null -eq $manifest) {
if ($env:MOSHIRO_ALLOW_UNVERIFIED -ne '1') {
Write-Error "the panel has no signed release for $role yet, so the image cannot be verified. Run again with `$env:MOSHIRO_ALLOW_UNVERIFIED='1' to install $image without verification"
return
}
Write-Host "warning: installing $image without signature verification"
} else {
if (-not (Test-MoShiroSignature $manifest $signature $releaseKey)) {
Write-Error 'the release signature does not verify against the built-in release key; refusing to install'
return
}
$release = [Text.Encoding]::UTF8.GetString($manifest) | ConvertFrom-Json
if ($release.component -ne $role) { Write-Error "the signed release is for '$($release.component)', not '$role'"; return }
if ($release.digest -notmatch '^sha256:[0-9a-f]{64}$') { Write-Error 'the signed release has a malformed digest'; return }
if ($release.image -notmatch '^[A-Za-z0-9._/:-]+$') { Write-Error 'the signed release has a malformed image name'; return }
$image = "$($release.image)@$($release.digest)"
Write-Host "verified release $($release.version) ($($release.tag)): $image"
}
Write-Host "pulling $image"
docker pull $image | Out-Null
if ($LASTEXITCODE -ne 0) {
Write-Error "could not pull $image (private registry? run: docker login $($image.Split('/')[0]))"
return
}
$arguments = @('run', '-d', '--name', $container, '--restart', 'unless-stopped',
'--read-only', '--tmpfs', '/tmp:rw,noexec,nosuid,size=64m', '--cap-drop', 'all', '--security-opt', 'no-new-privileges',
'-v', "${volume}:/etc/moshiro")
# Docker Desktop 没有宿主网络:Agent 把端口发布出来(面板里的地址需要指向这台机器的公网地址)
if ($role -eq 'agent') { $arguments += @('-p', '7443:7443/tcp', '-p', '7443:7443/udp', '-p', '7444:7444/tcp') }
$arguments += @('-e', "MOSHIRO_SERVER=$server", '-e', "MOSHIRO_JOIN=$code", '-e', "MOSHIRO_NAME=$name", $image)
Write-Host "starting $container as '$name'"
docker @arguments | Out-Null
if ($LASTEXITCODE -ne 0) {
Write-Error 'docker run failed'
return
}
# 等注册完成:第一次启动会用上面的令牌注册,成功之后才算装好
for ($i = 0; $i -lt 30; $i++) {
$running = docker inspect -f '{{.State.Running}}' $container 2>$null
$logs = (docker logs $container 2>&1) -join "`n"
if ($logs -match 'link up') {
Write-Host "done: $role '$name' is online. It shows up in the panel now."
Write-Host "logs: docker logs -f $container"
return
}
if ($running -ne 'true') {
($logs -split "`n" | Select-Object -Last 8) -join "`n" | Write-Host
Write-Error "$container stopped. If the name '$name' is taken, run again with `$env:MOSHIRO_NAME='<other-name>'. To start over: docker rm -f $container; docker volume rm $volume"
return
}
Start-Sleep -Seconds 1
}
Write-Host "$container is running, but has not reported online yet. Check: docker logs $container"
}
Install-MoShiro