Bug 61179 - TTLimit directive to set maximum allowed IP_TTL
Summary: TTLimit directive to set maximum allowed IP_TTL
Status: NEW
Alias: None
Product: Apache httpd-2
Classification: Unclassified
Component: Core (show other bugs)
Version: 2.5-HEAD
Hardware: All All
: P2 normal (vote)
Target Milestone: ---
Assignee: Apache HTTPD Bugs Mailing List
URL: http://blog.donatas.net/blog/2017/04/...
Keywords: PatchAvailable
Depends on:
Blocks:
 
Reported: 2017-06-12 19:07 UTC by Donatas Abraitis
Modified: 2020-08-15 07:19 UTC (History)
1 user (show)



Attachments
TTLimit directive (3.74 KB, text/plain)
2017-06-13 08:30 UTC, Donatas Abraitis
Details
TTLimit.patch (4.39 KB, patch)
2020-05-22 11:39 UTC, Donatas Abraitis
Details | Diff
TTLimit.patch (4.65 KB, patch)
2020-05-22 11:43 UTC, Donatas Abraitis
Details | Diff
TTLimit.patch (4.38 KB, patch)
2020-05-22 17:43 UTC, Donatas Abraitis
Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Donatas Abraitis 2017-06-12 19:07:34 UTC
Hi,

I would like to propose this patchset allowing to set maximum TTL value for incoming requests. This is not a usual use case, but I'm interested (maybe others too) to have this in place. The real use case would be like this one http://blog.donatas.net/blog/2017/04/20/http-request-validation/. 

TL;DR: if you want to deny requests bypassing proxy layer (in this case Apache operates as a backend). Hence set TTLimit to 1 and Apache will be able to handle requests coming almost from the local network, because packets with TTL usually come from local networks.

commit 5b28a17bdf8a86fa4cccc9a76ce474e5afdb434e
Author: ton31337 <donatas.abraitis@gmail.com>
Date:   Mon Jun 12 17:36:13 2017 +0300

    Add `TTLimit` security check feature

diff --git a/include/ap_listen.h b/include/ap_listen.h
index 58c2574..e20c566 100644
--- a/include/ap_listen.h
+++ b/include/ap_listen.h
@@ -133,6 +133,7 @@ AP_DECLARE_NONSTD(int) ap_close_selected_listeners(ap_slave_t *);
  * LISTEN_COMMANDS in their command_rec table so that these functions are
  * called.
  */
+AP_DECLARE_NONSTD(const char *) ap_set_ttl_limit(cmd_parms *cmd, void *dummy, const char *arg);
 AP_DECLARE_NONSTD(const char *) ap_set_listenbacklog(cmd_parms *cmd, void *dummy, const char *arg);
 AP_DECLARE_NONSTD(const char *) ap_set_listencbratio(cmd_parms *cmd, void *dummy, const char *arg);
 AP_DECLARE_NONSTD(const char *) ap_set_listener(cmd_parms *cmd, void *dummy,
@@ -144,6 +145,8 @@ AP_DECLARE_NONSTD(const char *) ap_set_receive_buffer_size(cmd_parms *cmd,
                                                            const char *arg);

 #define LISTEN_COMMANDS \
+AP_INIT_TAKE1("TTLimit", ap_set_ttl_limit, NULL, RSRC_CONF, \
+  "Maximum TTL value which will be accepted"), \
 AP_INIT_TAKE1("ListenBacklog", ap_set_listenbacklog, NULL, RSRC_CONF, \
   "Maximum length of the queue of pending connections, as used by listen(2)"), \
 AP_INIT_TAKE1("ListenCoresBucketsRatio", ap_set_listencbratio, NULL, RSRC_CONF, \
diff --git a/server/listen.c b/server/listen.c
index 98cd117..bc498e3 100644
--- a/server/listen.c
+++ b/server/listen.c
@@ -186,6 +186,21 @@ static apr_status_t make_sock(apr_pool_t *p, ap_listen_rec *server)
         return stat;
     }

+    if (ap_ttl_limit) {
+        int thesock;
+        apr_os_sock_get(&thesock, s);
+        if (setsockopt(thesock, IPPROTO_IP, IP_TTL,
+                       &ap_ttl_limit, sizeof(int)) < 0) {
+            stat = apr_get_netos_error();
+            ap_log_perror(APLOG_MARK, APLOG_CRIT, stat, p, APLOGNO(02638)
+                          "make_sock: for address %pI, apr_socket_opt_set: "
+                          "(IP_TTL)",
+                          server->bind_addr);
+            apr_socket_close(s);
+            return stat;
+        }
+    }
+
 #ifdef WIN32
     /* I seriously doubt that this would work on Unix; I have doubts that
      * it entirely solves the problem on Win32.  However, since setting
@@ -758,6 +773,7 @@ AP_DECLARE(void) ap_listen_pre_config(void)
     ap_listen_buckets = NULL;
     ap_num_listen_buckets = 0;
     ap_listenbacklog = DEFAULT_LISTENBACKLOG;
+    ap_ttl_limit = 255;
     ap_listencbratio = 0;

     /* Check once whether or not SO_REUSEPORT is supported. */
@@ -863,6 +879,26 @@ AP_DECLARE_NONSTD(const char *) ap_set_listenbacklog(cmd_parms *cmd,
     return NULL;
 }

+AP_DECLARE_NONSTD(const char *) ap_set_ttl_limit(cmd_parms *cmd,
+                                                 void *dummy,
+                                                 const char *arg)
+{
+    int b;
+    const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+
+    if (err != NULL) {
+        return err;
+    }
+
+    b = atoi(arg);
+    if (b < 1 || b > 255) {
+        return "TTLimit > 0 and TTLimit < 255";
+    }
+
+    ap_ttl_limit = b;
+    return NULL;
+}
+
 AP_DECLARE_NONSTD(const char *) ap_set_listencbratio(cmd_parms *cmd,
                                                      void *dummy,
                                                      const char *arg)

Waiting for comments,
D.
Comment 1 Donatas Abraitis 2017-06-13 08:30:33 UTC
Created attachment 35048 [details]
TTLimit directive
Comment 2 Donatas Abraitis 2020-05-20 06:34:30 UTC
Any updates on this?
Comment 3 Nick Kew 2020-05-22 10:43:03 UTC
Attached patch would be completed by a patch to document the new directive.  Somewhere in docs/manual/mod/mpm_common.xml
Comment 4 Donatas Abraitis 2020-05-22 11:39:03 UTC
Created attachment 37264 [details]
TTLimit.patch
Comment 5 Donatas Abraitis 2020-05-22 11:39:56 UTC
Thanks, Nick. Added a patch.
Comment 6 Donatas Abraitis 2020-05-22 11:43:37 UTC
Created attachment 37265 [details]
TTLimit.patch
Comment 7 Donatas Abraitis 2020-05-22 17:43:45 UTC
Created attachment 37267 [details]
TTLimit.patch
Comment 8 Donatas Abraitis 2020-05-22 18:31:05 UTC
The latest patch works as expected:

root@donatas-laptop:/home/donatas/httpd-2.4.43# grep TTLimit /usr/local/apache2/conf/httpd.conf 
TTLimit 5
root@donatas-laptop:/home/donatas/httpd-2.4.43# strace -esetsockopt /usr/local/apache2/bin/httpd -f /usr/local/apache2/conf/httpd.conf 
setsockopt(3, SOL_SOCKET, SO_REUSEPORT, [1], 4) = 0
setsockopt(4, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
setsockopt(4, SOL_SOCKET, SO_KEEPALIVE, [1], 4) = 0
setsockopt(4, SOL_IPV6, IPV6_V6ONLY, [0], 4) = 0
setsockopt(4, SOL_SOCKET, SO_SNDBUF, [1024], 4) = 0
setsockopt(4, SOL_TCP, TCP_NODELAY, [1], 4) = 0
setsockopt(4, SOL_IP, IP_TTL, [5], 4)   = 0
setsockopt(4, SOL_TCP, TCP_DEFER_ACCEPT, [30], 4) = 0
+++ exited with 1 +++
root@donatas-laptop:/home/donatas/httpd-2.4.43#
Comment 9 Donatas Abraitis 2020-06-17 06:33:31 UTC
Nick, all good with this?
Comment 10 Nick Kew 2020-08-14 22:39:33 UTC
Sorry, just revisited this.  Thanks for bugging me!

The "Usage" in your docs patch looks more like an application - your application - than an explanation of the feature itself.  Do you mind if I reword it a little: say it sets the socket option, and make your text an example of why someone might use it?  I think that's what's meant anyway, but if I hadn't read your posts (and blog) I might find it confusing!
Comment 11 Donatas Abraitis 2020-08-15 07:19:08 UTC
Hi, Nick,

that works for me if you adopt it how it's needed ;-)