Limit maximum size of all templates

Templates of an extremely large size can eat up tons of CPU time,
memory and storage. By refusing to parse any template over a certain
size, we can prevent users from abusing the service.

Fixes bug #1214234

Change-Id: I2f731c8e2fc9e1f497199e310de81fa48c9582af
This commit is contained in:
Clint Byrum 2013-08-21 13:24:17 -07:00
parent 1623b03517
commit 9fe8cbacd2
5 changed files with 31 additions and 1 deletions

View File

@ -70,6 +70,9 @@
# Keystone role for heat template-defined users (string value) # Keystone role for heat template-defined users (string value)
#heat_stack_user_role=heat_stack_user #heat_stack_user_role=heat_stack_user
# Maximum raw byte size of any template. (integer value)
#max_template_size=524288
# #
# Options defined in heat.common.crypt # Options defined in heat.common.crypt

View File

@ -81,7 +81,10 @@ service_opts = [
help='Instance connection to cfn/cw API validate certs if ssl'), help='Instance connection to cfn/cw API validate certs if ssl'),
cfg.StrOpt('heat_stack_user_role', cfg.StrOpt('heat_stack_user_role',
default="heat_stack_user", default="heat_stack_user",
help='Keystone role for heat template-defined users')] help='Keystone role for heat template-defined users'),
cfg.IntOpt('max_template_size',
default=524288,
help='Maximum raw byte size of any template.')]
db_opts = [ db_opts = [
cfg.StrOpt('sql_connection', cfg.StrOpt('sql_connection',

View File

@ -297,3 +297,7 @@ class HTTPExceptionDisguise(Exception):
def __init__(self, exception): def __init__(self, exception):
self.exc = exception self.exc = exception
self.tb = sys.exc_info()[2] self.tb = sys.exc_info()[2]
class TemplateTooBig(OpenstackException):
message = _('Template exceeds maximum allowed size.')

View File

@ -17,6 +17,12 @@ import re
import yaml import yaml
import json import json
from oslo.config import cfg
from heat.common import exception
cfg.CONF.import_opt('max_template_size', 'heat.common.config')
HEAT_VERSIONS = (u'2012-12-12',) HEAT_VERSIONS = (u'2012-12-12',)
CFN_VERSIONS = (u'2010-09-09',) CFN_VERSIONS = (u'2010-09-09',)
@ -43,6 +49,8 @@ def parse(tmpl_str, add_template_sections=True):
This includes determination of whether the string is using the This includes determination of whether the string is using the
JSON or YAML format. JSON or YAML format.
''' '''
if len(tmpl_str) > cfg.CONF.max_template_size:
raise exception.TemplateTooBig()
if tmpl_str.startswith('{'): if tmpl_str.startswith('{'):
tpl = json.loads(tmpl_str) tpl = json.loads(tmpl_str)
else: else:

View File

@ -14,8 +14,11 @@
from testtools import skipIf from testtools import skipIf
import os import os
import yaml
from heat.engine import clients from heat.engine import clients
from heat.common import config
from heat.common import exception
from heat.common import template_format from heat.common import template_format
from heat.tests.common import HeatTestCase from heat.tests.common import HeatTestCase
from heat.tests import utils from heat.tests import utils
@ -89,6 +92,15 @@ Outputs: {}
tpl2 = template_format.parse(yaml2) tpl2 = template_format.parse(yaml2)
self.assertEqual(tpl1, tpl2) self.assertEqual(tpl1, tpl2)
def test_long_yaml(self):
template = {'HeatTemplateVersion': '2012-12-12'}
template['Resources'] = ['a'] * (config.cfg.CONF.max_template_size / 3)
limit = config.cfg.CONF.max_template_size
long_yaml = yaml.safe_dump(template)
self.assertTrue(len(long_yaml) > limit)
self.assertRaises(exception.TemplateTooBig, template_format.parse,
long_yaml)
class YamlEnvironmentTest(HeatTestCase): class YamlEnvironmentTest(HeatTestCase):