Բոլոր դասերը
Linux և սերվերներՍկսնակ11 րոպե ընթերցումԻմ ստեկում

Linux-ի օգտատերեր, խմբեր և թույլտվություններ

Ով ինչ կարող է կարդալ, գրել և գործարկել՝ rwx, chmod, chown, sudo և դրանց պատճառած bug-երը։

  • Users & groups
  • chmod / chown
  • sudo
  • umask
Քայլ 1/5
ls -l-ը ցույց է տալիս ֆայլի դռան ցուցանակը՝ -rw-r-----։ Տեր alice-ը կարդում ու գրում է, devs խումբը՝ միայն կարդում, մնացածը՝ ոչինչ։

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

Linux-ում յուրաքանչյուր ֆայլ ունի տեր (owner), խումբ (group) և թույլտվությունների երեք հավաքածու՝ read, write և execute։ Յուրաքանչյուր պրոցես աշխատում է ինչ-որ օգտատիրոջ անունից։ Սերվերների «Permission denied» սխալների մեծ մասը գալիս է այն անհամապատասխանությունից, թե որ օգտատիրոջ անունից է աշխատում հավելվածը և ում են պատկանում իրեն անհրաժեշտ ֆայլերը։ Հասկացիր այդ մոդելը, և կկարողանաս այդ սխալները անվտանգ շտկել՝ chmod 777-ին ձեռք չտալով։

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

  • Յուրաքանչյուր կայքի կամ հավելվածի համար օգտագործում եմ առանձին system user, որպեսզի մեկ կոտրված կայքը չկարողանա կարդալ կամ փոխել մյուսը։
  • Թույլտվությունների սխալները production-ի ամենահաճախ խնդիրներից են, որոնք շտկում եմ․ վերջին դեպքերից մեկը cache դիրեկտորիա էր, որտեղ հավելվածը չէր կարողանում գրել։
  • Least privilege-ը յուրաքանչյուր սերվերի իմ անվտանգության baseline-ի մասն է․ մարդիկ մուտք են գործում իրենց անունով, sudo են օգտագործում անհրաժեշտության դեպքում, և ոչ ոք root-ով չի աշխատում։

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

01Օգտատերեր, խմբեր և root

Օգտատերը (user) թվային ID-ով (UID) ինքնություն է։ Մարդիկ օգտատերեր են, բայց նաև ծառայությունները․ Ubuntu-ում nginx-ը սովորաբար աշխատում է www-data-ի անունից (RHEL-ում՝ nginx), PostgreSQL-ը՝ postgres-ի։ Խումբը (group) օգտատերերի անվանված հավաքածու է, որը թույլ է տալիս մի քանի օգտատիրոջ տալ նույն հասանելիությունը։ root-ը (UID 0) կարող է ամեն ինչ անել և շրջանցում է թույլտվությունների սովորական ստուգումները, և հենց դրա համար ամեն օր root-ով աշխատել պետք չէ։ Օգտատերերի հաշիվները նշված են /etc/passwd-ում, գաղտնաբառերի hash-երը՝ /etc/shadow-ում (կարդացվում է միայն root-ի կողմից), իսկ խմբերը՝ /etc/group-ում։ id հրամանը ցույց է տալիս, թե ով ես և որ խմբերին ես պատկանում։

bash
whoami                       # current user
id                           # uid, gid and all groups
id www-data                  # who is the web server user?
getent passwd your_user      # account details
groups your_user             # group membership
sudo adduser your_user       # create a user (Debian/Ubuntu, interactive)
sudo useradd -m -s /bin/bash your_user   # portable, non-interactive
sudo usermod -aG www-data your_user      # ADD to a group (-a matters!)

02rwx և octal թույլտվությունների ընթերցում

Գործարկիր ls -l, և կտեսնես մոտավորապես այսպիսի բան՝ -rw-r--r-- 1 deploy www-data։ Առաջին նիշը տիպն է (- ֆայլ, d դիրեկտորիա, l link)։ Հետո գալիս են երեք եռյակ՝ տիրոջ իրավունքները, խմբի իրավունքները և մնացած բոլորի իրավունքները։ r-ը read է, w-ն՝ write, x-ը՝ execute։ Դիրեկտորիայի վրա իմաստը փոխվում է․ r-ը թույլ է տալիս ցուցակել անունները, w-ն՝ ներսում ստեղծել և ջնջել ֆայլեր, իսկ x-ը՝ մտնել դրա մեջ և ֆայլերին հասնել ճանապարհով։ Յուրաքանչյուր եռյակ նաև թիվ է՝ r = 4, w = 2, x = 1, իրար գումարած։ Այսպիսով, 644-ը rw-r--r-- է (տերը գրում է, մյուսները կարդում են), 755-ը՝ rwxr-xr-x, 600-ը միայն տիրոջն է, իսկ 640-ը թույլ է տալիս խմբին կարդալ։

  • 644՝ սովորական ֆայլեր։ 755՝ դիրեկտորիաներ և script-եր։ 600՝ secret-ներ, ինչպես SSH key-երը և .env ֆայլերը։
  • 640 / 750՝ տերը՝ լրիվ, խումբը՝ միայն կարդալ, մյուսները՝ ոչինչ․ լավ է հավելվածի config-ի համար, որը կիսվում է վեբ սերվերի խմբի հետ։
  • 777-ը նշանակում է, որ համակարգում ցանկացածը կարող է գրել։ Այն թաքցնում է իրական խնդիրը և անվտանգության անցք է բացում։
  • Հատուկ բիթեր՝ setgid-ը դիրեկտորիայի վրա (2775) ստիպում է նոր ֆայլերին ժառանգել դիրեկտորիայի խումբը, sticky bit-ը (1777, ինչպես /tmp-ում) թույլ չի տալիս օգտատերերին ջնջել ուրիշների ֆայլերը։

03chmod, chown և սեփականության շտկում

chmod-ը փոխում է թույլտվությունները, chown-ը՝ տիրոջը և խումբը։ Երկուսն էլ ընդունում են -R ռեկուրսիվ փոփոխությունների համար, ինչը հզոր է և վտանգավոր․ սխալ դիրեկտորիայի վրա անզգույշ chmod -R-ը կարող է կոտրել SSH-ը կամ ամբողջ համակարգը։ Փոքր, ճշգրիտ փոփոխությունների համար օգտագործիր symbolic ռեժիմներ (u+x-ը տիրոջն ավելացնում է execute, g-w-ն խմբից հանում է write), իսկ octal ռեժիմներ՝ երբ ուզում ես ճշգրիտ արդյունք։ Տարածված pattern է ֆայլերին և դիրեկտորիաներին տարբեր ռեժիմներ տալը find-ով, որովհետև դիրեկտորիաներին x պետք է, իսկ սովորական ֆայլերը սովորաբար այն չպետք է ունենան։

bash
chmod 600 ~/.ssh/id_ed25519          # private key: owner only
chmod u+x deploy.sh                   # make a script executable
chmod g-w config.php                  # remove group write
sudo chown deploy:www-data -R /var/www/app
# Directories 755, files 644, in one tree
find /var/www/app -type d -exec chmod 755 {} +
find /var/www/app -type f -exec chmod 644 {} +
# Inspect a path permission by permission
namei -l /var/www/app/storage/logs/app.log

04sudo և /etc/sudoers

sudo-ն մեկ հրաման գործարկում է root-ի (կամ այլ օգտատիրոջ) անունից՝ ստուգելուց հետո, որ դա քեզ թույլատրված է։ Յուրաքանչյուր օգտագործում գրանցվում է log-ում, ինչը հաշվետվողականության համար շատ ավելի լավ է, քան root գաղտնաբառը կիսելը։ Ubuntu-ում sudo խմբի անդամները ստանում են sudo-ի լրիվ իրավունքներ, RHEL-ում խումբը wheel-ն է։ Կանոնները գտնվում են /etc/sudoers-ում և /etc/sudoers.d-ի տակի ֆայլերում։ Երբեք դրանք մի խմբագրիր սովորական խմբագրիչով․ օգտագործիր visudo, որը պահպանելուց առաջ ստուգում է syntax-ը։ Կոտրված sudoers ֆայլը կարող է բոլորին զրկել sudo-ից։ Կարող ես նաև նեղ իրավունքներ տալ, օրինակ՝ deploy օգտատիրոջը թույլ տալ առանց գաղտնաբառի restart անել մեկ ծառայություն և ուրիշ ոչինչ։ Ubuntu-ի վերջին թողարկումները գալիս են sudo-rs-ով՝ memory-safe վերագրված տարբերակով՝ նույն հրամաններով և config-ով։

bash
sudo usermod -aG sudo your_user     # full sudo (wheel on RHEL)
sudo -l                             # what am I allowed to run?
sudo -u postgres psql               # run as another user
sudo visudo -f /etc/sudoers.d/deploy
# Content of /etc/sudoers.d/deploy:
deploy ALL=(root) NOPASSWD: /usr/bin/systemctl restart myapp.service

05umask, ծառայությունների օգտատերեր և ACL-ներ

umask-ը որոշում է նոր ֆայլերի լռելյայն թույլտվությունները՝ բիթեր հանելով։ Տարածված լռելյայն 022-ը նոր ֆայլերին տալիս է 644, իսկ նոր դիրեկտորիաներին՝ 755․ 002 umask-ը տալիս է 664 և 775, ինչը հարմար է խումբ կիսող թիմերի համար։ Հավելվածների համար ստեղծիր առանձին system user առանց login shell-ի և նրան տեր դարձրու միայն այն բանի, ինչ հավելվածը պետք է գրի։ ACL-ները (access control lists) owner/group/other մոդելի վրա լրացուցիչ կանոններ են ավելացնում, օրինակ՝ ևս մեկ օգտատիրոջ write հասանելիություն տալ մեկ դիրեկտորիայի։ Դրանք հարմար են, բայց թաքնված են արագ ls -l-ից (փնտրիր +-ը mode-ի վերջում), ուստի օգտագործիր դրանք խնայողաբար և փաստաթղթավորիր։

bash
umask                                   # show current mask, e.g. 0022
sudo useradd --system --no-create-home --shell /usr/sbin/nologin myapp
sudo chown -R myapp:myapp /var/lib/myapp
# ACL: let www-data write to one directory, now and for new files
sudo setfacl -R -m u:www-data:rwX /var/www/app/storage
sudo setfacl -R -d -m u:www-data:rwX /var/www/app/storage
getfacl /var/www/app/storage

06Դասական bug-ը՝ վեբ սերվերը չի կարողանում գրել

Տիպիկ դեպք՝ PHP կամ Node հավելվածը լոկալ աշխատում է, բայց production-ում log-ում գրում է «Permission denied», երբ cache, log-եր կամ upload-ներ է գրում։ Պատճառը գրեթե միշտ այն է, որ կոդը deploy է արվել մի օգտատիրոջ կողմից (deploy կամ root), մինչդեռ հավելվածն աշխատում է մեկ ուրիշի անունից (www-data կամ PHP-FPM pool-ի օգտատեր)։ Լուծումը 777-ը չէ։ Փոխարենը պարզիր, թե որ օգտատիրոջ անունից է աշխատում պրոցեսը, կոդը թող պատկանի deploy օգտատիրոջը և լինի read-only հավելվածի համար, իսկ հավելվածին write հասանելիություն տուր միայն այն դիրեկտորիաներին, որոնց դա իսկապես պետք է, օրինակ՝ Laravel-ում storage-ին և bootstrap/cache-ին։ setgid բիթը նոր ֆայլերը պահում է ճիշտ խմբում։ Հիշիր նաև, որ root-ով deploy անելը կամ artisan հրամաններ գործարկելը կարող է ստեղծել root-ին պատկանող cache ֆայլեր, որոնք հավելվածը հետո չի կարող վերագրել։

bash
ps -o user,pid,cmd -C php-fpm8.3     # which user runs PHP? (adjust version)
ps -o user,pid,cmd -C node
cd /var/www/app
sudo chown -R deploy:www-data .
sudo chmod -R 750 .
sudo chmod -R 2775 storage bootstrap/cache     # group write + setgid
sudo find storage bootstrap/cache -type f -exec chmod 664 {} +
sudo -u www-data php artisan cache:clear       # run as the app user, not root

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

  • Ցանկացած նոր սերվերի կարգավորում․ ստեղծիր անձնական օգտատերեր sudo-ով և յուրաքանչյուր հավելվածի համար առանձին system user։
  • Log-երում «Permission denied» կամ «EACCES» սխալների debug։
  • CI կամ deploy օգտատիրոջը նեղ իրավունքներ տալը, օրինակ՝ մեկ ծառայության restart։
  • Secret-ների պաշտպանություն․ .env ֆայլերը, private key-երը և տվյալների բազայի credential-ները պետք է լինեն 600 կամ 640։

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

  • Մի օգտագործիր chmod 777 կամ chown -R root որպես արագ լուծում․ այն թաքցնում է իրական պատճառը և թուլացնում է անվտանգությունը։
  • Ամեն ինչի համար մի հենվիր ACL-ների վրա․ եթե owner/group մոդելը կարող է արտահայտել կանոնը, պահիր այն պարզ և տեսանելի։
  • Մի կիսիր մեկ login (հատկապես root) մի քանի մարդու միջև․ կորցնում ես հաշվետվողականությունը։

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

  • ACL-ներ (setfacl)

    Լրացուցիչ կանոններ ըստ օգտատիրոջ կամ խմբի, երբ owner/group/other-ը բավարար չէ։

  • SELinux / AppArmor

    Mandatory access control սովորական թույլտվությունների վրա։ SELinux-ը լռելյայն է RHEL-ում, AppArmor-ը՝ Ubuntu-ում։

  • Կոնտեյներներ

    Docker-ը հավելվածներն ավելի է մեկուսացնում, բայց միևնույն է պետք է օգտատեր սահմանես կոնտեյների ներսում և mount արված volume-ների վրա։

  • systemd sandboxing

    DynamicUser, ProtectSystem և ReadWritePaths տարբերակները սահմանափակում են ծառայությունը՝ առանց ձեռքով օգտատերեր ստեղծելու։

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

  1. Մուտք գործիր անվանված օգտատիրոջով և օգտագործիր sudo․ անջատիր root-ի ուղիղ մուտքը SSH-ով։
  2. Յուրաքանչյուր հավելված գործարկիր իր system user-ով առանց login shell-ի, որին պատկանում է միայն այն, ինչ նա պետք է գրի։
  3. Օգտագործիր 600 private key-երի և env ֆայլերի համար, 644 / 755՝ սովորական ֆայլերի և դիրեկտորիաների համար։
  4. Խմբեր ավելացնելու համար միշտ օգտագործիր usermod -aG (-a-ով)․ առանց -a-ի փոխարինում ես օգտատիրոջ բոլոր խմբերը։
  5. sudo կանոնները խմբագրիր միայն visudo-ով, նախընտրելի է որպես առանձին ֆայլեր /etc/sudoers.d-ում։
  6. Հավելվածի հրամանները (cache-ի մաքրում, migration-ներ) գործարկիր հավելվածի օգտատիրոջով, օրինակ՝ sudo -u www-data։
  7. Նկատի ունեցիր, որ խմբերի փոփոխություններն ուժի մեջ են մտնում միայն հաջորդ login-ից (կամ newgrp-ով)։

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

  • chmod -R 777 վեբ root-ի վրա, ինչը թույլ է տալիս մեքենայի ցանկացած պրոցեսի վերագրել քո կոդը։
  • artisan, npm կամ deploy script root-ով գործարկել և թողնել root-ին պատկանող ֆայլեր, որոնց հավելվածը չի կարող դիպչել։
  • usermod -G՝ -aG-ի փոխարեն, ինչը աննկատ հանում է օգտատիրոջը sudo խմբից։
  • SSH-ը մերժում է key-ը, որովհետև ~/.ssh-ը կամ authorized_keys-ը խմբի կամ բոլորի համար գրելի է։
  • /etc/sudoers-ը ուղիղ խմբագրել և պահպանել syntax-ի սխալ, որը կոտրում է sudo-ն։

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

07Ինչպես է kernel-ն իրականում ստուգում հասանելիությունը

Յուրաքանչյուր open-ի ժամանակ kernel-ը համեմատում է պրոցեսի effective UID-ն և նրա խմբերը (հիմնականը և լրացուցիչները) ֆայլի սեփականատիրոջ և խմբի հետ։ Ստուգումն ընտրում է ճիշտ մեկ դաս՝ հերթականությամբ․ եթե դու սեփականատերն ես, հաշվի են առնվում միայն owner բիթերը, հակառակ դեպքում, եթե խմբերիցդ մեկը համընկնում է, միայն group բիթերը, հակառակ դեպքում՝ other բիթերը։ Դասերը չեն գումարվում, ուստի 0077 mode-ով ֆայլը մերժում է իր իսկ սեփականատիրոջը, թեև բոլոր մյուսները կարող են կարդալ այն։ Ֆայլին հասնելու համար անհրաժեշտ է նաև x (search) թույլտվություն ճանապարհի յուրաքանչյուր դիրեկտորիայի վրա, ինչը namei -l-ը ցույց է տալիս մեկ հայացքով։ Ֆայլի ջնջումը կամ վերանվանումը կախված է դիրեկտորիայի, ոչ թե ֆայլի գրելու թույլտվությունից։ Root-ը շրջանցում է այս ստուգումների մեծ մասը ոչ թե կախարդանքով, այլ capability-ների միջոցով, ինչպես CAP_DAC_OVERRIDE-ը, իսկ աշխատող պրոցեսը պահպանում է login-ի պահին ունեցած խմբերի ցուցակը, ինչի պատճառով նոր խմբին անդամակցությունը նոր session է պահանջում։

bash
cat /proc/self/status | grep -E '^(Uid|Gid|Groups)'   # real/effective IDs
chmod 077 test.txt && cat test.txt      # owner denied: only owner bits apply
namei -l /srv/app/shared/.env           # x needed on every directory
sudo -u www-data test -r /srv/app/shared/.env && echo readable
newgrp www-data                          # pick up a new group without relogin

08setuid, setgid և capability-ներ

Setuid ծրագիրն աշխատում է իր սեփականատիրոջ, այլ ոչ թե կանչողի արտոնություններով․ passwd-ը և sudo-ն setuid root են, որպեսզի սովորական օգտատերերը կարողանան վերահսկվող ձևով փոխել պաշտպանված ֆայլերը։ Յուրաքանչյուր setuid-root binary հարձակման մակերես է, ուստի աուդիտ արա դրանք և երբեք մի դիր այդ բիթը script-երի կամ սեփական գործիքների վրա։ Linux capability-ները root-ի ուժը բաժանում են մոտ քառասուն մասի, ուստի ծրագիրը կարող է ստանալ միայն մեկը․ cap_net_bind_service-ը թույլ է տալիս non-root պրոցեսին bind անել 1024-ից ցածր port-եր, cap_net_raw-ը թույլ է տալիս ping-ին raw փաթեթներ ուղարկել։ Ֆայլային capability-ները դրվում են setcap-ով, իսկ systemd-ն կարող է դրանք տալ ծառայությանը AmbientCapabilities-ով՝ մնացած ամեն ինչը հանելով CapabilityBoundingSet-ով։ Ֆայլային համակարգերը կարող են նաև mount արվել nosuid, nodev և noexec օպցիաներով, ինչը էժան hardening քայլ է upload-ի և ժամանակավոր դիրեկտորիաների համար, որոնք երբեք չպետք է գործարկվող ծրագրեր պարունակեն։

bash
find / -xdev -perm -4000 -type f 2>/dev/null   # setuid binaries
getcap -r /usr/bin 2>/dev/null                 # files with capabilities
sudo setcap 'cap_net_bind_service=+ep' /usr/local/bin/myserver
# In a systemd unit, instead of setcap:
#   AmbientCapabilities=CAP_NET_BIND_SERVICE
#   CapabilityBoundingSet=CAP_NET_BIND_SERVICE
findmnt -o TARGET,OPTIONS /tmp                  # check nosuid,noexec

09Երբ mode-երը ճիշտ են, բայց հասանելիությունը դեռ մերժվում է

Եթե ls -l-ը կատարյալ է թվում, իսկ հավելվածը դեռ ստանում է EACCES, դասական մոդելից վեր ինչ-որ բան «ոչ» է ասում։ Mandatory access control-ը սովորական կասկածյալն է․ Ubuntu-ում AppArmor-ը սահմանափակված ծրագրերին թույլ է տալիս միայն իրենց profile-ում նշված ճանապարհները, իսկ RHEL-ում SELinux-ը label է դնում յուրաքանչյուր ֆայլի և պրոցեսի վրա և մերժում է այն ամենը, ինչ policy-ն չի թույլատրում՝ անկախ սեփականատիրոջից և mode-ից։ SELinux-ում web root-ի մեջ տեղափոխված (ոչ թե պատճենված) ֆայլը պահպանում է իր հին label-ը, ինչը 403 սխալների դասական պատճառ է․ restorecon-ը շտկում է դա։ Այլ թաքնված շերտեր՝ chattr +i-ով դրված immutable ատրիբուտը, որը արգելափակում է նույնիսկ root-ին, մինչև այն հանվի, read-only կամ noexec mount-եր, ACL mask-եր, որոնք սահմանափակում են group գրառումները, և container-ներում՝ user namespace-ներ, որոնք container-ի root-ը քարտեզագրում են host-ի ոչ արտոնյալ UID-ի։ Ստուգիր սրանք՝ մինչև mode-երին ձեռք տալը։

bash
sudo aa-status                        # AppArmor profiles in enforce mode
sudo journalctl -k | grep -i apparmor  # AppArmor denials
ls -Z /var/www/html                    # SELinux labels (RHEL family)
sudo ausearch -m avc -ts recent        # SELinux denials
sudo restorecon -Rv /var/www/html      # reset labels to policy defaults
lsattr /etc/resolv.conf                # 'i' = immutable
getfacl /srv/app/storage | grep mask   # ACL mask limiting entries

10UID-ները container-ներում և volume-ներում

Kernel-ը պահում է թվեր, ոչ թե անուններ։ UID 1000-ին պատկանող ֆայլը պատկանում է նրան, ով ունի UID 1000 ընթացիկ օգտատերերի բազայում, իսկ այն տարբեր է host-ի և container image-ի միջև։ Դրա համար են bind-mount արված դիրեկտորիաները հաճախ ցույց տալիս տարօրինակ սեփականատերեր կամ դառնում անհասանելի գրելու համար․ container-ի պրոցեսն աշխատում է որպես UID 33 (www-data Debian image-ներում) կամ 1000 (node պաշտոնական Node image-ներում), մինչդեռ host-ի ֆայլերը պատկանում են քո deploy օգտատիրոջը։ Լուծումները՝ ամենապարզից․ container-ը գործարկիր host-ի UID-ով և GID-ով (user: "1000:1000" Compose-ում), volume-ը chown արա container-ի UID-ին, կամ օգտագործիր named volume-ներ, որոնք Docker-ը ինքն է նախապատրաստում։ Rootless Docker-ը և Podman-ը ավելացնում են user namespace-ներ, ուստի container-ի root-ը host-ում քարտեզագրվում է բարձր ոչ արտոնյալ UID-ի, ինչը բարելավում է անվտանգությունը, բայց փոխում է սկավառակի վրա տեսանելի թվերը։ systemd-ի DynamicUser-ը նույն ոգով յուրաքանչյուր ծառայության համար ժամանակավոր UID է հատկացնում։

bash
docker compose exec app id                  # UID inside the container
stat -c '%u:%g %n' ./storage                # numeric owner on the host
# docker-compose.yml
#   services:
#     app:
#       user: "1000:1000"                   # match the host deploy user
cat /etc/subuid                             # ranges for rootless containers

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

01

Կապալառուին պետք է հասանելիություն միայն մեկ կայքին

Խնդիր

Արտաքին ծրագրավորողը պետք է deploy անի և կարդա log-երը մեկ կայքի համար ընդհանուր սերվերի վրա, որտեղ մի քանի հաճախորդների կայքեր կան։ Նա չպետք է տեսնի մյուս կայքերի կոդը կամ secret-ները։

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

Ստեղծիր նրա համար անձնական account՝ սեփական SSH key-ով և առանց password-ի։ Յուրաքանչյուր կայք արդեն աշխատում է իր սեփական օգտատիրոջով, ուստի կապալառուին ավելացրու միայն այդ կայքի խմբին և համոզվիր, որ մյուս կայքերի դիրեկտորիաները 750 են կամ ավելի խիստ, որպեսզի «other»-ը ոչինչ չստանա։ Եթե նրան պետք է ծառայության restart, /etc/sudoers.d/-ում visudo-ով ավելացրու մեկ կանոն, որը թույլ է տալիս ճիշտ այդ systemctl հրամանը։ Կայքի log-երը կարդալու հասանելիություն տուր խմբային սեփականության կամ ACL-ի միջոցով, ոչ թե adm խմբի անդամակցությամբ, որը բացում է բոլոր log-երը։ Սահմանիր account-ի ժամկետ chage -E-ով, ստուգիր sudo log-երը auth.log-ում և ջնջիր account-ը, երբ աշխատանքն ավարտվի։

02

SSH-ն անտեսում է ճիշտ key-ը

Խնդիր

Օգտատիրոջ public key-ը authorized_keys-ում է, բայց SSH-ն դեռ password է հարցնում կամ ձախողվում է «Permission denied (publickey)» սխալով։ Key-ն աշխատում է այլ սերվերի վրա։

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

Նայիր սերվերի կողմը, ոչ թե client-ինը։ sshd-ն StrictModes-ով (լռելյայն) մերժում է key-երը, եթե home դիրեկտորիան, ~/.ssh-ը կամ authorized_keys-ը գրելի են խմբի կամ մյուսների համար կամ պատկանում են ուրիշին։ Գործարկիր namei -l /home/user/.ssh/authorized_keys և շտկիր chmod go-w ~, chmod 700 ~/.ssh, chmod 600 ~/.ssh/authorized_keys և chown -R user:user ~/.ssh հրամաններով։ Ստուգիր sudo journalctl -u ssh-ը (RHEL-ում sshd) «bad ownership or modes» տողերի համար։ SELinux համակարգերում վերականգնված home դիրեկտորիային կարող է restorecon -Rv ~/.ssh պետք լինել։ Համոզվիր նաև, որ տողը կոտրված չէ, և key-ի տիպը ընդունվում է սերվերի OpenSSH տարբերակի կողմից։

03

Container-ի ստեղծած ֆայլերը պատկանում են root-ին

Խնդիր

Bind-mount արված նախագծով container-ի ներսում composer install կամ npm ci գործարկելուց հետո host-ի deploy օգտատերն այլևս չի կարողանում թարմացնել կամ ջնջել vendor-ը և node_modules-ը։

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

Container-ի պրոցեսն աշխատել է որպես root, ուստի նրա ստեղծած ֆայլերը host-ում պատկանում են UID 0-ին։ Գոյություն ունեցող ֆայլերը մեկ անգամ շտկիր sudo chown -R deploy:deploy vendor node_modules-ով։ Հետո կանխիր կրկնությունը․ build հրամանները գործարկիր --user "$(id -u):$(id -g)"-ով կամ Compose ֆայլում դիր user:, կամ image-ը build արա non-root օգտատիրոջով, որի UID-ն համընկնում է host-ի հետ։ Երկար աշխատող հավելվածի container-ների համար նախընտրիր named volume-ներ կամ build-ի ժամանակ կոդը պատճենիր image-ի մեջ՝ production-ում source tree-ն bind-mount անելու փոխարեն։ Խուսափիր mount-ի վրա chmod 777-ով շտկելուց, որը պարզապես խնդիրը վերածում է անվտանգության խնդրի։

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

Ճանապարհի սեփականատերը, խումբը և mode-ը
stat -c '%U:%G %a %n' /srv/app/.env
Ստուգել հասանելիությունը որպես այլ օգտատեր
sudo -u www-data test -w /srv/app/storage && echo ok
Գտնել բոլորի համար գրելի ֆայլերը
find /srv -xdev -type f -perm -0002
Գտնել առանց վավեր սեփականատիրոջ ֆայլերը
find / -xdev \( -nouser -o -nogroup \)
Ցուցակել setuid binary-ները
find / -xdev -perm -4000 -type f
Կողպել account-ը և ավարտել ժամկետը
sudo usermod -L -e 1 contractor
Սահմանել account-ի ժամկետի ավարտի ամսաթիվ
sudo chage -E 2026-12-31 contractor
Ցույց տալ sudo-ի օգտագործման պատմությունը (Ubuntu)
sudo grep 'sudo:' /var/log/auth.log | tail
Թույլ տալ bind անել 80 port-ին առանց root-ի
sudo setcap 'cap_net_bind_service=+ep' /path/to/binary
Ֆայլը դարձնել immutable / mutable
sudo chattr +i file / sudo chattr -i file

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

  • Յուրաքանչյուր կայքի կամ հավելվածի համար առանձին system user-ը անվտանգության այն baseline-ի մասն է, որը կիրառում եմ բոլոր սերվերների վրա։
  • Շտկեցի production-ի cache-permission սխալը՝ նախ read-only ախտորոշմամբ պարզելով, թե ում են պատկանում ֆայլերը և որ օգտատիրոջ անունից է աշխատում հավելվածը։
  • Իմ կառավարած սերվերներում մարդիկ մուտք են գործում SSH key-երով՝ որպես անվանված օգտատերեր, ոչ թե գաղտնաբառերով։

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

Q1Ի՞նչ է նշանակում chmod 755, և ե՞րբ կօգտագործես 644 կամ 600։

Յուրաքանչյուր թվանշան read-ի (4), write-ի (2) և execute-ի (1) գումարն է տիրոջ, խմբի և մնացած բոլորի համար։ 755-ը rwxr-xr-x է․ տերը կարող է ամեն ինչ, մյուսները կարող են կարդալ և գործարկել։ Սա հարմար է դիրեկտորիաների համար, որովհետև դիրեկտորիայի վրա x-ը թույլ է տալիս մտնել դրա մեջ, և կատարելի script-երի համար։ 644-ը rw-r--r-- է՝ ֆայլերի սովորական ռեժիմը․ տերը խմբագրում է, մյուսները կարդում են։ 600-ը rw------- է՝ միայն տերը կարող է կարդալ կամ գրել, ինչը պետք է private SSH key-երին, .env ֆայլերին և այլ secret-ներին։ SSH-ն իրականում մերժում է այն key-երը, որոնք ուրիշները կարող են կարդալ, ուստի ռեժիմը պարտադրված է, ոչ թե պարզապես խորհուրդ տրված։

Q2Laravel հավելվածը storage-ում գրելիս «Permission denied» է տալիս։ Ինչպե՞ս կշտկես։

Նախ ստուգում եմ, թե որ օգտատերն է գրում՝ PHP-FPM pool-ի օգտատերը, հաճախ www-data-ն, թե կայքի առանձին օգտատեր։ Հետո ստուգում եմ սեփականությունը ls -l-ով և namei -l-ով ամբողջ ճանապարհով։ Սովորաբար կոդը deploy է արվել կամ artisan հրաման է գործարկվել root-ով կամ deploy օգտատիրոջով, ուստի ֆայլերը պատկանում են ուրիշի։ Deploy օգտատիրոջն եմ դարձնում տեր, վեբ սերվերի խումբը՝ խումբ, storage-ին և bootstrap/cache-ին տալիս եմ խմբի write setgid բիթով (2775), որպեսզի նոր ֆայլերը պահեն խումբը, և հետագա artisan հրամանները գործարկում եմ հավելվածի օգտատիրոջով։ Երբեք 777 չեմ օգտագործում․ դա պարզապես թույլ կտար ցանկացած պրոցեսի փոփոխել հավելվածը։

Q3Ինչո՞ւ օգտագործել sudo՝ root-ով մուտք գործելու փոխարեն։

sudo-ն յուրաքանչյուր մարդու տալիս է իր հաշիվը և log-ում գրանցում է յուրաքանչյուր արտոնյալ հրաման՝ այն գործարկած օգտատիրոջ հետ, ուստի ունես audit trail։ Այն root իրավունքների տևողությունը սահմանափակում է մեկ հրամանով, ինչը նվազեցնում է պատահական վնասը, և թույլ է տալիս նեղ իրավունքներ տալ, օրինակ՝ մեկ օգտատիրոջը թույլատրել restart անել մեկ ծառայություն։ Root-ի SSH մուտքն անջատելը նաև հեռացնում է այն մեկ հաշվի անունը, որը յուրաքանչյուր հարձակվող փորձում է առաջինը։ Root-ի դեպքում բոլորը կիսում են մեկ գաղտնաբառ, չես կարող ասել, թե ով ինչ արեց, և երկար root session-ում մեկ տառասխալը կարող է վնասել ամբողջ համակարգը։

Q4Ի՞նչ է umask-ը։

umask-ը թույլտվությունների բիթերի դիմակ է, որոնք հանվում են նոր ստեղծված ֆայլերից և դիրեկտորիաներից։ Ծրագրերը սովորաբար պահանջում են 666 ֆայլերի և 777 դիրեկտորիաների համար, իսկ umask-ը դրանից հանում է։ Տարածված լռելյայն 022-ի դեպքում նոր ֆայլերը 644 են, դիրեկտորիաները՝ 755․ միայն տերը կարող է գրել։ 002-ի դեպքում դրանք դառնում են 664 և 775, այնպես որ խումբը նույնպես կարող է գրել, ինչը հարմար է թիմի կամ խումբ կիսող deploy օգտատիրոջ և վեբ սերվերի համար։ 077-ի դեպքում ամեն նոր բան private է։ Կարող ես այն սահմանել ըստ shell-ի, ըստ օգտատիրոջ profile ֆայլերում կամ ըստ ծառայության՝ systemd unit-ում UMask=-ով։

Q5Ինչո՞ւ յուրաքանչյուր հավելված պետք է աշխատի իր օգտատիրոջով։

Որովհետև թույլտվությունները նույն սերվերի պրոցեսների միջև հիմնական պատն են։ Եթե բոլոր կայքերն աշխատում են www-data-ով, մեկ կայքի խոցելիությունը հարձակվողին թույլ է տալիս կարդալ մյուս բոլոր կայքերի config-ը և տվյալների բազաների գաղտնաբառերը և փոխել դրանց կոդը։ Ամեն հավելվածի համար առանձին օգտատիրոջ դեպքում յուրաքանչյուրը կարող է կարդալ միայն իր ֆայլերը և գրել միայն իր storage դիրեկտորիաներում, ուստի կոտրումը սահմանափակված է մնում։ Սա նաև հեշտացնում է ռեսուրսների օգտագործման և log-երի վերագրումը, և կարող ես systemd սահմանափակումներ կիրառել ըստ ծառայության։ Ծառայության օգտատերը չպետք է ունենա login shell և sudo իրավունքներ։

Q6Ի՞նչ են ACL-ները, և ե՞րբ կօգտագործես դրանք։

Access control list-ները հիմնական owner, group և others մոդելը ընդլայնում են լրացուցիչ գրառումներով, օրինակ՝ ևս մեկ օգտատիրոջ write հասանելիություն տալ մեկ դիրեկտորիայի՝ առանց դրա խումբը փոխելու։ Դրանք կառավարում ես setfacl-ով և դիտում getfacl-ով, իսկ default ACL-ները ստիպում են նոր ֆայլերին ժառանգել կանոնները։ Դրանք օգտակար են, երբ երկու տարբեր ծառայության պետք է տարբեր հասանելիություն նույն ֆայլերին, կամ ընդհանուր upload դիրեկտորիաների համար։ Թերությունը տեսանելիությունն է․ ls -l-ը ցույց է տալիս միայն գումարած նշան, ուստի debug-ի ժամանակ մարդիկ դրանք բաց են թողնում։ Ես նախ օգտագործում եմ պարզ owner-ը և group-ը և ACL-ների եմ դիմում միայն այն դեպքում, երբ այդ մոդելը չի կարող արտահայտել կանոնը։

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

S1Ինչպե՞ս կնախագծես PHP հավելվածի ֆայլերի թույլտվությունները, որպեսզի կոտրման դեպքում վնասը նվազագույն լինի։

Առանձնացրու նրան, ով deploy է անում, նրանից, ով գործարկում է։ Կոդը պատկանում է deploy օգտատիրոջը և PHP-FPM pool-ի օգտատիրոջ համար read-only է, ուստի հարձակվողը, որը հավելվածի միջոցով կոդ գործարկելու հնարավորություն է ստանում, չի կարող վերագրել հավելվածը կամ backdoor տեղադրել դրանում։ Runtime օգտատիրոջ համար գրելի են միայն այն դիրեկտորիաները, որտեղ հավելվածը պետք է գրի, օրինակ՝ storage-ը, cache-ը և uploads-ը, իսկ upload դիրեկտորիան nginx-ում սպասարկվում է առանց PHP-ի գործարկման և իդեալական դեպքում mount է արված noexec-ով։ Secret-ները պահվում են 640 env ֆայլում, որը կարդալի է միայն runtime խմբի համար։ Յուրաքանչյուր կայք ունի իր pool օգտատերը, ուստի կայքերը չեն կարող կարդալ միմյանց։ Հնարավորության դեպքում ավելացրու systemd կամ AppArmor սահմանափակում։ Գինը շփումն է․ deploy-ները և artisan հրամանները պետք է գործարկվեն ճիշտ օգտատիրոջով, ինչը script-երի կարգապահություն է պահանջում։

S2Անվտա՞նգ է deploy օգտատիրոջը systemctl-ի համար առանց password-ի sudo տալը։

Դա ամբողջությամբ կախված է նրանից, թե որքան նեղ է կանոնը։ deploy ALL=(root) NOPASSWD: /usr/bin/systemctl restart myapp.service-ի նման կանոնը՝ արգումենտների ամբողջական ցուցակով, թույլ է տալիս միայն այդ մեկ գործողությունը։ /usr/bin/systemctl-ը ցանկացած արգումենտով թույլատրող կանոնը փաստացի root է․ օգտատերը կարող է խմբագրել unit-ներ, գործարկել կամայական ծառայություններ կամ բացել pager, որը shell է բացում։ Wildcard-ները վտանգավոր են նույն պատճառով, ինչպես նաև հրամանները, որոնք բացում են editor-ներ կամ ընդունում են ֆայլերի ճանապարհներ, օրինակ՝ tee, cp կամ tar։ Ավելի լավ տարբերակներն են՝ root-ին պատկանող փոքր wrapper script առանց արգումենտների կամ polkit կանոններ, որոնք թույլ են տալիս կառավարել մեկ կոնկրետ unit։ Ինչ էլ ընտրես, deploy key-ը սահմանափակ պահիր, log արա sudo-ի օգտագործումը և կանոնները վերանայիր այնպես, կարծես deploy key-ը մի օր արտահոսելու է։

S3Հավելվածը ստանում է «Permission denied», բայց ls -l-ը ցույց է տալիս ճիշտ սեփականատեր և mode։ Էլ ի՞նչ ես ստուգում։

Նախ հաստատիր պրոցեսի ինքնությունը, ոչ թե այն օգտատիրոջը, ում կարծում ես՝ ps -o user,group,supgrp -p PID, քանի որ խմբային անդամակցությունը ֆիքսվում է պրոցեսի սկզբում, և ավելի ուշ ավելացված խումբը ակտիվ չէ մինչև restart։ Երկրորդ՝ անցիր ճանապարհով namei -l-ով, քանի որ յուրաքանչյուր ծնող դիրեկտորիա execute թույլտվություն է պահանջում։ Հետո նայիր discretionary թույլտվություններից վեր՝ AppArmor կամ SELinux մերժումները kernel log-ում կամ audit log-ում, ACL գրառումներն ու mask-երը getfacl-ով, immutable ատրիբուտը lsattr-ով և mount օպցիաները findmnt-ով, օրինակ՝ read-only կամ noexec։ systemd ծառայությունների համար ստուգիր sandboxing օպցիաները, ինչպես ProtectSystem-ը, ProtectHome-ը կամ ReadOnlyPaths-ը, systemctl cat-ով։ Container-ներում համեմատիր թվային UID-ները ներսում և դրսում, քանի որ անունները կարող են մոլորեցնել։

Բառարան

UID / GID
Օգտատիրոջ և խմբի թվային ID-ներ։ Kernel-ը պահում և համեմատում է այս թվերը, իսկ անունները միայն որոնման աղյուսակ են։
Effective UID
Ինքնությունը, որը kernel-ն օգտագործում է թույլտվությունների ստուգման համար։ Setuid ծրագիր գործարկելիս այն տարբերվում է իրական UID-ից։
Supplementary groups
Լրացուցիչ խմբեր, որոնց պրոցեսը պատկանում է իր հիմնական խմբից բացի․ դրանք ֆիքսվում են session-ի սկզբում։
setuid բիթ
Mode բիթ, որը ստիպում է ծրագրին աշխատել որպես իր ֆայլի սեփականատեր, հաճախ root, այլ ոչ թե այն օգտատերը, ով գործարկել է այն։
Capability
Root-ի արտոնությունների մեկ մասը, օրինակ՝ ցածր port-երին bind անելը, որը կարելի է առանձին տալ պրոցեսին։
MAC
Mandatory access control․ համակարգային policy (SELinux, AppArmor), որը կարող է մերժել հասանելիությունը, նույնիսկ երբ ֆայլի թույլտվությունները թույլ են տալիս։
ACL mask
ACL գրառում, որը սահմանափակում է անվանված օգտատերերի և խմբերի առավելագույն իրավունքները։ Group բիթերի վրա chmod-ը փոխում է այն։
User namespace
Kernel-ի հնարավորություն, որը container-ի ներսի UID-ները քարտեզագրում է host-ի այլ, սովորաբար ոչ արտոնյալ UID-ների։
StrictModes
sshd-ի կարգավորում, լռելյայն միացված, որը մերժում է key-երը, երբ home կամ .ssh դիրեկտորիան ունի անապահով սեփականություն կամ mode-եր։

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

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