Validate SSL certs accessed through urllib*

* Adds another module utility file which generalizes the
  access of urls via the urllib* libraries.
* Adds a new spec generator for common arguments.
* Makes the user-agent string configurable.

Fixes #6211
This commit is contained in:
James Cammarata
2014-03-10 16:06:52 -05:00
parent 6577ff5f85
commit 9730157525
23 changed files with 598 additions and 402 deletions

View File

@@ -52,6 +52,13 @@ options:
- Optional URL to submit the notification to. Use to send notifications to Airbrake-compliant tools like Errbit.
required: false
default: https://airbrake.io/deploys
validate_certs:
description:
- If C(no), SSL certificates for the target url will not be validated. This should only be used
on personally controlled sites using self-signed certificates.
required: false
default: 'yes'
choices: ['yes', 'no']
# informational: requirements for nodes
requirements: [ urllib, urllib2 ]
@@ -64,29 +71,12 @@ EXAMPLES = '''
revision=4.2
'''
HAS_URLLIB = True
try:
import urllib
except ImportError:
HAS_URLLIB = False
HAS_URLLIB2 = True
try:
import urllib2
except ImportError:
HAS_URLLIB2 = False
# ===========================================
# Module execution.
#
def main():
if not HAS_URLLIB:
module.fail_json(msg="urllib is not installed")
if not HAS_URLLIB2:
module.fail_json(msg="urllib2 is not installed")
module = AnsibleModule(
argument_spec=dict(
token=dict(required=True),
@@ -95,6 +85,7 @@ def main():
repo=dict(required=False),
revision=dict(required=False),
url=dict(required=False, default='https://api.airbrake.io/deploys.txt')
validate_certs=dict(default='yes', type='bool'),
),
supports_check_mode=True
)
@@ -123,18 +114,16 @@ def main():
module.exit_json(changed=True)
# Send the data to airbrake
try:
req = urllib2.Request(url, urllib.urlencode(params))
result=urllib2.urlopen(req)
except Exception, e:
module.fail_json(msg="unable to update airbrake via %s?%s : %s" % (url, urllib.urlencode(params), e))
data = urllib.urlencode(params)
response, info = fetch_url(module, url, data=data, validate_certs=module.params['validate_certs'])
if info['status'] == 200:
module.exit_json(changed=True)
else:
if result.code == 200:
module.exit_json(changed=True)
else:
module.fail_json(msg="HTTP result code: %d connecting to %s" % (result.code, url))
module.fail_json(msg="HTTP result code: %d connecting to %s" % (info['status'], url))
# import module snippets
from ansible.module_utils.basic import *
from ansible.module_utils.urls import *
main()

View File

@@ -24,7 +24,6 @@ along with Ansible. If not, see <http://www.gnu.org/licenses/>.
import json
import datetime
import urllib2
import base64
import os
@@ -74,12 +73,6 @@ EXAMPLES='''
'''
try:
import urllib2
HAS_URLLIB2 = True
except ImportError:
HAS_URLLIB2 = False
api_host = "api.boundary.com"
config_directory = "/etc/bprobe"
@@ -101,7 +94,7 @@ def build_url(name, apiid, action, meter_id=None, cert_type=None):
elif action == "delete":
return "https://%s/%s/meters/%s" % (api_host, apiid, meter_id)
def http_request(name, apiid, apikey, action, meter_id=None, cert_type=None):
def http_request(module, name, apiid, apikey, action, data=None, meter_id=None, cert_type=None):
if meter_id is None:
url = build_url(name, apiid, action)
@@ -111,11 +104,11 @@ def http_request(name, apiid, apikey, action, meter_id=None, cert_type=None):
else:
url = build_url(name, apiid, action, meter_id, cert_type)
auth = auth_encode(apikey)
request = urllib2.Request(url)
request.add_header("Authorization", "Basic %s" % (auth))
request.add_header("Content-Type", "application/json")
return request
headers = dict()
headers["Authorization"] = "Basic %s" % auth_encode(apikey)
headers["Content-Type"] = "application/json"
return fetch_url(module, url, data=data, headers=headers)
def create_meter(module, name, apiid, apikey):
@@ -126,14 +119,10 @@ def create_meter(module, name, apiid, apikey):
module.exit_json(status="Meter " + name + " already exists",changed=False)
else:
# If it doesn't exist, create it
request = http_request(name, apiid, apikey, action="create")
# A create request seems to need a json body with the name of the meter in it
body = '{"name":"' + name + '"}'
request.add_data(body)
response, info = http_request(module, name, apiid, apikey, data=body, action="create")
try:
result = urllib2.urlopen(request)
except urllib2.URLError, e:
if info['status'] != 200:
module.fail_json(msg="Failed to connect to api host to create meter")
# If the config directory doesn't exist, create it
@@ -160,15 +149,13 @@ def create_meter(module, name, apiid, apikey):
def search_meter(module, name, apiid, apikey):
request = http_request(name, apiid, apikey, action="search")
response, info = http_request(module, name, apiid, apikey, action="search")
try:
result = urllib2.urlopen(request)
except urllib2.URLError, e:
if info['status'] != 200:
module.fail_json("Failed to connect to api host to search for meter")
# Return meters
return json.loads(result.read())
return json.loads(response.read())
def get_meter_id(module, name, apiid, apikey):
# In order to delete the meter we need its id
@@ -186,16 +173,9 @@ def delete_meter(module, name, apiid, apikey):
if meter_id is None:
return 1, "Meter does not exist, so can't delete it"
else:
action = "delete"
request = http_request(name, apiid, apikey, action, meter_id)
# See http://stackoverflow.com/questions/4511598/how-to-make-http-delete-method-using-urllib2
# urllib2 only does GET or POST I believe, but here we need delete
request.get_method = lambda: 'DELETE'
try:
result = urllib2.urlopen(request)
except urllib2.URLError, e:
module.fail_json("Failed to connect to api host to delete meter")
response, info = http_request(module, name, apiid, apikey, action, meter_id)
if info['status'] != 200:
module.fail_json("Failed to delete meter")
# Each new meter gets a new key.pem and ca.pem file, so they should be deleted
types = ['cert', 'key']
@@ -214,17 +194,14 @@ def download_request(module, name, apiid, apikey, cert_type):
if meter_id is not None:
action = "certificates"
request = http_request(name, apiid, apikey, action, meter_id, cert_type)
try:
result = urllib2.urlopen(request)
except urllib2.URLError, e:
response, info = http_request(module, name, apiid, apikey, action, meter_id, cert_type)
if info['status'] != 200:
module.fail_json("Failed to connect to api host to download certificate")
if result:
try:
cert_file_path = '%s/%s.pem' % (config_directory,cert_type)
body = result.read()
body = response.read()
cert_file = open(cert_file_path, 'w')
cert_file.write(body)
cert_file.close
@@ -238,9 +215,6 @@ def download_request(module, name, apiid, apikey, cert_type):
def main():
if not HAS_URLLIB2:
module.fail_json(msg="urllib2 is not installed")
module = AnsibleModule(
argument_spec=dict(
state=dict(required=True, choices=['present', 'absent']),
@@ -268,5 +242,6 @@ def main():
# import module snippets
from ansible.module_utils.basic import *
from ansible.module_utils.urls import *
main()

View File

@@ -67,7 +67,6 @@ datadog_event: title="Testing from ansible" text="Test!"
'''
import socket
from urllib2 import urlopen, Request, URLError
def main():
module = AnsibleModule(
@@ -97,8 +96,7 @@ def main():
post_event(module)
def post_event(module):
uri = "https://app.datadoghq.com/api/v1/events?api_key=" + \
module.params['api_key']
uri = "https://app.datadoghq.com/api/v1/events?api_key=%s" % module.params['api_key']
body = dict(
title=module.params['title'],
@@ -117,22 +115,20 @@ def post_event(module):
json_body = module.jsonify(body)
headers = {"Content-Type": "application/json"}
request = Request(uri, json_body, headers, unverifiable=True)
try:
response = urlopen(request)
(response, info) = fetch_url(module, uri, data=json_body, headers=headers)
if info['status'] == 200:
response_body = response.read()
response_json = module.from_json(response_body)
if response_json['status'] == 'ok':
module.exit_json(changed=True)
else:
module.fail_json(msg=response)
except URLError, e:
module.fail_json(msg="URL error: %s." % e)
except socket.error, e:
module.fail_json(msg="Socket error: %s to %s" % (e, uri))
else:
module.fail_json(**info)
# import module snippets
from ansible.module_utils.basic import *
from ansible.module_utils.urls import *
main()

View File

@@ -75,29 +75,12 @@ EXAMPLES = '''
revision=1.0
'''
HAS_URLLIB = True
try:
import urllib
except ImportError:
HAS_URLLIB = False
HAS_URLLIB2 = True
try:
import urllib2
except ImportError:
HAS_URLLIB2 = False
# ===========================================
# Module execution.
#
def main():
if not HAS_URLLIB:
module.fail_json(msg="urllib is not installed")
if not HAS_URLLIB2:
module.fail_json(msg="urllib2 is not installed")
module = AnsibleModule(
argument_spec=dict(
token=dict(required=True),
@@ -134,29 +117,20 @@ def main():
module.exit_json(changed=True)
# Send the data to NewRelic
try:
req = urllib2.Request("https://rpm.newrelic.com/deployments.xml", urllib.urlencode(params))
req.add_header('x-api-key',module.params["token"])
result=urllib2.urlopen(req)
# urlopen behaves differently in python 2.4 and 2.6 so we handle
# both cases here. In python 2.4 it throws an exception if the
# return code is anything other than a 200. In python 2.6 it
# doesn't throw an exception for any 2xx return codes. In both
# cases we expect newrelic should return a 201 on success. So
# to handle both cases, both the except & else cases below are
# effectively identical.
except Exception, e:
if e.code == 201:
module.exit_json(changed=True)
else:
module.fail_json(msg="unable to update newrelic: %s" % e)
url = "https://rpm.newrelic.com/deployments.xml"
data = urllib.urlencode(params)
headers = {
'x-api-key': module.params["token"],
}
response, info = fetch_url(module, url, data=data, headers=headers)
if info['status'] in (200, 201):
module.exit_json(changed=True)
else:
if result.code == 201:
module.exit_json(changed=True)
else:
module.fail_json(msg="result code: %d" % result.code)
module.fail_json(msg="unable to update newrelic: %s" % info['msg'])
# import module snippets
from ansible.module_utils.basic import *
from ansible.module_utils.urls import *
main()

View File

@@ -87,24 +87,23 @@ EXAMPLES='''
import json
import datetime
import urllib2
import base64
def ongoing(name, user, passwd):
def ongoing(module, name, user, passwd):
url = "https://" + name + ".pagerduty.com/api/v1/maintenance_windows/ongoing"
auth = base64.encodestring('%s:%s' % (user, passwd)).replace('\n', '')
headers = {"Authorization": "Basic %s" % auth}
req = urllib2.Request(url)
req.add_header("Authorization", "Basic %s" % auth)
res = urllib2.urlopen(req)
out = res.read()
response, info = fetch_url(module, url, headers=headers)
if info['status'] != 200:
module.fail_json(msg="failed to lookup the ongoing window: %s" % info['msg'])
return False, out
return False, response.read()
def create(name, user, passwd, service, hours, desc):
def create(module, name, user, passwd, service, hours, desc):
now = datetime.datetime.utcnow()
later = now + datetime.timedelta(hours=int(hours))
@@ -113,15 +112,17 @@ def create(name, user, passwd, service, hours, desc):
url = "https://" + name + ".pagerduty.com/api/v1/maintenance_windows"
auth = base64.encodestring('%s:%s' % (user, passwd)).replace('\n', '')
headers = {
'Authorization': 'Basic %s' % auth,
'Content-Type' : 'application/json',
}
data = json.dumps({'maintenance_window': {'start_time': start, 'end_time': end, 'description': desc, 'service_ids': [service]}})
req = urllib2.Request(url, data)
req.add_header("Authorization", "Basic %s" % auth)
req.add_header('Content-Type', 'application/json')
res = urllib2.urlopen(req)
out = res.read()
response, info = fetch_url(module, url, data=data, headers=headers, method='POST')
if info['status'] != 200:
module.fail_json(msg="failed to create the window: %s" % info['msg'])
return False, out
return False, response.read()
def main():
@@ -149,10 +150,10 @@ def main():
if state == "running" or state == "started":
if not service:
module.fail_json(msg="service not specified")
(rc, out) = create(name, user, passwd, service, hours, desc)
(rc, out) = create(module, name, user, passwd, service, hours, desc)
if state == "ongoing":
(rc, out) = ongoing(name, user, passwd)
(rc, out) = ongoing(module, name, user, passwd)
if rc != 0:
module.fail_json(msg="failed", result=out)
@@ -161,4 +162,6 @@ def main():
# import module snippets
from ansible.module_utils.basic import *
from ansible.module_utils.urls import *
main()