[openstack-dev] [Heat] [Keystone] Heat cfn-push-stats failed with '403 SignatureDoesNotMatch', it may be Keystone problem.

Adam Young ayoung at redhat.com
Fri Apr 24 14:17:57 UTC 2015


On 08/24/2014 01:55 AM, Yukinori Sagara wrote:

Can you please submit this patch to Gerrit.  Taking it off the mailing 
list without a signed contributors agreement is problematic.


> Hi.
>
>
> I am trying Heat instance HA, using RDO Icehouse.
>
> After instance boot, instance push own stats to heat alarm with 
> cfn-push-stats command.
>
> But cfn-push-stats always failed with error '403 
> SignatureDoesNotMatch', this message is
>
> output to /var/log/cfn-push-stats.log.
>
>
> I debugged client and server side code. (i.e. cfn-push-stats, boto, 
> heat, keystone,
>
> keystoneclient) And I found curious code mismatch between boto and 
> keystoneclient about
>
> signature calculation.
>
>
> Here is a result of debugging, and code examination.
>
>
> * Client side
>
> cfn-push-stats uses heat-cfntools library, and heat-cfntools do 'POST' 
> request with boto.
>
> boto perfomes signature calculation. [1]
>
> for signature calculation, firstly it construct 'CanonicalRequest', 
> some strings are joined.
>
> And create a digest hash of the CanonicalRequest for signature 
> calculation.
>
> CanonicalRequest contains CanonicalQueryString, which is transfomed 
> URL query strings.
>
>
> CanonicalRequest =
>
>   HTTPRequestMethod + '\n' +
>
>   CanonicalURI + '\n' +
>
>   CanonicalQueryString + '\n' +
>
>   CanonicalHeaders + '\n' +
>
>   SignedHeaders + '\n' +
>
>   HexEncode(Hash(RequestPayload))
>
>
> **AWS original tool (aws-cfn-bootstrap-1.4) and boto uses empty string as
>
> CanonicalQueryString, when request is POST.**
>
>
> AWS original tool's code is following.
>
>
> cfnbootstrap/aws_client.py
>
> 110 class V4Signer(Signer):
>
>
> 144 (canonical_headers, signed_headers) = 
> self._canonicalize_headers(new_headers)
>
> 145         canonical_request += canonical_headers + '\n' + 
> signed_headers + '\n'
>
> 146         canonical_request += 
> hashlib.sha256(self._construct_query(params).encode('utf-8') if verb 
> == 'POST' else '').hexdigest()
>
>
> boto's code is following.
>
>
> boto/auth.py
>
> 283 class HmacAuthV4Handler(AuthHandler, HmacKeys):
>
>
> 393     def canonical_request(self, http_request):
>
> 394         cr = [http_request.method.upper()]
>
> 395 cr.append(self.canonical_uri(http_request))
>
> 396 cr.append(self.canonical_query_string(http_request))
>
> 397         headers_to_sign = self.headers_to_sign(http_request)
>
> 398 cr.append(self.canonical_headers(headers_to_sign) + '\n')
>
> 399 cr.append(self.signed_headers(headers_to_sign))
>
> 400         cr.append(self.payload(http_request))
>
> 401         return '\n'.join(cr)
>
>
> 337     def canonical_query_string(self, http_request):
>
> 338         # POST requests pass parameters in through the
>
> 339         # http_request.body field.
>
> 340         if http_request.method == 'POST':
>
> 341             return ""
>
> 342         l = []
>
> 343         for param in sorted(http_request.params):
>
> 344             value = 
> boto.utils.get_utf8_value(http_request.params[param])
>
> 345             l.append('%s=%s' % (urllib.quote(param, safe='-_.~'),
>
> 346 urllib.quote(value, safe='-_.~')))
>
> 347         return '&'.join(l)
>
>
> * Server side
>
> heat-api-cfn queries to keystone in order to check request authorization.
>
> keystone uses keystoneclient to check EC2 format request signature.
>
>
> In here, **keystoneclient uses (non-empty) query string as 
> CanonicalQueryString, even
>
> though request is POST.**
>
> And create a digest hash of the CanonicalRequest for signature 
> calculation.
>
>
> keystoneclient's code is following.
>
>
> keystoneclient/contrib/ec2/utils.py
>
>  28 class Ec2Signer(object):
>
>
> 154     def _calc_signature_4(self, params, verb, server_string, path, 
> headers,
>
> 155                           body_hash):
>
> 156         """Generate AWS signature version 4 string."""
>
>
> 235         # Create canonical request:
>
> 236         # http://docs.aws.amazon.com/general/latest/gr/
>
> 237         # sigv4-create-canonical-request.html
>
> 238         # Get parameters and headers in expected string format
>
> 239         cr = "\n".join((verb.upper(), path,
>
> 240                         self._canonical_qs(params),
>
> 241                         canonical_header_str(),
>
> 242 auth_param('SignedHeaders'),
>
> 243                         body_hash))
>
>
> 125     @staticmethod
>
> 126     def _canonical_qs(params):
>
> 127         """Construct a sorted, correctly encoded query string as 
> required for
>
> 128         _calc_signature_2 and _calc_signature_4.
>
> 129         """
>
> 130         keys = list(params)
>
> 131         keys.sort()
>
> 132         pairs = []
>
> 133         for key in keys:
>
> 134             val = Ec2Signer._get_utf8_value(params[key])
>
> 135             val = urllib.parse.quote(val, safe='-_~')
>
> 136             pairs.append(urllib.parse.quote(key, safe='') + '=' + val)
>
> 137         qs = '&'.join(pairs)
>
> 138         return qs
>
>
> So it should be different from boto(client side) to 
> keystoneclient(server side),
>
> cfn-push-stats always fails with error log '403 SignatureDoesNotMatch' 
> in such reason.
>
>
> I wrote a patch to resolve how to treat CanonicalQueryString mismatch,
>
> My patch honored AWS original tool and boto, so if request is POST,
>
> 'CanonicalQueryString' is regarded as a empty string.
>
>
> With my patch, Heat instance HA works fine.
>
>
> This bug affects Heat and Keystone, but patch is only needed in 
> python-keystoneclient.
>
> So I will report to python-keystoneclient launchpad and submit a patch 
> to Gerrit.
>
> Please confirm it.
>
> ----
>
> My environment is RDO Icehouse/CentOS6.5, and package versions is 
> following.
>
>
> * Client side
>
> cloud-init-0.7.4-2.el6.noarch
>
> heat-cfntools-1.2.6-2.el6.noarch
>
> python-boto-2.27.0-1.el6.noarch
>
>
> * Server side
>
> python-keystoneclient-0.9.0-1.el6.noarch
>
> python-keystone-2014.1.1-1.el6.noarch
>
> openstack-keystone-2014.1.1-1.el6.noarch
>
> ----
>
> References
>
> [1] 
> http://docs.aws.amazon.com/general/latest/gr/sigv4-create-canonical-request.html
>
>
> Thanks,
>
> Yukinori Sagara
>
>
>
>
> _______________________________________________
> OpenStack-dev mailing list
> OpenStack-dev at lists.openstack.org
> http://lists.openstack.org/cgi-bin/mailman/listinfo/openstack-dev

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.openstack.org/pipermail/openstack-dev/attachments/20150424/d10afe17/attachment.html>


More information about the OpenStack-dev mailing list