2023年10月28日 星期六

如何僅利用 sftp 判斷對方是 unix 還是 win

如何僅利用 sftp 判斷對方是 unix 還是 win

繼前幾篇的轉換

CHG: 在BAT中如何做 Ascii 文本的變換 更換換行符號 (Linux -> Win)
CHG: 在BAT中如何做 Ascii 文本的變換 更換換行符號 (Win -> Linux)

這篇要來講如何判斷對方是不是 Unix 伺服器,從設計上來說其實 sftp 並不具備檢測對方伺服器的標準API,只能瞎猜看看了

這邊用的判斷方式是檢測對方根目錄有沒有 etc 文件的存在,雖然無法百分百完全判定
,不過在大多數情況下足夠了



判斷的代碼

測試是否為Unix

function Test-SftpIsUnix {
    param (
        $LoginInfo
    )
    # 檢視根目錄
    $output = 'ls /' | sftp -oBatchMode=yes $LoginInfo 2>&1
    # 檢查輸出中是否有特定目錄存在
    if ($LastExitCode -eq 0) {
        $dirName = '/etc'
        if (-not ($output -match $dirName)) {
            # Write-Host "$dirName 目錄不存在,可能不是 Linux 系统。"
            return $false
        } else {
            # Write-Host "$dirName 目錄存在,可能是 Linux 系统。"
            return $true
        }
    } else {
        # Write-Error "連接失敗或無法存取跟目錄" -ErrorAction Stop
        return $null
    }
}

如此一來就可以透過該函式來判定要不要做換行代碼的轉換了



2023年10月23日 星期一

在BAT中如何做 Ascii 文本的變換 更換換行符號 (Win -> Linux)

在BAT中如何做 Ascii 文本的變換 更換換行符號 (Win -> Linux)

這是上一篇的延續 CHG: 在BAT中如何做 Ascii 文本的變換 更換換行符號 (Linux -> Win)

這邊就不多作介紹了,補上反過來的 Dos2unix 的函式,剩下自己參考上一篇

原始的 C# 函式 Dos2unix

using System.IO;
public class EOLConverter
{
    public static void Dos2unix(string sourcePath, string destinationPath)
    {
        const int readBufferSize = 16384;
        byte[] readBuffer = new byte[readBufferSize];
        byte[] writeBuffer = new byte[readBufferSize];
        using (FileStream readStream = new FileStream(sourcePath, FileMode.Open, FileAccess.Read))
        using (FileStream writeStream = new FileStream(destinationPath, FileMode.Create, FileAccess.Write))
        {
            int bytesRead;
            while ((bytesRead = readStream.Read(readBuffer, 0, readBufferSize)) > 0)
            {
                int writeIndex = 0;
                for (int i = 0; i < bytesRead; i++)
                {
                    if (readBuffer[i] != 13)
                    {
                        writeBuffer[writeIndex++] = readBuffer[i];
                    }
                }
                writeStream.Write(writeBuffer, 0, writeIndex);
            }
        }
    }
}



寫成一行的 bat

@echo off& Title EOLConverter By Charlotte
set "EOLConverter.cs=using System.IO; public class EOLConverter { public static void Dos2unix(string sourcePath, string destinationPath) { const int readBufferSize = 16384; byte[] readBuffer = new byte[readBufferSize]; byte[] writeBuffer = new byte[readBufferSize]; using (FileStream readStream = new FileStream(sourcePath, FileMode.Open, FileAccess.Read)) using (FileStream writeStream = new FileStream(destinationPath, FileMode.Create, FileAccess.Write)) { int bytesRead; while ((bytesRead = readStream.Read(readBuffer, 0, readBufferSize)) > 0) { int writeIndex = 0; for (int i = 0; i < bytesRead; i++) { if (readBuffer[i] != 13) { writeBuffer[writeIndex++] = readBuffer[i]; } } writeStream.Write(writeBuffer, 0, writeIndex); } } } }"
echo data\CRLF.txt| powershell -nop "Add-Type '%EOLConverter.cs%'; [string]$SrcPath=$input; $DstPath=$SrcPath+'.tmp'; [EOLConverter]::Dos2unix($SrcPath, $DstPath); #Move-Item -Path $DstPath -Destination $SrcPath -Force -ErrorAction Stop"
exit /b %errorlevel%



下面是完整的函式結合兩篇的 PowerShell

$EOLConverter = @"
using System.IO;
public class EOLConverter
{
    public static void Unix2dos(string sourcePath, string destinationPath)
    {
        const int readBufferSize = 16384;
        byte[] readBuffer = new byte[readBufferSize];
        byte[] writeBuffer = new byte[readBufferSize * 2];
        byte previousByte = 0;
        using (FileStream readStream = new FileStream(sourcePath, FileMode.Open, FileAccess.Read))
        using (FileStream writeStream = new FileStream(destinationPath, FileMode.Create, FileAccess.Write))
        {
            int bytesRead;
            while ((bytesRead = readStream.Read(readBuffer, 0, readBufferSize)) > 0)
            {
                int writeIndex = 0;
                for (int i = 0; i < bytesRead; i++)
                {
                    if (readBuffer[i] == 10 && previousByte != 13)
                    {
                        writeBuffer[writeIndex++] = 13;
                    }
                    writeBuffer[writeIndex++] = readBuffer[i];
                    previousByte = readBuffer[i];
                }
                writeStream.Write(writeBuffer, 0, writeIndex);
            }
        }
    }

    public static void Dos2unix(string sourcePath, string destinationPath)
    {
        const int readBufferSize = 16384;
        byte[] readBuffer = new byte[readBufferSize];
        byte[] writeBuffer = new byte[readBufferSize];
        using (FileStream readStream = new FileStream(sourcePath, FileMode.Open, FileAccess.Read))
        using (FileStream writeStream = new FileStream(destinationPath, FileMode.Create, FileAccess.Write))
        {
            int bytesRead;
            while ((bytesRead = readStream.Read(readBuffer, 0, readBufferSize)) > 0)
            {
                int writeIndex = 0;
                for (int i = 0; i < bytesRead; i++)
                {
                    if (readBuffer[i] != 13)
                    {
                        writeBuffer[writeIndex++] = readBuffer[i];
                    }
                }
                writeStream.Write(writeBuffer, 0, writeIndex);
            }
        }
    }
}
"@

Add-Type -TypeDefinition $EOLConverter
[EOLConverter]::Unix2dos("data\LF.txt", "tmp\CRLF.txt")
[EOLConverter]::Dos2unix("tmp\CRLF.txt", "tmp\LF.txt")






2023年10月22日 星期日

EverNote 如何找回舊版本載點

EverNote 如何找回舊版本載點

新版本令人詬病的地方實在太多了,雖人最近有更新的比較勤勞了,不過有一個本質上的區別是不再是線下保存筆記了,如果要保存筆記還是得裝舊版

雖然官方已經撤掉舊版的頁面不過其實載點沒撤掉,也就是能挖出連結就能從官方下載了

來自網路時光機的舊版網頁
https://web.archive.org/web/20230326100256/https://help.evernote.com/hc/en-us/articles/360052560314-Install-an-older-version-of-Evernote


Windows載點

最後一版的版號是 6.25.3.9348
https://cdn1.evernote.com/win6/public/Evernote_6.25.3.9348.exe

截止至發文當下 2023-10-23 還能下載我就不發分流了


Mac載點

蘋果的載點長這樣,不過我不清楚最後版本是多少,這個是討論串裡的人發的
https://cdn1.evernote.com/mac-smd/public/EvernoteLegacy_RELEASE_7.14.1_458325.zip



2023年10月15日 星期日

MSSQL 2022 快速建構 自簽署SSL證書

MSSQL 2022 快速建構 自簽署SSL證書

CHG: MSSQL 2022 如何創建 自簽署SSL憑證證書 導入並啟用加密連接

上一篇已經完整的解析如何建構自簽署SSL證書,這一篇是續集補上如何快速使用 PowerShell 建構的方式。不同於OpenSSL PowerShell 可以直接跳過中間步驟直接就簽署完了,步驟相對縮短很多。



伺服器端

創建證書

# 定義證書詳細資訊
$countryCode = "TW"
$state = "Taipei"
$city = "Taipei"
$companyName = "CHG"
$organizationalUnit = "CHG"
$dnsName = "192.168.3.68"

# 創建自簽名證書
$subject = "C=$countryCode, S=$state, L=$city, O=$companyName, OU=$organizationalUnit, CN=$dnsName"
$cert = New-SelfSignedCertificate -DnsName $dnsName -CertStoreLocation "cert:\LocalMachine\My" -Subject $subject -KeyExportPolicy Exportable -KeySpec Signature


給與SQLSERVER權限 (如果實例名不是預設的MSSQLSERVER記得修改)

# 將權限授權給 NT Service\MSSQLSERVER
$sqlLoginUser = "NT Service\MSSQLSERVER"
$keyPath = $cert.PrivateKey.CspKeyContainerInfo.UniqueKeyContainerName
$fullKeyPath = Join-Path "C:\ProgramData\Microsoft\Crypto\RSA\MachineKeys" $keyPath
icacls $fullKeyPath /grant "$($sqlLoginUser):F"


信任該證書 (自己連接自己用的)

# 導出公鑰到 .cer 文件
$publicKeyPath = "$env:USERPROFILE\certificate.cer"
Export-Certificate -Cert $cert -FilePath $publicKeyPath
# 將公鑰安裝到本機信任位置
Import-Certificate -FilePath $publicKeyPath -CertStoreLocation cert:\LocalMachine\Root


到這證書設置就完成了,接下來需要回到 SQLSERVER 啟用該證書。詳細可以參考文章開頭連結那篇,裡面定位到【4. 在SQLSERVER中啟用SSL證書】補一下這章節內容


設置完畢再來,自己連接自己驗證一下證書可用性

# 使用 Windows認證 驗證加密情況
sqlcmd -S . -E -Q 'SET NOCOUNT ON SELECT c.session_id, c.encrypt_option, s.login_name, c.client_net_address, s.program_name FROM sys.dm_exec_connections c JOIN sys.dm_exec_sessions s ON c.session_id = s.session_id WHERE c.session_id = @@SPID' -W

命令解析:("-S ." = 連接自己), ("-E" = 使用當前登入的Windwos用戶)


執行結果有看到第二個欄位 True 就是加密連接的意思了





客戶端

剛剛伺服器端預設是產在使用者目錄底下,自行把 cer 文件複製過來
("$env:USERPROFILE\certificate.cer")

# 定義公鑰的路徑
$publicKeyPath = ".\certificate.cer"

# 導入公鑰到受信任的根證書發行機構
Import-Certificate -FilePath $publicKeyPath -CertStoreLocation cert:\LocalMachine\Root


再來驗證一下,這邊用 sqlserver認證 比較方便

# 使用 SqlServer認證 驗證加密情況
sqlcmd -S '192.168.3.54,1433' -U 'chg' -P 'chg' -Q 'SET NOCOUNT ON SELECT c.session_id, c.encrypt_option, s.login_name, c.client_net_address, s.program_name FROM sys.dm_exec_connections c JOIN sys.dm_exec_sessions s ON c.session_id = s.session_id WHERE c.session_id = @@SPID' -W


執行這個命令需要 "檢視伺服器狀態(SERVER STATE)" 的權限,參考以下幾個方法

  • 賦予 SERVER STATE 權限
  • 從伺服器角色中給予 processadmin 或 setupadmin 權限
  • 用 sa 帳戶登入


賦予該權限的圖形介面操作可以參考這張圖





MSSQL 2022 如何創建 自簽署SSL憑證證書 導入並啟用加密連接

MSSQL 2022 如何創建 自簽署SSL憑證證書 導入並啟用加密連接

繼前幾篇


已經是可以用的狀態了,補上最後一偏安全的問題 SSL證書,這個一般是需要花錢購買的,由第三方CA機構簽名才能把自己的域名發布到公共信任清單上。本篇測試用的用自簽署證書來測試整個流程。


以下總共分成下列幾大項目

  1. 創建SSL證書
  2. 在伺服器上安裝私鑰
  3. 設置伺服器端證書權限
  4. 在SQLSERVER中啟用SSL證書
  5. 在客戶端上安裝公鑰
  6. 測試加密狀態

本篇是完全手動最麻煩的教學攻略,要快速建造SSL直接用PowerShell是最快的
詳細請參考這一篇:CHG: MSSQL 2022 快速建構 自簽署SSL證書

快速建構篇是以本篇已經完成後的狀態去做的,不過基本也包含本篇8程的內容了,唯一缺少的是【4. 在SQLSERVER中啟用SSL證書】,如果要偷吃步直接跳記得這個步驟要補上




0. 安裝環境 MSYS2 與 OpenSSL

安裝這個是因為上面有現成的 OpenSSL 可以用,這個要在Windows上官方沒有給編譯好的檔案,除了自行編譯外只能載第三方編譯的。

載點:https://www.msys2.org/#installation



打開之後直接下一步就行了,沒什麼選項



打開之後長這樣輸入該指令安裝 OpenSSL

pacman -S openssl


這樣就安裝好可以用了



1. 創建SSL證書

分以下幾個步驟創建

創建私鑰

openssl genpkey -algorithm RSA -out private_key.pem

這個執行完畢就創好了,私鑰要保存好不能公開



創建憑證請求

openssl req -new -key private_key.pem -out request.csr

這邊會需要輸入很多,唯一要注意的就是域名別亂填要填該伺服器主機的IP或域名
(這個會影響到之後接入的問題,如果IP是浮動會跑的趁現在趕緊固定一下)



簽署該請求 (本來應該是由第三方CA做的)

openssl x509 -req -days 365 -in request.csr -signkey private_key.pem -out certificate.cer

這樣就能拿到公鑰了 (這份私簽的證書不會上公服,要手動發給客戶端才能用)


這裡的私鑰其實應該是在產一個比較好,只是方便直接拿來用了
實際上提交給CA簽署的的時候,不用也不能連請求的私鑰也一起給出去

由CA簽署過的公鑰會被上傳到公共伺服器,實際上也不需要發送給客戶
只要證書還有效,客戶就能能從公共伺服器直接獲取並連上該域名



合成完整的證書

openssl pkcs12 -export -out certificate.pfx -inkey private_key.pem -in certificate.cer

這個可以把私鑰跟公鑰合併起來,合併後才是完整的證書

最小限度可以僅保存這個檔案就好,但公鑰cer建議保存,方便你發布到其他電腦
(不然丟了還要從 pfx 檔案打指令挖出來,或是安裝後按匯出公鑰)



確認檔案

最後 MSYS2 的預設使用者位置在這裡 (直接貼上就好)

C:\msys64\home\%UserName%

確認一下檔案都在這兩個框起來是需要的檔案





2. 在伺服器上安裝私鑰

安裝證書

對著pfx檔案點擊右鍵"安裝pfx"



進入安裝介面選擇"本機電腦"



選擇下一步,密碼跟設置成可匯出根據自己需求選擇



安裝到"個人"(否則在SqlServer中會看不到證書)





3. 設置伺服器端證書權限

再來需要設置權限給 SQLSserver 有權使用他

按下 CTRL+R 打開執行視窗輸入 "certlm.msc" 按下確定打開憑證管理員



然後在個人中找到剛剛安裝的證書右鍵管理私密金鑰



選擇新增



然後輸入 NT Service\MSSQLSERVER 後按下檢查名稱,之後按下確定

這裡 MSSQLSERVER 其實就是預設的實例名,如果忘了實例名
可以像圖中背景那樣從SQL設定管理員那裏抄過來就好


看一下有沒有多出 MSSQLSERVER 然後再按下確定


這樣就完成了SQL已經有權限可以使用他



4. 在SQLSERVER中啟用SSL證書

再來到 SQL 設定管理員中啟用該證書
對著通訊協定按右鍵內容 -> 選擇憑證分頁 -> 選擇你的憑證



前面一頁旗標的分頁打開強制加密



最後再把 SQLServer 重新啟動即可



至此就完成這樣伺服器端的設置了




5. 在客戶端上安裝公鑰

在客戶端上對著cer檔案按右鍵安裝憑證



選擇本機電腦



安裝到"受信任的根憑證授權單位"



這樣就完成了。 (注意這個不能裝到別的地方會無效)




6. 測試加密狀態

要測試的話可以參考這一篇
CHG: SQL Server 如何加密連接 Server/Client 端解說

這邊簡單把內容搬過來快速講一下,就是打入測試指令而已。
記得尾端要移除 -N -C 這樣才能測試伺服器端到底有沒有強制打開。

sqlcmd -S '192.168.3.54,1433' -U 'chg' -P 'chg' -Q 'SET NOCOUNT ON SELECT c.session_id, c.encrypt_option, s.login_name, c.client_net_address, s.program_name FROM sys.dm_exec_connections c JOIN sys.dm_exec_sessions s ON c.session_id = s.session_id WHERE c.session_id = @@SPID' -W


這邊有一點要注意的是連入的伺服器名稱必須跟證書上的一樣,我上面創建證書是用IP就只能用IP登入,原本其實打電腦名稱也可以登入導入後就不行了。



這邊可以看到有個 True 這個就是成功加密了

可能有人有注意到圖中是伺服器端自己連自己,這樣測試還有效嗎?答案是有效的。因為就如同剛剛所提 cer 沒有導入到"受信任的根憑證授權單位"就無法使用,自然就無法依靠安裝在"個人"中的 pfx 證書連上了。

可能會想說那我直接把 pfx 安裝在"受信任的根憑證授權單位"不就可以一舉兩得?想法很完美但 SqlServer 預設只會讀"個人"中的證書,會直接導致看不到證書無法設置定的。