iT邦幫忙

0

請問如何用 nginx 將流向某port(ex:5000)的http 重新導成https(port5000) 及api相關問題

環境:

GCP上(確認 80、443、5000 有開)
ubuntu 16.04
nginx version: nginx/1.10.3
Python 3.6.9
Flask 1.1.1
Werkzeug 0.15.4


問題1

我用flask(有開SSL)架了一個api是透過 port 5000 接收的
已確定從外部連 https://xxx.xxx:5000 是能正常使用的
http://xxx.xxx:5000 (http) 無法 (http狀態碼為 "fail")
希望能夠透過 nginx 將 http://xxx.xxx:5000 重新導向 https://xxx.xxx:5000
但是一直苦無成果QAQ

/etc/nginx/sites-available/default 設定:


server {
        root /var/www/html;

        index index.html index.htm index.nginx-debian.html;
    server_name xxx.hopto.org; # managed by Certbot

        location / {
                try_files $uri $uri/ =404;
        }

    listen [::]:443 ssl ipv6only=on; # managed by Certbot
    listen 443 ssl; # managed by Certbot
    ssl_certificate /etc/letsencrypt/live/xxx.hopto.org/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/xxx.hopto.org/privkey.pem; 
    include /etc/letsencrypt/options-ssl-nginx.conf; 
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; 
}

server {
   listen 80;
   server_name xxx.hopto.org;
   return 301 https://$host$request_uri;
}
server {
    listen 5000 ssl;
    ssl_certificate /etc/letsencrypt/live/xxx.hopto.org/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/xxx.hopto.org/privkey.pem;

    server_name xxx.hopto.org;
    #error_page 497 301 =307 https://$host:$server_port$request_uri;
    error_page 497 https://$host:$server_port$request_uri;
}

#server {
#   listen 5000;
#   server_name xxx.hopto.org;
#   return 301 https://$host$request_uri;
#}
# 這個註解掉的設定跟上面那個設定調換後依舊無果

想請問能否用nginx單純監聽 port 5000 的 http 而不是 https也監聽? (然後在導向https)

目前是有想說在 flask 端,檢查 https,如果不是就 return 301 到 https
但還是想知道 nginx 的解法

問題2

想請問在我的理解中 flask 已監聽 5000 (佔用狀態)
但 nginx -s reload 上方的設定檔後卻沒有噴 error 出來 (flask也沒噴錯)

不是應該是 port 5000 因為已被佔用,導致第二個要用的程式會出錯呢?
(還是flask已用5000,然後 nginx 吃不到才導致跳轉失敗?)
(亦或真的一個5000,2個監聽)


問題3

想請問如要建api以下觀念對嗎?

一般來說 如果要開api的話
在 nginx 設定應該是要改成

  1. 80 通通轉 443(https)
  2. 在 listen 443 下的 location 在依照後續網址分流
    https://xxx.com/int-to-str 的話 location 就
location ^~ /int-to-str { 
                proxy_pass http://127.0.0.1:5001;
                proxy_set_header Host $host; 
        }
  1. flask api 吃到 5001 後傳回 nginx 再回傳 user

1.(server對外端口只開 80、443,剩下通通藏起來,通通交給 433 location 溝通對嗎?)

2.(如果是用 https://xxx.com:5001 這樣的方式除了醜、不直覺以外有什麼隱患嗎? )

3.(一般來說 RESTful 就好,有複雜查詢再用 GraphQL ? 應該學這2個就好了還是有其他需要學的模式?)

算是未雨綢繆吧。

感謝看到這的您m(_ _)m

froce iT邦大師 6 級 ‧ 2019-07-18 15:44:39 檢舉
http和https都走TCP 5000?
做不到吧...
we684123 iT邦新手 5 級 ‧ 2019-07-18 15:53:08 檢舉
@froce 做不到的話... 我也只好放棄用nginx做了QQ
2
echochio
iT邦研究生 4 級 ‧ 2019-07-19 05:08:55
最佳解答

單一個 port (這裡是5000) 要做到同時兩個服務時做不到的,這是網路基本概念
但是當發生 SSL 錯誤時導向到 https 這是做得到的
有找到文章 :

Nginx has created a custom HTTP status code to allow you to force a redirect for anyone browsing a vhost via HTTP to the HTTPs version, called 497. This is only needed if you're running SSL on a custom port, since otherwise you can use the config shown above for redirecting.

To force the browser to redirect from HTTP to the HTTPs port, do the following. (這裡範例是 port 1234)

server {
  listen      1234 ssl;
  server_name your.site.tld;
  ssl         on;
  ...
  error_page  497 https://$host:1234$request_uri;
  ...

就是說 任何瀏覽器用 http 去瀏覽 https 時會發生 ssl 錯誤這個錯誤碼是 497 錯誤
然後用nginx 的導向將 497 錯誤導向 https 去運作

原文在這邊 :
https://ma.ttias.be/force-redirect-http-https-custom-port-nginx/

froce iT邦大師 6 級 ‧ 2019-07-19 07:43:33 檢舉

喔喔,學到一招。
沒想到可以用錯誤來判別。

we684123 iT邦新手 5 級 ‧ 2019-07-19 13:27:51 檢舉

喔喔喔 感謝回應
看了一下你的之後我再去看我原本的

server {
    listen 5000 ssl;
    ssl_certificate /etc/letsencrypt/live/xxx.hopto.org/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/xxx.hopto.org/privkey.pem;

    server_name xxx.hopto.org;
    #error_page 497 301 =307 https://$host:$server_port$request_uri;
    error_page 497 https://$host:$server_port$request_uri;
}

發現幾乎一模一樣,但是這是失敗的
想了一下後改成

server {
	listen 5000 ssl;
	server_name xxx.hopto.org;
	ssl on;

	ssl_certificate /etc/letsencrypt/live/xxx.hopto.org/fullchain.pem;
	ssl_certificate_key /etc/letsencrypt/live/xxx.hopto.org/privkey.pem;

	root /var/www/html;

	index index.html index.htm index.nginx-debian.html;

	error_page 497 https://$host:$server_port$request_uri;
}

補上檔案後就成功了!!!
可能我先前flask沒配合好才導致失敗的

0
froce
iT邦大師 6 級 ‧ 2019-07-18 16:21:01

問題1:同一IP不能在同一port開不同服務,所以做不到。
問題2:localhost 和 real ip 是不同IP,可以使用同port。
問題3:
1.對,要用的打開,不用的關閉。
2.會踩到corss orign,醜...API是給你藏在程式裡面的,沒有醜的問題。
3.那個需要學那個,用到再去學。

不過...你確定你這樣做ok?
不用架個uwsgi
然後我不懂你為何API要開放http給人轉址?
API是給你藏在程式裡面的、API是給你藏在程式裡面的、API是給你藏在程式裡面的(很重要要講3遍),不該給一般人直接存取。

we684123 iT邦新手 5 級 ‧ 2019-07-18 18:12:22 檢舉
  1. 好的,放棄 nginx 轉址

  2. 這裡說:只要運行的程式向系統提出訪問網路的申請,那麼系統就可以從這些Port號中分配一個供該程式使用。比如1024Port就是分配給第一個向系統發出申請的程式。在關閉程式進程後,就會釋放所佔用的Port號。

所以我的癥結點不是外部2個不同IP流入系統的同一個port,而是系統內部已經分配5000這個port給flask了,為什麼nginx也跟系統要求使用 port 5000,系統還給過了,沒有噴錯。

3-1. 恩恩 感謝釐清
3-2. 這個還真的沒想到,學到了!
3-3. 好的

目前啃 uwsgi 的教學中(先前不知道有這個),目前看起來好像是透過他的話,程式效率會提高(???)支援多項通訊協議(將流量監控、特定事件監控)降低app來源耦合 從應用程式抽離到前一層,讓app只專心做app,不管自己功能以外的事,這樣理解沒錯吧???

api開轉址只是因為怕前面的user少打s變http連不上,所以才開轉址(提高相容性,雖然可能微不足道)
(好吧,是我以前犯蠢 少打s過,所以想說順便避免)

api醜這個只是因為單純用port口沒有任何後綴(ex: /int-to-str),user可能不容易理解這個api是拿來幹嘛的(可能還要查表)
(其實這算是很低級的api方式吧,我的理解是這樣@@)

froce iT邦大師 6 級 ‧ 2019-07-18 20:36:56 檢舉

==a

我都說三遍了你還沒聽懂...
API不是開放給一般人用的,是開放給開發者用的,真正的用法是藏在介面後面。所以你不用介意後綴的port,這根本也沒意義。

這裡說:只要運行的程式向系統提出訪問網路的申....

你的nginx裡:

server {
    listen 5000 ssl;
    server_name xxx.hopto.org;   # 你有綁定domain
}

綁定domain後,表示你的nginx綁定的是經過domain反查後的ip,也就是你的 real ip(假設是211.x.x.x),flask如果不指定的話,則是綁定在localhost(127.0.0.1),這兩個ip是不同的,所以可以綁在同一個port number。
(211.x.x.x:5000 和 127.0.0.1:5000)
你把 server_name 那行註解掉,重開nginx,你就知道了。
應該會跳

nginx: [emerg] bind() to 0.0.0.0:5000 failed (98: Address already in use)
nginx: [emerg] bind() to 0.0.0.0:5000 failed (98: Address already in use)
nginx: [emerg] bind() to 0.0.0.0:5000 failed (98: Address already in use)
nginx: [emerg] bind() to 0.0.0.0:5000 failed (98: Address already in use)
nginx: [emerg] bind() to 0.0.0.0:5000 failed (98: Address already in use)
nginx: [emerg] still could not bind()
we684123 iT邦新手 5 級 ‧ 2019-07-19 13:36:29 檢舉

謝謝回應 算是懂你的意思了!

0
vegalou
iT邦新手 4 級 ‧ 2019-07-18 22:13:41

你試看看 wstunnel 的方式,在httpd上用r-proxy指到 tcp http:5000 上,本來該port就是雙功,在5000上面的憑證 KEY 不需要,套443的 ,我用 Apache 是可以轉 WS to WSS

我要發表回答

立即登入回答