summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES2
-rw-r--r--README.md69
-rwxr-xr-xminiircd37
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: