Hi everyone,
The joined patch is an integration of ironBee in HAProxy. This is absolutely experimental.
I’m not able to configure IronBee, so I just run some simple checks.
On my first test, I saw some blocking access initiated by ironbee. These access are to
/dev/random. Maybe these access can be disabled, but I don’t known the way to do this.
The configuration is easy, you can just include this line:
http-request deny if { ironbee(ironbee.waf.conf) -m bool }
“ironbee” is a sample fetch which returns true if an attach is detected.
Its easy to compile, after patching the last HAProxy version, you can compile with these options:
make ... \
USE_IRONBEE=1 \
IRONBEE_INC="/opt/ironbee/include" \
IRONBEE_INC="/opt/ironbee/lib"
Here the patch:
Download link
From 6b2910c3321a6632f5d7f67a35825502c9ae78a0 Mon Sep 17 00:00:00 2001
From: Thierry FOURNIER <tfournier@arpalert.org>
Date: Wed, 23 Dec 2015 00:36:51 +0100
Subject: [PATCH] MINOR: ironbee: Add Ironbee WAF engine
This patch adds ironbee waf engine:
---
Makefile | 8 +-
src/ironbee.c | 349 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 356 insertions(+), 1 deletion(-)
create mode 100644 src/ironbee.c
diff --git a/Makefile b/Makefile
index d42d5fb..1ec9071 100644
--- a/Makefile
+++ b/Makefile
@@ -39,6 +39,7 @@
# USE_DL : enable it if your system requires -ldl. Automatic on Linux.
# USE_DEVICEATLAS : enable DeviceAtlas api.
# USE_51DEGREES : enable third party device detection library from 51Degrees
+# USE_IRONBEE : enable the ironbee waf
#
# Options can be forced by specifying "USE_xxx=1" or can be disabled by using
# "USE_xxx=" (empty string).
@@ -606,6 +607,11 @@ $(error unable to automatically detect the Lua library name, you can enforce its
endif
endif
+ifneq ($(USE_IRONBEE),)
+OPTIONS_LDFLAGS += $(if $(IRONBEE_LIB),-L$(IRONBEE_LIB) -lironbee -libutil)
+OPTIONS_CFLAGS += -DUSE_IRONBEE $(if $(IRONBEE_INC),-I$(IRONBEE_INC))
+endif
+
OPTIONS_LDFLAGS += $(LUA_LD_FLAGS) -l$(LUA_LIB_NAME) -lm
ifneq ($(USE_DL),)
OPTIONS_LDFLAGS += -ldl
@@ -750,7 +756,7 @@ OBJS = src/haproxy.o src/base64.o src/protocol.o \
src/session.o src/stream.o src/hdr_idx.o src/ev_select.o src/signal.o \
src/acl.o src/sample.o src/memory.o src/freq_ctr.o src/auth.o src/proto_udp.o \
src/compression.o src/payload.o src/hash.o src/pattern.o src/map.o \
- src/namespace.o src/mailers.o src/dns.o src/vars.o
+ src/namespace.o src/mailers.o src/dns.o src/vars.o src/ironbee.o
EBTREE_OBJS = $(EBTREE_DIR)/ebtree.o \
$(EBTREE_DIR)/eb32tree.o $(EBTREE_DIR)/eb64tree.o \
diff --git a/src/ironbee.c b/src/ironbee.c
new file mode 100644
index 0000000..1f7d56a
--- /dev/null
+++ b/src/ironbee.c
@@ -0,0 +1,349 @@
+#ifdef USE_IRONBEE
+
+#include <ironbee/config.h> /* For ib_cfgparser_* */
+#include <ironbee/engine.h> /* For many things */
+#include <ironbee/state_notify.h> /* For ib_state_notify_* */
+#include <ironbee/string.h> /* For IB_S2SL */
+
+#include <common/standard.h>
+
+#include <types/proto_http.h>
+#include <types/sample.h>
+#include <types/stream.h>
+
+#include <proto/arg.h>
+#include <proto/connection.h>
+#include <proto/hdr_idx.h>
+#include <proto/obj_type.h>
+#include <proto/proto_http.h>
+#include <proto/sample.h>
+
+struct ironbee_config {
+ char *file;
+ ib_engine_t *engine;
+};
+
+static int analyse(const struct arg *args, struct sample *smp,
+ const char *kw, void *private)
+{
+ struct ironbee_config *conf;
+ struct connection *cli_conn;
+ char ip_src[INET6_ADDRSTRLEN + 1];
+ char ip_dst[INET6_ADDRSTRLEN + 1];
+ const char *ret;
+ struct http_txn *txn;
+ struct http_msg *msg;
+ int old_idx, cur_idx;
+ const char *cur_ptr, *cur_next, *p;
+ struct hdr_idx_elem *cur_hdr;
+ const char *hn, *hv;
+ int hnl, hvl;
+ ib_conn_t *conn;
+ ib_tx_t *tx;
+ ib_status_t rc;
+ ib_parsed_req_line_t *req_line;
+ ib_parsed_headers_t *headers;
+ int ret_code;
+
+ conf = (struct ironbee_config *)args[0].data.usr;
+ txn = smp->strm->txn;
+ ret_code = 0;
+
+ /* Create Connection
+ *
+ * A connection is some TCP/IP information and a sequence of transactions.
+ * Its primary purpose is to associate transactions.
+ */
+ rc = ib_conn_create(conf->engine, &conn, NULL);
+ if (rc != IB_OK)
+ goto analyse_return;
+
+ /* Extract source IP informations. */
+
+ cli_conn = objt_conn(smp->sess->origin);
+ if (!cli_conn)
+ goto analyse_destroy_conn;
+
+ conn_get_to_addr(cli_conn);
+
+ switch (cli_conn->addr.from.ss_family) {
+ case AF_INET:
+ ret = inet_ntop(AF_INET, &((struct sockaddr_in *)&cli_conn->addr.from)->sin_addr,
+ ip_src, INET6_ADDRSTRLEN + 1);
+ break;
+ case AF_INET6:
+ ret = inet_ntop(AF_INET6, &((struct sockaddr_in6 *)&cli_conn->addr.from)->sin6_addr,
+ ip_src, INET6_ADDRSTRLEN + 1);
+ break;
+ default:
+ goto analyse_destroy_conn;
+ }
+ if (!ret)
+ goto analyse_destroy_conn;
+
+ switch (cli_conn->addr.to.ss_family) {
+ case AF_INET:
+ ret = inet_ntop(AF_INET, &((struct sockaddr_in *)&cli_conn->addr.to)->sin_addr,
+ ip_dst, INET6_ADDRSTRLEN + 1);
+ break;
+ case AF_INET6:
+ ret = inet_ntop(AF_INET6, &((struct sockaddr_in6 *)&cli_conn->addr.to)->sin6_addr,
+ ip_dst, INET6_ADDRSTRLEN + 1);
+ break;
+ default:
+ goto analyse_destroy_conn;
+ }
+ if (!ret)
+ goto analyse_destroy_conn;
+
+ conn->local_ipstr = ip_dst;
+ conn->local_port = get_host_port(&cli_conn->addr.to);
+ conn->remote_ipstr = ip_src;
+ conn->remote_port = get_host_port(&cli_conn->addr.from);
+
+ /* Connection Opened */
+ rc = ib_state_notify_conn_opened(conf->engine, conn);
+ if (rc != IB_OK)
+ goto analyse_destroy_conn;
+
+ /* Create Transaction */
+ rc = ib_tx_create(&tx, conn, NULL);
+ if (rc != IB_OK)
+ goto analyse_destroy_conn;
+
+ /* Request Started */
+ rc = ib_parsed_req_line_create(&req_line, tx->mm,
+ /* raw */ txn->req.chn->buf->p, txn->req.sl.rq.l,
+ /* method */ txn->req.chn->buf->p, txn->req.sl.rq.m_l,
+ /* uri */ txn->req.chn->buf->p + txn->req.sl.rq.u, txn->req.sl.rq.u_l,
+ /* protocol */ txn->req.chn->buf->p + txn->req.sl.rq.v, txn->req.sl.rq.v_l);
+ if (rc != IB_OK)
+ goto analyse_destroy_tx;
+
+ rc = ib_state_notify_request_started(conf->engine, tx, req_line);
+ if (rc != IB_OK)
+ return 0;
+
+ /* Request Header */
+ rc = ib_parsed_headers_create(&headers, tx->mm);
+ if (rc != IB_OK)
+ goto analyse_destroy_tx;
+
+ /* Build array of headers. */
+ old_idx = 0;
+ cur_next = txn->req.chn->buf->p + hdr_idx_first_pos(&smp->strm->txn->hdr_idx);
+ while (1) {
+ cur_idx = txn->hdr_idx.v[old_idx].next;
+ if (!cur_idx)
+ break;
+ old_idx = cur_idx;
+
+ cur_hdr = &txn->hdr_idx.v[cur_idx];
+ cur_ptr = cur_next;
+ cur_next = cur_ptr + cur_hdr->len + cur_hdr->cr + 1;
+
+ /* look for ': *'. */
+ hn = cur_ptr;
+ for (p = cur_ptr; p < cur_ptr + cur_hdr->len && *p != ':'; p++);
+ if (p >= cur_ptr + cur_hdr->len)
+ continue;
+ hnl = p - hn;
+ p++;
+ while (p < cur_ptr + cur_hdr->len && ( *p == ' ' || *p == '\t' ))
+ p++;
+ if (p >= cur_ptr + cur_hdr->len)
+ continue;
+ hv = p;
+ hvl = cur_ptr + cur_hdr->len-p;
+
+ /* Add header. */
+ rc = ib_parsed_headers_add(headers, hn, hnl, hv, hvl);
+ if (rc != IB_OK)
+ goto analyse_destroy_tx;
+ }
+
+ rc = ib_state_notify_request_header_data(conf->engine, tx, headers);
+ if (rc != IB_OK)
+ goto analyse_destroy_tx;
+
+ /* Request Header Finished */
+ rc = ib_state_notify_request_header_finished(conf->engine, tx);
+ if (rc != IB_OK)
+ goto analyse_destroy_tx;
+
+ /* Request Body */
+ msg = &txn->req;
+ rc = ib_state_notify_request_body_data(conf->engine, tx,
+ b_ptr(msg->chn->buf, -http_data_rewind(msg)),
+ http_body_bytes(msg));
+ if (rc != IB_OK)
+ goto analyse_destroy_tx;
+
+ /* Request Finished */
+ rc = ib_state_notify_request_finished(conf->engine, tx);
+ if (rc != IB_OK)
+ goto analyse_destroy_tx;
+
+ /* Return ok. */
+ ret_code = 1;
+
+analyse_destroy_tx:
+
+ /* Transaction Done */
+ ib_tx_destroy(tx);
+
+analyse_destroy_conn:
+
+ /* Connection Done */
+ ib_conn_destroy(conn);
+
+analyse_return:
+
+ return ret_code;
+}
+
+/* IronBee requests that server modify headers before further processing. */
+static ib_status_t server_header(ib_tx_t *tx, ib_server_direction_t dir,
+ ib_server_header_action_t action,
+ const char *name, size_t name_length,
+ const char *value, size_t value_length,
+ void *cbdata)
+{
+ fprintf(stderr, "%s\n", __FUNCTION__);
+ return IB_OK;
+}
+
+/* IronBee requests that server respond with an error status. */
+static ib_status_t server_error(ib_tx_t *tx, int status, void *cbdata)
+{
+ fprintf(stderr, "%s\n", __FUNCTION__);
+ return IB_OK;
+}
+
+/* IronBee requests that server provide a certain header in error response. */
+static ib_status_t server_error_header(ib_tx_t *tx, const char *name,
+ size_t name_length, const char *value,
+ size_t value_length, void *cbdata)
+{
+ fprintf(stderr, "%s\n", __FUNCTION__);
+ return IB_OK;
+}
+
+/* IronBee requests that server provide a certain body in error response. */
+static ib_status_t server_error_data(ib_tx_t *tx, const char *data,
+ size_t data_length, void *cbdata)
+{
+ fprintf(stderr, "%s\n", __FUNCTION__);
+ return IB_OK;
+}
+
+static ib_status_t server_close(ib_conn_t *conn, ib_tx_t *tx, void *cbdata)
+{
+ fprintf(stderr, "%s\n", __FUNCTION__);
+ return IB_OK;
+}
+
+static ib_status_t server_body_edit(ib_tx_t *tx, ib_server_direction_t dir,
+ off_t start, size_t bytes, const char *repl,
+ size_t repl_len, void *cbdata)
+{
+ fprintf(stderr, "%s\n", __FUNCTION__);
+ return IB_OK;
+}
+
+static ib_status_t server_body_init(ib_tx_t *tx, int flags, void *x)
+{
+ fprintf(stderr, "%s\n", __FUNCTION__);
+ return IB_OK;
+}
+
+static ib_server_t server = {
+ IB_SERVER_HEADER_DEFAULTS,
+ "haproxy",
+ server_header, NULL,
+ server_error, NULL,
+ server_error_header, NULL,
+ server_error_data, NULL,
+ server_close, NULL,
+ server_body_edit, NULL,
+ server_body_init, NULL
+};
+
+static int load_rules(struct arg *arg, char **err_msg)
+{
+ struct ironbee_config *conf;
+ ib_cfgparser_t *parser;
+ ib_status_t rc;
+
+ /* Reserve a lot of memory. */
+ conf = malloc(sizeof(*conf));
+ if (!conf) {
+ memprintf(err_msg, "out fo memory error");
+ return 0;
+ }
+
+ /* Copy original file. */
+ conf->file = arg->data.str.str;
+
+ /* Create Engine */
+ rc = ib_engine_create(&conf->engine, &server);
+ if (rc != IB_OK) {
+ memprintf(err_msg, "error creating Ironbee engine: %s",
+ ib_status_to_string(rc));
+ return 0;
+ }
+
+ /* Load configuration */
+
+ rc = ib_cfgparser_create(&parser, conf->engine);
+ if (rc != IB_OK) {
+ memprintf(err_msg, "error loading Ironbee rule file '%s': %s",
+ conf->file, ib_status_to_string(rc));
+ return 0;
+ }
+
+ rc = ib_engine_config_started(conf->engine, parser);
+ if (rc != IB_OK) {
+ memprintf(err_msg, "error loading Ironbee rule file '%s': %s",
+ conf->file, ib_status_to_string(rc));
+ return 0;
+ }
+
+ rc = ib_cfgparser_parse(parser, conf->file);
+ if (rc != IB_OK) {
+ memprintf(err_msg, "error loading Ironbee rule file '%s': %s",
+ conf->file, ib_status_to_string(rc));
+ return 0;
+ }
+
+ rc = ib_engine_config_finished(conf->engine);
+ if (rc != IB_OK) {
+ memprintf(err_msg, "error loading Ironbee rule file '%s': %s",
+ conf->file, ib_status_to_string(rc));
+ return 0;
+ }
+
+ ib_cfgparser_destroy(parser);
+
+ /* Store the configuration. */
+ arg->type = ARGT_USR;
+ arg->data.usr = (struct userlist *)conf;
+ return 1;
+}
+
+static struct sample_fetch_kw_list sample_fetch_keywords = {ILH, {
+ { "ironbee", analyse, ARG1(1,STR), load_rules, SMP_T_SINT, SMP_USE_HRQHV },
+ { /* END */ },
+}};
+
+__attribute__((constructor))
+static void __ironbee_init(void)
+{
+ /* Initialize IronBee */
+ ib_initialize();
+
+ /* Register sample fetches keywords. */
+ sample_register_fetches(&sample_fetch_keywords);
+}
+
+#endif
--
2.1.4