Take the 2-minute tour ×
Server Fault is a question and answer site for professional system and network administrators. It's 100% free, no registration required.

Having an issue with some not-so-bright developers creating daemon processes that are binding to the (not actually reserved) local ports on our Linux servers. Those ports being defined in:

cat /proc/sys/net/ipv4/ip_local_port_range

Of course, occasionally, the start-up of their daemon, attempts to bind to a port that is already being used by another process ( in a perfectly valid way ). I've resorted to taking an ironfisted approach with them, using the following script, ran from 'root's crontab every five minutes:

#! /bin/bash

MINPORT=`cat /proc/sys/net/ipv4/ip_local_port_range | awk '{print $1}'`;
MAXPORT=`cat /proc/sys/net/ipv4/ip_local_port_range | awk '{print $2}'`;
BADPIDS=`lsof -i :${MINPORT}-${MAXPORT} -nP | grep LISTEN | grep -v COMMAND | awk '{print $2}'`

for PID in ${BADPIDS}
do
    echo KILLING ILLEGAL PROCESS ID: ${PID}
    kill -9 ${PID}
done

Does anyone know of a software package that does this, in a slightly more graceful way? Say, preventing them from binding to the ports in the first place.

share|improve this question
    
I guess if that is possible then by something like AppArmor or SELinux. –  Hauke Laging May 22 '13 at 0:49
1  
How their daemon gets to bind to already used port with an open socket? Just curious. –  Danila Ladner May 22 '13 at 0:56
1  
    
@HaukLaging - Switching to a different kernel would be a bit of a hard sell, especially when we are talking about 700+ nodes, already running custom built kernels/drivers. Also, the goal would be the prevention of bind()s to those ports, calls to connect(), even by the same processes, should still work. –  Jim Black May 22 '13 at 1:07
    
@DanilaLander I said they 'attempt' to bind to already used ports, didn't say they are binding to them. –  Jim Black May 22 '13 at 1:09

1 Answer 1

This is the worst hack I can think of but it works. The idea is to redefine the bind system call and to fool users to use your new version. It will not work if your users notice what you are doing and use the standard version of the system call.

If you want to forbid some users (even root) to bind on port 1234 with IPv4 and IPv6 (you can generalize that to any list of ports, just edit the code) this is what you need to do:

Put this code in a file (my_bind.c):

#define _GNU_SOURCE

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <dlfcn.h>
#include <arpa/inet.h>
#include <errno.h>

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen) {
  static int (*real_bind)(int, const struct sockaddr *, socklen_t) = NULL;

  if (!real_bind) {
    real_bind = dlsym(RTLD_NEXT, "bind");
    if (!real_bind) {
      fprintf(stderr, "Can not find real bind, contact yous sysadmin, exiting.\n");
      exit(20);
    }
  }

  if (addr->sa_family == AF_INET) {
    struct sockaddr_in *addr_in = (struct sockaddr_in*)addr;
    if (addr_in->sin_port == htons(1234)) {
      errno = EADDRINUSE;
      return -1;
    }
  }
  else if (addr->sa_family == AF_INET6) {
    struct sockaddr_in6 *addr_in = (struct sockaddr_in6*)addr;
    if (addr_in->sin6_port == htons(1234)) {
      errno = EADDRINUSE;
      return -1;
    }
  }

  return real_bind(sockfd, addr, addrlen);
}

Compile it with:

gcc -Wall -O2 -ldl -shared -fPIC -o my_bind.so my_bind.c

Put the my_bind.so file somewhere accessible to all users:

cp my_bind.so /usr/local/lib
chmod a+rx /usr/local/lib/my_bind.so

For the local shell, use the new version of the bind system call and try binding to port 1234:

export LD_PRELOAD="/usr/local/lib/my_bind.so"
nc -l -p 1234

I have the following result:

nc: bind to source :: 1234 failed: Address already in use
nc: bind to source 0.0.0.0 1234 failed: Address already in use
nc: failed to bind to any local addr/port

Now, put this line in the .bashrc of your "not-so-bright developers":

# Do not use port 1234, it is mine !
export LD_PRELOAD="/usr/local/lib/my_bind.so"

If you want to use the original system call, simply do:

export LD_PRELOAD=""
share|improve this answer
    
That actually made me shudder :) good work though! –  Flup May 23 '13 at 13:24

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.