# Container & Vitualization ## Task 3 Description In this task, you will learn about container and virtualization in Linux Enterprise. **What you should learn are in below:** * Setup docker or podman to your environment * Learn about basic command in podman or docker * Try to setup example container in your environment * Create container image using Dockerfile (setup any application container as you wish * Create local repository and upload your image to it * Give privilege to your application container * Try to manage storage in your container * Try to run your container in systemd * Create 2 container in different bridge interface and different IP Segment that can access each other * Learn about the distinction between System Container and Application Container `Write your learning activity to HackMD` ## System Design ![System Design Testt-Copy of Task 2.drawio](https://hackmd.io/_uploads/ryHVdMDUp.svg) ## Podman : [Pre-Requisite](https://medium.com/@amazingandyyy/introduction-to-network-namespaces-and-virtual-ethernet-veth-devices-304e0c02d084) ### Podman Local Instalation 🧑‍💻 1. Download and configure podman ![image](https://hackmd.io/_uploads/HyA0D1RE6.png) ## Podman : [Basic Command](https://medium.com/@amazingandyyy/introduction-to-network-namespaces-and-virtual-ethernet-veth-devices-304e0c02d084) ### Running Container 🎉 1. **Playing with Image Repository** * Pull nginx docker images ![image](https://hackmd.io/_uploads/SknoFy0Va.png) As you can see from here, we've created the linux namespace. * Listing podman images ![image](https://hackmd.io/_uploads/rkkZ6k0Ep.png) As you can see from here, we've created the linux namespace. * Run the nginx container ![image](https://hackmd.io/_uploads/r1m3qJ04a.png) ![image](https://hackmd.io/_uploads/HJ7o5yANp.png) * Listing podman container ![image](https://hackmd.io/_uploads/SknoFy0Va.png) As you can see from here, we've created the linux namespace. ### Image Creation 🎉 1. **Setup Local Repository** * Set Local Repo (With Volume) ```=bash podman container run -dt -p 5000:5000 --name rafli-regis --volume rafli-volume:/var/lib/registry:Z docker.io/library/registry:2 ``` As you can see from here the output from the creation. ```=output CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 01c9b2bfa441 docker.io/library/registry:2 /etc/docker/regis... 37 seconds ago Up 37 seconds ago 0.0.0.0:5000->5000/tcp rafli-regis ``` Check The volume inspect output ```=output [ { "Name": "rafli-volume", "Driver": "local", "Mountpoint": "/var/lib/containers/storage/volumes/rafli-volume/_data", "CreatedAt": "2023-12-05T06:40:56.539301178Z", "Labels": {}, "Scope": "local", "Options": {} } ] ``` * Test if the local repo is properly configured **Test with the alpine image** ```=bash podman image pull docker.io/library/alpine ``` ```=output [root@rafli]# podman image pull docker.io/library/alpine Trying to pull docker.io/library/alpine:latest... Getting image source signatures Copying blob c926b61bad3b skipped: already exists Copying config b541f20801 done Writing manifest to image destination Storing signatures b541f2080109ab7b6bf2c06b28184fb750cdd17836c809211127717f48809858 ``` **Change the label name to the local repo with the port** ```=bash podman image tag docker.io/library/alpine:latest localhost:5000/alpine:latest ``` ```=output REPOSITORY TAG IMAGE ID CREATED SIZE docker.io/library/registry 2 909c3ff012b7 4 days ago 26 MB docker.io/library/alpine latest b541f2080109 4 days ago 7.63 MB localhost:5000/alpine latest b541f2080109 4 days ago 7.63 MB ``` **Upload to the local registry** ```=bash podman image push localhost:5000/alpine:latest --tls-verify=false ``` ```=output Getting image source signatures Copying blob 9fe9a137fd00 done Copying config b541f20801 done Writing manifest to image destination Storing signatures ``` 2. **Containerized Existing App with Dockerfile** * Clone the repository in my case `wheater app` ```=bash git clone https://github.com/Sameerk22/City-Weather-App cd City-Weather-App ``` * Create a Containerfile ```=bash #Build a weather app FROM node:lts-alpine WORKDIR /usr/local/app COPY package.json package-lock.json ./ RUN npm install COPY public ./public COPY src ./src # RUN npm run build CMD ["npm", "run"] ``` * Build the Containerfile with custom name ```=bash podman build -t localhost:5000/wheather-app:1.0 . ``` * Push the image to the local registry ```=bash podman image push localhost:5000/wheather-app:1.0 --tls-verify=false ``` * Verify if the image is being uploaded to local registry ```=bash podman image search localhost:5000/ ``` ```=output INDEX NAME DESCRIPTION STARS OFFICIAL AUTOMATED localhost:5000 localhost:5000/alpine 0 localhost:5000 localhost:5000/wheather-app 0 ``` 3. **Managed Storage** By default all files created inside a container are stored on a writable container layer. This means that: * The data doesn't persist when that container no longer exists, and it can be difficult to get the data out of the container if another process needs it. * There are two options for containers to store files in the host machine so that the files are persisted even after the container stops: **volumes** and **bind mounts**. 1. Using podman - **bind mounts** * Create a directory with files innit ```=bash sudo mkdir /bindhost sudo sh -c "echo Hello from the HOST! > /bindhost/hello" ``` * Create a container with bind mount to the `/bindhost` ```=bash podman run -d -it --name bindtest --mount type=bind,source=/bindhost,target=/shared nginx ``` * Checks whether the contents of the container are the same as the bind mount path of the host ```=bash podman exec -it bindtest cat /shared/hello ``` * Host Storage related with Container Storage ```=bash sudo sh -c "echo Hello from the HOST the second time! > /bindhost/hello2" docker exec -it bindtest cat /shared/hello2 ``` * Share the Host Storage to Other Container Storage ```=bash podman run -d -it --name bindtest-2 --mount type=bind,source=/bindhost,target=/bersama nginx ``` ```=bash docker inspect bindtest-2 -f '{{json .Mounts }}'| python3 -m json.tool ``` ```=bash docker exec -it bindtest-2 ls -l /bersama ``` 2. Using podman - **volumes** * Create Volume with custom name ```=bash podman volume create volume-1 ``` * Build the Container with Docker Volume RO Configuration ```=bash podman run -d -it --name volumetest-2 --mount source=volume-1,target=/app,readonly nginx ``` * Inspect if the configuration is done ```=bash docker inspect voltest3 -f '{{json .Mounts }}' | python3 -m json.tool ``` 4. **Priviledge Container** Executing container engines with the --privileged flag tells the engine to launch the container process without any further "security" lockdown. But it comes with the risk: * **Container Escalation**: Privileged containers can escalate their access to the host system, potentially gaining unauthorized control. * **Resource Exhaustion**: Privileged containers can consume host resources without any restrictions, potentially causing resource exhaustion or denial-of-service attacks. * **Data Exposure**: Privileged containers can access sensitive data on the host system, potentially leading to data breaches or exposure. There are serveral impact to the list down system: * Read-only kernel file systems `Without Priviledge` ```=bash podman run fedora mount | grep /proc.*tmpfs ``` ```=output Resolved "fedora" as an alias (/etc/containers/registries.conf.d/shortnames.conf) Trying to pull registry.fedoraproject.org/fedora:latest... Getting image source signatures Copying blob 718a00fe3212 done Copying config 368a084ba1 done Writing manifest to image destination Storing signatures sysfs on /sys type sysfs (ro,nosuid,nodev,noexec,relatime) cgroup2 on /sys/fs/cgroup type cgroup2 (ro,nosuid,nodev,noexec,relatime) tmpfs on /proc/acpi type tmpfs (ro,relatime,size=0k,uid=1002,gid=1002,inode64) tmpfs on /proc/scsi type tmpfs (ro,relatime,size=0k,uid=1002,gid=1002,inode64) tmpfs on /sys/firmware type tmpfs (ro,relatime,size=0k,uid=1002,gid=1002,inode64) tmpfs on /sys/dev/block type tmpfs (ro,relatime,size=0k,uid=1002,gid=1002,inode64) proc on /proc/bus type proc (ro,relatime) proc on /proc/fs type proc (ro,relatime) proc on /proc/irq type proc (ro,relatime) proc on /proc/sys type proc (ro,relatime) proc on /proc/sysrq-trigger type proc (ro,relatime) ``` `With Priviledge` ```=bash podman run --privileged fedora mount | grep /proc.*tmpfs ``` None of the kernel file systems are mounted read-only in --privileged mode. Usually, this is required to allow processes inside of the container to actually modify the kernel through the kernel file system. * Linux capabilities `Without Priviledge` ```=bash podman run -d fedora sleep 100 podman top -l capeff ``` ```=output EFFECTIVE CAPS CHOWN,DAC_OVERRIDE,FOWNER,FSETID,KILL,NET_BIND_SERVICE,SETFCAP,SETGID,SETPCAP,SETUID,SYS_CHR ``` `With Priviledge` ```=bash podman run --privileged -d fedora sleep 100 ``` ```=output EFFECTIVE CAPS AUDIT_CONTROL,AUDIT_READ,AUDIT_WRITE,BLOCK_SUSPEND,CHOWN,DAC_OVERRIDE,DAC_READ_SEARCH,FOWNER,FSETID,IPC_LOCK,IPC_OWNER,KILL,LEASE,LINUX_IMMUTABLE,MAC_ADMIN,MAC_OVERRIDE,MKNOD,NET_ADMIN,NET_BIND_SERVICE,NET_BROADCAST,NET_RAW,SETFCAP,SETGID,SETPCAP,SETUID,SYSLOG,SYS_ADMIN,SYS_BOOT,SYS_CHROOT,SYS_MODULE,SYS_NICE,SYS_PACCT,SYS_PTRACE,SYS_RAWIO,SYS_RESOURCE,SYS_TIME,SYS_TTY_CONFIG,WAKE_ALARM,unknown,unknown,unknown ``` When you launch a container with --privileged mode, the container launches with almost of full list capabilities. * Syscall filtering - SECCOMP `Without Priviledge` ```=bash podman run -d fedora sleep 100 podman top -l seccomp ``` ```=output SECCOMP filter ``` `With Priviledge` ```=bash podman run --privileged -d fedora sleep 100 podman top -l seccomp ``` ```=output SECCOMP disabled ``` 5. **Container with Proxy Access** * Configuring and deploying Example Webapp ```=bash podman pull docker.io/library/httpd podman pull docker.io/library/nginx mkdir syscom sysorg ``` * Configuring `index.html` ```=bash $ cat << EOF > ./syscom/index.html <html> <header> <title>SysAdmin.com</title> </header> <body> <p>This is the SysAdmin website hosted on the .com domain</p> </body> </html> EOF $ cat << EOF > ./sysorg/index.html <html> <header> <title>SysAdmin.org</title> </header> <body> <p>This is the SysAdmin website hosted on the .org domain</p> </body> </html> EOF ``` * Configure the proxy ```=bash mkdir nginx touch nginx/default.conf nginx/syscom.conf nginx/sysorg.conf ``` `default.conf` ```= server { listen 80; listen [::]:80; server_name localhost; location / { root /usr/share/nginx/html; index index.html index.htm; } error_page 500 502 503 504 /50x.html; location = /50x.html { root /usr/share/nginx/html; } } ``` `syscom.conf` ```= server { listen 80; server_name sysadmin.com; location / { proxy_pass http://192.168.1.53:8080; } } ``` `sysorg.conf` ```= server { listen 80; server_name sysadmin.org; location / { proxy_pass http://192.168.1.53:8081; } } ``` * Run in proxy configuration in podman ```=bash podman run --name=nginx --privileged -p 80:80 -v /home/analyst/nginx:/etc/nginx/conf.d:Z -d docker.io/library/nginx ``` * Validation time with `curl` to the domain ![image](https://hackmd.io/_uploads/Hym7co6Sp.png) * Validation time with the browser ![image](https://hackmd.io/_uploads/r1Bucoar6.png) ### Container Networking 🎉 1. **Access your Nginx container on VM 1 from Host Machine via browser** ![image](https://hackmd.io/_uploads/rJh0BMD8a.png) 2. **Access your Nginx container from Namespace of VM 2 using `curl`** ![image](https://hackmd.io/_uploads/H1of8Mw8p.png)