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:
- býr Docker til gám úr myndinni,
- keyrir
echo, - sýnir frálagið,
- stöðvar gáminn,
- 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
-iheldur staðalílagi opnu,-tbýr til sýndarskjáhermi,shkemur í 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
-dmerkir bakgrunnsham (e. detached mode),--name sofandigefur 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 .
FROMvelur grunnmynd,WORKDIRvelur vinnumöppu næstu skrefa,COPYafritar skrá inn í myndina,USERforðast að keyra þjónustuna sem rót inni í gámnum,EXPOSEskjalfestir innra tengið en birtir það ekki sjálfkrafa,CMDskilgreinir 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,
ENTRYPOINTeðaCMD,- 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:
- les byggingarskrárnar,
- byggir mynd með skýru heiti,
- birtir tengi aðeins á
127.0.0.1, - setur minni- og ferlamörk,
- skoðar annála,
- 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.