---
# System prepended metadata

title: 遠端連線與傳輸

---

# 遠端連線與傳輸
> 維護網路伺服器最簡單的方式不是跑去實體伺服器前面登入，而是透過遠端連線伺服器連線功能來登入主機， 然後再來進行其他有的沒的維護就是了。[name=鳥哥]
- Book mode: https://hackmd.io/@ncnu-opensource/book

[TOC]
## 為什麼需要遠端連線
### 情境
- 在學校想用家裡的好電腦
- 想使用 server 但又不能直接進去機房
### 為什麼要遠端連線
- 更好的運算能力
- 方便管理主機
- 透過遠端連線伺服器的服務

## 遠端連線方式概覽
1. 文字介面明文傳輸： telnet, rsh (remote shell) 等為主，目前非常少用；
2. 文字介面加密傳輸： ssh 為主，是 telnet, rsh 等明文連線技術的替代品；
3. 圖形介面： VNC, RDP 等較為常見

### Telnet
- 比較古老的系統環境會支援
- 使用明文來傳輸資料(未加密)
  - 可以利用監聽軟體 (e.g tcpdump ) 的監聽竊取
  - 例子: 遠端連線進主機時，需要輸入帳號密碼，若沒有加密，帳號密碼都會被別人看光光
- 還有哪些明文傳送？
	- rsh 過時的遠端命令執行工具
	- ftp 用於電腦之間傳輸檔案的標準網路通訊協定
	- http 用於在客戶端（如網頁瀏覽器）和伺服器之間傳輸超媒體文件，如 HTML 頁面、圖像、影片等。

---

# SSH 網路傳輸協定
- 全名 : **S**ecure **Sh**ell 安全外殼協定
- 解決 telnet 明文傳輸的問題，但不代表使用 SSH 就不會被干擾、竊聽甚至解密
- 使用**非對稱式加密**方式做使用者驗證
:::info
### 不同加密方式
1. 對稱式加密：雙方都使用同一把密鑰加解密
![](https://i.imgur.com/wM9nfL4.png)
> 圖片來源 [基礎密碼學(對稱式與非對稱式加密技術). | by Chan @RiverChan | Medium](https://medium.com/@RiverChan/%E5%9F%BA%E7%A4%8E%E5%AF%86%E7%A2%BC%E5%AD%B8-%E5%B0%8D%E7%A8%B1%E5%BC%8F%E8%88%87%E9%9D%9E%E5%B0%8D%E7%A8%B1%E5%BC%8F%E5%8A%A0%E5%AF%86%E6%8A%80%E8%A1%93-de25fd5fa537)
+ 對稱式加密最大的弱點是，要是這把鑰匙在第一次傳送時，被中間人攔截且複製，再原封不動傳給接收方，那之後所有攔截到的加密訊息，都能被輕易破解
2. 非對稱式加密 (Asymmetric encryption)
+ 雙方各有一對公私鑰，互換公鑰後用對方的公鑰加密
+ 公鑰 (public key)：提供給遠端主機進行資料加密的行為，也就是說，**大家都能取得你的公鑰來將資料加密**的意思；
+ 私鑰 (private key)：遠端主機使用你的公鑰加密的資料，在本地端就能夠使用私鑰來進行解密。由於私鑰是這麼的重要， **因此私鑰是不能夠外流的！只能保護在自己的主機上**。

![](https://i.imgur.com/4rlNFQe.png)
> 圖片來源 [基礎密碼學(對稱式與非對稱式加密技術). | by Chan @RiverChan | Medium](https://medium.com/@RiverChan/%E5%9F%BA%E7%A4%8E%E5%AF%86%E7%A2%BC%E5%AD%B8-%E5%B0%8D%E7%A8%B1%E5%BC%8F%E8%88%87%E9%9D%9E%E5%B0%8D%E7%A8%B1%E5%BC%8F%E5%8A%A0%E5%AF%86%E6%8A%80%E8%A1%93-de25fd5fa537)
+ 若單純用公私鑰，仍然會有被偽造的風險
    + 數位簽章可解決
        + 傳送方除了使用接收方的公鑰加密外，也使用自己的私鑰對該封加密訊息的 Hash 簽名，以達到二次驗證
       
> FIXME: 多講數位簽章跟加密的東東
:::

:::info
### Hash 雜湊函式
$H(x) = x \mod B$
- 將不定長度訊息的輸入，演算成固定長度雜湊值的輸出[^hash]
    - 由雜湊值是無法反推出原來的訊息
    - 雜湊值必須隨明文改變而改變
:::
[^hash]: [[資料結構] 雜湊 (Hash) - iT 邦幫忙::一起幫忙解決難題，拯救 IT 人的一天](https://ithelp.ithome.com.tw/articles/10208884)

- SSH 的驗證方法
    1. 基於密碼的安全驗證
        - 知道帳號和密碼，就可以登入到遠端主機
    2. 基於金鑰的安全驗證
        - 使用者將公鑰放在需要存取的伺服器上
        - 使用者端提出身份驗證，告訴伺服器：「我要用 **某個公開金鑰** 登入」
        - 伺服器檢查這個公鑰是否存在於 `~/.ssh/authorized_keys`
        - 伺服器隨機產生一段訊息，加密後送給使用者端，並要求使用者端用 **自己的私鑰 (private key)** 來「簽署」這段挑戰
        - 若使用者端成功解開質詢，代表使用者端確實擁有私鑰 → 登入成功

---

假設今天我想要使用 SSH 傳一個訊息給 BT， 我們 ==*"已經互相擁有對方的公鑰"*== 了 ，並且擁有各自的私鑰，此時會有五個步驟：
1. 我用自己的私鑰將訊息簽名。
2. 我用 BT 的公鑰將訊息加密。（此時，被加密的訊息連我自己也無法還原-只有 BT 的私鑰可以！）
3. 我將訊息傳給 BT。
4. BT 將訊息用自己的私鑰解密。
5. BT 用我的公鑰來確認這個訊息是用我的私鑰簽名的。

上面的例子聽起來萬無一失，但是假設今天我與 BT 還沒有交換公鑰，他們就必須要先連線傳送公鑰給對方。如果壞人屁眼在我要把公鑰傳給 BT 時調包，換成屁眼自己的公鑰，這時與 BT 建立「安全連線」的人，就會是屁眼而不是我了，SSH 能夠提供的保護只在確保公鑰的擁有者同時擁有相對應的私鑰，但是不能保證擁有這個公私鑰組合的人是不是壞人。這就是著名的 **「中間人攻擊」(man-in-the-middle-attack)**。

:::info
### 中間人攻擊
![image alt](https://upload.wikimedia.org/wikipedia/commons/thumb/e/e7/Man_in_the_middle_attack.svg/800px-Man_in_the_middle_attack.svg.png?20110724142026 "title" =640x454)
> 圖片來源 [File:Man in the middle attack.svg](https://commons.wikimedia.org/w/index.php?title=File:Man_in_the_middle_attack.svg&oldid=676928010). by [Miraceti](https://commons.wikimedia.org/wiki/User:Miraceti) . Wikimedia Commons
    > - 圖中 Mallory 是密碼學中常用來代表「惡意的攻擊者」虛構角色的人名

 - **中間人攻擊** (**M**an-**i**n-**t**he-**M**iddle Attack, 縮寫: **MITM**)
 - 在密碼學和電腦安全領域中常提到的一個概念
 - 指攻擊者與通訊的兩端分別建立獨立的聯絡，並交換其所收到的資料，使通訊的兩端認為他們正在通過一個私密的連接與對方直接對話，但事實上整個對談都被攻擊者完全控制。
:::

:::warning
**我用我自己設定好的帳密登入，不就是用同一把密鑰登入，算不算對稱式加密？** 
:::

---

## SSH 連線完整流程
### 登場角色
+ Client：你輸入 `ssh user@server` 的電腦
+ Server：伺服器，第一次被連線

### 1. TCP 連線
Client 與 Server 透過 TCP 建立連線（通常在 port 22）

### 2. 協商與版本交換
雙方交換 SSH 協議版本與支援的演算法清單（加密、壓縮、MAC…），例：
+ Client：`SSH-2.0-OpenSSH_9.7`
+ Server：`SSH-2.0-OpenSSH_8.2`

### 3. Host Key 驗證 (伺服器身分驗證)
+ Server 把它的 **主機公鑰 (Host Public Key)** 傳給 Client。
+ Client 端檢查：
    + **第一次連線** → Client 本地 `~/.ssh/known_hosts` 沒有記錄 → 跳出提示：
    ```bash=
    The authenticity of host 'server (192.168.x.x)' can't be established.
    ECDSA key fingerprint is SHA256:xxxx...
    Are you sure you want to continue connecting (yes/no/[fingerprint])?
    ```
    + 使用者如果選「yes」→ 伺服器的 host key 會被寫入 `~/.ssh/known_hosts`。
+ 之後每次連線都會比對 host key，若不同就會警告（避免遭到中間人攻擊 MITM）。

**這一步不是驗證使用者，而是驗證伺服器真的是我們要的那台。**

### 4. Key Exchange (會話金鑰產生)
+ 雙方透過 **非對稱金鑰演算法 (例：Diffie–Hellman, ECDH, Curve25519)** 進行 **金鑰交換**。
+ 雙方獨立算出一個 **共享會話金鑰 (session key)**，不需在網路上傳輸。
+ 之後所有流量都用這個 session key 進行 **對稱式加密 (AES, ChaCha20 …)**。

**從這一步開始，所有通訊都在加密通道中進行。**

:::warning
**加密通道在 OSI 哪個 Layer？**
:::

### 5. 使用者身份驗證 (Authentication)
**密碼登入情況**
1. Client 輸入密碼。
2. 密碼經過已建立的 **加密通道** 傳到 Server。
3. Server 驗證密碼是否正確
4. 正確 → 登入成功；錯誤 → 拒絕。



:::info

### client-server model
![Client-server-model.svg](https://upload.wikimedia.org/wikipedia/commons/thumb/c/c9/Client-server-model.svg/1000px-Client-server-model.svg.png?20141205053432)
> 圖片來源 [File:Client-server-model.svg](https://commons.wikimedia.org/w/index.php?title=File:Client-server-model.svg&oldid=928363449).  由 Calimo 以 David Vignoni 的 [Gnome-fs-client.svg](https://commons.wikimedia.org/wiki/File:Gnome-fs-client.svg), [Gnome-fs-server.svg](https://commons.wikimedia.org/wiki/File:Gnome-fs-server.svg) 為基礎來創作. Wikimedia Commons. 
- 主從（ㄗㄨㄥˋ）式架構
- 客人與櫃台的關係
- **client** 特徵
    - 主動的角色（主）
    - 發送請求
    - 等待直到收到回應
- **server** 特徵
    - 被動的角色（從）
    - 等待來自使用者端的請求
    - 處理請求並傳回結果
:::

:::warning
**所以 SSH 到底是對稱還是非對稱加密？**
:::

---

## 基礎操作

### 安裝
- 更新軟體的最新資訊及列表: `sudo apt update`
- 安裝 server 端套件: `sudo apt install openssh-server`
- 同時安裝 server 及 client: `sudo apt install ssh`
:::info

### Appendix - ssh 好多種？
- `apt-cache search [keyword]` 找軟體指令
- ![](https://hackmd.io/_uploads/r1GbPzn-p.png)
- 可以觀察到有許多 ssh ，可以注意到以下這三種
    - `openssh-client`
    - `openssh-server`
    - `ssh` (metapackage)
- `openssh-server` for server ，必須等待來自使用者端的請求，所以於遠端的主機必須要有 ssh-server
- 而 `openssh-client` 只負責發送資訊給 server 對應的 port 
- ssh (metapackage) 則是包含 client and server 
    - metapackage 表示它不包含任何軟體安裝檔，它是導向到所需的相依安裝檔 
    - 把使用 ssh 完整服務所需要的所有檔案都安裝
    - 延伸閱讀：[MetaPackages - Community Help Wiki](https://help.ubuntu.com/community/MetaPackages)
- `openssh-client` 於 Ubuntu Desktop 中已內建，如果沒有要做連入的主機可以不安裝 `openssh-server`
> P.S: Windows 10 1809 的 cmd 中也內建了 openssh-client 
:::

### 確認 server 狀態
- 查看服務狀態
    - `sudo systemctl status ssh` 
- 查看 ssh 佔用 port
    - `sudo ss -ltpn`
        - `-l`: 列出傾聽狀態（listening）的 sockets
        - `-t`: 只列出 TCP 的 sockets
        - `-p`: 顯示使用 sockets 的程式資訊
        - `-n`: 不要嘗試把 port 號解析成服務名稱

:::success
**Q. http & https 分別用哪個 port?**
:::

### 開啟關閉與重啟 server
- `sudo systemctl start ssh` 開始
- `sudo systemctl stop ssh` 停止
- `sudo systemctl restart ssh` 重新啟動；關掉再打開

### 由 client 登入
- 登入本機
    - `ssh localhost` 
    - `ssh 127.0.0.1`
    - `ssh [username]@127.0.0.1`
        - 本機地址 / 局部回送位址 (local loopback addresses)
        - 通常 `localhost` 會被解析成 `127.0.0.1` 
            - 見 `/etc/hosts` 檔案
        - IP 中 `127.0.0.1/8` 被設定為保留地址，所有向這個 IP 發送的封包都會留在本機
        - 而 localhost 被作為頂級域名，不得有任何網域註冊此名稱

:::info
大部分情況下你不會特地 `ssh localhost`，因為直接在本機打指令就能做，最常見情況是：

剛安裝或修改過 `OpenSSH Server` 設定時，會用 `ssh localhost` 確認服務能連上、帳號登入沒問題
:::


- 登入遠端
    - `ssh [username]@[hostname]`
    - 不指定使用者則以目前狀態的使用者進行遠端登入

### 常用操作參數
- `ssh -p [port num] [username]@[hostname]` 指定 server 的 port 號（預設 22）
    - server 端可以在設定檔中改變 port 號，如 port 號被更改就需要利用此方式登入
- `ssh [username]@[hostname] '[cmd]'` 執行單行指令，很長的時候再包起來
- ![](https://hackmd.io/_uploads/SJ7BnLgfp.png)
- `ssh -f [username]@[hostname] '[cmd]'` 背景執行指令
    - 即送出指令後可在原本的本機做事情
- `ssh -v [username]@[hostname] ` 使用偵錯模式連線 
    - 可使用 `-vv`、`-vvv`，v 越多，過程顯示的越多
    - 有問題可以去看出了什麼問題
- `exit` / `logout` / `Ctrl+D` 退出連線
- `ssh -A` 允許 Forward agent
:::spoiler 點此展開進階用法，暫時略過
> FIXME: 可能要加一下 -A 和 -X 成功的 demo
- `ssh -X` 允許遠端打開圖形化介面視窗
> FIXME: -X 可能會無法運作，理解一下原因以及解法
> Ref: [xorg - X11 forwarding - Putty - Xming: Why app run on the server instead of the client side? - Super User](https://superuser.com/questions/1843704/x11-forwarding-putty-xming-why-app-run-on-the-server-instead-of-the-client)
> Ubuntu 22.04 預設是 Wayland，而不是 X server
- `ssh -o` 根據 ssh_config 做單次的設定
    - `ssh -o ConnectTimeout=3 [username]@[hostname]` 等待時間如果超過三秒即斷線
:::

---

## 密碼登入
- 流程圖
- ![](https://hackmd.io/_uploads/B1pHVWkGT.png =600x)
> 流程圖來源：1121 LSA-I Week 08 [Linux 遠端連線](https://hackmd.io/@wzray07/HJN8ChbC3#%E5%AF%86%E7%A2%BC%E7%99%BB%E5%85%A5) 講義 by @wzray07 
1. Client 請求登入，Server 回覆版本以及加密協議
2. Session Encryption Negotiation，進行密鑰協商
4. 密鑰協商後的 key 為最終資料傳輸的金鑰
5. Server 發公鑰給 Client，讓 Client 比對 Server 的身分
6. 上一步沒問題，使用者輸入帳密後，Cliient 將密碼使用 server 的公鑰加密後傳輸給 server
7. 最後雙方再以協商的密鑰，接收登入訊息並開始操作系統

:::success
**Lab 登入**

打開你的 terminal，登入

```bash=
ssh [s學號]@moli.lsa.tw
```

密碼是 `Temp#2025!`

:::

---

## 利用公鑰進行無密碼登入
一般我們登入伺服器可以透過密碼進行登入，但是安全性的程度會比較沒有像 SSH key 那個安全，而且如果使用 SSH key 登入的話可以就不用每次手動輸入密碼，會方便許多，本篇教如何產生 SSH key 並且設定到伺服器上。

### 1. 在自己的使用者端 (client) 電腦上，產生一組金鑰對: 公開金鑰 (public key) / 私密金鑰 (private key)
- `ssh-keygen -t ed25519`
    - `-t`: 指定用哪種加密方式，預設為 rsa
    - `-b`: 指定要生幾 bits 的金鑰 (不一定需要這參數)
    - 以安全性推薦使用 ed25519 代替 rsa，但舊的伺服器可能只支援 rsa
- 輸出：
```
$ ssh-keygen -t ed25519
Generating public/private ed25519 key pair.
Enter file in which to save the key (/home/user/.ssh/id_ed25519): /home/user/.ssh/id_ed25519_lsademo
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/user/.ssh/id_ed25519_lsademo
Your public key has been saved in /home/user/.ssh/id_ed25519_lsademo.pub
The key fingerprint is:
SHA256:icqDAThC3dD/aQItF7e5ZU4n0vXgn4ot3JvxicYtEjI user@host
The key's randomart image is:
+--[ED25519 256]--+
| ...+            |
|o  . o . .   o   |
|=     o o + o o  |
|.o   o = = * o . |
|  .   = S O o . .|
|   + . . E o   o |
|  . +   o + *.o  |
|     .     = B=..|
|            ++oo |
+----[SHA256]-----+
```

- 會問你要存在哪個位置，若不輸入直接按 Enter 則採用預設位置：
    - 私鑰會存在 `~/.ssh/id_ed25519` -> 自己留著，不能給別人的
    - 公鑰會存在 `~/.ssh/id_ed25519.pub` -> 傳給別人的
- 若遇到檔名已存在，輸入 `y` 會覆蓋過去
```
/home/user/id_ed25519 already exists.
Overwrite (y/n)? y
```
- passphrase 可輸可不輸
    - 若不輸入直接按 Enter 則無 passphrase
    - 若有設則後續每次使用此金鑰都會問你這段密碼
        - 但現代一點的 OS 會有 ssh-agent 幫你暫時記住
- Fingerprint / randomart
    - 舊版本只有 Fingerprint 供大家可以連線時核對，但要檢查一長串文字不太容易[^Random_art]
    - 新版本更新成隨機圖像樣式，讓大家在連線時核對更方便
    - 如何核對？
        - `ssh -o VisualHostKey=yes [username]@[hostname]`

[^Random_art]: [ssh-keygen 在建立金鑰時所產生的 randomart 是做什麼用的？ – G. T. Wang](https://blog.gtwang.org/tips/ssh-keygen-randomart/)
:::info

### RSA 和 ED25519 差別
- `rsa` 預設
    - **R**ivest–**S**hamir–**A**dleman，3 個人名組成的縮寫
    - 相容性最好，支援稍舊的版本
    - 密碼長度可選擇 `1,024` ~ `16,384` bits ，現在預設為 `2,048` bits
        - `2,048` bits 以下都不建議用，如需提升安全性可選擇更長的 key ，如：`4,096` bits
- `ed25519` 較推薦使用 
    - 較新，OpenSSH 6.5 版本以後就都支援
    - 安全性最好
    - 更快的產生金鑰
    - 更快的身分驗證
    - 長度固定
        - 公鑰 `256` bits，被 OpenSSH 編碼成 `68` 個 16 進位字元(chars)
    - 是使用 SHA-512（SHA-2）和 Curve25519 的 EdDSA 簽名方式
        - 扭曲的愛德華茲曲線
- `ecdsa`
    - 有 `256`, `384`, `521` bits 三種 key 長度可以選擇，無法選擇其他的使用
    - 公鑰長度相比 RSA 較小
    - 應用了**橢圓曲線加密法**

:::warning
**鑰匙長度越長 → 一定比較安全？**
- RSA
    - 安全性建立在「大整數分解困難」上。
    - 攻擊者必須把一個由兩個大質數乘起來的數拆解回去，這在數學上困難，但隨著硬體與演算法進步（像是 Number Field Sieve, NFS），RSA 的安全性會慢慢下降。
    - 因此要維持安全，只能一直「加大鑰匙長度」。
    - 今天常見的 **RSA-2048**，大約相當於 **112-bit 對稱金鑰強度**。
- Ed25519 (Elliptic Curve Digital Signature, Edwards Curve 25519)
    - 安全性建立在「橢圓曲線離散對數問題 (Elliptic Curve Discrete Logarithm Problem, ECDLP)」的困難度上。
    - 相較於整數分解，這個問題目前沒有像 NFS 那樣高效的解法。
    - 因此 **短很多的鑰匙長度就能達到相同甚至更高的安全強度**。
    - Ed25519 的 256-bit 鑰匙安全等級大約等同於 **128-bit 對稱金鑰強度**。

如果要在 RSA 達到類似安全強度，得用到 **RSA-3072 (≈128-bit 強度)** 或 **RSA-4096 (≈152-bit 強度)**。
:::


### 2. 把公鑰複製到遠端主機 server 上
- 方法一
    - 用 ssh-copy-id 傳送公鑰到遠端主機
        `ssh-copy-id -i ~/.ssh/id_ed25519.pub [username]@[hostname]`
    - 若要指定 port `ssh-copy-id -i ~/.ssh/id_ed25519.pub -p [port] [username]@[hostname]`
* 方法二，手動複製
    1. `cat ~/.ssh/id_ed25519.pub`
    2. 用帳號密碼登入遠端主機 server 
    3. `cd ~/.ssh`
    4. 在遠端主機新增檔案 `touch authorized_keys`
    5. 將 `id_ed25519.pub` 公鑰內容放入 `authorized_keys`中，以換行方式區分不同支公鑰
       `vim authorized_keys`
    5. `chmod -R go= ~/.ssh` 把這個資料夾中所有檔案的 group 及 other 權限拿掉

### 3. 測試是否成功
試試看是否能正常登入使用key登入
```
$ ssh username@remote_host
```

如果能夠不需要輸入密碼 就表示已經成功了

:::success
**Lab 把金鑰放到剛剛登入的 server**
:::

:::info
若遇到權限問題，用
`chmod 600 ~/.ssh/authorized_keys`
:::

:::info

### 更換 ssh-server 公鑰
- 為了讓大家得知什麼樣的情況會跳出警告
- 先關閉 ssh 服務
&gt; 做任何相關設定檔調整前建議關閉服務再進行調整
- `sudo rm /etc/ssh/ssh_host*` 刪除
- ![](https://hackmd.io/_uploads/Hy0MNvAbp.png =500x)
- `sudo dpkg-reconfigure openssh-server` 重新設定
- ![](https://hackmd.io/_uploads/S1WrNPRZp.png =600x)
- 再把 ssh 服務開啟
- 試著用連線過的 client 去連線
- ![](https://hackmd.io/_uploads/B1EerP0ba.png =500x)
- 因為公鑰已經被換過，所以比對的時候發現不一樣
- 解決辦法：
- `ssh-keygen -f "~/.ssh/known_hosts" -R "[hostname]"`
- ![](https://hackmd.io/_uploads/H1wxODRbp.png =500x)
- 或是手動刪除也可以
    - 根據警告提示行數刪掉
- Tips: 有時 SSH 升級時也會發現公鑰被替換的警告畫面，很可能是因為發現加密的密鑰存在漏洞，所以進行替換

> 手動刪除不會紀錄在 `known_host.old` 中

:::

---

## Forward Agent

:::info
**SSH Agent 是什麼？**

它是一個專門管理你的 **私鑰**，以及幫你作 **數位簽章** 的程式

在 SSH 中，**agent 就是一個「代簽私鑰的保管員」**，它不會交出私鑰本身，只在需要時幫忙完成簽章
:::

- ![](https://hackmd.io/_uploads/B1dy17-G6.png =400x)
> 圖片來源 [SSH 免除重複輸入金鑰密碼教學：SSH Agent 與 Forwarding – G. T. Wang](https://blog.gtwang.org/linux/using-ssh-agent-forwarding-to-avoid-being-asked-passphrase/)
- 範例狀況：client 端是員工筆電，Server A 與 Server B 為公司 Server
    - 無法使用 Server A 金鑰登入 Server B，密碼驗證也被關閉
    - Server B 設定不允許任何外部 IP 連入 
    - ==Server B 擁有 Client 的公鑰==
    - 實例： Server A 為網站 Server ， Server B 為公司用機
    &gt; 網站要做部署，要從 Server B 拿資料，但員工 Work from home
- 使用 Agent Forwarding 可解決，透過 client 端登入 Server A 時將 SSH Key 一起轉給 A ，A 就可以利用 client 的 Key 登入 B

流程：
1. client 在本地有 `ssh-agent`，裡面已經有私鑰。
2. client 連上 `ssh ServerA -A`（`-A` 開啟 agent forwarding）。
3. ServerA 上的 `ssh` 如果要再連 ServerB，它會把簽名請求透過 SSH 通道回傳到 client 的 agent。
4. client agent 幫忙完成簽章，再回傳結果。

關鍵點：
- 私鑰 **永遠不會離開本地電腦**。
- 遠端機器只能透過 SSH 通道「借用」你的 agent 做簽章。

:::danger
**舉一反三**
- 如果你登入的遠端主機 (hostA) 已被入侵，攻擊者可能利用你的 agent 代理去登入其他機器 (hostB)。
- 雖然私鑰不會洩漏，但只要 agent session 還活著，他們就能「借用」你的身分。
- 僅在信任的主機上使用 `-A`。

更安全的替代方案是 ProxyJump / ProxyCommand 或 ssh certificates。
:::

---

## ssh 相關設定檔案
- 設定檔中 `#` 為註解，且註解後通常為藍字
- 註解的內容為預設內容
- ssh 相關設定檔案 `/etc/ssh/`

### Client
- `/etc/ssh/ssh_config`
- ![](https://hackmd.io/_uploads/S1oowgbMp.png =500x)
:::spoiler 文字版
```config
Include /etc/ssh/ssh_config.d/*.conf

Host *
#   ForwardAgent no
#   ForwardX11 no
#   ForwardX11Trusted yes
#   PasswordAuthentication yes
#   HostbasedAuthentication no
#   GSSAPIAuthentication no
#   GSSAPIDelegateCredentials no
#   GSSAPIKeyExchange no
#   GSSAPITrustDNS no
#   BatchMode no
#   CheckHostIP yes
#   AddressFamily any
#   ConnectTimeout 0
#   StrictHostKeyChecking ask
#   IdentityFile ~/.ssh/id_rsa
#   IdentityFile ~/.ssh/id_dsa
#   IdentityFile ~/.ssh/id_ecdsa
#   IdentityFile ~/.ssh/id_ed25519
#   Port 22
#   Ciphers aes128-ctr,aes192-ctr,aes256-ctr,aes128-cbc,3des-cbc
#   MACs hmac-md5,hmac-sha1,umac-64@openssh.com
#   EscapeChar ~
#   Tunnel no
#   TunnelDevice any:any
#   PermitLocalCommand no
#   VisualHostKey no
#   ProxyCommand ssh -q -W %h:%p gateway.example.com
#   RekeyLimit 1G 1h
#   UserKnownHostsFile ~/.ssh/known_hosts.d/%k
    SendEnv LANG LC_*
    HashKnownHosts yes
    GSSAPIAuthentication yes
```

:::
- ForwardAgent 驗證代理
    - 讓本地 SSH Key 可由遠端 server 轉送
- IdentityFile
    - 指定要用哪個私鑰
- PasswordAuthentication 
    - 允許 / 不允許 client 使用密碼登入 ssh server
- CheckHostIP 
    - 確認已連線過的主機是否有 IP 不同的問題，發現就提醒
- ConnectTimeout
    - 連線超過指定秒數就斷線
- Port
    - 預設使用 22 port 進行連線
- 針對指定 Server 做此設定
    - 例如
    ```conf=
    Host ec2
        User lsa
        Port 22
        Hostname lsalab.moli.rocks
    ```
    - 如此在下 `ssh ec2` 指令時，會採用以上設定連線
    - 可放置在 `/etc/ssh_config` 中，也可以建立一個檔案 `~/.ssh/config`

### Server
- `/etc/ssh/sshd_config`
- **基礎設定**
- ![](https://hackmd.io/_uploads/SypzHWWGa.png =500x)
:::spoiler 文字版
```
#Port 22
#AddressFamily any
#ListenAddress 0.0.0.0
#ListenAddress ::

#HostKey /etc/ssh/ssh_host_rsa_key
#HostKey /etc/ssh/ssh_host_ecdsa_key
#HostKey /etc/ssh/ssh_host_ed25519_key
```
:::
- Port 選擇使用 port 號 （預設 22 ）
- ListenAddress 允許連入的 IP address
  預設 `0.0.0.0` 為 IPv4 中允許主機所擁有的任何 IP 連入
    - `::` 為 IPv6 中，允許主機所擁有的任何 IP 連入
- HostKey 存放私鑰位置
- **認證相關**
- ![](https://hackmd.io/_uploads/SJoF4WWzp.png =600x)
- PermitRootLogin 
    - 是否允許使用 root 登入（預設為禁止使用密碼登入）
- PubkeyAuthentication 
    - 是否允許使用 Public Key 登入 （預設為 yes）
- PasswordAuthentication ⭐
    - 是否允許 client 使用密碼登入（預設為 yes）
    - 若設 `no` ，剩下 public key 驗證模式，可大大降低猜密碼攻擊危險性
- PermitEmptyPasswords
    - 是否允許輸入空密碼登入（預設為 no）
- 針對單獨 client 設定
    - Match User anoncvs
    - 如果你只想針對某使用者進行特殊設定，可使用這個功能
    ```conf=
    Match User ray
        PasswordAuthentication no
        ClientAliveInterval 30
        ClientAliveCountMax 20
    ```
    - 針對 client 端叫 ray 的人給他這樣的設定
:::info

### SSH config include 設定
- 觀察檔案會有一行
- ![](https://hackmd.io/_uploads/HyZsJfWMT.png =300x)
- 在 `/etc/ssh/` 中會發現有一個目錄 `sshd_config.d`
- 用來存放使用者想更動的設定，為了防止 sshd_config 被改壞
- ssh 在運作時更改設定很可能會出問題，所以建議是以關閉狀態改變設定或是在這個目錄新增 `.conf` 檔去做設定
:::

---

# 遠端檔案傳輸
## FTP
- File Transfer Protocol
- 古老的檔案傳輸協定
- 明文傳輸 (沒有加密) 方式
- client 可以把檔案**上傳至 / 下載 from** ftp server
- 優點
    - 傳輸速度快
    - 與 HTTP 相比，他不需要包很多 Header ，所以封包自然也就比較輕
- 缺點
    - 傳輸不安全
    - 如需登入帳密會有安全性問題 
- port 號
    - 20 port 資料連線通道
    - 21 port 控制連線通道
- 雖然不安全，但如果是公共檔案就比較沒有問題
    - 安裝檔、給大家的~~作業~~文件, etc.
- 可匿名使用
    - 雖可匿名，但仍會紀錄登入請求來源

### FTP Server 
- 常見: vsftpd、proftpd
- 以下用 vsftpd 示範
- `sudo apt install vsftpd`
- `sudo service vsftpd status`
- ![](https://hackmd.io/_uploads/rk9gCP0fp.png)
- 安裝後確認
- `/etc/vsftpd.conf` 設定檔位置
- 以下介紹匿名模式的設定（不建議大家利用 ftp 傳送重要檔案）
- ![](https://hackmd.io/_uploads/S1tbW_0f6.png)

:::info
### 參數參考
- `anonymous_enable=YES (NO)`
    - 是否允許匿名者
- `anon_other_write_enable=YES (NO)`
    - 匿名者是否有寫入檔案的權限（預設為 NO ） 
- `anon_mkdir_write_enable=YES (NO)`
    - 匿名者是否有目錄創建的功能（預設為 NO ）
- `anon_upload_enable=YES (NO)`
    - 匿名者是否有上傳檔案的功能
- `no_anon_password=YES (NO)`
    - 匿名者登入是否需要密碼，預設為 NO ，需要密碼
- `anon_root = /var/ftp/`
    - 匿名者登入初始路徑
- `guest_enable=YES (NO)` 與 `local_enable=YES (NO)`
    - 如不允許實體使用者即可設定為 NO
- **如果沒有設定參數需自行加進 conf 檔**
:::
> FIXME: 開設定好匿名上傳下載的 server 給大家玩？
- 完成設定後，重啟 service
- 重啟 service 後可以先登入本機測試
- ![](https://hackmd.io/_uploads/SJWFSO0fT.png)
- 輸入 `anonymous` 表示匿名登入
- 密碼可以選擇不輸入（不過密碼會紀錄）
- 紀錄檔 `/var/log/vsftpd.log`
- ![](https://hackmd.io/_uploads/BylvOdRGT.png)

### FTP 操作
- `ftp [ip address]` 登入
- 如果是公眾的 server，username 可輸入`anonymous`作為匿名，密碼可空白
- `put [client_file] [server_file] ` 上傳檔案，如 server 上已有重複的檔案則覆蓋
- `get [server_file] [client_file]` 下載檔案，如 client 上已有重複的檔案則覆蓋
- `ls` 與 Linux 指令一樣， list
- `cd` 與 Linux 指令一樣， change directory
- `lpwd` 顯示本機端目前位置
- `lcd` 切換本機端位置
- `help` 查看更多指令
- `bye` 退出

---

## SFTP
- **S**SH **F**ile **T**ransfer **P**rotocol
- client 指令基本上與 FTP 語法一樣，只是 SFTP 是 SSH 的一部分
> 所以 server 端需要 SSH 才能使用
:::info
在 **sshd_config** 檔案中，有
```config
# override default of no subsystems
Subsystem       sftp    /usr/lib/openssh/sftp-server
```
:::
- 利用 SSH 傳輸，所以預設與 SSH 一樣使用 22 port
- 解決 FTP 明文傳輸的問題
- 效率
    - FTP &gt; SFTP
- 安全性
    - FTP &lt; SFTP
- 登入
    - `sftp [user]@[hostname]`

:::success
**實做**

在你本機捏一個 `學號.abc`

用 ftp or sftp 丟到 moli.lsa.tw `/var/class1001/` 裡頭
:::


## SCP
- **S**SH copy <!-- 不一定是 SSH backend? -->
- `cp` 在 Linux 是複製
- 而 `scp` 為遠端複製檔案
### scp 操作
- 用法
    - `scp [參數] [source] [destination]`
- 從本機上傳至遠端
    - `scp [本機檔案路徑] [usr_name]@[hostname]:[遠端檔案路徑]`
    - `path` 不輸入則為家目錄
- 從遠端下載到本機
    - `scp [usr_name]@[hostname]:[遠端檔案路徑] [本機檔案路徑]`
- `-r`: recursive ，目錄中所有檔案
    - `scp [folder]/** [destination]`

---

## Rsync
- Rsync 能做到 `cp` 跟 `scp` 的事，但 Rsync 的效率比 `cp` 跟 `scp` 好
- 靈活性高
- 除了複製檔案，也非常適合用來作備份工具
- 可使用於本機與遠端
- 支援連結檔與設備檔
- 支援複製後保留檔案的屬性（擁有者、群組與權限設定）
- 在第一次複製檔案時，會複製完整的檔案內容
- 如複製時發現有一樣檔案在裡面，會利用 delta-transfer 演算法**檢查新舊檔案之間的差異，只傳送有變動的部份** 
    - 節省複製的時間，可加快備份速度（大檔案傳輸時會有更明顯的差異）
    - 進行遠端傳輸時，可節省網路頻寬
- 支援資料的自動壓縮與解壓縮，也可節省網路頻寬

### rsync 操作
- 為內建工具，但如果沒安裝
- `sudo apt install rsync`
### 基礎語法
- `rsync [參數] [來源檔案] [目的檔案]`
### 常見參數


| 參數                    | 說明                                                                                               |
| ----------------------- |:-------------------------------------------------------------------------------------------------- |
| `-v`                    | verbose 模式，輸出比較詳細的訊息                                                                   |                                                  |
| `-a`                    | 封裝備份模式，遞迴備份所有子目錄下的目錄與檔案，保留連結檔、檔案的擁有者、群組、權限以及時間戳記。 |
| `-z`                    | 啟用壓縮。                                                                                         |
| `-h`                    | 將數字以比較容易閱讀的格式輸出。                                                                   |                    |
| `-P` / `--progress`     | 顯示傳輸進度                                                                                       |
| `--delete`              | 同步將不存在於來源端的檔案刪除                                                                     |
:::spoiler
> FIXME: 指令測試
> - rsync -az：什麼都不顯示
    > ![image](https://hackmd.io/_uploads/BJrxXVl2lx.png)
> - rsync -avz -P
    > ![image](https://hackmd.io/_uploads/HySqXEx3ex.png)
> - rsync -az -P
> ![image](https://hackmd.io/_uploads/ryIAXNl2ex.png)
> - rsync -avz --info=progress2：背景模式跑 rsync，且會有進度條
    > ![image](https://hackmd.io/_uploads/Sk0XxEe3ge.png)
:::

### 常見使用情境
### 基本本機複製 
- `rsync -avh /home/wzray/a/ /home/wzray/b/`
- `-a` 這個參數包含 `-r`，所以要複製整個目錄下的檔案也沒問題
- `rsync -avh /home/wzray/a/ /home/wzray/b/`
- 試著修改一個檔案，再敲一次指令後，觀察他處理了什麼檔案
- ![](https://hackmd.io/_uploads/B1XrvtRzp.png =500x)
### 遠端備份
- 本機 => 遠端
- `rsync -avzh ~/file wzray@192.168.1.12:/home/wzray/`
- 遠端 => 本機
- `rsync -avzh wzray@192.168.1.12:/home/wzray/ ~/file`
- 跟 `scp` 語法很像
### 同步刪除檔案
- 如果第二次複製時發送端的部分檔案已刪除，想要在目的端同步刪除該檔案
- `rsync -avh --delete /home/wzray/a/ /home/wzray/b/`
---

:::spoiler
> FIXME: BT 小筆記
- 不透過 ssh 的方式把遠端的資料 mirror 回來
- 沒有經過 ssl 加密 -> 密碼用明文
- read only: no -> 讓使用者可以寫入資料

步驟紀錄
![image](https://hackmd.io/_uploads/Sys2Rmxnll.png)
![image](https://hackmd.io/_uploads/Sy-C07xhlg.png)
![image](https://hackmd.io/_uploads/rJMkkVe3xl.png)
![image](https://hackmd.io/_uploads/SyVg1Elhle.png)
![image](https://hackmd.io/_uploads/Byeby4xhll.png)
![image](https://hackmd.io/_uploads/BJa-kNe2el.png)
![image](https://hackmd.io/_uploads/r1bz1Eghge.png)

:::

---

# SSH Tunnel
> 
- 基於 SSH Protocol 所延伸的技術
- 將網路上的 A、B 兩個端點用 Tunnel 連接起來，形成一個**隧道**
- 基本過程：
    - A 點上的某個 Port X 所傳送的資料轉送 至 B 點上的 Port Y
    - 又被稱 Port Forwarding
- **好處**
- 可突破防火牆限制
    - 如防火牆有擋掉特定 port ， 沒有擋掉 22 port ，可利用此種方式穿透此限制
- 將通訊內容加密避免洩漏
    - 利用 SSH 加密特性來進行通訊
- Tunneling 中通常會出現以下三種角色
    - **Client**
        - 任何你可以敲 ssh 指令來啟動 Port Forwarding 的機器
    - **SSH Server**
        - 可以被 Client 用 SSH 連進去的機器
    - **Target Server**
        - 某一台你想建立連線的機器，通常是為了對外開放這台機器上的服務
        - Client 與 SSH Server 本身都可以是 Target Server ，不是真的要有三台機器才可以進行 Port Forwarding！
        
---
        
## Local Port Forwarding 
- 語法
    - `ssh -L [bind_address:][port]:[host]:[host_port] [SSH Server]`
    - `[bind address:]` 不輸入預設為 `localhost`
- **情境一：**
- ![](https://hackmd.io/_uploads/B1AfAd7fT.png)
- 防火牆後有一台 Server ，但防火牆設定不接受 8080 port 連入，不過接受 22 port
- 使用 tunneling 將 client 中的某個 port （圖中範例 port 9090），對他發送的資料經 SSH 通道轉送至 8080 port，等於連上 client 9090 port 就可以連上 Server 8080 port  ，完成繞過防火牆限制
- ![image](https://hackmd.io/_uploads/S1wAKUDgyg.png)
- `ssh -L 9090:localhost:8080 [username]@[server]`

:::success
**Lab 作業！**
![圖片](https://hackmd.io/_uploads/BkOMc_jhlx.png)
我們在 moli.lsa.tw port 8080 開了一個簽到網站，請你用上 ssh tunnel 所學，想辦法進到這個網站完成簽到並截圖，網站的 url 也要截進去！

簽到的紀錄會放在 `/opt/signin-app/signlog.csv`，請你用上遠端檔案傳輸方式，把這個簽到紀錄下載到自己的虛擬機裡，截圖裡面的內容
:::

- **Client**
    - 你的電腦
- **SSH Server**
    - 防火牆後的 Server
    - `[username]@[server]`
- **Target Server**
    - 防火牆後的 Server
    - localhost:8080
&gt; `bind address`沒輸入代表使用預設 `localhost`
&gt; 如果要開放對外可以改成 `0.0.0.0`
&gt; `host` 的 `localhost` 是只針對 `server` 的 `localhost`

- **情境二：**
- ![](https://hackmd.io/_uploads/r1R2IUCza.png)
- 前面情境為 Server 能連入的情況，那如果今天權限不足以連入要怎麼辦
- 如果防火牆後有一台你的機器且可用 ssh 連入即可完成
:::spoiler
- ![](https://hackmd.io/_uploads/S1wRPLAfa.png)
:::
- ![圖片](https://hackmd.io/_uploads/rJSrtDG2xe.png)

- `ssh -L 9090:192.168.2.10:8080 [username]@[server]`
- **Client**
    - 你的電腦
- **SSH Server**
    - 你的機器
    - `192.168.2.10:8080`
- **Target Server**
    - 防火牆後的 Server
    - `[username]@[server]`

---

## Remote Port Forwarding
- 語法
    - `ssh -R [bind_address:][port]:[host]:[host_port] [username]@[server]`
- **情境一：**
- ![](https://hackmd.io/_uploads/H1Dn_YXMT.png)
- 你的電腦上完成了一個服務，並架上 8080 port ，但你的電腦沒有對外 ip，僅有內部 ip 
- 準備一台有 Internet 的機器當作對外機器，即可使用 Remote Forwarding 完成
- 對外機器開啟一 port 客戶連入 （圖片範例 port 9090）
- 對外機器藉由 SSH 通道將轉送到 port 8080，客戶即可取得服務
- ![](https://hackmd.io/_uploads/S1KIjtXfa.png)
- `ssh -R 0.0.0.0:9090:localhost:8080 [username]@[SSH Server]`
    - 因為要開放對外，所以設定`0.0.0.0` 
    - `localhost` 是針對你的電腦
- **Client**
    - 你的電腦
- **SSH Server**
    - 對外機器
    - `[username]@[server]`
- **Target Server**
    - 你的電腦
:::warning
- 基於安全考量，Remote Forwarding 的預設都只能夠 bind 在 SSH Server 的 localhost 上
- 所以必須要改變設定
- 至 sshd 內的設定檔
- `GatewayPorts yes`
:::
- **情境二：**
- 有一個在內網裡的內部服務，你的電腦可以用 IP `192.168.1.100` 和 Port 8080 連到這個服務，但因為都在內網所以大家都沒有 Internet IP，所以無法讓你從家裡透過 Internet 連回來：
- ![](https://hackmd.io/_uploads/Byzqeq7Ga.png)
- 與情境一不同，服務另外架在其他機器上
- 一樣也可以使用 Remote Forwarding
- ![](https://hackmd.io/_uploads/Bk45G9mza.png)
- `ssh -R 0.0.0.0:9090:192.168.1.100:8080 [username]@[SSH Server]` 
    - `192.168.1.100` 為內部其他電腦服務 ip，透過你的電腦做資料轉送
- **Client**
    - 你的電腦
- **SSH Server**
    - 對外機器
    - `[username]@[server]`
- **Target Server**
    - 內部服務
    - `192.168.1.100:8080`

---

## Dynamic Port Forwarding

一般的 Port Forwarding 只能夠轉送一個 IP 上的一個 Port ，當你有很多 IP 或很多 Port 想轉時就只能一個一個開，很不方便

- 語法
    - `ssh -D [bind_address:][port] [username]@[SSH Server]`
- 情境
- ![dynamic](https://hackmd.io/_uploads/HJx-Cvz2lx.png)
- 只要有一台位於內網且具有外部 IP 的機器，你就可以利用這個方法建立一個 SOCKS 代理伺服器，讓你能夠從外面連回內網裡的所有服務

> FIXME: 三種 tunnel 模式想個 demo

> FIXME: 線要畫確實，包括從哪進、從哪出，下面幾張圖亦然

> FIXME: 記得留時間，後面 tunnal 部分需要緩衝


---

# 作業
登入 lsa server，在 /var/exercise/ 裡有多個檔案，這些檔案裡頭除了有幾百行亂碼，裡面還參雜你們的學號，你們必須找到所有出現你學號的檔案

# 補充資料
- [如何讓兩台虛擬機可以用 ssh 連線 - HackMD](https://hackmd.io/@oJ7l_4AbTkCmGKJnAmApWw/ryXtBNePF)
- [遠端連線虛擬機 - HackMD](https://hackmd.io/@wzray07/Bkhh64xGT)