Appearance
SFTP on Kubernetes 部署
在 Kubernetes 中部署 SFTP 服務的兩種方案:輕量的 atmoz/sftp 與功能完整的 SFTPGo(推薦)。
概述
在 Kubernetes 環境中提供 SFTP 服務,有兩種主流選擇。atmoz/sftp 是一個輕量級映像,設定方式直覺,但有一個重要限制:每個使用者必須掛載獨立的 subPath,無法統一掛載在 home 目錄根目錄,對多使用者管理較不便。
SFTPGo 是功能更完整的解決方案,支援 Web 管理介面、多使用者管理、虛擬資料夾,並可同時搭配 Nginx 提供 HTTP 檔案瀏覽服務,是實際部署的推薦選擇。兩個方案都使用標準的 SFTP 協定(Port 22 或 2022),可透過 NodePort 或 Azure Internal LoadBalancer 對外暴露服務。
核心內容
方案比較
| 特性 | atmoz/sftp | SFTPGo |
|---|---|---|
| 映像 | atmoz/sftp | drakkan/sftpgo |
| Web 管理介面 | 無 | 有(Port 8080) |
| 使用者管理 | ConfigMap 靜態設定 | Web UI 動態管理 |
| Volume 掛載 | 每個使用者需獨立 subPath | 統一掛載,靈活分配 |
| HTTP 檔案瀏覽 | 不支援 | 可搭配 Nginx Sidecar |
| 容器權限需求 | 需要 SYS_ADMIN capability | 無特殊需求 |
| 推薦程度 | 簡單場景 | 一般推薦 |
SFTPGo 目錄結構設計
SFTPGo 使用三個 subPath 分別掛載不同用途的目錄:
| Volume subPath | 容器內路徑 | 用途 |
|---|---|---|
upload | /srv/sftpgo | 實際存放上傳檔案的目錄 |
home | /var/lib/sftpgo | SFTPGo 內部資料(使用者設定等) |
config | /etc/sftpgo | SFTPGo 設定檔 |
Nginx Sidecar 掛載同一個 PVC 的 upload subPath(唯讀),提供 HTTP 瀏覽功能,與 SFTPGo 共享同一份檔案。
AKS 與本地 K8s 的部署差異
在 AKS 環境中,Service 架構有所不同:
- Nginx 檔案瀏覽:透過 Ingress 對外暴露(HTTP/HTTPS)
- SFTP:透過獨立的 Azure Internal LoadBalancer Service 暴露(TCP 協定)
- WebAdmin 管理介面:同時透過 Internal LB(port 8080)及 Ingress 外部 domain + subpath(
bj6.wiwynn.com/sftpgo)兩種方式存取;Ingress 方式需搭配WEB_ROOT=/sftpgo環境變數,讓 SFTPGo 自行處理 subpath,不需要rewrite-target - 本地 K8s 通常使用 NodePort 即可應付所有服務
AKS 版本加入了時區設定(TZ=Asia/Taipei)、資源限制。最新架構已將檔案瀏覽服務從 Nginx Sidecar 改為獨立的 Caddy Deployment(sftpgo-caddy-server):
- SFTPGo Deployment:純粹負責 SFTP 服務與 WebAdmin,不再包含任何 Sidecar
- sftpgo-caddy-server Deployment:3 replicas +
requiredDuringSchedulingpod anti-affinity,確保跨節點高可用;每個 Pod 含兩個 Caddy 容器,分別服務內部(port 80)與公開存取(port 81) - Caddy 的 admin/metrics endpoint(port 2019/2020)透過獨立的 Internal LB Service 供 Prometheus 抓取(詳見 Caddy 靜態檔案伺服器)
WebAdmin CSRF Token 問題與修復(AKS 必做)
SFTPGo 的 WebAdmin 使用 JWT + CSRF Token 進行 session 管理,在 K8s + Azure LB 環境中有兩道常見失效機制,任何一道觸發都會出現「The form token is not valid」錯誤:
機制 A:Signing Key 未固定
signing_passphrase 若未設定,每次 Pod 啟動都會產生隨機金鑰:
- Pod 重啟 → 瀏覽器中的舊 cookie 立即失效
- 多 replica → 不同 Pod 各有金鑰 → 跨 Pod 的 token 驗證失敗
機制 B:IP Binding(AKS 預設觸發)
SFTPGo 預設將 token 綁定到簽發時的 Source IP。AKS 預設 externalTrafficPolicy: Cluster,kube-proxy 做 SNAT 後 Pod 看到的是「轉發節點的 IP」,而非真實 client IP。Azure LB 把 GET /login 和 POST /login 分散到不同節點 → Pod 眼中是兩個不同 IP → CSRF 驗證失敗。
三項修復(建議全上):
| 修復 | 設定 | 說明 |
|---|---|---|
| 修復 1 | SFTPGO_HTTPD__SIGNING_PASSPHRASE(Secret) | 固定簽章金鑰,解決 Pod 重啟和多副本問題 |
| 修復 2 | SFTPGO_HTTPD__TOKEN_VALIDATION=0 | 停用 IP 綁定檢查(0 = 不驗證 IP),K8s + LB 環境必備 |
| 修復 3 | Service 加 externalTrafficPolicy: Local | Pod 看到真實 client IP,讓 audit log、rate limit 正確運作;單副本無副作用,多副本須搭配 pod anti-affinity |
Scale-up 前置準備(HA 時才需要): SQLite on PVC 無法多副本共享,需換成外部 PostgreSQL/MySQL,並設定 SFTPGO_DATA_PROVIDER__IS_SHARED: "1" 讓 active transfers、password reset、OIDC tokens 寫入資料庫以跨副本共享狀態。
使用者 SSH Key 認證
SFTPGo 的 SSH Key 分兩類:Server Host Key(Server 身份,啟動時自動產生至 /var/lib/sftpgo/,PVC 掛載後重啟不變)與User Public Key(使用者認證,需手動設定)。
以下為使用者認證金鑰設定流程:
- 在用戶端機器產金鑰對(私鑰原則上只有使用者自己碰)
- 將公鑰上傳至 SFTPGo(WebAdmin UI 或 REST API)
- 測試連線
金鑰演算法建議 Ed25519;若連接方為老舊 Java ETL 或 Azure ADF 不支援 ed25519,改用 RSA-4096。
WebAdmin 進階認證設定:
- Key + 密碼雙驗(類 2FA):Login methods 勾
publickey+password - 僅允許 Key 登入:password 欄位留空 + Login methods 只勾
publickey - 一人一把 key 才能在 audit log 中追蹤到個人操作
關鍵要點
- SFTPGo 是推薦方案,提供 Web 管理介面和更靈活的使用者管理
- SFTPGo 搭配 Nginx Sidecar 共享同一 PVC,可同時提供 SFTP 和 HTTP 存取
- AKS 環境中 SFTP 服務透過 Internal LoadBalancer 而非 Ingress 暴露(TCP 協定)
- atmoz/sftp 需要
SYS_ADMINcapability,存在安全考量 - Ingress 需設定大 proxy-body-size(建議 1024m)以支援大檔案上傳
- AKS 部署必須設定
signing_passphraseSecret +TOKEN_VALIDATION=0(停用 IP 綁定),否則 WebAdmin 登入必定失敗(CSRF token 驗證) - WebAdmin 可透過 Ingress(
bj6.wiwynn.com/sftpgo)暴露,需搭配WEB_ROOT=/sftpgo;Ingress path 不需rewrite-target,SFTPGo 自行處理 subpath externalTrafficPolicy: Local讓 audit log 記錄真實 client IP,單副本下無副作用- 使用者 SSH Key 建議一人一把,並在用戶端機器產生(私鑰不離開使用者手中)
實際應用
檔案分享場景中,SFTPGo + Nginx 的組合讓使用者可以用 SFTP 客戶端上傳,再透過瀏覽器 HTTP 下載,非常適合作為內部檔案分發平台。搭配 Azure Internal LoadBalancer 固定 IP 可確保 SFTP 連線端點穩定,Ingress 則提供 HTTPS 保護的 Web 介面。另可參考類似架構的 Samba + Nginx 檔案分享服務(Kubernetes 部署)。
部署設定參考
以下為實際部署時使用的完整設定,供日後查詢與複製使用。
環境參數
| 項目 | 本地 K8s | AKS |
|---|---|---|
| Namespace | sftp | ingress-basic |
| SFTPGo 映像 | drakkan/sftpgo(latest) | drakkan/sftpgo:v2.7 |
| 檔案瀏覽映像 | nginx:stable(Sidecar) | caddy:2-alpine(獨立 Deployment,3 replicas) |
| SFTP Port | 2022 (NodePort 30022) | 22 → 2022 (Internal LB 10.30.196.60) |
| 管理介面 Port | 8080 (NodePort 30023) | 8080(Internal LB 10.30.196.60)/ bj6.wiwynn.com/sftpgo(Ingress HTTPS) |
| 檔案瀏覽 Port | 80 (NodePort 30024) | 80(內部 Ingress)/ 81(公開 Ingress) |
| Caddy Metrics | — | 2019/2020 (Internal LB 10.30.196.60) |
| PVC 大小 | 50Gi | 50Gi |
| StorageClass | nfs-client | managed-csi(視設定) |
atmoz/sftp 完整設定
Deployment + Service
yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: sftp-server
namespace: sftp
labels:
app: sftp-server
spec:
replicas: 1
selector:
matchLabels:
app: sftp-server
template:
metadata:
labels:
app: sftp-server
spec:
containers:
- name: sftp-server
image: atmoz/sftp
imagePullPolicy: IfNotPresent
ports:
- containerPort: 22
volumeMounts:
- mountPath: /home/bj6-admin
name: sftp-volume
subPath: bj6-admin
- mountPath: /etc/sftp/users.conf
name: sftp-users
subPath: users
securityContext:
capabilities:
add: ["SYS_ADMIN"]
volumes:
- name: sftp-volume
persistentVolumeClaim:
claimName: sftp-pvc
readOnly: false
- name: sftp-users
configMap:
name: sftp-users
---
apiVersion: v1
kind: Service
metadata:
name: sftp-server-service
namespace: sftp
spec:
type: NodePort
ports:
- port: 22
nodePort: 30022
targetPort: 22
protocol: TCP
selector:
app: sftp-serverPVC
yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: sftp-pvc
namespace: sftp
spec:
accessModes:
- ReadWriteOnce
storageClassName: nfs-client
resources:
requests:
storage: 30GiConfigMap(使用者設定)
yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: sftp-users
namespace: sftp
data:
users: |
bj6-admin:CMzK5uKd:1001:100:upload
bj6-user:HupWSmHx:1002:100:upload使用者格式:
使用者名稱:密碼:UID:GID:目錄
SFTPGo 本地 K8s 設定
Deployment + Service
yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: sftpgo-server
namespace: sftp
labels:
app: sftpgo-server
spec:
replicas: 1
selector:
matchLabels:
app: sftpgo-server
template:
metadata:
labels:
app: sftpgo-server
spec:
containers:
- name: sftpgo-server
image: drakkan/sftpgo
imagePullPolicy: IfNotPresent
ports:
- containerPort: 2022
- containerPort: 8080
volumeMounts:
- mountPath: /srv/sftpgo
name: sftpgo-volume
subPath: upload
- mountPath: /var/lib/sftpgo
name: sftpgo-volume
subPath: home
- mountPath: /etc/sftpgo
name: sftpgo-volume
subPath: config
- name: nginx-server
image: nginx:stable
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
volumeMounts:
- mountPath: /usr/share/nginx/html
name: sftpgo-volume
subPath: upload
- name: nginx-config-volume
mountPath: /etc/nginx/conf.d/default.conf
subPath: nginx.conf
volumes:
- name: sftpgo-volume
persistentVolumeClaim:
claimName: sftpgo-pvc
readOnly: false
- name: nginx-config-volume
configMap:
name: sftpgo-nginx-config
---
apiVersion: v1
kind: Service
metadata:
name: sftpgo-server-service
namespace: sftp
spec:
type: NodePort
ports:
- name: sftp
port: 2022
nodePort: 30022
targetPort: 2022
- name: http
port: 8080
nodePort: 30023
targetPort: 8080
- name: nginx
port: 80
nodePort: 30024
targetPort: 80
selector:
app: sftpgo-serverPVC
yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: sftpgo-pvc
namespace: sftp
spec:
accessModes:
- ReadWriteOnce
storageClassName: nfs-client
resources:
requests:
storage: 50GiNginx 設定(本地版)
nginx
server {
listen 80;
root /usr/share/nginx/html;
location / {
autoindex on;
}
charset utf-8;
}建立/更新 ConfigMap
bash
kubectl create configmap sftpgo-nginx-config --from-file=nginx.conf -n sftp -o yaml --dry-run | kubectl replace -f -SFTPGo AKS 設定
Secret(WebAdmin Signing Passphrase)
產生隨機 passphrase:
bash
openssl rand -base64 48建立 Secret(務必用 stringData: 而非 data: + base64,避免二次 base64 編碼問題):
yaml
apiVersion: v1
kind: Secret
metadata:
name: sftpgo-signing
namespace: ingress-basic
type: Opaque
stringData:
signing-passphrase: ygdlkyPFa4Saqwvw96d9ZeXiNEAz6OURZVjC465TESA4n0/9dIexBHMZEMgKckwuSFTPGo Deployment
SFTPGo 僅負責 SFTP 服務與 WebAdmin,不再包含 Sidecar。
yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: sftpgo-server
namespace: ingress-basic
labels:
app: sftpgo-server
spec:
replicas: 1
selector:
matchLabels:
app: sftpgo-server
template:
metadata:
labels:
app: sftpgo-server
spec:
containers:
- name: sftpgo-server
image: drakkan/sftpgo:v2.7
imagePullPolicy: Always
env:
- name: TZ
value: Asia/Taipei
- name: SFTPGO_HTTPD__SIGNING_PASSPHRASE
valueFrom:
secretKeyRef:
name: sftpgo-signing
key: signing-passphrase
- name: SFTPGO_HTTPD__TOKEN_VALIDATION
value: "0"
- name: SFTPGO_HTTPD__TOKEN_AUTH_EXPIRY_HOURS
value: "24"
- name: SFTPGO_HTTPD__WEB_ROOT
value: "/sftpgo"
ports:
- containerPort: 2022
- containerPort: 8080
volumeMounts:
- mountPath: /srv/sftpgo
name: sftpgo-volume
subPath: upload
- mountPath: /var/lib/sftpgo
name: sftpgo-volume
subPath: home
- mountPath: /etc/sftpgo
name: sftpgo-volume
subPath: config
resources:
requests:
memory: "500Mi"
cpu: "100m"
volumes:
- name: sftpgo-volume
persistentVolumeClaim:
claimName: sftpgo-pvc
readOnly: falseCaddy Deployment(檔案瀏覽服務)
Caddy 獨立部署,3 replicas + requiredDuringScheduling pod anti-affinity 確保副本分散於不同節點。每個 Pod 含兩個 Caddy 容器:caddy-server(port 80,服務內部網路)與 caddy-server-public(port 81,服務公開存取)。設定檔透過 ConfigMap sftpgo-caddy-config 注入,key 為 Caddyfile 和 Caddyfile.public。
yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: sftpgo-caddy-server
namespace: ingress-basic
labels:
app: sftpgo-caddy-server
spec:
replicas: 3
selector:
matchLabels:
app: sftpgo-caddy-server
template:
metadata:
labels:
app: sftpgo-caddy-server
spec:
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- sftpgo-caddy-server
topologyKey: "kubernetes.io/hostname"
containers:
- name: caddy-server
image: caddy:2-alpine
imagePullPolicy: Always
env:
- name: TZ
value: Asia/Taipei
ports:
- containerPort: 80
- containerPort: 2019
volumeMounts:
- mountPath: /srv/files
name: sftpgo-volume
subPath: upload
readOnly: true
- name: caddy-config-volume
mountPath: /etc/caddy/Caddyfile
subPath: Caddyfile
resources:
requests:
memory: "300Mi"
cpu: "150m"
- name: caddy-server-public
image: caddy:2-alpine
imagePullPolicy: Always
env:
- name: TZ
value: Asia/Taipei
ports:
- containerPort: 81
- containerPort: 2020
volumeMounts:
- mountPath: /srv/files
name: sftpgo-volume
subPath: upload
readOnly: true
- name: caddy-config-volume
mountPath: /etc/caddy/Caddyfile
subPath: Caddyfile.public
resources:
requests:
memory: "300Mi"
cpu: "150m"
volumes:
- name: sftpgo-volume
persistentVolumeClaim:
claimName: sftpgo-pvc
readOnly: false
- name: caddy-config-volume
configMap:
name: sftpgo-caddy-configCaddy ConfigMap(sftpgo-caddy-config)
Caddyfile.public(port 81,對應 caddy-server-public 容器,metrics port 2020)採用分層快取策略:大型壓縮檔案不快取(每次下載都是新鮮的)、前端資源短快取搭配 stale-while-revalidate、目錄瀏覽只開放 /public-updater* 路徑、其他檔案中等快取。
{
admin 0.0.0.0:2020
servers {
metrics {
per_host
}
}
}
:81 {
root * /srv/files
encode zstd gzip
# 大型下載檔案:無快取
@largeFiles path_regexp \.(zip|tar|gz|tgz|bz2|xz|7z|rar|exe|AppImage|deb|rpm)$
handle @largeFiles {
header {
Cache-Control "public, no-cache"
}
file_server
}
# 前端動態資源:短快取 + stale-while-revalidate
@staticAssets path_regexp \.(html|css|js|json|wasm)$
handle @staticAssets {
header {
Cache-Control "public, max-age=60, stale-while-revalidate=3600"
}
file_server
}
# 目錄瀏覽:僅開放 /public-updater 路徑
handle /public-updater* {
header {
Cache-Control "public, max-age=60, stale-while-revalidate=3600"
}
file_server browse
}
# 其他檔案:中等快取 + stale-while-revalidate
handle {
header {
Cache-Control "public, max-age=600, stale-while-revalidate=3600"
}
file_server
}
}
Caddyfile(port 80,內部caddy-server容器,metrics port 2019)設定尚未記錄;結構與Caddyfile.public相同,但admin改為0.0.0.0:2019、port 改為:80,快取策略可依內部需求調整。
建立或更新 ConfigMap:
bash
kubectl create configmap sftpgo-caddy-config \
--from-file=Caddyfile \
--from-file=Caddyfile.public \
-n ingress-basic \
-o yaml --dry-run=client | kubectl apply -f -
# 套用後重啟 Caddy Deployment
kubectl rollout restart deployment/sftpgo-caddy-server -n ingress-basicServices(AKS)
yaml
# ClusterIP(供 Ingress 使用)— selector 指向 Caddy Deployment
apiVersion: v1
kind: Service
metadata:
name: sftpgo-server-service
namespace: ingress-basic
spec:
ports:
- name: caddy
port: 80
targetPort: 80
- name: caddy-public
port: 81
targetPort: 81
selector:
app: sftpgo-caddy-server
---
# Internal LB — SFTP port 22、WebAdmin port 8080
apiVersion: v1
kind: Service
metadata:
name: sftpgo-server-sftp-service
namespace: ingress-basic
annotations:
service.beta.kubernetes.io/azure-load-balancer-internal: "true"
service.beta.kubernetes.io/azure-load-balancer-ipv4: 10.30.196.60
spec:
type: LoadBalancer
externalTrafficPolicy: Local
ports:
- name: sftp
port: 22
targetPort: 2022
- name: http
port: 8080
targetPort: 8080
selector:
app: sftpgo-server
---
# Internal LB — Caddy Prometheus metrics
apiVersion: v1
kind: Service
metadata:
name: sftpgo-caddy-metrics-service
namespace: ingress-basic
annotations:
service.beta.kubernetes.io/azure-load-balancer-internal: "true"
service.beta.kubernetes.io/azure-load-balancer-ipv4: 10.30.196.60
spec:
type: LoadBalancer
ports:
- name: private-caddy-metrics
port: 2019
targetPort: 2019
- name: public-caddy-metrics
port: 2020
targetPort: 2020
selector:
app: sftpgo-caddy-server
---
# ClusterIP — SFTPGo WebAdmin(供 Ingress sftpgo-web-ingress 使用)
apiVersion: v1
kind: Service
metadata:
name: sftpgo-web-service
namespace: ingress-basic
spec:
ports:
- name: http
port: 8080
targetPort: 8080
selector:
app: sftpgo-server| Service | 類型 | 說明 |
|---|---|---|
sftpgo-server-service | ClusterIP | Ingress 後端,轉發至 Caddy(port 80 內部 / 81 公開) |
sftpgo-server-sftp-service | Internal LB 10.30.196.60 | SFTP port 22、WebAdmin port 8080 |
sftpgo-caddy-metrics-service | Internal LB 10.30.196.60 | Caddy Prometheus metrics port 2019/2020 |
sftpgo-web-service | ClusterIP | Ingress 後端,轉發 WebAdmin(port 8080)至 sftpgo-server |
WebAdmin 部署後驗證
bash
# 確認 SFTPGo Pod 正常啟動
kubectl rollout restart deployment/sftpgo-server -n ingress-basic
# 確認 signing passphrase 環境變數被讀到(應顯示 [redacted])
kubectl logs -n ingress-basic -l app=sftpgo-server | grep -i signing
# 若登入仍失敗,查看 CSRF 相關錯誤
kubectl logs -n ingress-basic -l app=sftpgo-server --tail=100 | grep -i "csrf\|token"套用新設定後,必須清掉瀏覽器在
10.30.196.60:8080的全部 cookie,舊 token 不會自動失效。
SSH Key 認證設定
步驟 1:在用戶端機器產金鑰對(WSL2 / Linux / macOS)
bash
ssh-keygen -t ed25519 -C "wilson@wiwynn" -f ~/.ssh/sftpgo_wilson
chmod 600 ~/.ssh/sftpgo_wilson # Linux/WSL 必做,權限太寬 ssh client 會拒絕載入Windows PowerShell:
powershell
ssh-keygen -t ed25519 -C "wilson@wiwynn" -f "$env:USERPROFILE\.ssh\sftpgo_wilson"若連接方為老舊 Java 程式或不支援 Ed25519 的 legacy 工具,改用
ssh-keygen -t rsa -b 4096。
步驟 2A:WebAdmin UI 設定公鑰
- 登入
http://10.30.196.60:8080→ Users → 新增或編輯使用者 - 在
Public keys欄位貼入整行.pub內容(ssh-ed25519 AAAA...) - 一個使用者可放多把 key(不同裝置各自一把)
查看公鑰內容:
bash
cat ~/.ssh/sftpgo_wilson.pub步驟 2B:REST API 批次建立使用者
bash
TOKEN=$(curl -s -u admin:yourpassword \
http://10.30.196.60:8080/api/v2/token | jq -r .access_token)
curl -X POST http://10.30.196.60:8080/api/v2/users \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"username": "wilson",
"status": 1,
"home_dir": "/srv/sftpgo/wilson",
"permissions": {"/": ["*"]},
"public_keys": ["ssh-ed25519 AAAAC3Nz... wilson@wiwynn"]
}'步驟 3:測試連線
bash
sftp -i ~/.ssh/sftpgo_wilson -P 22 wilson@10.30.196.60WinSCP 設定:File protocol SFTP、Host 10.30.196.60、Port 22、Advanced → SSH → Authentication 選私鑰。
WinSCP 只接受
.ppk格式,OpenSSH 私鑰需先用 PuTTYgen 轉換。
Ingress(AKS)
pathType 改為 ImplementationSpecific,移除 proxy-buffers-number / proxy-buffer-size,改用 proxy-buffering: "off"。公開 Ingress 路由至 Caddy port 81(與內部的 port 80 分離)。另新增 sftpgo-web-ingress 將 WebAdmin 透過外部 domain + subpath 暴露:此 Ingress 不使用 rewrite-target,因 SFTPGo 已透過 SFTPGO_HTTPD__WEB_ROOT=/sftpgo 自行處理 subpath,Nginx 僅需轉發路徑即可。
yaml
# 內部 Ingress(bj6.wiwynn.com)→ Caddy port 80(檔案瀏覽)
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: sftpgo-server-ingress
namespace: ingress-basic
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /$2
nginx.ingress.kubernetes.io/use-regex: "true"
nginx.ingress.kubernetes.io/proxy-body-size: "1024m"
nginx.ingress.kubernetes.io/proxy-connect-timeout: "600"
nginx.ingress.kubernetes.io/proxy-send-timeout: "600"
nginx.ingress.kubernetes.io/proxy-read-timeout: "600"
nginx.ingress.kubernetes.io/proxy-buffering: "off"
spec:
ingressClassName: nginx
tls:
- secretName: aks-ingress-tls
rules:
- host: bj6.wiwynn.com
http:
paths:
- pathType: ImplementationSpecific
backend:
service:
name: sftpgo-server-service
port:
number: 80
path: /sftpgo-fileserver(/|$)(.*)
---
# 公開 Ingress(one.wiwynn.com)→ Caddy port 81(檔案瀏覽)
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: sftpgo-server-ingress-public
namespace: ingress-basic
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /$2
nginx.ingress.kubernetes.io/use-regex: "true"
nginx.ingress.kubernetes.io/proxy-body-size: "1024m"
nginx.ingress.kubernetes.io/proxy-connect-timeout: "600"
nginx.ingress.kubernetes.io/proxy-send-timeout: "600"
nginx.ingress.kubernetes.io/proxy-read-timeout: "600"
nginx.ingress.kubernetes.io/proxy-buffering: "off"
spec:
ingressClassName: nginx
tls:
- secretName: aks-ingress-tls
rules:
- host: one.wiwynn.com
http:
paths:
- pathType: ImplementationSpecific
backend:
service:
name: sftpgo-server-service
port:
number: 81
path: /sftpgo-fileserver(/|$)(.*)
---
# WebAdmin Ingress(bj6.wiwynn.com/sftpgo)→ SFTPGo port 8080
# 無 rewrite-target:WEB_ROOT=/sftpgo 由 SFTPGo 自行處理 subpath
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: sftpgo-web-ingress
namespace: ingress-basic
annotations:
nginx.ingress.kubernetes.io/proxy-body-size: "1024m"
nginx.ingress.kubernetes.io/proxy-connect-timeout: "600"
nginx.ingress.kubernetes.io/proxy-send-timeout: "600"
nginx.ingress.kubernetes.io/proxy-read-timeout: "600"
nginx.ingress.kubernetes.io/proxy-buffering: "off"
spec:
ingressClassName: nginx
tls:
- secretName: aks-ingress-tls
rules:
- host: bj6.wiwynn.com
http:
paths:
- pathType: ImplementationSpecific
path: /sftpgo
backend:
service:
name: sftpgo-web-service
port:
number: 8080| 域名 | 路徑 | 後端 Port | 說明 |
|---|---|---|---|
bj6.wiwynn.com | /sftpgo-fileserver/ | Caddy 80 | 內部檔案瀏覽 |
one.wiwynn.com | /sftpgo-fileserver/ | Caddy 81 | 公開檔案瀏覽 |
bj6.wiwynn.com | /sftpgo | SFTPGo 8080 | WebAdmin 管理介面(需搭配 WEB_ROOT=/sftpgo) |
Docker Compose 部署(非 K8s 環境)
yaml
version: '3'
services:
sftpgo:
image: drakkan/sftpgo
container_name: sftpgo
restart: always
volumes:
- "./upload:/srv/sftpgo"
- "./home:/var/lib/sftpgo"
- "./config:/etc/sftpgo"
ports:
- "2022:2022"
- "9888:8080"
sftpgo_nginx:
image: nginx:stable
container_name: sftpgo_nginx
restart: always
ports:
- "9889:80"
volumes:
- "./nginx.conf:/etc/nginx/conf.d/default.conf"
- "./upload/data/user:/usr/share/nginx/html"| 服務 | 主機 Port | 說明 |
|---|---|---|
| SFTPGo SFTP | 2022 | SFTP 連線 |
| SFTPGo Admin | 9888 | Web 管理介面 |
| Nginx 檔案 | 9889 | 檔案瀏覽 |
相關概念
- Caddy 靜態檔案伺服器 — SFTPGo 檔案瀏覽服務的後端;分層快取策略、內建 Prometheus metrics、目錄瀏覽設定
- Kubernetes Pod Anti-Affinity 與 Deployment 策略 — Caddy Deployment 使用 requiredDuringScheduling anti-affinity 的設計模式
- Samba + Nginx 檔案分享服務(Kubernetes 部署) — 類似的 Sidecar 共享儲存架構,提供 SMB 協定存取
- Kubernetes RBAC 帳號管理 — 管理叢集存取權限的通用模式