kubernetes--RBAC权限管理

1、K8s安全框架

K8安全控制框架主要由下面3个阶段进行控制,每一个阶段都支持插件方式,通过API Server配置来启用插件

1.Authentication(鉴权)

2.Authorization(授权)

3..Admission Control(准入控制)

客户端要想要访问K8s集群API Server,一般需要证书、Token或者用户名+密码,如果Pod访问,需要ServiceAccount

门卫--鉴权 门禁卡--授权 准入控制--安全扫描设备

1.鉴权(Authentication)

K8s Apiserver提供三种客户端身份认证

1.HTTPS证书认证:基于CA证书签名的数字证书认证(kubeconfig)

2.HTTP Token认证:通过一个Token来识别用户(serviceaccount)

3.HTTP Base认证:用户名+密码的方式认证(1.19版本弃用)

2. 授权(Authorization)

RBAC(Role-Based AccessControl,基于角色的访问控制):负责完成授权(Authorization)工作

RBAC

根据API请求属性,决定允许还是拒绝。

比较常见的授权维度:

user:用户名

group:用户分组

资源,例如pod、deployment

资源操作方法:get,list,create,update,patch,watch,delete

命名空间

API组

3.准入控制(Admission Control)

1)方便用户扩展apiserver工具(对所有请求处理的功能)

AdminssionControl实际上是一个准入控制器插件列表,发送到API Server的请求都需要经过这个列表中的每个准入控制器插件的检查,检查不通过,则拒绝请求。

启用一个准入控制器(改配置):

kube-apiserver--enable-admission-plugins=NamespaceLifecycle,LimitRanger ...

关闭一个准入控制器:

kube-apiserver --disable-admission-plugins=PodNodeSelector,AlwaysDeny...

查看默认启用(默认大部分启用,已经够用):

【】kubectl exec kube-apiserver-k8s-master1-n kube-system -- kube-apiserver -h | grep enable-admission-plugins

2、RBAC授权案例

基于角色的权限访问控制:RBAC

RBAC(Role-Based Access Cobtrol,基于角色的访问控制),是K8s默认授权策略,并且是动态配置策略(修改即时生效)

主体(subject)

user:用户

group:用户组,是通过kubectl去访问

serviceaccount:服务账号,程序是直接访问apiserver

角色(是权限的集合):

绑定多个角色,取交集

k8s预定好了四个集群角色供用户使用,使用kubectl get clusterrole查看

其中systemd:开头的为系统内部使用

内置集群角色

描述

cluster-admin

超级管理员,对集群所有权限

admin

主要用于授权命名空间所有读写权限

edit

允许对命名空间大多数对象读写操作,不允许查看或者修改角色、角色绑定

view

允许对命名空间大多数对象只读权限,不允许查看角色、角色绑定和Secret

role:授权特点命名空间的访问权限

ClusterRole:授权所有命名空间的访问权限

角色绑定:

RoleBinding:将角色绑定到主体(即subject)

ClusterRoleBinding:将集群角色绑定到主体

注:RoleBinding在指定命名空间中执行授权,ClusterRoleBinding在集群范围内执行授权

案例1:对用户授权访问K8s(TLS证书)

用户、用户组都是提取证书中的一个字段。不是在集群中创建的!!都是在客户端上的。

基于·用户

需求:为指定用户授权访问不同命名空间权限,例如新入职一个小弟,希望让他先熟悉K8s集群,为了安全性,先不能给他太大权限,因此先给他授权访问default命名空间pod读取权限。实施大致步骤

1.用K8s用K8s CA签发客户端证书

2.生成kubeconfig授权文件

3.创建RBAC权限策略

4.指定kubeconfig文件测试权限: kubectl get pods --kubeconfig=./zhang.kubeconfig

  1. 用K8sCA签发客户端证书

cat > ca-config.json <<EOF
{
  "signing": {
    "default": {
      "expiry": "87600h"
    },
    "profiles": {
      "kubernetes": {
        "usages": [
            "signing",
            "key encipherment",
            "server auth",
            "client auth"
        ],
        "expiry": "87600h"
      }
    }
  }
}
EOF

cat > zhang-csr.json <<EOF
{
  "CN": "zhang",
  "hosts": [],
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "CN",
      "ST": "BeiJing",
      "L": "BeiJing",
      "O": "k8s",
      "OU": "System"
    }
  ]
}
EOF
#C对应用户O对应用户组
cfssl gencert -ca=/etc/kubernetes/pki/ca.crt -ca-key=/etc/kubernetes/pki/ca.key -config=ca-config.json -profile=kubernetes zhang-csr.json | cfssljson -bare zhang

字段说明:

ca-config.json:可以定义多个 profiles,分别指定不同的过期时间、使用场景等参数;后续在签名证书时使用某个 profile;

signing:表示该证书可以签名其他证书;生成的ca.pem证书中 CA=TRUE;

server auth:表示client可以用该 CA 对server提供的证书进行验证;

client auth:表示server可以用该CA对client提供的证书进行验证;

“CN”:Common Name,kube-apiserver 从证书中提取该字段作为请求的用户名 (User Name);浏览器使用该字段验证网站是否合法;

“O”:Organization,kube-apiserver 从证书中提取该字段作为请求用户所属的组 (Group)

2.生成kubeconfig授权文件

authority=/etc/kubernetes/pki/ca.crt \
  --embed-certs=true \
  --server=https://11.0.1.100:16443 \
  --kubeconfig=zhang.kubeconfig
 
# 设置客户端认证
kubectl config set-credentials zhang \
  --client-key=zhang-key.pem \
  --client-certificate=zhang.pem \
  --embed-certs=true \
  --kubeconfig=zhang.kubeconfig

# 设置默认上下文
kubectl config set-context kubernetes \
  --cluster=kubernetes \
  --user=zhang \
  --kubeconfig=zhang.kubeconfig

# 设置当前使用配置
kubectl config use-context kubernetes --kubeconfig=zhang.kubeconfig

#下面为生成的kubeconfig文件,看看就行,别执行
apiVersion: v1
clusters:
- cluster:
    certificate-authority-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUM1ekNDQWMrZ0F3SUJBZ0lCQURBTkJna3Foa2lHOXcwQkFRc0ZBREFWTVJNd0VRWURWUVFERXdwcmRXSmwKY201bGRHVnpNQjRYRFRJeU1USXhOREUwTVRZeU1Gb1hEVE15TVRJeE1URTBNVFl5TUZvd0ZURVRNQkVHQTFVRQpBeE1LYTNWaVpYSnVaWFJsY3pDQ0FTSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnRVBBRENDQVFvQ2dnRUJBTURxCkU1bW1OZVRXVFRRREZCQ3J4NTc5bis2dUVpcDR5TWxNd1BOa2RNM3VnOWVaaGFwNmJ5dmE2VzhtUUI1UkFCWk8KcFBoRUtzR2F3WDZiTlpmS0VWa2VxZ09RRmc2OE5Fc2xZc3hSazJqMlRhNkExUk4wNGpYdUUxeG1CK1JsVjB5dQpjRVRmVlYvaUJBZ1I5Mkg5OTFiQXQwVGVFVmFBWGI3c0NyaDFucVNBWDk4S3RENzQ1b0FybzdOQm1EOFMyTWlBCnlWY0didFdCRnNOUzBObSthSEViVGtaOFd2RmRwMW9aRlIvK2diZWtELzhHOGZBQnMwRnE3UUFMaUFnbFRwNDAKVTBCRnZnOVlnRmFTRm5hVCtZeWlFVEYxYWEzb25ZYVV5eGx4SkhRUkJYRlZjdFhUWkdrVzZvenVDYjhGc3JmNgpoSU8yZ1ZIRDdiNVVObEc0MXQ4Q0F3RUFBYU5DTUVBd0RnWURWUjBQQVFIL0JBUURBZ0trTUE4R0ExVWRFd0VCCi93UUZNQU1CQWY4d0hRWURWUjBPQkJZRUZOWlhqU2FGOTBEdUNRcWZXdjhwcXNTZGNWbW9NQTBHQ1NxR1NJYjMKRFFFQkN3VUFBNElCQVFBQ1lyejhBSU9OamlDUGpEZjcrVVd2aWUvVjZ3d3UvdjRzQjhxa1JpNlF0ckd4N25PVwpDMjZnK21yQ3p2eVdEM1FKZWhYaEwzQzhYRzN6L29sOGp1SDVxa1ppNDRCTHhYaGdaTTU1ZThDRDhBUHZJTFRwCjg2ZllLOVpneE81MGU0MkNyMmdlMTNsUDI2WCt2enRtWEJ1RDB1RGlOVVNXZVJSUFVoMERGbStkVEp1dEg3ZDUKWk1TdWFxeXN1dnFBaEdVeDhXeFRRNkpTeFhNZjNYWW9HRHQwaldUaGUrWTE1U0ZnUC9KQkRic1ZBcW1PYXpKRwpMR0FwVFZlR01KMjUxbWw0WUNYSi9rdktNT2RQdVBjSzluRkRSYktmYVhJaEM5T3hQditBSktRelFsbnVLYWlxCmJUQ2NXY2dCNGg3aHphcS9RWVhqWXJvaWduMWRzUDN2NFRQUwotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==
    server: https://11.0.1.100:16443
  name: kubernetes
contexts:
- context:
    cluster: kubernetes
    user: zhang
  name: kubernetes
current-context: kubernetes
kind: Config
preferences: {}
users:
- name: zhang
  user:
    client-certificate-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURnekNDQW11Z0F3SUJBZ0lVQlNxS1FhM3JGR2paZFk1bmxlOHVEVnMxaTR3d0RRWUpLb1pJaHZjTkFRRUwKQlFBd0ZURVRNQkVHQTFVRUF4TUthM1ZpWlhKdVpYUmxjekFlRncweU1qRXlNVGt4TkRRME1EQmFGdzB6TWpFeQpNVFl4TkRRME1EQmFNR0V4Q3pBSkJnTlZCQVlUQWtOT01SQXdEZ1lEVlFRSUV3ZENaV2xLYVc1bk1SQXdEZ1lEClZRUUhFd2RDWldsS2FXNW5NUXd3Q2dZRFZRUUtFd05yT0hNeER6QU5CZ05WQkFzVEJsTjVjM1JsYlRFUE1BMEcKQTFVRUF4TUdZV3hwWVc1bk1JSUJJakFOQmdrcWhraUc5dzBCQVFFRkFBT0NBUThBTUlJQkNnS0NBUUVBdmE5TgpOZWpCdDdSemRvWlBSSHpsQmh6TFV5eXNvd1YxdlRjaW83K3ArenJnNDduQUNLMXRmeUlFWFg2ZzhMaVF0Ym5ZCjMzZmcxODB6RFZ5NERSUFluYXgyYmo5U2YweDU1L0EwSjNBRmxuL1E4Y21SL3oybmtRU2w0RGI0bkQzQllodEQKN0lTNDJzY0JRSzRiWnYzYlBRREpkRjZCcHNPTlE2UTdIdStWU003b0NrS2ZJYjNoSFlZaUhwYUVreUszOEdDUgovT3orSFJ0ZmRPOGZ3Vzh4aVFUL21MRU1CQTZGRVNkZmFSY0cxWE9mcFBiNmhZTWpWU2pEL2Q4TDh0ejhqN2s3CldFd1ZxWnhzbWM4SXBCL2J2SjBncGNHdHJucSs1Z1NBOTF6aGRvNE9ncFBqQ3lBZnp4RmNuY1JmOXJjMmdDWUkKOUVYby95UTd0TWtxaFpORmZRSURBUUFCbzM4d2ZUQU9CZ05WSFE4QkFmOEVCQU1DQmFBd0hRWURWUjBsQkJZdwpGQVlJS3dZQkJRVUhBd0VHQ0NzR0FRVUZCd01DTUF3R0ExVWRFd0VCL3dRQ01BQXdIUVlEVlIwT0JCWUVGUHBHCmlxTmYySmQwc1ozZVBCOFQ2emJ4alpTcE1COEdBMVVkSXdRWU1CYUFGTlpYalNhRjkwRHVDUXFmV3Y4cHFzU2QKY1Ztb01BMEdDU3FHU0liM0RRRUJDd1VBQTRJQkFRQW9GVEJFcm9EOUN2TGhRR1paOE5xRnF3OTNadXJjRjVjSApOMEM1MEkzRWNsU0JJVEV3WExRRDVaUDdzNGxHZVhNNDZaQklTSWRXamIvRkd6RU5EbTdZeFRDM0dBYWQxSEFOClgwNENYTWNoT1JCOXRyU2NESHdDQlNWcU4rVXIvcW1oZWZDcE9RYU9EUkhZWFJjUloxUUVpNTZ1T2hjcVN5RmkKYW1sUy9sRTFoQkxGTXNHaXo1cmRNZllzTm44NHZHTmhFVStBakp5YXBuVTE5UE5za3dqejlUd1JzVUloMFdCNQpXK0libzhQZUNvYTFPdGdoanlYd3Zudy8rcDhoN2F2dC9jM253REJGKzVnblRoT2lnU21qUVpBN1VWVS9TeEFTCkpUY1ROcm8vMW1OcTVqa3ZMVHBqWVNVWk15VjNTd3ZrbkRiM0NIRUhlY0FjTlpzNE54dncKLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=
    client-key-data: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFb3dJQkFBS0NBUUVBdmE5Tk5lakJ0N1J6ZG9aUFJIemxCaHpMVXl5c293VjF2VGNpbzcrcCt6cmc0N25BCkNLMXRmeUlFWFg2ZzhMaVF0Ym5ZMzNmZzE4MHpEVnk0RFJQWW5heDJiajlTZjB4NTUvQTBKM0FGbG4vUThjbVIKL3oybmtRU2w0RGI0bkQzQllodEQ3SVM0MnNjQlFLNGJadjNiUFFESmRGNkJwc09OUTZRN0h1K1ZTTTdvQ2tLZgpJYjNoSFlZaUhwYUVreUszOEdDUi9PeitIUnRmZE84ZndXOHhpUVQvbUxFTUJBNkZFU2RmYVJjRzFYT2ZwUGI2CmhZTWpWU2pEL2Q4TDh0ejhqN2s3V0V3VnFaeHNtYzhJcEIvYnZKMGdwY0d0cm5xKzVnU0E5MXpoZG80T2dwUGoKQ3lBZnp4RmNuY1JmOXJjMmdDWUk5RVhvL3lRN3RNa3FoWk5GZlFJREFRQUJBb0lCQUYxdkxsOHhxY3B0cnJwaApha1pZSnhBZHV1SGJqYVkzbUVsK0VTZ0x6eFViYVkrQy9kN0lYcmxTN2tlSGlvdEl2cTlsUGpqRmVoR1MyR1RxCm1SYUlBaG1ZekRWK0o0cmhCdU9DNUkzUWxsQU40Y1JYLy94MFNFZ2N2QUIzMDNwU3lTRWtRUThPU1dncXdxa3oKbWZmZmI5RGF4R0QzOUhGS3ZoamxtdUdnTGk3UU16UVMrN1dBZDBodXBZWVBYa0p5NHFHN3hiUCtEUTVDVXlOSApJSzJiOXpLMGk1c2JZdXN4VHRjbmxMYUc5alZ2eDR6Y1JVd2xzVzNLK2pWeDBYM2tVL1hSTVRWNUlnWlNYQ1JjCks3UDVNU2hONXRnK2Mwb0xaV2V3dmppVjdJSy9sZ3JyamlaL0hGeDBybVhsWGI2MlRVeWIybWJSR2hiZUQ5R04KTWtzT3NSa0NnWUVBMVcwaFAwZkNGUkgwc0tGWEovU0tTVE1mNWhob3ZiVFFDK0VpWlhTRlI5ems0Uml1TTJpLwp5NXVYYlVSbEJVbEJCSnVHWmMvZk5uT1dsWDJZY3RnMCt5ZTc0aE8vOVVzRG51VERpQXFtcExYWmI4QTg4bVVECmtJbzNHeGM3eU54MjZQNUxJd2tVdEZ4ZWgyQUJwa0FkVGR6azdUMENkd3Z6Z1A3MjM4ak9UN01DZ1lFQTQ0WEkKV2tmY3BDNXA0MlJPNHV6bmVLM0JCRlRHKzY2OEJLajBoWHJqTG9BZGlBMTUvT3dJeDlNdWtLcGVNZHJSSXZ4WApOQW5CVVZQN3gzQWRMZmgyaWVIaDJ0TWd6ZUtBM2VVUk5pYUM3YVVtODNvRi93V29XaWZzSHJNNlVpOWh5MXo0ClNGZVZyckxvaTNpQ1FhYVUvZkpYRlNEcGt1aGphY2RPcHp0ei9nOENnWUJJVjNNN2hkRXRYeWEzY0JpZ3ZHemIKSHU4akNRY0h0bkFHZHNzMzVHVUExTWpuMjN2QWhUTHRpdEdyQWRIVUZ0NW85Mlo0d0JzQzArOG93S2VaMWRzdgpmbXBhMlNMcHZBR0FMRDV5c0ZtSDN2UFN3ekJsWVFQeVRFY2RjYVNjRXM1amJ6NThvNmxXOFRCQlpyTm9iTGNDCkcydWxWbGF0ajJsOFFlL2dMcXB2clFLQmdRRGRxTENRaEFKSlVZNkhvbW5ZV3NVSHBQK3VvSWxiR2RqREVYZG4KczFreExFK1pCTWdieURpbHRQQmdzQm9rcC9Hb0MvOUpDMWZ6UzhhM2ZscHZOdWtGVzdNVjVTcklhaXJ5QXdESgpxS3RWcXRoUHpFbWJNbi9abFp0TUxZQmNJUjA5YXMzWWJMdSsvejNlNHdERHhPN1NtUTZOM3dxdnZRZGRQbmM2CkpZblRzUUtCZ0FqZW1rMkJ1Sm9uTXBhTlJnaU9PcXFCZGNNZTgxMHc2UHlENmU5Y1I1Q1FWa0xGNmVva1RydmwKeHExajdWbDEwazNPbEFZTUU1ZTdOWTJSVCtTcTJWWlZMYkNWTHQwZE9LeW9yQUNnUDNURTRyM0thN3U5d2VUdApEcGs4WTVJcVMyRVlMTDhha0Ewc25rdHJqcWFqRTlHRzVlajErR1AvQzUvVkdLM2M2a2lmCi0tLS0tRU5EIFJTQSBQUklWQVRFIEtFWS0tLS0tCg==

3.创建RBAC权限策略

kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  namespace: default   #在默认命名空间下
  name: pod-reader
rules:
- apiGroups: ["","apps"]   #api组,例如apps组,控制表示是核心API组(V1),向namesoace,pod,service,pv,pvc都在里面
  resources: ["pods","deployments"] #资源名称(复数),例如pods、deploymenys、services
  verbs: ["get", "watch", "list"]  #资源操作方法

---
#将主体和角色绑定
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: read-pods
  namespace: default
subjects:
- kind: User  #主体
  name: zhang  #主体名称,对应证书的CN字段
  apiGroup: rbac.authorization.k8s.io
roleRef:  #绑定的角色
  kind: Role
  name: pod-reader  #角色名称
  apiGroup: rbac.authorization.k8s.io

发现只能查看pod,因为只对核心组授权了,添加一下deplotment

查看想要的资源属于那个组

【】kubectl api-resources |grep deploy

修改对应组即可

4.指定kubeconfig文件测试权限:

【】kubectl get pods --kubeconfig=./zhang.kubeconfig

基于用户组

用户组的好处是无需单独为某个用户创建权限,统一为组名进行授权,所有的用户都以组的身份访问资源。

例如:为dev用户组统一授权

1、将certs.sh文件中的zhang-csr.json下的O字段改为dev,并重新生成证书和kubconfig文件

2、将dev用户组绑定role(pod-reader)

3、测试,只要O字段都是dev,这些用户持有的kubeconfig文件都拥有相同权限

参考用户修改即可

案例2:对应用程序授权访问K8s(ServiceAccount)

什么是ServiceAccount

calico/coredns/dashboard >>都需要访问apiserver

简称SA,是一种用于让程序访问K8sAPI的服务账号

1.当创建namespace时,会自动创建一个名为default的SA,这个SA没有绑定任何权限

2.当default SA创建时,会自动创建一个default-token-xxx的secret,并自动关联到SA

3.当创建Pod时,如果没有指定SA,会自动为pod以volume方式挂载这个default SA ,在容器目录:/var/run/secrets/kubernets.io/serviceaccount

验证默认SA权限

【】kubectl--as=system:serviceaccount:default:default get pods

需求:授权容器中Python程序对K8sApi访问权限

1、创建role

2、创建serviceaccount

3、将service与role绑定

4、为pod指定自定义SA

5、进入容器里面执行python程序测试操作K8S Api权限

1-4

apiVersion: v1
kind: ServiceAccount 
metadata:
  name: py-k8s 
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: default
  name: py-role 
rules:
- apiGroups: [""]
  resources: ["pods"]
  verbs: ["get", "watch", "list"]
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: py-role 
  namespace: default
subjects:
- kind: ServiceAccount 
  name: py-k8s 
roleRef:
  kind: Role
  name: py-role 
  apiGroup: rbac.authorization.k8s.io
---
apiVersion: v1
kind: Pod
metadata:
  name: py-k8s 
spec:
  serviceAccountName: py-k8s 
  containers:
  - image: python:3
    name: python
    command:
    - sleep 
    - 24h

测试sa权限

【】kubectl--as=system:serviceaccount:default:py-k8s get pods

【】kubectl cp k8s-api-test.py py-k8s:/

【】kubectl exec -it py-k8s bash

#python3 k8s-api-test.py

会报错,无模块,用pip装一下

#pip install kubernetes -I 指定国内源,不加比较慢

from kubernetes import client, config

with open('/var/run/secrets/kubernetes.io/serviceaccount/token') as f:
     token = f.read()

configuration = client.Configuration()
configuration.host = "https://kubernetes"  # APISERVER地址
configuration.ssl_ca_cert="/var/run/secrets/kubernetes.io/serviceaccount/ca.crt"  # CA证书 
configuration.verify_ssl = True   # 启用证书验证
configuration.api_key = {"authorization": "Bearer " + token}  # 指定Token字符串
client.Configuration.set_default(configuration)
apps_api = client.AppsV1Api() 
core_api = client.CoreV1Api() 
try:
  print("###### Deployment列表 ######")
  #列出default命名空间所有deployment名称
  for dp in apps_api.list_namespaced_deployment("default").items:
    print(dp.metadata.name)
except:
  print("没有权限访问Deployment资源!")

try:
  #列出default命名空间所有pod名称
  print("###### Pod列表 ######")
  for po in core_api.list_namespaced_pod("default").items:
    print(po.metadata.name)
except:
  print("没有权限访问Pod资源!")

案列3:授权SA只能查看test命名空间控制器的权限

需求:

创建一个角色,具备查看控制器的权限。

创建一个SA,将SA绑定到角色。

命令行使用:授权SA只能查看test命名空间控制器的权限

# 创建角色

kubectl create role role-test--verb=get,list \

--resource=deployments,daemonsets,statefulsets-n test

# 创建服务账号

kubectl create serviceaccount app-demo -ntest

# 将服务账号绑定角色

kubectl create rolebinding role-test:app-demo\

--serviceaccount=test:app-demo--role=role-test -n test

# 测试

kubectl--as=system:serviceaccount:test:app-demo \

get pods -n test

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值