{"record":{"id":"e35c21dac48ccc6a","repo":"shadow1ng/fscan","slug":"rsync-modules-failed","errorCode":null,"errorMessage":"rsync_modules_failed","messagePattern":"rsync_modules_failed","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"plugins/services/rsync.go","lineNumber":123,"sourceCode":"// doRsyncAuth 执行Rsync认证\nfunc (p *RsyncPlugin) doRsyncAuth(ctx context.Context, info *common.HostInfo, cred Credential, session *common.ScanSession) *AuthResult {\n\t// 先获取可用模块列表\n\tconn := p.connectToRsync(ctx, info, session)\n\tif conn == nil {\n\t\treturn &AuthResult{\n\t\t\tSuccess:   false,\n\t\t\tErrorType: ErrorTypeNetwork,\n\t\t\tError:     fmt.Errorf(\"%s\", i18n.GetText(\"rsync_connect_failed\")),\n\t\t}\n\t}\n\tmodules := p.getModules(conn, session.Config)\n\t_ = conn.Close()\n\n\tif len(modules) == 0 {\n\t\treturn &AuthResult{\n\t\t\tSuccess:   false,\n\t\t\tErrorType: ErrorTypeUnknown,\n\t\t\tError:     fmt.Errorf(\"%s\", i18n.GetText(\"rsync_modules_failed\")),\n\t\t}\n\t}\n\n\t// 提取第一个模块名\n\tvar firstModule string\n\tfor _, moduleLine := range modules {\n\t\tif fields := strings.Fields(moduleLine); len(fields) > 0 {\n\t\t\tfirstModule = fields[0]\n\t\t\tbreak\n\t\t}\n\t}\n\tif firstModule == \"\" {\n\t\treturn &AuthResult{\n\t\t\tSuccess:   false,\n\t\t\tErrorType: ErrorTypeUnknown,\n\t\t\tError:     fmt.Errorf(\"%s\", i18n.GetText(\"rsync_modules_failed\")),\n\t\t}\n\t}","sourceCodeStart":105,"sourceCodeEnd":141,"githubUrl":"https://github.com/shadow1ng/fscan/blob/95cc12e753bf43de7004e5aef42a9ffba3934303/plugins/services/rsync.go#L105-L141","documentation":"After successfully connecting, doRsyncAuth parses the module list returned by the daemon. If getModules yields an empty list, it fails with rsync_modules_failed (ErrorTypeUnknown) because there is no module to authenticate against — rsync auth is always per-module.","triggerScenarios":"connectToRsync succeeded and getModules ran, but the daemon returned no parsable module entries — typically every module is configured with 'list = no', or the LIST response contained only the banner and no module lines.","commonSituations":"Hardened rsyncd.conf hiding all modules from listing (list = no); a daemon exposing only restricted modules to the scanner IP; a non-rsync service mimicking the banner; parsing mismatch when the daemon uses an unusual rsync protocol version.","solutions":["Enumerate modules manually: rsync rsync://<host>/ and rsync <host>:: to see what is visible.","If modules are hidden (list = no), supply a known module name in the scan config instead of relying on listing.","Verify with the rsyncd admin that at least one module is listable for your source IP.","Check the rsync daemon protocol version compatibility."],"exampleFix":"// before (rsyncd.conf)\n[secret]\n    path = /srv/data\n    list = no // modules list empty -> rsync_modules_failed\n// after\n[secret]\n    path = /srv/data\n    list = yes","handlingStrategy":"validation","validationCode":"out, _ := exec.Command(\"rsync\", \"rsync://\"+host+\"/\").Output()\nif len(strings.Fields(string(out))) == 0 {\n    // no modules listed; provide explicit module name in config instead of relying on listing\n}","typeGuard":null,"tryCatchPattern":"if err != nil && strings.Contains(err.Error(), \"rsync_modules_failed\") {\n    // fall back to scanning with a known module name from config\n}","preventionTips":["Check rsyncd.conf 'list =' settings on audited servers.","Configure an explicit module name in scan config for hardened daemons.","Run 'rsync rsync://host/' manually when planning a scan."],"tags":["rsync","module-listing","configuration","enumeration"],"backgroundTag":"empty-result-set","analyzedSha":"95cc12e753bf43de7004e5aef42a9ffba3934303","analyzedAt":"2026-09-06T17:07:30.094Z","contentChangedAt":"2026-09-06T17:07:30.094Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}