├── .gitignore ├── .travis.yml ├── Makefile ├── README.md ├── mocker └── docker ├── nss.c ├── test.c └── valgrind.supp /.gitignore: -------------------------------------------------------------------------------- 1 | .*.sw? 2 | *.o 3 | /libnss_docker.so.2 4 | /test 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: c 2 | 3 | install: 4 | - sudo apt-get install valgrind 5 | - make all 6 | - sudo make install 7 | - | 8 | sudo sed -i -re 's/^(hosts: .*$)/\1 docker/' /etc/nsswitch.conf 9 | 10 | # add our fake docker client to the path 11 | before_script: 12 | - export PATH=`pwd`/mocker:$PATH 13 | 14 | script: 15 | - valgrind --leak-check=full --error-exitcode=2 --suppressions=valgrind.supp ./test 16 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | DESTDIR ?= 2 | NSSDIR ?= /usr/lib 3 | 4 | CC = gcc 5 | CFLAGS = -fPIC -Wall -Werror -ggdb `pkg-config --cflags glib-2.0` 6 | LDFLAGS = `pkg-config --libs glib-2.0` 7 | 8 | OBJS = \ 9 | nss.o 10 | 11 | MODULE = libnss_docker.so.2 12 | 13 | BINS = \ 14 | $(MODULE) \ 15 | test 16 | 17 | $(MODULE): $(OBJS) Makefile 18 | $(CC) -fPIC -shared -o $@ -Wl,-soname,$@ $< $(LDFLAGS) 19 | 20 | TEST_OBJS = \ 21 | test.o 22 | 23 | test: $(TEST_OBJS) $(MODULE) Makefile 24 | $(CC) -o $@ $< $(LDFLAGS) 25 | 26 | all: $(BINS) 27 | 28 | install: all 29 | mkdir -p $(DESTDIR)$(NSSDIR) 30 | install -m 0644 $(MODULE) $(DESTDIR)$(NSSDIR)/$(MODULE) 31 | ldconfig 32 | 33 | clean: 34 | rm -f $(BINS) $(OBJS) $(TEST_OBJS) 35 | 36 | .PHONY: all install clean 37 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | docker-nss 2 | ========== 3 | 4 | A libnss plugin for finding Docker containers 5 | 6 | This is still a work in progress! 7 | 8 | Installing 9 | ========== 10 | 11 | git clone https://github.com/danni/docker-nss.git 12 | cd docker-nss 13 | sudo make all install 14 | sudo sed -i -re 's/^(hosts: .*$)/\1 docker/' /etc/nsswitch.conf 15 | 16 | or edit `/etc/nsswitch.conf`: 17 | 18 | hosts: files dns mdns4_minimal myhostname docker 19 | ^ 20 | Testing 21 | ======= 22 | 23 | LD_LIBRARY_PATH=`pwd` PATH=`pwd`/mocker:$PATH ./test 24 | 25 | LD_LIBRARY_PATH=`pwd` getent hosts badger.docker 26 | LD_LIBRARY_PATH=`pwd` getent hosts 10.0.0.0 27 | 28 | ToDo 29 | ==== 30 | 31 | * Look up containers by image name/ID (not just container ID) 32 | * Look up container names for IP addresses 33 | -------------------------------------------------------------------------------- /mocker/docker: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # A fake docker binary for use in testing 4 | 5 | IP_LOOKUP="-format={{.NetworkSettings.IPAddress}}" 6 | 7 | case "$@" in 8 | # a container which exists 9 | "inspect $IP_LOOKUP badger") 10 | echo "172.11.22.33" 11 | exit 0 12 | ;; 13 | # an image name, this does not have an IP address 14 | "inspect $IP_LOOKUP stoat") 15 | echo "" 16 | exit 0 17 | ;; 18 | # a complete unknown container 19 | "inspect $IP_LOOKUP mushroom") 20 | echo "Error: No such image or container: mushroom" >&2 21 | exit 1 22 | ;; 23 | esac 24 | -------------------------------------------------------------------------------- /nss.c: -------------------------------------------------------------------------------- 1 | /* 2 | * NSS plugin for looking up Docker containers 3 | * 4 | * (c) 2014 Danielle Madeley 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Lesser General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Lesser General Public License for more details. 15 | 16 | * You should have received a copy of the GNU Lesser General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | /* define a suffix that containers have */ 30 | #define SUFFIX ".docker" 31 | 32 | #define ALIGN(a) (((a+sizeof(void*)-1)/sizeof(void*))*sizeof(void*)) 33 | 34 | static void 35 | pack_hostent(struct hostent *result, 36 | char *buffer, 37 | size_t buflen, 38 | const char *name, 39 | const void *addr) 40 | { 41 | char *aliases, *r_addr, *addrlist; 42 | size_t l, idx; 43 | 44 | /* we can't allocate any memory, the buffer is where we need to 45 | * return things we want to use 46 | * 47 | * 1st, the hostname */ 48 | l = strlen(name); 49 | result->h_name = buffer; 50 | memcpy (result->h_name, name, l); 51 | buffer[l] = '\0'; 52 | 53 | idx = ALIGN (l+1); 54 | 55 | /* 2nd, the empty aliases array */ 56 | aliases = buffer + idx; 57 | *(char **) aliases = NULL; 58 | idx += sizeof (char*); 59 | 60 | result->h_aliases = (char **) aliases; 61 | 62 | result->h_addrtype = AF_INET; 63 | result->h_length = sizeof (struct in_addr); 64 | 65 | /* 3rd, address */ 66 | r_addr = buffer + idx; 67 | memcpy(r_addr, addr, result->h_length); 68 | idx += ALIGN (result->h_length); 69 | 70 | /* 4th, the addresses ptr array */ 71 | addrlist = buffer + idx; 72 | ((char **) addrlist)[0] = r_addr; 73 | ((char **) addrlist)[1] = NULL; 74 | 75 | result->h_addr_list = (char **) addrlist; 76 | } 77 | 78 | static gboolean 79 | lookup_container_ip (const char *name, 80 | struct in_addr *addr) 81 | { 82 | char *stdout_s, *name_s; 83 | int exit_status; 84 | gboolean success = FALSE; 85 | 86 | /* remove the suffix */ 87 | name_s = g_strdup (name); 88 | *strrchr(name_s, '.') = '\0'; 89 | 90 | char *argv[] = { 91 | "docker", 92 | "inspect", 93 | "-format={{.NetworkSettings.IPAddress}}", 94 | name_s, 95 | NULL, 96 | }; 97 | 98 | if (!g_spawn_sync(NULL, 99 | argv, 100 | NULL, 101 | G_SPAWN_STDERR_TO_DEV_NULL | G_SPAWN_SEARCH_PATH, 102 | NULL, NULL, 103 | &stdout_s, NULL, 104 | &exit_status, 105 | NULL)) 106 | { 107 | goto finally; 108 | } 109 | 110 | if (exit_status != 0) 111 | { 112 | goto finally; 113 | } 114 | 115 | stdout_s = g_strstrip (stdout_s); 116 | success = inet_aton (stdout_s, addr); 117 | 118 | finally: 119 | 120 | g_free (name_s); 121 | g_free (stdout_s); 122 | 123 | return success; 124 | } 125 | 126 | enum nss_status 127 | _nss_docker_gethostbyname2_r (const char *name, 128 | int af, 129 | struct hostent *result, 130 | char *buffer, 131 | size_t buflen, 132 | int *errnop, 133 | int *h_errnop) 134 | { 135 | struct in_addr addr; 136 | 137 | if (af != AF_INET) 138 | { 139 | *errnop = EAFNOSUPPORT; 140 | *h_errnop = NO_DATA; 141 | return NSS_STATUS_UNAVAIL; 142 | } 143 | 144 | if (!g_str_has_suffix(name, SUFFIX)) 145 | { 146 | *errnop = ENOENT; 147 | *h_errnop = HOST_NOT_FOUND; 148 | return NSS_STATUS_NOTFOUND; 149 | } 150 | 151 | if (!lookup_container_ip (name, &addr)) 152 | { 153 | *errnop = ENOENT; 154 | *h_errnop = HOST_NOT_FOUND; 155 | return NSS_STATUS_NOTFOUND; 156 | } 157 | 158 | pack_hostent(result, buffer, buflen, name, &addr); 159 | 160 | return NSS_STATUS_SUCCESS; 161 | } 162 | 163 | enum nss_status 164 | _nss_docker_gethostbyname_r (const char *name, 165 | struct hostent *result, 166 | char *buffer, 167 | size_t buflen, 168 | int *errnop, 169 | int *h_errnop) 170 | { 171 | return _nss_docker_gethostbyname2_r(name, 172 | AF_INET, 173 | result, 174 | buffer, 175 | buflen, 176 | errnop, 177 | h_errnop); 178 | } 179 | 180 | enum nss_status 181 | _nss_docker_gethostbyaddr_r (const void *addr, 182 | socklen_t len, 183 | int af, 184 | struct hostent *result, 185 | char *buffer, 186 | size_t buflen, 187 | int *errnop, 188 | int *h_errnop) 189 | { 190 | if (af != AF_INET) 191 | { 192 | *errnop = EAFNOSUPPORT; 193 | *h_errnop = NO_DATA; 194 | return NSS_STATUS_UNAVAIL; 195 | } 196 | 197 | if (len != sizeof (struct in_addr)) 198 | { 199 | *errnop = EINVAL; 200 | *h_errnop = NO_RECOVERY; 201 | return NSS_STATUS_UNAVAIL; 202 | } 203 | 204 | /* FIXME: need to implement reverse lookups */ 205 | *errnop = EAFNOSUPPORT; 206 | *h_errnop = NO_DATA; 207 | return NSS_STATUS_UNAVAIL; 208 | /* 209 | pack_hostent(result, buffer, buflen, "container.docker", addr); 210 | 211 | return NSS_STATUS_SUCCESS; 212 | */ 213 | } 214 | -------------------------------------------------------------------------------- /test.c: -------------------------------------------------------------------------------- 1 | /* 2 | * NSS plugin for looking up Docker containers -- testcases 3 | * 4 | * (c) 2014 Danielle Madeley 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Lesser General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Lesser General Public License for more details. 15 | 16 | * You should have received a copy of the GNU Lesser General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #define BADGER_DOCKER_IP "172.11.22.33" 27 | 28 | static void 29 | test_gethostbyname (void) 30 | { 31 | struct hostent *results; 32 | char buffer[INET_ADDRSTRLEN]; 33 | 34 | results = gethostbyname("badger.docker"); 35 | 36 | g_assert(results != NULL); 37 | 38 | g_assert_cmpstr(results->h_name, ==, "badger.docker"); 39 | g_assert(results->h_aliases[0] == NULL); 40 | g_assert_cmpint(results->h_addrtype, ==, AF_INET); 41 | g_assert_cmpint(results->h_length, ==, 4); 42 | g_assert(results->h_addr_list[0] != NULL); 43 | g_assert(results->h_addr_list[1] == NULL); 44 | 45 | inet_ntop(AF_INET, results->h_addr_list[0], buffer, INET_ADDRSTRLEN); 46 | g_assert_cmpstr(buffer, ==, BADGER_DOCKER_IP); 47 | } 48 | 49 | static void 50 | test_gethostbyname_not_docker (void) 51 | { 52 | struct hostent *results; 53 | 54 | results = gethostbyname("badger"); 55 | 56 | g_assert(results == NULL); 57 | g_assert_cmpint(h_errno, ==, HOST_NOT_FOUND); 58 | } 59 | 60 | static void 61 | test_gethostbyname_by_image_name (void) 62 | { 63 | struct hostent *results; 64 | 65 | results = gethostbyname("stoat.docker"); 66 | 67 | g_assert(results == NULL); 68 | g_assert_cmpint(h_errno, ==, HOST_NOT_FOUND); 69 | } 70 | 71 | static void 72 | test_gethostbyname_unknown_name (void) 73 | { 74 | struct hostent *results; 75 | 76 | results = gethostbyname("mushroom.docker"); 77 | 78 | g_assert(results == NULL); 79 | g_assert_cmpint(h_errno, ==, HOST_NOT_FOUND); 80 | } 81 | 82 | static void 83 | test_gethostbyname2 (void) 84 | { 85 | struct hostent *results; 86 | char buffer[INET_ADDRSTRLEN]; 87 | 88 | results = gethostbyname2("badger.docker", AF_INET); 89 | 90 | g_assert(results != NULL); 91 | 92 | g_assert_cmpstr(results->h_name, ==, "badger.docker"); 93 | g_assert(results->h_aliases[0] == NULL); 94 | g_assert_cmpint(results->h_addrtype, ==, AF_INET); 95 | g_assert_cmpint(results->h_length, ==, 4); 96 | g_assert(results->h_addr_list[0] != NULL); 97 | g_assert(results->h_addr_list[1] == NULL); 98 | 99 | inet_ntop(AF_INET, results->h_addr_list[0], buffer, INET_ADDRSTRLEN); 100 | g_assert_cmpstr(buffer, ==, BADGER_DOCKER_IP); 101 | } 102 | 103 | static void 104 | test_gethostbyname2_inet6 (void) 105 | { 106 | struct hostent *results; 107 | 108 | results = gethostbyname2("badger.docker", AF_INET6); 109 | 110 | g_assert(results == NULL); 111 | g_assert_cmpint(errno, ==, EAFNOSUPPORT); 112 | g_assert_cmpint(h_errno, ==, NO_DATA); 113 | } 114 | 115 | # if 0 116 | static void 117 | test_gethostbyaddr (void) 118 | { 119 | struct hostent *results; 120 | struct in_addr addr; 121 | char buffer[INET_ADDRSTRLEN]; 122 | 123 | inet_pton(AF_INET, "10.0.0.0", &addr); 124 | 125 | results = gethostbyaddr(&addr, 4, AF_INET); 126 | 127 | g_assert (results != NULL); 128 | 129 | g_assert_cmpstr(results->h_name, ==, "container.docker"); 130 | g_assert(results->h_aliases[0] == NULL); 131 | g_assert_cmpint(results->h_addrtype, ==, AF_INET); 132 | g_assert_cmpint(results->h_length, ==, 4); 133 | g_assert(results->h_addr_list[0] != NULL); 134 | g_assert(results->h_addr_list[1] == NULL); 135 | 136 | inet_ntop(AF_INET, results->h_addr_list[0], buffer, INET_ADDRSTRLEN); 137 | g_assert_cmpstr(buffer, ==, "10.0.0.0"); 138 | } 139 | # endif 140 | 141 | int 142 | main (int argc, 143 | char **argv) 144 | { 145 | g_test_init (&argc, &argv, NULL); 146 | 147 | g_test_add_func("/test/gethostbyname", test_gethostbyname); 148 | g_test_add_func("/test/gethostbyname_not_docker", 149 | test_gethostbyname_not_docker); 150 | g_test_add_func("/test/gethostbyname_by_image_name", 151 | test_gethostbyname_by_image_name); 152 | g_test_add_func("/test/gethostbyname_unknown_name", 153 | test_gethostbyname_unknown_name); 154 | g_test_add_func("/test/gethostbyname2", test_gethostbyname2); 155 | g_test_add_func("/test/gethostbyname2_inet6", test_gethostbyname2_inet6); 156 | // g_test_add_func("/test/gethostbyaddr", test_gethostbyaddr); 157 | 158 | return g_test_run (); 159 | } 160 | -------------------------------------------------------------------------------- /valgrind.supp: -------------------------------------------------------------------------------- 1 | { 2 | GTest leaks 3 | Memcheck:Leak 4 | ... 5 | fun:g_strdup 6 | fun:g_test_get_root 7 | } 8 | 9 | { 10 | GTest leaks 2 11 | Memcheck:Leak 12 | ... 13 | fun:g_strdup 14 | fun:g_test_create_suite 15 | } 16 | 17 | { 18 | GTest leaks 3 19 | Memcheck:Leak 20 | ... 21 | fun:g_strdup 22 | fun:g_test_create_case 23 | } 24 | 25 | { 26 | GTest leaks 3.1 27 | Memcheck:Leak 28 | ... 29 | fun:g_slice_alloc0 30 | fun:g_test_create_case 31 | } 32 | 33 | { 34 | GTest leaks 4 35 | Memcheck:Leak 36 | ... 37 | fun:g_test_init 38 | } 39 | 40 | { 41 | GRandom leaks 42 | Memcheck:Leak 43 | ... 44 | fun:g_rand_new 45 | } 46 | --------------------------------------------------------------------------------