📱

Get Our Mobile App

Take your business learning on the go!

Download on the App StoreGet it on Google Play

Контейнеризация и безопасность: почему, зачем и как? // Курс «DevOps практики и инструменты»

OTUS IT Онлайн - образование1:23:08

Transcription

So, good evening everyone. Let's start our webinar today. Today we will talk about security in the context of using containers. Let's make sure that our audiovisual support is all good. If you can hear me, if the presentation is visible, if everything is okay with us, please put a plus sign in the chat so I know we can start. Great. I see a plus. So, everything is fine with us. At least, it's visible what I'm showing, and it's audible what I'm saying. Therefore, allow me to introduce myself. My name is Chugunov Mikhail. Currently, I have over 7 years of experience in business process automation and implementing DevOps practices at various business levels. Additionally, I have about 3 years of experience in data analysis and automating ETL and ELT processes. So, let's get acquainted with the rules of our webinar. I expect active participation and questions in the chat. I want to note that I see the questions in the chat almost immediately, but unfortunately, I won't be able to answer them instantly. Perhaps the answer will be given during my presentation, or I will have to take a specific pause, and during a logical pause, we will address the questions that have arisen. And, of course, a recording of the webinar will be sent to your email so you can rewatch it if you want to repeat or refresh something. We've met me, now let's get acquainted with you. Please write, what is your current job title, if it's not confidential, and what is your main goal for coming to our session today. Uh-huh. Thank you for your answers. I see that for the most part, consequently, our conversation today will be a bit, let's say, leaning towards development, although perhaps not only about development. So, before we begin, let's say a few words about the company OTUS, thanks to which we are all gathered here today. And what's worth noting is that OTUS is a team of authors, mostly, who create online courses for IT specialists of various levels, from juniors to seniors and leads, respectively. At the same time, one way or another, when developing educational programs, we focus on the real needs of the labor market. And if we see that a certain technology is starting to fade away, and another technology, on the contrary, is starting to, let's say, appear in terms of buzz, in terms of company requests, and is becoming more popular, then, naturally, it is introduced, and the emphasis, consequently, may shift towards it. Furthermore, OTUS is a company with an educational license, and this can be quite a good advantage for some. Why? Because in addition to, of course, diplomas of professional retraining and certificates of advanced training, you can also get a tax deduction. And a tax deduction can be a pleasant bonus for many. Currently, there are over 130 courses available on various topics, from classics, architecture, programming, security, infrastructure, testing to something more specific, such as analysis and analytics, because it is still a bit non-standard, as well as GameDev, data science, or with an emphasis, for example, on some DevOps practices, not only in general but also those that are useful, say, for DevOps leads. Well, not only for DevOps in general. That is, as a manager, as a manager is a somewhat peculiar figure. It can differ. And again, we know how to present the description of what makes this role special. And, of course, Java, Python, Kubernetes, SRE, and everything that is in demand in the market, one way or another, is also present with us. We have met me, you have met me, I have met you, we have talked about OTUS, and now we can move on to our main goal. To talk about security in the context of containerization, why it is needed, how it is implemented, and why we need to use all this in principle. I think, primarily, let's familiarize ourselves with our route today. How did it happen? Yes, not that it's fashionable, it seemed to me that it's always a certain moment. We will start a little from afar and talk about the history of containerization. Why will this be useful to us? In order to understand how many years it took, one way or another, to introduce certain practices and at what, let's say, stage of containerization development, in which part of the era of containerization development we are currently. We will separately talk about how security is ensured within containers, basic and, let's say, advanced. And we will consider useful practices, best practices, and tips that we can, one way or another, highlight with my colleagues, which, of course, are worth using. Furthermore, we will delve a little deeper and talk about the course within which our session today is taking place. And at the end, of course, we will reflect. What are our goals today? We have three main goals today. We want to learn the history of the development of security in containers, consider the specifics of security within Docker. Why? Yes, probably because Docker is still one of the most popular tools for working with containers to this day, no matter what anyone says. And, of course, to get acquainted with best practices. And why do we need all this? Because we want, one way or another, to create secure solutions that will subsequently be used for, well, of course, your projects. And we will start a little, of course, with history. The first thing I would like to highlight is what containerization is. Let's start with a definition, well, as they like to do in smart books. And containerization, in fact, if we consider it globally, is a method of virtualization. What is its difference? It is that the operating system kernel supports several isolated instances of user space simultaneously, and this has always been emphasized in terms of transitioning to using containers instead of one, user namespaces. And if we visualize this in comparison with traditional virtualization, yes, indeed, when we talk about regular virtual machines, we have our infrastructure, there is a rather heavy hypervisor, even if not heavy, we have a guest operating system that carries everything with it. Well, emulation of processor operation, emulation of memory operation, input-output systems, both disk subsystem and, of course, network. Again, an attempt to isolate ourselves as much as possible from what is happening nearby. Even if it's one large piece of hardware, virtual machines still live as if separately. And root on one cannot be root on another, and that's normal. In the case of containers, we understand that this huge layer of the guest operating system takes up too much space. And resources have always been expensive, from the 2000s to, of course, 2025. And therefore, at some point, it was decided to abandon the use of a hypervisor with a guest operating system with full virtualization of everything required for operation. And it was decided to leave only our hardware, the native operating system, orchestration, some orchestrator if necessary, or some containerization and management system, and then run our applications. The advantages are obvious, consequently, the advantages are that we free up a considerable amount of space and resources in general to run more identical applications on one host. And this idea is not new. Consequently, as we understand, containerization is a word that appeared quite a long time ago. And the history of containerization, consequently, goes back a little more than fully to freight transportation. And if I'm not mistaken, in the second half of the 20th century, I think in 1956, the first voyage using standardized containers took place. Consequently, before that, everything was transported in any way, and it was quite difficult and costly to calculate, just like with virtual machines. That is, we have a virtual machine, it has a certain amount of resources to deploy it. We have a certain amount of CPU, RAM, and disk that we provide to it. What's inside is not very clear. How many such virtual machines will fit is also not entirely clear, let's say. And this was somewhat similar to that mess when someone had barrels, someone had sacks, someone had boxes, someone had cages with chickens, and so on. And in IT, naturally, one way or another, things are adopted from good practices of the modern world. And, if I'm not mistaken, after 20-something years, around the 1980s or in 1978, that is, somewhere around there from '78 to '80, a thing called chroot appeared in Unix systems. This is a system call that allowed us to isolate the file system within our operating system. And this is, naturally, good. And if we look at mentions of containers, well, almost in the format we imagine them, then, consequently, the first mention was somewhere in 2004. At the same time, it is worth noting that 2 years before that, in 2002, namespaces were added to the Linux kernel, which provided a sufficiently good level of file system isolation. In 2004, the first containers appeared. In 2006, everything is quite close and nearby, the Cgroups function appeared in the Linux kernel to limit the resources used by processes. And the first containerization, specifically Linux containers, was released in 2008 and was called LXC. What was their problem? Let's just for interest. If you know, you can write. If you don't know, you can put a minus so I understand how deeply we want to delve into this rabbit hole of cybersecurity problems. Uh-huh. Great. So, let's start a little further back. Between 2004 and 2008, there was a technology called OpenVZ, which was considered relatively secure, but it was not very comfortable and convenient to use. In 2008, LXC appeared. What is special about LXC? Initially, LXC did not support a sufficiently high level of isolation. And for example, until 2014, when the Linux kernel version 3.8 was released, there was a problem that seems silly now, but then it was a global and quite dangerous problem. The root user in the container could execute any code on our parent system, on our host system. That is, we are trying to isolate ourselves, but we think we are isolated, everything is fine, let's give the user root privileges to make our lives as easy as possible. Yes, it seems cool. On the other hand, this same root user inside a seemingly isolated container executes something, some code, and we shoot ourselves in the foot. Why? Because this code can be executed directly on our host machine. Why did this happen? It's quite simple, because the root UID is zero, both in the container and in the system. Well, simply because, consequently, that's how it's done. And this problem was fixed. And, consequently, these root escapes were somewhat resolved only in 2014, when LXC 1.0 was released. How? By creating unprivileged containers, namely containers where UID 0 corresponds not to root, but to an unprivileged user. And from the outside, it only has rights to its own set of resources. And additionally, certain access settings and filtering were introduced using Linux kernel tools. We will talk about this later. It is worth noting that Docker appeared in 2013. Docker basically used LXC. Why? Because, in fact, if we dig very deep, then in 2008 it appeared alongside LXC, well, based on LXC, as a development of a company, I think it was called Dotcloud. So, what was the main idea? They wanted to create a PaaS platform with support for various programming languages. That is, you use any language you like, Java, Python, PHP, Node.js, it doesn't matter. You have a platform, you use this language, you are isolated, you are not limited by what is installed on the virtual machine, and at the same time, you don't have to pay for a large virtual machine. You can pay for a certain small space. And at some point, consequently, this evolved into a full-fledged Docker. In 2016, Docker version 1.10 was released. And this was a key moment. Why? Because in 2016, that is, in 2008, LXC appeared, in 2013 Docker appeared separately, in 2014 we learned not to escape as root users, and in 2016 Docker released its own separate engine so as not to interact with LXC. Well, it fully released it and completely abandoned LXC. Why? Because in December 2015, containerd was introduced. More precisely, containerd was not introduced, but a daemon called containerd was introduced, which is a Docker development. Let's add some additional things here, probably. It's not very convenient to write, but, let's say, containerd, but since it was introduced in December, that is, 12/2015, it is considered that Docker fully migrated in 2016. And, I think, in 2017, that is, here containerd begins to separate from Docker as a separate engine, and the guys from CNCF took it. So, let's try to write this CNCF. So, it took us 6 years. Well, I'm not saying it's bad, simply because in 6 years, consequently, the problem of privileged containers was solved. In about 2 years, well, in 3 years, their own engine was created. And literally a year after separating from LXC, the engine went out on its own and is still used today. In 2018, the guys from, well, a quite well-known company, or rather, the company itself, which, consequently, promotes Podman, it is, if I'm not mistaken, initially the guys from, what were they called, from CoreOS, but since CoreOS is a partial history, Red Hat. We will talk about Red Hat. Consequently, Red Hat had its own engine on which Kubernetes worked, which, along with Docker, was in the very, very, very earliest, let's say, form, but at some point, consequently, they buried that engine, it was called rkt, and released Podman. What was special about Podman compared to LXC and Docker? Podman initially declared two things as its killer features. First, rootless mode. Second, the ability to run without a daemon, unlike Docker. Moreover, the very first Podman, version, I think, was 0.2, which was released in 2018, initially declared this. What is the advantage? Well, it's an increase in security, because we are not running containers as root, we are not communicating with our kernel through root, we are also not using root internally, which means our context is quite limited, and security is increased as a result. Very much so, I think, they emphasized that in multi-user environments, when you can have a number of users on one virtual machine, you feel a bit safer when using Podman. And for comparison, that is, in 2018, Podman was released, which can do rootless. In 2014, LXC somehow moved away from rootless a bit, but it still remained. And Docker, which had already managed to use LXC, release its containerd, detach this containerd, almost give it to CNCF for development. It only reached this in version 20.10. And this happened, consequently, in 2020. Well, as there is no specific year officially, and it is not indicated anywhere when it was released, when it was released. Why? Because, consequently, it is not a separate product, it is one of the operating modes. It just so happened that in December, I think they like December 2020, in version 20.10, this operating mode officially started to be used. Before that, it was a beta testing option, and people could also get acquainted with it earlier. Most likely, somewhere near Podman. But officially for everyone, Rootless Docker is from 2020. Why? Because, consequently, official support, the ability to ensure security, and in general, consequently, here we are. So, we now have three container engines: Podman, which, in addition to working with containers, supports working with pods, thus helping us prepare for Kubernetes and is fully compatible with Docker. We have Docker, which has learned and is already quite stably capable of running in rootless mode, thus again, increased security. And LXC, which in version 2.0. We won't mention it now, because LXC is not such a popular story anyway, which actively uses all this. That is, and here we are, let's note, here we are, 2025, we have at least two engines that can run in rootless format and provide us with some level of security. But running without root, however cool it may be, however secure it may be initially, it is still a story that can put us in a difficult situation at some point. That is, some additional restrictions, some additional security measures, we still want to apply. So, what do we have in our arsenal? We have three main mechanisms that are directly used when working with container engines: these are namespaces, cgroups, and Linux capabilities. If namespaces and cgroups are more or less clear, that is, one limits the context, the others, consequently, teach us how to use resources correctly. There are also network namespaces, but we'll consider them as regular namespaces. Then, the correct use of Linux Capabilities directly ensures an increased level of security that we can provide ourselves. Well, let's talk in more detail about container security. Why? Because since ancient times, let's say, from around 2015, well, 2016, that is, when Docker was released, when it moved away from LXC, when containerd started to be released, and all that, all that was swirling in my head anyway. Consequently, the story is still leaky. There have been a lot of articles over 6 years, almost 7 years. From 2018 to 2025, I specifically remember about it being insecure. Switch to Podman, switch to LXC, switch to something else. Why? Because it wasn't the tool's leakiness that was the primary issue, but the inability, let's say, to use it correctly. Why? Because both security professionals, and DevOps, or as it's fashionable to say now, DevSecOps engineers, and everyone else, thought about how to teach everyone to work with it correctly. Because people, beings, will tell us: "We've always done it this way, why bother? Why do something different?" This is not good, it's unpleasant, it's something that needs to be eradicated from their thinking with fire and sword. Why? Because if you look at reports from companies like Snyk, which specialize in cybersecurity, and reports from Red Hat, which has invested a lot in the history of containerization and orchestration, simply their Podman, their OpenShift, which is considered an extremely secure and very strict platform regarding sloppiness. They indicated that, I think, 44% of, well, I haven't looked at this year's reports, I looked at last year's reports. But more or less around 44% of all containers, all those in commercial use, well, to which they have access and from which they can obtain information, unfortunately, contain certain vulnerabilities. And despite the fact that there are updates to base images, which we will talk about, that there are some good practices, not the best, just good, still 44% of all existing containers, and perhaps more at the moment, are subject to one vulnerability or another. And unfortunately, the threats are not limited to the easily detectable shortcomings. Why? Because malicious hackers are constantly looking for new attack vectors on our systems. And most systems are now built on the Kubernetes plus Docker combination. Someone will object: "What Docker? Wake up, we have containerd, we have CRI-O." But containerd is actually developed by Docker. And the communication, in fact, with the kernel by containerd is exactly the same as by Docker. Docker just communicates with containerd first. Therefore, the level of security and the security problem are actually more or less the same. If we look at the sensational incidents in 2024, then, well, from February to August, something could be highlighted, to be precise. Why, by the way, I mentioned Red Hat earlier. In February, in February, Red Hat guys suffered. To be precise, certain, let's say, vulnerabilities were found in OpenShift, to be precise, its API. Yes, of course, Andrey, there will be a recording. I mentioned that at the very beginning. It's a plus, it will be sent to your email. So, in February 2024, a certain number of vulnerabilities were found in the OpenShift API. The vulnerabilities were not reported as very large, but I think Red Hat reported that more than 500 clients whose confidential information could have been stolen. Well, at least according to semi-official sources. Most importantly, Red Hat reported financial losses. And these vulnerabilities and these 500 clients cost, I think, around 5 million dollars. What happened? Consequently, additional checks, security patches, and strengthening of security measures in general when working with OpenShift. Consequently, OpenShift, as I said, is considered one of the most secure, at least out-of-the-box orchestration platforms. You can make it secure, but, consequently, OpenShift gives us this almost immediately. What happened in March? In March, the losses were, I think, around 3 million. Yes, it's still a lot. What happened? Access to private repositories was gained through a certain vulnerability in container technology. Among the problems, consequently, I think I'll try to recall the exact figure now. I probably won't say the exact figure. I will say that more than 2,000 images were compromised, and some malicious libraries were accidentally included in them, and sensitive data was stolen. The Docker company, consequently, estimated the losses, as I already said, at about 3 million dollars. And yes, indeed, they significantly improved the security process, conducted internal investigations, as they said, and implemented stricter access controls and, let's say, multi-layered protection for private repositories, at least. In April, more serious players were affected. It would seem that Docker is almost the founding fathers of containerization. Red Hat. Founding fathers of OpenShift, rkt, Podman, and in general, guys who invest a huge amount of money and effort into containerization. Who could be bigger? Microsoft could be bigger. And in April, they had a data leak problem, again, unfortunately, related to containers on their platform, the Microsoft platform. The number of affected users was stated to be over a million. And the damage was quite serious, I think, the largest of what we are talking about, to be precise, about 10 million dollars, perhaps even more than 10 million dollars. And, consequently, this became a certain impetus, again, for working on security. In June, it was not as significant in terms of money, only about 2.5 million on the one hand. On the other hand, guys whom we, well, probably actively use, were also affected, to be precise. It was guys from GitHub. And due to a vulnerability in containerized applications, some confidential information leaked. I think it was over 3,000 different projects, and besides the 2.5 million dollars, there were also significant reputational risks, increased security, vulnerability detection mechanisms, and, consequently, again, work in the same direction. In July, Slack announced a problem due to a vulnerability in the container architecture. Some messages and files were stolen from them. They do not disclose the volume of lost data, but they do disclose the number of affected users. Over 100,000 platform users. And I wouldn't say it was a stupid problem, but the problem was, perhaps, I would say, human, that is, human factor due to, unfortunately, incorrect container configuration, access to sensitive data, and damages of about 4 million, which is again quite a lot. And if we talk, consequently, in general about what is happening, it's every time, well, we see a considerable amount of both money and problems in general. By the way, August, we need to check August, I don't remember who it was. We probably don't have it specified, or rather, I think there was something not very significant. Some company in the middle received a leak somewhere around the same amounts. But in any case, even in the first 1, 2, 3, 4, 5 months of 2024, very good sums were leaked, and very large companies suffered. What are the main attack vectors they most likely were subjected to? There are, consequently, only four of them: supply chain attacks, vulnerable images, privileged containers, and misconfigured security. If we talk, consequently, from the left side of the clock, let's start with supply chain attacks. This is probably one of the most frequent and, unfortunately, serious threats, specifically targeted, when malicious software or malicious code is introduced into public images that we use. And these images, for example, are on DockerHub. And this happens either through backdoors in libraries, or through dependencies, or something else. And the main vulnerability, when we need to pay attention to it, is

This is the use of automatic processes, well, more precisely, images of CICD automation. And often it happens that we take a compromised container, do not check it, a very big mistake. We will talk a little later about what it is worth checking. And here is such a compromised container. And then it spreads, spreads, spreads throughout, in fact, the entire system and leads to massive, unfortunately, compromise. Privileged containers or an attack on them, this is probably one of the most attacks. And, in fact, if we look from the perspective of the supply chain at the lack of checks, then here they try to attack, let's say, attack the containers themselves. And specifically, those containers that are launched with an increased set of privileges. What happens? In fact, the following happens. Malicious code can exploit vulnerabilities sometimes at the kernel level of the host system, because we have taken too many privileges. And, in fact, that's where the problem begins, right, as if LXC fixed it. Well, not LX, the Linux kernel fixed it on the one hand, on the other hand, there is such a vulnerability as CVE 2023 27 532. Let's probably drop it in the chat. Whoever wants can familiarize themselves with it in more detail. In fact, what is the point of this vulnerability? This vulnerability allowed to escalate privileges inside the container, which in turn led to the capture of control over the entire host machine. And Kubernetes, if we look, for example, at one of the requirements, it is precisely the prohibition of the use of privileged containers from the point of view of security specialists. Why? Because privilege escalation, which can happen, for example, due to the vulnerability I sent, leads to quite sad consequences. Threats associated with images, in fact, are close to supply chains, but there it is specifically the use of generally unchecked images. And about, probably 30% of everything that is on Docker Hub official. Not all official at all, only. That is, what we would like, it would seem, to hope for, to believe in, to use, contains problems, namely, these are critical vulnerabilities like outdated packages of some GIPC or GCC, insecure versions of OpenSSL, and so on and so forth. What, in fact, is the problem? It is the possibility of remote code execution or, again, leakage of some confidential information. But, but, in fact, this can be fixed. We will talk a little later about how to fix the problem with images. There are very cool tools for this, but one way or another, not all companies have implemented this yet. Well, and simply incorrect configuration, incorrect security configuration or exploitation, let's say, not entirely correctly configured containers. Well, this is a human factor. Here, unfortunately, it is a human factor, when those who are engaged in protection, well, or should ensure protection, DevOps, SysOps, it doesn't matter, DevSecOps, they simply, let's say, are a little lazy, maybe they look at it like this. And we have no restrictions on the use of privileges. or there are no restrictions from the point of view of security policies, network policies or post-security policies, if we are talking about registries. And in general, that is, for example, containers should not have the ability to interact with a large number of network components. To be precise, they should not interact with any at all. either they don't cut it, or they get access to it again due to the privileged containers that we have. What does this lead to? Again, to the risk of leakage of our beloved sensitive, confidential data due to, for example, incorrectly configured storage or incorrectly, in fact, configured container. What is the most basic principle of all this that we can highlight? In fact, yes, here is the principle, in fact, check the images, and here is the principle, in fact, configure the images. And one of the basic principles that we can talk about is the maximum minimization of privileges. We specifically highlight that this is a critically important aspect of the security of our containers. Why? Because vulnerabilities can arise, as Docker by default runs all our containers with a basic set of privileges, which includes basic Linux permissions. And as an example, what do we have? We can use, for example, `chown` in the container to change file owners, we can use `sudo` to bypass access restrictions to files already. The interest begins. And we can change file ownership, in fact, rights. And we can send a `kill`, it would seem, okay, within the container, well, `kill` is normal, if something goes wrong. But if there are too many privileges, we can send `kill` somewhere we shouldn't with privileged containers, for example, and this will lead us to, at best, shooting ourselves in the foot. Binding to privileged network ports less than 1024. Because we have Netbios services. Or changing the user ID or process group ID via `setuid` and `setgid`, which are also initially available to us. And we understand that in fact, for most of our applications, we do not need these privileges. How can we protect ourselves? We can protect ourselves, in fact, with one very useful flag when launching our container, namely, use `capabilities drop`, for example, `mknod`, `net_raw` and so on. Not all containers, as I already said, not everything we have is necessary for us. And this parameter can reset unnecessary permissions, limiting the container's capabilities to only what we really need for its functioning. If low-level functions are not needed, `net_raw` is fine. And, in fact, by removing privileges, we significantly reduce the risks for ourselves. What else, besides, in fact, Linux Capabilities? We also have network policies. And in general, network policies play one of the key roles not only in container security, but, I would say, probably in general in security in modern IT. Especially when we talk about large and complex environments. In the case of containers, the main pleasant thing is the ability to isolate through network settings to, in fact, prevent unauthorized access and data leakage. Docker in general more than supports isolation through networks, which allows us to control which containers can interact with each other and which cannot. And in the case of using it with an orchestrator, if you plan to, in fact, move to Kubernetes, then Kubernetes also has its own set of policies. It is called Kubernetes Network Policies, and they provide, in fact, a more detailed level of control over traffic between our containers, to be precise, between pods, for example, limiting the same incoming traffic. But this is from the basic options. And if we, in fact, use the base, but still don't trust ourselves, or an evil DevSecOps came to us, or an evil security specialist came, looked, and said: "No, guys, this won't work." You have, of course, slightly limited the networks, you have, of course, done `drop capabilities`, but let's go further. We want something else besides trimming privileges. What do we have advanced? For the advanced level, we have exactly three tools, namely, the use of AppArmor, Seccomp, and SELinux. And these are the tools that are most often recommended to master for increasing container security. Or, let's say, to use them to increase the security of our Docker containers. Why? Because they allow us to limit the actions of containers, thereby improving the overall system security. In fact, AppArmor security profiles, Seccomp system calls, and access control through SELinux. Let's talk about them, a little more detail about each. In fact, AppArmor, a system of access control that allows us to set security profiles for our containers, controls resource availability and thereby helps protect our system from potentially malicious container behavior. What options do we have? Well, for example, create a profile that allows our containers access only exclusively to certain system elements that are important for us to work. while limiting the ability to modify or delete critically important system files. Seccomp is already a tool that allows us to limit the set of calls, as we saw earlier on our previous slide, that are available to containers. Why? Because, just like with capabilities, unfortunately, Docker by default applies basic Seccomp settings, which, ideally, block system calls. There are some advantages. But a logical question arises. If basic settings are used at the system level, then why do we need this? What calls do we consider dangerous? Why do we consider them dangerous? And what do we want to do at all? After all, for example, working with files, opening it, reading it, writing it, on the one hand, something basic, something that we need, on the one hand, on the other hand, incorrect access to read, open, or write a file can lead, in fact, even to system damage or leakage of something that shouldn't be. And at the same time, unfortunately, opening a file, well, working with files in general, we cannot disable it, because simply everything will be paralyzed, we won't even be able to write logs to input/output, because it's still a file. Let's think about what we can disable to somehow protect ourselves a little more. If we talk about these default ones, then in general, there is a set of calls that are blocked and that should be blocked. For example, through Docker, a call like `mount` is blocked. In fact, so that there is no mounting of file systems from within the container, protecting, in fact, our data from unauthorized access. Yes, we can expose ourselves and mount a piece of what lives directly on our system. But again, we will mount some folder, for example, right, we will not mount everything, and this will still be some kind of security, probably. Although you can mount it incorrectly, but that's up to the conscience of the one who mounts. `reboot` again, the container cannot restart the system. It would seem logical, but no. This is a default setting specifically at the level of working with containers. Additionally, debugging tools that can give access to the host's memory and processes are blocked. For example, we won't have things like `ptrace` and `strace`. We won't have, I think, `kexec`. `kexec_load`, in fact, for loading and replacing the kernel. Again, this will provide us with some kind of security, but not complete, against system capture at a deeper level. It is clear that if you are an advanced user, if you are a great security specialist, or if you know a lot about Seccomp, you can create your own profile, provide yourself with an additional level of security through these additional profiles and limit not only default but also some specific system calls that you generally don't need. For example, what will lead to system damage or data leakage and at the same time, what your application will never use. Well, and SELinux is a rather powerful management system that allows us to control access to resources at, let's say, a more detailed level than standard kernel elements allow. It uses specific security labels that are applied to objects and processes, allowing access control based on various policies. In fact, within Docker, SELinux can be integrated for yourself additionally to increase, again, that very coveted level of isolation, for which we use containers and to protect containers from attacks. In fact, access to resources at the operating system level will be limited. And, of course, this is what we are doing it for. And in general, the joint use of AppArmor, SELinux, and Seccomp creates additional multi-layered protection for our containers. This means it significantly reduces the risk of exploiting vulnerabilities. Now let's talk about practices and tips. And in fact, the main, more main problem starts even not from the moment we launch the container. The main problem and the main, please excuse me, negligence starts precisely from the moment of building, from when we write that very Dockerfile. And if we look at, let's say, good not very good practices, we will see that it looks like this. Someone might use `user root`, someone `expose 22`. Well, `expose` itself is not very scary, not a very scary command, but, in fact, it is not worth opening ports like twenty-second and others in the container. Mutability in terms of tags, using the `ADD` command. And all this leads us to not very pleasant consequences. Among the pros are image scanning, using `healthcheck`, using `COPY` instead of `ADD`, using base images in ideal conditions. And, in fact, containers should be in Rootless format. If we assemble this as a certain funnel, that is, what should, where do we start, let's say, stuffing our Dockerfile? And what do we get in the end after applying various good practices, then, probably, the very first, the most important is the use of the correct base image. Why so? Because, firstly, we have already talked about the fact that the problem with the base image, it happens one way or another. We are all human, not everyone manages to update something. But again, again, there is such a moment that if we look at Docker Hub, even at base images, we usually have three formats. This is, in fact, the usual one, well, for example, some kind of Debian, there is Buster or what is the latest. This can be `slim`, and this will be, in fact, `distroless`. `Distroless` is not available for everyone, but, in fact, the lightest usual image. Of all this, I would recommend, in fact, at this step, removing unused dependencies, taking the smallest one. The smallest one, necessary for work. If we take, for example, Python or Java or PHP, it doesn't matter, based on Buster or what is the latest Debian now, it doesn't matter. That is, on the latest Debian or Bookworm or Buster. Well, the question is, what is the latest in any case, Bookworm was, Buster was one of the latest. We take it, and there will be all the libraries that are, in fact, supplied with this Debian. Do you need all of them for work? I think with a probability of, probably, 90%, you don't need most of what is there. We trim, we take `slim`, we try to launch. If it's enough, great, if we understand that `slim` still has something unnecessary, something extra, we try to use `distroless`. If we don't launch with `distroless` and we see what we are missing, and we understand that it is, for example, well, you know, one or two dependencies, it is easier for us to add this dependency and use the initially maximally lightened image without unnecessary problems. Of course, after we have done everything, we use a non-root account, or rather, not a root account, and get a generally secure Docker image. In general terms. Now it's Trixie. I'm really, really far behind, apparently, because I remember when Buster and Bookworm came out, and it was like, almost the main thing. Looking at it, probably many haven't even updated to that point. Most likely, Debian is still the basis for many. I mean, regarding images. At the same time, if we look at what the simplest secure Dockerfile looks like, I apologize if it offends someone, but Python is closer to me, it looks like this. In fact, we use the `slim` image. Why? Because `slim` is the smallest image that Python has, I think. Although, let me check on Docker Hub just in case, if Python has anything smaller than `slim`. But I don't think so. Yes, yes, it's better to build `distroless` yourself. So, basically, we only have `slim`. Here we have `slim`. We understand that in `slim` we are missing GCC. Well, at least update it to the latest version. Or to the version that we will need. In this case, we consider it the latest recommended. In fact, we launch. Oops, we get GCC. `COPY`. Many can leave `COPY`. This is also not a safe story. We should ideally place it in `UP`, or in `SRC`, or somewhere else relative to the location of the Dockerfile, our, in fact, sources, or our `.py` files. Why? Simply so that we can precisely copy specifically from the folder what we need. Alpine, Alpine is often lighter than Slim. That is, Alpine is generally lightweight, but Alpine has a peculiarity. Alpine works a little differently than Debian works, or how, say, Fedora, or CentOS works. On the one hand, we can get a maximally lightweight image by using Alpine. On the other hand, we need to know how Alpine works with the kernel, how Alpine works with memory, with the processor, what are the peculiarities of interaction of the same compilers. I think they are present there. And because of this, in fact, if it suits us, then Alpine will be more advantageous because it is a little safer, well, conditionally, and it is lighter. If we are not satisfied with how Alpine interacts with processes, how Alpine generally works under the hood. If something can hinder us, then, unfortunately, in fact, it will be no better or worse, it will be different. Better, yes, in weight, worse, well, because of how it interacts with its kernel. Or rather, how differently it works. If you launch successfully, if it is the cleanest and lightest image, then yes, of course, Alpine is recommended. Well, in most cases. At least, Alpine is usually much lighter, let's say, than even some slims. So, let's go back. Why, again, by the way, note that `COPY` is used here. On the one hand, the `ADD` command does more or less the same thing, if we are talking about working with the host system. But the `ADD` command has one peculiarity. The `ADD` command allows us to download something from the link, that is, from the internet. And this immediately raises, firstly, questions from security specialists, and secondly, questions about how much you, in general, want to expose yourself. Because we cannot be 100% sure, even if we do it from our internal artifact or something else, it doesn't matter, that we will get the file we need. First. Second, this file will remain on the `ADD` layer. Therefore, first, we use `COPY`, because `COPY` allows us, for example, in multi-stage builds, to take something from the previous image, or rather from the previous layers, to be precise, from the previous `FROM`s. And secondly, if it is really necessary to download some archive, rar, something else, zip, and so on, we do the same as with the `RUN` command. In this case, `wget`, `tar`, `cp`, `rm` the downloaded sources. Why? First, we will clean up the file we took on one layer immediately. Second, we are still doing it through a somewhat safer conditional mechanism, at least as a recommendation, this is probably the main principle. After this, the Dockerfile initially tells us that we are root and our root directory is just `/`. We specify `/app` as the working directory. Why? Because, firstly, we copied everything there, and secondly, it is the directory where our sources are located. Or our compiled jars, etc., depending on what we use. After this, in fact, we launch everything we need, like requirements, change the user, and launch. Container security, first, we must use, first, an official image, second, the lightest possible official image without unnecessary dependencies, of course, with checks and with the need to install or update some libraries. First. Second, we must, first, use `COPY`. Second, we must specify the content precisely for copying. This is the very preparation of the content that we will copy into the container, so as not to take anything extra. We must not launch as root, and we must choose, ideally, a working directory. These are the main criteria for building an image before we launch it. That is, it should be as light as possible, as empty as possible, not as root, and as controllable as possible in terms of what we are launching there, what we are copying there. If we consider it from the perspective of practices, they are divided into three categories: infrastructure, image, in fact, working with images, and working with access. From the perspective of infrastructure, we regularly update, in fact, our runtime, periodically clean containers, and generally maintain all this. We configure APIs and networks. From the perspective of not having unnecessary access, we monitor what we use in terms of resources, of course, we try to reduce the number of privileges, update and maintain isolation at the host level. And with the help of necessary tools, we filter the system calls used. In fact, hello, SELinux. Regarding images, only trusted Docker images. Not only official, but also trusted and verified. We constantly scan, look, see if new vulnerabilities have appeared, and always try to choose the minimal base image. When working from the perspective of access, minimize direct access, maximize encryption and security, and use some utility for working with secrets. This is if we look at the best practices that Docker offers us. If we expand them to something more practical, then some things will coincide, but, in fact, three other categories that my colleagues and I most often highlight, based on how much the security guys tell us, are host system security, in fact, images again, and, most importantly, runtime. If we talk about host system security, always try to keep both our host and our Docker up to date. What is meant by up-to-date versions is installing security updates and tracking the absence of known, found, and most importantly, fixed vulnerabilities. Sometimes it might be worth not updating, because there will be a vulnerability, and you don't have it yet, and it hasn't been fixed. This happens. Again, we limit the Docker daemon, do not allow the use of the socket, also known as `unix:///var/run/docker.sock`. And use TLS for remote management, for example, via Docker API. We try to launch. We have decided that we will have rootless containers. We try to launch Docker in rootless mode as well. In fact, both Docker daemon, and containerd, and Podman. Fortunately, Podman out of the box does this, so our containers run without root privileges to minimize the consequences of potential problems and use kernel security features, in fact, AppArmor, Seccomp, and SELinux to improve security and isolation level. This is regarding settings and applying some useful things related to the host system. If we talk about working with images, as I already said, official trusted images, and besides official images, you can use some golden images that exist in the company. Companies periodically do this to have the most secure image possible, on the basis of which you can build your Dockerfile, your Docker image for a Java application, for Python, for something else. And it doesn't necessarily have to live on, in fact, Docker Hub. But in general, maximally lightened, those that you trust, or your company trusts, and maximally protected. Alpine usually refers to, in fact, if everything works on Alpine, then one of the ideal moments, not counting, in fact, building from scratch. Or `scratch`. Always perform scanning? Yes, very often there is a situation where, for example, Harbor has built-in scanning, but you only find out about it when you fly to Harbor. When you push to Harbor, you will find out about the problem with the base image earlier, well, you might not even imagine how, in fact, how. That is, after you have assembled, at the same time, use scanning tools like `docker scout`, `trivy`, `anchore`, something else, precisely before building with your CI/CD, so that you can identify and, most importantly, eliminate vulnerabilities in time before we assemble and deploy. Avoid using root, as everywhere. Regularly update your images. Here, by the way, is a very interesting point, because, yes, there are cases when official base images can be compromised. Here the point about updating, not updating is very closely intertwined with scanning. There can be a situation where the global image provider simply removes everything old, or changes the name, or something else. My colleagues had such a problem around 2021, I think, when Microsoft completely revamped their .NET repository, and they had a very global ACD problem until they understood what exactly happened there. So, check, update images if necessary, because either security changes may occur, or, in fact, the image will simply disappear, and you will have to update to a newer version anyway, or build it yourself. And use multi-stage builds, again, so that the image is as light as possible, does not carry garbage and any extra information with it. And by the way, at runtime, again, what we have already talked about, but again, summarizing, limit privileges, avoid using the `--privileged` flag. Or more precisely, additionally, if you know which capabilities you definitely don't need, `--cap-drop` of the same Kubernetes, for example, you can do `drop all`. And this is very convenient if we understand that we practically don't need anything there. Try to use the file system in read-only format. Well, at least mount in read-only format, again, to prevent unauthorized changes, because in most cases containers do not need to change anything. While they are working, not all of them. There are those who need it.

But, for the most part. Usually, you just need to read what's happening there. Try to segment your network. Use containers, most often using a dedicated network. Either implement network policies to control inter-container interaction, or both. Try to manage secrets as securely as possible, use Ansible Vault, HashiCorp Vault, SOPS files, Docker Secrets, I think there's also a not-so-bad tool. That is, try as much as possible to hide the information that needs to be passed into the container. And it's desirable to pass it directly where we are launching. Monitor activity to be able to detect and react to suspicious or anomalous activity within the container as quickly as possible. And you can use, during development, at least, and when working with Docker, or Docker Hub, container trust for verifying the authenticity and integrity of images before deployment. And as I promised, a little bit about scanning. There are four useful tools. I would highlight them as three. This is Trivy. A fairly comprehensive tool, primarily focused on searching for CVEs. This is Docker Scout, again, a tool from Docker, also heavily focused on CVEs. And this is a combination from Anchore, open-source. Grype and Syft. One is SBOM, the other is CVE. And again, they work in pairs. If we talk about Trivy, it's a fairly universal tool. It's built into Nexus, I believe. It's built into Harbor. It allows for comprehensive analysis of our Docker images. But overall, it primarily finds vulnerabilities and configuration errors. And security professionals often recommend integrating Trivy into your overall CI/CD pipeline. Key aspects include scanning Docker images for vulnerabilities and configuration errors. This includes checking application dependencies and the ability to easily integrate into your required CI/CD platform. Jenkins, GitLab CI, GitHub Actions can all handle this. Docker Scout, essentially, monitors vulnerabilities, provides update recommendations if necessary, and tracks security status. This is a tool directly from the Docker developers. It appeared relatively recently, but is already actively used for monitoring and improving the security of our containers, meaning in the industry. It allows, as I said, to identify vulnerabilities. Key features include real-time monitoring and analysis of vulnerabilities. And, essentially, Syft and Grype are complementary tools from the Anchore developers. Syft is a software bill of materials generator, essentially an SBOM for creating and understanding what's happening. And essentially, we can easily and effortlessly see, let's say, a list of ingredients that are in our software. What's the advantage? It helps with inventory and understanding what we are actually using. The ecosystems that can be used are quite extensive. This includes Linux packages, DEB, RPM, APK, so no one is left out. This includes language-specific packages. Python, JavaScript, Java, and I believe Go modules are currently confirmed. And, essentially, the ability to create separate reports for yourself. Not answers, but separate reports. When is it recommended to use it, if we're talking about security? When we want to get a comprehensive and transparent view of what's inside our images, or our source code, or both. Security professionals love any SBOM. Syft is simply a free example. For what purpose? To comply with regulatory requirements. And to create a manifest of all our contents. Grype is a specialized vulnerability scanner, specifically for CVEs. It can analyze both our image and, in general, the data collected by Syft. Where does it look? It looks in publicly available databases. For example, this can be CVE, it can be NVD, the National Vulnerability Database, it can be GitHub Advisories, if I remember correctly how it's pronounced. And in fact, it's used similarly to Trivy. What's interesting is that the output of Grype and Trivy differs significantly. And you can use both. You can see which one, for example, suits you better and which one you would trust more. I think Grype has a slightly more interesting output. At least, that's how it seemed to me personally and to most of my colleagues. Syft provides, yes, Grype is still more focused on CVEs. At least, compared to Trivy, Grype is compared to Trivy. And they are in that direction. But we tried using this combination in several teams, and the security professionals were generally in favor. They said, yes, that's a good idea. As a free option, it's allowed, but with reservations, again, because not everyone likes open source. So, if you have any questions regarding what we've been talking about for, I think, over an hour now, please let me know. Please put a plus sign. If there are no questions, you can put a minus sign, or any reaction, so that I know we can move towards the conclusion. Then let's move on to reflecting on the testing of our software. By the way, that's a good question. I haven't encountered separate tools, but if I remember correctly, the guys from Dexhouse had something similar. The guys from Dexhouse had something about security. I wouldn't say it's perfect and it will definitely save us from everything. On the one hand, and on the other hand, they worked quite well with building and delivering containerized software. They didn't just throw it in, they developed secure secret lifecycle management quite well. I think they had something about, let's say, security as well, some other guys, I can't remember the name of the domestic analog right now. I'll try to find it. Maybe we had guys who were very focused on, well, generally on security. In general, Yandex, Yandex Cloud, cloud.ru had something separate, domestic solutions, but these are still more, how to put it better, these are still more cloud solutions. The question about vulnerability scanners is good, I need to look into it. Someone was a domestic alternative, but I can probably only name Dexhouse offhand right now because they are, well, somehow well-known. And it seems like Fland is a fairly serious company in this regard. No, unfortunately, when launching Docker, we can only reduce from the default or use some custom settings, but to reduce everything and open something is a bit, let's say, more for Kubernetes. If we talk about Kubernetes, yes, we can reduce everything and specifically say that we want this, and we don't want that. It's a bit easier there with containers. Units, let's say, are a bit more complicated. Well, DevSecOps pipeline in the cloud, yes, in principle, it's a set of tools, most often used for DAST and SAST, most likely. Although I could be mistaken, we can look into it. In any case, there should be a link to the survey at the top. Yes, I will probably duplicate it. If possible, I would like to receive your feedback because we read it. We really take into account how interesting and useful the topics were for you, and how much they need to be integrated more deeply. Now, let's talk a little about the course. The course itself is dedicated to DevOps practices and tools. It consists of, let's say, six modules, four of which are major. DevOps requires change and project work as introductory and concluding modules. A deeper dive into CI/CD using Ansible and Docker, container orchestration, infrastructure and configuration management, and monitoring and logging from a DevOps perspective, because finding and reacting as quickly as possible is exactly what we want to do and what we actually have to do in most real-world tasks. Here are the instructors of our course who will be waiting for you. And a little about the learning format. The learning takes place in a live format, exactly as we have now. Only you will have the opportunity not only to write in the chat but also to ask questions verbally, which, in my opinion, is most interesting for learning. It's an opportunity for networking. Because you can ask a question not only to the instructor but also to colleagues who are on the course with you. Perhaps someone already had a similar case and can share their experience. All recordings of classes and materials are, of course, saved and remain available to you in your personal account forever, not just for a certain period after the course ends. There will be a project that you can complete and thereby expand your portfolio. Again, the topic can be agreed upon and discussed with the instructor responsible for the project work. The instructor will provide feedback on each homework assignment. And the training program, as I said at the beginning, is updated with each launch to provide you with the most useful and, of course, in-demand skills in the market. The course will start very soon, in less than a month, on November 26, 2025. You can find more details about it via the link that this QR code leads to, on the page that this QR code leads to. And the course duration is, essentially, 5 months. And let's summarize. We, sorry, we learned about the history of establishing container security. We reviewed the features that can be used in Docker for security, and we got acquainted with the best practices recommended by the community. For what purpose? So that you can create secure solutions or secure existing ones. The next open lesson will be on November 11th, and it will be dedicated to CI/CD using Ansible and GitLab CI. So, if you want to receive more information about open lessons, tips from experts, and also follow and view graduate projects, you can join our Telegram channel Otus IT News. Once again, I would like to thank everyone for attending our session today. Of course, I'll repeat, I sent the link a little later. At the beginning, Olesya also sent you a link in the pinned message to fill out a survey about today's event, because, as I said, feedback is always welcome, and we, of course, study it. In any case, everyone have a good evening, and I look forward to seeing you again.