部署 Master

准备证书

Master 节点的准备证书操作只需要做一次,将生成的证书拷到每个 Master 节点上以复用。

前提条件:

  • 签发证书需要用到 生成 CA 证书 时创建的 CA 证书及其密钥文件,确保它们在当前目录
  • 确保 cfssl 在当前环境已安装,安装方法参考 这里

为 kube-apiserver 签发证书

kube-apiserver 是 k8s 的访问核心,所有 K8S 组件和用户 kubectl 操作都会请求 kube-apiserver,通常启用 tls 证书认证,证书里面需要包含 kube-apiserver 可能被访问的地址,这样 client 校验 kube-apiserver 证书时才会通过,集群内的 Pod 一般通过 kube-apiserver 的 Service 名称访问,可能的 Service 名称有:

  • kubernetes
  • kubernetes.default
  • kubernetes.default.svc
  • kubernetes.default.svc.cluster
  • kubernetes.default.svc.cluster.local

通过集群外也可能访问 kube-apiserver,比如使用 kubectl,或者部署在集群外的服务会连 kube-apiserver (比如部署在集群外的 Promethues 采集集群指标做监控),这里列一下通过集群外连 kube-apiserver 有哪些可能地址:

  • 127.0.0.1: 在 Master 所在机器通过 127.0.0.1 访问本机 kube-apiserver
  • Service CIDR 的第一个 IP,比如 flanneld 以 daemonset 部署在每个节点,使用 hostNetwork 而不是集群网络,这时无法通过 service 名称访问 apiserver,因为使用 hostNetwork 无法解析 service 名称 (使用的 DNS 不是集群 DNS),它会使用 apiserver 内部的 CLUSTER IP 去请求 apiserver。 kube-controller-manager 的 --service-cluster-ip-range 启动参数是 10.32.0.0/16,那么第一个 IP 就是 10.32.0.1
  • 自定义域名: 配了 DNS,通过域名访问 kube-apiserver,也要将域名写入证书
  • LB IP: 如果 Master 节点前面挂了一个负载均衡器,外界可以通过 LB IP 来访问 kube-apiserver
  • Master 节点 IP: 如果没有 Master 负载均衡器,管理员在节点上执行 kubectl 通常使用 Master 节点 IP 访问 kube-apiserver

准备 CSR 文件:

cat > apiserver-csr.json <<EOF
{
    "CN": "kubernetes",
    "hosts": [
      "127.0.0.1",
      "10.32.0.1",
      "10.200.16.79",
      "kubernetes",
      "kubernetes.default",
      "kubernetes.default.svc",
      "kubernetes.default.svc.cluster",
      "kubernetes.default.svc.cluster.local"
    ],
    "key": {
        "algo": "rsa",
        "size": 2048
    },
    "names": [
        {
            "C": "CN",
            "ST": "SiChuan",
            "L": "Chengdu",
            "O": "Kubernetes",
            "OU": "Kube API Server"
        }
    ]
}
EOF

hosts 这里只准备了必要的,根据需求可增加,通常 Master 节点 IP 也都要加进去,你可以执行了上面的命令后再编辑一下 apiserver-csr.json,将需要 hosts 都加进去。

cfssl gencert \
  -ca=ca.pem \
  -ca-key=ca-key.pem \
  -config=ca-config.json \
  -profile=kubernetes \
  apiserver-csr.json | cfssljson -bare apiserver

会生成下面两个重要的文件:

  • apiserver-key.pem: kube-apiserver 证书密钥
  • apiserver.pem: kube-apiserver 证书

为 kube-controller-manager 签发证书

cat > kube-controller-manager-csr.json <<EOF
{
  "CN": "system:kube-controller-manager",
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "CN",
      "ST": "SiChuan",
      "L": "Chengdu",
      "O": "system:kube-controller-manager",
      "OU": "Kube Controller Manager"
    }
  ]
}
EOF

cfssl gencert \
  -ca=ca.pem \
  -ca-key=ca-key.pem \
  -config=ca-config.json \
  -profile=kubernetes \
  kube-controller-manager-csr.json | cfssljson -bare kube-controller-manager

生成以下两个文件:

  • kube-controller-manager-key.pem: kube-controller-manager 证书密钥
  • kube-controller-manager.pem: kube-controller-manager 证书

为 kube-scheduler 签发证书

cat > kube-scheduler-csr.json <<EOF
{
  "CN": "system:kube-scheduler",
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "CN",
      "ST": "SiChuan",
      "L": "Chengdu",
      "O": "system:kube-scheduler",
      "OU": "Kube Scheduler"
    }
  ]
}
EOF

cfssl gencert \
  -ca=ca.pem \
  -ca-key=ca-key.pem \
  -config=ca-config.json \
  -profile=kubernetes \
  kube-scheduler-csr.json | cfssljson -bare kube-scheduler

生成以下两个文件:

  • kube-scheduler-key.pem: kube-scheduler 证书密钥
  • kube-scheduler.pem: kube-scheduler 证书公钥

签发 Service Account 密钥对

kube-controller-manager 会使用此密钥对来给 service account 签发 token,更多详情参考官方文档: https://kubernetes.io/docs/reference/access-authn-authz/service-accounts-admin/

cat > service-account-csr.json <<EOF
{
  "CN": "service-accounts",
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "CN",
      "ST": "SiChuan",
      "L": "Chengdu",
      "O": "Kubernetes",
      "OU": "Service Account"
    }
  ]
}
EOF

cfssl gencert \
  -ca=ca.pem \
  -ca-key=ca-key.pem \
  -config=ca-config.json \
  -profile=kubernetes \
  service-account-csr.json | cfssljson -bare service-account

生成以下两个文件:

  • service-account-key.pem: service account 证书公钥
  • service-account.pem: service account 证书私钥

为管理员签发证书

为最高权限管理员证书:

cat > admin-csr.json <<EOF
{
  "CN": "admin",
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "CN",
      "ST": "SiChuan",
      "L": "Chengdu",
      "O": "system:masters",
      "OU": "System"
    }
  ]
}
EOF

cfssl gencert \
  -ca=ca.pem \
  -ca-key=ca-key.pem \
  -config=ca-config.json \
  -profile=kubernetes \
  admin-csr.json | cfssljson -bare admin

生成一下两个文件:

  • admin-key.pem: 管理员证书密钥
  • admin.pem: 管理员证书

给用户签发证书后,用户访问 kube-apiserver 的请求就带上此证书,kube-apiserver 校验成功后表示认证成功,但还需要授权才允许访问,kube-apiserver 会提取证书中字段 CN 作为用户名,这里用户名叫 admin,但这只是个名称标识,它有什么权限呢?admin 是预置最高权限的用户名吗?不是的!不过 kube-apiserver 确实预置了一个最高权限的 ClusterRole,叫做 cluster-admin,还有个预置的 ClusterRoleBindingcluster-admin 这个 ClusterRolesystem:masters 这个用户组关联起来了,所以说我们给用户签发证书只要在 system:masters 这个用户组就拥有了最高权限。

以此类推,我们签发证书时也可以将用户设置到其它用户组,然后为其创建 RBAC 规则来细粒度的控制权限,减少安全隐患。

更多 K8S 预置的 Role 与 RoleBinding 请参考: https://kubernetes.io/docs/reference/access-authn-authz/rbac/#default-roles-and-role-bindings

准备 kubeconfig

部署 Master 的准备 kubeconfig 操作只需要做一次,将生成的 kubeconfig 拷到每个 Master 节点上以复用。

kubeconfig 主要是各组件以及用户访问 apiserver 的必要配置,包含 apiserver 地址、client 证书与 CA 证书等信息。下面介绍为各个组件生成 kubeconfig 的方法。

前提条件:

  • 我们使用 kubectl 来辅助生成 kubeconfig,确保 kubectl 已安装。
  • 生成 kubeconfig 会用到之前准备证书时创建的证书与密钥,确保这些生成的文件在当前目录。

确定 apiserver 访问入口

所有组件都会去连 apiserver,所以首先需要确定你的 apiserver 访问入口的地址:

  • 如果所有 master 组件都部署在一个节点,它们可以通过 127.0.0.1 这个 IP访问 apiserver。
  • 如果 master 有多个节点,但 apiserver 只有一个实例,可以直接写 apiserver 所在机器的内网 IP 访问地址。
  • 如果做了高可用,有多个 apiserver 实例,前面挂了负载均衡器,就可以写负载均衡器的访问地址。
  • 入口地址的域名或IP必须是在之前 为 kube-apiserver 签发证书 的 hosts 列表里。

这里我们用 APISERVER 这个变量表示 apiserver 的访问地址,其它组件都需要配置这个地址,根据自身情况改下这个变量的值:

APISERVER="https://10.200.16.79:6443"

为 kube-controller-manager 创建 kubeconfig

APISERVER="https://10.200.16.79:6443"
kubectl config set-cluster roc \
  --certificate-authority=ca.pem \
  --embed-certs=true \
  --server=${APISERVER} \
  --kubeconfig=kube-controller-manager.kubeconfig

kubectl config set-credentials system:kube-controller-manager \
  --client-certificate=kube-controller-manager.pem \
  --client-key=kube-controller-manager-key.pem \
  --embed-certs=true \
  --kubeconfig=kube-controller-manager.kubeconfig

kubectl config set-context default \
  --cluster=roc \
  --user=system:kube-controller-manager \
  --kubeconfig=kube-controller-manager.kubeconfig

kubectl config use-context default --kubeconfig=kube-controller-manager.kubeconfig

生成文件:

kube-controller-manager.kubeconfig

为 kube-scheduler 创建 kubeconfig

APISERVER="https://10.200.16.79:6443"
kubectl config set-cluster roc \
  --certificate-authority=ca.pem \
  --embed-certs=true \
  --server=${APISERVER} \
  --kubeconfig=kube-scheduler.kubeconfig

kubectl config set-credentials system:kube-scheduler \
  --client-certificate=kube-scheduler.pem \
  --client-key=kube-scheduler-key.pem \
  --embed-certs=true \
  --kubeconfig=kube-scheduler.kubeconfig

kubectl config set-context default \
  --cluster=roc \
  --user=system:kube-scheduler \
  --kubeconfig=kube-scheduler.kubeconfig

kubectl config use-context default --kubeconfig=kube-scheduler.kubeconfig

生成文件:

kube-scheduler.kubeconfig

为管理员创建 kubeconfig

这里为管理员生成 kubeconfig,方便使用 kubectl 来管理集群:

APISERVER="https://10.200.16.79:6443"
kubectl config set-cluster roc \
  --certificate-authority=ca.pem \
  --embed-certs=true \
  --server=${APISERVER} \
  --kubeconfig=admin.kubeconfig

kubectl config set-credentials admin \
  --client-certificate=admin.pem \
  --client-key=admin-key.pem \
  --embed-certs=true \
  --kubeconfig=admin.kubeconfig

kubectl config set-context default \
  --cluster=roc \
  --user=admin \
  --kubeconfig=admin.kubeconfig

kubectl config use-context default --kubeconfig=admin.kubeconfig

生成文件:

admin.kubeconfig

admin.kubeconfig 放到需要执行 kubectl 的机器的 ~/.kube/config 这个目录,这是 kubectl 读取 kubeconfig 的默认路径,执行 kubectl 时就不需要指定 kubeconfig 路径了:

mv admin.kubeconfig ~/.kube/config

下载安装控制面组件

wget -q --show-progress --https-only --timestamping \
  https://storage.googleapis.com/kubernetes-release/release/v1.16.1/bin/linux/amd64/kube-apiserver \
  https://storage.googleapis.com/kubernetes-release/release/v1.16.1/bin/linux/amd64/kube-controller-manager \
  https://storage.googleapis.com/kubernetes-release/release/v1.16.1/bin/linux/amd64/kube-scheduler

chmod +x kube-apiserver kube-controller-manager kube-scheduler
mv kube-apiserver kube-controller-manager kube-scheduler /usr/local/bin/

配置控制面组件

准备配置相关目录:

sudo mkdir -p /etc/kubernetes/config
sudo mkdir -p /var/lib/kubernetes

确定集群的集群网段 (Pod IP 占用网段)和 serivce 网段 (service 的 cluster ip 占用网段),它们可以没有交集。

记集群网段为 CLUSTER_CIDR:

CLUSTER_CIDR=10.10.0.0/16

记 service 网段为 SERVICE_CIDR:

SERVICE_CIDR=10.32.0.0/16

配置 kube-apiserver

放入证书文件:

sudo cp ca.pem ca-key.pem apiserver-key.pem apiserver.pem \
  service-account-key.pem service-account.pem /var/lib/kubernetes/

记所有 ETCD 实例的访问地址为 ETCD_SERVERS (替换 IP 为所有 ETCD 节点内网 IP):

ETCD_SERVERS="https://10.200.16.79:2379,https://10.200.17.6:2379,https://10.200.16.70:2379"

记当前节点内网 IP 为 INTERNAL_IP:

INTERNAL_IP=10.200.16.79

配置 systemd:

cat <<EOF | sudo tee /etc/systemd/system/kube-apiserver.service
[Unit]
Description=Kubernetes API Server
Documentation=https://github.com/kubernetes/kubernetes

[Service]
ExecStart=/usr/local/bin/kube-apiserver \\
  --enable-bootstrap-token-auth=true \\
  --advertise-address=${INTERNAL_IP} \\
  --allow-privileged=true \\
  --apiserver-count=3 \\
  --audit-log-maxage=30 \\
  --audit-log-maxbackup=3 \\
  --audit-log-maxsize=100 \\
  --audit-log-path=/var/log/audit.log \\
  --authorization-mode=Node,RBAC \\
  --bind-address=0.0.0.0 \\
  --client-ca-file=/var/lib/kubernetes/ca.pem \\
  --enable-admission-plugins=NamespaceLifecycle,NodeRestriction,LimitRanger,ServiceAccount,DefaultStorageClass,ResourceQuota \\
  --etcd-cafile=/var/lib/kubernetes/ca.pem \\
  --etcd-certfile=/var/lib/kubernetes/apiserver.pem \\
  --etcd-keyfile=/var/lib/kubernetes/apiserver-key.pem \\
  --etcd-servers=${ETCD_SERVERS} \\
  --event-ttl=1h \\
  --kubelet-certificate-authority=/var/lib/kubernetes/ca.pem \\
  --kubelet-client-certificate=/var/lib/kubernetes/apiserver.pem \\
  --kubelet-client-key=/var/lib/kubernetes/apiserver-key.pem \\
  --kubelet-https=true \\
  --runtime-config=api/all \\
  --service-account-key-file=/var/lib/kubernetes/service-account.pem \\
  --service-cluster-ip-range=${SERVICE_CIDR} \\
  --service-node-port-range=30000-32767 \\
  --tls-cert-file=/var/lib/kubernetes/apiserver.pem \\
  --tls-private-key-file=/var/lib/kubernetes/apiserver-key.pem \\
  --v=2
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target
EOF
  • --enable-bootstrap-token-auth=true 启用 bootstrap token 方式为 kubelet 签发证书

配置 kube-controller-manager

放入 kubeconfig:

sudo cp kube-controller-manager.kubeconfig /var/lib/kubernetes/

准备 systemd 配置 kube-controller-manager.service:

CLUSTER_CIDR=10.10.0.0/16
SERVICE_CIDR=10.32.0.0/16
cat <<EOF | sudo tee /etc/systemd/system/kube-controller-manager.service
[Unit]
Description=Kubernetes Controller Manager
Documentation=https://github.com/kubernetes/kubernetes

[Service]
ExecStart=/usr/local/bin/kube-controller-manager \\
  --address=0.0.0.0 \\
  --cluster-cidr=${CLUSTER_CIDR} \\
  --allocate-node-cidrs \\
  --cluster-name=kubernetes \\
  --cluster-signing-cert-file=/var/lib/kubernetes/ca.pem \\
  --cluster-signing-key-file=/var/lib/kubernetes/ca-key.pem \\
  --kubeconfig=/var/lib/kubernetes/kube-controller-manager.kubeconfig \\
  --leader-elect=true \\
  --root-ca-file=/var/lib/kubernetes/ca.pem \\
  --service-account-private-key-file=/var/lib/kubernetes/service-account-key.pem \\
  --service-cluster-ip-range=${SERVICE_CIDR} \\
  --use-service-account-credentials=true \\
  --v=2
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target
EOF

所有 kube-controller-manager 实例都使用相同的 systemd service 文件,可以直接将这里创建好的拷贝给其它 Master 节点

配置 kube-scheduler

放入 kubeconfig:

sudo cp kube-scheduler.kubeconfig /var/lib/kubernetes/

准备启动配置文件 kube-scheduler.yaml:

cat <<EOF | sudo tee /etc/kubernetes/config/kube-scheduler.yaml
apiVersion: kubescheduler.config.k8s.io/v1alpha1
kind: KubeSchedulerConfiguration
clientConnection:
  kubeconfig: "/var/lib/kubernetes/kube-scheduler.kubeconfig"
leaderElection:
  leaderElect: true
EOF

准备 systemd 配置 kube-scheduler.service:

cat <<EOF | sudo tee /etc/systemd/system/kube-scheduler.service
[Unit]
Description=Kubernetes Scheduler
Documentation=https://github.com/kubernetes/kubernetes

[Service]
ExecStart=/usr/local/bin/kube-scheduler \\
  --config=/etc/kubernetes/config/kube-scheduler.yaml \\
  --v=2
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target
EOF

启动

sudo systemctl daemon-reload
sudo systemctl enable kube-apiserver kube-controller-manager kube-scheduler
sudo systemctl start kube-apiserver kube-controller-manager kube-scheduler

RBAC 授权 kube-apiserver 访问 kubelet

kube-apiserver 有些情况也会访问 kubelet,比如获取 metrics、查看容器日志或登录容器,这是 kubelet 作为 server, kube-apiserver 作为 client,kubelet 监听的 https,kube-apiserver 经过证书认证访问 kubelet,但还需要经过授权才能成功调用接口,我们通过创建 RBAC 规则授权 kube-apiserver 访问 kubelet:

cat <<EOF | kubectl apply -f -
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRole
metadata:
  annotations:
    rbac.authorization.kubernetes.io/autoupdate: "true"
  labels:
    kubernetes.io/bootstrapping: rbac-defaults
  name: system:kube-apiserver-to-kubelet
rules:
  - apiGroups:
      - ""
    resources:
      - nodes/proxy
      - nodes/stats
      - nodes/log
      - nodes/spec
      - nodes/metrics
    verbs:
      - "*"
EOF

cat <<EOF | kubectl apply -f -
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
  name: system:kube-apiserver
  namespace: ""
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: system:kube-apiserver-to-kubelet
subjects:
  - apiGroup: rbac.authorization.k8s.io
    kind: User
    name: kubernetes
EOF

RBAC 授权 kubelet 创建 CSR 与自动签发和更新证书

节点 kubelet 通过 Bootstrap Token 调用 apiserver CSR API 请求签发证书,kubelet 通过 bootstrap token 认证后会在 system:bootstrappers 用户组里,我们还需要给它授权调用 CSR API,为这个用户组绑定预定义的 system:node-bootstrapper 这个 ClusterRole 就可以:

cat <<EOF | kubectl apply -f -
# enable bootstrapping nodes to create CSR
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: create-csrs-for-bootstrapping
subjects:
- kind: Group
  name: system:bootstrappers
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: system:node-bootstrapper
  apiGroup: rbac.authorization.k8s.io
EOF

这里的 CSR API 主要用于 kubelet 发起 client 和 server 证书签发请求

给 kubelet 授权自动审批通过 client 证书的 CSR 请求权限以实现自动创建新的 client 证书 (之前没创建过 client 证书,通过 bootstrap token 认证后在 system:bootstrappers 用户组里):

cat <<EOF | kubectl apply -f -
# Approve all CSRs for the group "system:bootstrappers"
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: auto-approve-csrs-for-group
subjects:
- kind: Group
  name: system:bootstrappers
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: system:certificates.k8s.io:certificatesigningrequests:nodeclient
  apiGroup: rbac.authorization.k8s.io
EOF

给已启动过的 kubelet 授权自动审批通过 server 证书的 CSR 请求权限以实现自动轮转 client 证书 (之前创建过证书,在证书还未过期前通过证书认证后在 system:nodes 用户组里):

cat <<EOF | kubectl apply -f -
# Approve renewal CSRs for the group "system:nodes"
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: auto-approve-renewals-for-nodes
subjects:
- kind: Group
  name: system:nodes
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: system:certificates.k8s.io:certificatesigningrequests:selfnodeclient
  apiGroup: rbac.authorization.k8s.io
EOF

注意上面两个授权都只是针对于 client 证书签发的自动审批权限,server 证书目前不支持自动审批,需要管理员通过 kubectl certificate approve <csr name> 来人工审批或者自己写外部 controller 来实现自动审批 (kubelet 访问 apiserver 使用 client 证书, apiserver 主动访问 kubelet 时才会用到 server 证书,通常用于获取 metrics 的场景)

创建 Bootstrap Token 与 bootstrap-kubeconfig

bootstrap token 用于 kubelet 自动请求签发证书,以 Secret 形式存储,不需要事先给 apiserver 配置静态 token,这样也易于管理。

创建了 bootstrap token 后我们利用它使用它来创建 bootstrap-kubeconfig 以供后面部署 Worker 节点用 (kubelet 使用 bootstrap-kubeconfig 自动创建证书),下面是创建方法:

APISERVER="https://10.200.16.79:6443"
# token id should match regex: [a-z0-9]{6}
TOKEN_ID=$(head -c 16 /dev/urandom | od -An -t x | tr -d ' ' | head -c 6)
# token secret should match regex: [a-z0-9]{16}
TOKEN_SECRET=$(head -c 16 /dev/urandom | od -An -t x | tr -d ' ' | head -c 16)

cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Secret
metadata:
  # Name MUST be of form "bootstrap-token-<token id>", 
  name: bootstrap-token-${TOKEN_ID}
  namespace: kube-system

# Type MUST be 'bootstrap.kubernetes.io/token'
type: bootstrap.kubernetes.io/token
stringData:
  # Human readable description. Optional.
  description: "The default bootstrap token used for signing certificates"

  # Token ID and secret. Required.
  token-id: "${TOKEN_ID}"
  token-secret: "${TOKEN_SECRET}"

  # Expiration. Optional.
  # expiration: 2020-03-10T03:22:11Z

  # Allowed usages.
  usage-bootstrap-authentication: "true"
  usage-bootstrap-signing: "true"

  # Extra groups to authenticate the token as. Must start with "system:bootstrappers:"
  # auth-extra-groups: system:bootstrappers:worker,system:bootstrappers:ingress
EOF

kubectl config --kubeconfig=bootstrap-kubeconfig set-cluster bootstrap --server="${APISERVER}" --certificate-authority=ca.pem --embed-certs=true
kubectl config --kubeconfig=bootstrap-kubeconfig set-credentials kubelet-bootstrap --token=${TOKEN_ID}.${TOKEN_SECRET}
kubectl config --kubeconfig=bootstrap-kubeconfig set-context bootstrap --user=kubelet-bootstrap --cluster=bootstrap
kubectl config --kubeconfig=bootstrap-kubeconfig use-context bootstrap

bootstrap token 的 secret 格式参考: https://kubernetes.io/docs/reference/access-authn-authz/bootstrap-tokens/#bootstrap-token-secret-format

生成文件:

bootstrap-kubeconfig

「真诚赞赏,手留余香」

roc

请我喝杯咖啡?

使用微信扫描二维码完成支付