mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2026-07-31 04:23:34 +00:00
* 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>
91 lines
2.7 KiB
Go
91 lines
2.7 KiB
Go
// Package auth provides authentication and authorization functionality for the SFTP server
|
|
package auth
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/seaweedfs/seaweedfs/weed/sftpd/user"
|
|
"golang.org/x/crypto/ssh"
|
|
)
|
|
|
|
// Provider defines the interface for authentication providers
|
|
type Provider interface {
|
|
// GetAuthMethods returns the SSH server auth methods
|
|
GetAuthMethods() []ssh.AuthMethod
|
|
}
|
|
|
|
// Manager handles authentication and authorization
|
|
type Manager struct {
|
|
userStore user.Store
|
|
passwordAuth *PasswordAuthenticator
|
|
publicKeyAuth *PublicKeyAuthenticator
|
|
certificateAuth *CertificateAuthenticator
|
|
enabledAuthMethods []string
|
|
}
|
|
|
|
// NewManager creates a new authentication manager.
|
|
//
|
|
// trustedUserCAKeysFile is the path to a file containing trusted CA public
|
|
// keys (OpenSSH authorized_keys format). It is required when "certificate"
|
|
// is listed in enabledAuthMethods and ignored otherwise.
|
|
func NewManager(userStore user.Store, enabledAuthMethods []string, trustedUserCAKeysFile string) (*Manager, error) {
|
|
manager := &Manager{
|
|
userStore: userStore,
|
|
enabledAuthMethods: enabledAuthMethods,
|
|
}
|
|
|
|
// Initialize authenticators based on enabled methods
|
|
passwordEnabled := false
|
|
publicKeyEnabled := false
|
|
certificateEnabled := false
|
|
|
|
for _, method := range enabledAuthMethods {
|
|
switch method {
|
|
case "password":
|
|
passwordEnabled = true
|
|
case "publickey":
|
|
publicKeyEnabled = true
|
|
case "certificate":
|
|
certificateEnabled = true
|
|
}
|
|
}
|
|
|
|
manager.passwordAuth = NewPasswordAuthenticator(userStore, passwordEnabled)
|
|
manager.publicKeyAuth = NewPublicKeyAuthenticator(userStore, publicKeyEnabled)
|
|
|
|
certAuth, err := NewCertificateAuthenticator(userStore, certificateEnabled, trustedUserCAKeysFile)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("init certificate auth: %w", err)
|
|
}
|
|
manager.certificateAuth = certAuth
|
|
|
|
return manager, nil
|
|
}
|
|
|
|
// GetSSHServerConfig returns an SSH server config with the appropriate authentication methods
|
|
func (m *Manager) GetSSHServerConfig() *ssh.ServerConfig {
|
|
config := &ssh.ServerConfig{}
|
|
|
|
// Add password authentication if enabled
|
|
if m.passwordAuth.Enabled() {
|
|
config.PasswordCallback = m.passwordAuth.Authenticate
|
|
}
|
|
|
|
// Wire the public-key channel. Certificate auth, when enabled, takes
|
|
// over the channel entirely (MinIO/OpenSSH-style): plain public keys
|
|
// are rejected even if "publickey" is also listed in enabledAuthMethods.
|
|
switch {
|
|
case m.certificateAuth.Enabled():
|
|
config.PublicKeyCallback = m.certificateAuth.Authenticate
|
|
case m.publicKeyAuth.Enabled():
|
|
config.PublicKeyCallback = m.publicKeyAuth.Authenticate
|
|
}
|
|
|
|
return config
|
|
}
|
|
|
|
// GetUser retrieves a user from the user store
|
|
func (m *Manager) GetUser(username string) (*user.User, error) {
|
|
return m.userStore.GetUser(username)
|
|
}
|