From afbb28589be54d1e348ca2959ac34165d1e5304c Mon Sep 17 00:00:00 2001 From: Andrew Donald Kennedy Date: Wed, 3 Aug 2016 14:00:09 +0100 Subject: [PATCH] Update Nginx config file generator and add support for connection upgrade --- .../brooklyn/entity/proxy/ProxySslConfig.java | 2 +- .../entity/proxy/nginx/NginxController.java | 2 - .../proxy/nginx/NginxControllerImpl.java | 44 ----- .../nginx/NginxDefaultConfigGenerator.java | 152 ++++++++++-------- 4 files changed, 85 insertions(+), 115 deletions(-) diff --git a/software/webapp/src/main/java/org/apache/brooklyn/entity/proxy/ProxySslConfig.java b/software/webapp/src/main/java/org/apache/brooklyn/entity/proxy/ProxySslConfig.java index 95bfdf837..25c6c4f3e 100644 --- a/software/webapp/src/main/java/org/apache/brooklyn/entity/proxy/ProxySslConfig.java +++ b/software/webapp/src/main/java/org/apache/brooklyn/entity/proxy/ProxySslConfig.java @@ -97,7 +97,7 @@ public static ProxySslConfig fromMap(Map map) { private String clientCertificateDestination; private boolean verifyClient = false; private boolean targetIsSsl = false; - private boolean reuseSessions = false; + private boolean reuseSessions = true; public ProxySslConfig() { } diff --git a/software/webapp/src/main/java/org/apache/brooklyn/entity/proxy/nginx/NginxController.java b/software/webapp/src/main/java/org/apache/brooklyn/entity/proxy/nginx/NginxController.java index 939deacd8..2e382b53f 100644 --- a/software/webapp/src/main/java/org/apache/brooklyn/entity/proxy/nginx/NginxController.java +++ b/software/webapp/src/main/java/org/apache/brooklyn/entity/proxy/nginx/NginxController.java @@ -137,8 +137,6 @@ public interface NginxController extends AbstractController, HasShortName { Iterable getUrlMappings(); - boolean appendSslConfig(String id, StringBuilder out, String prefix, ProxySslConfig ssl, boolean sslBlock, boolean certificateBlock); - AttributeSensor NGINX_URL_ANSWERS_NICELY = Sensors.newBooleanSensor("nginx.url.answers.nicely"); AttributeSensor PID_FILE = Sensors.newStringSensor("nginx.pid.file", "PID file"); diff --git a/software/webapp/src/main/java/org/apache/brooklyn/entity/proxy/nginx/NginxControllerImpl.java b/software/webapp/src/main/java/org/apache/brooklyn/entity/proxy/nginx/NginxControllerImpl.java index 94b97327b..908f7b937 100644 --- a/software/webapp/src/main/java/org/apache/brooklyn/entity/proxy/nginx/NginxControllerImpl.java +++ b/software/webapp/src/main/java/org/apache/brooklyn/entity/proxy/nginx/NginxControllerImpl.java @@ -322,48 +322,4 @@ public Iterable getUrlMappings() { public String getShortName() { return "Nginx"; } - - public boolean appendSslConfig(String id, - StringBuilder out, - String prefix, - ProxySslConfig ssl, - boolean sslBlock, - boolean certificateBlock) { - if (ssl == null) - return false; - if (sslBlock) { - out.append(prefix); - out.append("ssl on;\n"); - } - if (ssl.getReuseSessions()) { - out.append(prefix); - out.append("proxy_ssl_session_reuse on;"); - } - if (certificateBlock) { - String cert; - if (Strings.isEmpty(ssl.getCertificateDestination())) { - cert = "" + id + ".crt"; - } else { - cert = ssl.getCertificateDestination(); - } - - out.append(prefix); - out.append("ssl_certificate " + cert + ";\n"); - - String key; - if (!Strings.isEmpty(ssl.getKeyDestination())) { - key = ssl.getKeyDestination(); - } else if (!Strings.isEmpty(ssl.getKeySourceUrl())) { - key = "" + id + ".key"; - } else { - key = null; - } - - if (key != null) { - out.append(prefix); - out.append("ssl_certificate_key " + key + ";\n"); - } - } - return true; - } } diff --git a/software/webapp/src/main/java/org/apache/brooklyn/entity/proxy/nginx/NginxDefaultConfigGenerator.java b/software/webapp/src/main/java/org/apache/brooklyn/entity/proxy/nginx/NginxDefaultConfigGenerator.java index 341bf67bf..b756ce4f6 100644 --- a/software/webapp/src/main/java/org/apache/brooklyn/entity/proxy/nginx/NginxDefaultConfigGenerator.java +++ b/software/webapp/src/main/java/org/apache/brooklyn/entity/proxy/nginx/NginxDefaultConfigGenerator.java @@ -18,18 +18,17 @@ */ package org.apache.brooklyn.entity.proxy.nginx; -import static java.lang.String.format; - import java.util.Collection; -import org.apache.brooklyn.entity.proxy.ProxySslConfig; -import org.apache.brooklyn.util.text.Strings; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.collect.LinkedHashMultimap; import com.google.common.collect.Multimap; +import org.apache.brooklyn.entity.proxy.ProxySslConfig; +import org.apache.brooklyn.util.text.Strings; + /** * Generates the {@code server.conf} configuration file using sensors on an {@link NginxController}. */ @@ -42,33 +41,37 @@ public NginxDefaultConfigGenerator() { } @Override public String generateConfigFile(NginxDriver driver, NginxController nginx) { StringBuilder config = new StringBuilder(); - config.append("\n"); - config.append(format("pid %s;\n", driver.getPidFile())); - config.append("events {\n"); - config.append(" worker_connections 8196;\n"); - config.append("}\n"); - config.append("http {\n"); + config.append("\n") + .append("pid ").append(driver.getPidFile()).append(";\n") + .append("events {\n") + .append(" worker_connections 8196;\n") + .append("}\n") + .append("http {\n") + .append(" map $http_upgrade $connection_upgrade {\n") + .append(" default Upgrade;\n") + .append(" '' close;\n") + .append(" }\n"); ProxySslConfig globalSslConfig = nginx.getSslConfig(); if (nginx.isSsl()) { verifyConfig(globalSslConfig); - appendSslConfig("global", config, " ", globalSslConfig, true, true); + appendSslConfig("global", config, " ", globalSslConfig, true, true); } // If no servers, then defaults to returning 404 // TODO Give nicer page back - if (nginx.getDomain()!=null || nginx.getServerPoolAddresses() == null || nginx.getServerPoolAddresses().isEmpty()) { - config.append(" server {\n"); - config.append(getCodeForServerConfig()); - config.append(" listen "+nginx.getPort()+";\n"); - config.append(getCodeFor404()); - config.append(" }\n"); + if (Strings.isNonEmpty(nginx.getDomain()) || nginx.getServerPoolAddresses() == null || nginx.getServerPoolAddresses().isEmpty()) { + config.append(" server {\n") + .append(getCodeForServerConfig()) + .append(" listen ").append(nginx.getPort()).append(";\n") + .append(getCodeFor404()) + .append(" }\n"); } // For basic round-robin across the server-pool if (nginx.getServerPoolAddresses() != null && nginx.getServerPoolAddresses().size() > 0) { - config.append(format(" upstream "+nginx.getId()+" {\n")); + config.append(" upstream ").append(nginx.getId()).append(" {\n"); if (nginx.isSticky()){ config.append(" sticky;\n"); } @@ -81,11 +84,18 @@ public String generateConfigFile(NginxDriver driver, NginxController nginx) { if (globalSslConfig != null) { appendCodeForProxySSLConfig(nginx.getId(), config, " ", globalSslConfig); } - config.append(" listen "+nginx.getPort()+";\n"); - if (nginx.getDomain()!=null) - config.append(" server_name "+nginx.getDomain()+";\n"); + config.append(" listen ").append(nginx.getPort()).append(";\n"); + if (Strings.isNonEmpty(nginx.getDomain())) { + config.append(" server_name ").append(nginx.getDomain()).append(";\n"); + } config.append(" location / {\n"); - config.append(" proxy_pass "+(globalSslConfig != null && globalSslConfig.getTargetIsSsl() ? "https" : "http")+"://"+nginx.getId()+";\n"); + config.append(" proxy_pass "); + if (globalSslConfig != null && globalSslConfig.getTargetIsSsl()) { + config.append("https"); + } else { + config.append("http"); + } + config.append("://").append(nginx.getId()).append(";\n"); config.append(" }\n"); config.append(" }\n"); } @@ -103,43 +113,42 @@ public String generateConfigFile(NginxDriver driver, NginxController nginx) { for (UrlMapping um : mappings) { Collection addrs = um.getAttribute(UrlMapping.TARGET_ADDRESSES); if (addrs != null && addrs.size() > 0) { - config.append(format(" upstream "+um.getUniqueLabel()+" {\n")); + config.append(" upstream ").append(um.getUniqueLabel()).append(" {\n"); if (nginx.isSticky()){ config.append(" sticky;\n"); } for (String address: addrs) { - config.append(" server "+address+";\n"); + config.append(" server ").append(address).append(";\n"); } config.append(" }\n"); } } for (String domain : mappingsByDomain.keySet()) { - config.append(" server {\n"); - config.append(getCodeForServerConfig()); - config.append(" listen "+nginx.getPort()+";\n"); - config.append(" server_name "+domain+";\n"); - boolean hasRoot = false; + config.append(" server {\n") + .append(getCodeForServerConfig()) + .append(" listen ").append(nginx.getPort()).append(";\n") + .append(" server_name ").append(domain).append(";\n"); // set up SSL ProxySslConfig localSslConfig = null; for (UrlMapping mappingInDomain : mappingsByDomain.get(domain)) { ProxySslConfig sslConfig = mappingInDomain.getConfig(UrlMapping.SSL_CONFIG); - if (sslConfig!=null) { + if (sslConfig != null) { verifyConfig(sslConfig); - if (localSslConfig!=null) { + if (localSslConfig != null) { if (localSslConfig.equals(sslConfig)) { //ignore identical config specified on multiple mappings } else { LOG.warn("{} mapping {} provides SSL config for {} when a different config had already been provided by another mapping, ignoring this one", - new Object[] {this, mappingInDomain, domain}); + new Object[] { this, mappingInDomain, domain }); } - } else if (globalSslConfig!=null) { + } else if (globalSslConfig != null) { if (globalSslConfig.equals(sslConfig)) { //ignore identical config specified on multiple mappings } else { LOG.warn("{} mapping {} provides SSL config for {} when a different config had been provided at root nginx scope, ignoring this one", - new Object[] {this, mappingInDomain, domain}); + new Object[] { this, mappingInDomain, domain }); } } else { //new config, is okay @@ -152,35 +161,42 @@ public String generateConfigFile(NginxDriver driver, NginxController nginx) { appendCodeForProxySSLConfig(domain, config, " ", localSslConfig); } + boolean hasRoot = false; for (UrlMapping mappingInDomain : mappingsByDomain.get(domain)) { // TODO Currently only supports "~" for regex. Could add support for other options, // such as "~*", "^~", literals, etc. - boolean isRoot = mappingInDomain.getPath()==null || mappingInDomain.getPath().length()==0 || mappingInDomain.getPath().equals("/"); + boolean isRoot = Strings.isEmpty(mappingInDomain.getPath()) || mappingInDomain.getPath().equals("/"); if (isRoot && hasRoot) { - LOG.warn(""+this+" mapping "+mappingInDomain+" provides a duplicate / proxy, ignoring"); + LOG.warn("{} mapping {} provides a duplicate / proxy, ignoring", this, mappingInDomain); } else { hasRoot |= isRoot; String location = isRoot ? "/" : "~ " + mappingInDomain.getPath(); - config.append(" location "+location+" {\n"); + config.append(" location ").append(location).append(" {\n"); Collection rewrites = mappingInDomain.getConfig(UrlMapping.REWRITES); if (rewrites != null && rewrites.size() > 0) { for (UrlRewriteRule rule: rewrites) { - config.append(" rewrite \"^"+rule.getFrom()+"$\" \""+rule.getTo()+"\""); + config.append(" rewrite \"^").append(rule.getFrom()).append("$\" \"").append(rule.getTo()).append("\""); if (rule.isBreak()) config.append(" break"); config.append(" ;\n"); } } - config.append(" proxy_pass "+ - (localSslConfig != null && localSslConfig.getTargetIsSsl() ? "https" : - (localSslConfig == null && globalSslConfig != null && globalSslConfig.getTargetIsSsl()) ? "https" : - "http")+ - "://"+mappingInDomain.getUniqueLabel()+" ;\n"); + config.append(" proxy_pass "); + if (localSslConfig != null && localSslConfig.getTargetIsSsl()) { + config.append("https"); + } else if (localSslConfig == null && globalSslConfig != null && globalSslConfig.getTargetIsSsl()) { + config.append("https"); + } else { + config.append("http"); + } + config.append("://").append(mappingInDomain.getUniqueLabel()).append(" ;\n"); config.append(" }\n"); } } if (!hasRoot) { //provide a root block giving 404 if there isn't one for this server - config.append(" location / { \n"+getCodeFor404()+" }\n"); + config.append(" location / { \n") + .append(getCodeFor404()) + .append(" }\n"); } config.append(" }\n"); } @@ -192,7 +208,7 @@ public String generateConfigFile(NginxDriver driver, NginxController nginx) { protected String getCodeForServerConfig() { // See http://wiki.nginx.org/HttpProxyModule - return "" + + return // this prevents nginx from reporting version number on error pages " server_tokens off;\n"+ @@ -201,6 +217,11 @@ protected String getCodeForServerConfig() { // url-mappings, at URL "http://localhost:${port}/atC0" (with a trailing slash it does work). " proxy_set_header Host $http_host;\n"+ + // Sets the HTTP protocol version for proxying and include connection upgrade headers + " proxy_http_version 1.1;\n"+ + " proxy_set_header Upgrade $http_upgrade;\n"+ + " proxy_set_header Connection $connection_upgrade;\n"+ + // following added, as recommended for wordpress in: // http://zeroturnaround.com/labs/wordpress-protips-go-with-a-clustered-approach/#!/ " proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n"+ @@ -216,24 +237,22 @@ protected void appendCodeForProxySSLConfig(String id, StringBuilder out, String // Use the configured SSL certificate and key for the proxied server String cert; if (Strings.isEmpty(ssl.getCertificateDestination())) { - cert = "" + id + ".crt"; + cert = id + ".crt"; } else { cert = ssl.getCertificateDestination(); } - out.append(prefix); - out.append("proxy_ssl_certificate " + cert + ";\n"); + out.append(prefix).append("proxy_ssl_certificate ").append(cert).append(";\n"); String key; - if (!Strings.isEmpty(ssl.getKeyDestination())) { + if (Strings.isNonEmpty(ssl.getKeyDestination())) { key = ssl.getKeyDestination(); - } else if (!Strings.isEmpty(ssl.getKeySourceUrl())) { - key = "" + id + ".key"; + } else if (Strings.isNonEmpty(ssl.getKeySourceUrl())) { + key = id + ".key"; } else { key = null; } if (key != null) { - out.append(prefix); - out.append("proxy_ssl_certificate_key " + key + ";\n"); + out.append(prefix).append("proxy_ssl_certificate_key ").append(key).append(";\n"); } } } @@ -252,52 +271,49 @@ protected boolean appendSslConfig(String id, StringBuilder out, String prefix, P boolean sslBlock, boolean certificateBlock) { if (ssl == null) return false; if (sslBlock) { - out.append(prefix); - out.append("ssl on;\n"); + out.append(prefix).append("ssl on;\n"); } if (ssl.getReuseSessions()) { - out.append(prefix); - out.append(""); + out.append(prefix).append("proxy_ssl_session_reuse on;\n"); + } else { + out.append(prefix).append("proxy_ssl_session_reuse off;\n"); } if (certificateBlock) { String cert; if (Strings.isEmpty(ssl.getCertificateDestination())) { - cert = "" + id + ".crt"; + cert = id + ".crt"; } else { cert = ssl.getCertificateDestination(); } - out.append(prefix); - out.append("ssl_certificate " + cert + ";\n"); + out.append(prefix).append("ssl_certificate ").append(cert).append(";\n"); String key; if (!Strings.isEmpty(ssl.getKeyDestination())) { key = ssl.getKeyDestination(); } else if (!Strings.isEmpty(ssl.getKeySourceUrl())) { - key = "" + id + ".key"; + key = id + ".key"; } else { key = null; } if (key != null) { - out.append(prefix); - out.append("ssl_certificate_key " + key + ";\n"); + out.append(prefix).append("ssl_certificate_key ").append(key).append(";\n"); } if (ssl.getVerifyClient()) { - out.append("ssl_verify_client on;\n"); + out.append(prefix).append("ssl_verify_client on;\n"); String client; if (Strings.isEmpty(ssl.getClientCertificateDestination())) { - client = "" + id + ".cli"; + client = id + ".cli"; } else { client = ssl.getClientCertificateDestination(); } if (client != null) { - out.append(prefix); - out.append("ssl_client_certificate " + client + ";\n"); + out.append(prefix).append("ssl_client_certificate ").append(client).append(";\n"); } } - out.append("ssl_protocols TLSv1 TLSv1.1 TLSv1.2;\n"); + out.append(prefix).append("ssl_protocols TLSv1 TLSv1.1 TLSv1.2;\n"); } return true; }