Բոլոր դասերը
Linux և սերվերներԱռաջադեմ14 րոպե ընթերցումԻմ ստեկում

Սերվերի hardening՝ ամբողջական checklist

Քայլ առ քայլ, հրամաններով՝ նոր VPS-ից մինչև սերվեր, որին կարող ես վստահել production-ում։

  • SSH keys only
  • Firewall
  • Updates
  • Least privilege
  • Backups
Քայլ 1/6
Սերվեր՝ բաց SSH-ով։ Հաքերը գալիս է գաղտնաբառերի ցուցակով։ fail2ban-ը հերթապահում է (մի քիչ քնկոտ)։

30 վայրկյանում

Ինտերնետում նոր սերվերը scan է արվում րոպեների ընթացքում։ Hardening-ը նշանակում է կրճատել այն, ինչին հարձակվողը կարող է հասնել, և սահմանափակել այն, ինչ նա կարող է անել, եթե ներս մտնի․ միայն key-երով SSH, լռելյայն մերժող firewall, անվտանգության ավտոմատ թարմացումներ, առանձին օգտատերեր յուրաքանչյուր հավելվածի համար, private տվյալների բազաներ, HTTPS ամենուր, WAF առջևում և backup-ներ, որոնք իսկապես restore ես արել։ Ոչ մի առանձին քայլ բավարար չէ, բայց միասին դրանք փոքր սերվերը իսկապես դժվար կոտրելի են դարձնում։

Ինչու եմ օգտագործում

  • Յուրաքանչյուր սերվերի, որը կառավարում եմ, կիրառում եմ գրավոր անվտանգության baseline, որովհետև հետևողականությունը գերազանցում է հերոսությանը․ նույն ութ ստուգումները ամենուր։
  • Իրական կոտրումների մեծ մասը, որոնց մասին լսում եմ, խելացի exploit-ներ չեն, այլ բաց տվյալների բազայի port, password login կամ մոռացված թարմացում։
  • Իմ սեփական գործիքներին նույն ստանդարտն եմ պահանջում․ երբ սերվերների վրա աշխատում եմ AI agent-ի հետ, այն լռելյայն read-only է, և յուրաքանչյուր փոփոխություն իմ հաստատման կարիքն ունի։
  • Hardening արված, փաստաթղթավորված սերվերը ավելի հեշտ է հանձնել, audit անել և վերականգնել, և հենց դրա համար են հաճախորդները վճարում։

Ինչպես է աշխատում

01Մտածելակերպը՝ շերտեր և least privilege

Hardening-ը հետևում է երկու սկզբունքի։ Defence in depth՝ ենթադրիր, որ ցանկացած առանձին շերտ կարող է ձախողվել, ուստի դասավորիր մի քանիսը (edge WAF, firewall, SSH policy, հավելվածի թույլտվություններ, backup-ներ)։ Least privilege՝ յուրաքանչյուր օգտատեր, պրոցես և key ստանում է միայն իրեն անհրաժեշտ հասանելիությունը, ուստի կոտրումը մնում է փոքր։ Աշխատիր անվտանգ հերթականությամբ, որովհետև hardening-ի դասական դժբախտ պատահարը ինքդ քեզ դուրս թողնելն է։ Նախ ստեղծիր քո օգտատերն ու key-ը, երկրորդ տերմինալում հաստատիր, որ կարող ես մուտք գործել դրանով, և միայն դրանից հետո անջատիր root-ը և գաղտնաբառերը։ SSH-ը թույլատրիր firewall-ում մինչև այն միացնելը։ Provider-ի վեբ console-ը պահիր որպես վթարային դուռ։ Յուրաքանչյուր քայլ գրի առ կարճ runbook-ում կամ script-ում, որպեսզի հաջորդ սերվերը ստանա ճիշտ նույն վերաբերմունքը, և կարողանաս հաճախորդին ապացուցել, թե ինչ է արվել։

  • Կրճատիր attack surface-ը՝ ավելի քիչ բաց port-եր, ծառայություններ և փաթեթներ։
  • Սահմանափակիր blast radius-ը՝ առանձին օգտատերեր, private ցանցեր, սահմանափակ credential-ներ։
  • Ենթադրիր կոտրում՝ off-site backup-ներ, log-եր և alert-ներ, որոնք իսկապես կկարդաս։

021. Օգտատերեր և SSH՝ միայն key-եր, առանց root-ի

Ստեղծիր անձնական admin օգտատեր sudo-ով և տեղադրիր քո public key-ը (Ed25519 key-ը ժամանակակից լռելյայնն է)։ Հետո անջատիր root login-ը և password authentication-ը /etc/ssh/sshd_config.d/-ում drop-in ֆայլով, որն ավելի լավ է դիմանում փաթեթների թարմացումներին, քան հիմնական ֆայլի խմբագրումը։ Ubuntu-ում ծառայությունը կոչվում է ssh։ Reload-ից առաջ վավերացրու config-ը sshd -t-ով և ընթացիկ session-ը բաց պահիր, մինչ մեկ այլ տերմինալում նոր login ես ստուգում։ SSH port-ը փոխելը միայն նվազեցնում է log-ի աղմուկը․ դա անվտանգության վերահսկում չէ, ուստի արա դա միայն այն դեպքում, եթե ուզում ես ավելի հանգիստ log-եր։ Թիմերի համար յուրաքանչյուր մարդու տուր իր օգտատերն ու key-ը, որպեսզի հասանելիությունը կարելի լինի revoke անել առանձին, և հեռացրու հեռացողների key-երը։

bash
# On the server, as root (one time)
adduser your_user
usermod -aG sudo your_user
# From your laptop
ssh-copy-id -i ~/.ssh/id_ed25519.pub [email protected]
 
# /etc/ssh/sshd_config.d/99-hardening.conf
PermitRootLogin no
PasswordAuthentication no
KbdInteractiveAuthentication no
PubkeyAuthentication yes
MaxAuthTries 3
AllowUsers your_user
 
# Validate, then reload (keep the current session open!)
sudo sshd -t && sudo systemctl reload ssh

032. Firewall, թարմացումներ և fail2ban

Լռելյայն մերժիր ամբողջ մուտքային traffic-ը և բացիր միայն այն, ինչ սերվերը սպասարկում է՝ SSH, HTTP և HTTPS։ Նույն կանոնները կրկնիր cloud provider-ի firewall-ում, որպեսզի traffic-ը զտվի, մինչև նույնիսկ կհասնի մեքենային։ Մեկ կարևոր թակարդ՝ Docker-ը կոնտեյների port-երը publish է անում՝ գրելով իր iptables կանոնները, որոնք շրջանցում են ufw-ն։ Compose ֆայլերում publish արված port-երը bind արա 127.0.0.1-ին, եթե դրանք իսկապես public լինելու կարիք չունեն։ Հետո միացրու unattended-upgrades-ը անվտանգության patch-երի համար․ kernel-ի թարմացումների մեծ մասին դեռ reboot է պետք, ուստի պլանավորիր maintenance window կամ թույլատրիր ավտոմատ reboot-ներ հանգիստ ժամին։ Տեղադրիր fail2ban՝ brute-force աղբյուրները ban անելու համար։ Վերջապես հեռացրու այն ծառայությունները, որոնք չես օգտագործում․ յուրաքանչյուր լսող daemon ևս մեկ բան է, որը պետք է patch անել։

bash
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow OpenSSH
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
sudo ufw status verbose
 
sudo apt update && sudo apt install -y unattended-upgrades fail2ban
sudo dpkg-reconfigure -plow unattended-upgrades
# Optional: /etc/apt/apt.conf.d/50unattended-upgrades
#   Unattended-Upgrade::Automatic-Reboot "true";
#   Unattended-Upgrade::Automatic-Reboot-Time "04:00";
 
sudo ss -tulpn   # everything listening; nothing unexpected on 0.0.0.0

043. Հավելվածներ՝ առանձին օգտատերեր, թույլտվություններ, private տվյալների բազաներ, secret-ներ

Յուրաքանչյուր կայք կամ հավելված գործարկիր իր system user-ով՝ իր PHP-FPM pool-ով կամ process manager-ի գրառմամբ։ Եթե մեկ հավելված կոտրվում է, այն չի կարող կարդալ մեկ այլ հավելվածի կոդը, upload-ները կամ environment ֆայլը։ Կոդը պետք է պատկանի deploy օգտատիրոջը և գրելի չլինի վեբ պրոցեսի համար․ գրելի են միայն այն թղթապանակները, որոնք պետք է փոխվեն runtime-ում (Laravel-ի storage-ը և bootstrap/cache-ը, upload թղթապանակները)։ Environment ֆայլերը կարդացվում են միայն հավելվածի օգտատիրոջ կողմից։ Տվյալների բազաները և Redis-ը լսում են localhost-ում կամ private ցանցում, և յուրաքանչյուր հավելված ունի իր least-privilege տվյալների բազայի օգտատերը։ Secret-ները գտնվում են սերվերի environment ֆայլերում կամ secrets manager-ում, երբեք՝ Git-ում, և rotate են արվում, երբ ինչ-որ մեկը հեռանում է կամ արտահոսքի կասկած կա։ Repository-ի վրա գործարկիր secret scanner, որովհետև հին commit-երում արտահոսած key-երը կոտրումների տարածված աղբյուր են։

bash
# One system user per app, no login shell
sudo adduser --system --group --home /var/www/shop --shell /usr/sbin/nologin shop
 
# Code: owned by the deploy user, readable by the app group
sudo chown -R deploy:shop /var/www/shop
sudo find /var/www/shop -type d -exec chmod 750 {} +
sudo find /var/www/shop -type f -exec chmod 640 {} +
 
# Only runtime folders are writable by the app
sudo chmod -R 770 /var/www/shop/storage /var/www/shop/bootstrap/cache
 
# Secrets: readable by the app only
sudo chown deploy:shop /var/www/shop/.env && sudo chmod 640 /var/www/shop/.env

054. HTTPS ամենուր և WAF առջևում

Յուրաքանչյուր կայք ստանում է certificate, սովորաբար Let's Encrypt-ից certbot-ի միջոցով, ավտոմատ թարմացմամբ (certbot-ը տեղադրում է systemd timer․ ստուգիր այն dry run-ով)։ HTTP-ն redirect արա HTTPS-ի, օգտագործիր միայն TLS 1.2 և 1.3, և ավելացրու HSTS, երբ համոզված ես, որ բոլոր subdomain-ները աշխատում են HTTPS-ով։ Cloudflare-ը կամ նմանատիպ CDN/WAF առջևում դնելը թաքցնում է origin IP-ն, կլանում է հիմնական DDoS traffic-ը և թույլ է տալիս արգելափակել վատ bot-երին և կիրառել rate limit-եր edge-ում։ Օգտագործիր Full (strict) SSL ռեժիմը, որպեսզի Cloudflare-ից քո սերվեր միացումը գաղտնագրված լինի և certificate-ը ստուգվի․ Flexible-ը traffic-ը origin ուղարկում է չգաղտնագրված։ Լրացուցիչ պաշտպանության համար 80-ը և 443-ը թույլատրիր միայն Cloudflare-ի IP միջակայքերից կամ օգտագործիր tunnel, որպեսզի հարձակվողները չկարողանան շրջանցել edge-ը՝ ուղիղ հարվածելով origin IP-ին։

bash
sudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d example.com -d www.example.com
sudo certbot renew --dry-run
systemctl list-timers | grep certbot
 
# nginx: modern TLS and HSTS (add HSTS only when all subdomains use HTTPS)
ssl_protocols TLSv1.2 TLSv1.3;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
server_tokens off;

065. Backup-ներ, monitoring և audit-ներ

Hardening-ը նշանակում է նաև գոյատևել այն, ինչ չկարողացար կանխել։ Արա provider-ի snapshot-ներ և, առանձին, տվյալների բազաների և upload-ների հավելվածի մակարդակի backup-ներ՝ պատճենված այլ հաշիվ կամ provider։ Restore-ները ստուգիր ժամանակացույցով․ չստուգված backup-ը հույս է, ոչ թե պլան։ Հետևիր այն բաներին, որոնք սովորաբար սպանում են փոքր սերվերները՝ լցված սկավառակներ, հիշողության սպառում, ժամկետանց certificate-ներ և անջատված կայք։ Արտաքին uptime checker-ը (Uptime Kuma կամ provider-ի alert-ները) գումարած սկավառակի և load-ի alert-ները ծածկում են դրա մեծ մասը։ Log-երը պահիր բավական երկար՝ incident-ը հետաքննելու համար։ Հետո audit արա․ Lynis-ը scan է անում համակարգը և ցուցակում է hardening-ի կոնկրետ առաջարկներ գնահատականով, ինչը լավ ձև է բռնելու այն, ինչ checklist-ը բաց է թողել։ Փոփոխություններից հետո նորից գործարկիր այն և պահիր հաշվետվությունը։

bash
# Audit the system and read the suggestions
sudo apt install -y lynis
sudo lynis audit system
sudo less /var/log/lynis-report.dat
 
# Quick health checks
df -h                          # disk usage
free -h                        # memory
sudo last -n 20                # recent logins
sudo journalctl -p err -b      # errors since boot
sudo fail2ban-client status sshd

Օգտագործեք, երբ

  • Յուրաքանչյուր նոր սերվերի վրա՝ մինչև առաջին հավելվածի deploy-ը։
  • Երբ սերվեր ես ժառանգում՝ նախ audit, հետո checklist-ը կիրառիր քայլ առ քայլ։
  • Ցանկացած incident-ից կամ «քիչ էր մնում»-ից հետո՝ որպես վերանայում, թե որ շերտն է ձախողվել։
  • Պարբերաբար (օրինակ՝ եռամսյակը մեկ), որովհետև սերվերները շեղվում են, երբ մարդիկ արագ շտկումներ են անում։

Մի՛ օգտագործեք, երբ

  • Production սերվերի վրա ագրեսիվ kernel-ի կամ SSH-ի փոփոխություններ մի կիրառիր առանց երկրորդ session-ի և console fallback-ի։
  • Մի հետապնդիր կատարյալ audit գնահատական աշխատող, սպասարկելի համակարգի հաշվին․ առաջնահերթություն տուր իրական ռիսկերին։

Այլընտրանքներ

  • Managed պլատֆորմներ (PaaS)

    Provider-ը hardening և patch է անում ՕՀ-ն, իսկ դու պաշտպանում ես հավելվածը և դրա կոնֆիգուրացիան։

  • Ansible կամ cloud-init

    Ավտոմատացրու նույն checklist-ը, որպեսզի յուրաքանչյուր սերվեր hardening արվի նույնական և կրկնելի ձևով։

  • CIS Benchmarks

    Մանրամասն, ֆորմալ hardening ստանդարտներ․ օգտակար են compliance-ի համար, ավելի ծանր, քան պետք է փոքր թիմերի մեծ մասին։

  • Cloudflare Tunnel կամ VPN

    Ամբողջությամբ հեռացնում է origin-ի public port-երը․ հասանելիությունը անցնում է authenticated tunnel-ով։

Լավագույն պրակտիկաներ

  1. Միայն key-երով SSH, առանց root login-ի, անձնական հաշիվներ յուրաքանչյուր admin-ի համար։
  2. Default-deny firewall host-ի վրա և provider-ի մոտ․ բացիր միայն 22, 80 և 443։
  3. Անվտանգության ավտոմատ թարմացումներ և կանոնավոր reboot window։
  4. Մեկ system user յուրաքանչյուր հավելվածի համար, least-privilege ֆայլային թույլտվություններ և տվյալների բազայի օգտատերեր։
  5. Տվյալների բազաները և Redis-ը միայն localhost-ում կամ private ցանցում։
  6. HTTPS ամենուր ավտոմատ թարմացմամբ, Cloudflare Full (strict) և edge WAF կանոններ։
  7. Off-site backup-ներ, պլանավորված restore ստուգումներ, uptime-ի և սկավառակի alert-ներ և պարբերական Lynis audit։

Տարածված սխալներ

  • Password login-ն անջատել մինչև key login-ի աշխատանքը հաստատելը, հետո provider-ի console-ի կարիք ունենալ ներս վերադառնալու համար։
  • Վստահել ufw-ին, մինչ Docker-ը տվյալների բազայի port-երը publish է անում ամբողջ ինտերնետի համար։
  • Բոլոր կայքերը գործարկել www-data-ով, ուստի մեկ խոցելի plugin-ը բացահայտում է սերվերի բոլոր կայքերը։
  • Օգտագործել Cloudflare Flexible SSL՝ origin-ի միացումը թողնելով չգաղտնագրված։
  • Backup-ները պահել միայն նույն սերվերի վրա, որտեղ ransomware-ը կամ ջնջված droplet-ը դրանք նույնպես կտանի։

Խորը վերլուծություն

07SSH-ը ժամանակակից Ubuntu-ում ներսից. socket activation և կրիպտոգրաֆիա

Ubuntu 24.04-ից SSH-ը օգտագործում է systemd socket activation. ssh.socket-ը լսում է port-ը և առաջին կապի ժամանակ գործարկում ssh.service-ը։ systemd generator-ը sshd_config-ից կարդում է Port-ը և ListenAddress-ը՝ socket-ը կառուցելու համար, ուստի port-ը փոխելը նշանակում է daemon-reload գումարած ssh.socket-ի restart, ոչ թե պարզապես ծառայության reload։ Շատերն իրենց դուրս են թողել՝ փոխելով Port-ը, reload անելով ssh-ը և փակելով session-ը, մինչդեռ socket-ը դեռ լսում էր 22-ը, իսկ firewall-ը թույլատրում էր միայն նոր port-ը։ Կրիպտոգրաֆիայի առումով OpenSSH-ի ընթացիկ defaults-ը լավն են. վերջին release-ները ավտոմատ համաձայնեցնում են hybrid post-quantum key exchange, և Ed25519 key-երը ստանդարտ են։ Մի պատճենեք blog post-երից հին cipher ցուցակներ. դրանք հաճախ թուլացնում են ժամանակակից defaults-ը։ sshd -T-ն տպում է գործող կոնֆիգուրացիան բոլոր drop-in-երից հետո, իսկ ssh-audit գործիքը հաղորդում է թույլ ալգորիթմների մասին։ Ամենաբարձր վստահության համար օգտագործեք hardware-backed key-եր (ed25519-sk) security key-ի վրա, որպեսզի գողացված laptop-ի ֆայլն անօգուտ լինի։

bash
# /etc/ssh/sshd_config.d/99-hardening.conf
Port 2222
 
# Open the new port first, then rebuild the socket
sudo ufw allow 2222/tcp
sudo sshd -t
sudo systemctl daemon-reload
sudo systemctl restart ssh.socket
sudo ss -tlnp | grep -E ':22 |:2222 '
 
# Effective settings after all drop-ins
sudo sshd -T | grep -E 'permitrootlogin|passwordauthentication|port'

08Ծառայությունների sandboxing systemd-ով

Առանձին Unix user-ը սահմանափակում է, թե որ ֆայլերը կարող է կարդալ compromised հավելվածը, բայց process-ը դեռ տեսնում է ֆայլային համակարգի մեծ մասը, կարող է գրել /tmp-ում, բեռնել կամայական binary-ներ և բացել ցանկացած ցանցային socket։ systemd-ը կարող է յուրաքանչյուր ծառայություն շատ ավելի սահմանափակել drop-in-ի մի քանի տողով. օպերացիոն համակարգի read-only տեսք, home directory-ների անհասանելիություն, private /tmp, setuid binary-ների միջոցով privilege escalation-ի բացառում և միայն անհրաժեշտ address family-ներ։ systemd-analyze security-ն յուրաքանչյուր unit-ի տալիս է գնահատական 0-ից (լավ պաշտպանված) մինչև 10 (բաց) և թվարկում բացակայող option-ները, ինչը սա դարձնում է չափելի։ Option-ները կիրառեք աստիճանաբար և թեստավորեք, քանի որ չափազանց խիստ sandbox-ը runtime-ում դրսևորվում է permission սխալներով։ MemoryMax-ը և CPUQuota-ն նաև թույլ չեն տալիս մեկ անկառավարելի process-ին ամբողջ սերվերը վայր գցել։ Նույն գաղափարները կան container-ների համար. աշխատեցնել non-root-ով, read-only root filesystem, հեռացված capability-ներ։

ini
# /etc/systemd/system/web.service.d/hardening.conf
[Service]
User=web
NoNewPrivileges=yes
ProtectSystem=strict
ProtectHome=yes
PrivateTmp=yes
PrivateDevices=yes
ReadWritePaths=/var/www/web/shared
ProtectKernelTunables=yes
ProtectKernelModules=yes
ProtectControlGroups=yes
RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX
CapabilityBoundingSet=
MemoryMax=1G
# Then: sudo systemctl daemon-reload && systemd-analyze security web.service

09Kernel-ի և ցանցի կարգավորումներ

sysctl կարգավորումները ճշգրտում են kernel-ի վարքագիծը։ Ubuntu-ն արդեն /etc/sysctl.d/10-*.conf-ում առաքում է ողջամիտ defaults. SYN cookie-ներ, պաշտպանված symlink-ներ և hardlink-ներ, սահմանափակված kernel pointer-ներ, իսկ 24.04-ից՝ AppArmor սահմանափակումներ unprivileged user namespace-երի վրա։ Փոքր լրացուցիչ ֆայլը ավելի է ամրացնում ցանցային stack-ը. անտեսել ICMP redirect-ները և source-routed փաթեթները, որոնք կարող են տրաֆիկը վերաուղղել, չուղարկել redirect-ներ, քանի որ սերվերը router չէ, և log անել անհնար source հասցեներով փաթեթները։ Երկու զգուշացում։ Docker-ը, Kubernetes-ը և VPN սերվերները պահանջում են net.ipv4.ip_forward = 1, ուստի այդպիսի host-երում երբեք կուրորեն մի անջատեք forwarding-ը։ Իսկ խիստ reverse-path filtering-ը (rp_filter = 1) կարող է կոտրել մի քանի interface-ով և ասիմետրիկ routing-ով սերվերները. այդ պատճառով Ubuntu-ն լռելյայն օգտագործում է loose mode (2)։ Կիրառեք sysctl --system-ով և rollout-ից առաջ ստուգեք, որ ցանցը և container-ները դեռ աշխատում են։

ini
# /etc/sysctl.d/99-hardening.conf
net.ipv4.conf.all.accept_redirects = 0
net.ipv6.conf.all.accept_redirects = 0
net.ipv4.conf.all.send_redirects = 0
net.ipv4.conf.all.accept_source_route = 0
net.ipv6.conf.all.accept_source_route = 0
net.ipv4.conf.all.log_martians = 1
net.ipv4.tcp_syncookies = 1
kernel.dmesg_restrict = 1
kernel.kptr_restrict = 2
 
# Apply and check one value
sudo sysctl --system
sysctl net.ipv4.conf.all.accept_redirects

10Detection. իմանալ, երբ շերտը ձախողվել է

Կանխարգելումը վաղ թե ուշ ձախողվում է, ուստի պլանավորեք դա նկատել։ Log-երը ուղարկեք սերվերից դուրս (journald forwarding, Loki կամ hosted ծառայություն), քանի որ root ունեցող հարձակվողն առաջինը խմբագրում է լոկալ log-երը։ auditd-ը գրանցում է անվտանգության համար կարևոր իրադարձություններ, ինչպես /etc/passwd-ի, sudoers-ի կամ SSH key-երի փոփոխությունները։ AIDE-ի նման file integrity checker-ը կառուցում է համակարգային ֆայլերի hash-երի database և հաղորդում փոփոխությունների մասին. աշխատեցրեք այն timer-ով և baseline database-ը պահեք host-ից դուրս։ Patching-ը նույնպես ունի detection կողմ։ unattended-upgrades-ը տեղադրում է ուղղումները, բայց աշխատող process-ները հին գրադարանները պահում են հիշողության մեջ մինչև restart. needrestart-ը թվարկում է դրանք, իսկ /var/run/reboot-required-ը ասում է, որ նոր kernel-ը սպասում է reboot-ի։ Ubuntu Pro-ն, որն անվճար է փոքր քանակությամբ անձնական մեքենաների համար, ավելացնում է Livepatch՝ kernel-ի ուղղումների համար առանց reboot-ի, և Ubuntu Security Guide՝ ավտոմատացված CIS կամ DISA-STIG պրոֆիլների համար։ Alert դրեք նոր sudo user-ների, authorized_keys-ի նոր գրառումների և անսպասելի լսող port-երի վրա։

bash
# Watch changes to identity and SSH files
sudo apt install -y auditd
sudo auditctl -w /etc/passwd -p wa -k identity
sudo auditctl -w /etc/sudoers.d -p wa -k sudoers
sudo auditctl -w /home/your_user/.ssh/authorized_keys -p wa -k sshkeys
sudo ausearch -k sshkeys --start today
 
# Services still running old libraries, and pending reboot
sudo needrestart -r l
[ -f /var/run/reboot-required ] && cat /var/run/reboot-required.pkgs

Իրական իրավիճակներ

01

Ժառանգում եք սերվեր՝ գաղտնաբառով SSH-ով և բաց database-ով

Խնդիր

Հաճախորդը հանձնում է տարիներ աշխատած սերվեր։ Root-ը login է անում գաղտնաբառով, MySQL-ը լսում է 0.0.0.0-ում, ոչ ոք չգիտի, թե որ cron job-երն են կարևոր, և կայքը պետք է մնա online։

Ինչպես լուծել

Որևէ բան փոխելուց առաջ audit արեք. վերցրեք provider-ի snapshot, աշխատեցրեք ss -tulpn, կազմեք user-ների, sudoers-ի, authorized_keys-ի, crontab-երի և միացված ծառայությունների ցանկը և baseline-ի համար աշխատեցրեք Lynis։ Հետո ամենաբարձր ռիսկը ուղղեք ամենափոքր փոփոխությամբ. MySQL-ը bind արեք 127.0.0.1-ին (նախ ստուգեք, որ ոչ մի արտաքին հավելված կախված չէ դրանից) և provider-ի firewall-ում արգելափակեք 3306-ը։ Հաջորդը՝ ստեղծեք ձեր անձնական sudo user-ը key-ով, թեստավորեք այն երկրորդ session-ում, հետո անջատեք root-ի և գաղտնաբառով login-ը։ Միացրեք ufw-ն՝ նախ SSH-ը թույլատրելով։ Rotate արեք database-ի և root-ի գաղտնաբառերը, քանի որ դրանք կարող էին արտահոսել տարիներ առաջ։ Յուրաքանչյուր քայլ փաստաթղթավորեք հաճախորդի համար։

02

CPU-ն 100 տոկոս է անհայտ process-ի պատճառով

Խնդիր

Մոնիթորինգը ցույց է տալիս մշտական լիարժեք CPU։ top-ը բացահայտում է պատահական անունով process, որն աշխատում է web user-ով և միանում անհայտ հասցեի։ Կայքը դեռ աշխատում է։

Ինչպես լուծել

Վերաբերվեք սրան որպես compromise-ի, ոչ թե performance խնդրի։ Նախ disk-ի snapshot արեք որպես ապացույց, հետո մեկուսացրեք. provider-ի firewall-ում սահմանափակեք ելքային տրաֆիկը և կայքը դրեք maintenance mode։ Հավաքեք ինչ կարող եք (ps auxf, ss -tupn, binary-ի path-ը /proc/PID/exe-ով, web user-ի crontab-երը, web root-ում վերջերս փոփոխված ֆայլերը)։ Գտեք մուտքի կետը՝ սովորաբար հնացած plugin կամ PHP ընդունող upload form։ Մի սահմանափակվեք process-ը kill անելով և շարունակելով. ձեր baseline-ից վերակառուցեք մաքուր սերվեր, կոդը deploy արեք Git-ից, տվյալները վերականգնեք breach-ից առաջ վերցված backup-ից, rotate արեք սերվերի պահած յուրաքանչյուր secret և online անցնելուց առաջ փակեք անցքը։

03

Docker-ում Redis-ը հասանելի է ինտերնետից

Խնդիր

Արտաքին port scan-ը ցույց է տալիս բաց 6379, թեև ufw-ն արգելում է ամեն ինչ, բացի 22-ից, 80-ից և 443-ից։ Compose ֆայլը publish է անում '6379:6379', որպեսզի developer-ը կարողանա միանալ տնից։

Ինչպես լուծել

Docker-ի publish արված port-երը շրջանցում են ufw-ն, ուստի scan-ը ճիշտ է։ Անմիջապես mapping-ը փոխեք 127.0.0.1:6379:6379-ի կամ ամբողջությամբ հեռացրեք ports-ը և թողեք, որ մյուս container-ները Redis-ին հասնեն Compose ցանցով, հետո docker compose up -d։ Ամեն դեպքում սահմանեք Redis գաղտնաբառ կամ ACL user։ Developer-ի համար օգտագործեք SSH tunnel. ssh -L 6379:127.0.0.1:6379 [email protected]։ Ստուգեք Redis-ը չարաշահման նշանների համար, ինչպես անհայտ key-երը կամ փոխված dir-ը և dbfilename-ը, որոնք հարձակվողներն օգտագործում են ֆայլեր գրելու համար։ Ավելացրեք provider-ի firewall-ը որպես երկրորդ շերտ և պլանավորեք արտաքին port scan, որպեսզի հաջորդ սխալը բռնի մոնիթորինգը, ոչ թե հարձակվողը։

Արագ ուղեցույց

SSH-ի գործող կարգավորումները
sudo sshd -T | grep -Ei 'permitrootlogin|passwordauthentication'
Կիրառել SSH port-ի փոփոխությունը Ubuntu 24.04+-ում
sudo systemctl daemon-reload && sudo systemctl restart ssh.socket
Firewall կանոնները համարներով (մեկը ջնջելու համար)
sudo ufw status numbered && sudo ufw delete 3
Ամեն ինչ, որ լսում է, process-ով
sudo ss -tulpn
Scan անել ձեր սերվերը դրսից
nmap -Pn -p- 203.0.113.10
Նախադիտել ավտոմատ թարմացումները
sudo unattended-upgrade --dry-run --debug
Թարմացումներից հետո սպասվող reboot
cat /var/run/reboot-required.pkgs
Գնահատել ծառայության sandbox-ը
systemd-analyze security nginx.service
Վերջին ձախողված login-ները
sudo journalctl -u ssh --since today | grep -i 'invalid user'
Կարճակյաց SSH սերտիֆիկատ user-ի համար
ssh-keygen -s user_ca -I alice -n alice -V +8h id_ed25519.pub

Իմ իրական նախագծերում

  • Իմ կառավարած բոլոր սերվերների վրա ներդնում եմ ութ կետից բաղկացած baseline՝ միայն key-երով SSH, firewall միայն 22/80/443-ով, private տվյալների բազաներ, fail2ban unattended-upgrades-ի հետ, օգտատեր յուրաքանչյուր հավելվածի համար, snapshot-ներ ստուգված restore-ներով, monitoring և HTTPS՝ առջևում Cloudflare-ով։
  • Սերվերները կառավարում եմ key-based SSH-ով և AI agent-ի հետ աշխատում եմ խիստ կանոններով՝ լռելյայն read-only, յուրաքանչյուր փոփոխություն հաստատված իմ կողմից։
  • Մեկ սերվերի վրա սկավառակի զբաղվածությունը մոտ 91%-ից իջեցրի 66%-ի՝ մաքրելով log-երը, հին build-երը և չափազանց մեծ node_modules-ները, ինչը հիշեցում է, որ սկավառակի monitoring-ը անվտանգության մաս է։

Հարցեր և պատասխաններ

Q1Որո՞նք են առաջին բաները, որ անում ես նոր Linux սերվերի վրա։

Թարմացնում եմ փաթեթները, ստեղծում եմ անձնական sudo օգտատեր և տեղադրում իմ SSH key-ը, երկրորդ session-ում հաստատում եմ key login-ը, հետո անջատում եմ root login-ը և password authentication-ը։ Հետո ufw՝ default deny incoming-ով, թույլատրելով միայն SSH, 80 և 443, կրկնված cloud firewall-ում։ Հետո unattended-upgrades և fail2ban, ժամային գոտի և hostname, և ss-ով ստուգում, որ ոչ մի անսպասելի բան չի լսում։ Հավելվածներ deploy անելուց առաջ ստեղծում եմ system user յուրաքանչյուր հավելվածի համար, տվյալների բազաները պահում եմ localhost-ում, կարգավորում եմ certbot-ը ավտոմատ թարմացմամբ, առջևում դնում եմ Cloudflare Full (strict) ռեժիմով և կարգավորում եմ backup-ները և uptime monitoring-ը։ Իդեալում այս ամենը script է կամ Ansible role, որպեսզի յուրաքանչյուր սերվեր նույնական լինի։

Q2Արդյո՞ք SSH port-ը փոխելը անվտանգության լավ միջոց է։

Այն նվազեցնում է աղմուկը, ոչ թե ռիսկը։ SSH-ը 22 port-ից տեղափոխելը կրճատում է log-երում ավտոմատ login փորձերի քանակը, որովհետև շատ bot-եր փորձում են միայն լռելյայն port-ը, բայց ցանկացած թիրախային scan նոր port-ը գտնում է վայրկյանների ընթացքում։ Իրական պաշտպանությունը գալիս է միայն key-երով authentication-ից, root login-ի բացակայությունից, firewall-ից, որն իդեալում SSH-ը սահմանափակում է հայտնի IP-ներով կամ VPN-ով, և fail2ban-ից։ Եթե թիմը փոխում է port-ը, համոզվում եմ, որ firewall կանոնը թարմացված է մինչև SSH-ի փոփոխությունը, որպեսզի ոչ ոք դուրս չմնա, և որ դա փաստաթղթավորված է։ Ես դա համարում եմ ոչ պարտադիր կոսմետիկա իրական վերահսկումների վրա։

Q3Ինչո՞ւ Docker-ը կարող է շրջանցել ufw-ն, և ինչպե՞ս ես դա կանխում։

ufw-ն կառավարում է իր iptables chain-երը, բայց երբ կոնտեյները port է publish անում, Docker-ը իր NAT և forwarding կանոնները ավելացնում է chain-երում, որոնք գնահատվում են ufw-ի input կանոններից առաջ։ Այդ պատճառով ports 5432:5432-ով Compose ֆայլը PostgreSQL-ը հասանելի է դարձնում ինտերնետից, թեև ufw-ն ասում է deny։ Ամենապարզ լուծումը publish արված port-երը localhost-ին bind անելն է, օրինակ՝ 127.0.0.1:5432:5432, կամ ներքին ծառայությունները ընդհանրապես publish չանելը և թույլ տալը, որ կոնտեյներները խոսեն Docker ցանցով։ Cloud provider-ի firewall-ը երկրորդ safety net-ն է, որովհետև այն զտում է traffic-ը, մինչև այն կհասնի host-ին։ Ես միշտ ստուգում եմ դրսից port scan-ով։

Q4Ինչպե՞ս ես կառավարում secret-ները սերվերի վրա։

Secret-ները երբեք Git չեն մտնում։ Պարզ սերվերի վրա դրանք գտնվում են environment ֆայլերում, որոնք պատկանում են deploy օգտատիրոջը, կարդացվում են միայն հավելվածի խմբի կողմից, 640-ի նման թույլտվություններով, վեբ root-ից դուրս։ Ավելի մեծ setup-ների համար secrets manager-ը, ինչպես Vault-ը, AWS Secrets Manager-ը, Doppler-ը կամ SOPS-ով գաղտնագրված ֆայլերը, դրանք ներարկում են deploy-ի կամ գործարկման ժամանակ։ Յուրաքանչյուր հավելված և միջավայր ստանում է իր credential-ները նվազագույն scope-ով, ուստի staging-ի key-երը չեն կարող դիպչել production-ին։ Secret-ները rotate եմ անում, երբ մարդիկ հեռանում են կամ ցանկացած կասկածելի արտահոսքից հետո, և CI-ում գործարկում եմ secret scanning, որովհետև հին commit-երում արտահոսած key-երը կոտրումների հաճախակի աղբյուր են։

Q5Ի՞նչ է Cloudflare-ը ավելացնում սերվերի անվտանգությանը, և ո՞ր SSL ռեժիմը պետք է օգտագործենք։

Cloudflare-ը կանգնած է այցելուների և քո սերվերի միջև․ այն թաքցնում է origin IP-ն, կլանում է ծավալային DDoS-ը, տրամադրում է WAF, bot պաշտպանություն և rate limiting, և cache է անում static բովանդակությունը։ SSL ռեժիմը պետք է լինի Full (strict)․ այցելուները Cloudflare-ին միանում են HTTPS-ով, և Cloudflare-ը քո սերվերին միանում է HTTPS-ով՝ ստուգելով origin certificate-ը, լինի դա Let's Encrypt, թե Cloudflare origin certificate։ Flexible ռեժիմը origin-ի միացումը թողնում է չգաղտնագրված և հաճախ redirect loop-եր է առաջացնում։ Որպեսզի հարձակվողները չշրջանցեն Cloudflare-ը, 80 և 443 port-երը սահմանափակիր Cloudflare-ի IP միջակայքերով, օգտագործիր authenticated origin pulls կամ tunnel, որպեսզի ընդհանրապես ոչ մի վեբ port բաց չլինի։

Q6Ինչպե՞ս ես իմանում, որ սերվերն իսկապես hardening արված է, և ոչ թե պարզապես մեկ անգամ կարգավորված։

Անընդհատ ստուգելով, ոչ թե հիշողությանը վստահելով։ Baseline-ը պահում եմ որպես checklist կամ ավտոմատացում և նորից գործարկում եմ այն, Lynis-ով audit եմ անում համակարգը և ժամանակի ընթացքում համեմատում գնահատականները, և սերվերի public IP-ն scan եմ անում դրսից՝ տեսնելու, թե որ port-երն են իրականում բաց։ Monitoring-ը alert է տալիս սկավառակի, uptime-ի և certificate-ների ժամկետի մասին, իսկ fail2ban-ի և auth log-երը ցույց են տալիս login-ների ակտիվությունը։ Պարբերաբար վերանայում եմ օգտատերերի հաշիվները և SSH key-երը և հեռացնում հնացածները։ Եվ ստուգում եմ restore-ները, որովհետև վերականգնումը անվտանգության մաս է։ Կոնֆիգուրացիան շեղվում է, երբ մարդիկ արագ շտկումներ են անում, ուստի hardening-ը ապացույցներով կրկնվող գործընթաց է, ոչ թե միանգամյա առաջադրանք։

Սենիոր մակարդակի հարցեր

S1Patch անել սերվերները տեղում, թե՞ վերակառուցել image-ներից։

Տեղում patch անելը պարզ է և լավ է մի քանի երկարակյաց սերվերի համար. անվտանգության ավտոմատ թարմացումներ, reboot-ի պատուհան և configuration management՝ drift-ը կանխելու համար։ Թուլությունն այն է, որ յուրաքանչյուր սերվեր դանդաղորեն դառնում է եզակի, և incident-ից հետո չեք կարող վստահ լինել, թե ինչ կա դրա վրա։ Immutable infrastructure-ը յուրաքանչյուր փոփոխության համար կառուցում է թարմ image կամ container՝ Packer-ով, cloud-init-ով կամ Docker-ով, և սերվերները փոխարինում է, ոչ թե փոփոխում։ Drift-ն անհետանում է, rollback-ը redeploy է, իսկ compromised host-ը պարզապես ոչնչացվում է։ Դա պահանջում է stateless սերվերներ, ավտոմատացում և pipeline, ինչն իրական ներդրում է։ Իմ պրագմատիկ ճանապարհը. փոքր fleet-երի համար տեղում patching՝ script արված baseline-ներով, իսկ immutable image-ներ, երբ state-ն ապրում է managed ծառայություններում և կան մի քանի նույնական app սերվերներ։

S2Ինչպե՞ս եք կառավարում SSH հասանելիությունը աճող թիմի համար։

Public key-երը authorized_keys-ում պատճենելը չի մասշտաբավորվում. offboarding-ը նշանակում է խմբագրել յուրաքանչյուր սերվեր, և key-երը երբեք չեն սպառվում։ Ավելի լավ տարբերակներ՝ աճող հասունությամբ. key-երը կառավարեք կենտրոնացված Ansible-ով, որպեսզի մարդու ավելացումը կամ հեռացումը լինի մեկ փոփոխություն, առջևում դրեք bastion կամ VPN, որպեսզի սերվերներն ընդհանրապես SSH չբացահայտեն, հետո օգտագործեք SSH սերտիֆիկատներ։ Սերտիֆիկատների դեպքում սերվերները TrustedUserCAKeys-ով վստահում են մեկ CA key-ի, իսկ մարդիկ ստանում են մի քանի ժամով ստորագրված սերտիֆիկատներ, իդեալում թողարկված single sign-on-ից և MFA-ից հետո՝ identity-aware proxy-ի նման գործիքով։ Հասանելիությունն ավտոմատ սպառվում է, log-երը ցույց են տալիս, թե ով է միացել, իսկ offboarding-ը կատարվում է identity provider-ում։ Production-ի write access-ը նույնպես պետք է ավելի հազվադեպ լինի, քան read access-ը, և sudo-ն՝ log արված։

S3Cloudflare Tunnel, թե՞ firewall allowlist-եր origin-ը պաշտպանելու համար։

Allowlist-ը port 80-ը և 443-ը բաց է պահում, բայց ընդունում է միայն Cloudflare-ի հրապարակված range-երը։ Այն պարզ է և արագ, բայց ցուցակը ժամանակ առ ժամանակ փոխվում է և պետք է թարմացվի, իսկ Cloudflare օգտագործող ցանկացած այլ ոք տեսականորեն կարող է դրանով route անել դեպի ձեր origin, եթե միայն չեք օգտագործում նաև authenticated origin pulls։ Tunnel-ը սերվերում աշխատեցնում է փոքր daemon, որը ելքային կապով միանում է Cloudflare-ին, ուստի origin-ն ընդհանրապես մուտքային web port-եր չունի, և այն կարող է նույնիսկ SSH տեղափոխել Zero Trust access policy-ներով։ Գինը մոնիթորինգ պահանջող ևս մեկ process-ն է, մեկ vendor-ի հետ ավելի խիստ կապը և tunnel-ով debug անելը։ Cloudflare-ի հետևում փոքր fleet-ի համար նախընտրում եմ tunnel կամ allowlist գումարած origin սերտիֆիկատներ. երկուսն էլ ավելի լավ են, քան origin-ը բաց թողնելը։

Բառարան

Attack surface
Այն ամենը, ինչին հարձակվողը կարող է հասնել և փորձել. բաց port-եր, ծառայություններ, login-ներ, վերբեռնված ֆայլեր և բաց API-ներ։
Blast radius
Որքան վնաս կարող է հասցնել մեկ compromised account, key կամ ծառայություն, մինչև այլ կառավարման միջոցները կանգնեցնեն այն։
Defence in depth
Անվտանգության անկախ շերտերի դարսում, որպեսզի մեկի ձախողումը չբացահայտի ամբողջ համակարգը։
Drop-in file
Փոքր config ֆայլ .d directory-ում, որը վերագրում է հիմնական config-ը և պահպանվում է package update-ներից հետո։
Socket activation
Երբ systemd-ն ինքն է լսում port-ը և ծառայությունը գործարկում միայն կապ ստանալիս։
AppArmor
Ubuntu-ի mandatory access control համակարգը, որը սահմանափակում է, թե կոնկրետ ծրագրերը ինչ կարող են կարդալ, գրել կամ կատարել։
sysctl
Kernel-ի պարամետրերը runtime-ում կարդալու և սահմանելու interface, որոնք պահպանվում են /etc/sysctl.d ֆայլերում։
HSTS
Պատասխանի header, որը բրաուզերներին ասում է որոշակի ժամանակահատվածում դոմենի համար օգտագործել միայն HTTPS։
Bastion host
Մեկ ամրացված սերվեր, որն օգտագործվում է որպես private ցանց SSH-ով մուտքի միակ կետ։
SSH certificate
Վստահելի CA-ի ստորագրած user կամ host key՝ ներկառուցված ժամկետով, որը փոխարինում է ցրված authorized_keys գրառումներին։

Պրոդուկտի գաղափար ունե՞ք։Եկեք ստեղծենք այն։

Պատմեք ձեր գաղափարի, պրոդուկտի կամ թիմի մասին։ Սովորաբար պատասխանում եմ մեկ աշխատանքային օրվա ընթացքում։