iT邦幫忙

2025 iThome 鐵人賽

DAY 4
0
Cloud Native

我在 CKS 考完只拿 47% 後痛定思痛決定好好準備內容系列 第 4

[Day4] 1-3. Properly set up Ingress with TLS

  • 分享至 

  • xImage
  •  

Kubernetes 中的 Certificate

Server Certificate

  • Kube-api Server: apiserver.crt, apiserver.key
  • ETCD Server: etcdserver.crt, etcdserver.key
  • Kubelet Server: kubelet.crt, kubelet.key

Client Certificate

  • admin(user): admin.crt, admin.key 主要用於連線kube-api server
  • kube-scheduler: scheduler.crt, scheduler.key 主要用於連線kube-api server
  • kube controller-manager: controller-manager.crt, controller-manager.key 主要用於連線kube-api server
  • kube-proxy: kube-proxy.crt, kube-proxy.key 主要用於連線kube-api server
  • Kube-api Server: apiserver.crt, apiserver.key 作為client連線kubelet 以及 ETCD

Certificate Authority (CA)

可分為多個CA

  • ETCD Server
  • Kube-api Server

連線Cluster

方法一透過curl

curl https://kube-apiserver:6443/api/v1/pods \
  --key iroman.key --cert iroman.crt \
  --cacert ca.crt

但是每次要取得 pod 資訊或者操作時都需要指定,因此可以將上述寫於 kube-config.yaml中,
這個在 EKS 中也就是透過 aws eks update-kubeconfig取得連線,此時會在 ~/.kube/config儲存相關資訊。

kube-config.yaml

以 config 的形式儲存於 ~/.kube/config

apiVersion: v1
clusters:
- cluster:
    certificate-authority-data: ...
    server: https://1A2B3D4C5E6F.gr7.us-east-1.eks.amazonaws.com
  name: arn:aws:eks:us-east-1:123456789012:cluster/iroman-ut
contexts:
- context:
    cluster: arn:aws:eks:us-east-1:123456789012:cluster/iroman-ut
    user: arn:aws:eks:us-east-1:123456789012:cluster/iroman-ut
  name: arn:aws:eks:us-east-1:123456789012:cluster/iroman-ut
current-context: arn:aws:eks:us-east-1:123456789012:cluster/iroman-ut
kind: Config
preferences: {}
users:
- name: arn:aws:eks:us-east-1:123456789012:cluster/iroman-ut
  user:
    exec:
      apiVersion: client.authentication.k8s.io/v1beta1
      args:
      - --region
      - us-east-1
      - eks
      - get-token
      - --cluster-name
      - iroman-ut
      - --output
      - json
      command: aws

由上述可以看到在config中可以大致分為三個類別:(1)clusters (2)contexts (3)users
cluster及user分別是設定哪個cluster及user,context則是協助將cluster及user做mapping

如果需要取得相關連線資訊除了訪問檔案外可以透過

kubectl config get-contexts  # 取得可操控 cluster 的列表

或者是透過以下指令查看細節

kubectl config view --raw  # 查看完整的 config 細節
  • ETCD certificate查詢
journalctl -u etcd.service -l

基礎觀念

類型

Symmetric Encryption

說明:加密與解密都使用同一把金鑰

Asymmetric Encryption

說明:加密與解密是不同的金鑰,可以彼此作為加/解密的金鑰,可以分為Private Key以及 Public Key。

  • 用 Public Key 加密的資料,只能用對應的 Private Key 解密
  • 用 Private Key 加密的資料,只能用對應的 Public Key 解密

憑證建立

建立 ca

openssl genrsa -out ca.key 2048

ca 自簽憑證

openssl req -x509 -new -key ca.key -out ca.crt -days 3650 \
    -subj "/CN=My CA/O=My Organization/C=TW"

建立 iroman.key

openssl genrsa -out iroman.key 2048

建立sign request

openssl req -new -key iroman.key -subj "/CN=iroman/O=developers" -out iroman.csr

CA 使用自己的憑證和私鑰簽署 CSR

openssl x509 -req -in iroman.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out iroman.crt -days 1000
  • Certificate Authority: ca.crt 憑證簽發機構(驗證身份並簽發憑證)

  • Certificate Signing Request: iroman.csr 憑證申請書(包含公鑰和身份資訊)

  • 憑證檔案(包含公鑰和身份資訊): 通常是 .crt, .pem, .cer

  • 私鑰檔案: 通常是 .key, -key.pem

流程:申請者生成 CSR → 提交給 CA → CA 驗證並簽發憑證

舉例

1. http 暴露風險

  • 當只有透過http訪問網站時,有心人士可以直接取得user/password

2. ssh 原理

以 ssh 為例,當我們透過ssh-keygen建立金鑰時,可以建立 Private Key(id_rsa) 以及 Public Key(id_rsa.pub),
創建完後將 public key 上傳至 Server(EC2) 中,一般來說ssh public key 會存於 EC2的~/.ssh/authorized_keys
使用時可以透過 ssh 連線 ssh -i {private_key} {user_name}@{server 位置}連線,
當首次連線時會有以下的詢問

ED25519 key fingerprint is SHA256:ABCmrigLhkKz2bZC4MpOoliThtCKVnsRzPC1sA4TFKA.
This key is not known by any other names.
Are you sure you want to continue connecting (yes/no/[fingerprint])? 

當輸入 yes 後,該EC2的公鑰資訊會存於本機端的 ~/.ssh/known_hosts中,每當連線時就會再次驗證該公鑰的正確性。
此外由於公鑰長度很長因此在連線時會將資訊轉為SHA256以便使用者(人)進行讀取

額外補充 ssh log 的存放位置
Debian Linux: /var/log/auth.log
CentOS : /var/log/secure.
Amazon Linux: /var/log/secure.

在 Amazon Linux 中如果要啟動 ssh log 會需要先建立syslog

dnf install rsyslog
systemctl enable rsyslog --now

3. https 原理

TCP 交握 → TLS 交握 → 加密的 HTTP 通訊 → TLS/TCP 關閉

第一階段- TCP 三向交握

1. Client → Server: SYN
   - 客戶端發起連線請求

2. Server → Client: SYN-ACK  
   - 伺服器確認並回應連線請求

3. Client → Server: ACK
   - 客戶端確認,TCP 連線建立完成

第二階段- TLS 交握

4. Client Hello
   - TLS 版本、隨機數、支援的密碼套件等

5. Server Hello
   - 選擇的 TLS 版本、隨機數、密碼套件

6. Certificate
   - 伺服器憑證鏈 (包含 Server 的 public key)

7. Server Key Exchange (可選)
8. Certificate Request (可選)  
9. Server Hello Done

10. Certificate (可選)
    - Client 憑證(雙向認證時)

11. Client Key Exchange
    - Client 產生 pre-master secret
    - 用 Server 的 public key 加密 pre-master secret
    - 送給 Server
    
    → Server 用自己的 private key 解密取得 pre-master secret

12. Certificate Verify (可選)
    - Client 用自己的 private key 簽名(證明擁有憑證)

13. Change Cipher Spec (Client)
14. Finished (Client, 加密)
15. Change Cipher Spec (Server)  
16. Finished (Server, 加密)

第三階段- 應用資料傳輸

17. HTTP Request/Response (全部加密)

第四階段- 連線關閉

18. TLS Close Notify
19. TCP 四向揮手 (FIN, ACK, FIN, ACK)
[FIN, ACK] 客戶端發起關閉 - "我要關閉連線了,並確認之前的資料"
[ACK] 伺服器確認 - "好的,我知道你要關閉了"
[FIN, ACK] 伺服器也要關閉 - "我也要關閉連線了"
[ACK] 客戶端最終確認 - "好的,連線正式關閉"

4. 監聽http

以下方式可以提供測試,在環境中建立一個http flask 並且透過 tcpdump 監聽,當使用者輸入密碼時會被抓取資訊

from flask import Flask, request, render_template_string
import json
from datetime import datetime

app = Flask(__name__)

# 簡單的 HTML 表單
HTML_FORM = '''
<!DOCTYPE html>
<html>
<head>
    <title>HTTP 流量監聽實驗</title>
    <style>
        body { font-family: Arial, sans-serif; margin: 50px; }
        form { background: #f4f4f4; padding: 20px; border-radius: 5px; }
        input, button { margin: 10px 0; padding: 8px; width: 200px; }
        button { background: #007cba; color: white; border: none; cursor: pointer; }
        .result { margin-top: 20px; padding: 15px; background: #d4edda; border-radius: 5px; }
    </style>
</head>
<body>
    <h1>HTTP 流量監聽實驗</h1>
    <p>這個頁面用於示範 HTTP 未加密通訊的安全風險</p>
    
    <form method="POST" action="/submit">
        <h3>請輸入一些「敏感」資料:</h3>
        <br>用戶名稱: <input type="text" name="username" placeholder="例如: admin">
        <br>密碼: <input type="password" name="password" placeholder="例如: secret123">
        <br>API Token: <input type="text" name="api_token" placeholder="例如: abc123xyz789">
        <br>信用卡號: <input type="text" name="credit_card" placeholder="例如: 1234-5678-9012-3456">
        <br><button type="submit">提交資料</button>
    </form>
    
    {% if message %}
    <div class="result">
        <h3>結果:</h3>
        <p>{{ message }}</p>
        <p><strong>提示:檢查你的 tcpdump 終端,應該可以看到剛才提交的所有資料!</strong></p>
    </div>
    {% endif %}
</body>
</html>
'''

@app.route('/', methods=['GET', 'POST'])
def index():
    message = None
    return render_template_string(HTML_FORM, message=message)

@app.route('/submit', methods=['POST'])
def submit():
    # 取得表單資料
    data = {
        'timestamp': datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
        'username': request.form.get('username', ''),
        'password': request.form.get('password', ''),
        'api_token': request.form.get('api_token', ''),
        'credit_card': request.form.get('credit_card', '')
    }
    
    # 儲存到檔案(模擬資料處理)
    with open('/tmp/submitted_data.json', 'a') as f:
        f.write(json.dumps(data, ensure_ascii=False) + '\n')
    
    # 在終端顯示(方便觀察)
    print(f"[{data['timestamp']}] 收到資料:")
    print(f"  用戶名: {data['username']}")
    print(f"  密碼: {data['password']}")
    print(f"  API Token: {data['api_token']}")
    print(f"  信用卡: {data['credit_card']}")
    print("-" * 50)
    
    message = f"資料已成功提交!時間: {data['timestamp']}"
    return render_template_string(HTML_FORM, message=message)

if __name__ == '__main__':
    print("=" * 60)
    print("HTTP 流量監聽實驗服務器")
    print("=" * 60)
    print("1. 在另一個終端執行以下命令開始監聽:")
    print("""sudo tcpdump -i any -A -s 0 'port 5000' | grep -A 10 -B 10 "POST\|username\|password" """)
    print("")
    print("2. 然後在瀏覽器開啟: http://localhost:5000")
    print("3. 填寫表單並提交,觀察 tcpdump 的輸出")
    print("=" * 60)
    
    # 綁定到 localhost:5000
    app.run(host='127.0.0.1', port=5000, debug=True)


上一篇
[Day3] 1-2. Use CIS benchmark to review the security configuration of Kubernetes components
下一篇
[Day5] 1-4. Protect node metadata and endpoints
系列文
我在 CKS 考完只拿 47% 後痛定思痛決定好好準備內容5
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言