main.go 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. package main
  2. import (
  3. "bytes"
  4. "encoding/json"
  5. "fmt"
  6. "os"
  7. "strconv"
  8. "github.com/go-ldap/ldap/v3"
  9. "golang.org/x/crypto/ssh"
  10. )
  11. const (
  12. bindUsername = "cn=Directory Manager"
  13. bindPassword = "YOUR_ADMIN_PASSWORD_HERE"
  14. bindURL = "ldap://192.168.1.103:389"
  15. )
  16. type userFilters struct {
  17. DeniedLoginMethods []string `json:"denied_login_methods,omitempty"`
  18. }
  19. type minimalSFTPGoUser struct {
  20. Status int `json:"status,omitempty"`
  21. Username string `json:"username"`
  22. HomeDir string `json:"home_dir,omitempty"`
  23. UID int `json:"uid,omitempty"`
  24. GID int `json:"gid,omitempty"`
  25. Permissions map[string][]string `json:"permissions"`
  26. Filters userFilters `json:"filters"`
  27. }
  28. func exitError() {
  29. u := minimalSFTPGoUser{
  30. Username: "",
  31. }
  32. resp, _ := json.Marshal(u)
  33. fmt.Printf("%v\n", string(resp))
  34. os.Exit(1)
  35. }
  36. func printSuccessResponse(username, homeDir string, uid, gid int) {
  37. u := minimalSFTPGoUser{
  38. Username: username,
  39. HomeDir: homeDir,
  40. UID: uid,
  41. GID: gid,
  42. Status: 1,
  43. }
  44. u.Permissions = make(map[string][]string)
  45. u.Permissions["/"] = []string{"*"}
  46. // uncomment the next line to require publickey+password authentication
  47. //u.Filters.DeniedLoginMethods = []string{"publickey", "password", "keyboard-interactive", "publickey+keyboard-interactive"}
  48. resp, _ := json.Marshal(u)
  49. fmt.Printf("%v\n", string(resp))
  50. os.Exit(0)
  51. }
  52. func main() {
  53. // get credentials from env vars
  54. username := os.Getenv("SFTPGO_AUTHD_USERNAME")
  55. password := os.Getenv("SFTPGO_AUTHD_PASSWORD")
  56. publickey := os.Getenv("SFTPGO_AUTHD_PUBLIC_KEY")
  57. l, err := ldap.DialURL(bindURL)
  58. if err != nil {
  59. exitError()
  60. }
  61. defer l.Close()
  62. // bind to the ldap server with an account that can read users
  63. err = l.Bind(bindUsername, bindPassword)
  64. if err != nil {
  65. exitError()
  66. }
  67. // search the user trying to login and fetch some attributes, this search string is tested against 389ds using the default configuration
  68. searchRequest := ldap.NewSearchRequest(
  69. "dc=example,dc=com",
  70. ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
  71. fmt.Sprintf("(&(objectClass=nsPerson)(uid=%s))", username),
  72. []string{"dn", "homeDirectory", "uidNumber", "gidNumber", "nsSshPublicKey"},
  73. nil,
  74. )
  75. sr, err := l.Search(searchRequest)
  76. if err != nil {
  77. exitError()
  78. }
  79. // we expect exactly one user
  80. if len(sr.Entries) != 1 {
  81. exitError()
  82. }
  83. if len(publickey) > 0 {
  84. // check public key
  85. userKey, _, _, _, err := ssh.ParseAuthorizedKey([]byte(publickey))
  86. if err != nil {
  87. exitError()
  88. }
  89. authOk := false
  90. for _, k := range sr.Entries[0].GetAttributeValues("nsSshPublicKey") {
  91. key, _, _, _, err := ssh.ParseAuthorizedKey([]byte(k))
  92. // we skip an invalid public key stored inside the LDAP server
  93. if err != nil {
  94. continue
  95. }
  96. if bytes.Equal(key.Marshal(), userKey.Marshal()) {
  97. authOk = true
  98. break
  99. }
  100. }
  101. if !authOk {
  102. exitError()
  103. }
  104. } else {
  105. // bind to the LDAP server with the user dn and the given password to check the password
  106. userdn := sr.Entries[0].DN
  107. err = l.Bind(userdn, password)
  108. if err != nil {
  109. exitError()
  110. }
  111. }
  112. uid, err := strconv.Atoi(sr.Entries[0].GetAttributeValue("uidNumber"))
  113. if err != nil {
  114. exitError()
  115. }
  116. gid, err := strconv.Atoi(sr.Entries[0].GetAttributeValue("gidNumber"))
  117. if err != nil {
  118. exitError()
  119. }
  120. // return the authenticated user
  121. printSuccessResponse(username, sr.Entries[0].GetAttributeValue("homeDirectory"), uid, gid)
  122. }