MongoDB-ის Authentication failed შეცდომა, კოდი 18, ერთ რამეს ნიშნავს: სერვერმა მიიღო მომხმარებლის სახელი და პაროლი და ვერც ერთ მომხმარებელს ვერ შეუსაბამა. ეს არ ნიშნავს, რომ სერვერი გათიშულია, პორტი დახურულია ან TLS გაფუჭებულია - ასეთი პრობლემები უფრო ადრე ჩნდება და სხვანაირად გამოიყურება. როცა მონაცემები აშკარად სწორია, მიზეზი თითქმის ყოველთვის ოთხიდან ერთია: მომხმარებელი სხვა ბაზაშია, ვიდრე დრაივერი ეძებს (authSource), დრაივერმა სხვა პაროლი გაგზავნა, ვიდრე შენ აკრიფე (კოდირება, ბრჭყალები, ზედმეტი ახალი ხაზი), connection string არ არის ის, რომელიც გგონია, ან მექანიზმმა თუ +srv-ის ნაგულისხმევმა მნიშვნელობამ შეცვალა, რა იგზავნება.
პასუხამდე ყველაზე სწრაფი გზაა კლიენტის მხრიდან ვარაუდის შეწყვეტა: ჰკითხე სერვერს და შემდეგ იგივე კავშირი ხელით გაიმეორე mongosh-ში. ეს პოსტი ამ დიაგნოსტიკას თანმიმდევრობით გადის, შემდეგ კი თითოეული დრაივერის ხაფანგებს. იმის შესახებ, როგორ იწყობა URI თავიდანვე - ნაწილები, პარამეტრები, pool-ის ზომები - იხილე MongoDB connection strings; აქ ვვარაუდობთ, რომ string უკვე გაქვს და არ მუშაობს.
ჯერ დარწმუნდი, რომ ეს ნამდვილად ავტორიზაციის შეცდომაა#
საკმაოდ ბევრი დრო იკარგება პაროლების გასწორებაში იმ შეცდომებისთვის, რომლებსაც პაროლთან კავშირი არ ჰქონია. MongoDB შრეებად ვარდება და თითოეულ შრეს თავისი შეტყობინება აქვს. რამეს შეცვლამდე გაარკვიე, რომელ შრეზე გაჩერდი.
| შეტყობინება | შრე | რას შეხედო |
|---|---|---|
Authentication failed / code 18 | მონაცემები | მომხმარებელი, ბაზა, პაროლი |
bad auth : Authentication failed | მონაცემები (Atlas) | ბაზის მომხმარებელი და არა Atlas-ის ექაუნთი |
ServerSelectionTimeoutError | ქსელი ან TLS | ჰოსტი, პორტი, firewall, tls |
ECONNREFUSED | ქსელი | bind-მისამართი, პორტი, სერვისის გაშვება |
querySrv ENOTFOUND | DNS | +srv ჰოსტზე SRV ჩანაწერების გარეშე |
requires authentication (code 13) | მონაცემები არ არის | URI-ში მომხმარებელი არ წერია ან env არ ჩაიტვირთა |
not authorized on shop to execute | როლები | მომხმარებელი არსებობს, მაგრამ როლი არ აქვს |
ბოლო ორი პირველისგან გამოსაყოფია. command find requires authentication ნიშნავს, რომ ანონიმურად დაუკავშირდი - მონაცემები მოთხოვნაში საერთოდ არ მოხვდა, ჩვეულებრივ იმიტომ, რომ გარემოს ცვლადი ცარიელი იყო და კოდი mongodb://localhost:27017-ზე დაბრუნდა. not authorized ნიშნავს, რომ ავტორიზაცია გაიარა და მომხმარებელს უბრალოდ ამ ბაზაზე როლი არ აქვს. არცერთი მათგანი პაროლის შეცვლით არ მოგვარდება; ორივე მოგვარდება იმის შემოწმებით, რა უფლებები აქვს მომხმარებელს ან რომელი URI ჩატვირთა შენმა პროცესმა სინამდვილეში.
კარგი ჩვევა, სანამ წინ წახვალ: დაბეჭდე URI, რომელსაც აპლიკაცია რეალურად იყენებს, პაროლის დაფარვით. JavaScript-ში uri.replace(/:([^@/]+)@/, ":****@") გაჩვენებს ჰოსტს, პორტს, ბაზასა და პარამეტრებს და საიდუმლოს არ გაჟონავს. "სწორი პაროლით Authentication failed"-ის დიდი ნაწილი აღმოჩნდება staging-ის URI production-ის კონტეინერში ან კოდში დატოვებული ნაგულისხმევი მნიშვნელობა, რომელიც არავის გადაუფარავს.
ჰკითხე სერვერს, რა იყო არასწორი#
კლიენტი განზრახ ბუნდოვან შეტყობინებას იღებს, რადგან "ასეთი მომხმარებელი არ არსებობს" თავდამსხმელს დაეხმარებოდა. სერვერის ლოგი ბუნდოვანი არ არის. თუ mongod-ის ლოგზე წვდომა გაქვს, იპოვე სტრიქონი წარუმატებელი მცდელობისთვის; MongoDB 4.4-ში და შემდეგ ის სტრუქტურირებული JSON-ია შეტყობინებით Authentication failed და attr ობიექტით, სადაც წერია მექანიზმი, მომხმარებელი, ბაზა, რომელშიც ეძებდა, კლიენტის მისამართი და რეალური მიზეზი.
{"msg":"Authentication failed","attr":{"mechanism":"SCRAM-SHA-256", "principalName":"shopapp","authenticationDatabase":"shop", "remote":"203.0.113.40:51522", "error":"UserNotFound: Could not find user \"shopapp\" for db \"shop\""}}ეს ერთი სტრიქონი კითხვების უმეტესობას პასუხობს. წაიკითხე სამი ველი:
authenticationDatabaseარის ბაზა, რომელიც დრაივერმა მოითხოვა. თუ იქ წერიაshop, შენი მომხმარებელი კიadmin-ში შექმენი, გამოსწორება არისauthSource=admin, პაროლი კი არასდროს ყოფილა პრობლემა.error, რომელიც იწყებაUserNotFound-ით, ნიშნავს, რომ ამ სახელის მომხმარებელი იმ ბაზაში არ არსებობს. ან სახელია არასწორად დაწერილი, ან სხვა ბაზაშია, ან სხვა სერვერს ესაუბრები, არა იმას, რომელზეც შექმენი.error, რომელშიც SCRAM-ის შეცდომა ჩანს, მაგალითადstoredKey mismatch, ნიშნავს, რომ მომხმარებელი ნაპოვნია და პაროლი არ დაემთხვა. სწორედ ახლა, და მხოლოდ ახლა, ღირს თვითონ პაროლის შემოწმება.
ამ სტრიქონების ზუსტი ფორმულირება და აგებულება ვერსიების მიხედვით განსხვავდება, ძველი სერვერები JSON-ის ნაცვლად უბრალო ტექსტს წერდნენ, მაგრამ იგივე სამი ფაქტი ყველგან არის. თუ სერვერის ლოგს ვერ კითხულობ - მართვადი სერვისია ან სერვერი შენ არ გიმართავს - mongosh იმავე პასუხებამდე მიგიყვანს, როგორც ქვემოთაა აღწერილი.
authSource და მომხმარებელი, რომელიც სხვაგან ცხოვრობს#
ყველა MongoDB მომხმარებელი ერთ ბაზას ეკუთვნის. connection string-ის authSource ამბობს, რომელში ეძებოს, და როცა ის არ არის, დრაივერი იყენებს URI-ის path-ში მითითებულ ბაზას და admin-ზე ბრუნდება მხოლოდ მაშინ, როცა path ცარიელია. სწორედ ეს ნაგულისხმევი მნიშვნელობაა კოდი 18-ის ყველაზე გავრცელებელი მიზეზი.
კლასიკური შემთხვევა: მოგცეს ადმინისტრაციული მომხმარებელი, რომელიც admin-შია, შენ კი URI წერ აპლიკაციის ბაზაზე.
mongodb://root:pw@db.example.net:27017/shopდრაივერი root-ს shop-ში ეძებს, ვერავის პოულობს და წარუმატებლობას აცხადებს. დაამატე ?authSource=admin და იგივე მონაცემები იმუშავებს. საპირისპირო შეცდომაც ისევე ხშირია: მომხმარებელი შეიქმნა use shop-ის შემდეგ db.createUser(...)-ით, შემდეგ კი URI-ში ტუტორიალიდან გადმოწერილი authSource=admin ჩაიწერა. ეს მომხმარებელი admin-ში არ არსებობს, ამიტომ პასუხია authSource=shop - ან ხელახლა შექმენი მომხმარებელი admin-ში shop-ზე შეზღუდული როლით და ამ საკითხზე აღარ იფიქრო.
იმის გასაგებად, სად ცხოვრობს მომხმარებელი სინამდვილეში, დაუკავშირდი ნებისმიერი ექაუნთით, რომელსაც მომხმარებლების ნახვა შეუძლია, და ჰკითხე მთელ სერვერს:
db.adminCommand({ usersInfo: { forAllDBs: true } }).users .map(u => `${u.user} @ ${u.db}`)თითოეული სტრიქონი აჩვენებს მომხმარებლის სახელს და ბაზას, რომელსაც ის ეკუთვნის. ორი ბაზის ორი ერთნაირსახელიანი მომხმარებელი სრულიად ცალკე ექაუნთებია ცალკე პაროლებით, რაც ხანდახან გაჩენილ "ერთი აპიდან მუშაობს, მეორიდან არა" სიტუაციას ხსნის - ორი აპი სხვადასხვა მომხმარებლით ავტორიზდება, რომლებსაც უბრალოდ ერთი და იგივე სახელი აქვთ.
ერთი ბაზისთვის ასევე შეგიძლია გაუშვა db.getSiblingDB("shop").getUsers(). ცარიელი მასივი ნიშნავს, რომ shop-ში არავინაა, რაც authSource-ის საკითხს მაშინვე წყვეტს.
საერთოდ არასწორი სერვერი
UserNotFound მომხმარებლისთვის, რომელიც აუცილებლად შექმენი, უფრო ხშირად ბანალურ მიზეზს გულისხმობს, ვიდრე ადამიანები აღიარებენ: კლიენტი იმ სერვერს არ ესაუბრება, რომელზეც მომხმარებელი შექმენი. შენს ლეპტოპზე 27017-ზე მოსმენადი ლოკალური mongod სიამოვნებით უპასუხებს დაშორებული ჰოსტისთვის განკუთვნილ URI-ს, თუ SSH tunnel იმავე ლოკალურ პორტს აგზავნის ან .env ფაილი localhost-ზე მიუთითებს. შეამოწმე დაკავშირების შემდეგ db.hostInfo().system.hostname-ით ან ნახე remote მისამართი სერვერის ლოგში. თუ tunnel-ს იყენებ, გადამისამართე სხვა ლოკალურ პორტზე, მაგალითად 27018-ზე, რომ ორივე არასდროს აგერიოს - სინტაქსი აღწერილია პოსტში SSH tunnels და port forwarding.
პაროლი, რომელიც არ არის ის, რაც აკრიფე#
როცა სერვერის ლოგი ამბობს, რომ მომხმარებელი ნაპოვნია და პაროლი არ დაემთხვა, ივარაუდე, რომ ბაიტები, რომლებიც მივიდა, იმისგან განსხვავდება, რაც გინდოდა. ეს შემდეგი გზებით ხდება, დაახლოებით სიხშირის მიხედვით.
პროცენტული კოდირება, გამოტოვებული ან გაორმაგებული
პაროლში არსებული ნებისმიერი სიმბოლო : / ? # [ ] @ % URI-ში პროცენტულად უნდა დაიშიფროს. თუ ერთს დაშიფვრის გარეშე დატოვებ, დრაივერი ან URI-ს უარყოფს, ან პაროლის ნაწილს ჰოსტად აღიქვამს. საპირისპირო შეცდომა უფრო ჩუმია: ორჯერ დაშიფვრა. პაროლი p@ss URI-ში სწორად ხდება p%40ss; თუ ეს დაშიფრული string კიდევ ერთხელ გაივლის encodeURIComponent-ში, ხდება p%2540ss და სერვერი იღებს ტექსტს p%40ss. დაშიფრე ზუსტად ერთხელ, იქ, სადაც URI შენდება, და არასდროს შეინახო უკვე დაშიფრული პაროლი ცვლადში, რომელსაც სხვა რამ კიდევ დაშიფრავს.
იგივე ხდება პირიქით, როცა პაროლი URI-ში კი არა, დრაივერის ცალკე პარამეტრად გადაეცემა. ცალკე პარამეტრები URI-დეკოდირებას არ გადიან, ამიტომ დაშიფრული URI-დან გადაკოპირებული და password ველში ჩასმული პაროლი %40-ით მიდის. რომელ ფორმასაც არ უნდა იყენებდე, ცალკე ველი იღებს ნედლ პაროლს, URI კი - დაშიფრულს.
Shell-ის ბრჭყალები
თუ URI-ს ბრძანების ხაზზე ბრჭყალების გარეშე ჩასვამ, shell მას პირველი მიწვდება. პარამეტრებს შორის & ბრძანებას ფონზე უშვებს და ყველაფერს მის შემდეგ აგდებს, ამიტომ authSource=admin ჩუმად ქრება. ორმაგ ბრჭყალებში $ ცვლადს იწყებს: პაროლი pa$word ხდება pa პლუს ის, რაც $word-მა გაშალა, ჩვეულებრივ არაფერი. ინტერაქტიულ bash სესიაში !-მაც შეიძლება history-ის გაშლა გამოიწვიოს.
$ mongosh 'mongodb://shopapp:pa$word@db.example.net:27017/shop?authSource=admin'bash-სა და zsh-ში ერთი ბრჭყალები ყველაფერს პირდაპირ გადასცემს. PowerShell ორმაგ ბრჭყალებშიც აფართოებს $-ს და escape სიმბოლოდ backtick-ს იყენებს, ამიტომ იქაც ერთი ბრჭყალებია უსაფრთხო არჩევანი. კიდევ უკეთესია პაროლი საერთოდ არ ჩაწერო და mongosh-ს მისი მოთხოვნა მიანდო - ასე shell პრობლემიდან მთლიანად გამოდის.
გარემოს ფაილები, secrets და ბოლო ახალი ხაზები
საიდუმლო, რომელიც echo "pw" > file-ით შეიქმნა, პაროლის შემდეგ ახალ ხაზს შეიცავს; printf '%s' "pw" არ შეიცავს. წაიკითხე ეს ფაილი გარემოს ცვლადში ან Kubernetes secret-ში და დრაივერი გაგზავნის pw\n-ს, რაც არ ემთხვევა. ზოგი .env loader გარშემო ბრჭყალებს მნიშვნელობის ნაწილად ტოვებს, ზოგი აშორებს, და ქცევა ბიბლიოთეკებსა და ვერსიებს შორის განსხვავდება. Docker Compose-ი compose ფაილში $-ს ცვლადის ჩანაცვლებად თვლის, ამიტომ ლიტერალური დოლარის ნიშანი იქ $$-ად უნდა დაიწეროს.
სწრაფი შემოწმება: დაბეჭდე იმ პაროლის სიგრძე, რომელიც შენმა პროცესმა მიიღო, და შეადარე მოსალოდნელს. ერთი-ორი ზედმეტი სიმბოლო ახალი ხაზია ან ბრჭყალების წყვილი.
Docker-ის root მომხმარებელი, რომელიც არასდროს შექმნილა ხელახლა
ოფიციალური mongo image root მომხმარებელს ქმნის MONGO_INITDB_ROOT_USERNAME-დან და MONGO_INITDB_ROOT_PASSWORD-იდან, admin ბაზაში და მხოლოდ მაშინ, როცა მონაცემთა დირექტორია პირველ გაშვებაზე ცარიელია. თუ ეს ცვლადები მოგვიანებით შეცვალე, არაფერი ხდება: არსებულ volume-ში ძველი მომხმარებელი ძველი პაროლით რჩება. თუ პაროლი compose ფაილში შეცვალე და ავტორიზაცია ახლა ვარდება, ეს არის მიზეზი. ან შეცვალე პაროლი ბაზის შიგნიდან changeUserPassword-ით, ან განზრახ გადააგდე volume და თავიდან დაიწყე - რაც მონაცემებს წაშლის. ასევე გაითვალისწინე, რომ MONGO_INITDB_DATABASE ამ ბაზაში მომხმარებელს არ ქმნის; root მომხმარებელი მაინც admin-შია, ამიტომ მოქმედებს authSource=admin.
mongodb და mongodb+srv, და რას ცვლის ეს ავტორიზაციისთვის#
mongodb+srv:// string დრაივერს აიძულებს სერვერების სია DNS-ში მოძებნოს. ის ამავე სახელზე TXT ჩანაწერსაც კითხულობს და ამ TXT ჩანაწერს ზუსტად სამი პარამეტრის დაყენება შეუძლია: authSource, replicaSet და loadBalanced. ასე აკეთებენ მართვადი სერვისები, მაგალითად MongoDB Atlas, რომ authSource=admin შენი ჩაწერის გარეშე გაჩნდეს. თუ Atlas-ის სტილის string-ს დაშლი და უბრალო mongodb:// ჰოსტების სიად გადაწერ, ამ ნაგულისხმევ მნიშვნელობებს კარგავ და შედეგი ავტორიზაციაზე ვარდება, სანამ authSource=admin-ს თვითონ არ დაამატებ. URI-ში მითითებული პარამეტრები TXT ჩანაწერს გადაფარავს, ამიტომ ხელით დამატებული authSource ყოველთვის იმარჯვებს.
+srv-ის მეორე ნაგულისხმევი მნიშვნელობა TLS-ია, რომელიც ჩართულია. mongodb://-ზე გადაყვანა tls=true-ს გარეშე გაძლევს კავშირს, რომელსაც სერვერი უარყოფს, და ეს ავტორიზაციის შეცდომად კი არა, timeout-ად ჩანს.
Atlas-ისთვის კონკრეტულად, რამდენიმე პუნქტი "bad auth" ანგარიშების უმეტესობას ხსნის:
- URI-ში მითითებული მომხმარებელი უნდა იყოს ბაზის მომხმარებელი პროექტის Database Access გვერდიდან და არა ის ელფოსტა და პაროლი, რომლითაც Atlas-ის ვებსაიტზე შედიხარ. ეს ერთმანეთთან კავშირის არმქონე ექაუნთებია.
- ახლად შექმნილ ან შეცვლილ ბაზის მომხმარებელს კლასტერამდე მისვლას შეიძლება მცირე დრო დასჭირდეს; ცვლილებისთანავე წარუმატებლობის შემთხვევაში ერთხელ ხელახლა სცადე, სანამ დებაგს დაიწყებ.
- ქსელის წვდომის სიიდან გამოტოვებული მისამართი
bad auth-ს არ იძლევა. ის იძლევა server selection timeout-ს, რადგან კავშირი ავტორიზაციის დაწყებამდე წყდება.
როცა ხალხი ეძებს "mongodb get connection string"-ს, პასუხი იმაზეა დამოკიდებული, სად არის ბაზა. Atlas-ი გაძლევს string-ს კლასტერის Connect დიალოგიდან, +srv ფორმით. თვითმართვადი ან ნაქირავები ცალკე სერვერი გაძლევს ჰოსტს, პორტს, მომხმარებელსა და პაროლს და mongodb://user:pass@host:port/db?authSource=...-ს თვითონ აწყობ. ცალკე სერვერს SRV ჩანაწერები არ აქვს, ამიტომ +srv არ გამოდგება.
TLS-ის პარამეტრები: როცა პრობლემა ავტორიზაცია არ არის#
TLS-ის პრობლემები ხდება მანამ, სანამ სერვერი პაროლს დაინახავდეს, ამიტომ კოდ 18-ს არასდროს იძლევა. ისინი იძლევა ServerSelectionTimeoutError-ს ან handshake-ის შეცდომას, რომელშიც სერტიფიკატებია ნახსენები. პარამეტრების ცოდნა მაინც ღირს, რადგან ავტორიზაციის ბაგის ძებნისას TLS-ის პარამეტრების შეცვლა ხშირად პირველ შეცდომას მეორეს უმატებს.
| პარამეტრი | ნაგულისხმევი | დანიშნულება |
|---|---|---|
tls | false (true +srv-ით) | კავშირის დაშიფვრა |
tlsCAFile | სისტემური საცავი | CA, რომელმაც კერძო სერტიფიკატს მოაწერა ხელი |
tlsCertificateKeyFile | არ არის | კლიენტის სერტიფიკატი, X.509-ისთვის |
tlsAllowInvalidHostnames | false | სახელის შემოწმების გამოტოვება - მხოლოდ ტესტირებისთვის |
tlsAllowInvalidCertificates | false | ყველა შემოწმების გამოტოვება - მხოლოდ ტესტირებისთვის |
ssl=true არის tls=true-ს ძველი ალიასი და ისევ მიიღება. თუ სერვერი კერძო CA-ს სერტიფიკატს იყენებს, tlsCAFile მიუთითე ამ CA-ზე და tlsAllowInvalidCertificates-ს ნუ მიმართავ, რომელიც ყველა სერტიფიკატს ყველასგან იღებს და დაშიფვრის აზრს კარგავს. tls=true-ს დაყენება სერვერზე, რომელიც TLS-ზე არ არის გამართული, handshake-ზე ვარდება, მონაცემები რაც უნდა იყოს.
ერთი ავტორიზაციის მეთოდი TLS-ზეა დამოკიდებული: X.509 კლიენტის სერტიფიკატები. authMechanism=MONGODB-X509-ის დროს სერტიფიკატი თვითონ არის მონაცემი, პაროლი არ არსებობს და authSource უნდა იყოს $external, ვირტუალური ბაზა, რომელიც გარედან ავტორიზებულ მომხმარებლებს ინახავს. LDAP (PLAIN), Kerberos (GSSAPI) და AWS IAM (MONGODB-AWS) ასევე $external-ის წინააღმდეგ ავტორიზდებიან. თუ გადაკოპირებულ URI-ში წერია authSource=$external, შენ კი ჩვეულებრივ მომხმარებელს და პაროლს იყენებ, შენი ბაგი სწორედ ეს არის.
SCRAM მექანიზმები
ჩვეულებრივი პაროლიანი მომხმარებლები SCRAM-ით ავტორიზდებიან. MongoDB 4.0-იდან ახალი მომხმარებლები ნაგულისხმევად იღებენ ორივე, SCRAM-SHA-1 და SCRAM-SHA-256 მონაცემს, და დრაივერები უფრო ძლიერს არჩევენ. პრობლემები ჩნდება, როცა URI აიძულებს authMechanism=SCRAM-SHA-1-ს, მომხმარებელი კი მხოლოდ SHA-256-ით შეიქმნა (ან პირიქით), ან როცა დრაივერი, რომელიც SHA-256-ს არ იცნობს, ისეთ მომხმარებელს შეხვდება, რომელსაც მხოლოდ SHA-256 აქვს. გამოსწორება თითქმის ყოველთვის ისაა, რომ authMechanism URI-დან წაშალო და დრაივერს მოლაპარაკება მიანდო. usersInfo აჩვენებს მომხმარებლის mechanisms მასივს, თუ შემოწმება გჭირდება.
კავშირის ტესტირება mongosh-ით#
mongosh საცნობარო კლიენტია. თუ ის მნიშვნელობების რომელიმე ნაკრებით ერთვება, ეს მნიშვნელობები სწორია და ყველაფერი, რაც იმავე მნიშვნელობებით ვარდება, აპლიკაციის ან მისი გარემოს პრობლემაა. გატესტე ამ თანმიმდევრობით, ერთდროულად ერთი რამის შეცვლით.
- დაუკავშირდი მონაცემების გარეშე, ქსელური გზის დასამტკიცებლად:
mongosh "mongodb://db.example.net:27017" --eval 'db.runCommand({ ping: 1 })'.{ ok: 1 }ნიშნავს, რომ ჰოსტი, პორტი და TLS წესრიგშია; timeout ნიშნავს, რომ პაროლების ძებნა შეწყვიტე. - დაუკავშირდი ცალკე flag-ებით და მოთხოვნილი პაროლით, რომ არც კოდირებამ, არც shell-ის ბრჭყალებმა ხელი ვერ შეგიშალოს.
- როცა ეს იმუშავებს, დაუკავშირდი სრული URI-ით ერთ ბრჭყალებში. თუ მე-2 ნაბიჯი მუშაობს და მე-3 არა, პრობლემა URI-შია - კოდირება,
authSourceან რომელიმე პარამეტრი. - ბოლოს გაუშვი აპლიკაცია იმავე URI-ით. თუ აქ ვარდება, აპლიკაცია სხვას იღებს.
$ mongosh --host db.example.net --port 27017 \ -u shopapp -p --authenticationDatabase admin shop-p მნიშვნელობის გარეშე პაროლს ითხოვს. --authenticationDatabase არის authSource-ის სახელი ბრძანების ხაზზე, რაც ორ ფორმას შორის გადართვისას ხშირად დაბნეულობას იწვევს.
მონაცემების შემოწმება არსებული სესიის შიგნიდანაც შეგიძლია, რაც მოსახერხებელია, როცა უკვე ადმინისტრატორად ხარ დაკავშირებული და სხვა მომხმარებლის ცდა გინდა:
db.getSiblingDB("admin").auth("shopapp", passwordPrompt())db.runCommand({ connectionStatus: 1 }).authInfoauth() წარმატებისას აბრუნებს { ok: 1 }-ს, წინააღმდეგ შემთხვევაში კი Authentication failed შეცდომას აგდებს; ბაზა, რომელზეც მას გამოიძახებ, არის ტესტირებადი authSource. connectionStatus შემდეგ აჩვენებს, რომელი მომხმარებლები და როლები აქვს სესიას. Compass ისევე იქცევა: მის გაფართოებულ პარამეტრებში Authentication ჩანართს აქვს ველი "Authentication Database", რაც authSource-ია სხვა სახელით.
დრაივერების სპეციფიკური ხაფანგები#
ყველა დრაივერი ერთსა და იმავე URI-ს იღებს, მაგრამ თითოეულს თავისი გზა აქვს მონაცემების ცალკე პარამეტრებად მისაღებად და თითოეულს აქვს თავისი თავისებურება, რომელიც კოდ 18-ს იწვევს.
Node.js driver და Mongoose
Node-ის დრაივერი მონაცემებს იღებს ან URI-ში, ან auth: { username, password }-ით, authSource კი ზედა დონის პარამეტრია. ცალკე პარამეტრები არ დეკოდირდება, ამიტომ იქ ნედლი პაროლი გადაეცი.
const client = new MongoClient("mongodb://db.example.net:27017/shop", { auth: { username: "shopapp", password: process.env.MONGO_PASSWORD }, authSource: "admin",});Mongoose-ი უმატებს პარამეტრებს dbName, user და pass. dbName ცვლის, რომელ ბაზას იყენებს შენი მოდელები, მაგრამ ადვილია URI-ის path-სა და dbName-ს შორის შეუსაბამობა, რომ ისინი სხვადასხვა ბაზას გულისხმობდნენ. როცა dbName-ს იყენებ, authSource ცხადად მიუთითე და არა რომელიმე ნაგულისხმევ მნიშვნელობას მიანდო, და კითხვა არასდროს გაჩნდება.
PyMongo
PyMongo იღებს username, password და authSource keyword არგუმენტებს, რაც კოდირებას მთლიანად გვერდს უვლის. თუ URI მაინც აგებ, urllib.parse.quote_plus არის ფუნქცია, რომელსაც PyMongo-ს დოკუმენტაცია იყენებს. PyMongo შეცდომას აცხადებს როგორც OperationFailure code 18-ით და codeName: AuthenticationFailed-ით სრულ შეცდომაში; ServerSelectionTimeoutError-ის დაჭერა და "auth failed"-ის ლოგირება ქსელის პრობლემის არასწორი შეტყობინების უკან დამალვის გავრცელებული გზაა.
client = MongoClient( "db.example.net", 27017, username="shopapp", password=os.environ["MONGO_PASSWORD"], authSource="admin",)Go, Java და PHP
Go-ს დრაივერი ჯერ URI-ს იყენებს და შემდეგ საშუალებას გაძლევს მონაცემები options.Credential-ით გადაფარო, რომლის AuthSource ველი იმავე წესით ივსება, როგორც URI-ში. თუ SetAuth-ს ისეთ credential-ს გადასცემ, რომელშიც AuthSource ცარიელია, ნახე, რას ამბობს URI-ის path. Java-ში MongoCredential.createScramSha256Credential(user, source, password) ავტორიზაციის ბაზას მეორე არგუმენტად იღებს - თუ ჩვევით აპლიკაციის ბაზას იქ ჩაწერ, კლასიკურ ბაგს ახალ ენაზე გაიმეორებ. PHP-ის ბიბლიოთეკა URI-ის პარამეტრებს იღებს new MongoDB\Client($uri, $uriOptions)-ის მეორე არგუმენტად, სადაც username, password და authSource კოდირების გარეშე შეიძლება დაყენდეს.
Framework-ები, რომლებიც URI-ს შენს მაგივრად აგებენ
Spring Boot-ი, Laravel-ის პაკეტები და მსგავსი wrapper-ები ხშირად URI-ს ცალკე პარამეტრებისგან აწყობენ: ჰოსტი, ბაზა, მომხმარებელი და პაროლი. წაიკითხე, როგორ აკეთებს შენი. რამდენიმე მათგანი კონფიგურირებულ ბაზას URI-ის path-ში აქცევს, რაც მას ნაგულისხმევ authSource-ად აქცევს, და ავტორიზაციის ბაზისთვის ცალკე პარამეტრს გვთავაზობს, რომელიც ადვილად შეუმჩნეველია. როცა framework ჩართულია, ერთხელ დაბეჭდე საბოლოო URI (დაფარული პაროლით) გაშვებისას; აქედან ჩვეულებრივ ყველაფერი ცხადია.
პაროლის გადატვირთვა ან წვდომის აღდგენა#
თუ მომხმარებელი არსებობს და უბრალოდ პაროლი არ იცი, ადმინისტრატორს შეუძლია ახალი დააყენოს სხვა არაფრის შეხების გარეშე:
db.getSiblingDB("admin").changeUserPassword("shopapp", passwordPrompt())გაუშვი ბაზაზე, რომელსაც მომხმარებელი ეკუთვნის - აქ admin, ან shop, თუ მომხმარებელი იქ ცხოვრობს. არსებული კავშირები ავტორიზებული რჩება, ამიტომ ამის შემდეგ გადატვირთე აპლიკაცია, რომ ახალი პაროლის მუშაობა დაამტკიცო.
სერვერზე, რომელსაც თვითონ მართავ და სადაც ყველა ადმინისტრატორის პაროლი დაკარგე, დოკუმენტირებული გზაა mongod-ის გადატვირთვა გამორთული access control-ით (წაშალე ან გააკომენტარე security.authorization: enabled mongod.conf-ში), მხოლოდ 127.0.0.1-ზე bind-ით, ადმინისტრატორის შექმნა ან გადატვირთვა და შემდეგ authorization-ის ისევ ჩართვა. გააკეთე ეს მთელი ამ დროის განმავლობაში გარედან მიუწვდომელ ბაზაზე, რადგან იმ წუთებში ყველა, ვისაც დაკავშირება შეუძლია, ფაქტობრივად root-ია.
როცა მომხმარებლები საერთოდ არ არსებობს, MongoDB localhost-იდან დაკავშირებას პირველი მომხმარებლის შესაქმნელად უშვებს - ეს localhost exception-ია. როგორც კი რომელიმე მომხმარებელი არსებობს, გამონაკლისი იკეტება. სწორედ ამიტომ ახალი თვითმართვადი ინსტალაცია ხანდახან თვით სერვერიდან ავტორიზაციის გარეშე ბრძანებებს იღებს და ყველგან სხვაგან უარყოფს.
RE:NODE-ის database hosting-ზე MongoDB სერვერი იქმნება ამ სერვერისთვის გენერირებული მონაცემებით და მასთან პირდაპირ უკავშირდებიან გეგმაზე მითითებულ ჰოსტსა და პორტზე - წინ პროქსი არ დგას, ამიტომ ზემოთ აღწერილი TLS-ისა და +srv-ის ხაფანგები არ მოქმედებს, სანამ თვითონ არ დააყენებ. თუ ავტორიზაცია ვარდება, ჩვეულებრივი ეჭვმიტანილები ამ პოსტშია: ბაზა, რომელსაც მომხმარებელი ეკუთვნის, და როგორ მოხვდა პაროლი შენს URI-ში. გაიდების ბაზების განყოფილება აღწერს, როგორ მართავს პანელი ბაზის მონაცემებს.
FAQ#
რატომ ამბობს MongoDB, რომ ავტორიზაცია ჩავარდა, თუ ჩემი პაროლი სწორია?
იმიტომ, რომ სერვერმა მომხმარებელი სხვა ბაზაში მოძებნა, ვიდრე სადაც ის შეიქმნა. დრაივერი ეძებს authSource-ით დასახელებულ ბაზაში, და როცა ის არ არის, იყენებს URI-ის path-ში მითითებულ ბაზას. admin-ში შექმნილი მომხმარებელი და /shop-ით დამთავრებული URI ყოველთვის ვარდება, სანამ ?authSource=admin-ს არ დაამატებ.
რას ნიშნავს 'bad auth : Authentication failed' Atlas-ზე?
ეს Atlas-ის ფორმულირებაა იმავე შეცდომის კოდისთვის. შეამოწმე, რომ იყენებ ბაზის მომხმარებელს Database Access გვერდიდან და არა Atlas-ში შესვლის ექაუნთს, რომ URI-ში პაროლი პროცენტულად დაშიფრულია და რომ +srv string-ი უბრალო ჰოსტების სიად არ გადაგიწერია authSource=admin-ის დაბრუნების გარეშე.
როგორ გავიგო, რომელ ბაზას ეკუთვნის MongoDB-ის მომხმარებელი?
დაუკავშირდი ადმინისტრატორად და გაუშვი db.adminCommand({ usersInfo: { forAllDBs: true } }). თითოეულ ჩანაწერს აქვს user და db ველი; db-ის მნიშვნელობა არის ის, რაც authSource-ში იწერება. ადმინისტრაციული წვდომის გარეშე წარუმატებელი მცდელობის სერვერის ლოგის სტრიქონი აჩვენებს, რომელი ბაზა მოითხოვა კლიენტმა.
საჭიროა თუ არა პაროლის URL-დაშიფვრა, თუ მას ცალკე გადავცემ?
არა, და არც უნდა. კოდირება მხოლოდ URI-ს ეხება. დრაივერის პარამეტრები, როგორიცაა password, და mongosh-ის -p მოთხოვნა ნედლ პაროლს იღებს; იქ დაშიფრული მნიშვნელობა % თანმიმდევრობებით იგზავნება და ვარდება.
არის თუ არა authSource იგივე, რაც ბაზა, რომელსაც გამოყენება მინდა?
მხოლოდ მაშინ, როცა მომხმარებელი ამ ბაზაში შეიქმნა. URI-ის path-ში მითითებული ბაზა არის ადგილი, სადაც შენი მოთხოვნები ნაგულისხმევად მიდის; authSource არის ადგილი, სადაც სერვერი შენს ექაუნთს ეძებს. ისინი ხშირად განსხვავდება, სწორედ ამიტომ არსებობს ეს პარამეტრი.
შეიძლება თუ არა mongodb+srv-ის გამოყენება ერთ ნაქირავებ სერვერთან?
მხოლოდ იმ შემთხვევაში, თუ ვინმემ მისთვის SRV ჩანაწერები შექმნა, ნაქირავებ ერთ სერვერს კი ჩვეულებრივ არ აქვს. გამოიყენე mongodb:// ჰოსტითა და პორტით. თუ წაკითხვადი სახელი გინდა, მისამართზე მიმთითებელი A ჩანაწერი საკმარისია.


