Helm作為Kubernetes的套件管理器已成為雲原生應用部署的標準工具,然而隨著企業對容器化應用的依賴加深,Helm Chart的安全性成為不容忽視的關鍵議題。Chart儲存庫包含應用程式的部署定義、配置參數、敏感資訊以及依賴關係,一旦遭受未授權存取或惡意竄改將對整個Kubernetes叢集造成嚴重威脅。公開的Chart儲存庫如Artifact Hub提供豐富的開源Chart但缺乏存取控制,企業內部的私有Chart儲存庫則需要嚴密的安全防護機制。安全威脅包含未授權存取導致敏感配置洩露、惡意Chart注入植入後門或挖礦程式、中間人攻擊竄改傳輸中的Chart內容、供應鏈攻擊透過依賴項滲透、權限提升利用Chart中的服務帳戶配置以及合規性風險未能滿足產業法規要求。建構完整的Helm安全體系需要多層次的防護策略,從網路傳輸層的加密到應用層的身份驗證,從儲存庫存取控制到Kubernetes叢集的RBAC整合,從Chart內容掃描到持續稽核監控,形成縱深防禦的安全架構。
Helm Chart儲存庫的驗證機制與實作
Helm支援多種驗證機制保護Chart儲存庫的存取安全,依據安全需求與基礎設施環境選擇適當的驗證方式。基本驗證使用使用者名稱與密碼組合是最簡單的實作方式,適合小型團隊或開發環境的快速部署。憑證驗證採用X.509公鑰基礎建設提供更強的安全保證,透過數位憑證識別客戶端身份並加密通訊過程,適合企業級生產環境。OAuth2與OpenID Connect整合企業身份管理系統實現單一登入並支援細緻的權限控制,適合大型組織的多租戶環境。雲端原生驗證如AWS IAM或Azure AD利用雲端平台的身份服務簡化憑證管理。服務網格整合如Istio的mTLS提供Pod層級的雙向認證與加密。選擇驗證機制需要平衡安全性、管理複雜度、效能影響以及與現有基礎設施的整合程度。
基本驗證的實作簡單直接但安全強度有限。使用helm repo add命令時提供username與password參數將認證資訊儲存在本機配置檔案中,後續與儲存庫的互動自動附帶認證。然而密碼以明文或弱加密形式儲存於配置檔案存在洩露風險,單一密碼無法實現細緻的權限控制,密碼輪換困難需要更新所有客戶端配置,且缺乏審計追蹤能力無法識別特定使用者的操作。為了緩解這些風險建議採用強密碼策略結合定期輪換機制,限制配置檔案的檔案系統權限避免其他使用者讀取,在生產環境優先選擇憑證驗證或OAuth2等更安全的方案,並整合秘密管理系統如HashiCorp Vault動態生成與注入認證憑據。
憑證驗證基於公鑰密碼學提供雙重保障,既驗證客戶端身份又加密通訊內容。實作憑證驗證需要建立憑證授權機構負責簽發與管理憑證,生成伺服器憑證標識Chart儲存庫的身份,生成客戶端憑證標識每個使用者或服務的身份,並配置Helm客戶端與儲存庫伺服器使用這些憑證。憑證驗證的優勢包含強加密保護通訊內容防止竊聽與竄改,雙向認證伺服器驗證客戶端身份同時客戶端也驗證伺服器,細緻控制透過不同的客戶端憑證實現角色區分,審計能力每個憑證對應特定實體便於追蹤操作來源,以及自動化管理憑證生命週期管理工具簡化輪換與撤銷流程。挑戰則在於憑證管理的複雜性需要建立與維護PKI基礎設施,初始設置成本較高需要生成與分發大量憑證,以及與現有系統整合可能需要調整應用程式與基礎設施配置。
# Helm Chart安全配置管理系統
# 自動化生成憑證、配置儲存庫與驗證設置
import os
import yaml
import json
import subprocess
import datetime
from pathlib import Path
from cryptography import x509
from cryptography.x509.oid import NameOID, ExtensionOID
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.backends import default_backend
import secrets
import string
class HelmSecurityManager:
"""
Helm安全管理器
提供憑證生成、儲存庫配置與驗證管理功能
"""
def __init__(self, base_dir="./helm_security"):
"""
初始化安全管理器
參數:
base_dir: 基礎目錄路徑
"""
self.base_dir = Path(base_dir)
self.ca_dir = self.base_dir / "ca"
self.certs_dir = self.base_dir / "certs"
self.configs_dir = self.base_dir / "configs"
# 建立必要的目錄結構
for directory in [self.ca_dir, self.certs_dir, self.configs_dir]:
directory.mkdir(parents=True, exist_ok=True)
print(f"Helm安全管理器已初始化,基礎目錄: {self.base_dir}")
def generate_ca_certificate(self,
common_name="Helm CA",
organization="My Organization",
validity_days=3650):
"""
生成憑證授權機構(CA)的根憑證
參數:
common_name: CA的通用名稱
organization: 組織名稱
validity_days: 憑證有效期(天)
回傳:
ca_cert_path: CA憑證檔案路徑
ca_key_path: CA私鑰檔案路徑
"""
print(f"\n生成CA憑證: {common_name}")
# 生成私鑰
private_key = rsa.generate_private_key(
public_exponent=65537,
key_size=4096, # 使用4096位元提供更強的安全性
backend=default_backend()
)
# 建立憑證主體
subject = issuer = x509.Name([
x509.NameAttribute(NameOID.COUNTRY_NAME, "TW"),
x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, "Taiwan"),
x509.NameAttribute(NameOID.LOCALITY_NAME, "Taipei"),
x509.NameAttribute(NameOID.ORGANIZATION_NAME, organization),
x509.NameAttribute(NameOID.COMMON_NAME, common_name),
])
# 建立自簽名CA憑證
cert = x509.CertificateBuilder().subject_name(
subject
).issuer_name(
issuer
).public_key(
private_key.public_key()
).serial_number(
x509.random_serial_number()
).not_valid_before(
datetime.datetime.utcnow()
).not_valid_after(
datetime.datetime.utcnow() + datetime.timedelta(days=validity_days)
).add_extension(
# 標記為CA憑證
x509.BasicConstraints(ca=True, path_length=0),
critical=True,
).add_extension(
# 設定密鑰用途
x509.KeyUsage(
digital_signature=True,
key_cert_sign=True, # 允許簽發其他憑證
crl_sign=True, # 允許簽發憑證撤銷清單
key_encipherment=False,
content_commitment=False,
data_encipherment=False,
key_agreement=False,
encipher_only=False,
decipher_only=False,
),
critical=True,
).sign(private_key, hashes.SHA256(), default_backend())
# 儲存CA憑證
ca_cert_path = self.ca_dir / "ca.crt"
with open(ca_cert_path, "wb") as f:
f.write(cert.public_bytes(serialization.Encoding.PEM))
# 儲存CA私鑰(需嚴格保護)
ca_key_path = self.ca_dir / "ca.key"
with open(ca_key_path, "wb") as f:
f.write(private_key.private_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PrivateFormat.PKCS8,
encryption_algorithm=serialization.BestAvailableEncryption(
b"changeme" # 生產環境應使用強密碼或HSM保護
)
))
# 設定檔案權限(僅所有者可讀寫)
os.chmod(ca_key_path, 0o600)
print(f"CA憑證已生成: {ca_cert_path}")
print(f"CA私鑰已生成: {ca_key_path}")
print(f"憑證有效期: {validity_days} 天")
return str(ca_cert_path), str(ca_key_path)
def generate_server_certificate(self,
hostname="chartmuseum.example.com",
validity_days=365):
"""
生成伺服器憑證用於Chart儲存庫
參數:
hostname: 伺服器主機名稱或IP
validity_days: 憑證有效期(天)
回傳:
cert_path: 伺服器憑證檔案路徑
key_path: 伺服器私鑰檔案路徑
"""
print(f"\n生成伺服器憑證: {hostname}")
# 載入CA憑證與私鑰
ca_cert_path = self.ca_dir / "ca.crt"
ca_key_path = self.ca_dir / "ca.key"
if not ca_cert_path.exists():
print("錯誤: CA憑證不存在,請先生成CA憑證")
return None, None
# 讀取CA憑證
with open(ca_cert_path, "rb") as f:
ca_cert = x509.load_pem_x509_certificate(
f.read(), default_backend()
)
# 讀取CA私鑰
with open(ca_key_path, "rb") as f:
ca_key = serialization.load_pem_private_key(
f.read(),
password=b"changeme", # 對應CA私鑰的密碼
backend=default_backend()
)
# 生成伺服器私鑰
server_key = rsa.generate_private_key(
public_exponent=65537,
key_size=2048,
backend=default_backend()
)
# 建立伺服器憑證主體
subject = x509.Name([
x509.NameAttribute(NameOID.COUNTRY_NAME, "TW"),
x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, "Taiwan"),
x509.NameAttribute(NameOID.LOCALITY_NAME, "Taipei"),
x509.NameAttribute(NameOID.ORGANIZATION_NAME, "My Organization"),
x509.NameAttribute(NameOID.COMMON_NAME, hostname),
])
# 建立伺服器憑證
server_cert = x509.CertificateBuilder().subject_name(
subject
).issuer_name(
ca_cert.subject # 由CA簽發
).public_key(
server_key.public_key()
).serial_number(
x509.random_serial_number()
).not_valid_before(
datetime.datetime.utcnow()
).not_valid_after(
datetime.datetime.utcnow() + datetime.timedelta(days=validity_days)
).add_extension(
# 不是CA憑證
x509.BasicConstraints(ca=False, path_length=None),
critical=True,
).add_extension(
# 設定伺服器憑證的密鑰用途
x509.KeyUsage(
digital_signature=True,
key_encipherment=True,
key_cert_sign=False,
crl_sign=False,
content_commitment=False,
data_encipherment=False,
key_agreement=False,
encipher_only=False,
decipher_only=False,
),
critical=True,
).add_extension(
# 指定為TLS伺服器驗證
x509.ExtendedKeyUsage([x509.oid.ExtendedKeyUsageOID.SERVER_AUTH]),
critical=True,
).add_extension(
# 添加主體別名(支援多個主機名稱/IP)
x509.SubjectAlternativeName([
x509.DNSName(hostname),
x509.DNSName(f"*.{hostname}"), # 萬用字元支援子網域
x509.IPAddress(__import__('ipaddress').ip_address("127.0.0.1")),
]),
critical=False,
).sign(ca_key, hashes.SHA256(), default_backend())
# 儲存伺服器憑證
cert_path = self.certs_dir / f"{hostname}.crt"
with open(cert_path, "wb") as f:
f.write(server_cert.public_bytes(serialization.Encoding.PEM))
# 儲存伺服器私鑰
key_path = self.certs_dir / f"{hostname}.key"
with open(key_path, "wb") as f:
f.write(server_key.private_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PrivateFormat.PKCS8,
encryption_algorithm=serialization.NoEncryption() # 生產環境應加密
))
os.chmod(key_path, 0o600)
print(f"伺服器憑證已生成: {cert_path}")
print(f"伺服器私鑰已生成: {key_path}")
return str(cert_path), str(key_path)
def generate_client_certificate(self,
username="helm-user",
validity_days=365):
"""
生成客戶端憑證用於Helm CLI
參數:
username: 使用者名稱
validity_days: 憑證有效期(天)
回傳:
cert_path: 客戶端憑證檔案路徑
key_path: 客戶端私鑰檔案路徑
"""
print(f"\n生成客戶端憑證: {username}")
# 載入CA憑證與私鑰
ca_cert_path = self.ca_dir / "ca.crt"
ca_key_path = self.ca_dir / "ca.key"
if not ca_cert_path.exists():
print("錯誤: CA憑證不存在,請先生成CA憑證")
return None, None
with open(ca_cert_path, "rb") as f:
ca_cert = x509.load_pem_x509_certificate(
f.read(), default_backend()
)
with open(ca_key_path, "rb") as f:
ca_key = serialization.load_pem_private_key(
f.read(),
password=b"changeme",
backend=default_backend()
)
# 生成客戶端私鑰
client_key = rsa.generate_private_key(
public_exponent=65537,
key_size=2048,
backend=default_backend()
)
# 建立客戶端憑證主體
subject = x509.Name([
x509.NameAttribute(NameOID.COUNTRY_NAME, "TW"),
x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, "Taiwan"),
x509.NameAttribute(NameOID.ORGANIZATION_NAME, "My Organization"),
x509.NameAttribute(NameOID.COMMON_NAME, username),
])
# 建立客戶端憑證
client_cert = x509.CertificateBuilder().subject_name(
subject
).issuer_name(
ca_cert.subject
).public_key(
client_key.public_key()
).serial_number(
x509.random_serial_number()
).not_valid_before(
datetime.datetime.utcnow()
).not_valid_after(
datetime.datetime.utcnow() + datetime.timedelta(days=validity_days)
).add_extension(
x509.BasicConstraints(ca=False, path_length=None),
critical=True,
).add_extension(
# 客戶端憑證的密鑰用途
x509.KeyUsage(
digital_signature=True,
key_encipherment=True,
key_cert_sign=False,
crl_sign=False,
content_commitment=False,
data_encipherment=False,
key_agreement=False,
encipher_only=False,
decipher_only=False,
),
critical=True,
).add_extension(
# 指定為TLS客戶端驗證
x509.ExtendedKeyUsage([x509.oid.ExtendedKeyUsageOID.CLIENT_AUTH]),
critical=True,
).sign(ca_key, hashes.SHA256(), default_backend())
# 儲存客戶端憑證
cert_path = self.certs_dir / f"{username}.crt"
with open(cert_path, "wb") as f:
f.write(client_cert.public_bytes(serialization.Encoding.PEM))
# 儲存客戶端私鑰
key_path = self.certs_dir / f"{username}.key"
with open(key_path, "wb") as f:
f.write(client_key.private_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PrivateFormat.PKCS8,
encryption_algorithm=serialization.NoEncryption()
))
os.chmod(key_path, 0o600)
print(f"客戶端憑證已生成: {cert_path}")
print(f"客戶端私鑰已生成: {key_path}")
return str(cert_path), str(key_path)
def generate_basic_auth_credentials(self, username="admin"):
"""
生成基本驗證憑據
參數:
username: 使用者名稱
回傳:
credentials: 包含使用者名稱和密碼的字典
"""
print(f"\n生成基本驗證憑據: {username}")
# 生成強隨機密碼(16字元,包含大小寫字母、數字與符號)
alphabet = string.ascii_letters + string.digits + "!@#$%^&*"
password = ''.join(secrets.choice(alphabet) for _ in range(16))
credentials = {
"username": username,
"password": password,
"created_at": datetime.datetime.utcnow().isoformat()
}
# 儲存憑據到配置檔案
creds_file = self.configs_dir / f"{username}_credentials.json"
with open(creds_file, "w") as f:
json.dump(credentials, f, indent=2)
os.chmod(creds_file, 0o600)
print(f"使用者名稱: {username}")
print(f"密碼: {password}")
print(f"憑據已儲存至: {creds_file}")
return credentials
def generate_helm_repo_config(self,
repo_name="my-charts",
repo_url="https://chartmuseum.example.com",
auth_type="cert",
username=None,
password=None,
ca_file=None,
cert_file=None,
key_file=None):
"""
生成Helm儲存庫配置指令
參數:
repo_name: 儲存庫名稱
repo_url: 儲存庫URL
auth_type: 驗證類型 ('basic' 或 'cert')
username: 基本驗證使用者名稱
password: 基本驗證密碼
ca_file: CA憑證檔案路徑
cert_file: 客戶端憑證檔案路徑
key_file: 客戶端私鑰檔案路徑
回傳:
commands: Helm命令列表
"""
print(f"\n生成Helm儲存庫配置指令")
print(f"儲存庫名稱: {repo_name}")
print(f"儲存庫URL: {repo_url}")
print(f"驗證類型: {auth_type}")
commands = []
if auth_type == "basic":
# 基本驗證配置
if not username or not password:
print("錯誤: 基本驗證需要提供使用者名稱和密碼")
return None
cmd = f"helm repo add {repo_name} {repo_url} \\\n"
cmd += f" --username {username} \\\n"
cmd += f" --password {password}"
if ca_file:
cmd += f" \\\n --ca-file {ca_file}"
commands.append(cmd)
elif auth_type == "cert":
# 憑證驗證配置
if not cert_file or not key_file:
print("錯誤: 憑證驗證需要提供客戶端憑證和私鑰")
return None
cmd = f"helm repo add {repo_name} {repo_url} \\\n"
cmd += f" --cert-file {cert_file} \\\n"
cmd += f" --key-file {key_file}"
if ca_file:
cmd += f" \\\n --ca-file {ca_file}"
commands.append(cmd)
# 添加更新與驗證指令
commands.append(f"helm repo update {repo_name}")
commands.append(f"helm search repo {repo_name}")
print("\n生成的Helm指令:")
print("-" * 70)
for cmd in commands:
print(cmd)
print()
# 儲存指令到腳本檔案
script_file = self.configs_dir / f"{repo_name}_setup.sh"
with open(script_file, "w") as f:
f.write("#!/bin/bash\n")
f.write(f"# Helm儲存庫配置腳本: {repo_name}\n")
f.write(f"# 生成時間: {datetime.datetime.now().isoformat()}\n\n")
for cmd in commands:
f.write(cmd + "\n\n")
os.chmod(script_file, 0o755)
print(f"配置腳本已儲存至: {script_file}")
return commands
# 使用範例
if __name__ == "__main__":
# 初始化安全管理器
manager = HelmSecurityManager(base_dir="./helm_security")
print("="*70)
print("Helm Chart安全配置管理系統")
print("="*70)
# 步驟1: 生成CA憑證
print("\n步驟1: 生成憑證授權機構(CA)")
ca_cert, ca_key = manager.generate_ca_certificate(
common_name="Helm Chart CA",
organization="BlackCat DevOps",
validity_days=3650
)
# 步驟2: 生成伺服器憑證
print("\n步驟2: 生成Chart儲存庫伺服器憑證")
server_cert, server_key = manager.generate_server_certificate(
hostname="chartmuseum.blackcat.tw",
validity_days=365
)
# 步驟3: 生成客戶端憑證
print("\n步驟3: 生成Helm客戶端憑證")
client_users = ["devops-user", "ci-cd-bot", "admin-user"]
client_certs = {}
for user in client_users:
cert, key = manager.generate_client_certificate(
username=user,
validity_days=365
)
client_certs[user] = {"cert": cert, "key": key}
# 步驟4: 生成基本驗證憑據
print("\n步驟4: 生成基本驗證憑據")
basic_creds = manager.generate_basic_auth_credentials(username="helm-admin")
# 步驟5: 生成憑證驗證的Helm配置
print("\n步驟5: 生成憑證驗證的Helm配置")
cert_commands = manager.generate_helm_repo_config(
repo_name="secure-charts",
repo_url="https://chartmuseum.blackcat.tw",
auth_type="cert",
ca_file=ca_cert,
cert_file=client_certs["devops-user"]["cert"],
key_file=client_certs["devops-user"]["key"]
)
# 步驟6: 生成基本驗證的Helm配置
print("\n步驟6: 生成基本驗證的Helm配置")
basic_commands = manager.generate_helm_repo_config(
repo_name="private-charts",
repo_url="https://chartmuseum.blackcat.tw",
auth_type="basic",
username=basic_creds["username"],
password=basic_creds["password"],
ca_file=ca_cert
)
print("\n" + "="*70)
print("安全配置完成!")
print("="*70)
print(f"\n所有檔案已儲存至: {manager.base_dir}")
print("\n下一步:")
print("1. 將CA憑證分發給所有使用者")
print("2. 將伺服器憑證配置到ChartMuseum")
print("3. 將客戶端憑證分發給對應使用者")
print("4. 執行生成的配置腳本添加Helm儲存庫")
這段完整的Helm安全配置管理系統展示了如何自動化建立企業級的憑證管理與驗證機制。HelmSecurityManager類別封裝了從CA建立到客戶端配置的完整流程,使用cryptography函式庫生成符合X.509標準的數位憑證。CA憑證生成功能建立自簽名的根憑證授權機構,使用4096位元RSA金鑰提供強加密保護,設定BasicConstraints標記為CA並啟用密鑰簽章與CRL簽章能力,私鑰使用密碼加密儲存並設定嚴格的檔案權限防止未授權存取。伺服器憑證生成針對Chart儲存庫建立由CA簽發的憑證,配置SubjectAlternativeName支援多個主機名稱與IP位址,設定ExtendedKeyUsage限定用於TLS伺服器驗證,並包含萬用字元網域支援子網域存取。客戶端憑證生成為每個使用者或服務建立獨立的身份憑證,限定用於TLS客戶端驗證確保不會被誤用為伺服器憑證。基本驗證憑據生成使用secrets模組產生高強度隨機密碼包含大小寫字母數字與特殊符號,並將憑據以JSON格式儲存供後續查閱。Helm儲存庫配置生成根據選擇的驗證方式自動產生對應的helm repo add命令,包含所有必要的參數與檔案路徑,並建立可執行的Shell腳本簡化使用者操作。
ChartMuseum安全強化與TLS配置
ChartMuseum是開源的Helm Chart儲存庫伺服器,提供簡單的HTTP API支援Chart的上傳下載與索引管理。然而預設配置缺乏安全防護機制,需要透過額外配置啟用驗證與加密功能。基本驗證配置透過BASIC_AUTH_USER與BASIC_AUTH_PASS環境變數或命令列參數設定單一使用者名稱密碼組合,適合小型團隊的快速部署但無法支援多使用者與角色區分。憑證驗證配置需要啟用TLS並設定客戶端憑證驗證,透過TLS_CA_CERT參數指定信任的CA憑證用於驗證客戶端身份。TLS加密配置使用TLS_CERT與TLS_KEY參數指定伺服器憑證與私鑰檔案,確保所有HTTP通訊經過加密防止中間人攻擊與資料竊聽。儲存後端安全需要根據使用的儲存類型配置相應的存取控制,本機檔案系統需要設定適當的檔案權限,雲端物件儲存如S3或GCS需要配置IAM角色與存取策略,確保只有ChartMuseum程序能夠讀寫Chart檔案。
# ChartMuseum安全部署配置
# Kubernetes Deployment與Service定義
apiVersion: v1
kind: Namespace
metadata:
name: chartmuseum
labels:
name: chartmuseum
---
# ConfigMap儲存非敏感配置
apiVersion: v1
kind: ConfigMap
metadata:
name: chartmuseum-config
namespace: chartmuseum
data:
# ChartMuseum配置參數
STORAGE: "local"
STORAGE_LOCAL_ROOTDIR: "/charts"
PORT: "8080"
DEBUG: "false"
LOG_JSON: "true"
DISABLE_METRICS: "false"
DISABLE_API: "false"
ALLOW_OVERWRITE: "false"
# 啟用深度檢查確保Chart完整性
DEPTH: "0"
# 索引快取時間(秒)
CACHE: "300"
# 啟用Chart URL重寫
CHART_URL: "https://chartmuseum.blackcat.tw"
---
# Secret儲存敏感資訊
apiVersion: v1
kind: Secret
metadata:
name: chartmuseum-auth
namespace: chartmuseum
type: Opaque
stringData:
# 基本驗證憑據
BASIC_AUTH_USER: "admin"
# 注意: 生產環境應使用更複雜的密碼並定期輪換
BASIC_AUTH_PASS: "P@ssw0rd!2024"
---
# Secret儲存TLS憑證
apiVersion: v1
kind: Secret
metadata:
name: chartmuseum-tls
namespace: chartmuseum
type: kubernetes.io/tls
data:
# Base64編碼的TLS憑證
# 使用: kubectl create secret tls chartmuseum-tls --cert=server.crt --key=server.key -n chartmuseum
tls.crt: LS0tLS1CRUdJTi... # 伺服器憑證內容
tls.key: LS0tLS1CRUdJTi... # 伺服器私鑰內容
---
# Secret儲存CA憑證用於客戶端驗證
apiVersion: v1
kind: Secret
metadata:
name: chartmuseum-ca
namespace: chartmuseum
type: Opaque
data:
# Base64編碼的CA憑證
ca.crt: LS0tLS1CRUdJTi... # CA憑證內容
---
# PersistentVolumeClaim用於Chart儲存
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: chartmuseum-storage
namespace: chartmuseum
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
# 根據Chart數量調整儲存容量
storage: 10Gi
# 指定儲存類別確保效能與可靠性
storageClassName: fast-ssd
---
# Deployment部署ChartMuseum
apiVersion: apps/v1
kind: Deployment
metadata:
name: chartmuseum
namespace: chartmuseum
labels:
app: chartmuseum
spec:
# 單一副本避免並發寫入衝突(使用本機儲存時)
# 使用雲端物件儲存可以增加副本數
replicas: 1
selector:
matchLabels:
app: chartmuseum
template:
metadata:
labels:
app: chartmuseum
spec:
# 使用專用Service Account限制權限
serviceAccountName: chartmuseum
# 安全上下文配置
securityContext:
# 使用非root使用者執行
runAsNonRoot: true
runAsUser: 1000
fsGroup: 1000
# 強制SELinux標籤
seLinuxOptions:
type: container_runtime_t
containers:
- name: chartmuseum
# 使用官方映像檔指定版本標籤確保可重現部署
image: ghcr.io/helm/chartmuseum:v0.16.0
imagePullPolicy: IfNotPresent
# 容器安全設定
securityContext:
# 禁止權限提升
allowPrivilegeEscalation: false
# 移除所有Linux capabilities
capabilities:
drop:
- ALL
# 唯讀根檔案系統
readOnlyRootFilesystem: true
# 環境變數配置
env:
# 從ConfigMap載入一般配置
- name: STORAGE
valueFrom:
configMapKeyRef:
name: chartmuseum-config
key: STORAGE
- name: STORAGE_LOCAL_ROOTDIR
valueFrom:
configMapKeyRef:
name: chartmuseum-config
key: STORAGE_LOCAL_ROOTDIR
- name: PORT
valueFrom:
configMapKeyRef:
name: chartmuseum-config
key: PORT
- name: DEBUG
valueFrom:
configMapKeyRef:
name: chartmuseum-config
key: DEBUG
- name: LOG_JSON
valueFrom:
configMapKeyRef:
name: chartmuseum-config
key: LOG_JSON
- name: CHART_URL
valueFrom:
configMapKeyRef:
name: chartmuseum-config
key: CHART_URL
- name: ALLOW_OVERWRITE
valueFrom:
configMapKeyRef:
name: chartmuseum-config
key: ALLOW_OVERWRITE
# 從Secret載入敏感配置
- name: BASIC_AUTH_USER
valueFrom:
secretKeyRef:
name: chartmuseum-auth
key: BASIC_AUTH_USER
- name: BASIC_AUTH_PASS
valueFrom:
secretKeyRef:
name: chartmuseum-auth
key: BASIC_AUTH_PASS
# TLS配置
- name: TLS_CERT
value: "/tls/tls.crt"
- name: TLS_KEY
value: "/tls/tls.key"
# 客戶端憑證驗證(可選)
# 啟用後只有持有有效客戶端憑證的使用者可以存取
- name: TLS_CA_CERT
value: "/ca/ca.crt"
# 容器埠配置
ports:
- containerPort: 8080
name: http
protocol: TCP
# 健康檢查探針
livenessProbe:
httpGet:
path: /health
port: http
scheme: HTTPS # 使用HTTPS因為啟用了TLS
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
readinessProbe:
httpGet:
path: /health
port: http
scheme: HTTPS
initialDelaySeconds: 10
periodSeconds: 5
timeoutSeconds: 3
failureThreshold: 3
# 資源限制防止資源耗盡
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 512Mi
# 掛載Volume
volumeMounts:
# Chart資料儲存
- name: storage
mountPath: /charts
# TLS憑證
- name: tls
mountPath: /tls
readOnly: true
# CA憑證
- name: ca
mountPath: /ca
readOnly: true
# 暫存目錄(因為使用唯讀根檔案系統)
- name: tmp
mountPath: /tmp
# Volume定義
volumes:
# 持久化儲存
- name: storage
persistentVolumeClaim:
claimName: chartmuseum-storage
# TLS憑證Secret
- name: tls
secret:
secretName: chartmuseum-tls
defaultMode: 0400 # 唯讀權限
# CA憑證Secret
- name: ca
secret:
secretName: chartmuseum-ca
defaultMode: 0400
# 空目錄作為暫存空間
- name: tmp
emptyDir: {}
---
# Service暴露ChartMuseum服務
apiVersion: v1
kind: Service
metadata:
name: chartmuseum
namespace: chartmuseum
labels:
app: chartmuseum
spec:
type: ClusterIP
ports:
- port: 443
targetPort: 8080
protocol: TCP
name: https
selector:
app: chartmuseum
---
# ServiceAccount限制Pod權限
apiVersion: v1
kind: ServiceAccount
metadata:
name: chartmuseum
namespace: chartmuseum
---
# Ingress配置外部存取
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: chartmuseum
namespace: chartmuseum
annotations:
# 使用cert-manager自動管理憑證
cert-manager.io/cluster-issuer: "letsencrypt-prod"
# Nginx Ingress配置
nginx.ingress.kubernetes.io/backend-protocol: "HTTPS"
# 啟用TLS客戶端憑證驗證(可選)
nginx.ingress.kubernetes.io/auth-tls-verify-client: "optional"
nginx.ingress.kubernetes.io/auth-tls-secret: "chartmuseum/chartmuseum-ca"
# 速率限制防止DDoS
nginx.ingress.kubernetes.io/limit-rps: "10"
# 請求大小限制(Chart檔案可能較大)
nginx.ingress.kubernetes.io/proxy-body-size: "50m"
spec:
ingressClassName: nginx
tls:
- hosts:
- chartmuseum.blackcat.tw
secretName: chartmuseum-tls-ingress
rules:
- host: chartmuseum.blackcat.tw
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: chartmuseum
port:
number: 443
這份完整的ChartMuseum Kubernetes部署配置展示了企業級的安全最佳實踐。配置使用Namespace隔離ChartMuseum相關資源提供多租戶支援。ConfigMap儲存非敏感的配置參數如儲存類型端口號日誌格式等,方便集中管理與版本控制。Secret分別儲存基本驗證憑據TLS憑證與CA憑證,Kubernetes自動處理Secret的加密與存取控制確保敏感資訊不會洩露。PersistentVolumeClaim提供持久化儲存確保Chart檔案在Pod重啟後不會遺失,建議使用高效能SSD儲存類別並定期備份。Deployment配置採用嚴格的安全設定,使用非root使用者執行容器防止權限提升攻擊,啟用唯讀根檔案系統減少攻擊面,移除所有不必要的Linux capabilities遵循最小權限原則。健康檢查探針確保服務可用性並支援自動重啟故障實例。資源限制防止單一Pod耗盡節點資源影響其他工作負載。Service使用ClusterIP類型在叢集內部暴露服務,外部存取透過Ingress控制並配置TLS終止。Ingress整合cert-manager自動申請與更新Let’s Encrypt憑證,配置速率限制與請求大小限制防止濫用,可選啟用客戶端憑證驗證提供雙向TLS保護。
@startuml
!define PLANTUML_FORMAT svg
!theme _none_
skinparam dpi auto
skinparam shadowing false
skinparam linetype ortho
skinparam roundcorner 5
skinparam defaultFontName "Microsoft JhengHei UI"
skinparam defaultFontSize 16
skinparam minClassWidth 120
title Helm Chart安全架構與防護體系
package "使用者層" {
actor "開發者" as DEV
actor "CI/CD系統" as CICD
actor "運維人員" as OPS
actor "安全審計員" as AUDIT
}
package "身份驗證層" {
[基本驗證] as BASIC_AUTH
[憑證驗證] as CERT_AUTH
[OAuth2/OIDC] as OAUTH
[IAM整合] as IAM
}
package "傳輸安全層" {
[TLS加密] as TLS
[mTLS雙向認證] as MTLS
[VPN隧道] as VPN
}
package "存取控制層" {
[Kubernetes RBAC] as RBAC
[Chart權限] as CHART_PERM
[命名空間隔離] as NS_ISOLATION
[網路策略] as NETPOL
}
package "Chart儲存庫" {
[ChartMuseum] as CHARTMUSEUM
[Harbor] as HARBOR
[Artifactory] as ARTIFACTORY
[雲端儲存] as CLOUD_STORAGE
}
package "Chart安全檢查" {
[來源驗證] as PROVENANCE
[簽章檢查] as SIGNATURE
[漏洞掃描] as VULN_SCAN
[政策檢查] as POLICY_CHECK
}
package "Kubernetes叢集" {
[命名空間A] as NS_A
[命名空間B] as NS_B
[Service Account] as SA
[Pod安全政策] as PSP
}
package "監控與稽核" {
[存取日誌] as ACCESS_LOG
[審計追蹤] as AUDIT_TRAIL
[異常偵測] as ANOMALY
[告警系統] as ALERT
}
database "憑證管理" {
[CA憑證]
[伺服器憑證]
[客戶端憑證]
[憑證撤銷清單]
}
database "政策儲存庫" {
[安全政策]
[合規規則]
[准入控制]
}
DEV --> BASIC_AUTH : 使用者名稱/密碼
DEV --> CERT_AUTH : 客戶端憑證
CICD --> OAUTH : Service Token
OPS --> IAM : 雲端身份
AUDIT --> ACCESS_LOG : 查詢記錄
BASIC_AUTH --> TLS : 加密通道
CERT_AUTH --> MTLS : 雙向驗證
OAUTH --> TLS : 加密通道
IAM --> VPN : 專用網路
TLS --> CHARTMUSEUM : HTTPS請求
MTLS --> HARBOR : 安全連線
VPN --> ARTIFACTORY : 內部存取
CHARTMUSEUM --> PROVENANCE : Chart驗證
HARBOR --> SIGNATURE : 簽章核對
ARTIFACTORY --> VULN_SCAN : 安全掃描
PROVENANCE --> RBAC : 通過驗證
SIGNATURE --> CHART_PERM : 權限檢查
VULN_SCAN --> POLICY_CHECK : 政策評估
RBAC --> NS_A : 部署權限
RBAC --> NS_B : 唯讀權限
CHART_PERM --> SA : 綁定角色
NS_ISOLATION --> PSP : 安全限制
NS_A --> ACCESS_LOG : 記錄操作
NS_B --> AUDIT_TRAIL : 追蹤變更
PSP --> ANOMALY : 行為分析
ANOMALY --> ALERT : 觸發告警
憑證管理 --> CERT_AUTH : 提供憑證
憑證管理 --> MTLS : 憑證驗證
政策儲存庫 --> POLICY_CHECK : 載入規則
政策儲存庫 --> PSP : 強制執行
note right of BASIC_AUTH
驗證機制特性:
- 基本驗證: 簡單但安全性較低
- 憑證驗證: 強安全性與自動化
- OAuth2: 細緻權限與SSO
- IAM: 雲端原生整合
end note
note right of PROVENANCE
Chart安全檢查:
1. 來源驗證確認Chart發布者
2. 數位簽章防止竄改
3. 漏洞掃描識別安全問題
4. 政策檢查符合規範要求
end note
note right of RBAC
多層存取控制:
- Kubernetes RBAC控制叢集資源
- Chart權限限制可用Chart
- 命名空間隔離防止跨租戶存取
- 網路策略控制Pod通訊
end note
note right of ACCESS_LOG
監控與回應:
- 即時存取日誌記錄所有操作
- 審計追蹤支援合規查核
- 異常偵測識別可疑行為
- 自動告警加速事件回應
end note
@enduml這張Helm Chart安全架構圖完整呈現從使用者到叢集的多層防護機制。使用者層區分不同角色的存取需求,開發者進行日常Chart的查詢與部署,CI/CD系統執行自動化流程需要服務帳戶權限,運維人員管理儲存庫與叢集配置,安全審計員檢視日誌與合規報告。每個角色根據職責需要不同的驗證方式與權限範圍,遵循最小權限原則減少安全風險。
身份驗證層提供多種機制滿足不同安全需求。基本驗證適合開發與測試環境的快速配置,但生產環境應避免使用因為密碼管理困難且缺乏細緻控制。憑證驗證提供強認證與加密,每個使用者或服務持有唯一的客戶端憑證便於審計與撤銷,自動化工具如cert-manager簡化憑證生命週期管理。OAuth2與OpenID Connect整合企業身份提供商如Azure AD或Okta實現單一登入並支援多因素認證,透過Token scope控制細緻權限。雲端IAM整合如AWS IAM Roles或GCP Service Account利用雲端平台的身份服務簡化跨服務認證,特別適合雲端原生部署環境。
傳輸安全層確保資料在網路傳輸過程中的機密性與完整性。TLS加密所有HTTP通訊防止中間人攻擊與資料竊聽,使用TLS 1.2或更高版本並配置強密碼套件。mTLS雙向認證不僅伺服器驗證客戶端身份客戶端也驗證伺服器身份,在服務網格架構中廣泛應用提供Pod層級的安全通訊。VPN隧道在不安全網路環境中建立加密通道,適合遠端存取或跨資料中心的Chart同步。傳輸層安全是基礎防護但不能替代應用層的身份驗證與授權。
存取控制層實施細緻的權限管理。Kubernetes RBAC定義誰可以對哪些資源執行什麼操作,透過Role與RoleBinding綁定使用者或服務帳戶到特定權限集合。Chart權限控制哪些使用者可以存取哪些Chart,私有Chart只對授權團隊開放而公共Chart允許所有使用者讀取。命名空間隔離將不同專案或團隊的資源分隔避免互相干擾,每個命名空間有獨立的配額與網路策略。網路策略控制Pod之間的通訊只允許必要的網路流量,實現微隔離防止橫向移動攻擊。
Chart安全檢查在部署前驗證Chart的可信度與安全性。來源驗證透過Helm provenance檔案確認Chart的發布者身份,provenance檔案包含Chart的雜湊值與發布者的數位簽章由GPG金鑰簽發。簽章檢查驗證Chart內容未被竄改,任何修改會導致簽章驗證失敗。漏洞掃描工具如Trivy或Anchore分析Chart中引用的容器映像檔識別已知的安全漏洞並提供修復建議。政策檢查使用Open Policy Agent等工具評估Chart是否符合組織的安全政策,例如禁止特權容器限制資源請求或強制使用特定Registry。
監控與稽核層提供持續的可見性與事件回應能力。存取日誌記錄每次Chart的上傳下載與部署操作包含時間戳使用者身份與資源標識符。審計追蹤將日誌聚合到集中式平台如Elasticsearch支援查詢與分析,滿足合規要求如SOC2或ISO27001。異常偵測使用機器學習識別不尋常的行為模式如非工作時間的大量下載或頻繁失敗的認證嘗試,提前發現潛在的安全事件。告警系統在偵測到異常時通知安全團隊並可以觸發自動回應如暫時封鎖可疑使用者或要求多因素認證。
建議企業根據安全需求與現有基礎設施選擇適當的安全機制組合,小型團隊可以從基本驗證與TLS加密開始逐步增強安全性,大型企業應該實施憑證驗證RBAC整合與持續監控建構縱深防禦體系。定期進行安全評估與滲透測試識別潛在弱點,培訓開發與運維人員提升安全意識,建立事件回應計畫確保發生安全事件時能夠快速遏制與恢復。安全是持續改進的過程而非一次性專案,需要組織各層級的承諾與投入才能有效保護Helm Chart與Kubernetes叢集的安全。