Roll Your Own API Management Platform with nginx and Lua

Software

jon-moore
of 67
Description
Text
  1. 1. Roll Your Own API Management Platform with nginx and Lua Jon Moore Senior Fellow, Comcast Cable @jon_moore
  2. 2. access control
  3. 3. capacity management (“rate limiting”)
  4. 4. capacity management (“rate limiting”)
  5. 5. HTTP Proxy custom logic!
  6. 6. Lua
  7. 7. --- Validates the OAuth signature! -- @return Const.HTTP_UNAUTHORIZED if either the key or signature is invalid! -- this method is internal and should not be called directly! function _M.validate_signature(self)! local headers = self.req.get_oauth_params()! local key = headers[Const.OAUTH_CONSUMER_KEY]! local keyconf = self.conf.keys[key]! if keyconf == nil then! return {! code = Const.HTTP_UNAUTHORIZED! error = Const.ERROR_INVALID_CONSUMER_KEY! }! end! ! local sig = get_hmac_signature(self.req, keyconf.secret)! if sig ~= headers[Const.OAUTH_SIGNATURE] then! return {! code = Const.HTTP_UNAUTHORIZED,! error = Const.ERROR_INVALID_SIGNATURE! }! end! end!
  8. 8. Lua < 3k LOC
  9. 9. testing
  10. 10. function TestOAuth1:test_reject_request_when_signature_invalid()! local header = Header:new()! header[Const.OAUTH_SIGNATURE] = “invalid”! local req = Req:new({oauth_params = header })! local conf = Conf:new()! local oauth = OAuth1:new(conf, req)! ! local res = oauth:authorize()! assertEquals(res.code, Const.HTTP_UNAUTHORIZED)! assertEquals(res.error, Const.ERROR_INVALID_SIGNATURE)! end! ! lu = LuaUnit.new()! Lu:setOutputType(“tap”)! os.exit(lu:runSuite())!
  11. 11. ngx.log(ngx.ERR, “oops”)!
  12. 12. ngx.log(ngx.ERR, “oops”)!
  13. 13. function get_oauth_params_from_auth_header()!
  14. 14. function get_oauth_params_from_auth_header(env)! env = env or ngx! local auth_hdrs = env.req.get_headers()[“Authorization”]! ...!
  15. 15. function get_oauth_params_from_auth_header(env)! env = env or ngx! local auth_hdrs = env.req.get_headers()[“Authorization”]! ...!
  16. 16. function get_oauth_params_from_auth_header(env)! env = env or ngx! local auth_hdrs = env.req.get_headers()[“Authorization”]! ...!
  17. 17. function TestRequest:test_retrieve_oauth_params_from_header()! local header = [[Oauth realm=“example.com”, ]]! .. [[oauth_consumer_key=“mykey”,]]! .. [[oauth_version=“1.0”]]! local ngx = StubNgx:new({ Authorization = header })! local res = get_oauth_params_from_auth_header(ngx)! assertEquals(“1.0”, res.oauth_version)! ...!
  18. 18. function TestRequest:test_retrieve_oauth_params_from_header()! local header = [[Oauth realm=“example.com”, ]]! .. [[oauth_consumer_key=“mykey”,]]! .. [[oauth_version=“1.0”]]! local ngx = StubNgx:new({ Authorization = header })! local res = get_oauth_params_from_auth_header(ngx)! assertEquals(“1.0”, res.oauth_version)! ...!
  19. 19. function TestRequest:test_retrieve_oauth_params_from_header()! local header = [[Oauth realm=“example.com”, ]]! .. [[oauth_consumer_key=“mykey”,]]! .. [[oauth_version=“1.0”]]! local ngx = StubNgx:new({ Authorization = header })! local res = get_oauth_params_from_auth_header(ngx)! assertEquals(“1.0”, res.oauth_version)! ...!
  20. 20. function TestRequest:test_retrieve_oauth_params_from_header()! local header = [[Oauth realm=“example.com”, ]]! .. [[oauth_consumer_key=“mykey”,]]! .. [[oauth_version=“1.0”]]! local ngx = StubNgx:new({ Authorization = header })! local res = get_oauth_params_from_auth_header(ngx)! assertEquals(“1.0”, res.oauth_version)! ...!
  21. 21. function TestRequest:test_retrieve_oauth_params_from_header()! local header = [[Oauth realm=“example.com”, ]]! .. [[oauth_consumer_key=“mykey”,]]! .. [[oauth_version=“1.0”]]! local ngx = StubNgx:new({ Authorization = header })! local res = get_oauth_params_from_auth_header(ngx)! assertEquals(“1.0”, res.oauth_version)! ...!
  22. 22. test harness
  23. 23. nginx-oauth1.conf! test harness
  24. 24. nginx-oauth1.conf! test harness
  25. 25. nginx-oauth1.conf! test harness
  26. 26. nginx-oauth1.conf! test harness
  27. 27. def spec_using_valid_oauth_credentials(self, harness)! auth = OAuth1(“mykey”, “mysecret”)! body_data = “{‘function’: ‘tick’}”! harness.reset_data()! response = requests.post(root_url, data=body_data, auth=auth,! headers={‘Content-Type’:! ‘application/json’})! assert response.status_code == 200! assert “Authorization” in harness.forwarded_headers! assert “Oauth” in harness.forwarded_headers[“Authorization”]! assert harness.forwarded_body == body_data!
  28. 28. def spec_using_valid_oauth_credentials(self, harness)! auth = OAuth1(“mykey”, “mysecret”)! body_data = “{‘function’: ‘tick’}”! harness.reset_data()! response = requests.post(root_url, data=body_data, auth=auth,! headers={‘Content-Type’:! ‘application/json’})! assert response.status_code == 200! assert “Authorization” in harness.forwarded_headers! assert “Oauth” in harness.forwarded_headers[“Authorization”]! assert harness.forwarded_body == body_data!
  29. 29. def spec_using_valid_oauth_credentials(self, harness)! auth = OAuth1(“mykey”, “mysecret”)! body_data = “{‘function’: ‘tick’}”! harness.reset_data()! response = requests.post(root_url, data=body_data, auth=auth,! headers={‘Content-Type’:! ‘application/json’})! assert response.status_code == 200! assert “Authorization” in harness.forwarded_headers! assert “Oauth” in harness.forwarded_headers[“Authorization”]! assert harness.forwarded_body == body_data!
  30. 30. def spec_using_valid_oauth_credentials(self, harness)! auth = OAuth1(“mykey”, “mysecret”)! body_data = “{‘function’: ‘tick’}”! harness.reset_data()! response = requests.post(root_url, data=body_data, auth=auth,! headers={‘Content-Type’:! ‘application/json’})! assert response.status_code == 200! assert “Authorization” in harness.forwarded_headers! assert “Oauth” in harness.forwarded_headers[“Authorization”]! assert harness.forwarded_body == body_data!
  31. 31. def spec_using_valid_oauth_credentials(self, harness)! auth = OAuth1(“mykey”, “mysecret”)! body_data = “{‘function’: ‘tick’}”! harness.reset_data()! response = requests.post(root_url, data=body_data, auth=auth,! headers={‘Content-Type’:! ‘application/json’})! assert response.status_code == 200! assert “Authorization” in harness.forwarded_headers! assert “Oauth” in harness.forwarded_headers[“Authorization”]! assert harness.forwarded_body == body_data!
  32. 32. def spec_using_valid_oauth_credentials(self, harness)! auth = OAuth1(“mykey”, “mysecret”)! body_data = “{‘function’: ‘tick’}”! harness.reset_data()! response = requests.post(root_url, data=body_data, auth=auth,! headers={‘Content-Type’:! ‘application/json’})! assert response.status_code == 200! assert “Authorization” in harness.forwarded_headers! assert “Oauth” in harness.forwarded_headers[“Authorization”]! assert harness.forwarded_body == body_data!
  33. 33. def spec_using_valid_oauth_credentials(self, harness)! auth = OAuth1(“mykey”, “mysecret”)! body_data = “{‘function’: ‘tick’}”! harness.reset_data()! response = requests.post(root_url, data=body_data, auth=auth,! headers={‘Content-Type’:! ‘application/json’})! assert response.status_code == 200! assert “Authorization” in harness.forwarded_headers! assert “Oauth” in harness.forwarded_headers[“Authorization”]! assert harness.forwarded_body == body_data!
  34. 34. capacity management
  35. 35. N = XR # concurrent requests transaction rate response time
  36. 36. API Mgmt client origin 1s2 req/s
  37. 37. API Mgmt client origin 1s2 req/s N = XR = 2 req/s × 1s = 2 req
  38. 38. API Mgmt client origin 1s2 req/s N = XR = 2 req/s × 1s = 2 req
  39. 39. API Mgmt client origin 1s2 req/s N = XR = 2 req/s × 1s = 2 req
  40. 40. API Mgmt client origin 1s2 req/s N = XR = 2 req/s × 1s = 2 req
  41. 41. API Mgmt client origin 1s2 req/s N = XR = 2 req/s × 1s = 2 req
  42. 42. API Mgmt client origin 1s2 req/s
  43. 43. API Mgmt client origin 10s2 req/s N = XR = 2 req/s × 10s = 20 req
  44. 44. API Mgmt client origin 10s2 req/s N = XR = 2 req/s × 10s = 20 req
  45. 45. API Mgmt client origin 10s2 req/s N = XR = 2 req/s × 10s = 20 req
  46. 46. API Mgmt client origin 10s2 req/s N = XR = 2 req/s × 10s = 20 req
  47. 47. API Mgmt client origin 10s2 req/s N = XR = 2 req/s × 10s = 20 req
  48. 48. API Mgmt client origin 10s2 req/s N = XR = 2 req/s × 10s = 20 req
  49. 49. API Mgmt client origin 10s2 req/s ✗ N = XR = 2 req/s × 10s = 20 req
  50. 50. API Mgmt client origin 10s2 req/s ✗ ✗ N = XR = 2 req/s × 10s = 20 req
  51. 51. access_by_lua ...! log_by_lua ...! +1 -1
  52. 52. deployment
  53. 53. Version Control templates vault (keys) nginx.conf!ssh!
  54. 54. architecture
  55. 55. HAProxy HAProxy VIP . . . <API>
  56. 56. DC1 DC2 DC3 VIP VIP VIP entry-vip-dc1. A 10.1.0.1! <foo> <foo> <bar> <bar> foo-dc1. CNAME entry-vip-dc1.! foo. CNAME foo-dc1.! (GSLB) entry-vip-dc2. A 10.2.0.1! entry-vip-dc3. A 10.3.0.1!
  57. 57. DC1 DC2 DC3 VIP VIP VIP entry-vip-dc1. A 10.1.0.1! <foo> <foo> <bar> <bar> foo-dc1. CNAME entry-vip-dc1.! foo. CNAME foo-dc1.! (GSLB) entry-vip-dc2. A 10.2.0.1! entry-vip-dc3. A 10.3.0.1!
  58. 58. DC1 DC2 DC3 VIP VIP VIP entry-vip-dc1. A 10.1.0.1! <foo> <foo> <bar> <bar> foo-dc1. CNAME entry-vip-dc1.! foo. CNAME foo-dc1.! (GSLB) entry-vip-dc2. A 10.2.0.1! entry-vip-dc3. A 10.3.0.1!
  59. 59. DC1 DC2 DC3 VIP VIP VIP entry-vip-dc1. A 10.1.0.1! <foo> <foo> <bar> <bar> foo-dc1. CNAME entry-vip-dc1.! foo. CNAME foo-dc1.! (GSLB) entry-vip-dc2. A 10.2.0.1! entry-vip-dc3. A 10.3.0.1!
  60. 60. DC1 DC2 DC3 VIP VIP VIP entry-vip-dc1. A 10.1.0.1! <foo> <foo> <bar> <bar> foo-dc1. CNAME entry-vip-dc1.! foo. CNAME foo-dc1.! (GSLB) entry-vip-dc2. A 10.2.0.1! entry-vip-dc3. A 10.3.0.1!
  61. 61. DC1 DC2 DC3 VIP VIP VIP entry-vip-dc1. A 10.1.0.1! <foo> <foo> <bar> <bar> foo-dc1. CNAME entry-vip-dc1.! foo. CNAME foo-dc1.! (GSLB) entry-vip-dc2. A 10.2.0.1! entry-vip-dc3. A 10.3.0.1!
  62. 62. Roll Your Own API Management with nginx and Lua • nginx + Lua => great for HTTP middleware with a small amount of custom logic • Automated test and deployment pipeline with Vagrant, Python, and Ansible • Concurrent request limiting, not rate limiting • Network architecture with operational flexibility Presentation title (optional)67
Comments
Top