Webhook. Allow requests only from Telegram servers. // Optional

This commit is contained in:
Alex Root Junior 2017-11-16 15:27:15 +02:00
parent 6d9bdce935
commit f050d08c75
2 changed files with 44 additions and 1 deletions

View file

@ -2,6 +2,7 @@ import asyncio
import asyncio.tasks import asyncio.tasks
import datetime import datetime
import functools import functools
import ipaddress
import typing import typing
from typing import Dict, Optional, Union from typing import Dict, Optional, Union
@ -25,6 +26,21 @@ WEBHOOK = 'webhook'
WEBHOOK_CONNECTION = 'WEBHOOK_CONNECTION' WEBHOOK_CONNECTION = 'WEBHOOK_CONNECTION'
WEBHOOK_REQUEST = 'WEBHOOK_REQUEST' WEBHOOK_REQUEST = 'WEBHOOK_REQUEST'
TELEGRAM_IP_LOWER = ipaddress.IPv4Address('149.154.167.197')
TELEGRAM_IP_UPPER = ipaddress.IPv4Address('149.154.167.233')
LOCALHOST_IP = ipaddress.IPv4Address('127.0.0.1')
def _check_ip(ip: str) -> bool:
"""
Check IP in range
:param ip:
:return:
"""
address = ipaddress.IPv4Address(ip)
return TELEGRAM_IP_LOWER <= address <= TELEGRAM_IP_UPPER or address == LOCALHOST_IP
class WebhookRequestHandler(web.View): class WebhookRequestHandler(web.View):
""" """
@ -78,6 +94,11 @@ class WebhookRequestHandler(web.View):
:return: :class:`aiohttp.web.Response` :return: :class:`aiohttp.web.Response`
""" """
if self.request.app.get('_check_ip', False):
ip_address, accept = self.check_ip()
if not accept:
raise web.HTTPUnauthorized()
context.set_value('TELEGRAM_IP', ip_address)
context.update_state({'CALLER': WEBHOOK, context.update_state({'CALLER': WEBHOOK,
WEBHOOK_CONNECTION: True, WEBHOOK_CONNECTION: True,
@ -164,6 +185,26 @@ class WebhookRequestHandler(web.View):
if isinstance(result, BaseResponse): if isinstance(result, BaseResponse):
return result return result
def check_ip(self):
"""
Check client IP. Accept requests only from telegram servers.
:return:
"""
# For reverse proxy (nginx)
forwarded_for = self.request.headers.get('X-Forwarded-For', None)
if forwarded_for:
return forwarded_for, _check_ip(forwarded_for)
# For default method
peer_name = self.request.transport.get_extra_info('peername')
if peer_name is not None:
host, _ = peer_name
return host, _check_ip(host)
# Not allowed and can't get client IP
return None, False
def configure_app(dispatcher, app: web.Application, path=DEFAULT_WEB_PATH): def configure_app(dispatcher, app: web.Application, path=DEFAULT_WEB_PATH):
""" """

View file

@ -61,7 +61,7 @@ def start_pooling(dispatcher, *, loop=None, skip_updates=False, on_startup=None,
def start_webhook(dispatcher, webhook_path, *, loop=None, skip_updates=None, on_startup=None, on_shutdown=None, def start_webhook(dispatcher, webhook_path, *, loop=None, skip_updates=None, on_startup=None, on_shutdown=None,
**kwargs): check_ip=False, **kwargs):
log.warning('Start bot with webhook.') log.warning('Start bot with webhook.')
if loop is None: if loop is None:
loop = dispatcher.loop loop = dispatcher.loop
@ -72,8 +72,10 @@ def start_webhook(dispatcher, webhook_path, *, loop=None, skip_updates=None, on_
app['_startup_callback'] = on_startup app['_startup_callback'] = on_startup
app['_shutdown_callback'] = on_shutdown app['_shutdown_callback'] = on_shutdown
app['_skip_updates'] = skip_updates app['_skip_updates'] = skip_updates
app['_check_ip'] = check_ip
app.on_startup.append(_wh_startup) app.on_startup.append(_wh_startup)
app.on_shutdown.append(_wh_shutdown) app.on_shutdown.append(_wh_shutdown)
web.run_app(app, loop=loop, **kwargs) web.run_app(app, loop=loop, **kwargs)
return app