前一天提到幾個還沒有被解決的議題,今天決定要一併處理。首先當然是Kong log在匯入elasticsearch 的時候,沒有timestamp的欄位。這個問題會造成在查找log的時候,對於時間的判斷可能會失真。
但是Kong這麼專業的API Gateway,怎麼可能不記錄時間?因此筆者就去翻找了原廠文件,在Log中有關於時間的一些欄位,最後找到了一個可以參考的參數值:started_at
。根據原廠文件的描述,附註如下:
started_at
- timestamp this request came in, in milliseconds.
連結:Kong Log原廠文件
這表示說,Kong 在記錄一個請求的起始點,就是使用started_at
這個欄位。這很適合作為elasticsearch的@timestamp
。
在以前,筆者一定開始往logstash的組成去成。但如果為了這個簡單的工作,要另外再起一個logstash container,似乎有點太勞師動眾了。因為如果要做資料轉換或是映射,通常都會透過logstash來完成。
這時候,筆者注意到Ingest Node
這個功能,發現只要簡單設定,就能夠完成這個任務。
簡單介紹一下Ingest Node
功能,在 Elasticsearch 裡,Ingest Node 是一種內建的資料前處理功能。
它的作用有點像一條「生產線」,送進來的文件(log、事件…)會依序經過多個 Processor,每個 Processor 負責做一件事,例如:
所以這次的流水線大概如下:
[收到 log JSON] → [Date Processor: started_at → @timestamp] → [存到 ES]
圖 9-1 Create pipeline
首先第一步是先打開kibana的Stack Management
,並找到Ingest Pipelines
,接著按下Create pipeline
來建立。
圖 9-2 Add a processor
接下來先找到Add a processor
的按鈕,新增一個processor。
圖 9-3 Add a processor 2
接著將各個欄位資料如下填入:
Date
started_at
UNIX_MS
@timestamp
圖9-4 pipeline name
接著將Pipeline name 命名為:kong-timestamp-pipeline
,並按下儲存。
plugins:
- name: http-log
config:
# http_endpoint: http://elasticsearch:9200/kong-logs/_doc
http_endpoint: http://elasticsearch:9200/kong-logs/_doc?pipeline=kong-timestamp-pipeline
method: POST
content_type: application/json
將原有的http_endpoint的最後面,加上 ?pipeline=kong-timestamp-pipeline
(由於前面命名為kong-timestamp-pipeline
,因此這邊要一致)。
讀者在下載專案時,預設並不會將pipeline的url填入,原因是這個動作需要進入到kibana對elasticsearch 做一系列的操作。因此會先把這段yaml註解起來。
當一切都就緒之後,我們將 docker compose 重新啟動,指令如下:
docker compose down
docker compose up -d
如果服務都啟動完成了,請再次使用 postman 發動請求:
GET http://localhost:8000/my-service/weatherforecast
apikey
:user1-api-key
當一切都完成時,進入到Kibana
中,就可以看到@timestamp
成功的被記錄到elasticsearch
了。
圖 9-5 timestamp 出現了
前一天有提到,我們在Kong 傳到Elasticsearch 的時候,居然可以看到apikey,可以參考圖9-6。這個特徵如果在較為敏感的行業,一定會被拿出來檢討,因為這表示在維運Kong API Gateway的單位,就有機會接觸到機敏金鑰。
圖9-6 暴露的apikey
筆者特別去翻了一下Key-Auth
的原廠文件,其中有一段提到了:
Recommended:
Use config.key_in_header (enabled by default) as the most common and secure way to do service-to-service calls.
If you need to share links to browser clients, use config.key_in_query (enabled by default).Be aware that query parameter requests can appear within application logs and URL browser bars
, which expose the API key.
參考資料:Kong Plugin :key-auth
可以注意到,原廠其實對於將 apikey 放在 header 並沒有特別警示這個行為很危險,反倒是特別提出key_in_query
的方式,可能會被記錄在應用程式的logs
或是瀏覽器
上。
既然已經被提到了key_in_query
預設開啟,且也被告知較為危險,這時候當然要實驗看看到底在紀錄上看到的行為會是甚麼?因此筆者就以query
的方式來實驗,得到下面結果。
圖9-7 key in query
It's work!,接下來我們到Kibana看一下紀錄。
圖9-8 暴露的api key in url
實驗的過程可以得到結論,這種key_in_query
作法相對於key_in_header
還要更為有風險,因為url
通常會被留在各種設備上。包含從最前端的瀏覽器、Postman、網路設備、Web application firewall、IIS服務器到最後的應用程式。
設備為了可以協助網路通訊可以完成,不可能將url
進行加密。但如果將請求的機密資訊,放到 http header 中,只要將協議做加密(https
),基本上中間設備是看不到 header 中的資訊的。
至於elasticsearch 中會被看到,那是 Kong 原生要將請求與回應的資料,透過 Log 的 Plugin 傳入 elasticsearch中的正常行為。
如果真的也不允許 Log 中的 header 出現相關的機敏資訊,有另外一個做法可以參考。
The following HTTP authentication headers are
redacted
by default, if they appear in the request:
request.headers.authorization
request.headers.proxy-authorization
參考自:Kong log 原廠資訊
這段意思是說,如果請求的header中,如果key 是authorization
或是 proxy-authorization
時,該欄位則會被預設修改,讓Log中不會直接被看到。
既然有這個說明,當然也來實驗看看,首先可以先將kong.yml
修改如下:
# ironman2025\case_ELK\1.Kong_declarative\declarative\kong.yml
- name: key-auth
config:
key_names:
- Authorization #將原本apikey 修改為Authorization
hide_credentials: false
接下來重新啟動服務:
docker compose down
docker compose up -d
其實熟悉docker 的讀者,可以試試看找到運行kong的container,對其執行下述指令:
docker exec -it [container id] base
kong reload
畢竟如果只是更新 kong,不需要把所有 container重新啟動,不過教學文著重在簡單做出所有效果,所以指令的部分會盡量用重複且有用的為主。
接下來,來進行實驗(須注意postman原本header
中的apikey=user1-api-key
,需要修改成authorization=user1-api-key
),執行成功的畫面就略過,直接進行Log的查找,結果如下:
圖9-9 遮蔽後的欄位
圖9-9 會發現,原本會出現的user1-api-key
在Authorization
會自然的被遮蔽了,這種做法或許可以滿足內部對於機敏資訊不希望被Log系統的管理者看到的一個解決方案。
在探索解決方案的時候,通常會遇到一些沒預想到的狀況,通常有時候會讓探索的人困擾很久。但像Kong這麼完整的產品,通常會有非常多以經思考過的功能可以協助完成。
目前系列文已經將Elasticsearch
以及Kibana
引入,因此接下來的系列文,都會大量利用後端的Log來確認轉發後的結果,請期待下集~