使用環境
- 主機(win7 192.168.1.78) 虛機 (ubuntu 192.168.1.102)
- ubuntu 16.04
- nginx 1.10.0
- odoo 9.0c
問題描述
在odoo不使用代理的情況下,日誌中記錄的ip地址是正確的
配置nginx
$ sudo vim /etc/nginx/sites-available/odoo.conf # odoo.conf 配置如下 # 也可以先刪除/etc/nginx/sites-available/default, 因爲監聽80端口衝突了 --- odoo.conf --- server { listen 80 default; server_name _; location / { proxy_pass http://127.0.0.1:8069; proxy_next_upstream error timeout invalid_header http_500 http_502 http_504; proxy_buffer_size 128k; proxy_buffers 16 64k; proxy_redirect off; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; # 這一行必須要有 proxy_set_header X-Forwarded-HOST $host; # 這一行必須要有 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } location ~* /[a-zA-Z0-9_]*/static/ { proxy_cache_valid 200 60m; proxy_buffering on; expires 864000; proxy_pass http://127.0.0.1:8069; } } --- $ sudo ln /etc/nginx/sites-available/odoo.conf /etc/nginx/sites-enabled/odoo.conf $ sudo service nginx start
驗證
重新啓動odoo, 在 ~/.openerp_serverrc的文件中,我並沒有設定 xmlrpc_interface,但是 proxy_mode = True 一定要設置。odoo開啓後,我們有兩種方式可以從宿主機訪問:- 訪問 http://192.168.1.102/ 這種情況,將由Nginx代理之後,轉發請求給odoo
- 訪問 http://192.168.1.102:8069/ 這種情況,將直接訪問odoo
此時,werkzeug 的 log 就出現了錯誤,他並沒有處理正確的 IP 地址。
查看源碼,我們可以在
odoo-9.0/openerp/service/wsgi_server.py 中看到
這也是爲什麼在前面在nginx配置中一定要 set header X-Forwarded-Host的原因。但是,即使調用了ProxyFix, Werkzeug也並沒有 log 出正確的 IP地址, 所以我們有理由懷疑這個是Werkzeug的鍋
測試 Werkzeug
拋開odoo, 我們使用最簡單的wsgi app來測試werkzeug
在home目錄下,創建 app.py 文件,內容如下
#!/usr/bin/python
# coding: utf-8
import logging
from werkzeug.serving import run_simple, WSGIRequestHandler
from werkzeug.contrib.fixers import ProxyFix
def application(environ, start_response):
start_response('200 OK', [('Content-Type', 'text/plain')])
return 'Hello World\n'
# 這個是按照wsgi_server.py中源碼的樣式寫的
def dispatch_app(environ, start_response):
# 去掉了對 config 的要求,因爲我們沒有config
if 'HTTP_X_FORWARDED_HOST' in environ:
return ProxyFix(application)(environ, start_response)
else:
return application(environ, start_response)
if __name__ == "__main__":
logging.basicConfig(level=10)
run_simple('0.0.0.0', 8080, dispatch_app)
我們讓他跑起來, 並用宿主機進行訪問 http://192.168.1.102:8080
$ python ~/app.py
可以看到地址是正確的,
我們再來配置 Nginx, 使用反向代理來測試這個app
$ sudo vim /etc/nginx/sites-available/app.conf
# ----app.conf ------
server {
listen 8044 default;
server_name __;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr; # 這一行必須要有
proxy_set_header X-Forwarded-HOST $host; # 這一行必須要有
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
# -------------------
$ sudo ln /etc/nginx/sites-available/app.conf /etc/nginx/sites-enabled/app.conf
$ sudo service nginx restart
此時宿主機可以通過兩種方式訪問虛擬機
- http://192.168.1.102:8080 直接訪問 app
- http://192.168.1.102:8044 通過Nginx反向代理後,訪問 app
可以看出,拋出Odoo這個干擾項之後,Werkzeug還是不能記錄出正確的 IP 地址, 所以,我們決定肯定這鍋就是werkzeug的了。
解決方法
在google搜索一大堆之後,終於在搜索引擎中將odoo關鍵詞去掉,進而專注搜索werkzeug的問題,鄙人終於找到了解決方法, 重寫 WSGIRequestHandler.address_string方法
在app.py中添加
# 添加此函數
def fix_werkzeug_logging():
from werkzeug.serving import WSGIRequestHandler
def address_string(self):
# 這就是在nginx的config中,爲什麼一定要有X-Real-IP啦
return self.headers.get('x-real-ip', self.client_address[0])
WSGIRequestHandler.address_string = address_string
if __name__ == "__main__":
fix_werkzeug_logging() # 這是新增的行
logging.basicConfig(level=10)
run_simple('0.0.0.0', 8080, dispatch_app)
重新啓動 python ~/app.py, 用宿主機訪問,發現問題 解決啦!
那麼在odoo中修改 odoo-9.0/openerp/service/wsgi_server.py 的 application 函數,新增此行即可!
def application(environ, start_response):
if config['proxy_mode'] and 'HTTP_X_FORWARDED_HOST' in environ:
# 增加此行
werkzeug.serving.WSGIRequestHandler.address_string = lambda self: self.headers.get('x-real-ip', self.client_address[0])
return werkzeug.contrib.fixers.ProxyFix(application_unproxied)(environ, start_response)
else:
return application_unproxied(environ, start_response)
至此,問題解決!