Source Hash Algorithm

2020-03-29 Thread Andrew Heberle
I wondered what algorithm is used for the "source" load balancing option?

The reason for the question is due to wanting to load balance a service
that uses both TCP and UDP traffic... where the TCP and UDP must go to the
same back-end.

For TCP we are currently using HAproxy and simply drop the UDP traffic as
this is optional for the application (Microsoft RD/TS Gateway), but UDP can
improve interactive performance for remote RDP connections passed to the RD
Gateway.

My plan was to use LVS/keepalived for the UDP traffic but I was unsure if
the "source" option in HAProxy and the "sh" (source hash) scheduling
algorithm in LVS would yield consistent results.

The documentation for the "sh" algorithm in LVS notes the following
pseudo-code:

n <- servernode[src_ip];
if (n is dead) OR
   (n is overloaded, such as n.conns>2*n.weight) then
return NULL;
return n;

With the servernode array describe as "a 256-bucket hash table that maps
the hash index derived from packet source IP address to the current server
array".

Based on this would I expect the "source" balance option in HAProxy and the
"lh" selection algorithm in LVS to return a consistent back end?

Or should I simply use LVS for both connections (TCP and UDP)...although
HAProxy does other things on this device already...and is reasonably well
understood by our team...hence the desire to use HAProxy where possible.

-- 
Andrew Heberle


Re: HTX mode causes problems with VMWare Horizon View Zero Clients

2019-10-15 Thread Andrew Heberle
On Tue, 15 Oct 2019 at 4:45 pm, Christopher Faulet 
wrote:

> Le 14/10/2019 à 05:25, Andrew Heberle a écrit :
> > Hi All,
> >
> > We have a virtual desktop deployment under VMWare Horizon View that
> > uses PCoIP Zero clients that stopped working after upgrading the load
> > balancers in front of the internal Connection Servers (the virtual
> > desktop broker) from 1.8.12 to 2.0.7.
> >
>
> Hi,
>
> When you said your client stopped working, what does it mean exactly ?
> What did
> you observe to said the request failed ? Looking at the logs you provided,
> it
> seems to be ok from the HAProxy point of view.


The error is reported by the client with a horribly cryptic error that
suggests it’s not parsing/understanding the XML payload that is retrieved
initially.

>

>
> Just a question. It if happens for a specific client application, are you
> sure
> this client is able to handle HTTP headers in a case-insensitive manner ?
> In
> HAProxy 2.0, HTTP headers names are sent in lower case when the HTX is
> enabled.
> It may be a problem for bogus applications.


To be honest that was my initial thought as other clients using the same
service but on different platforms are fine, so it’s a client/device
specific interaction with HTX mode...which I’d bet HAProxy is more likely
to be “compliant” than these particular Zero clients.

I am happy to gather some additional traces though, to rule out some
strange edge case however, but as you say, the initial requests seem fine
so it’s questionable if traces will show anything useful.

Do you think it’s worthwhile collection debug logs?


>
> --
> Christopher Faulet
>
-- 
Andrew Heberle


HTX mode causes problems with VMWare Horizon View Zero Clients

2019-10-13 Thread Andrew Heberle
tion:\ Close\r\n\r\n
option log-health-checks
http-check expect string clientlaunch-default

# View Connection servers
server viewcs01 172.16.0.55:443 ssl check ca-file RootCA.pem
server viewcs02 172.16.0.56:443 ssl check ca-file RootCA.pem


Logs from a unsuccessful connection:

Oct 14 10:31:13 lb-01 local2.info haproxy[3142]: 172.19.4.80:50835
[14/Oct/2019:10:31:13.348] fe_viewcs~ be_viewcs/viewcs01 0/0/80/24/104
200 1108 - -  2884/1/0/0/0 0/0 "POST /broker/xml HTTP/1.1"
Oct 14 10:31:13 lb-01 local2.info haproxy[3142]: 172.19.4.80:62416
[14/Oct/2019:10:31:13.504] fe_viewcs~ be_viewcs/viewcs02 0/0/4/8/12
200 574 - -  2884/1/0/0/0 0/0 "POST /broker/xml HTTP/1.1"

And a successful one:

Oct 14 10:59:44 lb-01 local2.info haproxy[7100]: 172.19.4.80:59025
[14/Oct/2019:10:59:44.294] fe_viewcs~ be_viewcs/viewcs02 0/0/4/13/17
200 1120 - -  55/1/0/1/0 0/0 "POST /broker/xml HTTP/1.1"
Oct 14 10:59:44 lb-01 local2.info haproxy[7100]: 172.19.4.80:57390
[14/Oct/2019:10:59:44.828] fe_viewcs~ be_viewcs/viewcs01 0/0/3/49/52
200 1120 - -  55/1/0/1/0 0/0 "POST /broker/xml HTTP/1.1"
Oct 14 10:59:45 lb-01 local2.info haproxy[7100]: 172.19.4.80:53001
[14/Oct/2019:10:59:44.977] fe_viewcs~ be_viewcs/viewcs01 0/0/9/57/66
200 905 - -  47/1/0/1/0 0/0 "POST /broker/xml HTTP/1.1"
Oct 14 10:59:45 lb-01 local2.info haproxy[7100]: 172.19.4.80:61455
[14/Oct/2019:10:59:45.065] fe_viewcs~ be_viewcs/viewcs01 0/0/1/7/8 200
446 - -  47/1/0/1/0 0/0 "POST /broker/xml HTTP/1.1"
Oct 14 10:59:45 lb-01 local2.info haproxy[7100]: 172.19.4.80:49178
[14/Oct/2019:10:59:45.127] fe_viewcs~ be_viewcs/viewcs01 0/0/2/52/54
200 964 - -  48/1/0/1/0 0/0 "POST /broker/xml HTTP/1.1"
Oct 14 10:59:46 lb-01 local2.info haproxy[7100]: 172.19.4.80:49434
[14/Oct/2019:10:59:45.251] fe_viewcs~ be_viewcs/viewcs01
0/0/75/1550/1625 200 1570 - -  41/1/0/1/0 0/0 "POST /broker/xml
HTTP/1.1"


-- 
Andrew Heberle



[PATCH] MEDIUM: config: Add user/group options to program section

2019-07-11 Thread Andrew Heberle
This patch adds "user" and "group" config options to the "program"
section so the configured command can be run as a different user.
---
 doc/configuration.txt  |  8 ++
 include/types/global.h |  2 ++
 src/mworker-prog.c | 70 ++
 3 files changed, 80 insertions(+)

diff --git a/doc/configuration.txt b/doc/configuration.txt
index a46384bf..98940a0e 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -2232,6 +2232,14 @@ command  [arguments*]
   mandatory option of the program section. Arguments containing spaces must
   be enclosed in quotes or double quotes or be prefixed by a backslash.
 
+user 
+  Changes the executed command user ID to the  from /etc/passwd.
+  See also "group".
+
+group 
+  Changes the executed command group ID to the  from /etc/group.
+  See also "user".
+
 option start-on-reload
 no option start-on-reload
   Start (or not) a new instance of the program upon a reload of the master.
diff --git a/include/types/global.h b/include/types/global.h
index df0111c7..b6ba6737 100644
--- a/include/types/global.h
+++ b/include/types/global.h
@@ -215,6 +215,8 @@ struct mworker_proc {
int timestamp;
struct server *srv; /* the server entry in the master proxy */
struct list list;
+   int uid;
+   int gid;
 };
 
 extern struct global global;
diff --git a/src/mworker-prog.c b/src/mworker-prog.c
index ba52406e..1d401a3c 100644
--- a/src/mworker-prog.c
+++ b/src/mworker-prog.c
@@ -15,6 +15,7 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -91,6 +92,23 @@ int mworker_ext_launch_all()
mworker_cleanlisteners();
mworker_cleantasks();
 
+   /* setgid / setuid */
+   if (child->gid != -1) {
+   if (getgroups(0, NULL) > 0 && 
setgroups(0, NULL) == -1)
+   ha_warning("[%s.main()] Failed 
to drop supplementary groups. Using 'gid'/'group'"
+   " without 'uid'/'user' 
is generally useless.\n", child->command[0]);
+
+   if (setgid(child->gid) == -1) {
+   ha_alert("[%s.main()] Cannot 
set gid %d.\n", child->command[0], child->gid);
+   exit(1);
+   }
+   }
+
+   if (child->uid != -1 && setuid(child->uid) == 
-1) {
+   ha_alert("[%s.main()] Cannot set uid 
%d.\n", child->command[0], child->gid);
+   exit(1);
+   }
+
execvp(child->command[0], child->command);
 
ha_alert("Cannot execute %s: %s\n", 
child->command[0], strerror(errno));
@@ -143,6 +161,8 @@ int cfg_parse_program(const char *file, int linenum, char 
**args, int kwm)
ext_child->ipc_fd[0] = -1;
ext_child->ipc_fd[1] = -1;
ext_child->options |= PROC_O_START_RELOAD; /* restart the 
programs by default */
+   ext_child->uid = -1;
+   ext_child->gid = -1;
LIST_INIT(_child->list);
 
list_for_each_entry(child, _list, list) {
@@ -219,6 +239,56 @@ int cfg_parse_program(const char *file, int linenum, char 
**args, int kwm)
err_code |= ERR_ALERT | ERR_FATAL;
goto error;
}
+   } else if (!strcmp(args[0], "user")) {
+   struct passwd *ext_child_user;
+   if (*(args[1]) == '\0') {
+   ha_alert("parsing [%s:%d]: '%s' expects a user name.\n",
+file, linenum, args[0]);
+   err_code |= ERR_ALERT | ERR_FATAL;
+   goto error;
+   }
+
+   if (alertif_too_many_args(1, file, linenum, args, _code))
+   goto error;
+
+   if (ext_child->uid != -1) {
+   ha_alert("parsing [%s:%d] : user/uid already specified. 
Continuing.\n", file, linenum);
+   err_code |= ERR_ALERT;
+   goto out;
+   }
+
+   ext_child_user = getpwnam(args[1]);
+   if (ext_child_user != NULL) {
+   ext_child->uid = (int)ext_child_user->pw_uid;
+   } else {
+   ha_alert("parsing [%s:%d] : cannot find user id for 
'%s' (%d:%s)\n", file, linenum, args[1], errno, strerror(errno));
+   err_code |= ERR_ALERT | ERR_FATAL;
+   }
+   } else if (!strcmp(args[0], "group")) {
+   struct group *ext_child_group;
+   if (*(args[1]) == 

Re: [PATCH] MEDIUM: config: Add user/group options to program section

2019-07-11 Thread Andrew Heberle
Hi Willy,

It looks like my mailer was mangling the tabs so I'm hoping
my (first) attempt at using git send-patch is more
successful.

I have also updated the commit message.

Thanks.

Regards,

Andrew Heberle



[PATCH] MEDIUM: config: Add user/group options to program section

2019-07-11 Thread Andrew Heberle
This patch adds "user" and "group" config options to the "program"
section so the configured command can be run as a different user.

I re-used the setuid/setgid code from "haproxy.c" for this so I'm
hoping there are not terrible bugs I've introduced :)

Regards,

Andrew Heberle

>From 571715863738524e3f01fa842f8816f181777b89 Mon Sep 17 00:00:00 2001
From: Andrew Heberle 
Date: Thu, 11 Jul 2019 10:57:19 +0800
Subject: [PATCH] MEDIUM: config: Add user/group options to program section

---
doc/configuration.txt | 8 ++
include/types/global.h | 2 ++
src/mworker-prog.c | 70 ++
3 files changed, 80 insertions(+)

diff --git a/doc/configuration.txt b/doc/configuration.txt
index a46384bf..98940a0e 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -2232,6 +2232,14 @@ command  [arguments*]
mandatory option of the program section. Arguments containing spaces must
be enclosed in quotes or double quotes or be prefixed by a backslash.
+user 
+ Changes the executed command user ID to the  from /etc/passwd.
+ See also "group".
+
+group 
+ Changes the executed command group ID to the  from /etc/group.
+ See also "user".
+
option start-on-reload
no option start-on-reload
Start (or not) a new instance of the program upon a reload of the master.
diff --git a/include/types/global.h b/include/types/global.h
index df0111c7..b6ba6737 100644
--- a/include/types/global.h
+++ b/include/types/global.h
@@ -215,6 +215,8 @@ struct mworker_proc {
   int timestamp;
   struct server *srv; /* the server entry in the master proxy */
   struct list list;
+   int uid;
+   int gid;
};
extern struct global global;
diff --git a/src/mworker-prog.c b/src/mworker-prog.c
index ba52406e..1d401a3c 100644
--- a/src/mworker-prog.c
+++ b/src/mworker-prog.c
@@ -15,6 +15,7 @@
#include 
#include 
#include 
+#include 
#include 
#include 
#include 
@@ -91,6 +92,23 @@ int mworker_ext_launch_all()
   mworker_cleanlisteners();
   mworker_cleantasks();
+   /* setgid / setuid */
+   if (child->gid != -1) {
+   if (getgroups(0, NULL) > 0 && setgroups(0, NULL) == -1)
+   ha_warning("[%s.main()] Failed to drop
supplementary groups. Using 'gid'/'group'"
+   " without 'uid'/'user' is generally
useless.\n", child->command[0]);
+
+   if (setgid(child->gid) == -1) {
+   ha_alert("[%s.main()] Cannot set gid %d.\n",
child->command[0], child->gid);
+   exit(1);
+   }
+   }
+
+   if (child->uid != -1 && setuid(child->uid) == -1) {
+   ha_alert("[%s.main()] Cannot set uid %d.\n",
child->command[0], child->gid);
+   exit(1);
+   }
+
   execvp(child->command[0], child->command);
   ha_alert("Cannot execute %s: %s\n", child->command[0],
strerror(errno));
@@ -143,6 +161,8 @@ int cfg_parse_program(const char *file, int
linenum, char **args, int kwm)
   ext_child->ipc_fd[0] = -1;
   ext_child->ipc_fd[1] = -1;
   ext_child->options |= PROC_O_START_RELOAD; /* restart the
programs by default */
+   ext_child->uid = -1;
+   ext_child->gid = -1;
   LIST_INIT(_child->list);
   list_for_each_entry(child, _list, list) {
@@ -219,6 +239,56 @@ int cfg_parse_program(const char *file, int
linenum, char **args, int kwm)
   err_code |= ERR_ALERT | ERR_FATAL;
   goto error;
   }
+   } else if (!strcmp(args[0], "user")) {
+   struct passwd *ext_child_user;
+   if (*(args[1]) == '\0') {
+   ha_alert("parsing [%s:%d]: '%s' expects a user name.\n",
+file, linenum, args[0]);
+   err_code |= ERR_ALERT | ERR_FATAL;
+   goto error;
+   }
+
+   if (alertif_too_many_args(1, file, linenum, args, _code))
+   goto error;
+
+   if (ext_child->uid != -1) {
+   ha_alert("parsing [%s:%d] : user/uid already specified.
Continuing.\n", file, linenum);
+   err_code |= ERR_ALERT;
+   goto out;
+   }
+
+   ext_child_user = getpwnam(args[1]);
+   if (ext_child_user != NULL) {
+   ext_child->uid = (int)ext_child_user->pw_uid;
+   } else {
+   ha_alert("parsing [%s:%d] : cannot find user id for '%s'
(%d:%s)\n", file, linenum, args[1], errno, strerror(errno));
+   err_code |= ERR_ALERT | ERR_FATAL;
+   }
+   } else if (!strcmp(args[0], "group")) {
+   struct group *ext_child_group;
+   if (*(args[1]) == '\0') {
+   ha_alert("parsing [%s:%d]: '%s' expects a group name.\n",
+file, linenum, args[0]);
+   err_code |=

Proof of concept SPOE based SSO solution

2019-07-04 Thread Andrew Heberle
Hi All,

I have put together a Go based proof of concept SPOE agent that also
implements a SAML 2 Service Provider (SP) in order to do "SSO" in
HAProxy.

The code is located here:

https://gitlab.com/andrewheberle/go-http-auth-sso

The basic process is that SPOA is used to check if the user is logged
in or not and then based on the set variables you can make decisions
via "http-request" rules.

This originally started out without the SPOE part and was using the
Lua http-auth-request script
(https://github.com/TimWolla/haproxy-auth-request), however with the
release of the Go SPOE package
(https://github.com/Aestek/haproxy-connect/tree/master/spoe) I rewrote
it based on that.

Our use case is to have the SP pointed to a IdP in Azure so we can do
single-sign-on to Office 365 and we have "http-request" rules in place
to set some custom headers that our application uses for
authentication/authorisation.  These are set based on the variables
that come back from the SPOA, which come from the claims in the
authentication process.

Hopefully this is of some use to people.

Any feedback and constructive criticism is welcome.

-- 
Andrew Heberle



PATCH: s6 readiness notification support

2019-04-30 Thread Andrew Heberle
I have added working (for me at least) s6 notification support to
haproxy so it can signal that it's ready when running under the s6
supervision suite.

The process used under s6 is that the daemon signals readiness by
writing a line to an arbitrary FD (that the supervision suite has been
told about), then closing the FD as documented here:

https://skarnet.org/software/s6/notifywhenup.html

This signals the transition from started to ready.

At this stage the FD written to and closed is hard coded as fd@3 as I
have reached the end of my C expertise getting this far...

Although it seems like it's not something that many people are hanging
out for based on the responses to a previous email to the list, I
thought I'd send the patch through as-is anyway.

Regards,

Andrew Heberle

>From 9668e699b85bc5494d1a27e550e987ce43cb Mon Sep 17 00:00:00 2001
From: Andrew Heberle 
Date: Wed, 1 May 2019 09:40:41 +0800
Subject: [PATCH] Add s6 notification support

---
include/types/global.h | 1 +
src/haproxy.c | 12 
2 files changed, 13 insertions(+)

diff --git a/include/types/global.h b/include/types/global.h
index ba3738b..9481d64 100644
--- a/include/types/global.h
+++ b/include/types/global.h
@@ -72,6 +72,7 @@
#define GTUNE_BUSY_POLLING (1<<11)
#define GTUNE_LISTENER_MQ (1<<12)
#define GTUNE_SET_DUMPABLE (1<<13)
+#define GTUNE_USE_S6_NOTIFY (1<<14)
/* Access level for a stats socket */
#define ACCESS_LVL_NONE 0
diff --git a/src/haproxy.c b/src/haproxy.c
index 4c5c839..99a6f67 100644
--- a/src/haproxy.c
+++ b/src/haproxy.c
@@ -446,6 +446,7 @@ static void usage(char *name)
#if defined(USE_SYSTEMD)
   " -Ws master-worker mode with systemd notify support.\n"
#endif
+   " -Wn master-worker mode with s6 notify support.\n"
   " -q quiet mode : don't display messages\n"
   " -c check mode : only check config files and exit\n"
   " -n sets the maximum total # of connections (uses ulimit -n)\n"
@@ -652,6 +653,13 @@ static void mworker_loop()
   if (global.tune.options & GTUNE_USE_SYSTEMD)
   sd_notifyf(0, "READY=1\nMAINPID=%lu", (unsigned long)getpid());
#endif
+   /* if -Wn mode was requested then open fd 3, write an arbritrary
line and close the fd */
+   if (global.tune.options & GTUNE_USE_S6_NOTIFY) {
+   FILE *notifyfd;
+   notifyfd = fdopen(3, "w");
+   fprintf(notifyfd, "UP\n");
+   fclose(notifyfd);
+   }
   /* Busy polling makes no sense in the master :-) */
   global.tune.options &= ~GTUNE_BUSY_POLLING;
@@ -1427,6 +1435,10 @@ static void init(int argc, char **argv)
   usage(progname);
#endif
   }
+   else if (*flag == 'W' && flag[1] == 'n') {
+   arg_mode |= MODE_MWORKER | MODE_FOREGROUND;
+   global.tune.options |= GTUNE_USE_S6_NOTIFY;
+   }
   else if (*flag == 'W')
   arg_mode |= MODE_MWORKER;
   else if (*flag == 'q')
-- 
2.9.0.windows.1



Readiness notification support under s6

2019-04-29 Thread Andrew Heberle
Hi All,

In addition to the current readiness notification when running under
systemd via "sd_notify", I was hoping to have the daemon readiness
notification supported by "s6" built into HAProxy.

The mechanism used by s6, which amounts to the daemon writing to, then
closing an arbitrary file descriptor, is covered here:

https://skarnet.org/software/s6/notifywhenup.html

The supervision suite waits for this before transitioning the service
from up (ie daemon has been spawned) to ready (ie daemon is ready to
do useful work).

I've got a quick and dirty (and relatively untested) patch for this
already, but wanted to see if this was something that held any
interest to anyone apart from myself.

-- 
Andrew Heberle



Certificate bundles seem to be non-functional

2017-12-19 Thread Andrew Heberle
I am attempting to utilise certificate bundles so we can have multi-type
certs in haproxy however this seems non-functional.

I have a two cert bundles as follows (only testing with RSA certs at the
moment):

/etc/haproxy/ssl # ls -l /etc/haproxy/ssl/
total 16
-rw-r--r-- 1 root root 1184 Dec 20 01:39 test1.pem.issuer.rsa
-rw-r--r-- 1 root root 2888 Dec 20 01:26 test1.pem.rsa
-rw-r--r-- 1 root root 1184 Dec 20 01:40 test2.pem.issuer.rsa
-rw-r--r-- 1 root root 2888 Dec 20 01:30 test2.pem.rsa

With the following config of my two front-ends:

frontend test1
bind *:5000 ssl crt test1.pem
default_backend app1

frontend test2
bind *:5001 ssl crt test2.pem
default_backend app2

But this then fails:

/etc/haproxy/ssl # haproxy -f /etc/haproxy/haproxy.cfg -c
[ALERT] 353/014339 (59) : parsing [/etc/haproxy/haproxy.cfg:34] : 'bind
*:5000' : unable to stat SSL certificate from fi
le '/etc/haproxy/ssl/test1.pem' : No such file or directory.
[ALERT] 353/014339 (59) : parsing [/etc/haproxy/haproxy.cfg:38] : 'bind
*:5001' : unable to stat SSL certificate from fi
le '/etc/haproxy/ssl/test2.pem' : No such file or directory.
[ALERT] 353/014339 (59) : Error(s) found in configuration file :
/etc/haproxy/haproxy.cfg
[ALERT] 353/014339 (59) : Fatal errors found in configuration.

Build Options:

HA-Proxy version 1.7.9 2017/08/18
Copyright 2000-2017 Willy Tarreau <wi...@haproxy.org>

Build options :
  TARGET  = linux2628
  CPU = generic
  CC  = gcc
  CFLAGS  = -Os -fomit-frame-pointer
  OPTIONS = USE_ZLIB=1 USE_OPENSSL=1 USE_LUA=1 USE_PCRE=1

Default settings :
  maxconn = 2000, bufsize = 16384, maxrewrite = 1024, maxpollevents = 200

Encrypted password support via crypt(3): yes
Built with zlib version : 1.2.11
Running on zlib version : 1.2.11
Compression algorithms supported : identity("identity"),
deflate("deflate"), raw-deflate("deflate"), gzip("gzip")
Built with OpenSSL version : LibreSSL 2.6.3
Running on OpenSSL version : LibreSSL 2.6.3
OpenSSL library supports TLS extensions : yes
OpenSSL library supports SNI : yes
OpenSSL library supports prefer-server-ciphers : yes
Built with PCRE version : 8.41 2017-07-05
Running on PCRE version : 8.41 2017-07-05
PCRE library supports JIT : no (USE_PCRE_JIT not set)
Built with Lua version : Lua 5.3.4
Built with transparent proxy support using: IP_TRANSPARENT IPV6_TRANSPARENT
IP_FREEBIND

Available polling systems :
  epoll : pref=300,  test result OK
   poll : pref=200,  test result OK
 select : pref=150,  test result OK
Total: 3 (3 usable), will use epoll.

Available filters :
[COMP] compression
    [TRACE] trace
[SPOE] spoe

--
Andrew Heberle