Hoppa yfir í efnið

Docker og Podman

CTF-verkefni koma oft með Dockerfile eða compose.yml. Þessar skrár segja ekki endilega hvernig við leysum verkefnið, en þær geta gert okkur kleift að ræsa nákvæmlega sama hugbúnað og höfundurinn notaði. Gámar nýtast líka til að halda sérhæfðum verkfærum og viljandi gölluðum æfingavefjum frá venjulegu vinnuumhverfi okkar.

Docker er þó ekki grunnforsenda Linux. Við tökum það fyrir hér, eftir að við höfum lært um skrár, ferli, tengi og heimildir, því gámar byggja á öllum þessum hugmyndum.

Gámur er ekki fullkominn sandkassi

Gámar deila Linux-kjarna hýsingarvélarinnar. Ótraust mynd getur innihaldið skaðlegan hugbúnað og veikleiki í kjarnanum eða gámakeyrslunni getur rofið einangrun. Keyrum óþekktar CTF-myndir helst inni í einnota sýndarvél og gefum þeim aldrei --privileged, Docker-tengilinn eða óþarfa binditengingar.

Hvað er gámur?

Hugbúnaðargámur (e. software container) er einangrað ferli ásamt skráakerfissýn, netstillingum og takmörkuðum auðlindum. Frá sjónarhóli forritsins getur umhverfið líkst lítilli sjálfstæðri Linux-vél, en í raun keyrir ferlið á sama kjarna og hýsillinn.

Hýsingarvél
├── Linux-kjarni
├── venjuleg ferli
├── gámur A: Flask + Python
└── gámur B: gagnagrunnur

Sýndarvél inniheldur yfirleitt heilan gestakjarna. Gámur er léttari vegna þess að hann deilir kjarnanum með hýsingarvélinni.

Eiginleiki Gámur Sýndarvél
Kjarni Deilir kjarna hýsils Hefur eigin gestakjarna
Ræsing Oft sekúndubrot Yfirleitt hægari
Einangrun Ferla- og nafnarýma-einangrun Sterkari vélbúnaðarmörk
Dæmigerð notkun Pakka og keyra forrit Keyra heilt annað stýrikerfi eða ótraust umhverfi

Vissir þú?

Docker fann ekki upp Linux-gáma. Það sameinaði eldri Linux-eiginleika á borð við nafnarými (e. namespaces) og cgroups í þægilegt vinnuflæði með myndum og skráningarsöfnum.

Mynd, gámur og skráningarsafn

Þrjú orð ruglast auðveldlega saman:

  • Gámamynd (e. container image) er lesanlegt sniðmát með skráakerfislögum og lýsingu á því hvernig forrit á að ræsa.
  • Gámur (e. container) er keyrandi eða stöðvað eintak af mynd.
  • Gámaskráningarsafn (e. container registry) geymir og dreifir myndum, til dæmis Docker Hub.
Dockerfile ──docker build──► mynd ──docker run──► gámur
                                  ▲
                        docker pull úr skráningarsafni

Myndir eru byggðar úr lögum. Þegar gámur keyrir fær hann lítið skrifanlegt lag ofan á myndina. Ef gámnum er eytt hverfa breytingarnar í þessu lagi nema gögn hafi verið vistuð í binditengingu eða gagnageymslu.

Merki og auðkenni

Myndarnafn getur litið svona út:

python:3.13-alpine

python er geymsluheitið og 3.13-alpine er merki (e. tag). Merkið latest merkir ekki „öruggasta“ eða „nýjasta stöðuga útgáfan“; það er aðeins nafn sem útgefandi stjórnar. Fyrir endurtakanleg verkefni veljum við skýrt útgáfumerki eða myndauðkenni.

Docker og Podman

Docker notar venjulega biðlaraskipunina docker og bakgrunnsþjón sem stjórnar myndum, netum og gámum. Podman er sambærilegt tól sem getur keyrt án miðlægs bakgrunnsþjóns og styður rótlausa gáma vel.

Margar grunnskipanir eru eins:

docker run --rm alpine:3.20 echo 'Halló úr gámi'
podman run --rm alpine:3.20 echo 'Halló úr gámi'

Dæmin hér nota Docker. Ef námsumhverfið notar Podman má oft skipta docker út fyrir podman, en ekki eru allar stillingar og Compose-útfærslur nákvæmlega eins.

Athugum uppsetninguna:

docker version
docker info

docker-hópurinn jafngildir nánast rótaraðgangi

Notandi sem má stjórna hefðbundnum Docker-þjóni getur tengt rót hýsingarvélarinnar inn í gám og breytt henni. Aðild að docker-hópnum er því ekki venjuleg lágmarksheimild. Á sameiginlegum kerfum notum við rótlausa uppsetningu eða sérhannaða þjónustu.

Fyrsta keyrslan

docker run --rm alpine:3.20 echo 'Halló úr gámi'

Ef myndin er ekki til staðbundið sækir Docker hana fyrst. Síðan:

  1. býr Docker til gám úr myndinni,
  2. keyrir echo,
  3. sýnir frálagið,
  4. stöðvar gáminn,
  5. eyðir honum vegna --rm.

Skoðum myndir sem hafa verið sóttar:

docker image ls

Gagnvirk skel

docker run --rm -it alpine:3.20 sh
  • -i heldur staðalílagi opnu,
  • -t býr til sýndarskjáhermi,
  • sh kemur í stað sjálfgefinnar skipunar myndarinnar.

Inni í gámnum getum við prófað:

cat /etc/os-release
ps
id
exit

exit stöðvar aðalferlið og þar með gáminn.

Æfing 1: skammvinnt skráakerfi

Búðu til skrá og eyddu gámnum

Ræstu Alpine-gám með --rm, búðu til /tmp/skilabod.txt og lokaðu gámnum. Ræstu síðan nýjan gám úr sömu mynd og athugaðu hvort skráin sé til.

Lausn

Fyrri gámurinn:

docker run --rm -it alpine:3.20 sh
echo 'fyrsti gámur' > /tmp/skilabod.txt
cat /tmp/skilabod.txt
exit

Nýr gámur:

docker run --rm alpine:3.20 \
  sh -c 'ls -l /tmp/skilabod.txt || echo skráin er horfin'

Nýi gámurinn fær nýtt skrifanlegt lag. Skráin var ekki hluti myndarinnar og hvarf þegar fyrri gámnum var eytt.

Gámar sem lifa lengur

Gefum gámi nafn og látum hann keyra í bakgrunni:

docker run -d --name sofandi alpine:3.20 sleep 600
  • -d merkir bakgrunnsham (e. detached mode),
  • --name sofandi gefur læsilegt nafn.

Skoðum stöðuna:

docker ps
docker ps -a
docker inspect sofandi

docker ps sýnir keyrandi gáma en docker ps -a einnig stöðvaða. inspect skilar miklum JSON-upplýsingum um stillingar gámsins.

Keyrum skipun inni í keyrandi gámi:

docker exec sofandi cat /etc/os-release
docker exec -it sofandi sh

Skoðum frálag aðalferlis:

docker logs sofandi
docker logs -f sofandi

Stöðvum og fjarlægjum:

docker stop sofandi
docker rm sofandi

Ef gámurinn var ræstur með --rm eyðir Docker honum sjálfkrafa þegar hann stöðvast.

Flytjum skrár með docker cp

docker cp afritar milli hýsils og gáms:

docker cp skra.txt sofandi:/tmp/skra.txt
docker cp sofandi:/etc/os-release ./os-release-fra-gami

Tvípunkturinn aðgreinir gámsheitið frá slóðinni, svipað og í scp.

docker cp hentar til að sækja eina niðurstöðu úr þegar keyrandi gámi. Fyrir endurtekið þróunarvinnuflæði henta binditengingar betur.

Tengi og net

Forrit inni í gámi getur hlustað á tengi án þess að það sé aðgengilegt frá hýsingarvélinni. Með -p birtum við gámatengi á hýsingarvélinni:

-p HÝSILVISTFANG:HÝSILTENGI:GÁMATENGI

Búum til litla vefsíðu:

mkdir -p ~/ctf/docker-vefur
printf '<h1>Halló úr gámi</h1>\n' > ~/ctf/docker-vefur/index.html

Ræsum Python-vefþjón í gámi:

docker run --rm --name ctf-vefur \
  -p 127.0.0.1:8080:8000 \
  --mount type=bind,src="$HOME/ctf/docker-vefur",dst=/srv,readonly \
  python:3.13-alpine \
  python -m http.server 8000 --directory /srv --bind 0.0.0.0

Í öðrum skeljaglugga:

curl http://127.0.0.1:8080/

Flæðið er:

127.0.0.1:8080 á hýsli → tengi 8000 í gámi → Python-vefþjónn

Bindum viljandi gölluð verkefni við loopback

-p 8080:8000 birtir þjónustuna venjulega á öllum netviðmótum. Notum -p 127.0.0.1:8080:8000 svo aðeins sama tölva nái til CTF-labbsins.

Æfing 2: staðfestu tengivörpun

Ræstu vefinn og finndu tengið

Ræstu dæmið að ofan í bakgrunni með -d. Notaðu docker ps, curl og docker logs til að staðfesta tengivörpun og beiðnina.

Lausn
docker run -d --rm --name ctf-vefur \
  -p 127.0.0.1:8080:8000 \
  --mount type=bind,src="$HOME/ctf/docker-vefur",dst=/srv,readonly \
  python:3.13-alpine \
  python -m http.server 8000 --directory /srv --bind 0.0.0.0

docker ps
curl http://127.0.0.1:8080/
docker logs ctf-vefur
docker stop ctf-vefur

Binditengingar og gagnageymslur

Binditenging (e. bind mount) birtir tiltekna skrá eða möppu hýsils inni í gámi. Gámurinn sér þá sömu gögn og hýsillinn.

--mount type=bind,src="$PWD/gogn",dst=/data,readonly

readonly eða :ro hindrar venjuleg skrif úr gámnum. Án þess getur ferli í gámnum breytt eða eytt hýsilskrám sem notandinn hefur aðgang að.

Gagnageymsla gáms (e. container volume) er hins vegar geymd og nafngreind af gámakerfinu:

docker volume create ctf-gogn
docker run --rm \
  --mount type=volume,src=ctf-gogn,dst=/data \
  alpine:3.20 sh -c 'date > /data/timi.txt'
docker run --rm \
  --mount type=volume,src=ctf-gogn,dst=/data,readonly \
  alpine:3.20 cat /data/timi.txt

Gámunum er eytt en gagnageymslan lifir áfram þar til við fjarlægjum hana:

docker volume rm ctf-gogn

Í CTF-vinnu notum við binditengingu í lesham þegar verkfæri þarf að lesa handout-skrá úr vinnumöppunni okkar.

Æfing 3: gögn lifa lengur en gámurinn

Prófaðu nafngreinda gagnageymslu

Búðu til gagnageymsluna docker-aefing, skrifaðu skrá í hana úr skammvinnum Alpine-gámi og lestu skrána úr öðrum gámi í lesham. Fjarlægðu síðan gagnageymsluna.

Lausn
docker volume create docker-aefing

docker run --rm \
  --mount type=volume,src=docker-aefing,dst=/data \
  alpine:3.20 sh -c 'echo varanleg gögn > /data/skilabod.txt'

docker run --rm \
  --mount type=volume,src=docker-aefing,dst=/data,readonly \
  alpine:3.20 cat /data/skilabod.txt

docker volume rm docker-aefing

Fyrri gámnum er eytt með --rm, en skráin helst í gagnageymslunni þar til við fjarlægjum hana sérstaklega.

Byggjum eigin mynd

Dockerfile er textaskrá með byggingarskrefum. Búum til tóma möppu:

mkdir -p ~/ctf/docker-smidi
cd ~/ctf/docker-smidi
printf '<h1>gg{docker_smidi}</h1>\n' > index.html

Dockerfile:

FROM python:3.13-alpine

WORKDIR /app
COPY index.html /app/index.html

USER 1000:1000
EXPOSE 8000

CMD ["python", "-m", "http.server", "8000", "--bind", "0.0.0.0"]

Byggjum myndina:

docker build -t ctf-static:1 .
  • FROM velur grunnmynd,
  • WORKDIR velur vinnumöppu næstu skrefa,
  • COPY afritar skrá inn í myndina,
  • USER forðast að keyra þjónustuna sem rót inni í gámnum,
  • EXPOSE skjalfestir innra tengið en birtir það ekki sjálfkrafa,
  • CMD skilgreinir sjálfgefna aðalferlið.

Keyrum:

docker run --rm \
  -p 127.0.0.1:8080:8000 \
  ctf-static:1

Byggingarsamhengið

Punkturinn aftast í docker build ... . er byggingarsamhengið (e. build context). Docker getur sent skrár úr því til byggingarinnar. Notum .dockerignore til að útiloka til dæmis .git/, sýndarumhverfi, stór gögn og leyndarmál:

.git
.venv
__pycache__
*.key

Æfing 4: breyttu myndinni

Ný útgáfa

Breyttu fyrirsögninni í index.html, byggðu myndina sem ctf-static:2 og staðfestu að nýi textinn birtist. Berðu síðan saman docker image ls ctf-static.

Lausn
printf '<h1>Önnur útgáfa</h1>\n' > index.html
docker build -t ctf-static:2 .
docker image ls ctf-static
docker run --rm -p 127.0.0.1:8080:8000 ctf-static:2

Í öðrum glugga:

curl http://127.0.0.1:8080/

Docker Compose

Compose lýsir einum eða fleiri gámum í YAML-skrá. Fyrir eina litla þjónustu er docker run oft skýrara, en Compose nýtist þegar CTF-labb hefur vef og gagnagrunn eða margar stillingar.

compose.yml fyrir vefinn okkar:

services:
  web:
    build: .
    ports:
      - "127.0.0.1:8080:8000"
    read_only: true
    cap_drop:
      - ALL
    security_opt:
      - no-new-privileges:true

Ræsum í forgrunni:

docker compose up --build

Compose sýnir þá annála beint. Við stöðvum með Ctrl+C. Eða ræsum í bakgrunni:

docker compose up --build -d
docker compose ps
docker compose logs

Stöðvum og fjarlægjum gáma og sjálfgefið net verkefnisins:

docker compose down

down eyðir ekki sjálfkrafa öllum nafngreindum gagnageymslum. Stillingin --volumes gerir það og getur því eytt gögnum.

Auðlindamörk

Ótraust eða gallað forrit getur notað allt minni, búið til of mörg ferli eða haldið örgjörva uppteknum. Við getum sett einföld mörk:

docker run --rm \
  --memory 256m \
  --cpus 1 \
  --pids-limit 128 \
  alpine:3.20 sh -c 'echo innan marka'

Þessi mörk gera gám ekki öruggan, en draga úr sumum rekstraráhrifum.

Að skoða CTF-verkefni

Ef verkefni afhendir Dockerfile byrjum við á að lesa það:

less Dockerfile
find . -maxdepth 2 -type f -print

Við leitum að:

  • grunnmynd og útgáfu,
  • skrám sem eru afritaðar inn,
  • uppsettum pökkum,
  • umhverfisbreytum,
  • notanda og vinnumöppu,
  • ENTRYPOINT eða CMD,
  • tengjum sem þjónustan notar.

Bygging getur keyrt RUN-skipanir úr Dockerfile og sótt kóða af netinu. Keyrsla getur síðan haft netaðgang og aðgang að binditengdum skrám. Við byggjum því ekki óþekkt verkefni blint á mikilvægri hýsingarvél.

Stillingar sem rjúfa meginvörnina

Forðumst sérstaklega:

--privileged
--network host
-v /:/host
-v /var/run/docker.sock:/var/run/docker.sock
--device /dev/...

Docker-tengillinn veitir nánast fulla stjórn á hýsils-Docker og þar með oft hýsingarvélinni. --privileged opnar flest tækja- og kjarnamörk. Notum slíkt aðeins í sérhönnuðu einnota rannsóknarumhverfi þegar við skiljum nákvæmlega þörfina.

Hreinsun

Skoðum fyrst hvað við eigum:

docker ps -a
docker image ls
docker volume ls
docker system df

Fjarlægjum nafngreinda hluti sem við bjuggum til:

docker rm nafn-a-gami
docker image rm ctf-static:1 ctf-static:2
docker volume rm ctf-gogn

docker system prune fjarlægir ónotaðar auðlindir í einu:

docker system prune

Ekki hreinsa í blindni

Á sameiginlegri þróunarvél geta ónotaðar myndir og stöðvaðir gámar tilheyrt öðrum verkefnum. docker system prune -a --volumes er sérstaklega víðtæk skipun og getur eytt myndum og gagnageymslum sem erfitt er að endurheimta. Skoðum listana og fjarlægjum okkar eigin hluti með nafni.

Lokaverkefni: ræsum labb með varfærnum hætti

Frá Dockerfile að hreinsun

Þú færð staðbundið CTF-labb með Dockerfile. Skrifaðu vinnuferli sem:

  1. les byggingarskrárnar,
  2. byggir mynd með skýru heiti,
  3. birtir tengi aðeins á 127.0.0.1,
  4. setur minni- og ferlamörk,
  5. skoðar annála,
  6. stöðvar og fjarlægir gáminn.
Möguleg lausn

Fyrst skoðum við inntakið:

find . -maxdepth 2 -type f -print
less Dockerfile

Ef byggingarskref, grunnmynd og skrár eru innan umfangs:

docker build -t ctf-labb:local .
docker run -d --rm --name ctf-labb \
  -p 127.0.0.1:8080:8000 \
  --memory 256m --cpus 1 --pids-limit 128 \
  ctf-labb:local

docker ps
docker logs -f ctf-labb

Eftir æfinguna:

docker stop ctf-labb
docker image rm ctf-labb:local

Við notum hvorki --privileged né binditengingar því verkefnið krafðist þeirra ekki.

Samantekt

Í þessum kafla lærðum við að:

  • greina gám frá sýndarvél,
  • greina gámamynd, gám og skráningarsafn,
  • keyra skammvinna og gagnvirka gáma,
  • skoða ferli, annála og stillingar,
  • afrita skrár með docker cp,
  • birta gámatengi aðeins á loopback,
  • varðveita gögn með binditengingu eða gagnageymslu,
  • byggja litla mynd úr Dockerfile,
  • ræsa skilgreint labb með Docker Compose,
  • setja einföld auðlindamörk,
  • þekkja hættulegar stillingar og ótraustar myndir,
  • hreinsa aðeins þær auðlindir sem við ætlum að fjarlægja.

Næst skoðum við Git og útgáfustjórnun: hvernig við sækjum verkefni, berum saman breytingar og leitum í útgáfusögu án þess að keyra ótraustan kóða.