From 9533bb1a225c23f5d506f8604a6a4bbf1c37c036 Mon Sep 17 00:00:00 2001 From: William Durand Date: Thu, 25 Dec 2014 01:55:48 +0100 Subject: [PATCH 1/3] remove useless config_dev.json file --- config/config_dev.json | 17 ----------------- 1 file changed, 17 deletions(-) delete mode 100755 config/config_dev.json diff --git a/config/config_dev.json b/config/config_dev.json deleted file mode 100755 index db65bf0..0000000 --- a/config/config_dev.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "server": { - "debug": true, - "accessLog": "/tmp/proxy2_access.log", - "workers": 2, - "maxSockets": 100, - "deadBackendTTL": 10, - "tcpTimeout": 10, - "retryOnError": 3, - "deadBackendOn500": true, - "httpKeepAlive": false, - "lruCache": { - "size": 5, - "ttl": 5 - } - } -} From 945d6476fb0f0248e8efed59752ad7867f3b7bf6 Mon Sep 17 00:00:00 2001 From: William Durand Date: Thu, 25 Dec 2014 02:46:14 +0100 Subject: [PATCH 2/3] Fix config and give some love to the doc --- README.md | 304 +++++++++++++++++++++++++++-------------- lib/config/defaults.js | 9 +- 2 files changed, 208 insertions(+), 105 deletions(-) diff --git a/README.md b/README.md index f42da1f..7851e88 100644 --- a/README.md +++ b/README.md @@ -1,32 +1,38 @@ -Hipache: a distributed HTTP and websocket proxy +Hipache: a Distributed HTTP and WebSocket Proxy =============================================== -[![NPM version][npm-image]][npm-url] [![Build Status][travis-image]][travis-url] [![Dependency Status][depstat-image]][depstat-url] [![Coverage Status][coveralls-image]][coveralls-url] [![Code Climate][codeclimate-image]][codeclimate-url] [![Stories in Ready][waffle-image]][waffle-url] +[![NPM version][npm-image]][npm-url] [![Build Status][travis-image]][travis-url] +[![Dependency Status][depstat-image]][depstat-url] [![Coverage +Status][coveralls-image]][coveralls-url] [![Code +Climate][codeclimate-image]][codeclimate-url] [![Stories in +Ready][waffle-image]][waffle-url] WARNING ------------ +------- -This is the documentation for `master`. If you are running **Hipache release**, you should look at -the documentation on the `0.3.x` branch. +This is the documentation for `master`. If you are running **Hipache release**, +you should look at the documentation on the `0.3.x` branch. -What is it? +What Is It? ----------- -Hipache (pronounce `hɪ'pætʃɪ`) is a distributed proxy designed to route high volumes of http and -websocket traffic to unusually large numbers of virtual hosts, in a highly -dynamic topology where backends are added and removed several times per second. -It is particularly well-suited for PaaS (platform-as-a-service) and other -environments that are both business-critical and multi-tenant. +Hipache (pronounce `hɪ'pætʃɪ`) is a [fully-featured](#features) distributed +proxy designed to route high volumes of HTTP and WebSocket traffic to unusually +large numbers of virtual hosts, in a highly dynamic topology where backends are +added and removed several times per second. It is particularly well-suited for +PaaS (Platform-as-a-Service) and other environments that are both +business-critical and multi-tenant. Hipache was originally developed at [dotCloud](http://www.dotcloud.com), a popular platform-as-a-service, to replace its first-generation routing layer -based on a heavily instrumented nginx deployment. It currently serves -production traffic for tens of thousands of applications hosted on dotCloud. -Hipache is based on the node-http-proxy library. +based on a heavily instrumented nginx deployment. It currently serves production +traffic for tens of thousands of applications hosted on dotCloud. Hipache is +based on the [node-http-proxy](https://github.com/nodejitsu/node-http-proxy) +library. -Run it! +Run It! ------- ### 1. Installation @@ -35,58 +41,112 @@ From the shell: $ npm install hipache -g -*The '-g' option will make the 'hipache' bin-script available system-wide (usually linked from '/usr/local/bin')* +_The '-g' option will make the 'hipache' bin-script available system-wide +(usually linked from '/usr/local/bin')._ -### 2. Configuration (config.json) +### 2. Configuration File -Basic Hipache configuration is described in a json file. For example, this is the configuration -file for the `master` version of Hipache (i.e. under development, you should rather look at the -documentation of the latest stable version you installed): +Basic Hipache configuration is described in a `config.json` file. For example, +this is the configuration file for the `master` version of Hipache (i.e. under +development, you should rather look at the documentation of the latest stable +version you installed): { "server": { - "accessLog": "/var/log/hipache_access.log", - "workers": 5, + "debug": false, + "workers": 10, "maxSockets": 100, + "tcpTimeout": 30, "deadBackendTTL": 30, + "retryOnError": 3, + "accessLog": "/var/log/hipache/access.log", + "httpKeepAlive": false, + "deadBackendOn500": true, "staticDir": null }, "http": { "port": 80, - "bind": ["127.0.0.1", "::1"] + "bind": ["127.0.0.1"] }, "https": { + "bind": [], "port": 443, - "bind": ["127.0.0.1", "::1"], + "ca": [], + "secureProtocol": "SSLv23_method", + "secureOptions": 50331648, "key": "/etc/ssl/ssl.key", - "cert": "/etc/ssl/ssl.crt" + "cert": "/etc/ssl/ssl.crt", + "passphrase": undefined, + "ciphers": "DH+ECDSA+AESGCM EECDH+aRSA+AESGCM EECDH+ECDSA+SHA384 EECDH+ECDSA+SHA256 EECDH+a RSA+SHA384 EECDH+aRSA+SHA256 EECDH+aRSA+RC4 EECDH EDH+aRSA RC4 !aNULL !eNULL !LOW !3DES !MD5 !EXP !PSK !SRP !DSS !RC4", + "honorCipherOrder": true }, - "driver": "redis://:password@127.0.0.1:6379/0" + "driver": "redis:" + "user": "www-data", + "group": "www-data" } - * __server__: generic server settings, like accesslog location, or number of workers - * __server.accessLog__: location of the Access logs, the format is the same as -nginx. Defaults to `/var/log/hipache/access.log` if not specified. - * __server.workers__: Number of workers to be spawned. You need to request to have at least 1 worker, as the -master process does not serve any request. Defaults to `10` if not specified. - * __server.maxSockets__: The maximum number of sockets which can be opened on each backend (per worker). Defaults to `100` if not specified. - * __server.deadBackendTTL__: The number of seconds a backend is flagged as -'dead' before retrying to proxy another request to it (doesn't apply if you are using a third-party health checker). Defaults to `30`. - * __server.staticDir__: The absolute path of the directory containing your custom static error pages. Default value `null` means it uses Hipache's pages. - * __http__: specifies on which ips/ports hipache will listen for http traffic. By default, hipache listens only on 127.0.0.1:80 + * __server__: generic server settings, like accesslog location, or number of + workers + * __server.debug__: debug mode. + * __server.workers__: number of workers to be spawned. You need to request + to have at least 1 worker, as the master process does not serve any + request. Defaults to `10` if not specified. + * __server.maxSockets__: the maximum number of sockets which can be opened + on each backend (per worker). Defaults to `100` if not specified. + * __server.tcpTimeout__: the number of seconds of inactivity on the socket + to wait before timeout-ing it. If sets to `0`, then the existing idle + timeout is disabled. Defaults to `30` seconds. + * __server.deadBackendTTL__: the number of seconds a backend is flagged as + 'dead' before retrying to proxy another request to it (doesn't apply if + you are using a third-party health checker). Defaults to `30` seconds. + * __server.retryOnError__: retries limit. Defaults to `3`. + * __server.accessLog__: location of the Access logs, the format is the same + as nginx. Defaults to `/var/log/hipache/access.log` if not specified. + * __server.httpKeepAlive__: enable/disable keep-alive functionality. + Defaults to `false` (disabled). + * __server.deadBackendOn500__: consider `500` HTTP status code as critical + error if sets to `true`. Defaults to `true`. + * __server.staticDir__: the absolute path of the directory containing your + custom static error pages. Default value `null` means it uses Hipache's + pages. Defaults to Hipache's `static/` directory. + * __http__: specifies on which ips/ports Hipache will listen for http traffic. + By default, Hipache listens only on 127.0.0.1:80 * __http.port__: port to listen to for http. Defaults to `80`. - * __http.bind__: IPv4 (or IPv6) address, or addresses to listen to. You can specify a single ip, an array of ips, or an array of objects `{address: IP, port: PORT}` if you want to use a specific port on a specific ip. Defaults to `127.0.0.1`. - * __https__: specifies on which ips/ports hipache will listen for https traffic. By default, hipache doesn't listens for https traffic. + * __http.bind__: IPv4 (or IPv6) address, or addresses to listen to. You can + specify a single ip, an array of ips, or an array of objects `{address: + IP, port: PORT}` if you want to use a specific port on a specific ip. + Defaults to `127.0.0.1`. + * __https__: specifies on which ips/ports Hipache will listen for https + traffic. By default, Hipache doesn't listens for https traffic. * __https.port__: port to listen to for https. Defaults to `443`. * __https.key__: path to key file to use. No default. - * __https.passphrase__: optional passphrase for the key file. + * __https.passphrase__: optional passphrase for the key file. No default. * __https.cert__: path to certificate file to use. No default. - * __https.ca__: optional path to additional CA file to serve. Might be a string, or an array. - * __https.bind__: similarly to http.bind, you can specific a single ip, an array of ip, or an array of objects to override the port, key/cert/ca files on a per-ip basis. - * __driver__: Redis url to connect to for dynamic vhost configurations. If you want a master/slave Redis, specify a second url for the master, eg: `driver: ["redis://slave:port", "redis://master:port"]`. More generally, the driver syntax is: `redis://:password@host:port/database#prefix` - all parameter are optional, hence just `redis:` is a valid driver uri. More infos about drivers in [lib/drivers](https://github.com/dotcloud/hipache/tree/master/lib/drivers). You can omit this entirely to use the local redis on the default port, which is the default. - * __user__: if starting as root (which you might do if you want to use a privileged port), will drop root privileges as soon as it's bound. Defaults to `www-data`. Note that you MUST specify a user if you start hipache as root. You can specify `user: root` if you don't mind (strongly discouraged!). You can use either user names or ids. - * __group__: if starting as root, will downgrade group to this. If left empty, will try to downgrade to a group named after the specified `user`. Defaults to `www-data`. + * __https.ca__: optional path to additional CA file to serve. Might be a + string, or an array. + * __https.bind__: similarly to http.bind, you can specific a single IP, an + array of IP, or an array of objects to override the port, key/cert/ca + files on a per-IP basis. + * __https.secureProtocol__: SSL/TLS protocol to use. Defaults to + `SSLv23_method` (auto-negotiation). + * __https.secureOptions__: extra options to pass to the SSL/TLS layer. Raw + values must be provided. For instance, defaults is `50331648`, and stands + for `SSL_OP_NO_SSLv3 | SSL_OP_NO_SSLv2` (constants). + * __https.ciphers__: cipher suites. See the default value above. + * __https.honorCipherOrder__: when choosing a cipher, use the server's + preferences instead of the client preferences. Defaults to `true`. + * __driver__: driver URL to connect to for dynamic VHOST configurations. See + [drivers section](#drivers) for more information. Defaults to `redis:`. + * __user__: if starting as `root` (which you might do if you want to use a + privileged port), will drop root privileges as soon as it's bound. Defaults + to `www-data`. Note that you MUST specify a user if you start Hipache as + root. You can specify `user: 'root'` if you don't mind (strongly + discouraged!). You can use either user names or identifiers. + * __group__: if starting as `root`, will downgrade group to this. If left + empty, will try to downgrade to a group named after the specified `user`. + Defaults to `www-data`. + ### 3. Spawning @@ -94,7 +154,7 @@ From the shell (defaults to using the `config/config.json` file): $ hipache -If you use a privileged port (eg: 80): +If you use a privileged port (e.g.: `80`): $ sudo hipache @@ -109,8 +169,8 @@ If you want to just test a specific configuration file: __Managing multiple configuration files:__ The default configuration file is `config/config.json`. It's possible to have -different configuration files named `config_.json`, where the suffix -is the value of an environment variable named `SETTINGS_FLAVOR`. +different configuration files named `config_.json`, where the suffix is +the value of an environment variable named `SETTINGS_FLAVOR`. For instance, here is how to spawn the server with the `config_test.json` configuration file in order to run the tests. @@ -118,57 +178,57 @@ configuration file in order to run the tests. $ SETTINGS_FLAVOR=test hipache -### 4. Configuring a vhost (redis) +### 4. VHOST Configuration -All vhost configuration is managed through Redis. This makes it possible to -update the configuration dynamically and gracefully while the server is -running, and have that state shared across workers and even across Hipache instances. +All VHOST configuration is managed through a configuration backend (cf. +[drivers](#drivers)). This makes it possible to update the configuration +dynamically and gracefully while the server is running, and have that state +shared across workers and even across Hipache instances. -It also makes it simple to write configuration adapters. It would be trivial -to load a plain text configuration file into Redis (and update it at runtime). +The recommended backend to use is **Redis**. It also makes it simple to write +configuration adapters. It would be trivial to load a plain text configuration +file into Redis (and update it at runtime). Different configuration adapters will follow, but for the moment you have to provision the Redis manually. -Let's take an example, I want to proxify requests to 2 backends for the -hostname www.dotcloud.com. The 2 backends IP are 192.168.0.42 and 192.168.0.43 -and they serve the HTTP traffic on the port 80. +Let's take an example to proxify requests to 2 backends for the hostname +`www.dotcloud.com`. The 2 backends IP are `192.168.0.42` and `192.168.0.43` and +they serve the HTTP traffic on the port `80`. `redis-cli` is the standard client tool to talk to Redis from the terminal. -Here are the steps I will follow: +Follow these steps: -1. __Create__ the frontend and associate an identifier +1. __Create__ the frontend and associate an identifier: $ redis-cli rpush frontend:www.dotcloud.com mywebsite (integer) 1 The frontend identifer is `mywebsite`, it could be anything. -2. __Associate__ the 2 backends +2. __Associate__ the 2 backends: $ redis-cli rpush frontend:www.dotcloud.com http://192.168.0.42:80 (integer) 2 $ redis-cli rpush frontend:www.dotcloud.com http://192.168.0.43:80 (integer) 3 -3. __Review__ the configuration +3. __Review__ the configuration: $ redis-cli lrange frontend:www.dotcloud.com 0 -1 1) "mywebsite" 2) "http://192.168.0.42:80" 3) "http://192.168.0.43:80" -While the server is running, any of these steps can be re-run without messing -up with the traffic. +While the server is running, any of these steps can be re-run without messing up +with the traffic. -### 5. OS integration +### 5. OS Integration __Upstart__ -Copy upstart.conf to __/etc/init/hipache.conf__. - -Then you can use: +Copy `upstart.conf` to __/etc/init/hipache.conf__. Then you can use: ``` start hipache @@ -178,10 +238,42 @@ restart hipache The configuration file used is `/etc/hipache.json`. + +Drivers +------- + +Hipache supports several drivers for dynamic VHOST configurations. + +### Redis + +This is the default backend. + +If you want a master/slave Redis, specify a second url for the master, e.g.: +`driver: ["redis://slave:port", "redis://master:port"]`. More generally, the +driver syntax is: `redis://:password@host:port/database#prefix` - all parameter +are optional, hence just `redis:` is a valid driver URI. You can omit this +entirely to use the local redis on the default port, which is the default. + +### Memcached + +See the [drivers +documentation](https://github.com/dotcloud/hipache/tree/master/lib/drivers/README.md). + +### etcd + +See the [drivers +documentation](https://github.com/dotcloud/hipache/tree/master/lib/drivers/README.md). + +### Zookeeper + +See the [drivers +documentation](https://github.com/dotcloud/hipache/tree/master/lib/drivers/README.md). + + Features -------- -### Load-balancing across multiple backends +### Load-Balancing Across Multiple Backends As seen in the example above, multiple backends can be attached to a frontend. @@ -192,41 +284,41 @@ The backend to use for a specific request is determined randomly. Subsequent requests coming from the same client won't necessarily be routed to the same backend (since backend selection is purely random). -### Dead backend detection +### Dead Backend Detection -If a backend stops responding, it will be flagged as dead for a -configurable amount of time. The dead backend will be temporarily removed from -the load-balancing rotation. +If a backend stops responding, it will be flagged as dead for a configurable +amount of time. The dead backend will be temporarily removed from the +load-balancing rotation. -### Multi-process architecture +### Multi-Process Architecture To optimize response times and make use of all your available cores, Hipache uses the cluster module (included in NodeJS), and spreads the load across -multiple NodeJS processes. A master process is in charge of spawning workers -and monitoring them. When a worker dies, the master spawns a new one. +multiple NodeJS processes. A master process is in charge of spawning workers and +monitoring them. When a worker dies, the master spawns a new one. -### Memory monitoring +### Memory Monitoring -The memory footprint of Hipache tends to grow slowly over time, indicating -a probable memory leak. A close examination did not turn up any memory leak -in Hipache's code itself; but it doesn't prove that there is none. Also, -we did not investigate (yet) thoroughly the code of Hipache's external -dependencies, so the leaks could be creeping there. +The memory footprint of Hipache tends to grow slowly over time, indicating a +probable memory leak. A close examination did not turn up any memory leak in +Hipache's code itself; but it doesn't prove that there is none. Also, we did not +investigate (yet) thoroughly the code of Hipache's external dependencies, so the +leaks could be creeping there. While we profile Hipache's memory to further reduce its footprint, we -implemented a memory monitoring system to make sure that memory use doesn't -go out of bounds. Each worker monitors its memory usage. If it crosses -a given threshold, the worker stops accepting new connections, it lets -the current requests complete cleanly, and it stops itself; it is then -replaced by a new copy by the master process. +implemented a memory monitoring system to make sure that memory use doesn't go +out of bounds. Each worker monitors its memory usage. If it crosses a given +threshold, the worker stops accepting new connections, it lets the current +requests complete cleanly, and it stops itself; it is then replaced by a new +copy by the master process. -### Dynamic configuration +### Dynamic Configuration -You can alter the configuration stored in Redis at any time. There is no -need to restart Hipache, or to signal it that the configuration has changed: -Hipache will re-query Redis at each request. Worried about performance? -We were, too! And we found out that accessing a local Redis is helluva fast. -So fast, that it didn't increase measurably the HTTP request latency! +You can alter the configuration stored in Redis at any time. There is no need to +restart Hipache, or to signal it that the configuration has changed: Hipache +will re-query Redis at each request. Worried about performance? We were, too! +And we found out that accessing a local Redis is helluva fast. So fast, that it +didn't increase measurably the HTTP request latency! ### WebSocket @@ -236,36 +328,40 @@ on its own and relies entirely on NodeJS and node-http-proxy. ### SSL Hipache supports SSL for "regular" requests as well as WebSocket upgrades. +Hipache's default configuration matches latest recommandations for a secure and +well-configured SSL/TLS layer. -### Custom HTML error pages +### Custom HTML Error Pages -When something wrong happens (e.g., a backend times out), or when a request -for an undefined virtual host comes in, Hipache will display an error page. -Those error pages can be customized. +When something wrong happens (e.g., a backend times out), or when a request for +an undefined virtual host comes in, Hipache will display an error page. Those +error pages can be customized, and a configuration parameter is available to +specify where these custom pages are located. -### Wildcard domains support +### Wildcard Domains Support When adding virtual hosts in Hipache configuration, you can specify wildcards. -E.g., instead (or in addition to) www.example.tld, you can insert -*.example.tld. Hipache will look for an exact match first, and then for a -wildcard one up to 5 subdomains deep, e.g. foo.bar.baz.qux.quux will attempt to -match itself first, then *.bar.baz.qux.quux, then *.baz.qux.quux, etc. +E.g., instead (or in addition to) `www.example.tld`, you can insert +`*.example.tld`. Hipache will look for an exact match first, and then for a +wildcard one up to 5 subdomains deep, e.g. `foo.bar.baz.qux.quux` will attempt +to match itself first, then `*.bar.baz.qux.quux`, then `*.baz.qux.quux`, etc. ### Active Health-Check Even though Hipache support passive health checks, it's also possible to run -active health checks. This mechanism requires to run an external program (see third-party softwares below). +active health checks. This mechanism requires to run an external program (see +third-party softwares below). -Third party softwares of interest -------------------- +Third-Party Softwares of Interest +--------------------------------- Health-checkers: * [hipache-hchecker (golang)](https://github.com/samalba/hipache-hchecker) * [hipcheck (node.js)](https://github.com/runnable/hipcheck). -A web interface to manage vhosts: +A web interface to manage VHOSTs: * [airfield](https://github.com/emblica/airfield) diff --git a/lib/config/defaults.js b/lib/config/defaults.js index 292bab7..0ccd5e6 100644 --- a/lib/config/defaults.js +++ b/lib/config/defaults.js @@ -23,7 +23,14 @@ ca: [], secureProtocol: "SSLv23_method", /*jshint camelcase:false*/ - secureOptions: constants.SSL_OP_NO_SSLv3 | constants.SSL_OP_NO_SSLv2 + secureOptions: constants.SSL_OP_NO_SSLv3 | constants.SSL_OP_NO_SSLv2, + ciphers: [ + 'DH+ECDSA+AESGCM EECDH+aRSA+AESGCM EECDH+ECDSA+SHA384', + 'EECDH+ ECDSA+SHA256 EECDH+a RSA+SHA384 EECDH+aRSA+SHA256', + 'EECDH+aRSA+RC4 EECDH EDH+aRSA RC4 !aNULL !eNULL !LOW !3DES', + '!MD5 !EXP !PSK !SRP !DSS !RC4' + ].join(' '), + honorCipherOrder: true }, // By default use a single redis server on localhost default port From b045fd059f153b4873f3f27a59f572fd9e2c8038 Mon Sep 17 00:00:00 2001 From: William Durand Date: Fri, 26 Dec 2014 23:37:30 +0100 Subject: [PATCH 3/3] Fix doc about the test suite --- test/functional/README.md | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/test/functional/README.md b/test/functional/README.md index 3da0ea9..12696a1 100644 --- a/test/functional/README.md +++ b/test/functional/README.md @@ -9,14 +9,17 @@ The tests use the Python `unittest` framework. Why the ... use Python test insid Setting up the test environment ------------------------------- -To run the tests, you need to be able to run Hipache on your machine. You also need Python 2.7 and a couple of Python dependencies. +To run the tests, you need to be able to run Hipache on your machine. You also +need Python 2.7 and a couple of Python dependencies. + +**Important:** all commands below must be run at the root of the project. First, install Hipache: - # at the root of the repository npm install -Then, create a Python workspace to run the tests (we *highly* recommend you to install `virtualenv` and `virtualenvwrapper` for that purpose): +Then, create a Python workspace to run the tests (we *highly* recommend you to +install `virtualenv` and `virtualenvwrapper` for that purpose): # Note: you need at least Python 2.7 # (Python 2.6 doesn't have tests discovery) @@ -24,7 +27,7 @@ Then, create a Python workspace to run the tests (we *highly* recommend you to i Install the required Python dependencies: - pip install -r test/requirements.txt + pip install -r test/functional/requirements.txt Running the tests