From 5cc528c55d27d0e671f7ae99424c0fefb594bf3e Mon Sep 17 00:00:00 2001 From: Rezrov Frotz Date: Wed, 11 Jun 2014 11:58:26 -0400 Subject: Added options for chroot and setting the server uid/gid. --- CHANGES | 2 +- README.md | 69 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ miniircd | 37 +++++++++++++++++++++++++++++++++- 3 files changed, 106 insertions(+), 2 deletions(-) diff --git a/CHANGES b/CHANGES index a98d127..b57af64 100644 --- a/CHANGES +++ b/CHANGES @@ -1,5 +1,5 @@ -Unreleased + * Added support for --chroot and -setuid (fork by rezrov) * Added support for the LUSERS command (from Alex Wright). * Added basic SSL support (from Leandro Lucarella). diff --git a/README.md b/README.md index f59f699..b772a49 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,9 @@ miniircd is a small and limited IRC server written in Python. Despite its size, it is a functional alternative to a full-blown ircd for private or internal use. Installation is simple; no configuration is required. +This fork adds the ability to specify a chroot jail for the server process, +as well as the ability to set the user/group of the process. (*nix OSes only) + Features -------- @@ -17,6 +20,7 @@ Features * No configuration. * No ident lookup (so that people behind firewalls that filter the ident port without sending NACK can connect without long timeouts). +* Reasonably secure when used with --chroot and --setuid Limitations ----------- @@ -41,6 +45,70 @@ Installation None. Just run "./miniircd --help" (or "python miniircd --help") to get some help. +Some Notes on --chroot and --setuid +----------------------------------- + +In order to use the --chroot or --setuid options, you must be using an OS +that supports these functions (most \*nixes), and you must start the server +as root. Generally, you would not want to run just any random thing you've +grabbed from github and run it as root! Fortunately this script is short enough that the +average programmer can puruse it in a few minutes and determine whether or +not it is overtly malicious. + +Creating a Jail for --chroot +---------------------------- + +If you want to run miniircd in a chroot jail, do something like the +following as root (only tested on Linux). Assuming your chroot jail is +going to be /var/jail/miniircd, first create some required device nodes: + +``` +# mkdir -p /var/jail/miniircd/dev +# mknod /var/jail/miniircd/dev/null c 1 3 +# mknod /var/jail/miniircd/dev/random c 1 8 +# mknod /var/jail/miniircd/dev/urandom c 1 9 +# chmod 666 /var/jail/miniircd/dev/* +``` +If you have a motd file or an SSL pem file, you'll need to put them in the +jail as well: + +``` +# cp miniircd.pem motd.txt /var/jail/miniircd +``` + +Remember to specify the paths for --statedir, --logdir, --motd, and +--ssl-pem-file from within the jail, e.g.: + +``` +# ./miniircd --statedir=/ --logdir=/ --motd=/motd.txt \ + --ssl-pem-file=/miniircd.pem --chroot=/var/jail/miniircd +``` +Make sure your jail is writable by whatever user/group you are running +the server as. Also, keep your jail clean. Ideally it should only contain +the files mentioned above and the state/log files from miniircd. You should +**not** place the miniircd script itself, or any executables, in the jail. + +``` +# ls -alR /var/jail/miniircd +.: +total 36 +drwxrwxr-x 3 nobody rezrov 4096 Jun 10 16:20 . +drwxr-xr-x 4 root root 4096 Jun 10 18:40 .. +-rw------- 1 nobody nobody 26 Jun 10 16:20 #channel +-rw-r--r-- 1 nobody nobody 1414 Jun 10 16:51 #channel.log +drwxr-xr-x 2 root root 4096 Jun 10 16:19 dev +-rw-r----- 1 rezrov nobody 5187 Jun 9 22:25 ircd.pem +-rw-r--r-- 1 rezrov nobody 17 Jun 9 22:26 motd.txt + +./dev: +total 8 +drwxr-xr-x 2 root root 4096 Jun 10 16:19 . +drwxrwxr-x 3 nobody rezrov 4096 Jun 10 16:20 .. +crw-rw-rw- 1 root root 1, 3 Jun 10 16:16 null +crw-rw-rw- 1 root root 1, 8 Jun 10 16:16 random +crw-rw-rw- 1 root root 1, 9 Jun 10 16:19 urandom +``` + License ------- @@ -57,3 +125,4 @@ Contributors Alex Wright Leandro Lucarella Matt Behrens +Ron Fritz diff --git a/miniircd b/miniircd index 33f6867..9c66e4a 100755 --- a/miniircd +++ b/miniircd @@ -32,7 +32,8 @@ import tempfile import time from datetime import datetime from optparse import OptionParser - +from pwd import getpwnam +from grp import getgrnam def create_directory(path): if not os.path.isdir(path): @@ -639,6 +640,8 @@ class Server(object): self.verbose = options.verbose self.debug = options.debug self.logdir = options.logdir + self.chroot = options.chroot + self.setuid = options.setuid self.statedir = options.statedir self.name = socket.getfqdn()[:63] # Server name limit from the RFC. self.channels = {} # irc_lower(Channel name) --> Channel instance. @@ -744,6 +747,14 @@ class Server(object): serversockets.append(s) del s self.print_info("Listening on port %d." % port) + if self.chroot: + os.chdir(self.chroot) + os.chroot(self.chroot) + self.print_info("Changed root directory to %s" % self.chroot) + if self.setuid: + os.setresgid(self.setuid[1],self.setuid[1],self.setuid[1]) + os.setresuid(self.setuid[0],self.setuid[0],self.setuid[0]) + self.print_info("Setting uid:gid to %s:%s" % (self.setuid[0], self.setuid[1])) last_aliveness_check = time.time() while True: (iwtd, owtd, ewtd) = select.select( @@ -833,6 +844,16 @@ def main(argv): "--verbose", action="store_true", help="be verbose (print some progress messages to stdout)") + if os.name == "posix": + op.add_option( + "--chroot", + metavar="X", + help="Change filesystem root to directory X after startup (requires root)") + op.add_option( + "--setuid", + metavar="U[:G]", + help="Change process user (and optionally group) after startup (requires root)") + (options, args) = op.parse_args(argv[1:]) if options.debug: options.verbose = True @@ -845,6 +866,20 @@ def main(argv): options.ports = "6667" else: options.ports = "6697" + if options.chroot: + if os.getuid() != 0: + op.error("Must be root to use --chroot") + if options.setids: + if os.getuid() != 0: + op.error("Must be root to use --setuid") + match = re.findall(r"([a-z_][a-z0-9_-]*[\$]?)", options.setids) + + if len(match) > 1: + options.setids = (int(getpwnam(match[0]).pw_uid),int(getgrnam(match[1]).gr_gid)) + elif len(match) == 1: + options.setids = (int(getpwnam(match[0]).pw_uid),int(getpwnam(match[0]).pw_gid)) + else: + op.error("Specify a user, or user and group separated by a semicolon, e.g. --setuid daemon, --setuid nobody:nobody") ports = [] for port in re.split(r"[,\s]+", options.ports): try: -- cgit v1.2.3