Xavfsizlik — root siz, maxfiy ma'lumotlar, image skanerlash

~15 daq · Docker · Production

Muammodan boshlaylik

Hujumchi ilovangizdagi zaiflik orqali konteyner ichida buyruq bajarishga muvaffaq bo'ldi. Keyin nima bo'ladi?

  • Konteyner root sifatida ishlayapti → tizim fayllarini o'zgartirishi mumkin
  • env buyrug'i → baza paroli, JWT kaliti, AWS kalitlari ko'rinib turibdi
  • Image da curl, bash, apt bor → qo'shimcha vositalarni yuklab oladi
  • Konteyner baza bilan bitta tarmoqda → bazaga to'g'ridan-to'g'ri ulanadi
  • docker.sock ulangan → butun server uning qo'lida

Konteyner — xavfsizlik chegarasi, lekin mutlaq emas. Bu dars — himoya qatlamlari haqida: bittasi o'tib ketilsa, keyingisi to'xtatadi.


1. Root siz ishlash

RUN addgroup -S ilova && adduser -S ilova -G ilova
COPY --chown=ilova:ilova . .
USER ilova

Yoki tayyor foydalanuvchi: node (node image), www-data (php), nginx (nginxinc/nginx-unprivileged).

Tekshirish:

docker exec api whoami        # root bo'lmasligi kerak
docker exec api id

1024 dan kichik portlar root talab qiladi — shuning uchun root siz konteynerda 8080 ni tinglang va tashqarida 80:8080 bilan bog'lang.


2. Imtiyozlarni cheklash

services:
  api:
    image: dokon-api:1.4.2
    read_only: true                      # fayl tizimi faqat o'qish uchun
    tmpfs:
      - /tmp                             # yozish kerak bo'lgan joy — xotirada
    cap_drop:
      - ALL                              # barcha Linux imtiyozlarini olib tashlash
    security_opt:
      - no-new-privileges:true           # setuid orqali imtiyoz oshirish taqiqlangan
  • read_only — hujumchi fayllarni o'zgartira olmaydi, zararli dastur yozolmaydi
  • cap_drop: ALL — root bo'lsa ham ko'p narsa qila olmaydi (tarmoq sozlash, fayl egasini o'zgartirish...). Kerak bo'lsa bittasini qaytarish: cap_add: [NET_BIND_SERVICE]
  • no-new-privileges — sudo / setuid fayllar orqali root ga ko'tarilish yo'q

[!WARNING] privileged: true — barcha himoyalarni o'chiradi: konteyner amalda server ustidan root. Faqat aniq bilganingizda (masalan, Docker-in-Docker CI runner) va hech qachon veb-ilova uchun emas.

docker.sock

volumes:
  - /var/run/docker.sock:/var/run/docker.sock    # ⚠️

Docker socket ga kirish = serverda root (yangi privileged konteyner yaratib, butun diskni ulash mumkin). Faqat zarur vositalarga (Traefik, monitoring) va iloji bo'lsa socket proxy orqali (37-dars).


3. Maxfiy ma'lumotlar (secrets)

Qayerda saqlamaslik kerak

# ❌ Image ichida — har kim docker history yoki qatlamdan o'qiydi
ENV DB_PASSWORD=Sir123
COPY .env .
ARG NPM_TOKEN
RUN echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" > .npmrc && npm ci

Oxirgi misolda .npmrc keyingi qatorda o'chirilsa ham — u oldingi qatlamda qoladi (14-dars). ARG qiymatlari esa docker history da ko'rinadi.

docker history --no-trunc dokon-api:1.0     # ARG va ENV lar shu yerda

Build vaqtidagi sirlar — --mount=type=secret

RUN --mount=type=secret,id=npmrc,target=/root/.npmrc npm ci
docker build --secret id=npmrc,src=$HOME/.npmrc -t dokon-api .

Fayl faqat shu RUN vaqtida mavjud — hech bir qatlamga yozilmaydi.

Ishga tushirish vaqtidagi sirlar

Usul Xavfsizlik Izoh
environment: da qiymat (compose fayl Git da) ❌ Kodda parol
.env fayl (Git dan tashqari) ⚠️ Asosiy env va docker inspect da ko'rinadi
Compose secrets: ✅ Yaxshiroq Fayl sifatida /run/secrets/ da
Vault, AWS Secrets Manager, Doppler ✅✅ Katta loyihalar

Compose secrets:

services:
  baza:
    image: postgres:17-alpine
    environment:
      POSTGRES_PASSWORD_FILE: /run/secrets/db_parol
    secrets:
      - db_parol

secrets:
  db_parol:
    file: ./secrets/db_parol.txt          # Git da emas!

Ko'p rasmiy image lar *_FILE o'zgaruvchilarini qo'llab-quvvatlaydi — parol muhit o'zgaruvchisida emas, faylda. O'z ilovangizda: fs.readFileSync('/run/secrets/db_parol', 'utf8').trim().


4. Image skanerlash

Image dagi har bir paket — potentsial zaiflik (CVE). Skanerlar ma'lum zaifliklar bazasi bilan solishtiradi:

# Docker Scout (Docker Desktop bilan)
docker scout quickview dokon-api:1.4.2
docker scout cves dokon-api:1.4.2

# Trivy (bepul, ochiq kodli, CI da eng ommabop)
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock \
  aquasec/trivy image dokon-api:1.4.2

trivy image --severity HIGH,CRITICAL --exit-code 1 dokon-api:1.4.2

--exit-code 1 — jiddiy zaiflik topilsa CI ni to'xtatish (41-dars).

Natijada odatda:

Total: 23 (HIGH: 4, CRITICAL: 1)
│ openssl │ CVE-2024-XXXX │ CRITICAL │ 3.1.4-r0 │ 3.1.4-r5 │

Ko'pincha yechim oddiy: asos image ni yangilash (node:22-alpine ning yangi versiyasi) va qayta build.


5. Image ni kichik va toza saqlash

Image da qancha kam narsa bo'lsa — shuncha kam zaiflik va hujumchi uchun kamroq vosita (17-dars):

  • Multi-stage — kompilyator va dev bog'liqliklar yakuniy image da yo'q
  • -alpine, -slim, distroless — shell ham yo'q
  • Asos image — rasmiy yoki "Docker Official / Verified Publisher"; noma'lum muallif image lari — yo'q
  • Versiya qotirilgan (node:22.11-alpine), yoki digest (38-dars)

6. Tarmoq va host

  • Faqat proxy tashqariga ochiq; baza — internal: true tarmoqda (25-dars)
  • 127.0.0.1: bilan bog'lash — faqat lokal xizmatlar uchun
  • Docker portlari UFW ni chetlab o'tadi — 25-darsdagi ogohlantirish
  • Docker va server tizimini yangilab turing

Tekshiruv ro'yxati

[ ] USER — root emas
[ ] Image da sirlar yo'q (docker history ni tekshiring)
[ ] .dockerignore da .env, .git
[ ] Asos image versiyasi qotirilgan va yangi
[ ] Trivy/Scout — CRITICAL yo'q
[ ] read_only, cap_drop: ALL, no-new-privileges
[ ] privileged yo'q, docker.sock faqat zarur joyda
[ ] Baza tashqariga ochiq emas
[ ] Sirlar — secrets yoki tashqi menejerda
[ ] Resurs chegaralari (40-dars)

Nima uchun shunday?

Nima uchun bu choralarning hammasi kerak — bittasi yetmaydimi?

Chunki xavfsizlik — chuqur himoya (defense in depth): har bir qatlam boshqasi muvaffaqiyatsiz bo'lishini nazarda tutadi. Ilovada zaiflik bo'ladi — bu muqarrar. Savol — undan keyin hujumchi qancha narsa qila oladi. Root siz foydalanuvchi — tizimni o'zgartira olmaydi; read_only — hech narsa yozolmaydi; minimal image — vositalari yo'q; izolyatsiyalangan tarmoq — bazaga yetolmaydi; sirlar faylda — env bo'sh. Har bir qatlam zararni kichraytiradi, va birgalikda "konteyner buzildi" hodisasi "butun kompaniya ma'lumotlari sizdi" ga aylanmaydi.


Xulosa

Eslab qoling:

  • USER — root emas; portlar 1024 dan yuqori
  • read_only, cap_drop: ALL, no-new-privileges; privileged — yo'q
  • docker.sock = server ustidan root
  • Sirlar image da emas: ENV, ARG, COPY .env — ❌; build uchun --mount=type=secret
  • Ishga tushirishda — Compose secrets va *_FILE
  • Skanerlash: trivy image, docker scout cves; yechim — ko'pincha asos image ni yangilash
  • Minimal image, qotirilgan versiyalar, izolyatsiyalangan tarmoq
  • Chuqur himoya — har bir qatlam muhim

Keyingi dars

40-dars: Resurslar — CPU, xotira chegaralari va restart siyosati