Ismert ssh gazdagépek elemzése PowerShell és reguláris kifejezésekkel
Az utóbbi időben azzal töltöttem az időt, hogy többet tanuljak az ssh-ről. Sajnos ritkán volt szükségem az ssh megtanulására és használatára. De természetesen a PowerShell 7 és az ssh-alapú távvezérlés révén ideje feljavítani a játékomat. Elkezdtem telepíteni az ssh-kiszolgáló összetevőt a Windows tesztkiszolgálóira (erről egy másik napon írok), és megvizsgálom, hogyan használhatom az ssh-t az automatizálási és szkriptelési munkám során.
A legutóbbi tesztelés során megpróbáltam csatlakozni egy távoli gazdagéphez, ahol újratelepítettem az ssh-t. Ez biztonsági figyelmeztetést eredményezett, amely szerint az ujjlenyomatok nem egyeznek az aktuális ismert gazdagép-bejegyzéssel. Így végre eljutottam ahhoz, hogy megtanuljam, hol tárolják a távoli gazdagép konfigurációját. (Teljesen elismerem, hogy többet nem tudok az ssh-ről, mint amit csinálok.) Az ssh kliens a távoli gazdagép információit egy szöveges fájlban tárolja az .ssh könyvtárban, amely a felhasználó saját mappájának gyökerében található. Erre a fájlra ~\.ssh\known_hosts néven hivatkozhat.
Ha megnyitja a fájlt egy szövegszerkesztőben, valami ilyesmit fog látni:
localhost ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBGCbSDjrVNB/oGITy7qKovJam+k2HKYCtJzyiYTjevW/mYIF5umy/1eOG7Nb2AtNgpI0p6ahZtChttdT/hcZfAU=
192.168.3.199 ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBOZuPhlOpxsbkWNT2YKr4OgjSz05B0CYfHbmPlFeshOVo3r5GaXDZ+N5aRfbbPf6VYqMK/XPgO7VQ2rNcHdAxOw=
192.168.3.200 ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBOmQ3X0Xa74ntBTAbTwCK64ZkQqkU2q0xBNWAp00xAMbSNwAvWMxysq6zWkHUx0/+yS/Rewxs8vjiHpzhuf+f5A=Ez a tartalom a Windows 10 asztalon lévő fájlból. Egyes ügyfeleknél a tartalom a következő formában jelenhet meg:
|1|p+R0sPINIOBsfxHaXM42p+ndtJY=|6TxeZvVc26Emf4yLsKnLJjdxd2w= ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBO5YNHNaeOKpLorx4BqqXRBr/kK2+ZEd1OKHMSmozLLt7yS4hEHVG5Dg71qBVHZiE1dIUxeMmNEgexix4Odj/hM=
|1|0yLDq/BJ0VCDcNlshrO5ngGMLOQ=|hhWDD/C5KvfhpYzA+6q6XgEgMMA= ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBHswks7/UNgwKica/vF0cswPYoEVuMznbLKXLRXs+WWAULXZeEcUEgHm5NDeHGs+oU+7UeIoQ8xjiwl8T7MF/iw=Az ssh_config fájlban található egy HashKnownHosts nevű beállítás. Az én Windows rendszeremen ez úgy van beállítva, hogy tudja, így a gazdagép információi egyszerű szövegben kerülnek tárolásra. Ellenkező esetben kivonatolt bejegyzést kap az ismert_hosts mezőben, és nincs mód a folyamat visszafordítására. Ezt tartsa szem előtt, miközben folytatom.
Egy kis kutatás után megtudtam, hogy van egy szerkezete az ismert_hosts fájlnak. Sajnos ez nem olyan egyszerű, mint egy szóközzel tagolt fájl. Bár ezt kipróbálhatnád a fájlodban.
$known = "~\.ssh\known_hosts"
Import-CSV $known -Delimiter " " -Header "Hostname","Keytype","Thumbprint"Előfordulhat azonban, hogy a következő eredményeket kapja:
Az én esetemben voltak bejegyzéseim portokkal, gazdagépnevek szögletes zárójelekkel, és néhány bejegyzésem opcionális IP-címmel. De nem számít. Ha ismerem a struktúrát, minden bejegyzést reguláris kifejezésekkel elemelhetek, és létrehozhatok egy egyéni objektumot. És ezt tettem.
Reguláris kifejezések rögzítése
A folyamat egyszerűsítése érdekében (őszintén szólva!) kidolgoztam egy reguláris kifejezés-mintát, amely elnevezett rögzítéseket használ.
[regex]$rx = "^(?<host>^\S+?)((?=:))?((?<=:)(?<port>\d+))?(,(?<address>\S+))?\s(?<type>[\w-]+)\s(?<thumbprint>.*)"A minta a host nevű elnevezésű rögzítés meghatározásával kezdődik, amely megfelel a nem szóköz karakterek csoportjának, de egy kevésbé mohó egyezést használ.
^(?<host>^\S+?)Ezt a mintát követheti egy kettőspont. Ez egy pozitív előretekintés.
((?=:)A második rögzítés a portra vonatkozik, amely opcionális lehet. Olyan számcsoportot keresek, amely előtt kettőspont áll. Ez egy betekintés a mögé. És ez az egész rögzítés opciókból áll.
((?<=:)(?<port>\d+))?Hasonlóképpen előfordulhat, hogy egy IP-cím előtt vessző szerepel.
(,(?<address>\S+))?Akkor tudom, hogy egy szóköznek (\s) kell lennie, majd az ujjlenyomat típusának, amelyet tetszőleges szókarakterként és kötőjelként definiálok.
(?<type>[\w-]+)
Van még egy szóköz (\s), a többi pedig az ujjlenyomat.
(?<thumbprint>.*)A szövegfájl feldolgozásához úgy találtam, hogy a legegyszerűbb karakterláncok tömbjévé alakítani.
$content = (Get-Content -Path $known) -split "`n"Most minden bejegyzésnél kipróbálhatom a regex-mintát.
Minden elnevezett rögzítés egy csoport.
Könnyen megszerezhetem az egyes elnevezett felvételeket, így:
$matched.groups["host"].value -replace ":$|\[|\]", ""A házigazda esetében a szögletes zárójeleket és az esetleges kettőspontokat lecserélem, semmivel. Valószínűleg van mód erre a reguláris kifejezés rögzítésével, de ez elég bonyolult, és egy ilyen kis plusz lépés nem nagy baj. Ezzel a technikával egyéni objektumot készíthetek.
[pscustomobject]@{
PSTypeName = "sshKnownHost"
Hostname = $sshHost
Port = $matched.groups["port"].value
Address = $IP
Keytype = $matched.groups["type"].value
Thumbprint = $matched.groups["thumbprint"].value
}Meghatároztam egy típusnevet az sshKnownHost nevű egyéni objektumhoz. Ezt azért tettem, hogy ha akarok, létrehozhassak egyéni formátumot vagy gépelhessek bővítményeket.
Itt van a teljes funkció, amelyet a GitHubon lényegében találhat meg.
#requires -version 5.1
Function Get-sshKnownHost {
<#
.Synopsis
Parse ssh known hosts
.Description
This command will parse the ssh known_hosts file and write custom objects to
the pipeline. If HashKnownHosts is set to yes in ssh_config, you may not get
meaningful results.
.Parameter Hostname
Specify a hostname. Wildcards are permitted. Leave blank to see all known
ssh hosts.
.Example
PS C:\> Get-sshKnownHost
Hostname : localhost
Port :
Address :
Keytype : ecdsa-sha2-nistp256
Thumbprint : AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBGCbSDjrVN
B/oGITy7qKovJam+k2HKYCtJzyiYTjevW/mYIF5umy/1eOG7Nb2AtNgpI0p6ah
ZtChttdT/hcZfAU=
Hostname : 192.168.3.199
Port :
Address :
Keytype : ecdsa-sha2-nistp256
Thumbprint : AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBOZuPhlOpx
sbkWNT2YKr4OgjSz05B0CYfHbmPlFeshOVo3r5GaXDZ+N5aRfbbPf6VYqMK/XP
gO7VQ2rNcHdAxOw=
...
.Example
PS C:\> Get-KnownHost -hostname srv*
Hostname : srv1
Port :
Address : 192.168.3.50
Keytype : ecdsa-sha2-nistp256
Thumbprint : AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBP6unwvoWX
AbQ2ymqO3/TB2zSBayXP1ke2J+YxxOe57WoJ9ZEWdDyNdXwjYPzO139eVa8gFz
gPSV4DgDm/hLfYM=
Hostname : srv2
Port :
Address : 192.168.3.51
Keytype : ecdsa-sha2-nistp256
Thumbprint : AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBKcUMuWoDN
oM0RojPT+p/z8F2N7pPychlc49oTvCsGH5urCaTu6R4Fu9tctxLvuPKRIpl08+
DRTnXOGI3m/wDbw=
.Notes
Learn more about PowerShell: https://jdhitsolutions.com/blog/essential-powershell-resources/
.Link
ssh-keygen
.Inputs
none
.Outputs
sshKnownHost
#>
[cmdletbinding()]
[alias("gkh")]
[Outputtype("sshKnownHost")]
Param(
[Parameter(Position = 0, HelpMessage = "Specify a hostname. Wildcards are permitted. Leave blank to see all known hosts.")]
[string]$Hostname
)
Write-Verbose "Starting $($MyInvocation.MyCommand)"
#define a list to hold host data
$data = [System.Collections.Generic.list[object]]::New()
[regex]$rx = "^(?<host>^\S+?)((?=:))?((?<=:)(?<port>\d+))?(,(?<address>\S+))?\s(?<type>[\w-]+)\s(?<thumbprint>.*)"
$known = "~\.ssh\known_hosts"
Write-Verbose "Testing for $known"
if (Test-Path $known) {
$content = (Get-Content -Path $known) -split "`n"
Write-Verbose "Found $($content.count) entries"
#process all entries even if searching for a single hostname because there
#might be multiple entries
foreach ($entry in $content) {
$matched = $rx.Match($entry)
$sshHost = $matched.groups["host"].value -replace ":$|\[|\]", ""
$IP = $matched.groups["address"].value -replace "\[|\]", ""
Write-Verbose "Processing $sshHost"
#regex named captures are case-sensitive
#I haven't perfected the regex capture so I'll manually trim and trailing : in the hostname capture
$obj = [pscustomobject]@{
PSTypeName = "sshKnownHost"
Hostname = $sshHost
Port = $matched.groups["port"].value
Address = $IP
Keytype = $matched.groups["type"].value
Thumbprint = $matched.groups["thumbprint"].value
}
#add each new object to the list
$data.Add($obj)
} #foreach entry
}
else {
Write-Warning "Can't find $known. Is the ssh client installed?"
}
if ($Hostname -AND $data.count -gt 0) {
Write-Verbose "Searching for $hostname"
$data.where({$_.hostname -like $hostname})
}
elseif ($data.count -gt 0) {
$data
}
Write-Verbose "Ending $($MyInvocation.MyCommand)"
} #close function
Ez továbbra is működik kivonatolt bejegyzésekkel.
Az utolsó bejegyzés egyszerű szöveges, mert megváltoztattam a konfigurációs fájlt. De most van egy PowerShell eszközöm, amelyet egyszerűen használhatok a konzolról.
Összegzés
Nincsenek illúzióim, hogy ez a legjobb út a célomhoz. Azt is tudom, hogy valószínűleg lehetséges egy alternatív regex minta létrehozása. De tudom, hogy ezen dolgozva felépítették a regex izmaim, és a végeredmény egy PowerShell eszköz, amely kielégíti az igényeket. A PowerShell a folyamatban lévő objektumokról szól. Ideális esetben nem szeretne szöveget elemezni, kivéve, ha objektumok létrehozásához szöveget kell elemeznie, ahogyan ezt ezzel a funkcióval tettem.
Ha ssh-t használ, remélem megadja a funkciót, és megpróbálja elmondani, hogyan működik az Ön számára. Egyéb kérdéseket vagy megjegyzéseket azzal kapcsolatban, hogy mit csináltam vagy miért, a szokásos módon várjuk.





