diff options
-rw-r--r-- | mds/NTP.txt | 144 | ||||
-rw-r--r-- | mds/oneclientforeverything.txt | 96 | ||||
-rw-r--r-- | mds/securedocker.txt | 611 | ||||
-rw-r--r-- | put_in_db.js | 8 |
4 files changed, 850 insertions, 9 deletions
diff --git a/mds/NTP.txt b/mds/NTP.txt index 8060191..ebb7997 100644 --- a/mds/NTP.txt +++ b/mds/NTP.txt @@ -1,7 +1,7 @@ -== After NTP Comes NTS +== After NTP Comes NTS. After NTS comes sdwdate. Well for this one I will be talking a bit about NTP and NTS. Unlike the -DNS post there isnt much going on here. +DNS post there isn’t much going on here. NTP is plain-text, NTS uses TLS so if our requests are tampered with, we can know. There is the ``oooh, you cant see what I’m sending now'' but @@ -19,6 +19,9 @@ tampered with * REQ-003: It should not be known which time servers are being used upstream by the client +If you are wondering why any of this even matters you can have a look +https://www.whonix.org/wiki/Time_Attacks[here]. + Now talk about the problem. The protocol is fine. We are sending TCP with TLS here. That’s brilliant. We get all this: @@ -34,7 +37,7 @@ with TLS here. That’s brilliant. We get all this: * Performance: NTS must not significantly degrade the quality of the time transfer. The encryption and authentication used when actually transferring time should be lightweight. .... -exerpt from https://www.rfc-editor.org/rfc/rfc8915[RFC 8915] +Excerpt from https://www.rfc-editor.org/rfc/rfc8915[RFC 8915] If we find a client that lets us use a SOCKS5 proxy, then we can send our NTS requests over Tor and then call it a day. REQ-002 and REQ-003 @@ -51,7 +54,7 @@ SOCKS5 proxies. * for ntpd-rs look https://github.com/pendulum-project/ntpd-rs/discussions/1365[here] -Which menas our setup is not complete. +Which means our setup is not complete. === Implementation @@ -106,7 +109,7 @@ listen = "10.167.131.1:123" listen = "[::1]:123" ---- -[source,config] +[source,conf] ---- nts enable nts key /etc/letsencrypt/live/nts.dehein.org/privkey.pem @@ -169,15 +172,142 @@ volumes: vault: ---- +=== What comes after NTS + +Above we looked at NTP and NTS. We failed to find a client that supports +SOCKS5 but that’s a trivial matter. What is not trivial, however, is how +NTS and NTP work, and by that I mean you will still have to ask a server +to tell you the time. Doing so over Tor or other anonymizing networks +should be fine but we can choose to try out another method of doing +things. Enter `sdwdate` + +==== sdwdate + +It still has the same flaw as NTP/NTS as in we still have to trust a +server not to lie +https://www.kicksecure.com/wiki/Sdwdate#sdwdate_Source_Pools[please look +here]. Personally, It is a bit of a disappointment that the protocol +that’s supposed to be oh-so-much-shinier and newer than NTP has the same +flawed mechanism as NTP. Now granted having hardware that tells you the +time so that you can share that with everyone else is not something +trivial or readily-available but this only makes sdwdate desirable in +the absence of an NTS client that support SOCKS5 proxy. Once that is +done, the larger user pool of NTS/NTP will offer more protection against +the smaller userbase of sdwdate. sdwdate gives a table of comparison +between itself and NTP. Let’s take at look at that: + +Let’s take a look at `sdwdate`. It is a roller-coaster. And I do mean +that. So don’t make up your mind until the very end. There is a +comparison between NTP and sdwdate made +https://www.kicksecure.com/wiki/Sdwdate#Sdwdate_vs_NTP[here] by +kicksecure themselves. + +[cols=",,",options="header",] +|=== +|category |sdwdate |ntp +|written in memory-safe language |Yes |No +|distributed trust |Yes |No +|secure connection by default |Yes |No +|gradual clock adjustments |Yes |Yes +|daemon |Yes |Yes +|functional over tor |Yes |No +|tor not required |No |Yes +|client, time fetcher |Yes |Yes +|Server, time provider |No |Yes +|AppArmor profile |Yes |Yes +|systemd security hardening,seccomp |Yes |? +|drop-in config folder |Yes |No +|proxy support |Yes |No +|possible to secure by default on GNU/Linux distribution level |Yes |No +|secure |Yes |No +|optional GUI |Yes |No +|=== + +* memory-safety: I mean its good and all that sdwdate uses a memory-safe +language(python) but NTP is a protocol. Not sure how NTP is bound to a +single programming language. The one client we mentioned before uses +rust which guarantees memory safety. +* secure connection by default: NTS uses TLS v1.3 . Not sure why sdwdate +is being compared against NTP and not NTS. +* functional over Tor: again, NTS uses TCP which can pass through a +SOCKS5 proxy as is implemented by the current incarnation of Tor. Also, +not sure, but are we comparing against the NTP protocol or a specific +implementation? +* Tor not required: what if I want to use +https://github.com/PurpleI2P/i2pd[i2p] or +https://github.com/yggdrasil-network/yggdrasil-go[yggdrasil] to sync +time over? Why does it have to be Tor? +* apparmor profile: not sure why this is even included. You can write +one for NTP implementations. +* systemd security hardening, seccomp: same as above. You can do it for +NTP/NTS implementations as well. +* drop-in config folder: what’s a folder? Is that supposed to be a +directory? Second, what does that even mean? And third, who is writing +these? The only kind of people who make this sort of mistake are people +who use MS Windows more than Linux. This is official kicksecure +documentation. You have Windows users writing these for the ultra secure +and hardened ``Linux'', I’ll say it again, ``Linux'', distro? +* proxy support: again, NTS uses TCP so it supports SOCKS5 proxies as +well but for whatever reason we are comparing against NTP(though whether +we are comparing against the protocol or an implementation is something +left to be decided by the next generation of humans) +* possible to secure by default on GNU/Linux distribution level: whats +the GNU/Linux distribution level? What does this even mean? You can +secure it on the OS level? I mean it’s software so I would hope that it +would be possible to secure it on the software level. +* secure: what are the criteria? Secure against what? And again, why are +we comparing to NTP and not NTS? +* optional GUI: again not sure why we keep zig-zagging between comparing +implementations and the protocols. In conclusion, why is that table even +there? What purpose does it even serve? + +If we were going to base our judgement on the documentation provided on +kicksecure’s website, I am sorry to say that `sdwdate` does a very poor +job but fortunately that’s not all there is to it. + +Now let’s go take a look at the github README for the project: + +[source,txt] +---- +At randomized intervals, sdwdate connects to a variety of webservers and extracts the time stamps from http headers (RFC 2616)) +---- + +This is our first spark of brilliance. The second spark is when we +consider the practical meaning of only being able to use Tor v3 +addresses. Like a wise man once said: + +[source,txt] +---- +amateurs practice something until they can get it right. pros practice something until they can't get it wrong. +---- + +The result of using only Tor v3 addresses is that you cannot leak your +real IP address no matter what happens. You either have a working Tor +proxy in which case the IP address will be that of the exit node or none +at all. + +Now we know we definitely are dealing with a very promising solution. +`sdwdate' extracts the time stamp in the http header so we are not +asking a known NTP server about the time, we are just doing a normal +http request. + +=== DISCLAIMER + +Although unrelated, it is worth noting that the kicksecure docs are +pretty good even if you are not planning on using kicksecure. + === Links * https://www.rfc-editor.org/rfc/rfc8915[RFC 8915] * https://github.com/jauderho/nts-servers[Here] you can find a list of publicly available servers that support NTS +* https://github.com/Kicksecure/sdwdate[sdwdate’s github page] +* https://www.kicksecure.com/wiki/Sdwdate[sdwdate doc] +* https://www.rfc-editor.org/rfc/rfc2616[RFC 2616] -timestamp:1709418680 +timestamp:1713478033 -version:1.0.0 +version:1.1.0 https://blog.terminaldweller.com/rss/feed diff --git a/mds/oneclientforeverything.txt b/mds/oneclientforeverything.txt index 9d61a5b..dff38b7 100644 --- a/mds/oneclientforeverything.txt +++ b/mds/oneclientforeverything.txt @@ -244,9 +244,101 @@ volumes: matterircddb: ---- -timestamp:1699398469 +==== SMS + +I have an entire post about how one can get their SMS on IRC +link:posts/how_to_get_your_sms_on_irc[here]. You need a piece of +software on your phone to forward the SMS to a web hook server and then +we send the SMS over to IRC. You can find the web-hook server that I use +https://github.com/terminaldweller/sms-webhook[here]. + +==== where to sink all the bridges + +Bridges connect two things. You need to have a sink for your bridges. I +was contemplating making a lot of invite-only channels protected by +password on public networks then I found out about +https://github.com/ergochat/ergo[ergo]. I’d say the main advantage of +using ergo is, it’s easy to setup. You don’t need any other services to +run to get basic functionality like chanserv or nickserv. You don’t even +need a bouncer if you need to have your messages when your client +disconnects. ergo has that functionality built-in. Here are some other +perks: + +* ergo allow you to define a ``private'' IRC network. You do that by +requiring SASL while connecting, so others can’t connect to your +instance without having an account +* it is under active development +* it has good documentation +* its one executable written in go so it’s very easy to deploy + +==== bots + +We have LLMs now. The genie is out of the box. They are useful.’ I +needed a bunch of them to I wrote +https://github.com/terminaldweller/milla[milla]. At the time of writing +milla supports chatgpt, gemini and of course ollama. + +===== Deploying milla -version:0.1.0 +[source,yaml] +---- +version: "3.9" +services: + milla: + image: milla + build: + context: . + deploy: + resources: + limits: + memory: 64M + logging: + driver: "json-file" + options: + max-size: "100m" + networks: + - millanet + restart: unless-stopped + command: ["--config", "/opt/milla/config.toml"] + volumes: + - ./config.toml:/opt/milla/config.toml + cap_drop: + - ALL + dns: + - 9.9.9.9 + environment: + - SERVER_DEPLOYMENT_TYPE=deployment + entrypoint: ["/milla/milla"] +networks: + millanet: +---- + +[source,toml] +---- +ircServer = "irc.terminaldweller.com" +ircPort = 6697 +ircNick = "mybot" +ircSaslUser = "mybot" +ircSaslPass = "mypass" +ircChannel = "#mychannel" +ollamaEndpoint = "" +temp = 0.2 +ollamaSystem = "" +requestTimeout = 10 +millaReconnectDelay = 60 +enableSasl = true +model = "llama2-uncensored" +chromaStyle = "rose-pine-moon" +chromaFormatter = "terminal256" +provider = "ollama" # ollama, chatgpt, gemini +apikey = "key" +topP = 0.9 +topK = 20 +---- + +timestamp:1713480455 + +version:1.0.0 https://blog.terminaldweller.com/rss/feed diff --git a/mds/securedocker.txt b/mds/securedocker.txt new file mode 100644 index 0000000..62a4796 --- /dev/null +++ b/mds/securedocker.txt @@ -0,0 +1,611 @@ +== Docker, Linux, Security. Kinda. + +We will be exploring some Linux features in the context of a docker +application container. Another way of explaining it would be to say we +will talk about how to make more secure application containers. We will +not talk about firewall and apparmor because they are tools that enhance +security on the host in general and not specific to a docker application +container. A secure host means a more secure application container but +that is discussion for another post. We will focus on Linux containers +since FreeBSD containers are still experimental(see +https://wiki.freebsd.org/Docker[here] and +https://github.com/samuelkarp/runj[here]). Yes, windows containers +exist. We will not discuss performance. Here be performance penalties, +but again that is not the focus of this post. + +Before we begin, Linux docker containers are Linux. They are using most +of the functionality that existed before application containers in the +form of docker were a thing. Knowing Linux better means you know Linux +Docker containers(application containers is a more correct term) better. +We will see this point throughout this post. + +=== Base Image + +We start with the first building block of a new docker image, The base +image. By far the most used base images are the Alpine docker base +image, followed by Debian and Ubuntu docker base images. These distros +have two major differences that we want to focus on: + +* C standard library implementation +* the userspace utility implementation + +Debian and Ubuntu(we are not forgetting that Ubuntu itself is a Debian +derivative) both use glibc, as in gnu’s +https://www.gnu.org/software/libc/[libc] implementation. Alpine uses +https://www.musl-libc.org/[musl-libc] as its C standard library +implementation. The major difference here which will come into play +later on again is glibc has been around for much longer, so it has to +keep backwards compatibility for a much longer period of time and for +far more many things. Also the general attitude with the glibc team is +that they have to support everything since if they don’t then who will? +Libmusl on the other hand, does not try to support everything under the +sun, a relatively newer project, comparatively, and, they keep their +codebase lean. As a result not all applications are supported by libmusl +but a good number of them are. In simpler terms, libmusl has a far +smaller attack surface compared to glibc. + +On to our second point, which is the cli utilities’ implementation. +Debian and Ubuntu use gnu’s +https://www.gnu.org/software/coreutils/[Coreutils] while Alpine uses +https://busybox.net/[Busybox](remember, we are talking about the most +used application container bases. You can install a desktop version of +Alpine with GNU coreutils). Here we have the same situation as before, +The GNU coreutils are bigger, do more and have a larger attack surface. +Busybox is smaller, does not support as many features as GNU Coreutils +but does support enough of them to make them useful. Needless to say, +busybox is small and hence, it has a smaller attack surface. + +To get a feel for how this plays out in the real world, you can look at +some of the popular images that come in both Debian and Alpine flavours +on dockerhub. Take a look at the number of reported vulnerabilities for +both bases. The theme we observe is simple. The bigger the attack +surface the bigger the number of vulnerabilities. + +Alpine images are small, lean and functional, just like libmusl and +busybox but there are still quite a few things on an alpine image that +are extraneous. We can take them out and have a perfectly functioning +application container. + +That’s how we get +https://github.com/GoogleContainerTools/distroless[distroless]. +Distroless base images follow the same pattern as alpine base docker +images, as in, less functionality while still keeping enough +functionality to be able to do the job and minimize the attack surface. +Minimizing a base image like this means that the base images are very +specialized so we have base images for golang, python, java and the +like. + +=== Dokcer Runtimes + +By default docker uses containerd which in turn uses runc for the +runtime. There are two additional runtimes that we want to focus on who +try to provide a more secure runtime environment for docker. + +* gvisor +* kata + +==== gvisor + +gVisor creates a sandbox environment. Containers interact with the host +through this sandboxed environment. gvisor has two components. Gofer and +Sentry. Sentry is a kernel that runs the containers and intercepts and +responds to system calls made by the application so as not to have an +application directly control the syscalls that it makes. Gofer handles +filesystem access(not /proc) for the application. The application is a +regular application. gVisor aims to provide an environment equivalent to +Linux 4.4. gvisor presently does not implement every system call, +`/proc` file or `/sys` file. Every sandbox environment gets its own +instance of Sentry. Every container in the sandbox gets its own instance +of Gofer. gVisor currently does not support all system calls. You can +find the list of supported system calls for amd64 +https://gvisor.dev/docs/user_guide/compatibility/linux/amd64/[here]. + +.... + ------------- + |Application| + ------------- + |system calls + | + -------- 9p ------- + |Sentry|<------->|Gofer| + -------- ------- + | limited |system + | syscalls |calls + --------------- + | Host Kernel | + --------------- + | + |hardware +.... + +==== kata + +Kata creates a sandbox environment for containers to interact with as +proxy, not too dissimilar to gvisor but the main point of difference is +that kata uses a VM to achieve this. + +gVisor and katacontainers allow us to implement defense in depth when it +comes to application containers and host system security. + +=== Capabilites and Syscalls + +Let’s talk about capabilities for a bit. + +From +https://manpages.debian.org/bookworm/manpages/capabilities.7.en.html[man +7 capabilities]: + +[source,txt] +---- +For the purpose of performing permission checks, traditional UNIX implementations distinguish two +categories of processes: privileged processes (whose effective user ID is 0, referred to as +superuser or root), and unprivileged processes (whose effective UID is nonzero). Privileged +processes bypass all kernel permission checks, while unprivileged processes are subject to full +permission checking based on the process's credentials (usually: effective UID, effective GID, and +supplementary group list). + +Starting with Linux 2.2, Linux divides the privileges traditionally associated with superuser into +distinct units, known as capabilities, which can be independently enabled and disabled. +Capabilities are a per-thread attribute. +---- + +Capabilities give you a more granular control over which privileges to +give instead of just root and non-root. Docker let’s us choose which +capabilities to give to a container. So we can for example allow a +non-privileged process to bind to privileged ports using capabilities. +As an example, a simple application making calls to API endpoints and +writing results back to a database does not require any capabilities. It +can run under a non-privileged user with no capabilities and do all the +tasks that it needs to do. That being said, determining which +capabilities are required can be a bit challenging when it comes to +certain applications since there is no straightforward way of achieving +this. In certain cases we can get away with dropping all capabilities, +running our application and then trying to figure out, based on the +received error messages, which capability is missing and needs to be +given to the application. But in certain cases this may not be feasible +or practical. + +From +https://manpages.debian.org/bookworm/manpages-dev/syscalls.2.en.html[man +2 sycalls]: + +[source,txt] +---- +The system call is the fundamental interface between an application and the Linux kernel. +---- + +The Linux kernel lets us choose which ones of these interface calls can +be allowed to be made by an application. We can essentially filter which +syscalls are allowed and which ones are not on a per application basis. +Docker enables this functionality with an arguably more friendly +approach. Capabilities and syscall filtering are tools to implement +principle of least privilege. Ideally, we would like to allow a +container to only have access to what it needs and just that. Not more, +and obviously not less. + +==== capabilities in the wild + +Capabilities are a Linux feature, docker allows us to use that with +application containers. We’ll look at a very simple example of how one +can set capabilities for a regular executable on Linux. +https://manpages.debian.org/bookworm/libcap2-bin/setcap.8.en.html[man 8 +setcap] lets us set capabilities for a file. + +==== syscall Filtering in the wild + +As an example we will look at +https://manpages.debian.org/bookworm/bubblewrap/bwrap.1.en.html[man 1 +bwrap]. https://github.com/containers/bubblewrap[Bubblewrap] allows us +to sandbox an application, not too dissimilar to docker. Flatpaks use +bubblewrap as part of their sandbox. Bubblewrap can optionally take in a +list of syscalls to +https://www.kernel.org/doc/html/v4.19/userspace-api/seccomp_filter.html[filter]. +The filter is expressed as a BPF(Berkley Packet Filter program - +remember when I said docker gives you a +https://docs.docker.com/engine/security/seccomp/[friendlier] interface +to seccomp?) program. Below is a short program that defines a BPF +program that can be passed to an application using bwrap that lets us +log all the sycalls the application makes to syslog. + +[source,c] +---- +#include <fcntl.h> +#include <seccomp.h> +#include <stdbool.h> +#include <unistd.h> + +void log_all_syscalls(void) { + scmp_filter_ctx ctx = seccomp_init(SCMP_ACT_LOG); + seccomp_arch_add(ctx, SCMP_ARCH_X86_64); + seccomp_export_bpf(ctx, 1); + seccomp_export_pfc(ctx, 2); + seccomp_release(ctx); +} + +int main(int argc, char **argv) { + log_all_syscalls(); +} +---- + +Building is straightforward. Just remember to link against `libseccomp` +with `-lseccomp`. + +[source,bash] +---- +gcc main.c -lseccomp +---- + +Running the above code we get this: + +[source,txt] +---- + > 5@# +# pseudo filter code start +# +# filter for arch x86_64 (3221225534) +if ($arch == 3221225534) + # default action + action LOG; +# invalid architecture action +action KILL; +# +# pseudo filter code end +# +---- + +[source,bash] +---- +#!/usr/bin/dash + +TEMP_LOG=/tmp/seccomp_logging_filter.bpf + +./a.out > ${TEMP_LOG} + +bwrap --seccomp 9 9<${TEMP_LOG} bash +---- + +Then we can go and see where the logs end up. On my host, they are +logged under `/var/log/audit/audit.log` and they look like this: + +.... +type=SECCOMP msg=audit(1716144132.339:4036728): auid=1000 uid=1000 gid=1000 ses=1 subj=unconfined pid=19633 comm="bash" exe="/usr/bin/bash" sig=0 arch=c000003e syscall=13 compat=0 ip=0x7fa58591298f code=0x7ffc0000AUID="devi" UID="devi" GID="devi" ARCH=x86_64 SYSCALL=rt_sigaction +type=SECCOMP msg=audit(1716144132.339:4036729): auid=1000 uid=1000 gid=1000 ses=1 subj=unconfined pid=19633 comm="bash" exe="/usr/bin/bash" sig=0 arch=c000003e syscall=13 compat=0 ip=0x7fa58591298f code=0x7ffc0000AUID="devi" UID="devi" GID="devi" ARCH=x86_64 SYSCALL=rt_sigaction +type=SECCOMP msg=audit(1716144132.339:4036730): auid=1000 uid=1000 gid=1000 ses=1 subj=unconfined pid=19633 comm="bash" exe="/usr/bin/bash" sig=0 arch=c000003e syscall=13 compat=0 ip=0x7fa58591298f code=0x7ffc0000AUID="devi" UID="devi" GID="devi" ARCH=x86_64 SYSCALL=rt_sigaction +type=SECCOMP msg=audit(1716144132.339:4036731): auid=1000 uid=1000 gid=1000 ses=1 subj=unconfined pid=19633 comm="bash" exe="/usr/bin/bash" sig=0 arch=c000003e syscall=13 compat=0 ip=0x7fa58591298f code=0x7ffc0000AUID="devi" UID="devi" GID="devi" ARCH=x86_64 SYSCALL=rt_sigaction +type=SECCOMP msg=audit(1716144132.339:4036732): auid=1000 uid=1000 gid=1000 ses=1 subj=unconfined pid=19633 comm="bash" exe="/usr/bin/bash" sig=0 arch=c000003e syscall=13 compat=0 ip=0x7fa58591298f code=0x7ffc0000AUID="devi" UID="devi" GID="devi" ARCH=x86_64 SYSCALL=rt_sigaction +type=SECCOMP msg=audit(1716144132.339:4036733): auid=1000 uid=1000 gid=1000 ses=1 subj=unconfined pid=19633 comm="bash" exe="/usr/bin/bash" sig=0 arch=c000003e syscall=14 compat=0 ip=0x7fa5859664f4 code=0x7ffc0000AUID="devi" UID="devi" GID="devi" ARCH=x86_64 SYSCALL=rt_sigprocmask +type=SECCOMP msg=audit(1716144132.339:4036734): auid=1000 uid=1000 gid=1000 ses=1 subj=unconfined pid=19633 comm="bash" exe="/usr/bin/bash" sig=0 arch=c000003e syscall=13 compat=0 ip=0x7fa58591298f code=0x7ffc0000AUID="devi" UID="devi" GID="devi" ARCH=x86_64 SYSCALL=rt_sigaction +type=SECCOMP msg=audit(1716144132.339:4036735): auid=1000 uid=1000 gid=1000 ses=1 subj=unconfined pid=19633 comm="bash" exe="/usr/bin/bash" sig=0 arch=c000003e syscall=1 compat=0 ip=0x7fa5859ce5d0 code=0x7ffc0000AUID="devi" UID="devi" GID="devi" ARCH=x86_64 SYSCALL=write +type=SECCOMP msg=audit(1716144132.339:4036736): auid=1000 uid=1000 gid=1000 ses=1 subj=unconfined pid=19633 comm="bash" exe="/usr/bin/bash" sig=0 arch=c000003e syscall=1 compat=0 ip=0x7fa5859ce5d0 code=0x7ffc0000AUID="devi" UID="devi" GID="devi" ARCH=x86_64 SYSCALL=write +type=SECCOMP msg=audit(1716144132.339:4036737): auid=1000 uid=1000 gid=1000 ses=1 subj=unconfined pid=19633 comm="bash" exe="/usr/bin/bash" sig=0 arch=c000003e syscall=270 compat=0 ip=0x7fa5859d77bc code=0x7ffc0000AUID="devi" UID="devi" GID="devi" ARCH=x86_64 SYSCALL=pselect6 +.... + +Docker allows us to do the +https://docs.docker.com/engine/security/seccomp/[same]. We can give +docker a seccomp profile to filter out the syscalls that are not +required for a specific container. You can find the default docker +seccomp profile +https://github.com/moby/moby/blob/master/profiles/seccomp/default.json[here]. + +=== Namespaces + +.... +A namespace wraps a global system resource in an abstraction that makes it appear to the processes +within the namespace that they have their own isolated instance of the global resource. Changes +to the global resource are visible to other processes that are members of the namespace, but are +invisible to other processes. One use of namespaces is to implement containers. +.... + +From +https://manpages.debian.org/bookworm/manpages/namespaces.7.en.html[man 7 +namespaces]. You can think of namespaces as almost the same thing as a +namespace does in some programming languages. Docker uses its own +namespaces for the containers so as to further isolate the application +containers from the host system. + +==== Namespaces in the Wild + +As an example let’s look at the script provided below. Here we are +creating a new network namespace. The new interface is provided by +simply connecting an android phone for USB tethering. Depending on the +situation you have going on and the `udev` naming rules the interface +name will differ but the concept is the same. We are creating a new +network namespace for a second internet provider, which in this case, is +our android phone. We then use this network namespace to execute +commands in the context of this specific network namespace. Essentially, +we can choose which applications get to use our phone internet and which +ones use whatever it is we were previously connected to. + +[source,sh] +---- +#!/usr/bin/env sh +PHONE_NS=phone_ns +IF=enp0s20f0u6 + +sudo ip netns add ${PHONE_NS} +sudo ip link set ${IF} netns ${PHONE_NS} +sudo ip netns exec ${PHONE_NS} ip link set ${IF} up +sudo ip netns exec ${PHONE_NS} ip link set dev lo up +sudo ip netns exec ${PHONE_NS} dhclient ${IF} +---- + +[source,sh] +---- +$ sudo ip netns exec home_ns curl -4 icanhaveip.com +113.158.237.102 +$ curl -4 icanhasip.com +114.201.132.98 +---- + +*_HINT_*: The IP addresses are made up. The only thing that matters is +that they are different. + +Since we have the android phone’s interface on another namespace the two +cannot interfere with each other. This is pretty much how docker uses +namespaces. Without a network namespace we would have to make a small +VM, run a VPN on the VM and then make a socks5 proxy to the VM from the +host and then have applications pass their traffic through a socks5 +proxy with varying degrees of success. *_NOTE_*: since we are not +running the script on a hook, you might blow out your net having two +upstreams at the same time. In which case, run the script, then restart +NetworkManager or whatever you have. + +=== SBOM and Provenance Attestation + +What is SBOM? NIST defines SBOM as a ``formal record containing the +details and supply chain relationships of various components used in +building software.''. It contains details about the components used to +create a certain piece of software. SBOM is meant to help mitigate the +threat of supply chain attacks(remember xz?). + +What is provenance? + +.... +The provenance attestations include facts about the build process, including details such as: + + Build timestamps + Build parameters and environment + Version control metadata + Source code details + Materials (files, scripts) consumed during the build +.... + +https://docs.docker.com/build/attestations/sbom/[source] + +==== Example + +Let’s review all that we learned about in the form of a light exercise. + +For the first build, we use a non-vendored version. Vendoring means that +you store your dependencies locally. This means you are in control of +your dependencies. You don’t need to pull them from a remote. Even if +one or more of your dependencies One of the more famous examples is Lua. +The Lua foundation actually recommend vendoring your Lua dependency. +Vendoring helps with build reproducability. + +We will use https://github.com/terminaldweller/milla[milla] as an +exmaple. It’s a simple go codebase. + +[source,dockerfile] +---- +FROM alpine:3.19 as builder +RUN apk update && \ + apk upgrade && \ + apk add go git +WORKDIR /milla +COPY go.sum go.mod /milla/ +RUN go mod download +COPY *.go /milla/ +RUN go build + +FROM alpine:3.19 +ENV HOME /home/user +RUN set -eux; \ + adduser -u 1001 -D -h "$HOME" user; \ + mkdir "$HOME/.irssi"; \ + chown -R user:user "$HOME" +COPY --from=builder /milla/milla "$HOME/milla" +RUN chown user:user "$HOME/milla" +ENTRYPOINT ["home/user/milla"] +---- + +The first docker image build is fairly simple. We copy the source code +in, get our dependencies and build a static executable. As for the +second stage of the build, we simply put the executable into a new base +image and we are done. + +The second build which is a vendored build with a golang distroless +base. We copy over the source code for the project and all its +dependencies and then do the same as before. + +[source,dockerfile] +---- +FROM golang:1.21 as builder +WORKDIR /milla +COPY go.sum go.mod /milla/ +RUN go mod download +COPY *.go /milla/ +RUN CGO_ENABLED=0 go build + +FROM gcr.io/distroless/static-debian12 +COPY --from=builder /milla/milla "/usr/bin/milla" +ENTRYPOINT ["milla"] +---- + +Below You can see an example docker compose file. Milla can optionally +use a postgres database to store messages. We also include a pgadmin +instance. Now let’s talk about the docker compose file. + +[source,yaml] +---- +services: + terra: + image: milla_distroless_vendored + build: + context: . + dockerfile: ./Dockerfile_distroless_vendored + deploy: + resources: + limits: + memory: 128M + logging: + driver: "json-file" + options: + max-size: "100m" + networks: + - terranet + user: 1000:1000 + restart: unless-stopped + entrypoint: ["/usr/bin/milla"] + command: ["--config", "/config.toml"] + volumes: + - ./config.toml:/config.toml + - /etc/localtime:/etc/localtime:ro + cap_drop: + - ALL + runtime: runsc + postgres: + image: postgres:16-alpine3.19 + deploy: + resources: + limits: + memory: 4096M + logging: + driver: "json-file" + options: + max-size: "200m" + restart: unless-stopped + ports: + - "127.0.0.1:5455:5432/tcp" + volumes: + - terra_postgres_vault:/var/lib/postgresql/data + - ./scripts/:/docker-entrypoint-initdb.d/:ro + environment: + - POSTGRES_PASSWORD_FILE=/run/secrets/pg_pass_secret + - POSTGRES_USER_FILE=/run/secrets/pg_user_secret + - POSTGRES_INITDB_ARGS_FILE=/run/secrets/pg_initdb_args_secret + - POSTGRES_DB_FILE=/run/secrets/pg_db_secret + networks: + - terranet + - dbnet + secrets: + - pg_pass_secret + - pg_user_secret + - pg_initdb_args_secret + - pg_db_secret + runtime: runsc + pgadmin: + image: dpage/pgadmin4:8.6 + deploy: + resources: + limits: + memory: 1024M + logging: + driver: "json-file" + options: + max-size: "100m" + environment: + - PGADMIN_LISTEN_PORT=${PGADMIN_LISTEN_PORT:-5050} + - PGADMIN_DEFAULT_EMAIL=${PGADMIN_DEFAULT_EMAIL:-devi@terminaldweller.com} + - PGADMIN_DEFAULT_PASSWORD_FILE=/run/secrets/pgadmin_pass + - PGADMIN_DISABLE_POSTFIX=${PGADMIN_DISABLE_POSTFIX:-YES} + ports: + - "127.0.0.1:5050:5050/tcp" + restart: unless-stopped + volumes: + - terra_pgadmin_vault:/var/lib/pgadmin + networks: + - dbnet + secrets: + - pgadmin_pass +networks: + terranet: + driver: bridge + dbnet: +volumes: + terra_postgres_vault: + terra_pgadmin_vault: +secrets: + pg_pass_secret: + file: ./pg/pg_pass_secret + pg_user_secret: + file: ./pg/pg_user_secret + pg_initdb_args_secret: + file: ./pg/pg_initdb_args_secret + pg_db_secret: + file: ./pg/pg_db_secret + pgadmin_pass: + file: ./pgadmin/pgadmin_pass +---- + +We are assigning memory usage limits for the containers. We are also +limiting the size of the logs we are keeping on disk. One thing that we +did not talk about before is the networking side of compose. As can be +seen, the postgres and pgadmin container share one network while the +postgres container and milla share another network. This makes it so +that milla and pgadmin do not have access to each other. This is inline +with principle of least privilege. Milla and pgadmin don’t need to talk +to each other so they can’t do that. Also we refrain from using host +networking. We are also binding the open ports to the host’s localhost +interface. This does not let us connect to the endpoints directly. In +our example we don’t need the ports to be exposed to the internet but we +will need access to them. What we can do is bind the open ports to the +host’s localhost and then use ssh to forward the ports onto our own +machine, assuming the docker host is a remote. + +[source,sh] +---- +ssh -L 127.0.0.1:5460:127.0.0.1:5455 user@remotehost +---- + +While building milla, for the second stage of the build, we made a +non-privileged user and our now mapping a non-privileged user on the +host to that user. We are removing all capabilities from milla since +milla will be making requests and has no server functionality. Milla +will only need to bind to high-numbered ports which does not require a +special privileges. We run both postgres and milla with gvisor’s runsc +runtime since it’s possible to do so. Finally we use docker secrets to +put the secrets into the container’s runtime environment. + +Now onto the attestations. In order to view the SBOM for the image we +will use docker https://docs.docker.com/scout/install/[scout]. + +[source,sh] +---- +docker scout sbom milla +docker scout sbom milla_distroless_vendored +---- + +The SBOMs can be viewed +https://gist.github.com/terminaldweller/8e8ecdcb68d4052aecb6804823648b4d[here] +and +https://gist.github.com/terminaldweller/f4ede7122f159506f8e6e6be2bfd6a8b[here] +respectively. + +Now lets look at the provenance attestations. + +[source,sh] +---- +docker buildx imagetools inspect terminaldweller/milla:main --format "{{ json .Provenance.SLSA }}" +---- + +And +https://gist.github.com/terminaldweller/033ae07a9e685db85b18eb822dea4be3[here] +you can look at the result. + +=== Further Reading + +* https://manpages.debian.org/bookworm/manpages/cgroups.7.en.html[man 7 +cgroups] +* system containers using https://github.com/lxc/incus[lxc/incus] +* https://katacontainers.io/[katacontainers] ++ ++ +timestamp:1716163133 ++ +version:1.0.0 ++ +https://blog.terminaldweller.com/rss/feed ++ +https://raw.githubusercontent.com/terminaldweller/blog/main/mds/securedocker.md ++ diff --git a/put_in_db.js b/put_in_db.js index e641187..af0a5ad 100644 --- a/put_in_db.js +++ b/put_in_db.js @@ -68,6 +68,14 @@ var blogs = [ keywords: ["NTP", "NTS", "SOCKS5"], lastUpdatedAt: Date.now(), }, + { + title: "Docker, Linux, Security. Kinda.", + slug: "docker_linux_security", + body: fs.readFileSync(path.join(__dirname) + "/mds/securedocker.md"), + teaser: fs.readFileSync(path.join(__dirname) + "/mds/securedocker.txt"), + keywords: ["docker", "linux", "security"], + lastUpdatedAt: Date.now(), + }, ]; db.blogposts.insertMany(blogs); |