日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網為廣大站長提供免費收錄網站服務,提交前請做好本站友鏈:【 網站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

  • 1 TLS/SSL 的原理
  • 2 對應用透明的加密通道的方案
  • 3 mTLS
  • 4 解決方案
技術大佬教你如何使用Nginx在公網上搭建加密數據通道?

 


最近在跨機房做一個部署,因為機房之間暫時沒有專線,所以流量需要經過公網。對于經過公網的流量,我們一般需要做以下的安全措施:

  1. 只能允許已知的 IP 來訪問;
  2. 流量需要加密。

第一項很簡單,一般的防火墻,或者 Iptables 都可以做到。

對于加密的部分,最近做了一些實驗和學習,這篇文章總結加密的實現方案,假設讀者沒有 TLS 方面的背景知識,會簡單介紹原理和所有的代碼解釋。

1 TLS/SSL 的原理

TLS 是加密傳輸數據,保證數據在傳輸的過程中中間的人無法解密,無法修改。(本文中將 TLS 與 SSL 作為同義詞。所以提到 SSL 的時候,您可以認為和 TLS 沒有區別。)

傳輸的加密并不是很困難,比如雙方用密碼加密就可以。但是這樣一來,問題就到了該怎么協商這個密碼。顯然使用固定的密碼是不行的,比如每個人都要訪問一個網站,如果網站使用固定的密碼,那么和沒有密碼也沒有什么區別了,每個人都可以使用這個密碼去偽造網站。

TLS 要解決的問題就是,能證明你,是你。現在使用的是非對稱加密的技術。非對稱加密會有兩個秘鑰,一個是公鑰,一個是私鑰。公鑰會放在互聯網上公開,私鑰不公開,只有自己知道。只有你有私鑰,我才相信你是你。非對稱加密的兩個秘鑰提供了一下功能(本文不會詳細介紹這部分原理,只簡單提到理解后續內容需要的知識):

  1. 公鑰加密的數據,只有用私鑰可以解密;
  2. 私鑰可以對數據進行簽名,公鑰拿到數據之后可以驗證數據是否由私鑰的所有者簽名的。

有了這兩點,網站就可以和訪問者構建一個加密的數據通道。

首選,網站將公鑰公開(即我們經常說的“證書”),訪客連接到網站的服務器第一件事就是下載網站的證書。因為證書是公開的,每個人都能下載到此網站的證書,那么怎么確定對方就是此證書的所有者呢?客戶端會生成一個隨機數,并使用公鑰進行加密,發送給服務器:請解密這段密文。

這就是上文提到的功能1,即公鑰加密的數據,只有私鑰才能解密。服務器解密之后發回來(當然,并不是明文發回來的,詳細的 TLS 握手過程,見這里[1]),客戶端就相信對方的確是這個證書的所有者。后續就可以通過非對稱加密協商一個密碼,然后使用此密碼進行對稱加密傳輸(性能快)。

但是這樣就足夠驗證對方身份了嗎?假設這樣一種情況,我并不是 google.com 這個域名的所有者,但是我生成了一對證書,然后自己部署,將用戶訪問 google.com 的流量劫持到自己這里來,是不是也能使用自己的證書和用戶進行加密傳輸呢?

所以就有了另一個問題:訪客不僅要驗證對方是證書的真實所有者,還要驗證對方的證書的合法性。即 google.com 的證書只有 Google 公司可以擁有,我的博客的證書只有我的博客可以擁有。私自簽發的證書不合法。

為了解決這個問題,就需要有一個權威的機構,做如下的保證:只有網站的所有者,才能擁有網站的證書。然后訪客只要信任這個“權威的機構”就可以了。

技術大佬教你如何使用Nginx在公網上搭建加密數據通道?

 

CA 扮演的角色

CA 的全稱是 Certification Authority,是一個第三方機構,在上述加密的流程中,扮演的角色同時被訪客和網站所信任。

網站需要去 CA 申請證書,而 CA 要對自己頒發(簽名)的證書負責,即確保證書頒發給了對方,頒發證書之前要驗證你是你。申請證書的時候,CA 一般會要求你完成一個 Challenge 來證明身份,比如,要求你將某個 URL 返回特定內容,或者要求你將 DNS 的某個 text record 返回特定內容來證明你的確擁有此域名(詳見 validation standards[2])。只有你證明了你是你,CA 才會簽證書給你。

訪客是怎么驗證證書的呢?這就用到了上文提到的功能2:“私鑰可以對數據進行簽名,公鑰拿到數據之后可以驗證數據是否由私鑰的所有者簽名的。” CA 也有自己的一套私鑰公鑰,CA 使用私鑰對網站的證書進行簽名(擔保),訪客拿到網站的證書之后,使用 CA 的公鑰校驗簽名即可驗證這個“擔保”的有效性。

那么 CA 的公鑰是怎么來的呢?答案是直接存儲在客戶端的。linux 一般存儲在 /etc/ssl/certs。由此可見,CA 列表更新通常意味著要升級系統,一個新的 CA 被廣泛接受是一個漫長的過程。新 CA 簽發的證書可能有一些老舊的系統依然不信任。

比如 letsencrypt 的 CA[3],之前就是使用交叉簽名的方式工作,即已有的 CA 為我做擔保,我可以給其他的網站簽發證書。這也是中級證書的工作方式。每天有這么多網站要申請證書,CA 怎么簽發的過來呢?于是 CA 就給很多中級證書簽名,中級證書給網站簽名。這就是“信任鏈”。訪客既然信任 CA,也就信任 CA 簽發的中級,也就信任中級簽發的證書。

被信任很漫長,被不信任很簡單。

CA (以及中級證書機構)有著非常大的權利。舉例,CA 假如給圖謀不軌的人簽發了 Google 的證書,那么攻擊者就可以冒充 Google。即使 Google 和這個 CA 并沒有任何業務往來,但是自己的用戶還是被這個 CA 傷害了。所以 CA 必須做好自己的義務:

  1. 保護自己的私鑰不被泄漏;
  2. 做好驗證證書申請者身份的義務;
  3. 如果(2)有了疏忽,對于錯誤簽發的證書要及時吊銷。

案例:賽門鐵克證書占了活躍證書的 30% – 45%(當時[4]),但是被 Google 發現其錯誤頒發了 3 萬個證書,發現后卻不作為。因此逐步在后續的 Chrome 版本中吊銷了賽門鐵克的證書。

案例2:let’sencrypt 今年 1 月份發現自己的 TLS-ALPN-01 chanllege 有問題,于是按照規定,在 5 天后吊銷了這期間通過 TLS-ALPN-01 頒發的所有證書。

說道這里我想繼續跑一個題。我以前給博客部署證書的時候(2017年[5])就想:CA 給我發一個證書居然要收我的錢?這個不是零成本的東西嗎?他們想發多少就發多少。看到現在讀者應該明白了,這并不是一個零成本的事情:簽發證書的驗證服務需要花錢,而 CA Root key 的保護要花更多的錢。整個 CA 公司(組織)的核心資產就是一個 key,如果這個 key 暴露了,后果不堪設想。

所以,一個無比重要卻要一直使用的 key 在一個上千萬人的組織里怎么被使用而不暴露給任何一個人呢?這是要花很多錢的。Root key 的生成會有一個儀式(Key ceremony),全程錄像,有 20 多個不同組織的代表會現場參加并監督,會有 3000 多個人觀看實時錄像,確保 key 的生成是標準流程。

在 Root key 的保存和使用上,Root key 只會簽中級 CA,以減少使用次數以及 Root key 需要被 revoke(代價太大)的風險。Root Key 保存在一個特殊的硬件中(HSM,Hardware security module),完全離線保存,HSM 也放在特殊的機房中,7×24 有人看守,并離線錄像,機房有 Class 5 Alarm System,有多把鎖,沒有一個人可以單獨進入。

使用這個 Root Key 必須物理上進入這個機房,使用過程全程錄像,并且記錄使用過程,如果有問題可以很快地將 Root Key 簽的內容 revoke。這里有一個視頻介紹 Key Signing Ceremony[6],非常有趣。所以說 CA 機構并不是一個搖錢樹,Let’s Encrypt 這種組織簡直就是慈善機構。

以上就是 TLS,證書,CA 大致的工作原理,稍稍有些跑題,有了這些知識我們就可以利用 TLS 來建立一個加密的數據通道了。后續幾乎都是實際的操作。筆者對這部分也不是精通,如果有錯誤,歡迎指出。

2 對應用透明的加密通道的方案

上文是通過網站部署 HTTPS 來講的 TLS 的工作原理。其實網站部署 HTTPS 還算是比較簡單:你只需要找一個 CA,申請證書,完成 CA 的驗證,部署證書,就可以了。

現在要解決的問題更加復雜一些:我們的兩個組件之間是通過自己研發的協議通訊(基于 TCP),現在要分別部署在兩個機房,通過公網進行通訊。

我們的方案要對通訊的兩邊做好安全防護:

  1. 數據要進行加密傳輸;
  2. 要對兩邊做身份驗證,比如 A 向 B 發起連接,A 要驗證 B 的身份,B 也要驗證 A 的身份;
  3. 最好對于應用來說透明,即應用完全不修改代碼,依然按照原來的方式工作,但是我們將中間的流量進行加密。

3 mTLS

mTLS 的全稱是 Mutual TLS,即雙向的 TLS 驗證。HTTPS 只是訪客驗證了網站的身份,網站并沒有驗證訪客的身份。其實要驗證也是可以的,網站發送證書之后可以跟訪客說:“現在該輪到你出示你的證書了”。如果訪客不能提供有效的證書,網站可以拒絕服務。

其實,ssh 方式就是一個雙向驗證的過程。我們都知道通過 ssh key 登錄 server 的時候,需要讓 server 信任你的 key(即將你的 pubkey 放到 server 上去)。但是還有一個過程容易被忽略掉,在第一次通過 ssh 連接服務器的時候,ssh 客戶端會給你展示 server 的 pubkey,問你是否信任。如果之后這個 key 變了,說明有可能你連接到的并不是目的服務器。

技術大佬教你如何使用Nginx在公網上搭建加密數據通道?

 

第一次連接到服務器的提示

如果之后這個 key 變了,ssh 客戶端就會拒絕連接。

技術大佬教你如何使用Nginx在公網上搭建加密數據通道?

 

Git 也是通過走 ssh 協議的,所以也是一個雙向認證。你在使用 Github 的時候要互相信任對方:

  • Github 信任你的方式是:你將自己的 pubkey 上傳到 GitHub (設置,profile,keys)
  • 你信任 GitHub 的方式是:GitHub 將自己的 pubkey 公布在網上[7]。

4 解決方案

為了實現對應用透明的加密通訊,我們在兩個機房各搭建一個 Nginx,這里兩個 Nginx 之間通過 mTLS 相互認證對方。應用將請求明文發給同機房的 Nginx,然后 Nginx 負責加密發給對方。對于應用來說,對方機房的組件就如同和自己工作在相同機房一樣。最終搭建起來如下圖所示。

技術大佬教你如何使用Nginx在公網上搭建加密數據通道?

 

搭建過程

因為用 HTTP 流量來搭建,相關的工具和日志會更友好一些。所以我們會先用 HTTP 將這個通道搭建起來,然后換成 tcp steam。

準備證書

我們一共需要兩套證書,一套給 Client,一套給 Server。

因為我們這里主要要解決的問題內部互相信任的問題,不需要開給外面的用戶,所以這里我們采用 self signed certificate,即,我們自己做 CA,給自己簽發證書。自簽發證書的好處是很靈活,方便,壞處是有一些安全隱患(畢竟不像權威機構那樣專業)。所以我把這個過程寫在博客上,請大家幫忙看看流程有沒有問題。

首先我們創建一個 CA 的 key,即私鑰。CA 的 key 最好給一個密碼保護,每次使用這個 CA 簽發證書的時候,都需要輸入密碼。

生成 key 的命令:

openssl genrsa -des3 -out ca.key 4096

輸出(其中按照提示輸入密碼):

Generating RSA private key, 4096 bit long modulus (2 primes)
.............................................................++++
....................................................................................................................................................................................++++
e is 65537 (0x010001)
Enter pass phrase for ca.key:<passphrase>
Verifying - Enter pass phrase for ca.key:<passphrase>

命令的解釋:

  • openssl:cert 和 key 相關的操作我們都用 openssl 來完成
  • genrsa:生成 RSA 私鑰
  • -des3:生成的 key,使用 des3 進行加密,如果不加這個參數,就不會提示讓你輸入密碼
  • 4096:生成 key 的長度

這里我們假設所使用的密碼是 hello。

然后我們來生成 CA 的公鑰部分,即證書。

openssl req -new -x509 -days 365 -key ca.key -out ca.crt

這時會詢問你一些信息,比如地區,組織名字之類的。其中,Organization Name 和 Common Name 需要留意。CA 的這一步填什么都可以。Common Name 又簡稱 CN,就是證書簽發給哪一個域名(也可以是 IP)的意思。

輸出會是如下所示:

You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:
State or Province Name (full name) [Some-State]:
Locality Name (eg, city) []:
Organization Name (eg, company) [Internet Widgits Pty Ltd]:CertAuth
Organizational Unit Name (eg, section) []:
Common Name (e.g. server FQDN or YOUR name) []:
Email Address []:

命令的解釋:

  • req:創建證書請求;
  • -new:產生新的證書;
  • -x509:直接使用 x509 產生新的自簽名證書,如果不加這個參數,會產生一個“證書簽名請求”而不是一個證書。
  • -days 365:證書1年之后過期,也可以省略這個參數,設置為永不過期;
  • key:創建公共證書的私鑰,會被提示輸入私鑰的密碼;
  • -out:生成的證書。

到這里,我們有了一對 CA 證書,ca.key 和 ca.crt 兩個文件。接下來申請 server 端的證書。

Server 端證書依然是先生成一個 key,這里就不需要密碼保護了:

openssl genrsa -out server.key 4096

然后這里下一步不是直接生成證書,而是生成一個證書請求。但是那些問題依然是要回答一遍的。

openssl req -new -key server.key -out server.csr

回答問題的時候要注意兩個地方:

  • Organization Name: 不能和 CA 的一樣;
  • Common Name: 必須要寫一個,可以寫一個不存在的域名,比如 proxy.example.com。否則,會有錯誤:“* SSL: unable to obtain common name from peer certificate”。

否則證書無法使用。

到這里其實也可以看出,CA 的證書和其他的證書沒有什么不同,也是一個普通的證書而已。

這個 .csr 文件是 Ceritifcate Signing Request,即請求簽名。接下來我們使用我們的 CA 給這個 Server 證書簽名(作擔保!)。

openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -set_serial 01 -out server.crt

這個命令需要輸入 CA key 的密碼,就是剛剛說的 hello。

命令的解釋:

  • x509:公有證書的標準格式;
  • -CA:使用 CA 對其簽名;
  • -CAkey:CA key(沒有這個豈不是人人可以用 CA 證書簽名了?);
  • -set_serial 01:簽發的序列號,如果證書有過期時間的話,過期之后,可以直接用這個 .csr 修改序列號重新簽一個,不需要重新生成 .csr 文件;

如此,就得到了 server.crt 文件。

我們可以使用這條命令驗證生成的證書是 ok 的:

# openssl verify -verbose -CAfile ca.crt server.crt 
server.crt: OK

重復此流程再簽發一個 client 端的證書。

結束后,我們有以下內容:

  • ca.key
  • ca.crt
  • CA 的密碼,需要保存
  • server.key
  • server.crt
  • server.csr:部署不需要用到,可以只保存在安全的地方即可;
  • Server 證書簽發序列:只保存即可;
  • client.key
  • client.crt
  • client.csr:部署不需要用到,可以只保存在安全的地方即可;
  • Client 證書簽發序列:只保存即可;

然后接下來就可以部署起來了。

搭建遠程 Server 端的 Nginx

為了模擬轉發到后端應用的場景,這里的 Nginx 不使用靜態文件,而是用一個 fastapi 寫的樣例程序來做后端:

from typing import Optionalfrom fastapi import FastAPIApp = FastAPI()@app.get("/")def read_root():    return {"Hello": "World"}

啟動的命令是:

uvicorn app:app

程序默認會運行在 8000 端口。

然后修改 Nginx 的配置,nginx.conf 不變,我們只修改 default 的配置,將 default rename 成 remote_server,然后修改成成如下配置:

server {        listen 443 default_server ssl;        listen [::]:443 default_server ssl;        server_name _;        ssl_certificate /home/vagrant/cert/server.crt;        ssl_certificate_key /home/vagrant/cert/server.key;        location / {                proxy_pass http://127.0.0.1:8000;        }

這就是一個很簡單的 Nginx HTTPS 配置,證書配置上了我們剛剛自己簽發的證書:

  • ssl_certificate:告訴 Nginx 使用哪一個公有證書;
  • ssl_certificate_key:此證書對用的私鑰是什么,服務器需要有私鑰才能工作。

證書已經配置好了。這時候我們去 cURL 443 端口會出現錯誤:“curl: (60) SSL: unable to obtain common name from peer certificate”,cURL 不信任這個服務器的證書。這是當然了,因為這個證書是我們自己作為 CA 簽的。

要正常訪問,必須使用 cURL --ca ./ca.cert 來告訴 cURL 我們信任這個 CA (所簽發的所有證書)。

另外還要注意的是,記得我們之前的 Server 證書是簽發給 proxy.example.com 的嗎?我們這里必須要訪問這個域名才行。需要這樣使用:

curl -v https://proxy.example.com --cacert ./ca.crt --connect-to proxy.example.com:443:127.0.0.1:443

--connect-to 的意思是,所有發往這個域名的請求,都直接發給這個 IP。

Client 對 Server 的驗證就配置好了,接下來再配置 Server 對 Client 的驗證。

我們只需要將上面的配置文件改成如下即可:

server {        listen 443 default_server ssl;        listen [::]:443 default_server ssl;        server_name _;        ssl_certificate /home/vagrant/cert/server.crt;        ssl_certificate_key /home/vagrant/cert/server.key;        ssl_verify_client on;        ssl_client_certificate /home/vagrant/cert/ca.crt;        location / {                proxy_pass http://127.0.0.1:8000;        }}

添加的內容的含義:

  • ssl_verify_client:需要驗證客戶端的證書;
  • ssl_client_certificate:我們信任這個 CA 所簽發的所有證書。

這里有一個小插曲:Nginx 的文檔上說,ssl_trusted_certificate 和 ssl_client_certificate 這兩個配置效果都是一樣的,唯一的區別是 ssl_client_certificate 會將信任的 CA 列表發送給客戶端,但是 ssl_trusted_certificate 不會發。

發送是合理的,因為客戶端如果有很多證書,讓客戶端一個一個去嘗試哪一個能建連是沒有意義并且很浪費的。ssl_trusted_certificate 的作用是驗證 OCSP Response。但是我嘗試了 ssl_trusted_certificate,Nginx 會直接 fail 掉語法檢查:

The server fails to start with error: nginx: [emerg] no ssl_client_certificate for ssl_verify_clientb

這里發現一個 ticket 詢問和我一樣的問題:https://trac.nginx.org/nginx/ticket/1902,不過至今沒有回復。我以為是 Nginx 版本的 Bug,然后嘗試了最新的版本依然是一樣的結果。如果讀者知道可以指點一下,謝謝。

這樣配置之后 reload Nginx,就開啟了對客戶端的證書驗證了。這時候我們繼續使用上面那個 cURL,就無法得到響應。

<head><title>400 No required SSL certificate was sent</title></head>

Nginx 會要求你提供證書。

如下的 cURL,帶上證書,就可以正常拿到響應。

curl -v https://proxy.example.com --cacert ./ca.crt --connect-to proxy.example.com:443:127.0.0.1:443 --cert client.crt --key client.key

這樣,遠端的 Nginx 就配置好了,它會提供證書證明自己的身份,也會要求客戶端提供證書進行驗證。

接下來搭建本地的 Nginx,將明文請求加密對接到遠端的 Nginx。

搭建本地 Client 端的 Nginx

本地機房開啟一個 Nginx,監聽 80 端口,轉發到遠程的 443 端口。

配置如下:

upstream remote{ server 127.0.0.1:443;}server {        listen 80 default_server;        listen [::]:80 default_server;        server_name _;        location / {                proxy_pass https://remote;                proxy_ssl_trusted_certificate /home/vagrant/cert/ca.crt;                proxy_ssl_verify on;                proxy_ssl_server_name on;                proxy_ssl_name proxy.example.com;                proxy_ssl_certificate /home/vagrant/crt/client.crt;                proxy_ssl_certificate_key /home/vagrant/cert/client.key;        }}

這個配置可以分成兩部分看,第一部分,是要驗證對方的證書:

  • proxy_ssl_verify:需要對方提供證書;
  • proxy_ssl_trusted_certificate:我們只信任這個 CA 簽發的所有證書;
  • proxy_ssl_server_name:不像 cURL 的 --connect-to 選項,這里我們直接指定目標 IP 轉發,但是我們使用 SNI 功能來告訴對方我們要連接哪一個 domain,來驗證相關 domain 的證書;
  • proxy_ssl_name:我們需要哪一個 domain 的證書。

然后第二部分是提供自己的證書:

  • proxy_ssl_certificate:我的證書;
  • proxy_ssl_certificate_key:我的私鑰,不會發送給對方,只是本地 Nginx 自己使用。

然后就可以 cURL 本地的 80 端口了:

curl http://127.0.0.1 -v *   Trying 127.0.0.1:80...* TCP_NODELAY set* Connected to 127.0.0.1 (127.0.0.1) port 80 (#0)> GET / HTTP/1.1> Host: 127.0.0.1> User-Agent: curl/7.68.0> Accept: */*> * Mark bundle as not supporting multiuse< HTTP/1.1 200 OK< Server: nginx/1.18.0 (Ubuntu)< Date: Wed, 16 Mar 2022 03:49:05 GMT< Content-Type: application/json< Content-Length: 17< Connection: keep-alive< * Connection #0 to host 127.0.0.1 left intact{"Hello":"World"}

可以看到我們從客戶端(cURL)發出明文 HTTP 請求,到服務端(fastapi)收到明文 HTTP 請求,兩邊都不知道中間流量加密過程,但是走公網的部分已經被加密了。就實現了本文開頭的需求。

代理 TCP steam

以上是 HTTP 的配置,將其換成 TCP Steam 的代理也很簡單,相應的配置修改一下就可以。這里我們以 redis 服務為例來展示一下配置。

/etc/nginx/nginx.conf

user www-data;
worker_processes auto;
pid /run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;

events {
        worker_connections 768;
}

stream {

        ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; # Dropping SSLv3, ref: POODLE
        ssl_prefer_server_ciphers on;

        include /etc/nginx/conf.d/*.conf;
        include /etc/nginx/sites-enabled/*;
}

Remote Server 的配置:/etc/nginx/sites-enabled/remote_server

server {
        listen 443 ssl;
        proxy_pass 127.0.0.1:6379;

        ssl_certificate /home/vagrant/cert/server.crt;
        ssl_certificate_key /home/vagrant/cert/server.key;

        ssl_verify_client on;
        ssl_client_certificate /home/vagrant/cert/ca.crt;
}

local_client 的配置:/etc/nginx/sites-enabled/client_server

upstream remote{
 server 127.0.0.1:443;
}

server {
        listen 80;
        listen [::]:80;

        proxy_pass remote;
        proxy_ssl_trusted_certificate /home/vagrant/cert/ca.crt;
        proxy_ssl_verify on;
        proxy_ssl_server_name on;
        proxy_ssl_name config.example.com;

        proxy_ssl on;
        proxy_ssl_certificate /home/vagrant/cert/client.crt;
        proxy_ssl_certificate_key /home/vagrant/cert/client.key;
}

基本上就是把 HTTP 代理換成了 TCP 代理指令。

這樣配置好之后,我們就可以用 redis-cli 去連接本地的 80 端口了。

redis-cli -p 80
127.0.0.1:80> get foo
"bar"

分享到:
標簽:加密 數據
用戶無頭像

網友整理

注冊時間:

網站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網站吧!
最新入駐小程序

數獨大挑戰2018-06-03

數獨一種數學游戲,玩家需要根據9

答題星2018-06-03

您可以通過答題星輕松地創建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學四六

運動步數有氧達人2018-06-03

記錄運動步數,積累氧氣值。還可偷

每日養生app2018-06-03

每日養生,天天健康

體育訓練成績評定2018-06-03

通用課目體育訓練成績評定