SSH tunnel გადააქვს TCP კავშირს უკვე სანდო SSH სესიის შიგნით. ssh -L 3307:127.0.0.1:3306 deploy@203.0.113.10 შენი ლეპტოპის 3307 პორტს აკავშირებს სერვერის loopback ინტერფეისის 3306 პორტთან, ამიტომ ბაზა, რომელიც მხოლოდ 127.0.0.1-ზე უსმენს, შენი მაგიდიდან ხელმისაწვდომი ხდება firewall-ში ერთი პორტის გახსნის გარეშე. -R საპირისპიროს აკეთებს: სერვერის პორტი უკან, შენს მანქანაზე რაღაცასთან გადადის. -D სესიას SOCKS proxy-დ აქცევს. ეს სამი flag და -N (shell-ის გამოსატოვებლად) ფარავს თითქმის ყველაფერს, რისთვისაც tunnel-ებს იყენებენ.
ერთსტრიქონიან მაგალითებში გამოტოვებულია ის, რაც მოგვიანებით გაწუხებს: რომელ მისამართზე იკრავს forward ბმას, რაზე შეიძლება უარი თქვას სერვერმა, რატომ კვდება tunnel ღამით უხმოდ და რატომ გადასცემს არასწორ ინტერფეისზე მიბმული forward შენს ბაზას ჩუმად ინტერნეტს. ეს პოსტი ამ ყველაფერს ფარავს და ვარაუდობს, რომ გასაღებით შესვლა უკვე შეგიძლია - თუ ვერ შედიხარ, დაიწყე აქედან: SSH keys და sshd_config hardening.
სამი სახის forwarding ერთი შეხედვით#
| Flag | სად უსმენს | სად მიდის |
|---|---|---|
-L local | შენს მანქანაზე | ჰოსტი და პორტი სერვერის თვალით |
-R remote | სერვერზე | ჰოსტი და პორტი შენი მანქანის თვალით |
-D dynamic | შენს მანქანაზე | ნებისმიერ ადგილას, თითო კავშირზე ცალკე (SOCKS) |
წესი, რომელიც სინტაქსს იოლად წასაკითხს ხდის: პორტი იმ მხარეს უსმენს, რომელსაც flag ასახელებს, ხოლო spec-ში მითითებულ დანიშნულების ჰოსტს მეორე ბოლო ამოხსნის. -L 3307:db.internal:3306-ში სახელს db.internal SSH სერვერი ეძებს და არა შენი ლეპტოპი, სწორედ ამიტომ შეუძლია tunnel-ს მიაღწიოს სახელებსა და მისამართებს, რომლებიც შენს მანქანას არასდროს სმენია.
ყოველი forward ერთსა და იმავე დაშიფრულ TCP კავშირზე მიდის 22 პორტისკენ (ან იქ, სადაც sshd უსმენს). დანიშნულების სერვისმა არ იცის, რომ tunnel-ში გადის; ის ჩვეულებრივ TCP კავშირს ხედავს იმ სერვერიდან, სადაც თვითონ მუშაობს, ან იმ ჰოსტიდან, რომელსაც სერვერი გარეთ დაუკავშირდა.
tunnel-ებზე რაიმეს დაგეგმვამდე ორი შეზღუდვა უნდა იცოდე. SSH აგზავნის მხოლოდ TCP-ს - UDP forwarding არ არსებობს, ამიტომ თამაშის ტრაფიკი, UDP-ზე DNS და ხმოვანი პროტოკოლების უმეტესობა -L-სა და -R-ში ვერ გაივლის. გარდა ამისა, ყველა არხი ერთ TCP კავშირს იზიარებს, ამიტომ ერთი დიდი გადაცემა ანელებს ერთ სესიაში არსებულ ყველა სხვა forward-ს, ხოლო გზაზე პაკეტების დაკარგვა ყველას ერთდროულად აჩერებს.
Local forwarding: ბაზამდე მისვლა, რომელიც localhost-ზე უსმენს#
ეს არის შემთხვევა, რომლითაც უმეტესობა მოდის. VDS-ზე MySQL ან PostgreSQL სერვერი სწორად არის გამართული მხოლოდ loopback-ზე მოსასმენად - MySQL-ში bind-address = 127.0.0.1, PostgreSQL-ში listen_addresses = 'localhost' - და შენ გინდა მისი გახსნა GUI კლიენტში საკუთარ კომპიუტერზე.
$ ssh -N -L 3307:127.0.0.1:3306 deploy@203.0.113.10წაიკითხე ასე: მოუსმინე ლოკალურ 3307 პორტს და ყოველი კავშირი გაგზავნე 127.0.0.1:3306-ზე სერვერის თვალსაზრისით. -N ეუბნება SSH-ს, რომ remote shell არ გაუშვას, ამიტომ ბრძანება ადგილზე რჩება და tunnel-ს ღიად ინახავს, სანამ Ctrl+C-ს არ დააჭერ. შემდეგ, მეორე ტერმინალში:
$ mysql -h 127.0.0.1 -P 3307 -u app -p appdb$ psql "host=127.0.0.1 port=5433 user=app dbname=appdb" # for a -L 5433:...:5432სამი დეტალი წყვეტს, იმუშავებს თუ არა ეს პირველივე ცდაზე.
- კლიენტის მხარეს გამოიყენე `127.0.0.1`, არა `localhost`. MySQL კლიენტი
localhost-ს აღიქვამს როგორც "გამოიყენე ლოკალური Unix socket" და პორტს უგულებელყოფს, ამიტომmysql -h localhost -P 3307უერთდება იმას, რაც შენს ლეპტოპზე MySQL-ია, ან წყდება, მაგრამ tunnel-ს არასდროს ეხება. ამ ქცევას სრულად განმარტავს MySQL remote connections. - აირჩიე თავისუფალი ლოკალური პორტი.
3306-ის3306-ზე გადამისამართებაbind [127.0.0.1]:3306: Address already in useშეცდომით მთავრდება, როგორც კი ლოკალური MySQL გაქვს გაშვებული. რეალური პორტი პლუს ერთი (3307,5433) ჩვევაა, რომელიც ამას თავიდან გაცილებს. Linux-ზე1024-ზე დაბალი ლოკალური პორტის დაკავებას root სჭირდება, ესეც მაღალი პორტის არჩევის კიდევ ერთი მიზეზია. - ბაზა კავშირს თვით სერვერიდან მომავლად ხედავს. MySQL-ისთვის ეს ნიშნავს, რომ ანგარიში უნდა არსებობდეს
127.0.0.1-ის ანlocalhost-ისთვის (ეს უკანასკნელი მხოლოდ მაშინ ემთხვევა, როცა სახელების ამოხსნა ჩართულია, რაც ნაგულისხმევად ასეა). PostgreSQL-ისთვის კავშირი TCP-ით მოდის127.0.0.1-დან, ამიტომpg_hba.conf-ს127.0.0.1/32-ისთვისhostხაზი სჭირდება; დისტრიბუციის პაკეტების უმეტესობას ის აქვს. tunnel ბაზის ავტორიზაციას არ აუვლის გვერდს - ის მხოლოდ კარამდე მიგიყვანს.
გადამისამართება სხვა მანქანაზე ან Unix socket-ზე
დანიშნულება არ არის აუცილებელი SSH სერვერი იყოს. თუ ბაზა კერძო მისამართზეა იმ მანქანის უკან, რომელზეც შესვლა შეგიძლია, დაასახელე ის:
$ ssh -N -L 5433:10.0.0.20:5432 deploy@203.0.113.10ახლა SSH სერვერი შენი სახელით ხსნის კავშირს 10.0.0.20:5432-ზე. ეს მონაკვეთი სერვერსა და ბაზას შორის SSH-ით არ იშიფრება; ის კერძო ქსელში ჩვეულებრივი TCP კავშირია. შენ მიერ კონტროლირებად ქსელში ეს ჩვეულებრივ საკმარისია, და როცა არ არის, ამის ხმამაღლა თქმა ღირს.
OpenSSH 6.7-მა და უფრო ახალმა შეიძლება სერვერზე Unix socket-ზეც გადაამისამართოს, რაც საშუალებას გაძლევს ისეთ ბაზას მიაღწიო, რომელსაც TCP მსმენელი საერთოდ არ აქვს:
$ ssh -N -L 5433:/var/run/postgresql/.s.PGSQL.5432 deploy@203.0.113.10PostgreSQL მაშინ ლოკალური socket კავშირს ხედავს და pg_hba.conf თავის host წესების ნაცვლად local წესებს იყენებს. socket forwarding სერვერზე ცალკე კონტროლდება AllowStreamLocalForwarding-ით, რომლის ნაგულისხმევი მნიშვნელობა yes-ია.
tunnel-ების მუდმივად გახდომა ~/.ssh/config-ით#
spec-ის ყოველ ჯერზე აკრეფა მობეზრდება, ხოლო config ფაილი იმავეს სახელით იღებს:
Host db-tunnel HostName 203.0.113.10 User deploy IdentityFile ~/.ssh/id_ed25519 LocalForward 3307 127.0.0.1:3306 LocalForward 5433 127.0.0.1:5432 ExitOnForwardFailure yes ServerAliveInterval 30 ServerAliveCountMax 3ssh -N db-tunnel ახლა ორივე forward-ს ხსნის. გაითვალისწინე, რომ LocalForward მოსასმენ პორტს და დანიშნულებას ორწერტილით კი არა, გამოტოვებით (space) ყოფს. RemoteForward და DynamicForward იმავე ფორმით არსებობს.
ExitOnForwardFailure yes ის არის, რომელიც ყოველთვის უნდა დაამატო. მის გარეშე, თუ პორტი უკვე დაკავებულია, SSH გაფრთხილებას დაბეჭდავს, მაინც შეგიშვებს და დაგტოვებს სესიით, რომელსაც tunnel არ აქვს - რაც გარეგნულად არ განსხვავდება მომუშავისგან, სანამ კლიენტი დაკავშირებას არ შეეცდება. მასთან ერთად SSH შეცდომით გამოდის და ნებისმიერ მეთვალყურეს შეუძლია თავიდან სცადოს.
თუ ბაზის ჰოსტზე მხოლოდ bastion-ის გავლით მიდიხარ, forwarding-ს ProxyJump-თან გააერთიანე და ორი tunnel ხელით ჯაჭვად ნუ დააკავშირებ. ssh -J bastion -N -L 5433:127.0.0.1:5432 deploy@10.0.0.20 bastion-ის გავლით უერთდება შიდა მანქანას და იქიდან აკეთებს forward-ს, ხოლო ავთენტიფიკაცია ყოველ ნახტომზე შენს ლეპტოპზე ხდება.
კიდევ ორი flag ღირს ცოდნად. -f ავთენტიფიკაციის შემდეგ SSH-ს ფონზე გადაიყვანს, რაც სკრიპტებისთვის მოსახერხებელია: ssh -f -N db-tunnel. და უკვე გახსნილ სესიას forward-ის დამატება შეგიძლია escape მიმდევრობით ~C, რომელიც ხაზის დასაწყისში იკრიფება, შემდეგ კი -L 8080:127.0.0.1:80. OpenSSH 9.2-დან ეს ბრძანების ხაზი ნაგულისხმევად გამორთულია და შენს კლიენტის config-ში EnableEscapeCommandline yes სჭირდება.
Remote forwarding: შენს მანქანაზე არსებულის გამოჩენა#
-R სერვერზე მოსასმენ პორტს ხსნის და კავშირებს სესიის გავლით უკან, შენს მხარეს აბრუნებს.
$ ssh -N -R 8080:127.0.0.1:3000 deploy@203.0.113.10სერვერის 8080 პორტზე კავშირები ახლა შენი ლეპტოპის 127.0.0.1:3000-ს აღწევს - ეს შეიძლება იყოს დეველოპმენტის web სერვერი, webhook მიმღები, რომელსაც დებაგავ, ან სახლის ქსელში არსებული სერვისი საჯარო მისამართის გარეშე. იგივე იდეაა, რასაც კომერციული tunnel სერვისები აფასოვებენ, ოღონდ მესამე მხარის გარეშე.
აი, სიურპრიზი, რომელსაც თითქმის ყველა ხვდება: ნაგულისხმევად remote პორტი უსმენს მხოლოდ სერვერის loopback-ზე. გაუშვი ss -tlnp სერვერზე და დაინახავ 127.0.0.1:8080-ს და არა 0.0.0.0:8080-ს, ანუ ინტერნეტში მასთან ვერავინ მივა. ამ ნაგულისხმევს სერვერის sshd_config-ში GatewayPorts განსაზღვრავს:
GatewayPorts | Remote forward-ები იკვრება | როდის გამოიყენო |
|---|---|---|
no (ნაგულისხმევი) | მხოლოდ loopback-ზე | თითქმის ყოველთვის |
yes | ყველა ინტერფეისზე | იშვიათად; ყოველი -R საჯარო ხდება |
clientspecified | იქ, სადაც კლიენტი მოითხოვს | როცა ზოგი forward საჯარო უნდა იყოს |
clientspecified-თან კლიენტი მისამართს spec-ში ირჩევს: -R 0.0.0.0:8080:127.0.0.1:3000 ყველა ინტერფეისს ითხოვს, ხოლო -R 8080:... მაინც loopback-ს იღებს. ეს ორი არანაგულისხმევი მნიშვნელობიდან უფრო უსაფრთხოა, რადგან გამოჩენას ყოველი tunnel-ისთვის შეგნებულ ქმედებად აქცევს და არა ყველასთვის გვერდითი ეფექტად.
უფრო გავრცელებული და უსაფრთხო ხერხია GatewayPorts no-ს დატოვება და სერვერზე loopback პორტის წინ reverse proxy-ის დაყენება. Nginx, რომელიც 443-ზე უსმენს სერტიფიკატით და 127.0.0.1:8080-ზე აპროქსირებს, გადამისამართებულ სერვისს სათანადო სახელსა და TLS-ს აძლევს, ხოლო tunnel-ის ნედლი პორტი არასდროს ხდება საჯარო. თუ პორტს firewall-ში აღებ, ufw-ის სახელმძღვანელო განმარტავს, როგორ დაუშვა ის მხოლოდ იმ მისამართებიდან, რომლებსაც სჭირდებათ.
remote პორტი 0 სერვერს თავისუფალი პორტის არჩევას სთხოვს და მას დაბეჭდავს, რაც სკრიპტებში გამოდგება. ხოლო OpenSSH 7.6-დან -R მხოლოდ პორტით და დანიშნულების გარეშე (-R 1080) სერვერზე ქმნის SOCKS proxy-ს, რომელიც შენი მანქანის გავლით გადის გარეთ - -D-ის სარკისებური ანარეკლი.
Dynamic forwarding: SOCKS proxy ერთი flag-ით#
$ ssh -N -D 1080 deploy@203.0.113.10ეს 127.0.0.1:1080-ზე SOCKS4 და SOCKS5 proxy-ს უშვებს. ნებისმიერ აპლიკაციას, რომელიც მას მიუთითებ, ყოველი კავშირი შენი მანქანიდან კი არა, სერვერიდან ეხსნება იმ დანიშნულებამდე, რომელსაც ის ითხოვს. მიუთითე ბრაუზერს და სერვერის მისამართიდან დაათვალიერებ ვებს; მიუთითე ბაზის ინსტრუმენტს და ყველა კერძო ჰოსტს მიწვდები, რომელსაც სერვერი წვდება.
$ curl --socks5-hostname 127.0.0.1:1080 http://10.0.0.20:9000/health--socks5-hostname მნიშვნელოვანია: ის სახელს ამოსახსნელად proxy-ს უგზავნის და არა ლოკალურად ხსნის. უბრალო --socks5 სახელს ჯერ შენს მანქანაზე ეძებს, რაც შიდა სახელებზე ვერ ხერხდება და მოძიებას შენს ლოკალურ resolver-ს აჟონებს. ბრაუზერებსაც იგივე გადამრთველი აქვთ - Firefox-ში ეს არის ვარიანტი DNS-ის proxy-ზე გასატარებლად SOCKS v5-ის გამოყენებისას.
SSH-ით SOCKS proxy გონივრული გზაა კერძო ქსელში admin ინტერფეისამდე მისასვლელად, ან იმის შესამოწმებლად, როგორ პასუხობს საიტი სხვა ქვეყნის სერვერიდან. ეს VPN არ არის: proxy-ში მხოლოდ ის აპლიკაციები გადიან, რომლებიც მის გამოსაყენებლად არის გამართული, UDP არ გადის, და რასაც მისკენ მიმართვას დაივიწყებ, პირდაპირ გადის. თუ რეალურად გინდა, რომ მოწყობილობის მთელი ტრაფიკი სერვერზე გადიოდეს, სწორი ინსტრუმენტი ნამდვილი VPN-ია - რას იცავს VPN და რას არა ზღვარს ავლებს.
tunnel-ის ფეხზე დაყენება: autossh და systemd#
ხელით გახსნილი tunnel კვდება ლეპტოპის wifi-თან ერთად, უქმ სესიებს წყვეტილ router-თან ერთად და სერვერის ყოველ გადატვირთვასთან ერთად. tunnel-ისთვის, რაზეც რაღაც არის დამოკიდებული - მაგალითად, ანგარიშგების ამოცანა, რომელიც დისტანციურ ბაზას კითხულობს, ან სახლის სერვისი, რომელიც -R-ით არის გამოჩენილი - მეთვალყურეობა სჭირდება.
პირველი დაცვა keepalive წყვილია: ServerAliveInterval 30 და ServerAliveCountMax 3. ისინი აიძულებენ კლიენტს, დაშიფრული არხით ყოველ 30 წამში გაგზავნოს ზონდი და სამი უპასუხო ზონდის შემდეგ დანებდეს, ამიტომ მკვდარი კავშირი ჩანს დაახლოებით 90 წამში და არა უსასრულოდ ჩამოკიდებული. დანებება ზუსტად ის არის, რაც გინდა: პროცესი, რომელიც გამოდის, შეიძლება თავიდან გაეშვას.
autossh კლასიკური გადამტვირთავია. ის ssh-ს უშვებს და ყოველ ჯერზე თავიდან იწყებს, როცა ის გამოდის.
$ autossh -M 0 -f -N \ -o "ServerAliveInterval 30" -o "ServerAliveCountMax 3" \ -o "ExitOnForwardFailure yes" \ -L 3307:127.0.0.1:3306 deploy@203.0.113.10-M 0 autossh-ის საკუთარ მონიტორინგის პორტს თიშავს და სანაცვლოდ SSH-ის keepalive-ებს ეყრდნობა, რაც ჩვეულებრივი თანამედროვე გამართვაა. ერთი ქცევა უნდა იცოდე: ნაგულისხმევად autossh ssh-ს, რომელიც პირველ 30 წამში გამოდის, ფატალურ შეცდომად თვლის და ჩერდება (ამას გარემოს ცვლადი AUTOSSH_GATETIME აკონტროლებს), რაც არასწორ პაროლს ან host key-ს იჭერს და უსასრულოდ არ ცდილობს. -f-ით გაშვებისას ის ამ დროს ნულზე აყენებს და განაგრძობს ცდას.
Linux მანქანაზე systemd-ით autossh საერთოდ არ გჭირდება. Restart=always-იანი unit იმავე საქმეს აკეთებს და ლოგებს journal-ში ინახავს:
[Unit]Description=SSH tunnel to the databaseAfter=network-online.targetWants=network-online.target[Service]User=tunnelExecStart=/usr/bin/ssh -N -o ExitOnForwardFailure=yes \ -o ServerAliveInterval=30 -o ServerAliveCountMax=3 \ -i /home/tunnel/.ssh/id_ed25519 -L 3307:127.0.0.1:3306 tunnel@203.0.113.10Restart=alwaysRestartSec=10[Install]WantedBy=multi-user.target$ sudo systemctl daemon-reload$ sudo systemctl enable --now db-tunnel$ journalctl -u db-tunnel -fორი რამ ხდის ამას უსაფრთხოდ დასატოვებლად. გასაღებს, რომელსაც ის იყენებს, passphrase არ აქვს - ვერც ექნება, რადგან ვინმე არ არის იქ მის აკრეფად - ამიტომ ის უნდა იყოს ცალკე ანგარიშის ცალკე გასაღები, რომელიც სერვერზე შეზღუდულია იმ ერთ forward-ზე, რაც სჭირდება, როგორც შემდეგი თავი აჩვენებს. და პირველი კავშირი ერთხელ ხელით უნდა დამყარდეს ამავე მომხმარებლით, რომ host key მისმა known_hosts-მა მიიღოს; სერვისი თითის ანაბეჭდის კითხვას ვერ უპასუხებს და მისი თავიდან ასაცილებლად არასდროს უნდა გამართო StrictHostKeyChecking no.
რას უშვებს სერვერი: AllowTcpForwarding და მისი მეგობრები#
ყველაფერი ზემოთ მუშაობს, რადგან OpenSSH სერვერი forwarding-ს ნაგულისხმევად უშვებს. ეს სწორი ნაგულისხმევია ადმინისტრატორის საკუთარი ანგარიშისთვის და არასწორი - უმეტესი სხვისთვის. ეს არის კონტროლები, ყველა sshd_config-ში:
| Directive | ნაგულისხმევი | რას აკეთებს |
|---|---|---|
AllowTcpForwarding | yes | no, local, remote ან all |
GatewayPorts | no | -R პორტები loopback-ს გარეთ იკვრება თუ არა |
PermitOpen | any | დანიშნულებები, რომლებსაც -L-ს შეუძლია მიაღწიოს |
PermitListen | any | პორტები, რომლებზეც -R-ს შეუძლია მოსმენა (OpenSSH 7.8+) |
AllowStreamLocalForwarding | yes | Unix socket forwarding |
PermitTunnel | no | Layer 3 tun მოწყობილობები ssh -w-ით |
DisableForwarding | no | ყველა სახეობას ერთდროულად თიშავს |
რასაც ხალხი ამჩნევს: forwarding-ს shell არ სჭირდება. მომხმარებელს, რომლის ანგარიში ForceCommand internal-sftp-ით SFTP-ზეა დაკეტილი, ან რომლის shell /usr/sbin/nologin-ია, მაინც შეუძლია ssh -N -L ...-ის გაშვება და შენი სერვერის გამოყენება საფეხურად საკუთარ კერძო ქსელში, თუ მისთვის forwarding არ არის გამორთული. ამიტომ შეზღუდული ანგარიშებისთვის Match ბლოკმა ეს პირდაპირ უნდა თქვას:
Match Group sftp-only AllowTcpForwarding no AllowStreamLocalForwarding no X11Forwarding noMatch User tunnel AllowTcpForwarding local PermitOpen 127.0.0.1:3306 X11Forwarding noიგივე შეზღუდვები გასაღებზეც შეიძლება დაწესდეს authorized_keys-ში, რაც ერთი დანიშნულების სერვისული ანგარიშისთვის უფრო ლამაზი ვარიანტია:
restrict,port-forwarding,permitopen="127.0.0.1:3306",command="/bin/false" ssh-ed25519 AAAAC3Nza... tunnel@reportingrestrict ყველა არასავალდებულოს თიშავს, port-forwarding forwarding-ს ისევ რთავს, permitopen მას ბაზის პორტით ზღუდავს, ხოლო იძულებითი ბრძანება ნიშნავს, რომ გასაღებს არაფრის გაშვება არ შეუძლია, თუნდაც ვინმემ shell მოითხოვოს - -N ბრძანებას არასდროს უშვებს, ამიტომ tunnel არ ზიანდება. permitlisten ეს არის ექვივალენტი გასაღებისთვის, რომელსაც მხოლოდ ერთი -R პორტის შექმნა უნდა შეეძლოს. ამ გასაღების მოპარული ასლი მაშინ ზუსტად ერთი ბაზის კავშირი ღირს, რომელმაც მაინც უნდა გაიაროს ბაზაში ავტორიზაცია.
როგორც ყოველთვის sshd-თან, გადატვირთვამდე გამოსცადე sshd -t-ით და არსებული სესია ღია დატოვე.
უსაფრთხოების გაფრთხილებები, რომლებიც სერიოზულად უნდა მიიღო#
tunnel დაშიფრულია, ავთენტიფიცირებულია და მოსახერხებელია, და ეს თვისებები იოლად გვავიწყებს, რომ ის იმ საზღვრის ხვრელიცაა, რომელიც გაავლე.
- ლოკალური forward ღიაა ყველასთვის შენს მანქანაზე.
-L 3307:...127.0.0.1-ზე იკვრება, ამიტომ ამ კომპიუტერის ყველა პროცესს და მომხმარებელს შეუძლია მასთან დაკავშირება. ლეპტოპზე, რომელსაც მხოლოდ შენ იყენებ, ეს ნორმალურია. გაზიარებულ jump box-ზე ან build სერვერზე კი შენი ბაზის კავშირი გადაეცემა ყველას, ვისაც შესვლა აქვს. ბაზის პაროლი მაინც მოქმედებს; იმოქმედე ისე, თითქოს პორტი საჯარო იყოს. - არასდროს მიაბა forward შემთხვევით ყველა ინტერფეისს.
-g,GatewayPorts yesშენს კლიენტის config-ში, ან spec ისეთი, როგორიცაა-L 0.0.0.0:3307:..., forward-ს შენს ქსელზეც ამყოფებს მოსასმენად. კაფეს ქსელში ეს შენი ბაზაა, დარბაზისთვის შეთავაზებული.-D 0.0.0.0:1080უარესია: ღია proxy სერვერის კერძო ქსელში. - Remote forward-ებს შეუძლიათ შენი firewall-ის გვერდის ავლა.
-Rოფისის ქსელში მყოფი ლეპტოპიდან მის გარეთ მყოფ სერვერზე შიდა სერვისს აქვეყნებს ყველასთვის, ვისაც ამ სერვერის პორტამდე მისვლა შეუძლია. ორგანიზაციები გამავალ SSH-ს სწორედ ამიტომ ბლოკავენ. ნუ გამოიყვან tunnel-ს ისეთი ქსელიდან, რომლის მფლობელიც წინააღმდეგი იქნებოდა. - ბოლო მონაკვეთი შეიძლება ღია ტექსტი იყოს.
-L 5433:10.0.0.20:5432შიფრავს SSH სერვერამდე და არც ერთი ნაბიჯით შემდეგ. ბაზის საკუთარი TLS გამოიყენე ყველაფრისთვის, რაც შენთვის არასანდო ქსელს კვეთს. - Agent forwarding port forwarding არ არის.
-Aშენს გასაღებებს remote ჰოსტზე root-ს უხსნის და ზემოთ აღწერილიდან არაფრისთვის არ არის საჭირო. SSH keys პოსტი განმარტავს, რატომ ცვლის მასProxyJump.
გულწრფელი შედარება სერვისის უბრალოდ გამოჩენასთანაა. ბაზა, რომელიც ინტერნეტში ღიაა TLS-ითა და ძლიერი პაროლით და პირდაპირ უერთდებიან, ლეგიტიმური არქიტექტურაა - ბაზის უსაფრთხოების checklist აღწერს, რა სჭირდება ამას. ბაზა loopback-ზე, რომელსაც მხოლოდ SSH-ით აღწევენ, ბაზის შესვლას თავდასხმის ზედაპირიდან მთლიანად ხსნის. tunnel ჩვეულებრივ ადმინისტრატორებისა და ზოგჯერ გამოყენებული ინსტრუმენტებისთვის უკეთესი არჩევანია, ხოლო პირდაპირი კავშირი - აპლიკაციებისთვის, რომლებსაც მთელი დღე უნდა ჰქონდეთ კავშირი და არ სურთ მეთვალყურე პროცესის მოვლა.
შეცდომები, რომლებსაც ხალხი ხვდება#
`bind [127.0.0.1]:3306: Address already in use` და `Could not request local forwarding.` შენს მანქანაზე რაღაც უკვე უსმენს ამ პორტს - ხშირად ლოკალური ბაზა, ან გუშინდელი tunnel, რომელიც ფონზე ისევ მუშაობს. აირჩიე სხვა ლოკალური პორტი, ან ძველი პროცესი იპოვე ss -tlnp-ით Linux-ზე, lsof -iTCP:3306 -sTCP:LISTEN-ით macOS-ზე, ან netstat -ano | findstr 3306-ით Windows-ზე.
`channel 2: open failed: connect failed: Connection refused`. tunnel კარგადაა; დანიშნულებამ უარი თქვა. SSH სერვერი შენს spec-ში მითითებულ ჰოსტსა და პორტს დაუკავშირდა და იქ არაფერი უსმენდა. შეამოწმე, რომ სერვისი გაშვებულია და იმ მისამართზეა, რომელიც დაასახელე: კერძო მისამართზე მიბმული ბაზა 127.0.0.1-ს უარყოფს, ხოლო loopback-ზე მიბმული - კერძო მისამართს.
`channel 2: open failed: administratively prohibited: open failed`. სერვერის პოლიტიკამ უარი თქვა: AllowTcpForwarding no, PermitOpen, რომელშიც შენი დანიშნულება არ შედის, ან restrict გასაღები port-forwarding-ის გარეშე. ეს კონფიგურაციის პასუხია და არა ქსელის.
`Warning: remote port forwarding failed for listen port 8080`. სერვერმა შენი -R-ისთვის პორტი ვერ გახსნა: ის დაკავებულია (ხშირად წინა სესიით, რომელსაც ვადა ჯერ არ გასვლია), 1024-ზე დაბალია და root არ ხარ, ან PermitListen მას გამორიცხავს. მკვდარი სესია, რომელიც პორტს ინახავს, მეთვალყურეობის ქვეშ მყოფ tunnel-ებზე გავრცელებულია; სერვერზე ClientAliveInterval მას ასუფთავებს.
tunnel უერთდება, მაგრამ კლიენტი ჰკიდია. ჩვეულებრივ კლიენტი tunnel-ს საერთოდ არ იყენებს - localhost socket-ის გზას ირჩევს, ან GUI ინსტრუმენტს საკუთარი SSH tunnel პარამეტრი აქვს, შენსთან ერთად. ჯერ შეამოწმე nc -vz 127.0.0.1 3307-ით: თუ ეს უერთდება, tunnel მუშაობს და პრობლემა კლიენტშია.
tunnel გარკვეული უქმობის შემდეგ კვდება. შენსა და სერვერს შორის router-მა ან firewall-მა უქმი TCP კავშირი გათიშა. დაამატე ServerAliveInterval 30 და მეთვალყურეობა დაუწესე ყველაფერს, რაც ფეხზე უნდა იდგეს.
სად ჯდება tunnel-ები RE:NODE-ზე#
ამ პოსტის ყველა ტექნიკას სჭირდება მანქანა, რომელზეც sshd მუშაობს და შენ ადმინისტრირებ, რაც RE:NODE-ზე VDS-ს ნიშნავს: სრული root წვდომა, ამიტომ sshd_config, GatewayPorts და forwarding-ის შეზღუდვები შენი გასაწყობია. პირველი საათი ახალ VDS-ზე ამ მანქანას იმ წერტილამდე მიიყვანს, სადაც tunnel შემდეგი ნაბიჯია.
ბაზის გეგმები სხვანაირად მუშაობს და tunnel საერთოდ არ სჭირდებათ. PostgreSQL, MySQL, MongoDB, SQL Server ან Valkey სერვერს database hosting-ზე პირდაპირ აღწევ პანელში ნაჩვენებ ჰოსტსა და პორტზე, ამ სერვერისთვის გენერირებული მონაცემებით, ამიტომ კლიენტი მას ისე უერთდება, როგორც ნებისმიერ დისტანციურ ბაზას - TLS-ი და ძლიერი პაროლი აკეთებს იმას, რასაც სხვაგან tunnel. თამაშის, აპლიკაციისა და web სერვერებს ყველა სერვერზე SFTP და პანელში კონსოლი აქვთ, ამიტომ ის ყოველდღიური მიზეზები, რომლებიც ხალხს საკუთარ მანქანებთან tunnel-ების გასახსნელად აიძულებს, იქ სხვაგვარად წყდება.
FAQ#
SSH tunnel იგივეა, რაც VPN?
არა. tunnel ერთ SSH კავშირზე კონკრეტულ TCP პორტებს გადაამისამართებს, ან -D-ის გამოსაყენებლად გამართულ აპლიკაციებს აპროქსირებს. VPN მოწყობილობის მთელ ტრაფიკს IP დონეზე მარშრუტიზებს, UDP-სა და DNS-ის ჩათვლით. SSH-ს შეუძლია layer 3 tunnelling ssh -w-ითა და PermitTunnel-ით, მაგრამ ეს მოუხერხებელია და სპეციალურად შექმნილი VPN ამას უკეთ აკეთებს.
შემიძლია UDP-ის გადაგზავნა SSH-ით?
OpenSSH-ის port forwarding-ით არა, ის მხოლოდ TCP-ზეა. არსებობს გამოსავლები, რომლებიც UDP-ს TCP-ში ახვევენ socat-ით ორივე ბოლოზე, მაგრამ ისინი დაყოვნებას ამატებენ და არღვევენ დროს, რომელზეც რეალური დროის პროტოკოლები, მაგალითად თამაშის ტრაფიკი, არიან დამოკიდებული. თამაშის სერვერისთვის გამოიყენე სათანადო tunnel ან ჰოსტინგზე გაშვებული სერვერი და არა SSH.
ანელებს tunnel ჩემი ბაზის მოთხოვნებს?
ოდნავ, და იშვიათად იმდენად, რომ მნიშვნელობა ჰქონდეს. თანამედროვე CPU-ებზე დაშიფვრა ძალიან მცირე ღირს; რეალური ხარჯი გზის დამატებითი დაყოვნებაა და ის, რომ ყველა forward ერთ TCP კავშირს იზიარებს. admin სესიისთვის ან ღამის ანგარიშისთვის ეს უხილავია. აპლიკაციისთვის, რომელიც წამში ათასობით პატარა მოთხოვნას აკეთებს, პირდაპირ დაუკავშირდი TLS-ით.
რომელი პორტი გამოვიყენო ჩემს მხარეს?
ნებისმიერი თავისუფალი პორტი 1024-ის ზემოთ. რეალური სერვისის პორტი პლუს ერთი (3307 MySQL-ისთვის, 5433 PostgreSQL-ისთვის) იოლად დასამახსოვრებელია და ლოკალურად გაშვებულ არაფერს ეჯახება. პორტის ნომრები შენს მხარესა და სერვერის მხარეს ერთმანეთისგან დამოუკიდებელია.
შემიძლია SSH tunnel-ების გამოყენება Windows-ზე?
დიახ. Windows 10-სა და 11-ს OpenSSH კლიენტი აქვს და აქ არსებული ყველა ბრძანება მუშაობს PowerShell-ში ან Command Prompt-ში. PuTTY-ს იგივე ფუნქცია აქვს განყოფილებაში Connection, SSH, Tunnels, Local, Remote და Dynamic ვარიანტებით. ბაზის ბევრ GUI ინსტრუმენტს, მათ შორის DBeaver-სა და MySQL Workbench-ს, SSH tunnel-ის ჩანართიც ჩაშენებული აქვს.


