mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2026-09-08 15:41:15 +02:00
sftpd: support SSH user certificates signed by a trusted CA (#9815)
* sftpd: support SSH user certificates signed by a trusted CA Adds a new "certificate" auth method to weed sftp. When enabled, the server loads trusted CA public keys from -trustedUserCAKeysFile (OpenSSH authorized_keys format, one or more keys) and accepts only ssh.Certificate blobs of type UserCert on the public-key channel. Validation uses ssh.CertChecker: CA signature, ValidAfter/ValidBefore, non-empty ValidPrincipals and SSH login user must appear in ValidPrincipals. The authenticated user must exist in the user store; home dir and permissions resolve as before. Behaviour mirrors MinIO's --sftp=trusted-user-ca-key and OpenSSH's TrustedUserCAKeys: when certificate auth is active, plain (non-cert) public keys are rejected even if "publickey" is also listed. Default authMethods remain "password,publickey", so existing deployments are unaffected. * Update weed/sftpd/auth/certificate.go Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> * sftpd: address review feedback on certificate auth - Pre-marshal trusted CA public keys in IsUserAuthority instead of re-marshaling on every authentication attempt (gemini-code-assist). - Differentiate user-not-found from underlying store errors via errors.As(*user.UserNotFoundError) so backend/read failures are no longer reported as bad credentials (coderabbitai). - Fix the corresponding sanity check in the missing-file test to use errors.As instead of errors.Is (UserNotFoundError has no Is method, so the previous check never matched) (coderabbitai). * sftpd: register trustedUserCAKeysFile flag in filer and server commands The new field on SftpOptions is dereferenced unconditionally in resolvePaths(), but only the standalone `weed sftp` command was wiring its flag. `weed filer` and `weed server` both embed an SftpOptions value and call resolvePaths() on it, so they hit a nil pointer dereference at startup. Register `-sftp.trustedUserCAKeysFile` in both commands and update the -sftp.authMethods help text to mention the new "certificate" method. Fixes the SFTP Integration Tests CI failure on this PR. * helm: expose SFTP certificate auth in the SeaweedFS chart Adds Helm-chart support for the new SSH user-certificate auth method: - values.yaml (sftp:) gains `trustedUserCAKeys` (inline OpenSSH authorized_keys-format CA public keys) and `existingCAKeysSecret` (reference an externally managed Secret). Same pair added under allInOne.sftp with a null default that falls back to the top-level sftp.* setting. - New template templates/sftp/sftp-ca-secret.yaml renders a chart-managed Secret <release>-sftp-ca-secret with `ca_user.pub`, but only when SFTP is enabled, "certificate" is in authMethods, inline keys are provided, and no existingCAKeysSecret is set. - templates/sftp/sftp-deployment.yaml and the all-in-one deployment template add `-trustedUserCAKeysFile=/etc/sw/sftp_ca/ca_user.pub` to the weed sftp command, mount the CA secret at /etc/sw/sftp_ca and add the corresponding volume. All cert-auth bits are guarded by `contains "certificate" authMethods` so existing users see no change. - authMethods help text updated to mention "certificate". Verified end-to-end on a local k3d cluster: cert login succeeds, plain-pubkey login is rejected with "public key without certificate not allowed". * helm: fail render when SFTP certificate auth lacks CA keys When certificate is in authMethods but neither trustedUserCAKeys nor existingCAKeysSecret is set, the deployment mounted a secret that the chart never renders, leaving the pod stuck on a missing volume. Fail at template time with a clear message instead. * sftpd: fix stale auth-method list in SFTPServiceOptions comment keyboard-interactive was never implemented; certificate is the new supported method. Match the CLI help text. * sftpd: test Manager wiring of certificate vs public-key channel Cover the channel takeover at the Manager level: certificate auth displaces plain public-key auth when both are enabled, public-key auth stays put otherwise, and enabling certificate without a CA file errors. --------- Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Co-authored-by: Chris Lu <chris.lu@gmail.com>
This commit is contained in:
co-authored by
gemini-code-assist[bot]
Chris Lu
parent
df879e1ed7
commit
ce6a51468a
@@ -272,6 +272,9 @@ spec:
|
||||
{{- $authMethods := .Values.allInOne.sftp.authMethods | default .Values.sftp.authMethods }}
|
||||
{{- if $authMethods }}
|
||||
-sftp.authMethods={{ $authMethods }} \
|
||||
{{- if contains "certificate" $authMethods }}
|
||||
-sftp.trustedUserCAKeysFile=/etc/sw/sftp_ca/ca_user.pub \
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- $maxAuthTries := .Values.allInOne.sftp.maxAuthTries | default .Values.sftp.maxAuthTries }}
|
||||
{{- if $maxAuthTries }}
|
||||
@@ -319,6 +322,12 @@ spec:
|
||||
name: config-users
|
||||
readOnly: true
|
||||
{{- end }}
|
||||
{{- $aioAuthMethods := .Values.allInOne.sftp.authMethods | default .Values.sftp.authMethods }}
|
||||
{{- if and $aioAuthMethods (contains "certificate" $aioAuthMethods) }}
|
||||
- mountPath: /etc/sw/sftp_ca
|
||||
name: config-sftp-ca
|
||||
readOnly: true
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- if .Values.filer.notificationConfig }}
|
||||
- name: notification-config
|
||||
@@ -454,6 +463,18 @@ spec:
|
||||
defaultMode: 420
|
||||
secretName: {{ default (printf "%s-sftp-secret" (include "seaweedfs.fullname" .)) (or .Values.allInOne.sftp.existingConfigSecret .Values.sftp.existingConfigSecret) }}
|
||||
{{- end }}
|
||||
{{- $aioAuthMethodsVol := .Values.allInOne.sftp.authMethods | default .Values.sftp.authMethods }}
|
||||
{{- if and $aioAuthMethodsVol (contains "certificate" $aioAuthMethodsVol) }}
|
||||
{{- $aioCASecret := or .Values.allInOne.sftp.existingCAKeysSecret .Values.sftp.existingCAKeysSecret }}
|
||||
{{- $aioCAKeys := or .Values.allInOne.sftp.trustedUserCAKeys .Values.sftp.trustedUserCAKeys }}
|
||||
{{- if and (not $aioCASecret) (not $aioCAKeys) }}
|
||||
{{- fail "allInOne.sftp.authMethods includes \"certificate\" but neither trustedUserCAKeys nor existingCAKeysSecret is set" }}
|
||||
{{- end }}
|
||||
- name: config-sftp-ca
|
||||
secret:
|
||||
defaultMode: 420
|
||||
secretName: {{ default (printf "%s-sftp-ca-secret" (include "seaweedfs.fullname" .)) $aioCASecret }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- if .Values.filer.notificationConfig }}
|
||||
- name: notification-config
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
{{- include "seaweedfs.compat" . -}}
|
||||
{{- /*
|
||||
Render a chart-managed Secret carrying the SSH user CA public key(s) for
|
||||
certificate-based SFTP authentication, but only when:
|
||||
- SFTP (standalone or all-in-one) is enabled,
|
||||
- "certificate" is in the relevant authMethods list,
|
||||
- the user provided inline CA keys via sftp.trustedUserCAKeys, and
|
||||
- no existingCAKeysSecret is set (which would supersede this Secret).
|
||||
*/}}
|
||||
{{- $sftpEnabled := or .Values.sftp.enabled (and .Values.allInOne.enabled .Values.allInOne.sftp.enabled) -}}
|
||||
{{- $authMethods := .Values.sftp.authMethods -}}
|
||||
{{- if and .Values.allInOne.enabled .Values.allInOne.sftp.authMethods -}}
|
||||
{{- $authMethods = .Values.allInOne.sftp.authMethods -}}
|
||||
{{- end -}}
|
||||
{{- $certInMethods := and $authMethods (contains "certificate" $authMethods) -}}
|
||||
{{- $inlineCAKeys := .Values.sftp.trustedUserCAKeys -}}
|
||||
{{- if and .Values.allInOne.enabled .Values.allInOne.sftp.trustedUserCAKeys -}}
|
||||
{{- $inlineCAKeys = .Values.allInOne.sftp.trustedUserCAKeys -}}
|
||||
{{- end -}}
|
||||
{{- $existingSecret := .Values.sftp.existingCAKeysSecret -}}
|
||||
{{- if and .Values.allInOne.enabled .Values.allInOne.sftp.existingCAKeysSecret -}}
|
||||
{{- $existingSecret = .Values.allInOne.sftp.existingCAKeysSecret -}}
|
||||
{{- end -}}
|
||||
{{- if and $sftpEnabled $certInMethods $inlineCAKeys (not $existingSecret) }}
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
type: Opaque
|
||||
metadata:
|
||||
name: {{ include "seaweedfs.fullname" . }}-sftp-ca-secret
|
||||
namespace: {{ .Release.Namespace }}
|
||||
labels:
|
||||
app.kubernetes.io/name: {{ template "seaweedfs.name" . }}
|
||||
helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}
|
||||
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
app.kubernetes.io/component: sftp
|
||||
stringData:
|
||||
ca_user.pub: |
|
||||
{{ $inlineCAKeys | indent 4 }}
|
||||
{{- end }}
|
||||
@@ -135,6 +135,9 @@ spec:
|
||||
{{- end }}
|
||||
{{- if .Values.sftp.authMethods }}
|
||||
-authMethods={{ .Values.sftp.authMethods }} \
|
||||
{{- if contains "certificate" .Values.sftp.authMethods }}
|
||||
-trustedUserCAKeysFile=/etc/sw/sftp_ca/ca_user.pub \
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- if .Values.sftp.maxAuthTries }}
|
||||
-maxAuthTries={{ .Values.sftp.maxAuthTries }} \
|
||||
@@ -176,6 +179,11 @@ spec:
|
||||
- mountPath: /etc/sw/ssh
|
||||
name: config-ssh
|
||||
readOnly: true
|
||||
{{- if and .Values.sftp.authMethods (contains "certificate" .Values.sftp.authMethods) }}
|
||||
- mountPath: /etc/sw/sftp_ca
|
||||
name: config-sftp-ca
|
||||
readOnly: true
|
||||
{{- end }}
|
||||
{{- if include "seaweedfs.securityConfigEnabled" . }}
|
||||
- name: security-config
|
||||
readOnly: true
|
||||
@@ -256,6 +264,19 @@ spec:
|
||||
{{- else }}
|
||||
secretName: {{ include "seaweedfs.fullname" . }}-sftp-ssh-secret
|
||||
{{- end }}
|
||||
{{- if and .Values.sftp.authMethods (contains "certificate" .Values.sftp.authMethods) }}
|
||||
{{- if and (not .Values.sftp.existingCAKeysSecret) (not .Values.sftp.trustedUserCAKeys) }}
|
||||
{{- fail "sftp.authMethods includes \"certificate\" but neither sftp.trustedUserCAKeys nor sftp.existingCAKeysSecret is set" }}
|
||||
{{- end }}
|
||||
- name: config-sftp-ca
|
||||
secret:
|
||||
defaultMode: 420
|
||||
{{- if .Values.sftp.existingCAKeysSecret }}
|
||||
secretName: {{ .Values.sftp.existingCAKeysSecret }}
|
||||
{{- else }}
|
||||
secretName: {{ include "seaweedfs.fullname" . }}-sftp-ca-secret
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- if eq .Values.sftp.logs.type "hostPath" }}
|
||||
- name: logs
|
||||
hostPath:
|
||||
|
||||
@@ -1138,7 +1138,7 @@ sftp:
|
||||
# SSH server configuration
|
||||
sshPrivateKey: "/etc/sw/seaweedfs_sftp_ssh_private_key" # Path to the SSH private key file for host authentication
|
||||
hostKeysFolder: "/etc/sw/ssh" # path to folder containing SSH private key files for host authentication
|
||||
authMethods: "password,publickey" # Comma-separated list of allowed auth methods: password, publickey, keyboard-interactive
|
||||
authMethods: "password,publickey" # Comma-separated list of allowed auth methods: password, publickey, certificate
|
||||
maxAuthTries: 6 # Maximum number of authentication attempts per connection
|
||||
bannerMessage: "SeaweedFS SFTP Server" # Message displayed before authentication
|
||||
loginGraceTime: "2m" # Timeout for authentication
|
||||
@@ -1155,6 +1155,18 @@ sftp:
|
||||
# Set to the name of an existing kubernetes Secret with the list of ssh private keys for sftp
|
||||
existingSshConfigSecret: null
|
||||
|
||||
# SSH user-certificate authentication (CA-signed user certs). Mirrors
|
||||
# OpenSSH `TrustedUserCAKeys` and MinIO `--sftp=trusted-user-ca-key`.
|
||||
# Add "certificate" to `authMethods` to activate; when active, plain
|
||||
# public keys are rejected on the public-key channel.
|
||||
#
|
||||
# Inline CA public keys in OpenSSH authorized_keys format (one per
|
||||
# line). Ignored when `existingCAKeysSecret` is set.
|
||||
trustedUserCAKeys: ""
|
||||
# Set to the name of an existing kubernetes Secret carrying the CA
|
||||
# public keys under data key `ca_user.pub`.
|
||||
existingCAKeysSecret: null
|
||||
|
||||
# Additional resources
|
||||
sidecars: []
|
||||
initContainers: ""
|
||||
@@ -1545,6 +1557,10 @@ allInOne:
|
||||
existingConfigSecret: null
|
||||
# Set to the name of an existing kubernetes Secret with the SSH keys
|
||||
existingSshConfigSecret: null
|
||||
# SSH user-certificate authentication. See sftp.trustedUserCAKeys above.
|
||||
# (null on either field inherits from the top-level sftp.* setting.)
|
||||
trustedUserCAKeys: null
|
||||
existingCAKeysSecret: null
|
||||
|
||||
# Service settings
|
||||
service:
|
||||
|
||||
Reference in New Issue
Block a user