[389-devel] Please Review: Add SELinux policy for SNMP subagent (ldap-agent)

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 




>From 48853ed48fa47146c83638bbe04381e04fd79721 Mon Sep 17 00:00:00 2001
From: Nathan Kinder <nkinder@xxxxxxxxxx>
Date: Wed, 16 Sep 2009 09:28:56 -0700
Subject: [PATCH] Add SELinux policy for ldap-agent.

This adds SELinux policy to confine the SNMP subagent (ldap-agent).
There were some changes required around the aubagent to make it
work in a more standard fashion.

I moved the ldap-agent binary and wrapper to sbindir.  It was
previously in bindir, yet it is not a user command.  The location
really should be sbindir per FHS.

I added init scripts for the subagent, so it can now be managed
using "service dirsrv-snmp [start|stop|restart|condrestart|status]".
While doing this, I found that the parent process was exiting with
1 on success instead of 0, so I fixed that.

I added a default config file for the subagent as well.  When using
the init script, the config file is hardcoded into this standard
location.  Having this config template should also hopefully cut
down on configuration errors since it's self documenting.

The pid file location was also changed to go into /var/run per FHS.
Previously, it was written to the same directory as the log file.

There are a few notes in the policy .te file about some bugs that
we are working around for now.  These bugs are mainly minor issues
in the snmp policy that is a part of the selinux-policy pacakge.
Once those bugs are fixed, we can clean our policy .te file up.
---
 Makefile.am                          |   21 ++-
 ldap/servers/snmp/ldap-agent.conf.in |   30 +++++
 ldap/servers/snmp/ldap-agent.h       |    2 +-
 ldap/servers/snmp/main.c             |   37 +++---
 selinux/dirsrv.fc.in                 |    4 +
 selinux/dirsrv.te                    |   85 +++++++++++++-
 wrappers/ldap-agent-initscript.in    |  221 ++++++++++++++++++++++++++++++++++
 wrappers/ldap-agent.in               |    2 +-
 8 files changed, 373 insertions(+), 29 deletions(-)
 create mode 100644 ldap/servers/snmp/ldap-agent.conf.in
 create mode 100644 wrappers/ldap-agent-initscript.in

diff --git a/Makefile.am b/Makefile.am
index 665b1f4..7f35c6a 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -135,10 +135,10 @@ defaultgroup=@defaultgroup@
 #------------------------
 # Build Products
 #------------------------
-sbin_PROGRAMS = ns-slapd
+sbin_PROGRAMS = ns-slapd ldap-agent-bin
 
-bin_PROGRAMS = dbscan-bin dsktune-bin infadd-bin ldap-agent-bin \
-	ldclt-bin ldif-bin migratecred-bin mmldif-bin pwdhash-bin rsearch-bin
+bin_PROGRAMS = dbscan-bin dsktune-bin infadd-bin ldclt-bin \
+	ldif-bin migratecred-bin mmldif-bin pwdhash-bin rsearch-bin
 
 server_LTLIBRARIES = libslapd.la libns-dshttpd.la
 
@@ -193,7 +193,8 @@ policy_DATA = $(POLICY_MODULE)
 
 config_DATA = $(srcdir)/lib/ldaputil/certmap.conf \
 	$(srcdir)/ldap/schema/slapd-collations.conf \
-	ldap/admin/src/template-initconfig
+	ldap/admin/src/template-initconfig \
+	ldap/servers/snmp/ldap-agent.conf
 
 # the schema files in this list are either not
 # standard schema, not tested, or not compatible
@@ -274,13 +275,13 @@ sbin_SCRIPTS = ldap/admin/src/scripts/setup-ds.pl \
 	ldap/admin/src/scripts/remove-ds.pl \
 	ldap/admin/src/scripts/start-dirsrv \
 	ldap/admin/src/scripts/stop-dirsrv \
-	ldap/admin/src/scripts/restart-dirsrv
+	ldap/admin/src/scripts/restart-dirsrv \
+        wrappers/ldap-agent
 
 bin_SCRIPTS = ldap/servers/slapd/tools/rsearch/scripts/dbgen.pl \
 	wrappers/dbscan \
 	wrappers/dsktune \
 	wrappers/infadd \
-	wrappers/ldap-agent \
 	wrappers/ldclt \
 	wrappers/ldif \
 	$(srcdir)/ldap/admin/src/logconv.pl \
@@ -343,7 +344,8 @@ task_SCRIPTS = ldap/admin/src/scripts/template-bak2db \
 	ldap/admin/src/scripts/template-verify-db.pl \
 	ldap/admin/src/scripts/template-dbverify
 
-init_SCRIPTS = wrappers/$(PACKAGE_NAME)
+init_SCRIPTS = wrappers/$(PACKAGE_NAME) \
+	wrappers/$(PACKAGE_NAME)-snmp
 
 initconfig_DATA = ldap/admin/src/$(PACKAGE_NAME)
 
@@ -1315,3 +1317,8 @@ endif
 %/$(PACKAGE_NAME): %/initconfig.in
 	if [ ! -d $(dir $@) ] ; then mkdir -p $(dir $@) ; fi
 	$(fixupcmd) $^ > $@
+
+%/$(PACKAGE_NAME)-snmp: %/ldap-agent-initscript.in
+	if [ ! -d $(dir $@) ] ; then mkdir -p $(dir $@) ; fi
+	$(fixupcmd) $^ > $@
+
diff --git a/ldap/servers/snmp/ldap-agent.conf.in b/ldap/servers/snmp/ldap-agent.conf.in
new file mode 100644
index 0000000..6593685
--- /dev/null
+++ b/ldap/servers/snmp/ldap-agent.conf.in
@@ -0,0 +1,30 @@
+# The agentx-master setting defines how to communicate
+# with the SNMP master agent using the AgentX protocol.
+# The default is to use a UNIX domain socket.  If your
+# master agent is listening on a tcp port for AgentX
+# subagents, use a line like the following:
+#
+# agentx-master localhost:705
+agentx-master /var/agentx/master
+
+# The agent-logdir settings defines where the subagent
+# will write it's logfile.
+agent-logdir @localstatedir@/log/@package_name@
+
+# The server setting specifies a Directory Server
+# instance that you want to monitor. You must use one
+# server setting for each Directory Server instance. The
+# subagent requires at least one server setting to be
+# specified. The server setting
+# should be set to the name of the Directory Server
+# instance you would like to monitor. For example:
+# 
+# server slapd-phonebook
+# 
+# To monitor multiple Directory Server instances,  add
+# an additional server parameter for each instance:
+# 
+# server slapd-phonebook
+# server slapd-example
+# server slapd-directory
+
diff --git a/ldap/servers/snmp/ldap-agent.h b/ldap/servers/snmp/ldap-agent.h
index 30253d1..664d7e2 100644
--- a/ldap/servers/snmp/ldap-agent.h
+++ b/ldap/servers/snmp/ldap-agent.h
@@ -90,7 +90,7 @@ extern          "C" {
 #define CACHE_REFRESH_INTERVAL 15
 #define UPDATE_THRESHOLD 20
 #define SNMP_NUM_SEM_WAITS 10
-#define LDAP_AGENT_PIDFILE ".ldap-agent.pid"
+#define LDAP_AGENT_PIDFILE "ldap-agent.pid"
 #define LDAP_AGENT_LOGFILE "ldap-agent.log"
 
 /*************************************************************
diff --git a/ldap/servers/snmp/main.c b/ldap/servers/snmp/main.c
index 5b2ad68..04c4ee3 100644
--- a/ldap/servers/snmp/main.c
+++ b/ldap/servers/snmp/main.c
@@ -191,7 +191,7 @@ main (int argc, char *argv[]) {
         fscanf(pid_fp, "%d", &child_pid);
         fclose(pid_fp);
         printf("ldap-agent: Started as pid %d\n", child_pid);
-        exit(1);
+        exit(0);
     }
 
     /* initialize the agent */
@@ -205,7 +205,7 @@ main (int argc, char *argv[]) {
     signal(SIGTERM, stop_server);
     signal(SIGINT, stop_server);
 
-    /* create pidfile in config file dir */
+    /* create pidfile */
     child_pid = getpid();
     if ((pid_fp = fopen(pidfile, "w")) == NULL) {
         snmp_log(LOG_ERR, "Error creating pid file: %s\n", pidfile);
@@ -272,25 +272,24 @@ load_config(char *conf_path)
     } 
 
     /* set pidfile path */
+    if ((pidfile = malloc(strlen(LOCALSTATEDIR) + strlen("/run/") +
+        strlen(LDAP_AGENT_PIDFILE) + 1)) != NULL) {
+        strncpy(pidfile, LOCALSTATEDIR, strlen(LOCALSTATEDIR));
+        /* The above will likely not be NULL terminated, but we need to
+         * be sure that we're properly NULL terminated for the below
+         * strcat() to work properly. */
+        pidfile[strlen(LOCALSTATEDIR)] = (char)0;
+        strcat(pidfile, "/run/");
+        strcat(pidfile, LDAP_AGENT_PIDFILE);
+    } else {
+        printf("ldap-agent: malloc error processing config file\n");
+        error = 1;
+        goto close_and_exit;
+    }
+
+    /* set default logdir to location of config file */
     for (p = (conf_path + strlen(conf_path) - 1); p >= conf_path; p--) {
         if (*p == '/') {
-            /* set pidfile path */
-            if ((pidfile = malloc((p - conf_path) +
-                                   strlen(LDAP_AGENT_PIDFILE) + 2)) != NULL) {
-                strncpy(pidfile, conf_path, (p - conf_path + 1));
-                /* The above will likely not be NULL terminated, but we need to
-                 * be sure that we're properly NULL terminated for the below
-                 * strcat() to work properly. */
-                pidfile[(p - conf_path + 2)] = (char)0;
-                strcat(pidfile, LDAP_AGENT_PIDFILE);
-                pidfile[((p - conf_path) + strlen(LDAP_AGENT_PIDFILE) + 1)] = (char)0;
-            } else {
-                printf("ldap-agent: malloc error processing config file\n");
-                error = 1;
-                goto close_and_exit;
-            }
-
-            /* set default logdir to location of config file */
             if ((agent_logdir = malloc((p - conf_path) + 1)) != NULL) {
                 strncpy(agent_logdir, conf_path, (p - conf_path));
                 agent_logdir[(p - conf_path)] = (char)0;
diff --git a/selinux/dirsrv.fc.in b/selinux/dirsrv.fc.in
index ae768b1..f61a871 100644
--- a/selinux/dirsrv.fc.in
+++ b/selinux/dirsrv.fc.in
@@ -4,14 +4,18 @@
 # MCS categories: <none>
 
 @sbindir@/ns-slapd			--	gen_context(system_u:object_r:dirsrv_exec_t,s0)
+@sbindir@/ldap-agent			--	gen_context(system_u:object_r:initrc_exec_t,s0)
+@sbindir@/ldap-agent-bin		--	gen_context(system_u:object_r:dirsrv_snmp_exec_t,s0)
 @sbindir@/start-dirsrv			--	gen_context(system_u:object_r:initrc_exec_t,s0)
 @sbindir@/restart-dirsrv		--	gen_context(system_u:object_r:initrc_exec_t,s0)
 @serverdir@					gen_context(system_u:object_r:dirsrv_lib_t,s0)
 @serverdir@(/.*)				gen_context(system_u:object_r:dirsrv_lib_t,s0)
 @localstatedir@/run/@package_name@		gen_context(system_u:object_r:dirsrv_var_run_t,s0)
 @localstatedir@/run/@package_name@(/.*)		gen_context(system_u:object_r:dirsrv_var_run_t,s0)
+@localstatedir@/run/ldap-agent.pid		gen_context(system_u:object_r:dirsrv_snmp_var_run_t,s0)
 @localstatedir@/log/@package_name@		gen_context(system_u:object_r:dirsrv_var_log_t,s0)
 @localstatedir@/log/@package_name@(/.*)		gen_context(system_u:object_r:dirsrv_var_log_t,s0)
+@localstatedir@/log/@package_name@/ldap-agent.log	gen_context(system_u:object_r:dirsrv_snmp_var_log_t,s0)
 @localstatedir@/lock/@package_name@		gen_context(system_u:object_r:dirsrv_var_lock_t,s0)
 @localstatedir@/lock/@package_name@(/.*)	gen_context(system_u:object_r:dirsrv_var_lock_t,s0)
 @localstatedir@/lib/@package_name@		gen_context(system_u:object_r:dirsrv_var_lib_t,s0)
diff --git a/selinux/dirsrv.te b/selinux/dirsrv.te
index 872e42f..b505c89 100644
--- a/selinux/dirsrv.te
+++ b/selinux/dirsrv.te
@@ -5,12 +5,26 @@ policy_module(dirsrv,1.0.0)
 # Declarations
 #
 
+# NGK - this can go away when bz 478629, bz 523548,
+# and bz 523771 are addressed.  See the notes below
+# where we work around those issues.
+require {
+    type snmpd_var_lib_t;
+    type snmpd_t;
+}
+
 # main daemon
 type dirsrv_t;
 type dirsrv_exec_t;
 domain_type(dirsrv_t)
 init_daemon_domain(dirsrv_t, dirsrv_exec_t)
 
+# snmp subagent daemon
+type dirsrv_snmp_t;
+type dirsrv_snmp_exec_t;
+domain_type(dirsrv_snmp_t)
+init_daemon_domain(dirsrv_snmp_t, dirsrv_snmp_exec_t)
+
 # dynamic libraries
 type dirsrv_lib_t;
 files_type(dirsrv_lib_t)
@@ -23,10 +37,18 @@ files_type(dirsrv_var_lib_t)
 type dirsrv_var_log_t;
 logging_log_file(dirsrv_var_log_t)
 
+# snmp log file
+type dirsrv_snmp_var_log_t;
+logging_log_file(dirsrv_snmp_var_log_t)
+
 # pid files
 type dirsrv_var_run_t;
 files_pid_file(dirsrv_var_run_t)
 
+# snmp pid file
+type dirsrv_snmp_var_run_t;
+files_pid_file(dirsrv_snmp_var_run_t)
+
 # lock files
 type dirsrv_var_lock_t;
 files_lock_file(dirsrv_var_lock_t)
@@ -93,7 +115,7 @@ files_pid_filetrans(dirsrv_t, dirsrv_var_run_t, { file sock_file })
 # ldapi socket
 manage_sock_files_pattern(dirsrv_t, dirsrv_var_run_t, dirsrv_var_run_t)
 
-#lock files
+# lock files
 manage_files_pattern(dirsrv_t, dirsrv_var_lock_t, dirsrv_var_lock_t)
 manage_dirs_pattern(dirsrv_t, dirsrv_var_lock_t, dirsrv_var_lock_t)
 files_lock_filetrans(dirsrv_t, dirsrv_var_lock_t, { file })
@@ -128,3 +150,64 @@ allow dirsrv_t self:tcp_socket { create_stream_socket_perms };
 init_use_fds(dirsrv_t)
 init_use_script_ptys(dirsrv_t)
 domain_use_interactive_fds(dirsrv_t)
+
+
+########################################
+#
+# dirsrv-snmp local policy
+#
+
+# Some common macros
+files_read_etc_files(dirsrv_snmp_t)
+miscfiles_read_localization(dirsrv_snmp_t)
+libs_use_ld_so(dirsrv_snmp_t)
+libs_use_shared_libs(dirsrv_snmp_t)
+dev_read_rand(dirsrv_snmp_t)
+dev_read_urand(dirsrv_snmp_t)
+files_read_usr_files(dirsrv_snmp_t)
+fs_getattr_tmpfs(dirsrv_snmp_t)
+fs_search_tmpfs(dirsrv_snmp_t)
+allow dirsrv_snmp_t self:fifo_file { read write };
+sysnet_read_config(dirsrv_snmp_t)
+sysnet_dns_name_resolve(dirsrv_snmp_t)
+
+# Net-SNMP /var/lib files (includes agentx unix domain socket)
+snmp_dontaudit_read_snmp_var_lib_files(dirsrv_snmp_t)
+snmp_dontaudit_write_snmp_var_lib_files(dirsrv_snmp_t)
+# NGK - there really should be a macro for this. (see bz 523771)
+allow dirsrv_snmp_t snmpd_var_lib_t:file append;
+# NGK - use snmp_stream_connect(dirsrv_snmp_t) when it is made
+# available on all platforms we build on (see bz 478629 and bz 523548)
+stream_connect_pattern(dirsrv_snmp_t, snmpd_var_lib_t, snmpd_var_lib_t, snmpd_t)
+
+# Net-SNMP agentx tcp socket
+corenet_tcp_connect_agentx_port(dirsrv_snmp_t)
+
+# Net-SNMP persistent data file
+files_manage_var_files(dirsrv_snmp_t)
+
+# stats file semaphore
+rw_files_pattern(dirsrv_snmp_t, dirsrv_tmpfs_t, dirsrv_tmpfs_t)
+
+# stats file
+read_files_pattern(dirsrv_snmp_t, dirsrv_var_run_t, dirsrv_var_run_t)
+
+# process stuff
+allow dirsrv_snmp_t self:capability { dac_override dac_read_search };
+
+# config file
+read_files_pattern(dirsrv_snmp_t, dirsrv_config_t, dirsrv_config_t)
+
+# pid file
+admin_pattern(dirsrv_snmp_t, dirsrv_snmp_var_run_t)
+files_pid_filetrans(dirsrv_snmp_t, dirsrv_snmp_var_run_t, { file sock_file })
+search_dirs_pattern(dirsrv_snmp_t, dirsrv_var_run_t, dirsrv_var_run_t)
+
+# log file
+manage_files_pattern(dirsrv_snmp_t, dirsrv_var_log_t, dirsrv_snmp_var_log_t);
+filetrans_pattern(dirsrv_snmp_t, dirsrv_var_log_t, dirsrv_snmp_var_log_t, file)
+
+# Init script handling
+init_use_fds(dirsrv_snmp_t)
+init_use_script_ptys(dirsrv_snmp_t)
+domain_use_interactive_fds(dirsrv_snmp_t)
diff --git a/wrappers/ldap-agent-initscript.in b/wrappers/ldap-agent-initscript.in
new file mode 100644
index 0000000..d4e791f
--- /dev/null
+++ b/wrappers/ldap-agent-initscript.in
@@ -0,0 +1,221 @@
+#!/bin/sh
+#
+# @package_name@-snmp    This starts and stops @package_name@-snmp
+#
+# chkconfig:   - 22 78
+# description: @capbrand@ Directory Server SNMP Subagent
+# processname: ldap-agent-bin
+# config:   @sysconfdir@/@package_name@/config/ldap-agent.conf
+# pidfile:     @localstatedir@/run/ldap-agent.pid
+#
+
+# Source function library.
+if [ -f /etc/rc.d/init.d/functions ] ; then
+. /etc/rc.d/init.d/functions
+fi
+# Source networking configuration.
+if [ -f /etc/sysconfig/network ] ; then
+. /etc/sysconfig/network
+fi
+
+# Check that networking is up.
+if [ "${NETWORKING}" = "no" ]
+then
+    echo "Networking is down"
+    exit 0
+fi
+
+# figure out which echo we're using
+ECHO_N=`echo -n`
+
+# some shells echo cannot use -n - linux echo by default cannot use \c
+echo_n()
+{
+    if [ "$ECHO_N" = '-n' ] ; then
+        echo "$*\c"
+    else
+        echo -n "$*"
+    fi
+}
+
+# failure and success are not defined on some platforms
+type failure > /dev/null 2>&1 || {
+failure()
+{
+    echo_n " FAILED"
+}
+}
+
+type success > /dev/null 2>&1 || {
+success()
+{
+    echo_n " SUCCESS"
+}
+}
+
+baseexec="ldap-agent"
+exec="@sbindir@/$baseexec"
+processname="ldap-agent-bin"
+prog="@package_name@-snmp"
+pidfile="@localstatedir@/run/ldap-agent.pid"
+configfile="@sysconfdir@/@package_name@/config/ldap-agent.conf"
+
+
+
+[ -f $exec ] || exit 0
+
+
+umask 077
+
+start() {
+    echo_n "Starting $prog: "
+    ret=0
+    subagent_running=0
+    subagent_started=0
+
+    # the subagent creates a pidfile and writes
+    # the pid to it when it is fully started.
+    if [ -f $pidfile ]; then
+        pid=`cat $pidfile`
+        name=`ps -p $pid | tail -1 | awk '{ print $4 }'`
+        if kill -0 $pid && [ $name = "$processname" ]; then
+            echo_n " already running"
+            success; echo
+            subagent_running=1
+        else
+            echo " not running, but pid file exists"
+            echo_n "    ... attempting to start anyway"
+        fi
+    fi
+
+    if [ $subagent_running -eq 0 ] ; then
+        rm -f $pidfile
+        $exec $configfile > /dev/null 2>&1
+        if [ $? -eq 0 ]; then
+            subagent_started=1 # well, perhaps not running, but started ok
+        else
+            failure; echo
+            ret=1
+        fi
+    fi
+    # ok, if we started the subagent successfully, let's see
+    # if it is really running and ready to serve requests.
+    if [ $subagent_started -eq 1 ] ; then
+        loop_counter=1
+        # wait for 10 seconds
+        max_count=10
+        while test $loop_counter -le $max_count ; do
+            loop_counter=`expr $loop_counter + 1`
+            if test ! -f $pidfile ; then
+                if kill -0 $pid > /dev/null 2>&1 ; then
+                    sleep 1
+                else
+                    break
+                fi
+            else
+                pid=`cat $pidfile`
+                break
+            fi
+        done
+        if kill -0 $pid > /dev/null 2>&1 && test -f $pidfile ; then
+            success; echo
+        else
+            failure; echo
+            ret=1
+        fi
+    fi
+
+    exit $ret
+}
+
+stop() {
+    echo_n "Shutting down $prog: "
+    if [ -f $pidfile ]; then
+        pid=`cat $pidfile`
+        subagent_stopped=0
+        if kill -0 $pid > /dev/null 2>&1 ; then
+            kill $pid
+            if [ $? -eq 0 ]; then
+                subagent_stopped=1
+            else
+                failure; echo
+            fi
+        else
+            echo_n " subagent not running"
+            failure; echo
+        fi
+        if [ $subagent_stopped -eq 1 ] ; then
+            loop_counter=1
+            # wait for 10 seconds
+            max_count=10
+            while test $loop_counter -le $max_count; do
+                loop_counter=`expr $loop_counter + 1`
+                if kill -0 $pid > /dev/null 2>&1 ; then
+                    sleep 1
+                else
+                    if test -f $pidfile ; then
+                        rm -f $pidfile
+                    fi
+                    break
+                fi
+            done
+            if test -f $pidfile ; then
+                failure; echo
+            else
+                success; echo
+                rm -f $pidfile
+            fi
+        fi
+    else
+        echo_n " subagent already stopped"
+        failure; echo
+    fi
+}
+
+reload() {
+    stop
+    start
+}
+
+restart() {
+    stop
+    start
+}
+
+condrestart() {
+    if [ -f $pidfile ]; then
+        pid=`cat $pidfile`
+        name=`ps -p $pid | tail -1 | awk '{ print $4 }'`
+        if kill -0 $pid && [ $name = "$processname" ]; then
+            restart
+        fi
+    fi
+}
+
+status() {
+     ret=0
+     if [ -f $pidfile ]; then
+         pid=`cat $pidfile`
+         if kill -0 $pid > /dev/null 2>&1 ; then
+             echo "$prog (pid $pid) is running..."
+         else
+            echo "$prog dead but pid file exists"
+            ret=1
+         fi
+     else
+         echo "$prog is stopped"
+         ret=3
+     fi
+     exit $ret
+}
+
+
+case "$1" in
+    start|stop|restart|reload|condrestart|status)
+        $1
+        ;;
+    *)
+        echo Unknown command $1
+        echo "Usage: $0 {start|stop|restart|reload|condrestart|status}"
+        exit 2
+esac
diff --git a/wrappers/ldap-agent.in b/wrappers/ldap-agent.in
index 0b19d8e..266507a 100755
--- a/wrappers/ldap-agent.in
+++ b/wrappers/ldap-agent.in
@@ -5,7 +5,7 @@
 ###############################################################################
 
 LIB_DIR=@nss_libdir@:@nspr_libdir@:@ldapsdk_libdir@:@netsnmp_libdir@
-BIN_DIR=@bindir@
+BIN_DIR=@sbindir@
 COMMAND=ldap-agent-bin
 
 # We don't need to load any mibs, so set MIBS to nothing.
-- 
1.6.2.5

--
389-devel mailing list
389-devel@xxxxxxxxxx
https://www.redhat.com/mailman/listinfo/fedora-directory-devel

[Index of Archives]     [Fedora Directory Announce]     [Fedora Users]     [Older Fedora Users Mail]     [Fedora Advisory Board]     [Fedora Security]     [Fedora Devel Java]     [Fedora Desktop]     [ATA RAID]     [Fedora Marketing]     [Fedora Mentors]     [Fedora Package Review]     [Fedora Art]     [Fedora Music]     [Fedora Packaging]     [CentOS]     [Fedora SELinux]     [Big List of Linux Books]     [KDE Users]     [Fedora Art]     [Fedora Docs]

  Powered by Linux