Linux-ի ցանց և SSH
IP-ներ և port-եր, միացումների debug, ճիշտ կարգավորված SSH key-եր, tunnel-ներ և հիմնական firewall։
30 վայրկյանում
Սերվերը աշխարհի հետ խոսում է ցանցային ինտերֆեյսների, IP հասցեների և port-երի միջոցով։ Մի քանի գործիք ցույց է տալիս, թե ինչ է լսում, ստուգում է՝ արդյոք URL-ը կամ DNS գրառումը աշխատում է, և թույլ է տալիս անվտանգ մուտք գործել SSH key-երով։ Ավելացրու պարզ firewall, որը թույլ է տալիս միայն քեզ անհրաժեշտը, և Linux սերվերի ամենօրյա ցանցային աշխատանքի մեծ մասը ծածկված է։
Ինչու եմ օգտագործում
- Բոլոր սերվերները կառավարում եմ SSH-ով key-երով, և «միայն SSH key-եր, առանց password login-ի»-ն իմ անվտանգության baseline-ի առաջին կետն է։
- Սերվերների վրա իմ firewall կանոնը պարզ է՝ ufw գումարած provider-ի firewall-ը, որտեղ բաց են միայն 22, 80 և 443 port-երը։
- Երբ կայքը հասանելի չէ, ss-ի, curl-ի և dig-ի նման գործիքները մեկ րոպեում ասում են ինձ՝ DNS-ն է, firewall-ը, nginx-ը, թե հավելվածը։
Ինչպես է աշխատում
01Ինտերֆեյսներ, IP հասցեներ և port-եր
Ցանցային ինտերֆեյսը միացման կետ է՝ ֆիզիկական կամ վիրտուալ քարտ, ինչպես eth0-ն կամ ens3-ը, գումարած lo-ն՝ loopback ինտերֆեյսը։ Յուրաքանչյուր ինտերֆեյս ունի մեկ կամ ավելի IP հասցե։ 127.0.0.1-ը (և IPv6-ում ::1-ը) localhost-ն է․ այն հասանելի է միայն հենց մեքենայից, ինչը այն դարձնում է ճիշտ տեղ nginx-ի հետևում գտնվող տվյալների բազաների և app սերվերների համար։ 10.0.0.0/8-ի և 192.168.0.0/16-ի նման private միջակայքերն օգտագործվում են ցանցերի ներսում, իսկ public IP-ն հասանելի է ինտերնետից։ Port-ը 1-ից 65535 թիվ է, որը նույնականացնում է ծառայությունը IP-ի վրա՝ 22՝ SSH, 80՝ HTTP, 443՝ HTTPS, 5432՝ PostgreSQL, 3306՝ MySQL։ 0.0.0.0-ի վրա լսող հավելվածն ընդունում է միացումներ բոլոր ինտերֆեյսներով, այդ թվում՝ public-ով։
ip -br addr # interfaces and IPs, brief
ip route # routing table and default gateway
hostname -I # all IPs of this host
ping -c 3 example.com # basic reachability (ICMP may be blocked)
traceroute example.com # path to a host (apt install traceroute)
02Ինչն է լսում՝ ss, curl և DNS գործիքներ
Ժամանակակից դիստրիբուտիվներում ss-ը փոխարինել է netstat-ին։ ss -tlnp-ն ցույց է տալիս լսող TCP socket-ները՝ թվային port-երով և դրանց տեր պրոցեսով․ ստուգիր, որ տվյալների բազաները և ներքին ծառայությունները լսում են 127.0.0.1-ի, ոչ թե 0.0.0.0-ի վրա։ curl-ը HTTP debug-ի գործիքն է․ -I-ն ցույց է տալիս միայն header-ները, -v-ն՝ ամբողջ խոսակցությունը, ներառյալ TLS-ը, իսկ --resolve-ը թույլ է տալիս ստուգել նոր սերվերը, մինչև DNS-ը կուղղվի դրան։ wget-ը ավելի լավ է պարզապես ֆայլեր ներբեռնելու համար։ dig-ը DNS-ին հարցում է անում ուղիղ, ինչը ասում է՝ արդյոք domain-ը resolve է լինում այն սերվերին, որը սպասում ես։ @1.1.1.1-ով public resolver-ին հարցնելը շրջանցում է լոկալ cache-ը։
sudo ss -tlnp # listening TCP ports + process
sudo ss -tnp state established # current connections
curl -I https://example.com # status and headers only
curl -v http://127.0.0.1:3000/health # talk to the app directly
curl --resolve example.com:443:203.0.113.10 https://example.com/
dig +short example.com A # IPv4 address
dig example.com MX # mail servers
dig @1.1.1.1 example.com # ask a specific resolver
dig +trace example.com # follow delegation from the root
03SSH key-եր՝ ինչպես են աշխատում և ինչպես կարգավորել
SSH-ը քեզ տալիս է գաղտնագրված shell հեռավոր մեքենայի վրա։ Key-based login-ի դեպքում key pair ես ստեղծում քո համակարգչում՝ private key, որը երբեք դուրս չի գալիս այնտեղից, և public key, որը պատճենում ես սերվերի ~/.ssh/authorized_keys-ում։ Միանալիս սերվերը ստուգում է, որ դու ունես private key-ը, առանց որ այն երբևէ ուղարկվի։ Օգտագործիր ed25519 key-ի տիպը․ այն կարճ է, արագ և ժամանակակից լռելյայնը։ Private key-ը պաշտպանիր passphrase-ով և թող ssh-agent-ը հիշի այն, որպեսզի մեկ session-ում մեկ անգամ մուտքագրես։ Առաջին միացման ժամանակ SSH-ը ցույց է տալիս սերվերի fingerprint-ը և պահում այն known_hosts-ում․ եթե հետագայում այդ fingerprint-ը փոխվի, SSH-ը կզգուշացնի, որովհետև դա կարող է նշանակել, որ ինչ-որ մեկը մեջտեղում է։
# On your laptop
ssh-keygen -t ed25519 -C "your_user@laptop"
ssh-copy-id [email protected] # installs the public key
ssh [email protected]
# If ssh-copy-id is not available, append the .pub file manually:
# cat ~/.ssh/id_ed25519.pub | ssh [email protected] "mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys"
# Permissions on the server must be strict:
chmod 700 ~/.ssh && chmod 600 ~/.ssh/authorized_keys
04~/.ssh/config և password login-ի անջատում
Laptop-իդ ~/.ssh/config ֆայլը յուրաքանչյուր սերվերի տալիս է կարճ անուն՝ իր օգտատիրոջով, հասցեով, key-ով և տարբերակներով, այնպես որ ssh web1-ը փոխարինում է երկար հրամանին։ Սերվերի վրա, երբ key-ը աշխատում է, անջատիր password login-ները և root-ի ուղիղ մուտքը․ դա վերացնում է brute-force հարձակումները, որոնք ամեն public SSH port հարվածում են րոպեների ընթացքում։ Ներկայիս Ubuntu-ում կարգավորումներդ դիր /etc/ssh/sshd_config.d/-ի տակ ֆայլում, որը ներառվում է հիմնական ֆայլից առաջ (առաջին սահմանված արժեքն է հաղթում)։ Միշտ վավերացրու sshd -t-ով և ընթացիկ session-ը բաց պահիր, մինչ նորն ես ստուգում, որպեսզի սխալը չկարողանա քեզ դուրս թողնել։ Որոշ cloud image-ներ այդ դիրեկտորիայում ավելացնում են իրենց ֆայլը, որը նորից միացնում է գաղտնաբառերը, ուստի ստուգիր այն։
# ~/.ssh/config on your laptop
Host web1
HostName 203.0.113.10
User your_user
IdentityFile ~/.ssh/id_ed25519
# /etc/ssh/sshd_config.d/10-hardening.conf on the server
PasswordAuthentication no
KbdInteractiveAuthentication no
PermitRootLogin no
# Then: sudo sshd -t && sudo systemctl reload ssh (sshd on RHEL)
05Ֆայլերի պատճենում՝ scp և rsync
scp-ն ֆայլեր է պատճենում SSH-ով cp-ի նման syntax-ով և նորմալ է մեկ ֆայլի համար։ rsync-ն ավելի լավ է ավելի մեծ ցանկացած բանի համար․ այն փոխանցում է միայն տարբերությունները, կարող է շարունակել ընդհատվածը, -a-ով պահպանում է թույլտվություններն ու ժամանակները և --delete-ով կարող է դիրեկտորիան mirror անել։ Վերջի slash-ը կարևոր է․ rsync src/ dest/-ը պատճենում է src-ի բովանդակությունը, իսկ rsync src dest/-ը ստեղծում է dest/src։ Ավերիչ rsync-ը միշտ նախ գործարկիր --dry-run-ով (-n)՝ տեսնելու, թե ինչ կփոխվի։ Երկու գործիքն էլ օգտագործում են քո SSH config-ը, ուստի host alias-ները աշխատում են։
scp ./dump.sql web1:/tmp/ # one file up
scp web1:/var/log/nginx/error.log . # one file down
rsync -avz --progress ./dist/ web1:/srv/myapp/dist/
rsync -avzn --delete ./dist/ web1:/srv/myapp/dist/ # dry run first!
rsync -avz web1:/var/www/app/storage/app/ ./storage-backup/
06Port forwarding և tunnel-ներ
SSH-ը կարող է այլ traffic տանել իր գաղտնագրված միացման միջով։ Local forwarding-ը (-L) ամենաօգտակարն է․ այն laptop-իդ վրա բացում է port, որը տանում է սերվերի port-ին, այնպես որ կարող ես հասնել տվյալների բազային կամ admin պանելին, որը լսում է միայն սերվերի localhost-ում, առանց այն երբևէ ինտերնետին բացելու։ Remote forwarding-ը (-R) անում է հակառակը և laptop-իդ port-ը բացում է սերվերի վրա, օրինակ՝ լոկալ հավելվածը ցուցադրելու համար։ -D-ն ստեղծում է SOCKS proxy։ Jump host-ը (ProxyJump կամ -J) թույլ է տալիս հասնել private սերվերներին մեկ public bastion-ի միջով։ Ավելացրու -N, երբ ուզում ես միայն tunnel, առանց shell-ի։
# Reach the server's local PostgreSQL at localhost:5433 on your laptop
ssh -N -L 5433:127.0.0.1:5432 web1
# Expose your local port 3000 as port 8080 on the server's localhost
ssh -N -R 8080:127.0.0.1:3000 web1
# Reach a private server through a bastion
ssh -J [email protected] [email protected]
# Or in ~/.ssh/config: ProxyJump bastion
07Firewall-ի հիմունքներ ufw-ով
Firewall-ը որոշում է, թե որ մուտքային և ելքային միացումներն են թույլատրված։ Ubuntu-ում ufw-ն (uncomplicated firewall) kernel-ի netfilter կանոնների հարմար front end է, RHEL-ը օգտագործում է firewalld։ Անվտանգ լռելյայնն է՝ արգելել ամբողջ մուտքային traffic-ը, թույլատրել ելքայինը և բացել միայն այն, ինչ սպասարկում ես՝ SSH, HTTP և HTTPS։ SSH-ը թույլատրիր մինչև firewall-ը միացնելը, այլապես կկտրես սեփական session-դ։ Որպես երկրորդ շերտ օգտագործիր provider-ի cloud firewall-ը․ այն արգելափակում է traffic-ը, մինչև այն կհասնի սերվերին։ Մեկ կարևոր թակարդ՝ Docker-ը կոնտեյների port-երը publish է անում՝ գրելով իր սեփական iptables կանոնները, որոնք շրջանցում են ufw-ն, ուստի կոնտեյներները bind արա 127.0.0.1-ին, եթե դրանք իսկապես public լինելու կարիք չունեն։
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow OpenSSH # allow 22 BEFORE enabling
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw limit OpenSSH # rate-limit repeated SSH attempts
sudo ufw enable
sudo ufw status verbose
sudo ufw allow from 10.0.0.0/16 to any port 5432 # DB from private net only
Օգտագործեք, երբ
- Ցանկացած նոր սերվերի կարգավորում՝ key-եր, առանց password login-ի, firewall, մինչև որևէ բան deploy անելը։
- «Կայքը չի աշխատում» հաղորդումների debug՝ DNS, port, TLS կամ հավելված, շերտ առ շերտ։
- Տվյալների բազաներին և ներքին գործիքներին անվտանգ հասնել SSH tunnel-ներով՝ դրանք բացելու փոխարեն։
- Ֆայլերի deploy կամ backup rsync-ով SSH-ի միջով։
Մի՛ օգտագործեք, երբ
- Տվյալների բազայի port-երը մի բացիր ինտերնետին միայն GUI client միացնելու համար․ օգտագործիր SSH tunnel։
- Որպես անվտանգություն մի հենվիր SSH port-ը փոխելու վրա․ այն միայն նվազեցնում է log-երի աղմուկը։ Աշխատանքը key-երն ու firewall-ն են անում։
- Շատ սերվերներով թիմերի համար հում ընդհանուր key-երը խառնաշփոթ են դառնում․ փոխարենը դիտարկիր SSH certificate-ներ կամ access գործիք։
Այլընտրանքներ
- firewalld / nftables
firewalld-ը լռելյայն է RHEL-ում, nftables-ը kernel-ի կանոնների ժամանակակից համակարգն է, որը կարող են օգտագործել և՛ ufw-ն, և՛ firewalld-ը։
- Cloud provider-ի firewall-ներ
Զտում են traffic-ը, մինչև այն կհասնի VM-ին։ Օգտագործիր դրանք host firewall-ի հետ միասին։
- WireGuard / Tailscale
Private VPN mesh, որպեսզի admin port-երը ընդհանրապես երբեք public չլինեն։
- Session manager-ներ
AWS Systems Manager-ի կամ Teleport-ի նման գործիքները տալիս են audit արվող shell հասանելիություն՝ առանց բաց SSH port-երի։
Լավագույն պրակտիկաներ
- Օգտագործիր ed25519 key-եր passphrase-ով և ssh-agent․ յուրաքանչյուր սարքի համար մեկ key, որպեսզի կարողանաս մեկը revoke անել։
- Անջատիր PasswordAuthentication-ը և PermitRootLogin-ը և reload անելուց առաջ ստուգիր sshd -t-ով։
- SSH-ի կամ firewall-ի կարգավորումները փոխելիս երկրորդ SSH session բաց պահիր։
- Լռելյայն արգելիր մուտքայինը․ բացիր միայն 22, 80 և 443, գումարած provider-ի firewall-ը որպես երկրորդ շերտ։
- Տվյալների բազաները, Redis-ը և app սերվերները bind արա 127.0.0.1-ին կամ private ցանցին, երբեք՝ 0.0.0.0-ին public host-ի վրա։
- Ավելացրու fail2ban՝ SSH-ի կամ վեբ login-ների անընդհատ ձախողվող IP-ները ban անելու համար։
- Ցանկացած --delete-ից առաջ rsync-ը գործարկիր --dry-run-ով։
Տարածված սխալներ
- ufw-ն միացնել մինչև SSH-ը թույլատրելը և ինքդ քեզ դուրս թողնել սերվերից։
- Password login-ն անջատել՝ չստուգելով, որ key-ը իսկապես աշխատում է։
- Docker port-եր publish անել (-p 5432:5432) և ենթադրել, որ ufw-ն պաշտպանում է դրանք։
- ~/.ssh-ի կամ authorized_keys-ի թույլ թույլտվություններ, ինչի պատճառով SSH-ը աննկատ անտեսում է key-ը։
- Անտեսել «host key changed» զգուշացումը և ջնջել known_hosts-ի գրառումը՝ առանց պատճառը ստուգելու։
Խորը վերլուծություն
08TCP connection-ների վիճակները և ինչ են դրանք ասում
Յուրաքանչյուր TCP connection անցնում է վիճակներով, և դրանց հաշվելը արագ health check է։ Սերվերի socket-ը գտնվում է LISTEN վիճակում․ kernel-ն ավարտում է three-way handshake-ը և նոր connection-ները հերթագրում է accept backlog-ում, մինչև հավելվածը ընդունի դրանք։ ESTABLISHED connection-ները տվյալներ են փոխանցում։ Կողմը, որն առաջինն է փակում, մոտ մեկ րոպե մնում է TIME_WAIT-ում, որպեսզի ուշացած փաթեթները չվնասեն նոր connection-ը․ զբաղված proxy-ի վրա հազարավոր TIME_WAIT socket-ները նորմալ են։ CLOSE_WAIT-ը այլ է․ դիմացի կողմը փակել է, բայց քո հավելվածը չի փակել իր ծայրը։ CLOSE_WAIT socket-ների աճող քանակը նշանակում է bug, սովորաբար pool-ում արտահոսող connection, և ի վերջո պրոցեսին file descriptor-ները չեն բավականացնում։ Երբ backlog-ը լցվում է, նոր client-ները timeout են տեսնում, թեև պրոցեսը առողջ է թվում․ ss-ը listening socket-ների վրա ցույց է տալիս հերթերի չափերը։
ss -s # summary of sockets by state
ss -tan state close-wait | wc -l # leaked connections?
ss -tan state time-wait | wc -l
ss -ltn # Recv-Q/Send-Q = backlog use/size
sysctl net.core.somaxconn net.ipv4.ip_local_port_range
nstat -az | grep -i -E 'ListenOverflows|ListenDrops'
09SSH connection-ի ներսում և SSH certificate-ներ
SSH session-ը սկսվում է key exchange-ով․ client-ը և սերվերը համաձայնեցնում են ալգորիթմները և ստանում session key-եր, իսկ սերվերն ապացուցում է իր ինքնությունը host key-ով, որը client-ը ստուգում է known_hosts-ի հետ։ OpenSSH 10.0-ից (ապրիլ 2025) լռելյայն key exchange-ը mlkem768x25519-sha256-ն է՝ դասական X25519-ի և post-quantum ML-KEM-ի հիբրիդ, իսկ DSA key-երն ամբողջությամբ հանված են։ Միայն դրանից հետո է տեղի ունենում օգտատիրոջ authentication-ը՝ key-ով, certificate-ով կամ այլ մեթոդներով։ Թիմերի համար պարզ authorized_keys ֆայլերը վատ են մասշտաբավորվում․ յուրաքանչյուր սերվեր ունի սպասարկման ենթակա ցուցակ, և մարդու հասանելիությունը հետ վերցնելը նշանակում է խմբագրել բոլորը։ SSH certificate-ները լուծում են սա։ Ներքին certificate authority-ն ստորագրում է օգտատիրոջ public key-ը ինքնությամբ, թույլատրված login անուններով և ժամկետով, իսկ սերվերները վստահում են CA-ին մեկ տողով՝ TrustedUserCAKeys։ Կարճաժամկետ certificate-ներն ինքնուրույն են լրանում, ուստի offboarding-ը հիմնականում ավտոմատ է։
ssh -Q kex # key exchange algorithms supported
ssh -v web1 2>&1 | grep -i 'kex: algorithm'
# Certificate authority (keep the CA private key offline or in a vault)
ssh-keygen -t ed25519 -f user_ca -C "user CA"
ssh-keygen -s user_ca -I [email protected] -n your_user -V +8h ~/.ssh/id_ed25519.pub
ssh-keygen -L -f ~/.ssh/id_ed25519-cert.pub # inspect the certificate
# On each server, in sshd_config.d:
# TrustedUserCAKeys /etc/ssh/user_ca.pub
10Netfilter․ որտեղ են հանդիպում ufw-ն, Docker-ը և nftables-ը
Linux-ի բոլոր firewall-ները netfilter-ի front end-եր են՝ kernel-ի փաթեթների ֆիլտրման framework-ի։ Փաթեթներն անցնում են hook-երով՝ PREROUTING, երբ ժամանում են, հետո INPUT, եթե նախատեսված են այս host-ի համար, կամ FORWARD, եթե route են արվում այլ տեղ, OUTPUT՝ տեղում ստեղծված փաթեթների համար, և POSTROUTING՝ դուրս գալիս։ ufw-ն կառավարում է միայն INPUT տիպի կանոններ հենց host-ի համար։ Docker-ը container-ների port-երը հրապարակում է DNAT-ով PREROUTING-ում, ինչը թրաֆիկը վերածում է forward արվող թրաֆիկի, որը երբեք չի հանդիպում ufw-ի INPUT կանոններին․ սա է հայտնի շրջանցման մեխանիզմը, և դրա համար է գոյություն ունի DOCKER-USER chain-ը։ Connection tracking-ը (conntrack) հիշում է հոսքերը, ուստի կանոնները կարող են էժան թույլ տալ established պատասխանները․ շատ զբաղված host-երում conntrack աղյուսակը կարող է լցվել և նոր connection-ներ գցել։ Ընթացիկ դիստրիբուտիվներում iptables հրամանը սովորաբար ներքևում nftables կանոններ է գրում, իսկ nft list ruleset-ը ցույց է տալիս ամեն ինչ մեկ տեղում։
sudo nft list ruleset | less # every rule, all tools
sudo iptables -L DOCKER-USER -n -v # Docker's filter hook
# Allow only one network to reach published containers
sudo iptables -I DOCKER-USER -i eth0 ! -s 198.51.100.0/24 -j DROP
sudo conntrack -C # tracked connections (conntrack pkg)
sysctl net.netfilter.nf_conntrack_max
11Ինչպես է host-ը լուծում անունները
dig-ը խոսում է անմիջապես DNS սերվերների հետ, բայց հավելվածներն օգտագործում են համակարգային resolver-ը, և երկուսը կարող են չհամընկնել։ C գրադարանը հետևում է /etc/nsswitch.conf-ին, որը սովորաբար ասում է՝ նախ ստուգիր /etc/hosts-ը, հետո DNS-ը։ Ubuntu-ում DNS-ը գնում է systemd-resolved՝ տեղական caching stub, որը լսում է 127.0.0.53-ի վրա և forward է անում netplan-ով կամ DHCP-ով կարգավորված upstream սերվերներին։ Ուստի հավելվածը կարող է տեսնել հին հասցե cache-ից կամ /etc/hosts-ի override-ից, մինչդեռ dig-ը ցույց է տալիս նորը։ getent hosts-ն օգտագործում է ճիշտ նույն ճանապարհը, ինչ հավելվածները, ուստի այն ճիշտ թեստն է։ resolvectl-ը ցույց է տալիս upstream սերվերները ըստ interface-ի և կարող է մաքրել cache-ը։ Լեզուներն էլ են ավելացնում սեփական caching․ JVM-ը cache է անում lookup-ները, իսկ connection pool-երը շարունակում են օգտագործել հին IP-ն, մինչև նորից միանան, ինչը կարևոր է միգրացիաների ժամանակ։
getent hosts example.com # what applications will see
resolvectl status # upstream DNS per interface (Ubuntu)
resolvectl query example.com # resolve through systemd-resolved
sudo resolvectl flush-caches
grep hosts /etc/nsswitch.conf # lookup order
cat /etc/resolv.conf # usually points to 127.0.0.53
Իրական իրավիճակներ
SSH-ի տեղափոխումն այլ port Ubuntu 24.04-ում
Խնդիր
Log-երի աղմուկը կրճատելու համար փոխում ես Port-ը sshd_config.d ֆայլում և restart անում ssh-ը, բայց սերվերը շարունակում է լսել 22-ի վրա։ Մեկ այլ փորձի ժամանակ քիչ էր մնում՝ ինքդ քեզ դրսում թողնեիր։
Ինչպես լուծել
Ubuntu 22.10-ից SSH-ը socket-activated է․ systemd-ի ssh.socket-ին է պատկանում listening port-ը, և port-ը գեներացվում է sshd_config-ից daemon-reload-ի պահին։ Նախ բացիր նոր port-ը ufw-ում և provider-ի firewall-ում՝ 22-ը բաց թողնելով։ Ավելացրու Port 2222 (առայժմ Port 22-ի կողքին) /etc/ssh/sshd_config.d/-ում, գործարկիր sudo sshd -t, հետո sudo systemctl daemon-reload և sudo systemctl restart ssh.socket։ Ստուգիր ss -tlnp-ով և մուտք գործիր նոր port-ով երկրորդ terminal-ից։ Միայն դրանից հետո հանիր 22 port-ը config-ից և firewall-ից։ Հիշիր, որ փոխված port-ը միայն նվազեցնում է աղմուկը․ իրական պաշտպանությունը key-երն են, password-ների բացակայությունը և fail2ban-ը։
Connection refused, թե՞ connection timed out
Խնդիր
8080 port-ի վրա նոր API-ն անհասանելի է գրասենյակից։ Որոշ գործընկերներ հաղորդում են «connection refused», մյուսները տեսնում են, որ request-ը կախվում է մինչև timeout։
Ինչպես լուծել
Երկու սխալները ցույց են տալիս տարբեր շերտեր։ Refused-ը նշանակում է, որ host-ն ակտիվորեն պատասխանել է reset-ով․ փաթեթը հասել է, բայց այդ հասցեում և port-ում ոչինչ չի լսում, հաճախ այն պատճառով, որ հավելվածը bind է արված 127.0.0.1-ին կամ crash է եղել․ ստուգիր ss -tlnp-ն սերվերի վրա։ Timeout-ը նշանակում է, որ փաթեթները լուռ գցվում են, ինչը firewall-ի (ufw, provider-ի firewall, գրասենյակի ցանց) կամ routing-ի խնդիր է։ Փորձարկիր հենց սերվերից curl localhost:8080-ով, հետո նույն տարածաշրջանի այլ սերվերից, հետո դրսից։ Փորձարկելիս գործարկիր tcpdump -ni eth0 port 8080՝ տեսնելու, թե արդյոք փաթեթները ընդհանրապես հասնում են։ Սովորաբար ճիշտ լուծումը 8080-ը բացելը չէ, այլ API-ն nginx-ի միջոցով 443-ով proxy անելը։
Հավելվածը մի քանի ժամ հետո դադարում է connection-ներ ընդունել
Խնդիր
Node ծառայությունն աշխատում է յուրաքանչյուր restart-ից հետո, բայց ժամերի ընթացքում դադարում է արձագանքել։ CPU-ն ցածր է, իսկ log-երը ցույց են տալիս «EMFILE: too many open files»։
Ինչպես լուծել
Հաշվիր socket-ները ըստ վիճակի՝ ss -tanp | grep node | awk '{print $1}' | sort | uniq -c։ CLOSE_WAIT-ում հազարավոր socket-ներ նշանակում են, որ հավելվածը չի փակում connection-ները, որոնց դիմացի կողմն արդեն փակել է, սովորաբար connection-ներ արտահոսող HTTP client կամ database pool, կամ response-ներ, որոնք երբեք չեն կարդացվում։ Յուրաքանչյուր socket file descriptor է, ուստի պրոցեսը հասնում է իր սահմանին, որը երևում է /proc/PID/limits-ում։ Որպես բուֆեր բարձրացրու LimitNOFILE-ը unit-ում, բայց շտկիր կոդը․ վերաօգտագործիր մեկ HTTP agent keep-alive-ով և խելամիտ timeout-ներով, pool-ի connection-ները միշտ ազատիր finally բլոկներում և սահմանիր սերվերի timeout-ներ։ Ավելացրու մետրիկա բաց file descriptor-ների համար, որպեսզի հաջորդ արտահոսքը շուտ բռնվի։
Արագ ուղեցույց
- Socket-ների ամփոփում ըստ վիճակի
ss -s- Որ պրոցեսին է պատկանում 443 port-ը
sudo ss -ltnp 'sport = :443'- Դիտել փաթեթները port-ի վրա
sudo tcpdump -ni any port 443 -c 20- Փորձարկել TCP port առանց curl-ի
nc -zv 203.0.113.10 5432- Ստուգել սերվերի ներկայացրած certificate-ը
openssl s_client -connect example.com:443 -servername example.com </dev/null- Ինչի է լուծում անունը հավելվածների համար
getent hosts example.com- Ցույց տալ firewall-ի ամբողջական ruleset-ը
sudo nft list ruleset- Մեկ SSH connection-ը վերաօգտագործել շատ session-ների համար
ControlMaster auto / ControlPath ~/.ssh/cm-%r@%h:%p / ControlPersist 10m- Կիրառել SSH port-ի փոփոխությունը Ubuntu 24.04-ում
sudo sshd -t && sudo systemctl daemon-reload && sudo systemctl restart ssh.socket- fail2ban-ի SSH jail-ի ban վիճակը
sudo fail2ban-client status sshd
Իմ իրական նախագծերում
- Սերվերներս կառավարում եմ SSH-ով key-based login-ով, և իմ baseline-ը միայն SSH key-երն են՝ անջատված password login-ով։
- Իմ firewall ստանդարտը ufw գումարած provider-ի firewall-ն է, որտեղ բաց են միայն 22, 80 և 443, իսկ տվյալների բազաները bind են արված localhost-ին կամ private ցանցին։
- Իմ աջակցած սերվերներում կան fail2ban և ufw, իսկ որոշ կայքերի առջևում DNS-ի համար կանգնած է Cloudflare-ը։
Հարցեր և պատասխաններ
Q1Ինչպե՞ս է աշխատում SSH key authentication-ը, և ինչո՞ւ է այն ավելի լավ, քան գաղտնաբառերը։
Դու գեներացնում ես key pair։ Public key-ը գնում է սերվերի authorized_keys, private key-ը մնում է քո մեքենայում։ Միանալիս սերվերն ուղարկում է challenge, որը կարող է ստորագրել միայն private key-ի տերը, և ստորագրությունը ստուգում է public key-ով։ Private key-ը երբեք ցանցով չի անցնում։ Գաղտնաբառերը կարելի է գուշակել, կրկին օգտագործել կամ phishing-ով գողանալ, և ամեն public SSH port անընդհատ brute-force փորձեր է ստանում։ Key-ն ունի շատ ավելի մեծ entropy, քան ցանկացած գաղտնաբառ, և passphrase-ով պաշտպանված լինելու դեպքում այն անօգուտ է նաև ֆայլը գողացողի համար։ Կարող ես նաև revoke անել մեկ սարքի key-ը՝ առանց մյուսների վրա ազդելու։
Q2Կայքը չի բեռնվում։ Ինչպե՞ս կանես troubleshooting սերվերի կողմից։
Գնում եմ շերտ առ շերտ։ Նախ DNS՝ dig +short example.com-ը պետք է վերադարձնի սերվերի IP-ն։ Հետո հասանելիություն՝ curl -I դրսից, և ստուգում եմ, որ provider-ի և ufw-ի firewall-ները թույլատրում են 80 և 443։ Սերվերի վրա ss -tlnp-ն ցույց է տալիս՝ արդյոք nginx-ը լսում է այդ port-երով, իսկ systemctl status nginx-ը և nginx -t-ն՝ արդյոք այն աշխատում է վավեր config-ով։ Հետո շրջանցում եմ nginx-ը և curl եմ անում հավելվածը ուղիղ իր լոկալ port-ով։ Եթե դա ձախողվում է, հավելվածի journal-ը կամ log-ը ասում է՝ ինչու։ Եթե աշխատում է, խնդիրը proxy config-ն է կամ TLS-ը, ինչը curl -v-ն պարզ ցույց է տալիս։
Q3Ի՞նչ է ցույց տալիս ss -tlnp-ն, և ինչո՞ւ է կարևոր՝ արդյոք ծառայությունը լսում է 0.0.0.0-ի վրա։
ss-ը ցուցակում է socket-ները՝ -t TCP-ի համար, -l լսողների համար, -n թվային port-երի համար՝ անունների փոխարեն, և -p տեր պրոցեսի համար։ Այն պատասխանում է «ինչն է բաց այս մեքենայի վրա և ով է այն բացել» հարցին։ Հասցեն կարևոր է․ 127.0.0.1-ը նշանակում է, որ կարող են միանալ միայն լոկալ պրոցեսները, 0.0.0.0-ն (կամ [::]-ը)՝ բոլոր ինտերֆեյսները, ներառյալ public-ը։ Ինտերնետին նայող host-ի վրա 0.0.0.0-ում լսող տվյալների բազան կամ Redis-ը մեկ firewall սխալի հեռավորության վրա է բացահայտվելուց, իսկ բաց տվյալների բազաները scanner-ները գտնում և հարձակվում են ժամերի ընթացքում։ Այդ պատճառով ներքին ծառայությունները bind եմ անում localhost-ին կամ private ցանցային ինտերֆեյսին։
Q4Ինչպե՞ս կմիանաս production տվյալների բազային, որը բաց չէ ինտերնետին։
SSH tunnel-ով։ ssh -N -L 5433:127.0.0.1:5432 your_user@server-ը laptop-իս վրա բացում է 5433 port-ը, և դրան միացող ամեն ինչ անցնում է գաղտնագրված SSH session-ի ներսով և սերվերի վրա դուրս է գալիս որպես լոկալ միացում PostgreSQL-ին։ Իմ database client-ը ուղղված է localhost:5433-ին։ Տվյալների բազան շարունակում է լսել միայն localhost-ում, ուստի firewall-ում ոչինչ չի բացվում։ Եթե տվյալների բազան աշխատում է առանձին private host-ի վրա, tunnel եմ անում app սերվերի կամ bastion-ի միջով ProxyJump-ով։ Թիմի համար WireGuard-ի նման VPN-ը կամ կառավարվող access գործիքը կարող է փոխարինել ad-hoc tunnel-ներին։
Q5Արդյո՞ք ufw-ն պաշտպանում է Docker-ի publish արած port-երը։
Լռելյայն՝ ոչ, ինչը շատերին զարմացնում է։ Երբ port ես publish անում -p 8080:80-ով, Docker-ը ավելացնում է իր iptables կանոնները, որոնք traffic-ը փոխանցում են կոնտեյներին մինչև ufw-ի կանոնների ստուգումը, ուստի port-ը հասանելի է ինտերնետից, նույնիսկ եթե ufw-ն արգելում է այն։ Ամենապարզ լուծումը publish անելն է միայն localhost-ի վրա, օրինակ՝ -p 127.0.0.1:8080:80, և առջևում nginx դնելը։ Docker-ը աջակցում է նաև զտմանը DOCKER-USER chain-ում, իսկ սերվերի առջևում cloud provider-ի firewall-ը միևնույն է արգելափակում է traffic-ը։ Ես միշտ ստուգում եմ դրսից port scan-ով կամ curl-ով։
Q6Ո՞րն է տարբերությունը scp-ի և rsync-ի միջև։
Երկուսն էլ ֆայլեր են պատճենում SSH-ով։ scp-ն պարզ պատճենում է, ինչպես cp-ն մեքենաների միջև․ այն միշտ փոխանցում է ամբողջ ֆայլը և քիչ տարբերակներ ունի։ rsync-ը համեմատում է աղբյուրն ու նպատակակետը և ուղարկում է միայն փոխվածը, ուստի մեծ դիրեկտորիաների կրկնվող sync-երն արագ են։ Այն կարող է շարունակել ընդհատված փոխանցումները, -a-ով պահպանել թույլտվությունները, տերերին և timestamp-ները, -z-ով սեղմել, բացառել pattern-ներ և --delete-ով դիրեկտորիան ճշգրիտ mirror անել։ Դա այն դարձնում է ճիշտ գործիք static build-երի deploy-ի և backup-ների համար։ Երբ --delete կա, միշտ նախ գործարկում եմ --dry-run-ով և ուշադրություն եմ դարձնում աղբյուրի ճանապարհի վերջի slash-ին։
Սենիոր մակարդակի հարցեր
S1Ինչպե՞ս կտաս ինժեներների թիմին SSH հասանելիություն տասնյակ սերվերների։
Անձնական public key-երը յուրաքանչյուր սերվերի վրա պատճենելը աշխատում է երկու մարդու և հինգ սերվերի համար․ դրանից այն կողմ onboarding-ը և հատկապես offboarding-ը դառնում են սխալաշատ։ Ավելի լավ մոդելը կենտրոնացված ինքնությունն է կարճաժամկետ credential-ներով։ Ներքին CA-ի կողմից ստորագրված SSH certificate-ները, որոնք տրվում են մի քանի ժամով single sign-on-ից հետո, նշանակում են, որ սերվերները վստահում են մեկ CA key-ի, և երբ ինչ-որ մեկը հեռանում է, ոչինչ մաքրելու կարիք չկա։ Teleport-ի նման գործիքները, cloud session manager-ները կամ HashiCorp Vault-ի SSH engine-ը ավտոմատացնում են տրամադրումը և ավելացնում session-ների ձայնագրում։ Սերվերները դիր private network-ում, որը հասանելի է bastion-ի կամ WireGuard-ի կամ Tailscale-ի նման VPN mesh-ի միջոցով, որպեսզի SSH-ն ընդհանրապես հրապարակային չլինի։ Գինը CA-ի կամ գործիքի սպասարկումն է և break-glass ճանապարհ ունենալը, եթե այն անջատված է։
S2Ինչո՞ւ է SSH agent forwarding-ը ռիսկային, և ի՞նչ ես օգտագործում դրա փոխարեն։
Agent forwarding-ով (-A) հեռավոր սերվերը ստանում է socket դեպի քո տեղական ssh-agent։ Քո private key-ը երբեք չի լքում laptop-ը, բայց ցանկացած մեկը, ով root ունի այդ սերվերում կամ հասանելիություն ունի քո օգտատիրոջն այնտեղ, կարող է socket-ն օգտագործել՝ քո անունից authentication անցնելու ցանկացած այլ host-ում, քանի դեռ դու միացած ես։ Ընդհանուր կամ կոտրված jump host-ի վրա սա lateral movement-ի իրական ռիսկ է։ Ներքին սերվերներին հասնելու համար ProxyJump-ը (-J) ավելի անվտանգ փոխարինողն է․ թիրախին connection-ը tunnel է արվում bastion-ի միջով, բայց authentication-ը կատարվում է end to end քո laptop-ից, ուստի bastion-ը երբեք չի դիպչում քո key-երին։ Սերվերի վրա Git-ից pull անելու համար օգտագործիր read-only deploy key՝ հատուկ այդ repository-ի համար, ոչ թե forward արա անձնական agent-դ։
S3Host firewall, cloud firewall, թե՞ երկուսն էլ։ Ինչպե՞ս ես մտածում VPS-ի ցանցային պաշտպանության մասին։
Երկուսն էլ, քանի որ դրանք տարբեր կերպ են ձախողվում։ Provider-ի cloud firewall-ը ֆիլտրում է թրաֆիկը, մինչև այն հասնի մեքենային, դիմանում է սխալ կարգավորված կամ մաքրված host ruleset-ին և չի շրջանցվում Docker-ի iptables կանոններով։ Host firewall-ն աշխատում է անկախ provider-ից, կարող է արտահայտել ավելի նուրբ կանոններ, օրինակ՝ ըստ interface-ի կամ աղբյուրի սահմանափակումներ, և շարունակում է պաշտպանել host-ը, եթե cloud ruleset-ը սխալմամբ խմբագրվի։ Ոչ մեկը չի փոխարինում ներքին ծառայությունները localhost-ին կամ private interface-ին bind անելուն, ինչը ամբողջությամբ վերացնում է բացահայտությունը՝ ֆիլտրելու փոխարեն։ Դրանց վրա ավելանում են rate limit-երը և fail2ban-ը login endpoint-ների համար, և CDN կամ proxy վեբ թրաֆիկի առջև DDoS-ը կլանելու համար, իսկ origin-ը վեբ թրաֆիկ է ընդունում միայն proxy-ի range-երից։ Գինը երկու ruleset-ը համաձայնեցված պահելն է, իդեալական դեպքում՝ կոդով։
Բառարան
- Three-way handshake
- SYN, SYN-ACK, ACK․ փոխանակում, որը բացում է TCP connection-ը՝ մինչև որևէ տվյալ ուղարկելը։
- Accept backlog
- Ավարտված connection-ների kernel-ային հերթ, որոնք սպասում են, որ հավելվածը ընդունի դրանք։ Գերլցումը client-ների մոտ timeout է առաջացնում։
- TIME_WAIT
- Վիճակ, որը կարճ ժամանակով պահում է առաջինը փակած կողմը, որպեսզի ուշացած փաթեթները չշփոթեցնեն նոր connection-ը։ Մեծ քանակով նորմալ է։
- CLOSE_WAIT
- Դիմացի կողմը փակել է, բայց տեղական հավելվածը՝ ոչ։ Դրանց մեծ քանակը ցույց է տալիս connection-ների արտահոսք հավելվածում։
- Host key
- Սերվերի սեփական SSH key pair-ը։ Client-ները գրանցում են դրա fingerprint-ը՝ հետագա connection-ներում կեղծումը հայտնաբերելու համար։
- SSH certificate
- Վստահելի CA-ի կողմից ստորագրված օգտատիրոջ կամ host-ի key՝ անուններով և ժամկետով, որը փոխարինում է ամեն սերվերի key-երի ցուցակներին։
- Netfilter
- Linux kernel-ի framework, որը ֆիլտրում, թարգմանում և հետևում է փաթեթներին։ iptables-ը, nftables-ը, ufw-ն և firewalld-ը կարգավորում են այն։
- DNAT
- Destination NAT․ փաթեթի նպատակային հասցեի կամ port-ի վերագրում, որով Docker-ը հրապարակված port-երը forward է անում container-ներին։
- conntrack
- Netfilter-ի ակտիվ հոսքերի աղյուսակը, որը թույլ է տալիս կանոններին բաց թողնել պատասխան թրաֆիկը և NAT-ին աշխատել։ Այն ունի չափի սահման։
- Stub resolver
- Տեղական DNS forwarder, ինչպես systemd-resolved-ը 127.0.0.53-ի վրա, որը cache է անում պատասխանները և query-ները forward է անում upstream։
Շարունակեք սովորել
Պրոդուկտի գաղափար ունե՞ք։Եկեք ստեղծենք այն։
Պատմեք ձեր գաղափարի, պրոդուկտի կամ թիմի մասին։ Սովորաբար պատասխանում եմ մեկ աշխատանքային օրվա ընթացքում։