Go has a neat keyword called defer that is used to ensure that a function call is performed later in a program’s execution, usually for purposes of cleanup.
Suppose we wanted to create a file, write to it, and then close when we’re done:
package main
import"fmt" import"os"
funccreateFile(p string) *os.File { fmt.Println("creating") f, err := os.Create(p) if err != nil { panic(err) } return f }
funcmain() { f := createFile("/tmp/defer.txt") defer closeFile(f) writeFile(f) }
Immediately after getting a file object with createFile, we defer the closing of that file with closeFile. This will be executed at the end of the enclosing function (main), after writeFile has finished.
Running the program confirms that the file is closed after being written:
C++ has a neat feature called Resource acquisition is initialization, a.k.a RAII. There are a lot of resources online that explain what is RAII and how it works, Tom Dalling’s for example.
But why do we need the excess [&]() { ... ; } part? and what is it anyway? [&] tells the compiler to pass all locals by reference, and () is used to indicate function args. We want this behaviour for allDEFER calls, so let’s put it in the macro:
The neat part is that we can call DEFER multiple times without having to name variables, because each DEFER call creates a ScopeGuard with a random name in order to avoiding colissions;
// curly-braces and trailing comma's are not mandatory. // the previous statement could've been written like this too: DEFER ( std::cout << "closing file" << std::endl; f.close() );
You must be thinking that I’m re-inventing the wheel. Well, I’m not. Let look at the following scenario:
There’s a binary that you want to run in a non-interactive session.
You want the binary to run with different permissions then the current user.
You don’t want the user to be able to run any binary with any permissions, only the one you want, with the requested user / group.
You don’t want a child process to get created, because you want to run the binary as part of a filter without any other processes getting in the way.
A good example would be to debug an elevated app, while running your editor regularly. for example -> running gdb and debugging a binary as root.
You probably don’t want to turn on Set owner User ID because that’s a major security hole. You also can’t use su / sudo as part of your editor / IDE because they execute the target process as child, which causes many issues.
sudo is also somewhat complex to configure, and honestly, I prefer to avoid using it alltogether.
Solution
A tool that is easy to configure & runs the target binary with requested owner:group. runas is that tool. It does one thing, and (hopefully) does it well.
runas doesn’t have any complicated flags or knobs.
$ runas Usage: bin/runas user-spec command [args]
version: 0.1.2, license: MIT
It just lets you run binaries:
$ runas root:root bash -c 'whoami && id' You can't execute '/bin/bash -c whoami && id' as 'root:root': Operation not permitted
But you need need the proper permissions to do so.
Notice I added /usr/bin/bash which is linked to /bin/bash. runas follows links to their source, to make sure the right binary is called. It also mimics the way shells parse commands so the configuration and command should be identical.
For instance, 'whoami && id' is concatenated by the shell into one argument. runas makes sure you don’t have to think about the way things get parsed.
And now any member of the docker group can restart the docker daemon!
Fine-grained permissions
runas uses c++ 14, which comes with a built-in ECMAScript flavored regex library. Using regular expressions can be really helpful when you want to have a lot of control over given permissions, which is still easy to understand..
A good example would be to allow the user to run only “readonly” operations on systemd units:
gosu is a tool that was invented to solve TTY & signaling issues, mainly for containers. As I said before, sudo and su run the target process as a child, which means all signals are passed to them, and sometimes aren’t forwarded propely. gosu solves that issue, but doesn’t provide a permissions mechanism which makes it practically impossible to use on regular systems that need an extra layer security.
gosu is also written in Go, which is notoriously known for creating really big binaries:
1.23MB for the amd64 release
1.1MB for the i386 release
runas‘s binary takes only 200KB unpacked, and ~60KB when packed with UPX.
A few days ago I installed PulseSecure’s client to gain access to the corporate VPN. For reasons comepletely unknown to me, these guys are stuck in the past, providing only a 32bit client for linux. WTF.
Anyway, I got everything working with Open Connect so I could safely remove all 32bit dependencies I added to my system.
How? I ran dpkg --remove-architecture i386 && apt-get purge ".*:i386". BIG. BIG MISTAKE.
TL;DR: many parts of the system broke, but I got everything up and running after an hour or so.
There were only two things that stayed broken:
The date/time clock, which is usually centered at the top bar, moved to the right.
I had a window list bar stuck at the bottom of the screen.
I couldn’t find solutions to either. It seemed that everyone online were trying to move the clock to the right, not to the middle. Moreover, every time I tried to disable the Window List extension, it came back.
While trying to remove the extension, I found a small configuration file at /usr/share/gnome-shell/modes called classic.json, with the following content:
What should we do, or leave undone, in a day or a lifetime? How much messiness should we accept? What balance of the new and familiar is the most fulfilling? These may seem like uniquely human quandaries, but they are not. Computers, like us, confront limited space and time, so computer scientists have been grappling with similar problems for decades. And the solutions they’ve found have much to teach us.
In a dazzlingly interdisciplinary work, Brian Christian and Tom Griffiths show how algorithms developed for computers also untangle very human questions. They explain how to have better hunches and when to leave things to chance, how to deal with overwhelming choices and how best to connect with others. From finding a spouse to finding a parking spot, from organizing one’s inbox to peering into the future, Algorithms to Live By transforms the wisdom of computer science into strategies for human living.
I’ll try and give you a little taste of the book - which I’m still reading (more precisely, listening), so you’ll know what you’re getting into.
A week ago I migrated my blog from Ghost to Hexo to gain better performance and save money.
Hexo is said to be “Blazing Fast”, but while I did “feel” that my Hexo based site was snappier than its predecessor, it was far from “Blazing Fast”.
Performance is extremely important. There are a lot of articles on the subject, most of which point out that website performance & uptime are key to user satisfaction. WebpageFX wrote a nice summary of the subject - Why Website Speed is Important.
I’m not a web developer, and have almost zero knowledge in website optimizations. Nonetheless, I’ve optimized more then a few apps in my career and know how to approach such problems.
All I need is to figure out the tooling to find the bottlenecks and fix them to gain good enough performance. That is, I’m not looking into optimizing every single piece of the website, only making it fast enough so it’ll feel snappy.
This blog post explains the steps I took in order to dramatically decrease the average page size to less then 350k.
7 months ago I made a new years resolution to master vim: The Road to Mastering Vim. I’m not a master just yet, it’s going to take a few years. After 7 months of exclusively editing text & code with vim, I can honestly say that I’m feeling at home and I can’t go back.
A few days ago I told the world that I’m moving to Cybereason. I didn’t say that I’m going as hard core as it gets - joining the team that develops the agent on Linux endpoints.
Today I needed to add a few features to an existing .NET Core application. I’m running Fedora 25, but that shouldn’t be an issue, right? because -
It appears that it doesn’t love Fedora 25, because it’s still not officially supported. Instead of hacking around and trying to get this thing working, and wasting my whole day doing so, I thought - why not use Docker?
The idea was simple - create a container that fires up Visual Studio Code inside a container that has .NET Core installed.