
On 04/10/2014 05:44 PM, Daniel Barboza wrote:
From: Daniel Henrique Barboza <danielhb@linux.vnet.ibm.com>
The kimchid script is now launching a reverse proxy (nginx) that will forward all requests made to the kimchi port to the kimchid process running as root in a different port, which can be made inacessible to the outside using firewall rules.
Signed-off-by: Daniel Henrique Barboza <danielhb@linux.vnet.ibm.com> --- src/kimchid.in | 111 ++++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 102 insertions(+), 9 deletions(-)
diff --git a/src/kimchid.in b/src/kimchid.in index 8b63b57..fd755eb 100644 --- a/src/kimchid.in +++ b/src/kimchid.in @@ -16,13 +16,18 @@ # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301 USA
You don't need to break line here. Just remove the extra space "02110-1301 USA" to achieve the 80 characters.
import logging import os +import signal +import subprocess import sys sys.path.insert(1, '@pythondir@')
+from string import Template + import kimchi.server import kimchi.config
@@ -35,7 +40,63 @@ if not paths.installed: ACCESS_LOG = "kimchi-access.log" ERROR_LOG = "kimchi-error.log"
+ +def create_proxy_config(p_port, k_port, + p_ssl_port, k_ssl_port): + """Create nginx configuration file based on current ports config + + To allow flexibility in which port kimchi runs, we need the same + flexibility with the nginx proxy. This method creates the config + file dynamically by using 'nginx.conf.in' as a template, creating + the file 'nginx_kimchi.config' which will be used to launch the + proxy. + + Arguments: + p_port - proxy port + kimchid_port - kimchid port + p_ssl_port - proxy SSL port + kimchid_ssl_port - kimchid SSL port + """ + + # get SSL paths to be used by the proxy + config_dir = paths.conf_dir + cert = '%s/kimchi-cert.pem' % config_dir + key = '%s/kimchi-key.pem' % config_dir +
+ with open(os.path.join(config_dir, "nginx.conf.in")) as template: + data = template.read()
This patch uses "nginx.conf.in" but it is not in this patch. You should merge this patch with patch 2 to have a consistent commit.
+ + data = Template(data) + data = data.safe_substitute(proxy_port=p_port, + kimchid_port=k_port, + proxy_ssl_port=p_ssl_port, + kimchid_ssl_port=k_ssl_port, + cert_pem=cert, cert_key=key) + + config_file = open(os.path.join(config_dir, "nginx_kimchi.conf"), "w") + config_file.write(data) + config_file.close() + + +def start_proxy(): + """Start nginx reverse proxy.""" + config_dir = paths.conf_dir + config_file = "%s/nginx_kimchi.conf" % config_dir + cmd = ['nginx', '-c', config_file] + subprocess.call(cmd) + + +def terminate_proxy(): + """Stop nginx process.""" + term_proxy_cmd = ['nginx', '-s', 'stop'] + subprocess.call(term_proxy_cmd) + + def main(options): + # Script must run as root or with sudo. + if not os.geteuid() == 0: + sys.exit("\nMust be root to run this script. Exiting ...\n") + host = config.get("server", "host") port = config.get("server", "port") ssl_port = config.get("server", "ssl_port") @@ -44,20 +105,52 @@ def main(options): logLevel = config.get("logging", "log_level")
parser = OptionParser() - parser.add_option('--host', type="string", default=host, help="Hostname to listen on") - parser.add_option('--port', type="int", default=port, help="Port to listen on") - parser.add_option('--ssl-port', type="int", default=ssl_port, help="Enable a SSL server on the given port") - parser.add_option('--log-level', default=logLevel, help="Logging level") - parser.add_option('--access-log', default=os.path.join(logDir,ACCESS_LOG), help="Access log file") - parser.add_option('--error-log', default=os.path.join(logDir,ERROR_LOG), help="Error log file") - parser.add_option('--environment', default=runningEnv, help="Running environment of kimchi server") - parser.add_option('--test', action='store_true', help="Run server in mock model") + parser.add_option('--host', type="string", default=host, + help="Hostname to listen on") + parser.add_option('--port', type="int", default=port, + help="Port to listen on") + parser.add_option('--ssl-port', type="int", default=ssl_port, + help="Enable a SSL server on the given port") + parser.add_option('--log-level', default=logLevel, + help="Logging level") + parser.add_option('--access-log', + default=os.path.join(logDir, ACCESS_LOG), + help="Access log file") + parser.add_option('--error-log', + default=os.path.join(logDir, ERROR_LOG), + help="Error log file") + parser.add_option('--environment', default=runningEnv, + help="Running environment of kimchi server") + parser.add_option('--test', action='store_true', + help="Run server in mock model") (options, args) = parser.parse_args()
# Add non-option arguments setattr(options, 'ssl_cert', config.get('server', 'ssl_cert')) setattr(options, 'ssl_key', config.get('server', 'ssl_key'))
+ # The following method is used when the process receives a + # SIGINT signal (CTRL+C). + def terminate_kimchi(signal, frame): + terminate_proxy() + sys.exit(0) + signal.signal(signal.SIGINT, terminate_kimchi)
I think the better way to do it is subscribe to the cherrypy exit function in server.py cherrypy.engine.subscribe('exit', <function to kill proxy>)
+ + proxy_port = options.port + proxy_ssl_port = options.ssl_port +
+ # The max value between port and proxy_port + # will be used to calculate kimchid ports. + next_port = max(proxy_port, proxy_ssl_port) + 1 + options.port = next_port + next_port += 1 + options.ssl_port = next_port +
Automatically discover the ports can be an easy point of errors. The ports can be in used by other application. I think we should add more options to set the proxy ports and set a default value for it in the kimchi.conf file So I expect: kimchid --proxy-port=XXXX --proxy-ssl-port=YYYY
+ # Launch reverse proxy: create config file and start. + create_proxy_config(proxy_port, options.port, + proxy_ssl_port, options.ssl_port) + start_proxy() + kimchi.server.main(options)
if __name__ == '__main__':