cloudtools / troposphere

troposphere - Python library to create AWS CloudFormation descriptions
BSD 2-Clause "Simplified" License
4.93k stars 1.45k forks source link

WAF Problems #489

Closed ismaelfernandezscmspain closed 8 years ago

ismaelfernandezscmspain commented 8 years ago

I had tried to build a WAF template, but when I like build template, I have the next error: TypeError: <class 'troposphere.waf.SqlInjectionMatchTuples'>: None.FieldToMatch is <class 'troposphere.waf.FieldToMatch'>, expected <class 'troposphere.waf.FieldToMatch'>

My code: from troposphere import waf from troposphere.waf import FieldToMatch from troposphere.waf import SqlInjectionMatchTuples

t = Template() t.add_description( "AWS WAF Deployment" )

    wafSqliMatchSetName = b'["sqlimatchset"]',
    wafFieldToMatch_sqli = FieldToMatch(
        Type=b'Random')
    print(isinstance(wafFieldToMatch_sqli, FieldToMatch))
    wafSqliMatchSetTuples = SqlInjectionMatchTuples(FieldToMatch=wafFieldToMatch_sqli)

------HERE BREAK WITH THE ERROR------- wafSqliMatchSet = waf.SqlInjectionMatchSet(Name=wafSqliMatchSetName, SqlInjectionMatchTuples=wafSqliMatchSetTuples)

I have printed anyvars and isInstance(): And this --> print(isinstance(wafFieldToMatch_sqli, FieldToMatch)) = True and in troposphere/init.py

Entry for: 131 # Single type so check the type of the object and compare against 132 # what we were expecting. Special case AWS helper functions. 133 elif isinstance(value, expected_type): 134 return self.properties.setitem(name, value) 135 else: 136 print (value) 137 self._raise_type(name, value, expected_type)

And this value return --> <troposphere.waf.FieldToMatch object at 0x10d5be210>

Why have I this error?

phobologic commented 8 years ago

Can you please share the full stack trace of the exception that is raised - that'll make it easier to track down where the code is breaking.

phobologic commented 8 years ago

This might be the problem - it looks like waf.SqlInjectionMatchSet expects a list of SqlInjectionMatchTuples as the SqlInjectionMatchTuples argument, but you've only provided the object itself (not a list of them).

https://github.com/cloudtools/troposphere/blob/master/troposphere/waf.py#L96

ismaelfernandezscmspain commented 8 years ago

of course:

Traceback (most recent call last):
  File "./deploy_waf.py", line 25, in <module>
    waf.create_template_waf()
  File "/Users/ismaelfernandez/vibbo_cloudfront/functions/waf.py", line 49, in create_template_waf
    wafSqliMatchSetTuples = SqlInjectionMatchTuples(FieldToMatch=wafFieldToMatch_sqli)
  File "/usr/local/lib/python2.7/site-packages/troposphere/__init__.py", line 215, in __init__
    super(AWSProperty, self).__init__(title, **kwargs)
  File "/usr/local/lib/python2.7/site-packages/troposphere/__init__.py", line 67, in __init__
    self.__setattr__(k, v)
  File "/usr/local/lib/python2.7/site-packages/troposphere/__init__.py", line 138, in __setattr__
    self._raise_type(name, value, expected_type)
  File "/usr/local/lib/python2.7/site-packages/troposphere/__init__.py", line 156, in _raise_type
    expected_type))
TypeError: <class 'troposphere.waf.SqlInjectionMatchTuples'>: None.FieldToMatch is <class 'troposphere.waf.FieldToMatch'>, expected <class 'troposphere.waf.FieldToMatch'>
ismaelfernandezscmspain commented 8 years ago

about this:

This might be the problem - it looks like waf.SqlInjectionMatchSet expects a list of SqlInjectionMatchTuples as the SqlInjectionMatchTuples argument, but you've only provided the object itself (not a list of them).

https://github.com/cloudtools/troposphere/blob/master/troposphere/waf.py#L96

My Run not call to waf.SqlInjectionMatchSet, because the crash is whe called to SqlInjectionMatchTuples.

phobologic commented 8 years ago

Yeah - it was hard to tell where things were breaking exactly without the stack trace. That said, the issue I pointed out will still probably give you trouble. I'm not sure why your template is breaking - is there anyway you can provide a simplified version of your code so that I can try and dig into it? That error is very strange, so this seems like there might be a deeper underlying bug.

If you can come up with a simple version of your code, please share it in a gist, and I'll take a closer look. Also, if you could provide the version of troposphere that you are using, that'd be great. Thanks!

ismaelfernandezscmspain commented 8 years ago

Version Troposphere: troposphere (1.6.0): All my code:

#!/usr/bin/env python
import argparse
import configparser
import sys
import os
import boto.cloudformation
import yaml
from random import randint
from troposphere import GetAtt, Join, Output
from troposphere import Parameter, Ref, Template
from troposphere import route53
from troposphere import waf
from troposphere.waf import FieldToMatch
from troposphere.waf import SqlInjectionMatchTuples

class Waf(object):

    def __init__(self, accountName, environment):
        self.account_name = accountName
        self.environment = environment
        self.label=self.account_name+'-'+self.environment
        self.stackName=self.environment+"-waf"

    def get_connection_cloudformation(self):
        if self.environment is not None:
            config = configparser.ConfigParser()
            try:
              config.read(os.path.expanduser('~')+'/.aws/credentials')
            except Exception as error:
                  print "config file doesn't exist"
                  print error
                  exit(1)
        aws_access_key = config[self.label]['aws_access_key_id']
        aws_secret_key = config[self.label]['aws_secret_access_key']
        region = config[self.label]['region']

        conn=boto.cloudformation.connect_to_region(region, aws_access_key_id=aws_access_key, aws_secret_access_key=aws_secret_key)
        return conn

    def create_template_waf(self):
        t = Template()
        t.add_description(
            "AWS WAF Deployment"
            )

        wafSqliMatchSetName = b'["sqlimatchset"]',
        wafFieldToMatch_sqli = FieldToMatch(Type=b'Ranodm')
        wafSqliMatchSetTuples = SqlInjectionMatchTuples(FieldToMatch=wafFieldToMatch_sqli)

        wafSqliMatchSet = waf.SqlInjectionMatchSet(Name=wafSqliMatchSetName,
                                                   SqlInjectionMatchTuples=wafSqliMatchSetTuples)

        wafRuleName_sqli = [b'sqli']
        wafRuleMetricName_sqli = [b'sqlinjectioncount']
        wafRule_sqli = waf.Rule(
            Name = wafRuleName_sqli,
            MetricName = wafRuleMetricName_sqli,
            )

        wafDefaultAcl_sqli = "Allow"
        wafMetricName_sqli = "SqlInjWebACL"
        wafName_sqli = "Web ACL to block SQL injection in the query string"
        Waf = waf.WebACL(
            DefaultAction = wafDefaultAcl_sqli,
            MetricName = wafMetricName_sqli,
            Name = wafName_sqli,
            Rules = [ wafRule_sqli ],
            )
        t.add_resource(Waf)
        template = t.to_json()

        # 1.    Generate the SQL Injection Condition
        # 2.    Add a Condition Match to the Condition to analyze the Query String in the URL.
        # 3.    Create the Rule
        # 4.    Add the Condition to the Rule
        # 5.    Create the Web ACL
        # 6.    Add the Rule to the Web ACL

        print template
        conn = self.get_connection_cloudformation()
        exist = self.exist_cloud_formation(conn)
        print exist
        if exist:
            try:
                result = conn.update_stack(self.stackName, template_body=t.to_json(), capabilities=['CAPABILITY_IAM'])
            except boto.exception.BotoServerError as e:
                if e.message != "No updates are to be performed.":
                    print("[ERROR] " + e.message)
                    exit(1)
                else:
                    print(e.message)
                    exit(0)

        else:
                result = conn.create_stack(self.stackName, template_body=t.to_json(), capabilities=['CAPABILITY_IAM'])
        print result

    def exist_cloud_formation(self, cf):
        stack_summaries = cf.describe_stacks()
        if len(stack_summaries) > 0:
            for StackSummary in stack_summaries:
                if StackSummary.stack_name == self.stackName:
                    exist = True
                    break
                else:
                    exist = False

        return exist

And I called this class from main.py, like:

waf = Waf(account_name, environment)
waf.create_template_waf()

I think thats is all.

phobologic commented 8 years ago

Ok, something very strange is going on here. I've been able to reproduce, though for the life of me I can't figure out where the problem lies. I have to do some other stuff, but I'll dive into this more later. Thanks for reporting!

markpeek commented 8 years ago

There was a duplicate class FieldToMatch which was causing the conflict. Pushed a fix just now.

phobologic commented 8 years ago

Nice find!

ismaelfernandezscmspain commented 8 years ago

thanks a lot!