iT邦幫忙

2025 iThome 鐵人賽

DAY 9
0

幾個待解議題

Kong log 的timestamp

前一天提到幾個還沒有被解決的議題,今天決定要一併處理。首先當然是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

Ingest Node (Pipeline) in elasticsearch

在以前,筆者一定開始往logstash的組成去成。但如果為了這個簡單的工作,要另外再起一個logstash container,似乎有點太勞師動眾了。因為如果要做資料轉換或是映射,通常都會透過logstash來完成。

這時候,筆者注意到Ingest Node這個功能,發現只要簡單設定,就能夠完成這個任務。

簡單介紹一下Ingest Node功能,在 Elasticsearch 裡,Ingest Node 是一種內建的資料前處理功能。
它的作用有點像一條「生產線」,送進來的文件(log、事件…)會依序經過多個 Processor,每個 Processor 負責做一件事,例如:

  • 把數字轉成時間(Date Processor)
  • 從字串中抓 IP(Grok Processor)
  • 加新欄位、刪舊欄位
  • 對欄位內容做正規表達式替換
  • 將 JSON 字串解析成物件

所以這次的流水線大概如下:

[收到 log JSON] → [Date Processor: started_at → @timestamp] → [存到 ES]

設定Ingest Node (Pipeline) vai Kibana

https://ithelp.ithome.com.tw/upload/images/20250920/20162800yqXEjIJaxB.png
圖 9-1 Create pipeline

首先第一步是先打開kibana的Stack Management,並找到Ingest Pipelines,接著按下Create pipeline來建立。

https://ithelp.ithome.com.tw/upload/images/20250920/20162800Ax5bzkmAaa.png
圖 9-2 Add a processor

接下來先找到Add a processor的按鈕,新增一個processor。

https://ithelp.ithome.com.tw/upload/images/20250920/20162800Lo4BpOWiXc.png
圖 9-3 Add a processor 2

接著將各個欄位資料如下填入:

  • ProcessorDate
  • Fieldstarted_at
  • FormatsUNIX_MS
  • Target field@timestamp

https://ithelp.ithome.com.tw/upload/images/20250920/20162800s4xubVqK2D.png
圖9-4 pipeline name

接著將Pipeline name 命名為:kong-timestamp-pipeline,並按下儲存。

修改 kong.yml

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 發動請求:

  • Request: GET http://localhost:8000/my-service/weatherforecast
  • Headers: apikeyuser1-api-key

當一切都完成時,進入到Kibana中,就可以看到@timestamp成功的被記錄到elasticsearch了。

https://ithelp.ithome.com.tw/upload/images/20250920/201628004PWiCVM8ey.png
圖 9-5 timestamp 出現了

API Key暴露的議題

前一天有提到,我們在Kong 傳到Elasticsearch 的時候,居然可以看到apikey,可以參考圖9-6。這個特徵如果在較為敏感的行業,一定會被拿出來檢討,因為這表示在維運Kong API Gateway的單位,就有機會接觸到機敏金鑰。

https://ithelp.ithome.com.tw/upload/images/20250920/20162800x6ouVvzWmZ.png
圖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的方式來實驗,得到下面結果。

https://ithelp.ithome.com.tw/upload/images/20250920/20162800Kz8blI41QW.png
圖9-7 key in query

It's work!,接下來我們到Kibana看一下紀錄。

https://ithelp.ithome.com.tw/upload/images/20250920/20162800mw4MgEEoXP.png
圖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的查找,結果如下:

https://ithelp.ithome.com.tw/upload/images/20250920/20162800Xtcc4hriwe.png
圖9-9 遮蔽後的欄位

圖9-9 會發現,原本會出現的user1-api-keyAuthorization會自然的被遮蔽了,這種做法或許可以滿足內部對於機敏資訊不希望被Log系統的管理者看到的一個解決方案。

小結

在探索解決方案的時候,通常會遇到一些沒預想到的狀況,通常有時候會讓探索的人困擾很久。但像Kong這麼完整的產品,通常會有非常多以經思考過的功能可以協助完成。

目前系列文已經將Elasticsearch 以及Kibana引入,因此接下來的系列文,都會大量利用後端的Log來確認轉發後的結果,請期待下集~


上一篇
Day 8:Kong + API logs in Elasticsearch + Kibana - 2
系列文
解鎖API超能力:我的30天Kong可觀測性與管理實戰之旅9
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言