OpenBSD as a “Desktop” (Laptop)

My daughter has finally finished transferring all of her files off to her new laptop, and I have graciously inherited the old one.  The laptop is a Lenovo Flex 3.  It has one of those screens that folds all the way around, and a touch screen so you can treat it like a “tablet” sometimes.  It has one RJ45 jack for a Realtek 8168 chipset ethernet device.  This can do up to 1000BaseT full-duplex, which is nice.

The built in wireless is an unsupported chipset, so I’m not using wireless networking on it, yet.  I’ll probably pull the adapter from the Hak5 field kit for use with this laptop.

There is no CD or DVD drive included.  This meant having to use a USB thumb drive for the installation.  This wasn’t a problem, just something to note.

The first thing I did was use the already made Linux Mint 18.1 thumb drive I had laying around from building my new work laptop so that I could boot the machine into a live system that I could take a disk image from.  I plugged in my 3TB Western Digital external drive, ran “dd” to dump the internal drive to an image file on the external drive, and then put the Western Digital back in its place on the shelf.

Next, I built a new USB disk with the OpenBSD 6.1 Release installation media image.  I had to go into the BIOS / UEFI settings and change the boot order for the USB drive to boot first, which I had already done to get Mint working in the previous step.  However, to get OpenBSD working, I also had to change the boot type to “Legacy Boot.”  I found documentation that says OpenBSD should work in UEFI mode, but it refused to install in that mode, so I’m noting it here that this setting had to be changed.  I also took advantage of the time to turn on the virtualization settings in the BIOS / UEFI, because I plan to play with the new vmm commands at some point.

Once I got OpenBSD safely installed using the default “use whole disk” and default partition scheme, I set up my package URL settings and pulled down the xfce package and its dependencies.  I only did this, because I use the XFCE desktop environment on Mint, and I wanted a familiar X experience, to start.  I don’t normally run X on OpenBSD, and it comes with some nice light weight window managers by default, but I want to ease into playing with those, so I installed the fluff.  I also installed Firefox and its dependencies, because I intend to do a lot of the work for both UnixSecLab and Jack of all Hobbies from this machine, moving forward.  To get “startx” to load XFCE, I created a “.xinitrc” file in my normal users’ home directories that just contain the line “xfce4-session” for now.  I am unhappy with the fact that as I type, if I brush the touch pad, it causes the mouse to steal focus and my typing either jumps around, or accidentally “clicks” on something it shouldn’t.  There is a utility built into OpenBSD that should help prevent that behavior, I just haven’t taken the time yet to set it up and tweak it.  It’s on my to-do list.

So far, everything works swimmingly well.  I have three users created: one is my “main” with doas permissions (doas replaced sudo recently,) as well as one each for the two blogs / businesses I’m running.

Speaking of “doas,” I’m actually happy that I have the option to install sudo as a package, while still retaining “doas” as the primary privilege escalation mechanism.  It means that when I do research and articles on sudo configuration, I can do all kinds of crazy configurations that are “broken” without breaking my production privilege escalation configuration.

Firefox works for all of my needs.  I’m able to do my Canva images for the “quote graphics” I’ve been posting, lately.  I’m able to watch YouTube videos in HTML5 mode without issues, other than the ads not playing.  I’m okay with that, except that I sometimes like to let the ad run for 30 seconds before skipping it, so that the channel gets some credit for it, and can get paid.  I like to support the people I like to watch, and that’s one way to do it.  You have to go to your YouTube settings to turn on HTML5 mode, if it’s not working for you by default.  Just google “youtube html5” to find the link for it.  Speaking of Google, all of its tools work fine as expected.  The only thing I can’t do from the OpenBSD machine that I can do from my Windows laptop is play Guild Wars 2, I think.

I still have some of my older series content that needs finalization, such as the SSH Start to Finish series, which should be easier to get going again on this new setup.

All in all, I’m happy with the system, but I’m an OpenBSD fan boy.  It’s hard to get mad at a system that generally “just works.”

Sudo Policy Fixes and Fails – The Runas Alias

Continuing our Sudo policy series, we’re going to focus on the Runas_Alias label this week.  In many regards, this looks much like the User_Alias in that it takes users and groups and the syntax is the same.

However, its purpose is much different.  Where the User_Alias identifies who CAN run this policy entry, the Runas_Alias defines which users and groups the policy may be executed under.

The typical default entry for this (much like the Host_Alias label) is “ALL” which allows the command to be executed under any user or group identified by the calling user.

Typical sudo calls look like this:

sudo my_command

This defaults to calling “my_command” as the root user.  The policy probably had “(ALL)” as the Runas_Alias label, as mentioned above.  We don’t always want to let a user run a command as root, though.  Sometimes we want to allow one user to call a command as another non privileged user, just because that process belongs to that user.  To do this, we set a Runas_Alias label that restricts the command to being executed under that user, not root, and not “ALL.”

With such a policy in place, the user has to know how to call sudo to execute under someone other than the default “root” user.  The “-u” flag allows this.

sudo -u otheruser my_command

Sometimes, we also need to call the command as a different group.  In order to do this, we can pass the “-g” flag.  A combination of both is valid.

sudo -u otheruser -g othergroup my_command

This allows for more granular control over the policies being written, and enforces safe policy that doesn’t grant access to root except when absolutely necessary.

This is the end goal of any good policy.  Only allow what is strictly necessary, and nothing more than that.

Sudo Policy Fixes and Fails – The Host_Alias

Last week we looked at the User_Alias label.  This week, we’re going to look at the Host_Alias label.  Within a policy line, the “Host_Alias” is the next section of that policy.  This dictates on which systems/servers this policy will be allowed to run.

When the policy is unique to each server, is not maintained as a centralized monolithic structure that gets shared out to all end points, or isn’t stored in a database such as LDAP, the usual default is the “ALL” entry.  This simplifies figuring out which items to put here for a given server.  It may seem like that answer is simple, but in reality, it’s not, due to the way sudo checks the host when called to execute a policy request.

What do I mean by that?  The below snippet from the sudoers man page addresses this better than my own words ever could.

A Host_List is made up of one or more host names, IP addresses, network numbers, netgroups (prefixed with ‘+’) and other aliases.

If you do not specify a netmask along with the network number, sudo will query each of the local host's network interfaces and, if the network number corresponds to one of the hosts's network interfaces, the corresponding netmask will be used.  The netmask may be specified either in standard IP address notation (e.g. 255.255.255.0 or ffff:ffff:ffff:ffff::), or CIDR notation (number of bits, e.g. 24 or 64).  A host name may include shell-style wildcards (see the Wildcards section below), but unless the host name command on your machine returns the fully qualified host name, you'll need to use the fqdn option for wildcards to be useful.  Note that sudo only inspects actual network interfaces; this means that IP address 127.0.0.1 (localhost) will never match.  Also, the host name “localhost” will only match if that is the actual host name, which is usually only the case for non-networked systems.

As you can see, there is some reliance upon what the “host name” command returns, whether an IP is ever checked based on the actual network interfaces configured, and so on.  The loopback interface is never checked.  It is recommended to create a test policy with different Host_Alias entries that LOOK like they should work before making any assumptions about how your policy will actually behave once installed.  If you frequently change the IP address(es) of your systems, a host name is preferred.  If you change the name frequently, such as during a migration or similar, setting policy by IP might be better for your environment.  Or maybe the name is better, and you include the name change inside your distributed monolithic policy file, or LDAP entry, or what have you.  In the end, you need to figure out what will work best for your environment and be consistent with how you handle things.  It doesn’t hurt to use both host name and IP in a policy, so you can set your alias label for either, or both.

Also, you can do negation with this just as you can with User_Alias entries.  You could say “ALL, !www.example.com” to make this apply to any system that isn’t “www.example.com” (for example.)

The Host_Alias label is less important to change from the almighty “ALL” if you aren’t doing some kind of distributed shared policy, but if you get to more than a few end point servers that are not identical use case machines, you will eventually want to consider such a move.  This is why it is important to learn how the Host_Alias actually behaves for policy on each machine, and test your policy to be sure it does what you expect.

Next week we’ll cover Runas_Alias, as it is the next section of a policy line.

Thanks for reading!

Sudo Policy Fixes and Fails – The User Alias

We covered this on a very basic level last week, but today we’re going to focus on the User_Alias policy entries.

A sudo policy begins with a list of users and/or groups.  This list can be a raw listing, or it can be a label created by a “User_Alias” entry.  Organization of the policy file depends on your needs, but one common way to organize things is to use the aliases to define how pieces of a policy should look, then put all of the policies at the bottom where the alias labels are used to build out those policies.  There are other ways to structure this, as well, and we’ll cover several of them later.  For now, we’ll assume that we are doing one section of the file to cover all User_Alias entries, another section for all Cmnd_Alias entries, and so on.

A User_Alias entry is simply the key word “User_Alias” followed by the label name you would like to use, and equal sign “=” and the list of users and/or groups, comma separated, that the labeled alias will define.

User_Alias U_WEBADMINS = %webslingers

If you want to allow a group, but restrict one member of the group from having the access to the policy, you can modify it like so:

User_Alias U_WEBADMINS = %webslingers, !venom

Less useful in most situations, but still available as an option, is to use UID and GID numbers to represent the users and groups.  If the “webslingers” group has GID 404, and the “venom” user has a UID of 666, the same policy as above could be written this way, instead:

User_Alias U_WEBADMINS = %#404, !666

If you are using yellow pages with net groups, you can also reference a netgroup using a “+” instead of “%” to represent the group.

Finally, non-Unix group names and GIDs are represented with “%:” and “%:#” prefixes, instead of just “%” or “%#” per normal.

This is simple stuff, but I’ve seen poorly constructed “user alias” entries before.  This is why we’re taking it slow and hitting each type separately as we go.  A lot of this will seem redundant, but hopefully it lays it out in a simple and easily understood manner.

Thanks for reading!  Next week will be Host_Alias labels, simply because it is the next piece of the policy entry as read from left to right.

Sudo Policy Fixes and Fails – The Basics of a Sudo Policy File

I tried to hold out on switching topics, but I’m going to switch to dealing with Sudo Policy for a while.  This SSH using GnuPG keys situation is taking longer than I ever intended, and I can’t keep posting “sorry for the delay” posts.  It isn’t right for me, and it isn’t right for you, my readers.  So today, I’m officially starting the Sudo Policy series.  My oldest daughter is getting a new laptop as a late graduation gift, and after she has transferred all of her files, I’ll be taking a drive dump of that laptop for archival purposes, then installing either OpenBSD (if it will work) or some flavor-of-the-month version of Linux on it.  I might install FreeBSD if OpenBSD doesn’t work, but we’ll have to wait and see how things shake out.

In the mean time, on to today’s topic!

The sudo command is nothing more than a policy engine.  This means it takes a requested input, compares it to a policy file that tells it what is and is not permitted, then either executes the input command on behalf of the calling user, or it does not.  This is as simple as it gets when describing the program itself.  In order to take advantage of this policy engine to allow some super user privilege level tasks to be granted to a set of non-privileged users, so that we (the sysadmins) don’t have to do their work for them, but also not give them the keys to the kingdom (root password,) we need to understand the policy file that sudo uses.

The master policy file for sudo is the /etc/sudoers file.  This file contains entries that tell sudo how to check that the requesting user is allowed to run the requested command as some other user on this system.  Let’s break that sentence down a bit and go over what that means, by looking at a policy entry.

%admin ALL=(ALL) ALL

The “requesting user” has a special label within the policy file, called the “User_Alias” label.  In this case, the “User_Alias” entry is a group called “admin.”  We know it is a group, because of the percent sign in front of the name.  This corresponds to a group name at a system level.

The “on this system” portion of the policy entry is next.  The first “ALL” you see, just before the equal sign indicates that this policy is valid for all hosts.  The special label for this is the “Host_Alias.”  For simplicity, this is often left as “ALL” on most systems.  However, this gives us the flexibility to deal with using one monolithic policy file on multiple systems without accidentally granting more than we mean to on any one given system.

The “as some other user” portion of the policy entry comes after the equal sign, and is surrounded by parenthesis.  This is another label that may contain a list of users, groups, or a mix of both.  Groups still need a percent sign to distinguish them from users.  The label for this is “Runas_Alias.”

The “requested command” is the last portion of the policy entry, and it  contains a list of commands to allow or deny, depending on whether you are white-listing the command, or black-listing it.  In all cases, assume that black-listing is not even an option.  I will explain why in a later post as we cover things more in depth.  The label for this is “Cmnd_Alias.”

There is one more thing do consider for a standard policy entry, and that’s the modifier TAG that can come before a Cmnd_Alias to change its behavior.  By default, sudo prompts the user for their password when calling it to execute a command, but if you put the NOPASSWD: tag in front, it doesn’t prompt.  This should technically be used sparingly, and we’ll cover what the pitfalls are with some of these tags as we go along.

Finally, understand that the default settings compiled into sudo may be overridden with a different kind of policy entry called the “Defaults” label.  There are ways to also modify any of the “*_Alias” labels with options from the “Defaults” list, but we won’t cover those until much later in this series.

So for now, the most basic way to look at a policy line is like this:

User_Alias Host_Alias=(Runas_Alias) TAG: Cmnd_Alias

We’ll cover each of these components more in depth each week, but know that as soon as I get the GnuPG/Yubikey/SSH situation finally stable, repeatable, and without bugs, I’ll be returning to the SSH Start to Finish Mastery series to finish it up before returning to this new series overall.

Thanks again for reading, and leave a comment if you enjoyed this content!

Sudo Policy Fixes and Fails – Importance of auditing your sudoers policy files.

Wait, what?  Yes.  That is correct.  This week is skipping the SSH series to begin discussions on Sudo policy review.  We will return to the SSH series soon, but I really, really, really want the next post to cover the use of token devices (using the Yubikey 4 for the first discussion on this,) and I’m simply not prepared for it.  I’ve run into several road blocks on getting my new key programmed, all of which will be covered when I write that post.

Since I don’t want to skip a day of content, I decided to introduce the next major topic I’ll go into after wrapping up the SSH series.

Sudo isn’t the only privilege elevation policy engine available.  It isn’t even the only open source privilege elevation policy engine.  It is, however, one of the most popular, and powerful in its own right.  However, it is also one of the most easily misconfigured tools, and this can (and does) lead to very dangerous policy.

For our first foray into what does and does not constitute “Bad Sudo,” we will look at one of the most overlooked utilities provided by the sudo package: sudoedit.

Before we discuss “sudoedit,” we should look at the problem of granting elevated privileges to editors in general.

Let’s start with the classic “edit a file with vi” entry.

Cmnd_Alias EDITFILE = /usr/bin/vi /path/to/file
someuser ALL=(ALL) EDITFILE

The user “someuser” now has permission to call /usr/bin/vi to edit the file /path/to/file as root.  When this happens, sudo calls the /usr/bin/vi command as the root user.  Why is this dangerous?  The “vi” editor allows for calling out to the shell to execute commands.  You can do this by hitting the escape key, then “:!/path/to/bad/command.”

There is a tag that is often used to try to suppress this command from being able to do harm.  The NOEXEC tag can often be seen in configurations where this was attempted.

Cmnd_Alias EDITFILE = /usr/bin/vi /path/to/file
someuser ALL=(ALL) NOEXEC: EDITFILE

Unfortunately, this is a bandaid that may or may not work as intended.  It relies on interrupting a program that was built with dynamically linked libraries.  It also relies on the sudo command itself being compiled with the NOEXEC support built in.  If the command in question were “ed” instead of “vi” (or on older systems, “/bin/vi” instead of “/usr/sbin/vi” where “/bin/vi” was often statically compiled so that it could be used to help repair a bad /etc/fstab for boot issues, and /usr wasn’t mounted properly) this would be useless.  The tag would do nothing to prevent calling out to the shell.

Now let’s look at the correct way to handle this.

Cmnd_Alias EDITFILE = sudoedit /path/to/file
someuser ALL=(ALL) EDITFILE

As you can see, instead of calling “/usr/bin/vi” we call “sudoedit” to modify that file.  What does this do?  It’s simple.  The user will call “sudoedit /path/to/file” instead of “sudo vi /path/to/file.”  The sudoedit command will then obtain elevated privilege to access the file, make a copy of it to a temp file, then open an editor for the user AS THAT USER.  This means in our example, that the editor will be called as if “someuser” had called it himself.  The user can call out to the shell for command execution all day, but it will only run those commands without any more privilege than the user already had.

How does sudoedit know which editor to use?  From the man pages:

The editor specified by the policy is run to edit the temporary files.  The sudoers policy uses the SUDO_EDITOR, VISUAL and EDITOR environment variables (in that order).  If none of SUDO_EDITOR, VISUAL or EDITOR are set, the first program listed in the editor sudoers(5) option is used.

So we need to be sure to set a policy of allowing a handful of editors for our users, and then clean up any policy that is granting access directly to editors to fix bad policy.  In order to do this, we need to set a Defaults directive to list the available permissible editors.

Defaults editor="/usr/bin/vi:/usr/bin/emacs:/bin/ed:/usr/bin/nano"
Cmnd_Alias EDITFILE = sudoedit /path/to/file
someuser ALL=(ALL) EDITFILE

This will let users use either vi, emacs, ed, or nano, depending on preference. There is an “env_editor” option, but if it is set, it lets users set any editor they like. This could be something that isn’t actually an editor, so the safe thing to do is just give the list of approved editors in the option I listed, instead.

I will cover some more “gotchas” like this eventually, but I’m hoping I get all of the kinks worked out of my Yubikey adventure before next week, so I can get back to the SSH posts, first.

I hope this was useful to some of you!