diff --git a/CHANGES.txt b/CHANGES.txt
index 4419c4b8e1d7469606af7997d80dc61b48f59bf3..7ede921ded955238e43db7c47b54cbc74260d02d 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,6 +1,16 @@
 Changes
 =======
 
+0.38 (Unreleased)
+-----------------
+ * No change yet.
+
+0.37 (2011-11-24)
+-----------------
+
+ * KVM : allow access to several KVM instances without SSL certificate duplicate
+   problem. [Cedric de Saint Martin]
+
 0.36 (2011-11-16)
 -----------------
 
diff --git a/component/git/buildout.cfg b/component/git/buildout.cfg
index 9ff6bc1e69df7d302cd9b69298f1c36264f03874..0d75e871602f191c5638f46b2213b709fa2cd046 100644
--- a/component/git/buildout.cfg
+++ b/component/git/buildout.cfg
@@ -15,8 +15,8 @@ parts =
 recipe = hexagonit.recipe.cmmi
 # url = http://kernel.org/pub/software/scm/git/git-1.7.4.5.tar.bz2
 # Circumvent kernel.org downtime
-url = http://ftp.free.fr/mirrors/ftp.kernel.org/software/scm/git/git-1.7.4.5.tar.bz2
-md5sum = 2fa6c4c847ed87523cf55de54af457eb
+url = http://git-core.googlecode.com/files/git-1.7.7.4.tar.gz
+md5sum = 2f3277475b8da2eb92cef54c4c641a2c
 configure-options =
   --with-curl=${curl:location}
   --with-openssl=${openssl:location}
diff --git a/component/nodejs/buildout.cfg b/component/nodejs/buildout.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..6aae162fe3964799e97ea3b9cdd255401257de30
--- /dev/null
+++ b/component/nodejs/buildout.cfg
@@ -0,0 +1,45 @@
+[buildout]
+extends =
+  ../git/buildout.cfg
+  ../pkgconfig/buildout.cfg
+  ../openssl/buildout.cfg
+  ../python-2.7/buildout.cfg
+  ../zlib/buildout.cfg
+
+parts =
+  nodejs
+
+[nodejs]
+# Server-side Javascript.
+<= nodejs-0.4
+
+[nodejs-0.4]
+recipe = hexagonit.recipe.cmmi
+url = http://nodejs.org/dist/node-v0.4.12.tar.gz
+md5sum = a6375eaa43db5356bf443e25b828ae16
+configure-options =
+  --openssl-includes=${openssl:location}/include
+  --openssl-libpath=${openssl:location}/lib
+make-options = -j1
+environment =
+  PATH=${pkgconfig:location}/bin:${python2.7:location}/bin:%(PATH)s
+  PKG_CONFIG_PATH=${openssl:location}/lib/pkgconfig/
+  CPPFLAGS=-I${zlib:location}/include
+  LDFLAGS=-Wl,-rpath=${openssl:location}/lib -L${zlib:location}/lib -Wl,-rpath=${zlib:location}/lib
+
+[npm]
+# Node.js Package Manager
+recipe = plone.recipe.command
+location = ${buildout:parts-directory}/${:_buildout_section_name_}
+stop-on-error = true
+commit = 3136abc5c6b3ed332c4700ece24450fada63639b
+branch = upstream
+origin = https://github.com/isaacs/npm.git
+git-bin = ${git:location}/bin/git
+command = (${:git-bin} clone --quiet ${:origin} ${:location} && cd ${:location} && ${:git-bin} reset --hard ${:commit} && ${:location}/configure --prefix=${:location} &&${:git-bin} submodule update --init --recursive && ${nodejs:location}/bin/node cli.js install npm -g -f) || (rm -fr ${:location}; exit 1)
+update-command =
+
+# After installing, add the following path to your NODE_PATH enviornment
+# variable to have npm libraries picked up:
+# {HOMEBREW_PREFIX}/lib/node_modules
+
diff --git a/setup.py b/setup.py
index de2738c4b039e489b99045711c1abf4e0e7b5068..7bf0d1c01ae053c848885a723aca1bc89bcb75a3 100644
--- a/setup.py
+++ b/setup.py
@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
 import glob
 import os
 
-version = '0.36'
+version = '0.38-dev'
 name = 'slapos.cookbook'
 long_description = open("README.txt").read() + "\n" + \
     open("CHANGES.txt").read() + "\n"
diff --git a/slapos/recipe/kvm_frontend/__init__.py b/slapos/recipe/kvm_frontend/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..5e6fad4dbe331f48280cd34940c87ba8b72cccf3
--- /dev/null
+++ b/slapos/recipe/kvm_frontend/__init__.py
@@ -0,0 +1,268 @@
+##############################################################################
+#
+# Copyright (c) 2010 Vifib SARL and Contributors. All Rights Reserved.
+#
+# WARNING: This program as such is intended to be used by professional
+# programmers who take the whole responsibility of assessing all potential
+# consequences resulting from its eventual inadequacies and bugs
+# End users who are looking for a ready-to-use solution with commercial
+# guarantees and support are strongly adviced to contract a Free Software
+# Service Company
+#
+# This program is Free Software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 3
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#
+##############################################################################
+from slapos.recipe.librecipe import BaseSlapRecipe
+import os
+import pkg_resources
+import hashlib
+import sys
+import zc.buildout
+import zc.recipe.egg
+import ConfigParser
+
+
+class Recipe(BaseSlapRecipe):
+
+  def getTemplateFilename(self, template_name):
+    return pkg_resources.resource_filename(__name__,
+        'template/%s' % template_name)
+
+  def _install(self):
+    self.path_list = []
+    self.requirements, self.ws = self.egg.working_set()
+
+    # self.cron_d is a directory, where cron jobs can be registered
+    self.cron_d = self.installCrond()
+    self.logrotate_d, self.logrotate_backup = self.installLogrotate()
+    self.killpidfromfile = zc.buildout.easy_install.scripts(
+        [('killpidfromfile', 'slapos.recipe.erp5.killpidfromfile',
+          'killpidfromfile')], self.ws, sys.executable, self.bin_directory)[0]
+
+    self.path_list.append(self.killpidfromfile)
+
+    # This should come from parameter.
+    frontend_port_number = self.parameter_dict.get("port", 4443)
+    frontend_domain_name = self.parameter_dict.get("domain",
+        "host.vifib.net")
+    redirect_plain_http = self.parameter_dict.get("redirect_plain_http", False)
+
+    # Get all slaves, add them to config
+    slave_instance_list = self.parameter_dict.get('slave_instance_list', [])
+    rewrite_rule_list = []
+    slave_dict = {}
+    base_url = 'https://%s:%s/' % (frontend_domain_name, frontend_port_number)
+    for slave_instance in slave_instance_list:
+      current_slave_dict = dict()
+      current_slave_dict['host'] = slave_instance.get('host')
+      current_slave_dict['port'] = slave_instance.get('port')
+      if current_slave_dict['host'] is None \
+          or current_slave_dict['port'] is None:
+        continue
+      current_slave_dict['https'] = slave_instance.get('https', False)
+      reference = slave_instance.get('slave_reference')
+      current_slave_dict['reference'] = reference
+      slave_dict[reference] = "%s%s" % (base_url, reference.replace('-', ''))
+      rewrite_rule_list.append(current_slave_dict)
+
+    # Cert stuffs
+    valid_certificate_str = self.parameter_dict.get('domain_ssl_ca_cert')
+    valid_key_str = self.parameter_dict.get('domain_ssl_ca_key')
+    if valid_certificate_str is None and valid_key_str is None:
+      ca_conf = self.installCertificateAuthority()
+      key, certificate = self.requestCertificate(frontend_domain_name)
+    else:
+      ca_conf = self.installValidCertificateAuthority(
+          frontend_domain_name, valid_certificate_str, valid_key_str)
+      key = ca_conf.pop('key')
+      certificate = ca_conf.pop('certificate')
+
+    # Install node + js script
+    node_parameter_dict = self.installFrontendNode(
+        ip=self.getGlobalIPv6Address(),
+        port=frontend_port_number,
+        plain_http=redirect_plain_http,
+        name=frontend_domain_name,
+        rewrite_rule_list=rewrite_rule_list,
+        key=key, certificate=certificate)
+
+    for reference, url in slave_dict.iteritems():
+      self.setConnectionDict(dict(site_url=url), reference)
+
+    self.setConnectionDict(
+      dict(site_url=node_parameter_dict['site_url'],
+           domain_ipv6_address=self.getGlobalIPv6Address(),
+           domain_ipv4_address=self.getLocalIPv4Address()))
+    return self.path_list
+
+  def installLogrotate(self):
+    """Installs logortate main configuration file and registers its to cron"""
+    logrotate_d = os.path.abspath(os.path.join(self.etc_directory,
+      'logrotate.d'))
+    self._createDirectory(logrotate_d)
+    logrotate_backup = self.createBackupDirectory('logrotate')
+    logrotate_conf = self.createConfigurationFile("logrotate.conf",
+        "include %s" % logrotate_d)
+    logrotate_cron = os.path.join(self.cron_d, 'logrotate')
+    state_file = os.path.join(self.data_root_directory, 'logrotate.status')
+    open(logrotate_cron, 'w').write('0 0 * * * %s -s %s %s' %
+        (self.options['logrotate_binary'], state_file, logrotate_conf))
+    self.path_list.extend([logrotate_d, logrotate_conf, logrotate_cron])
+    return logrotate_d, logrotate_backup
+
+  def registerLogRotation(self, name, log_file_list, postrotate_script):
+    """Register new log rotation requirement"""
+    open(os.path.join(self.logrotate_d, name), 'w').write(
+        self.substituteTemplate(self.getTemplateFilename(
+          'logrotate_entry.in'),
+          dict(file_list=' '.join(['"'+q+'"' for q in log_file_list]),
+            postrotate=postrotate_script, olddir=self.logrotate_backup)))
+
+  def requestCertificate(self, name):
+    hash = hashlib.sha512(name).hexdigest()
+    key = os.path.join(self.ca_private, hash + self.ca_key_ext)
+    certificate = os.path.join(self.ca_certs, hash + self.ca_crt_ext)
+    parser = ConfigParser.RawConfigParser()
+    parser.add_section('certificate')
+    parser.set('certificate', 'name', name)
+    parser.set('certificate', 'key_file', key)
+    parser.set('certificate', 'certificate_file', certificate)
+    parser.write(open(os.path.join(self.ca_request_dir, hash), 'w'))
+    return key, certificate
+
+  def installCrond(self):
+   timestamps = self.createDataDirectory('cronstamps')
+   cron_output = os.path.join(self.log_directory, 'cron-output')
+   self._createDirectory(cron_output)
+   catcher = zc.buildout.easy_install.scripts([('catchcron',
+     __name__ + '.catdatefile', 'catdatefile')], self.ws, sys.executable,
+     self.bin_directory, arguments=[cron_output])[0]
+   self.path_list.append(catcher)
+   cron_d = os.path.join(self.etc_directory, 'cron.d')
+   crontabs = os.path.join(self.etc_directory, 'crontabs')
+   self._createDirectory(cron_d)
+   self._createDirectory(crontabs)
+   # Use execute from erp5.
+   wrapper = zc.buildout.easy_install.scripts([('crond',
+     'slapos.recipe.librecipe.execute', 'execute')], self.ws, sys.executable,
+     self.wrapper_directory, arguments=[
+       self.options['dcrond_binary'].strip(), '-s', cron_d, '-c', crontabs,
+       '-t', timestamps, '-f', '-l', '5', '-M', catcher]
+     )[0]
+   self.path_list.append(wrapper)
+   return cron_d
+
+  def installValidCertificateAuthority(self, domain_name, certificate, key):
+    ca_dir = os.path.join(self.data_root_directory, 'ca')
+    ca_private = os.path.join(ca_dir, 'private')
+    ca_certs = os.path.join(ca_dir, 'certs')
+    ca_crl = os.path.join(ca_dir, 'crl')
+    self._createDirectory(ca_dir)
+    for path in (ca_private, ca_certs, ca_crl):
+      self._createDirectory(path)
+    key_path = os.path.join(ca_private, domain_name + ".key")
+    certificate_path = os.path.join(ca_certs, domain_name + ".crt")
+    self._writeFile(key_path, key)
+    self._writeFile(certificate_path, certificate)
+    return dict(certificate_authority_path=ca_dir,
+        ca_crl=ca_crl,
+        certificate=certificate_path,
+        key=key_path)
+
+  def installCertificateAuthority(self, ca_country_code='XX',
+      ca_email='xx@example.com', ca_state='State', ca_city='City',
+      ca_company='Company'):
+    backup_path = self.createBackupDirectory('ca')
+    self.ca_dir = os.path.join(self.data_root_directory, 'ca')
+    self._createDirectory(self.ca_dir)
+    self.ca_request_dir = os.path.join(self.ca_dir, 'requests')
+    self._createDirectory(self.ca_request_dir)
+    config = dict(ca_dir=self.ca_dir, request_dir=self.ca_request_dir)
+    self.ca_private = os.path.join(self.ca_dir, 'private')
+    self.ca_certs = os.path.join(self.ca_dir, 'certs')
+    self.ca_crl = os.path.join(self.ca_dir, 'crl')
+    self.ca_newcerts = os.path.join(self.ca_dir, 'newcerts')
+    self.ca_key_ext = '.key'
+    self.ca_crt_ext = '.crt'
+    for d in [self.ca_private, self.ca_crl, self.ca_newcerts, self.ca_certs]:
+      self._createDirectory(d)
+    for f in ['crlnumber', 'serial']:
+      if not os.path.exists(os.path.join(self.ca_dir, f)):
+        open(os.path.join(self.ca_dir, f), 'w').write('01')
+    if not os.path.exists(os.path.join(self.ca_dir, 'index.txt')):
+      open(os.path.join(self.ca_dir, 'index.txt'), 'w').write('')
+    openssl_configuration = os.path.join(self.ca_dir, 'openssl.cnf')
+    config.update(
+        working_directory=self.ca_dir,
+        country_code=ca_country_code,
+        state=ca_state,
+        city=ca_city,
+        company=ca_company,
+        email_address=ca_email,
+    )
+    self._writeFile(openssl_configuration, pkg_resources.resource_string(
+      __name__, 'template/openssl.cnf.ca.in') % config)
+    self.path_list.extend(zc.buildout.easy_install.scripts([
+      ('certificate_authority', 'slapos.recipe.erp5.certificate_authority',
+         'runCertificateAuthority')],
+        self.ws, sys.executable, self.wrapper_directory, arguments=[dict(
+          openssl_configuration=openssl_configuration,
+          openssl_binary=self.options['openssl_binary'],
+          certificate=os.path.join(self.ca_dir, 'cacert.pem'),
+          key=os.path.join(self.ca_private, 'cakey.pem'),
+          crl=os.path.join(self.ca_crl),
+          request_dir=self.ca_request_dir
+          )]))
+
+    # configure backup
+    backup_cron = os.path.join(self.cron_d, 'ca_rdiff_backup')
+    open(backup_cron, 'w').write(
+        '''0 0 * * * %(rdiff_backup)s %(source)s %(destination)s'''%dict(
+          rdiff_backup=self.options['rdiff_backup_binary'],
+          source=self.ca_dir,
+          destination=backup_path))
+    self.path_list.append(backup_cron)
+
+    return dict(
+      ca_certificate=os.path.join(config['ca_dir'], 'cacert.pem'),
+      ca_crl=os.path.join(config['ca_dir'], 'crl'),
+      certificate_authority_path=config['ca_dir']
+    )
+
+  def _getProxyTableContent(self, rewrite_rule_list):
+    proxy_table_content = '{'
+    for rewrite_rule in rewrite_rule_list:
+      rewrite_part = self.substituteTemplate(
+         self.getTemplateFilename('proxytable-host.json.in'), rewrite_rule)
+      proxy_table_content = """%s
+%s""" % (proxy_table_content, rewrite_part)
+    proxy_table_content = '%s}' % proxy_table_content
+    return proxy_table_content
+
+  def installFrontendNode(self, ip, port, key, certificate, plain_http,
+                            name, rewrite_rule_list):
+    map_name = "proxy_table.json"
+    map_content = self._getProxyTableContent(rewrite_rule_list)
+    map_file = self.createConfigurationFile(map_name, map_content)
+
+    self.path_list.append(map_file)
+    wrapper = zc.buildout.easy_install.scripts([(
+        name, 'slapos.recipe.librecipe.execute', 'execute')], self.ws,
+        sys.executable, self.wrapper_directory, arguments=[
+        ip, port, key, certificate, plain_http]
+      )[0]
+    self.path_list.extend(wrapper)
+
+    return dict(site_url="https://%s:%s/" % (name, port))
diff --git a/slapos/recipe/kvm_frontend/template/logrotate_entry.in b/slapos/recipe/kvm_frontend/template/logrotate_entry.in
new file mode 100644
index 0000000000000000000000000000000000000000..bfa2abf0970af28f8cab98793db9b09db85d1847
--- /dev/null
+++ b/slapos/recipe/kvm_frontend/template/logrotate_entry.in
@@ -0,0 +1,13 @@
+%(file_list)s {
+  daily
+  dateext
+  rotate 30
+  compress
+  notifempty
+  sharedscripts
+  create
+  postrotate
+    %(postrotate)s
+  endscript
+  olddir %(olddir)s
+}
diff --git a/slapos/recipe/kvm_frontend/template/openssl.cnf.ca.in b/slapos/recipe/kvm_frontend/template/openssl.cnf.ca.in
new file mode 100644
index 0000000000000000000000000000000000000000..8a450a68762145e72923635273e06a00e80d34ca
--- /dev/null
+++ b/slapos/recipe/kvm_frontend/template/openssl.cnf.ca.in
@@ -0,0 +1,350 @@
+#
+# OpenSSL example configuration file.
+# This is mostly being used for generation of certificate requests.
+#
+
+# This definition stops the following lines choking if HOME isn't
+# defined.
+HOME			= .
+RANDFILE		= $ENV::HOME/.rnd
+
+# Extra OBJECT IDENTIFIER info:
+#oid_file		= $ENV::HOME/.oid
+oid_section		= new_oids
+
+# To use this configuration file with the "-extfile" option of the
+# "openssl x509" utility, name here the section containing the
+# X.509v3 extensions to use:
+# extensions		= 
+# (Alternatively, use a configuration file that has only
+# X.509v3 extensions in its main [= default] section.)
+
+[ new_oids ]
+
+# We can add new OIDs in here for use by 'ca', 'req' and 'ts'.
+# Add a simple OID like this:
+# testoid1=1.2.3.4
+# Or use config file substitution like this:
+# testoid2=${testoid1}.5.6
+
+# Policies used by the TSA examples.
+tsa_policy1 = 1.2.3.4.1
+tsa_policy2 = 1.2.3.4.5.6
+tsa_policy3 = 1.2.3.4.5.7
+
+####################################################################
+[ ca ]
+default_ca	= CA_default		# The default ca section
+
+####################################################################
+[ CA_default ]
+
+dir		= %(working_directory)s		# Where everything is kept
+certs		= $dir/certs		# Where the issued certs are kept
+crl_dir		= $dir/crl		# Where the issued crl are kept
+database	= $dir/index.txt	# database index file.
+#unique_subject	= no			# Set to 'no' to allow creation of
+					# several ctificates with same subject.
+new_certs_dir	= $dir/newcerts		# default place for new certs.
+
+certificate	= $dir/cacert.pem 	# The CA certificate
+serial		= $dir/serial 		# The current serial number
+crlnumber	= $dir/crlnumber	# the current crl number
+					# must be commented out to leave a V1 CRL
+crl		= $dir/crl.pem 		# The current CRL
+private_key	= $dir/private/cakey.pem # The private key
+RANDFILE	= $dir/private/.rand	# private random number file
+
+x509_extensions	= usr_cert		# The extentions to add to the cert
+
+# Comment out the following two lines for the "traditional"
+# (and highly broken) format.
+name_opt 	= ca_default		# Subject Name options
+cert_opt 	= ca_default		# Certificate field options
+
+# Extension copying option: use with caution.
+# copy_extensions = copy
+
+# Extensions to add to a CRL. Note: Netscape communicator chokes on V2 CRLs
+# so this is commented out by default to leave a V1 CRL.
+# crlnumber must also be commented out to leave a V1 CRL.
+# crl_extensions	= crl_ext
+
+default_days	= 3650			# how long to certify for
+default_crl_days= 30			# how long before next CRL
+default_md	= default		# use public key default MD
+preserve	= no			# keep passed DN ordering
+
+# A few difference way of specifying how similar the request should look
+# For type CA, the listed attributes must be the same, and the optional
+# and supplied fields are just that :-)
+policy		= policy_match
+
+# For the CA policy
+[ policy_match ]
+countryName		= match
+stateOrProvinceName	= match
+organizationName	= match
+organizationalUnitName	= optional
+commonName		= supplied
+emailAddress		= optional
+
+# For the 'anything' policy
+# At this point in time, you must list all acceptable 'object'
+# types.
+[ policy_anything ]
+countryName		= optional
+stateOrProvinceName	= optional
+localityName		= optional
+organizationName	= optional
+organizationalUnitName	= optional
+commonName		= supplied
+emailAddress		= optional
+
+####################################################################
+[ req ]
+default_bits		= 2048
+default_md		= sha1
+default_keyfile 	= privkey.pem
+distinguished_name	= req_distinguished_name
+#attributes		= req_attributes
+x509_extensions	= v3_ca	# The extentions to add to the self signed cert
+
+# Passwords for private keys if not present they will be prompted for
+# input_password = secret
+# output_password = secret
+
+# This sets a mask for permitted string types. There are several options. 
+# default: PrintableString, T61String, BMPString.
+# pkix	 : PrintableString, BMPString (PKIX recommendation before 2004)
+# utf8only: only UTF8Strings (PKIX recommendation after 2004).
+# nombstr : PrintableString, T61String (no BMPStrings or UTF8Strings).
+# MASK:XXXX a literal mask value.
+# WARNING: ancient versions of Netscape crash on BMPStrings or UTF8Strings.
+string_mask = utf8only
+
+# req_extensions = v3_req # The extensions to add to a certificate request
+
+[ req_distinguished_name ]
+countryName			= Country Name (2 letter code)
+countryName_value		= %(country_code)s
+countryName_min			= 2
+countryName_max			= 2
+
+stateOrProvinceName		= State or Province Name (full name)
+stateOrProvinceName_value	= %(state)s
+
+localityName			= Locality Name (eg, city)
+localityName_value		= %(city)s
+
+0.organizationName		= Organization Name (eg, company)
+0.organizationName_value	= %(company)s
+
+# we can do this but it is not needed normally :-)
+#1.organizationName		= Second Organization Name (eg, company)
+#1.organizationName_default	= World Wide Web Pty Ltd
+
+commonName			= Common Name (eg, your name or your server\'s hostname)
+commonName_max			= 64
+
+emailAddress			= Email Address
+emailAddress_value = %(email_address)s
+emailAddress_max		= 64
+
+# SET-ex3			= SET extension number 3
+
+#[ req_attributes ]
+#challengePassword		= A challenge password
+#challengePassword_min		= 4
+#challengePassword_max		= 20
+#
+#unstructuredName		= An optional company name
+
+[ usr_cert ]
+
+# These extensions are added when 'ca' signs a request.
+
+# This goes against PKIX guidelines but some CAs do it and some software
+# requires this to avoid interpreting an end user certificate as a CA.
+
+basicConstraints=CA:FALSE
+
+# Here are some examples of the usage of nsCertType. If it is omitted
+# the certificate can be used for anything *except* object signing.
+
+# This is OK for an SSL server.
+# nsCertType			= server
+
+# For an object signing certificate this would be used.
+# nsCertType = objsign
+
+# For normal client use this is typical
+# nsCertType = client, email
+
+# and for everything including object signing:
+# nsCertType = client, email, objsign
+
+# This is typical in keyUsage for a client certificate.
+# keyUsage = nonRepudiation, digitalSignature, keyEncipherment
+
+# This will be displayed in Netscape's comment listbox.
+nsComment			= "OpenSSL Generated Certificate"
+
+# PKIX recommendations harmless if included in all certificates.
+subjectKeyIdentifier=hash
+authorityKeyIdentifier=keyid,issuer
+
+# This stuff is for subjectAltName and issuerAltname.
+# Import the email address.
+# subjectAltName=email:copy
+# An alternative to produce certificates that aren't
+# deprecated according to PKIX.
+# subjectAltName=email:move
+
+# Copy subject details
+# issuerAltName=issuer:copy
+
+#nsCaRevocationUrl		= http://www.domain.dom/ca-crl.pem
+#nsBaseUrl
+#nsRevocationUrl
+#nsRenewalUrl
+#nsCaPolicyUrl
+#nsSslServerName
+
+# This is required for TSA certificates.
+# extendedKeyUsage = critical,timeStamping
+
+[ v3_req ]
+
+# Extensions to add to a certificate request
+
+basicConstraints = CA:FALSE
+keyUsage = nonRepudiation, digitalSignature, keyEncipherment
+
+[ v3_ca ]
+
+
+# Extensions for a typical CA
+
+
+# PKIX recommendation.
+
+subjectKeyIdentifier=hash
+
+authorityKeyIdentifier=keyid:always,issuer
+
+# This is what PKIX recommends but some broken software chokes on critical
+# extensions.
+#basicConstraints = critical,CA:true
+# So we do this instead.
+basicConstraints = CA:true
+
+# Key usage: this is typical for a CA certificate. However since it will
+# prevent it being used as an test self-signed certificate it is best
+# left out by default.
+# keyUsage = cRLSign, keyCertSign
+
+# Some might want this also
+# nsCertType = sslCA, emailCA
+
+# Include email address in subject alt name: another PKIX recommendation
+# subjectAltName=email:copy
+# Copy issuer details
+# issuerAltName=issuer:copy
+
+# DER hex encoding of an extension: beware experts only!
+# obj=DER:02:03
+# Where 'obj' is a standard or added object
+# You can even override a supported extension:
+# basicConstraints= critical, DER:30:03:01:01:FF
+
+[ crl_ext ]
+
+# CRL extensions.
+# Only issuerAltName and authorityKeyIdentifier make any sense in a CRL.
+
+# issuerAltName=issuer:copy
+authorityKeyIdentifier=keyid:always
+
+[ proxy_cert_ext ]
+# These extensions should be added when creating a proxy certificate
+
+# This goes against PKIX guidelines but some CAs do it and some software
+# requires this to avoid interpreting an end user certificate as a CA.
+
+basicConstraints=CA:FALSE
+
+# Here are some examples of the usage of nsCertType. If it is omitted
+# the certificate can be used for anything *except* object signing.
+
+# This is OK for an SSL server.
+# nsCertType			= server
+
+# For an object signing certificate this would be used.
+# nsCertType = objsign
+
+# For normal client use this is typical
+# nsCertType = client, email
+
+# and for everything including object signing:
+# nsCertType = client, email, objsign
+
+# This is typical in keyUsage for a client certificate.
+# keyUsage = nonRepudiation, digitalSignature, keyEncipherment
+
+# This will be displayed in Netscape's comment listbox.
+nsComment			= "OpenSSL Generated Certificate"
+
+# PKIX recommendations harmless if included in all certificates.
+subjectKeyIdentifier=hash
+authorityKeyIdentifier=keyid,issuer
+
+# This stuff is for subjectAltName and issuerAltname.
+# Import the email address.
+# subjectAltName=email:copy
+# An alternative to produce certificates that aren't
+# deprecated according to PKIX.
+# subjectAltName=email:move
+
+# Copy subject details
+# issuerAltName=issuer:copy
+
+#nsCaRevocationUrl		= http://www.domain.dom/ca-crl.pem
+#nsBaseUrl
+#nsRevocationUrl
+#nsRenewalUrl
+#nsCaPolicyUrl
+#nsSslServerName
+
+# This really needs to be in place for it to be a proxy certificate.
+proxyCertInfo=critical,language:id-ppl-anyLanguage,pathlen:3,policy:foo
+
+####################################################################
+[ tsa ]
+
+default_tsa = tsa_config1	# the default TSA section
+
+[ tsa_config1 ]
+
+# These are used by the TSA reply generation only.
+dir		= /etc/pki/tls		# TSA root directory
+serial		= $dir/tsaserial	# The current serial number (mandatory)
+crypto_device	= builtin		# OpenSSL engine to use for signing
+signer_cert	= $dir/tsacert.pem 	# The TSA signing certificate
+					# (optional)
+certs		= $dir/cacert.pem	# Certificate chain to include in reply
+					# (optional)
+signer_key	= $dir/private/tsakey.pem # The TSA private key (optional)
+
+default_policy	= tsa_policy1		# Policy if request did not specify it
+					# (optional)
+other_policies	= tsa_policy2, tsa_policy3	# acceptable policies (optional)
+digests		= md5, sha1		# Acceptable message digests (mandatory)
+accuracy	= secs:1, millisecs:500, microsecs:100	# (optional)
+clock_precision_digits  = 0	# number of digits after dot. (optional)
+ordering		= yes	# Is ordering defined for timestamps?
+				# (optional, default: no)
+tsa_name		= yes	# Must the TSA name be included in the reply?
+				# (optional, default: no)
+ess_cert_id_chain	= no	# Must the ESS cert id chain be included?
+				# (optional, default: no)
diff --git a/slapos/recipe/kvm_frontend/template/proxytable-host.json.in b/slapos/recipe/kvm_frontend/template/proxytable-host.json.in
new file mode 100644
index 0000000000000000000000000000000000000000..b34b1ef6c1b81fe660630a506dd8f2ded075f71b
--- /dev/null
+++ b/slapos/recipe/kvm_frontend/template/proxytable-host.json.in
@@ -0,0 +1,5 @@
+  "/%(reference)s": {
+    "port": %(port)s,
+    "host": "%(host)s",
+    "https": %(https)s
+  }
\ No newline at end of file
diff --git a/software/kvm-frontend/instance.cfg b/software/kvm-frontend/instance.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..dc78420fb10e3c3069a4ea3445244d9a98071b4b
--- /dev/null
+++ b/software/kvm-frontend/instance.cfg
@@ -0,0 +1,14 @@
+[buildout]
+parts =
+  instance
+
+eggs-directory = ${buildout:eggs-directory}
+develop-eggs-directory = ${buildout:develop-eggs-directory}
+
+[instance]
+recipe = ${instance-recipe:egg}:${instance-recipe:module}
+node_binary = ${nodejs:location}/bin/node
+npm_binary = ${npm:location}/bin/npm
+openssl_binary = ${openssl:location}/bin/openssl
+dcrond_binary = ${dcron:location}/sbin/crond
+
diff --git a/software/kvm-frontend/software.cfg b/software/kvm-frontend/software.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..6a829ec6d7e6b7bfc03b35be19fd20fd93b67e9e
--- /dev/null
+++ b/software/kvm-frontend/software.cfg
@@ -0,0 +1,39 @@
+[buildout]
+extends =
+  ../../stack/nodejs.cfg
+
+parts =
+  template
+  node
+  npm
+
+  dcron
+  logrotate
+
+# Buildoutish
+  eggs
+  instance-recipe-egg
+
+# XXX: Workaround of SlapOS limitation
+# Unzippig of eggs is required, as SlapOS do not yet provide nicely working
+# development / fast switching environment for whole software
+unzip = true
+
+[instance-recipe]
+# Note: In case if specific instantiation recipe is used this is the place to
+# put its name
+egg = slapos.cookbook
+module = kvm-frontend
+
+[instance-recipe-egg]
+recipe = zc.recipe.egg
+python = python2.7
+eggs = ${instance-recipe:egg}
+
+[template]
+# Default template for apache instance.
+recipe = slapos.recipe.template
+url = ${:_profile_base_location_}/instance.cfg
+md5sum = 7686228221c684fe772d63a5fe581c74
+output = ${buildout:directory}/template.cfg
+mode = 0644
diff --git a/stack/nodejs.cfg b/stack/nodejs.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..ed10ceb684e2cbf5602360676cfeb202a900bc6d
--- /dev/null
+++ b/stack/nodejs.cfg
@@ -0,0 +1,34 @@
+[buildout]
+extends =
+  ../component/lxml-python/buildout.cfg
+  ../component/nodejs/buildout.cfg
+  ../stack/shacache-client.cfg
+
+find-links +=
+    http://www.nexedi.org/static/packages/source/slapos.buildout/
+
+# Use only quite well working sites.
+allow-hosts =
+  *.nexedi.org
+  *.python.org
+  *.sourceforge.net
+  alastairs-place.net
+  dist.repoze.org
+  effbot.org
+  github.com
+  peak.telecommunity.com
+  psutil.googlecode.com
+  www.dabeaz.com
+
+parts =
+#  template
+  eggs
+#  instance-recipe-egg
+  nodejs
+  npm
+
+[eggs]
+recipe = zc.recipe.egg
+eggs =
+  ${lxml-python:egg}
+  slapos.cookbook