Kubernates集群中的認證、授權與kubeconfig

fans news 發佈 2021-11-22T15:10:23+00:00

在 helm2 中,helm 操作需要配合集群內部署 tiller 的 pod 來負責資源的創建,因此 tiller 需要賦予一定的權限,一般為了簡單會為 tiller 的 pod 賦予 cluster-admin 的最高權限。

K8S 提供了豐富的認證和授權機制,可以滿足各種場景細粒度的訪問控制。本文會介紹 k8s 中的用戶認證、授權機制,並通過例子闡述 kubeconfig 的生成和原理,最後會列舉常見的證書問題,如過期、吊銷、租戶控制等。

兩種用戶

k8s中的客戶端訪問有兩類用戶:

普通用戶(Human User),一般是集群外訪問,如 kubectl 使用的證書

service account: 如集群內的 Pod

有什麼區別呢?

k8s 不會對 user 進行管理,並不存儲 user 信息,你也不能通過調用k8s api來增刪查這個 user。你可以認為這個 user 指的就是公司內的人員,一般需要對接內部權限系統,user 的增刪操作都是在 k8s 外部進行,k8s 只做認證不做管理。

k8s 會對serviceaccount 進行管理,他的作用是給集群內運行的 pod 提供一種認證的方式,如果你這個 pod 想調用apiserver操作一些資源如獲取 node列表,就需要綁定一個serviceaccount帳戶給自己,並為這個serviceaccount賦予一定的權限,這樣就做到了實體和權限的分離,也就是後面會提到的 rbac 授權。

作用範圍:

User獨立在 K8S 之外,也就是說User是可以作用於全局的,跨 namespace,並且需要在全局唯一

ServiceAccount是K8S的一種資源,是存在於某個namespace之中的,在不同namespace中可以同名,代表了不同的資源。

舉例說明:

為 user 生成 kubeconfig

一個同事張三都想要一份集群的 kubeconfig 用來日常 kubectl 操作集群,但限定只能操作名為 test 的 namespace,公司有獨立的權限系統,他的用戶 ID 是唯一的,叫zhangsan。接下來手動為這個用戶生成 kubeconfig

生成證書:

得到:

zhangsan.pem

zhangsan-key.pem

接下來為 zhangsan 授權,首先生成一份角色:test-role, 權限為test 的命名空間下的所有資源的所有操作權限

然後將這個角色role綁定到zhangsan這個 user 上,代表 zhangsan 擁有了這個 role 的權限

為張三生成專屬的 kubeconfig 文件,名為zhangsan.conf

獲取 test 下所有的 pod

如果獲取其他 namespace 下的pod,則報權限錯誤

為 pod 創建 serviceaccount

以 metrics-server的 pod 為例,需要對 pod、node、ns 等資源進行 get list操作,因此權限配置為:

pod 的 yaml 配置中聲明serviceAccountName為metrics-server

以上是 user用戶使用 kubeconfig、pod 使用 serviceaccount 的示例,pod 使用serviceaccount是比較好理解的,但其實 kubeconfig 也可以直接用serviceaccount來生成,不一定非得用 user,只是大多數情況下,kubeconfig 的管理是和用戶的聲明周期一致的,即子用戶可以申領一份自己的 kubeconfig,管理員可以隨時吊銷或者禁用子用戶 kubeconfig 的效力。這個在後面的」多租戶下的kubeconfig「中會提到。

三種機制

談 k8s 的認證和訪問控制,一般都會看到這張圖:

k8s 中所有的 api 請求都要通過一個 gateway 也就是 apiserver 組件來實現,是集群唯一的訪問入口。既然是 gateway,最基礎的功能就是 api 的認證 + 鑒權了。對應了圖上的步驟1和2,而 k8s 中還提供了第 3 步的 admission Control(准入控制),可以更方便地攔截、校驗資源請求。

三種機制:

1、認證:Authentication,即身份認證。檢查用戶是否為合法用戶,如客戶端證書、密碼、Bootstrap tookens和JWT tokens等方式。

2、鑒權:Authorization,即權限判斷。判斷該用戶是否具有該操作的權限,k8s 中支持 Node、RBAC(Role-Based Access Control)、ABAC、webhook等機制,RBAC 為主流方式

3、准入控制:Admission Control。請求的最後一個步驟,一般用於拓展功能,如檢查 pod 的resource是否配置,yaml配置的安全是否合規等。一般使用admission webhooks來實現

1-2-3 全部通過後api 請求會被處理,在 k8s 中也就意味著資源變更可以落庫到 etcd。

K8S 中的認證

上面提到 k8s 並不存儲 user,只知道一個 user 名稱,因此 user 在訪問api 時怎麼做的認證?

Kubernetes 支持很多種認證機制,包括:

  • X509 client certs
  • Static Token File
  • Bootstrap Tokens
  • Static Password File
  • Service Account Tokens
  • OpenId Connect Tokens
  • Webhook Token Authentication
  • Authticating Proxy
  • Anonymous requests
  • User impersonation
  • Client-go credential plugins

前面提到的 user 生成 kubeconfig就是X509 client certs方式, 而 metric-server 就是Service Account Tokens方式。

X509 client certs

即客戶端證書認證,X509 是一種數字證書的格式標準,是 kubernetes 中默認開啟使用最多的一種,也是最安全的一種,api-server 啟動時會指定 ca 證書以及 ca 私鑰,只要是通過同一個 ca 簽發的客戶端 x509 證書,則認為是可信的客戶端,kubeadm 安裝集群時就是基於證書的認證方式。

客戶端證書認證叫作 TLS 雙向認證,也就是伺服器、客戶端互相驗證證書的正確性,在都正確的情況下協調通信加密方案。apiserver 、controller-manager、scheduler、kubelet 等組件之間的交互,一般也是基於X509的客戶端認證方式,目前最常用的 X509 證書製作工具有 openssl、cfssl ,上面生成 kubeconfig 時用到就是 cfssl 工具簽發的證書。

還是舉個例子,在手動部署 k8s 集群時需要做的證書操作,如果你已經熟悉這個過程或者用了 kubeadm 等部署工具,可以跳過這一段。

1、基礎證書生成

ca-config.json

創建用來生成 CA 文件的 JSON 配置文件,這個文件後面會被各種組件使用,包括了證書過期時間的配置,expiry欄位

ca-csr.json

創建用來生成 CA 證書籤名請求(CSR)的 JSON 配置文件

生成基礎 ca 證書

創建自簽名 CA 證書:這裡只需要ca-csr.json文件,執行後會生成三個文件:

ca.csr:證書籤名請求,一般用於提供給證書頒發機構,自簽的就不需要了

ca.pem:證書,公共證書

ca-key.pem:CA密鑰

2、生成 apiserver 證書

apiserver-csr.json

hosts列表不僅包含了三台 master 機器的ip,還包括了 對應的負載均衡的 ip和外網 ip(如果有的話),以及 kubernetes 的 svc IP:172.18.0.1

這個 ip 是 svc ip range 中的第一個 ip,如果沒有這個 ip,集群內的 pod 將無法通過 serviceaccount 的形式訪問 apiserver 並鑒權,會報證書錯誤。

創建apiserver 的 CA 證書:這裡需要4 個文件

  • apiserver-csr.json:apiserver的證書配置
  • ca.pem:基礎公鑰
  • ca-key.pem:基礎私鑰
  • ca-config.json:配置文件,如過期時間

執行後會生成三個文件:

  • apiserver.csr
  • apiserver.pem
  • apiserver-key.pem

** 使用證書**

Service Account Tokens

也就是 service account 使用的認證方式。

你會發現x509的認證方式比較複雜,需要做很多證書,如果我在集群中部署了一個 pod,比如一些 operator,想訪問apiserver獲取集群的一些信息,甚至對集群的資源進行改動,這種認證屬於 k8s 管理範疇,因此 k8s 定義了一種資源serviceaccounts來定義一個 pod 應該擁有什麼權限。

serviceaccounts 是面向 namespace 的,每個 namespace 創建的時候,kubernetes 會自動在這個 namespace 下面創建一個默認的 serviceaccounts,並且這個 serviceaccounts 只能訪問該 namespace 的資源。serviceaccounts 和 pod、service、deployment 一樣是 kubernetes 集群中的一種資源,用戶可以創建自己的serviceaccounts。

service account 主要包含了三個內容:namespace、token 和 ca

  • namespace: 指定了 pod 所在的 namespace
  • token: token 用作身份驗證
  • ca: ca 用於驗證 apiserver 的證書

每個 service account 都對應一個 secret,這三個信息就存放在這個 secret 里,以 base64 編碼。service account 通過 mount 的方式保存在 pod 的文件系統中,其三者都是保存在 /var/run/secrets/kubernetes.io/serviceaccount/目錄下。

kubeconfig 的生成與含義

kubeconfig 是用來訪問 k8s 集群的憑證,生成 kubeconfig 的步驟很簡單但參數很多,這裡以生成 admin 的 kubeconfig 為例,解釋各參數的含義。

生成最高權限的 kubeconfig。

一般情況下集群創建之後,會先生成一份最高權限的 kubeconfig,即管理員角色,可以操作集群的所有資源,並為其他用戶創建或刪除權限,可以稱之為 admin 證書,生成方式是:

admin-csr.json

admin 證書

生成 admin.conf,即最高權限的 kubeconfig

集群參數

本段設置了所需要訪問的集群的信息。

使用 set-cluster 設置了需要訪問的集群,如上為 kubernetes 這只是個名稱,實際為 –server 指向的 apiserver 所在的集群。

–certificate-authority 設置了該集群的公鑰

–embed-certs 為 true 表示將 –certificate-authority 證書寫入到 kubeconfig 中

–server 則表示該集群的 apiserver 地址

用戶參數

本段主要設置用戶的相關信息,主要是用戶證書。

用戶名: zhangsan

證書: /etc/kubernetes/ssl/zhangsan.pem

私鑰: /etc/kubernetes/ssl/zhangsan-key.pem

這一步操作是指客戶端的證書首先要經過集群 CA 的簽署,否則不會被集群認可,認證就失敗。

此處使用的是 客戶端 x509 認證方式,也可以使用token認證,如kubelet的 TLS Boostrap機制下的bootstrapping 使用的就是 token 認證方式

上下文參數

集群參數和用戶參數可以同時設置多對,而上下文參數就是集群參數和用戶參數關聯起來。

上面的上下文名稱為 kubenetes,集群為 kubenetes(apiserver 地址對應的集群),用戶為zhangsan,表示使用 zhangsan 的用戶憑證來訪問

kubenetes 集群

最後使用 kubectl config use-context kubernetes 來使用名為 kubenetes 的環境項來作為配置。

如果配置了多個環境項,可以通過切換不同的環境項名字來訪問到不同的集群環境。

kubeconfig 的認證過程

正向生成 kubeconfig 我們已經做完了,apiserver 認證請求時,如何解析 kubeconfig 文件的內容呢?

我們可以看下 kubeconfig 的內容:

除了 context,裡面有三個證書欄位,都是 base64 編碼後的內容

certificate-authority-data: server 端的證書,用於驗證 apiserver 的合法性

client-certificate-data: 客戶端證書

client-key-data: 客戶端私鑰

可以提取出來 certificate-authority-data 的內容放到一個文件cert.txt,然後base64解碼

ertificate-authority-data:

得到的內容其實就是 ca.pem 即服務端證書,apiserver 的證書也是基於ca.pem簽發,因為 TLS 是雙向認證,apiserver 在認證 kubectl請求時,kubectl 也需要驗證 apiserver 的證書,防止中間人攻擊,驗證的欄位就是certificate-authority-data

client-certificate:

因為 k8s 沒有 user 這種資源,因此在使用 kubeconfig 訪問時,身份信息就「隱藏」在client-certificate的數據中,我們來查看一下。

將 kubeconfig 中的client-certificate-data的內容放在一個文件 client.txt 中,然後解碼:

查看證書內容:

輸出內容可以看到Subject: organization=system:masters, common_name=kubernetes-admin

apiserver 驗證、解析請求,得到 system:masters 的http上下文信息,並傳給後續的authorizers來做權限校驗。

O 和 CN 的含義

「O」:Organization, apiserver接到請求後從證書中提取該欄位作為請求用戶所屬的組 (Group)
「CN」:Common Name,apiserver從證書中提取該欄位作為請求的用戶名 (User Name)

在admin-csr.json中, admin使用了system:masters作為組 (Group)

k8s 預定義了 RoleBinding:cluster-admin 將 Group system:masters 與 Role cluster-admin 綁定,該 Role 授予了調用 k8s 相關 API 的權限,權限極高。

即:

  • Group: system:masters
  • ClusterRole: cluster-admin
  • ClusterRoleBinding: cluster-admin


k8s 核心組件的默認權限

admin權限:system:masters組,clusterrole 和 rolebinding 都叫 cluster-admin

kubelet: system:Nodes組,clusterrole 和 rolebinding 都叫system:nodes,下同

kube-proxy: system:kube-proxy組

scheduler: system:kube-scheduler組

controller-manager: system:kube-controller-manager組

對應關係如圖所示

即 k8s 所有自身組件使用的權限都是基於內置的 clusterrole,生成出來的 kubeconfig 被各組件進程使用,權限都是默認已有 role,如果希望自己創建 role ,就要使用 rbac 授權了

至此,我們應該知道了kubeconfig 的生成流程、驗證方式,以及為什麼採用了 admin.conf 作為kubeconfig,kubectl就能擁有最高權限。下面是一幅示意圖

K8S 中的授權

無論是 user的x509認證 還是 service account的 token認證,認證完後,都要到達第 2 步:授權,
K8S 目前支持了如下四種授權機制:

  • Node
  • ABAC
  • RBAC
  • Webhook

用的最多的就是 RBAC,即基於角色做權限控制。

Node

僅 v1.7 版本以上支持 Node 授權,配合 NodeRestriction 准入控制來限制 kubelet,使其僅可訪問 node、endpoint、pod、service 以及 secret、configmap、pv、pvc 等相關的資源,在 apiserver 中使用以下配置來開啟

node 的鑒權機制:

RBAC

RBAC(Role-Based Access Control)是 kubernetes 中基於角色的訪問控制,通過自定義角色並將角色和特定的 user,group,serviceaccounts 關聯起來達到權限控制的目的。

RBAC 中有三個比較重要的概念:

  • Role: 角色,它其實是一組規則,定義了一組對 Kubernetes API 對象的操作權限;
  • Subject: 被作用者,包括 user、group、service account,通俗來講就是認證機制中所識別的用戶;
  • RoleBinding: 定義了「被作用者」和「角色」的綁定關係,也就是將用戶以及操作權限進行綁定;

RBAC 其實就是通過創建角色(Role),通過 RoleBinding 將被作用者(subject)和角色(Role)進行綁定。下圖是 RBAC 中的幾種綁定關係:

示例:

role:

權限聲明:

apiGroups為「」代表所有核心資源即 v1 group

apiGroups為「*」代表所有group

指定 pod 下的子對象如 kubectl logs xx,在resources中寫為pods/log

對於 CRD 的權限可以定義為:

deployment、sts 等不在核心組,

以 prometheus pod 所需要的權限為例:


K8S 中的准入控制


准入控制是請求的最後一個步驟,准入控制有許多內置的模塊,可以作用於對象的 「CREATE」、」UPDATE」、」DELETE」、」CONNECT」 四個階段。在這一過程中,如果任一準入控制模塊拒絕,那麼請求立刻被拒絕。一旦請求通過所有的准入控制器後就會寫入 etcd 中。

准入控制是在 apiserver 中進行配置啟用的:

kubectl api-versions |grep admission 來確認是否開啟「admissionregistration.k8s.io/v1beta1」

准入控制的配置是有序的,不同的順序會影響 kubernetes 的性能。若需要對 kubernetes 中的對象做一些擴展,可以使用准入控制,比如:創建 pod 時添加 initContainer 或者校驗欄位等。准入控制最常使用的擴展方式就是 admission webhooks,分兩種

  • MutatingAdmissionWebhook:在對象持久化之前進行修改
  • ValidatingAdmissionWebhook:在對象持久化之前進行

istio就是通過 mutating webhooks 來自動將Envoy這個 sidecar 容器注入到 Pod 中去的:https://istio.io/docs/setup/kubernetes/sidecar-injection/。

admission webhooks是同步調用,需要部署webhook server,並創建對象ValidatingWebhookConfiguration 或 MutatingWebhookConfiguration來指向自己的 server,以ValidatingAdmissionWebhook為例:

部署 webhook:

配置:ValidatingWebhookConfiguration

你需要等待幾秒,然後通過通過Deployment或者直接創建Pod,這時創建Pod的請求就會被apiserver攔住,調用ValidatingAdmissionWebhook進行檢查是否Admit通過。比如,上面的example-webhook是檢查容器鏡像是否以」gcr.io」為前綴的。

示例中的 webhook邏輯比較簡單,只是檢查下 image name,然後啟動 http server

AdmissionWebhook 與 Initializers 的區別:

二者都能實現動態可擴展載入admission controller, Initializers是串行執行,在高並發場景容易導致對象停留在uninitialized狀態,影響繼續調度。Alpha Initializers特性在k8s 1.14版本被移除了,官方更推薦AdmissionWebhook;MutatingAdmissionWebhook是串行執行,ValidatingAdmissionWebhook是並行執行,性能更好。

證書吊銷、過期更換

對於kubeconfig 這種 x509證書來說,只要證書不泄露,可以認為是很安全的。但是頒發證書容易,卻沒有很好的方案註銷證書、續期證書

吊銷

想一下如果某個核心成員離職,該如何回收他的admin kubeconfig證書?或者不小心把 kubeconfig 泄露,如何讓這個 kubeconfig 無效呢?

先看下封禁手段:

如果是離職,且 k8s 集群在內網環境,就算他把證書帶出公司也沒關係,畢竟有內網訪問限制。但如果是雲上 k8s 集群,且開放了公網的入口,那麼安全風險就很高了,kubeconfig 只是一個文件,你無法通過限制 ip 來源來封禁。

如果是轉崗,封禁就比較困難了,仍然在內網環境,且 kubeconfig 一般在辦公電腦上使用,即辦公網絡到服務內網,通過來源封禁是不可能的。

再看下證書吊銷:

kubeconfig 證書不支持吊銷,參考 ISSUE。準確的說 k8s 沒有實現CRL(證書吊銷列表)或 OCSP(在線證書狀態協議),並沒有一個統一的地方管理這些證書,因此如果您的密鑰被盜用,Kubernetes也無法在身份驗證層知道。

如何解決這種問題:

  • 重新簽發證書,涉及到apiserver 等組件的重啟
  • 如果用了 rbac,封禁這個角色的權限

如何預防這種問題:

重要:不要使用最高權限的證書,不要使用自帶權限的證書如 system:master,這種只適合kubelet 等組件使用,不適合用來簽發 kubeconfig

無論是用 user 還是 service account 來生成kubeconfig,都使用 rbac 來授權,這樣就算 kubeconfig 丟失,也可以解除 clusterrolebinding來吊銷權限。即管理給用戶的授權,就變成管理clusterrolebinding了

證書的過期時間設置的短一點,如 1 個月就失效,將影響範圍降低

合理規劃主帳號和子帳戶,如主帳戶只允許有一份 admin 證書,其他子用戶全部基於 rbac 來操作,甚至主帳戶也可以用 rbac 來操作,只是權限特別大而已

集成外部認證系統

證書之所以無法吊銷,是因為證書沒有統一的認證中心,換句話,K8S只是定義了一些角色,並沒有實現用戶管理、權限管理,因此專業的人做專業的事,接入認證系統才是生產環境嚴肅認真的解決方案。

Kubernetes支持集成第三方Id Provider(IdP),主流的如AD、LADP以及OpenStack Keystone等。一般都是基於 OpenID Connect(OIDC)Token 的認證和授權。

當前支持OpenID Connect的產品有很多,如:

  • Keycloak
  • UAA
  • Dex
  • OpenUnison
  • 雲廠商

證書續簽

證書配置中有一個欄位:」expiry」: 「87600h」代表了證書的過期時間,到期後證書認證會失敗。

kubeadm 創建的 Kubernetes 集群, apiserver、controller-manager、kubelete 等組件的證書默認有效期只有一年。

因為 k8s 版本疊代很快,官方推薦一年之內至少用 kubeadm 更新一次 Kubernetes 版本,同時會自動更新證書。

查看根 CA 證書的有效期,默認為 10 年:

查看當前證書有效期

重新簽發證書:續簽全部證書

也可以局部進行續簽

如apiserver-etcd-client 、apiserver-kubelet-client、apiserver、etcd-healthcheck-client、etcd-peer、etcd-server、front-proxy-client
kubeadm alpha certs renew apiserver-etcd-client

如果不是 kubeadm 創建的集群,需要手動重新生成所有組件的證書

kubelet 從 v1.8.0 開始支持證書輪換,當證書過期時,可以自動生成新的密鑰,並從 Kubernetes API 申請新的證書。

更換 apiserver 的 ip 或接入 nginx

如果因為機器故障,需要更換 apiserver 機器,則最好保持 ip 不變,否則證書需要重簽,因為證書文件中聲明了機器 ip和負載均衡 ip,如果訪問時只用到了負載均衡 ip,那麼機器 ip 可以不用聲明,也不用擔心機器更換問題。

apiserver 接入 nginx

使用 nginx 的 passthrough,即 4 層轉發 ssl 請求(配置簡單,不需要apiserver證書)

方法二:使用七層正常的 http 轉發

多租戶集群中的的用戶訪問控制

rbac 權限控制是多租戶集群中最基礎的隔離手段,如基於 namespace 做租戶隔離:

企業內部集群:也就是公司內的集群,是很多 k8s 客戶的使用模式,因為集群在公司內網環境,網絡風險可控,因此一般通過 namespace 對部門或產品線做隔離,如:

  • 集群管理員:admin 角色,最高權限,可以擴、縮節點、升級集群,負責分配PV、租戶等全局資源,一般是集群負責人。
  • 租戶管理員:op 角色,租戶內(namespace)的最高權限,管理租戶內的 rbac 權限、存儲、計算資源等
  • 普通用戶:rd 角色,使用權限,根據開發測試角色不同,功能不同,可能會限制get/list/edit 等權限。

namespace 名一般和部門 id 是一致的,方便對接內部權限系統如 SSO,用戶登錄後只能看到自己的 ns 下的業務 pod,同時可以下載自己的 kubeconfig 文件。

因為是通過 namespace 做 rbac 權限上的隔離,因此網絡層面也要隔離 namespace,禁止互訪,如果是跨租戶的訪問需要開放白名單。而存儲和主機特權的隔離需要根據業務來決定,如seccomp/AppArmor/SELinux等是否允許業務使用。

一般情況下,namespace 的權限隔離能滿足大多數企業 k8s 的需求,也是很多 paas 平台的租戶實現方式。

saas & serveless 平台:一般出現在公有雲上,該場景下用戶沒有 k8s 的概念,且不同的服務可以混布在不同的 namespace,如函數計算、AI 離線計算等,你只需要在 saas 控制台上點擊部署 wordpress,選擇軟體版本,就能得到一個完整的博客平台,不需要關心後面的調度邏輯。

這種混布的業務如果有較高的安全需求,k8s 原生是無法滿足的,還需要使用安全容器如 kata在容器運行時來強化租戶安全。

其他場景下的認證需求

ingress

除了 k8s 集群的認證,ingress 也需要 https 證書,而 ssl 證書的續期管理都是一件麻煩事,如果你用的是雲廠商,可以直接在雲上購買 ssl 證書並綁定域名,然後通過 ingress-controller 實現 ingress 的功能,續簽和證書管理都在雲上進行,不需要自己關心。

如果你覺得正規的 ca證書太貴,想用 Let’s Encrypt 等簽發方式,又覺得續簽麻煩,可以使用Cert manager來管理你的證書實現證書自動續簽。

cert-manager + nginx-ingress-controller 結合可以參考這個文章:https://cert-manager.io/docs/tutorials/acme/ingress/

helm

在 helm2 中,helm 操作需要配合集群內部署 tiller 的 pod 來負責資源的創建,因此 tiller 需要賦予一定的權限,一般為了簡單會為 tiller 的 pod 賦予 cluster-admin 的最高權限

tiller pod 運行在kube-system 下,擁有集群所有ns、所有資源的操作權限,不過你也可以限定tiller的使用範圍,比如只能在特定的 namespace 下工作,如:

在特定 namespace 中部署 tiller,並僅限於在該 namespace 中部署資源

運行 helm init 來在 tiller-world namespace 中安裝 Tiller

而在 helm3 中已經不需要 tiller 這個組件,只需要一個 helm 二進位,因此helm 命令使用的 kubeconfig 的權限,也就決定了能夠在哪個 namespace 操作什麼資源,helm 權限和 kubectl 統一,更加方便。


【靠X男友】⚠️ 男人真的是嗅覺動物阿...

2021-11-09T06:17:37.596504+00:00

想被撲倒的又不信邪的,歡迎去試試!

交往3年多,發現他主動的次數越來越少,

但自從上個月換洗「體香沐浴露」🇰🇷 他整個ㄉ一ㄠˊ住!

因為我身上真的好香,所以就好想... 現在我反而被推倒到會怕了哈哈

 

當初以為感情要生變了,他對我總是異常「冷靜」

後來姐妹從韓國帶了【天使體香沐浴露】送我,

好像從那時開始,他就像變了個人似的~

睡覺開始會從背後環抱著我睡 😳 那檔事也變得積極主動!

問姐妹帶回來的到底是什麼?

 

 

這樣厲害... 她說這瓶是韓國票選「男性最愛的香味」🏆

 屬於清新的棉花籽,沒有太over的化學調香,是男女生都會愛的~

有時到隔天中午,還能微微聞到那個舒服的香味

 

 

除此之外,它洗起來也很保濕,洗完摸皮膚會「滑滑嫩嫩」

木頭男友都摸得出差別,而且有1000ml的大容量,CP值很高

 

 

最後還是想靠X一下男友,到底知不知道害我上班遲到被扣多少錢??

非常懷疑裡頭是不是加了費X蒙... 😂

 

這麼累的事情,絕對不能只有我體會💖

想被撲倒的又不信邪的,歡迎去試試【天使體香沐浴露】有多厲害... 

但強烈建議評估自己體力!不是每個人都禁得起「腿軟」的齁 😂

 ➡️ https://www. cashin.tw/product/000000000034171

 

 

商品資訊

 ▼韓妞維持熱戀的秘訣大公開!!▼  

⇢用香味喚醒他的激情 不自覺一直想妳

 ﹍﹍﹍﹍﹍﹍﹍﹍﹍﹍﹍﹍﹍﹍﹍﹍﹍

\穿在身上的香水 純潔果香沐浴露誕生/

 榮獲韓國男性最有好感的香味 NO.1🥇


https://www. cashin.tw/product/000000000034171