Vue监听store中数据变化的两种方式


2023年4月09日 21:58:01   2,255 次浏览

方式一

在Vue中,我们可以使用watch选项来监听数据的变化。下面是一种监听$store.state.userInfo.Name的变化的方式:

watch: {
  "$store.state.userInfo.Name":{
    handler:function(newVal,oldVal){
      console.log(newVal,oldVal);
    }
  }
}

 

#方式二

computed: {
  isEdit () {
    return this.$store.state.userInfo.Name;  //需要监听的数据
  }
},
watch: {
  isEdit(newVal, oldVal) {
    console.log(newVal,oldVal);
  }
}

 

#区别

::: info 区别一:第二种方式是先通过计算属性时刻监测store的数据变化,从而触发isEdit的监听函数,明显需要多一步 区别二:如果监听store的数据是一个对象,第一种方式只需要加上深度监听,也可以实现数据的变化监听,而第二种方式却无法监听到store的对象数据变化 :::

#例如

#第一种方式

watch: {
  //此时我监听的是对象,当$store.state.userInfo.Name发生修改时,此时需要深度监听才能监听到数据变化
  "$store.state.userInfo":{
    deep:true,//深度监听设置为 true
    handler:function(newVal,oldVal){
      console.log("数据发生变化啦"); //修改数据时,能看到输出结果
      console.log(newVal,oldVal);
    }
  }
}

 

#第二种方式

computed: {
  isEdit () {
    return this.$store.state.userInfo;  //需要监听的数据
  },
},
watch: {
  isEdit(newVal, oldVal) {
    console.log("数据发生变化啦"); //修改数据时,看不到该输出结果,因为无法监听到数据的变化
    console.log(newVal,oldVal);
  }
}

 

使用KCPTun中转加速境外服务器&多种方案


2023年4月05日 21:03:23   1,113 次浏览

前段时间研究了下多台中转服务器加速境外 SS 服务器方案,用的是 KCP 加速,当然没啥要求也可以用 iptables 端口转发,需要注意的是 kcptun 只支持单端口加速,如果需要加速多个端口,需要新建多条规则,KCP 还可以和 BBR 共同使用。

kcptun 简介

kcptun 是一个非常简单和快速的,基于 KCP 协议的 UDP 隧道,它可以将 TCP 流转换为 KCP + UDP 流。而 KCP 是一个快速可靠协议,能用比 TCP 浪费 10%-20% 的带宽的代价,换取平均延迟降低 30%-40%,延迟最大能能降低三倍。由于 kcptun 使用 Go 语言编写,内存占用低(能在 64M 内存服务器上稳定运行),而且适用于所有平台,包括 ARM 平台。

项目地址:
KCP协议
kcptun

kcptun 安装

注意服务端和客户端的版本最好一致,如果不一致可以去 kcptun 的项目地址中下载对应你系统的最新版。

服务端

  1. 这里采用一键脚本部署,SSH 连接服务器执行以下命令,CentOS 系统需要关闭防火墙或者放行相应端口
# CentOS 7+ / Debian 8+ / Ubuntu 16+
wget --no-check-certificate https://github.com/kuoruan/shell-scripts/raw/master/kcptun/kcptun.sh
chmod +x ./kcptun.sh
./kcptun.sh
  1. 上面的命令运行后,会有一系列参数设置项供选择,大部分回车选择默认即可,少部分需要自己设置,具体如下
  • 端口:默认是 29900,kcptun 客户端连接服务端使用的端口,可以保持默认,添加多条规则时需要修改
  • 加速的地址:默认为本机 127.0.0.1,加速其他服务器,需要填写其公网IP
  • 加速的端口:需要加速的端口,例如 Shadowsocks 服务端口
  • 密码:自己设置,不要使用默认密码,由于 kcptun 客户端连接
  • 加密方式选择:建议默认 aes,较强的加密方式会影响网速
  • 加速模式:默认 fast 即可,越快越浪费带宽
  • MTU:默认 1350 即可
  • sndwnd:发送窗口大小,与服务器的上传带宽大小有关,这项与 rcvwnd 的比例会影响加速效果,可以暂时设置为默认的512,不要大于你的本地宽带
  • rcvwnd:接收窗口大小,与服务器的下载带宽大小有关,可以暂设置为默认的 512 或 1024,不要大于你的本地宽带
  • 数据压缩:y,关闭数据压缩,可以一定程度上提升传输效率
  • 其他参数保持默认即可……
  1. 常用功能及命令
supervisorctl start kcptun  # 启动
supervisorctl stop kcptun  # 停止
supervisorctl restart kcptun  # 重启
supervisorctl status kcptun  # 状态
./kcptun.sh uninstall  # 卸载

客户端

这里只例举 Windows 和 Linux 客户端,其他见 kuptun 项目
kcptun:https://github.com/xtaci/kcptun/releases/

  1. Windows 客户端
    图形界面:https://github.com/dfdragon/kcptun_gclient/releases
    Windows 32位下载:windows-386,64位下载:windows-amd64
    将下载好的 kcptun 客户端解压,拖入 kcptun_gclient 目录中,点击右上角的浏览按钮,选择 client_windows_xxx.exe 的路径,最后根据服务端生成的配置信息,填写启动即可。 
  2. Linux 客户端
    根据你的系统架构下载对应的包,这里以 amd64 CentOS 7 举例,下载我打包好的 kcpclient 客户端,将目录中的 client_linux_amd64 替换为最新的版本,编辑 client-config.json 文件,修改为你的 kcptun 服务端连接信息,赋予 .sh 和 client_linux_amd64 文件执行权限,最后执行 bash start.sh 即可,如果要添加多条规则,新建多个 client-config.json 文件然后重启 kcpclient 即可 bash restart.sh

kcpclient:https://github.com/Fog-Forest/scripts/tree/main/kcptunclient

服务器架构

比较推荐使用一台中转服务器加速,直接加速效果可能不好,两台中转服务器成本太高

两台中转服务器加速

国内服务器部署 kcptun 客户端连接香港 kcptun 服务端,香港服务器部署 kcptun 服务端以及 BBR,加速境外服务器指定端口

一台中转服务器加速

有两种方案:
1. 中转服务器和境外服务器都部署 BBR,直接使用 iptables 端口转发即可
2. 境外服务器部署 BBR 和 kcptun 服务端,中转服务器部署 kcptun客户端,连接 kcptun服务端

直接加速

境外服务器部署 BBR 和 kcptun 服务端,用户端部署 kcptun 客户端,连接 kcptun 服务端即可

总结

使用 KCP 和端口转发的加速方案,提速效果明显,能够降低延迟,但是配置较为繁琐,如果有多台境外服务器用于 SS 的话,还可以配置负载均衡。

解决因磁盘空间耗尽导致的锁定或无法写入问题


2022年12月11日 22:42:37   896 次浏览

故障表现

  • 部署的应用程序突然无法将数据写入数据库,但是可以正常读取数据。
  • 通过Mongo Shell连接数据库进行排查时,测试写入一条数据,返回错误信息:not authorized on xxxx to execute command
db.customer.insert({"name":"zhangsan"})
WriteCommandError({
        "operationTime" : Timestamp(1563437183, 1),
        "ok" : 0,
        "errmsg" : "not authorized on db1 to execute command { insert: \"customer\", ordered: true, lsid: { id: UUID(\"8d43461c-5c51-49ef-b9b3-9xxxxxxxxf\") }, $clusterTime: { clusterTime: Timestamp(1563437183, 1), signature: { hash: BinData(0, 0C3FAAE747xxxxxx), keyId: 668293399xxxxxx } }, $db: \"db1\" }",
        "code" : 13,
        "codeName" : "Unauthorized",
        "$clusterTime" : {
                "clusterTime" : Timestamp(1563437183, 1),
                "signature" : {
                        "hash" : BinData(0,"DD+q50dPTuIQKTzytT5SiTPYX4Q="),
                        "keyId" : NumberLong("66xxxxxxxx")
                }
        }
})

 

查看磁盘空间使用率。本案例中,查看到Shard节点的磁盘空间的使用率超过了100%,由此可判断磁盘空间被耗尽

Linux下出现Read-only file system的解决办法


2022年9月09日 13:16:36   1,767 次浏览

问题描述

涉及到修改/保存条目等需要写磁盘操作的命令都无法使用(如tar、cp、mv、rm、chmod、chown、wget下载等指令),总是提示Read-only file system,也就是说系统是只读的,什么也写不了。

处理过程

1、查看/etc/fstab文件,在其中发现这样的一样记录(注意errors=remount-ro段),如下:

/dev/sda1 /ext3 errors=remount-ro 0 1

这种情况通常都是由于系统发现磁盘硬件故障或文件系统中文件被损坏之后而采取的保护机制导致的。为了保护数据不破坏分区中已有内容,Linux在挂载文件系统时就只用read-only只读方式加载了。至于挂载的文件系统为什么会莫名地变成以只读方式挂载的具体原因,这就不知道了。可能的原因有

a、系统文件损坏;

b、 磁盘有坏道;

c、fstab文件配置错误,如分区格式错误错误(将ntfs写成了fat)、配置指令拼写错误等。

如果能够确认数据和系统的文件没有被损坏,修复fstab文件配置后只要重新R/W加载或reboot就能够恢复正常。

以读写方式重新挂载文件系统

mount -o remount rw /

如果机器上有重要文件,在重新加载文件系统前可以用scp命令将其备份到远程主机上:

1 scp -r import_dir/import_file user@host:backup_dir

之所以使用scp -r命令备份重要目录/文件到远程主机上,而不用tar命令打包压缩后再传输,因为在用tar命令打包压缩文件/目录时会涉及到写磁盘操作,这会引起Read-only file system的错误。

如果是文件系统有问题,那就需要在umount状态下执行fsck命令来检查文件系统并修复文件系统中的错误。

nohup fsck -y /dev/VolGroup00/LogVol00 > /dev/shm/fscklog &

# 检查好后重启 reboot

如果是磁盘硬件损坏,最好的方法就是直接换一个新硬盘。如果你觉得旧硬盘扔了可惜,还可以将它低格之后,再重新安装系统,系统重新安装后,磁盘会重新分区。

如果仅仅是想将数据备份出来而且机器又在身边的话,你可以用live-cd从光盘启动系统,然后直接备份。当然此时你也可以修改硬盘中的配置文件,如/etc/fstab。

建议与总结

附:

另一种处理方法,方便简洁:

1、以读写方式重新挂载文件系统 mount -o remount rw /

2、在‘~’root用户的家目录下执行命令11 -a,到找.viminfo,在执行tar、cp、mv、rm、chmod、chown、wget命令,测试正常。

Kubernetes 环境下部署单节点 Redis


2022年6月03日 15:57:54   1,320 次浏览

创建namespace:  kubectl create namespace redis

创建configmap 存放redis配置文件信息

kind: ConfigMap
apiVersion: v1
metadata:
  name: redis-config
  namespace: redis
  labels:
    app: redis
data:
  redis.conf: |-
    dir /data
    port 6379
    bind 0.0.0.0
    appendonly yes
    protected-mode no
    requirepass yourpassword
    pidfile /data/redis-6379.pid

 

创建pvc存储

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: redis-pvc
  namespace: redis
  labels:
    app: redis
  annotations:
    volume.beta.kubernetes.io/storage-class: "nfs-storage"
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 2Gi

创建deployment

---
apiVersion: v1
kind: Service
metadata:
  name: redis-svc
  namespace: redis
  labels:
    app: redis
spec:
  type: ClusterIP
  ports:
    - name: redis
      port: 6379
  selector:
    app: redis
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: redis-dep
  namespace: redis
  labels:
    app: redis
spec:
  replicas: 1
  selector:
    matchLabels:
      app: redis
  template:
    metadata:
      labels:
        app: redis
    spec:
       initContainers:
        - name: system-init
          image: busybox:1.32
          imagePullPolicy: IfNotPresent
          command:
            - "sh"
            - "-c"
            - "echo 2048 > /proc/sys/net/core/somaxconn && echo never > /sys/kernel/mm/transparent_hugepage/enabled"
          securityContext:
            privileged: true
            runAsUser: 0
          volumeMounts:
          - name: sys
            mountPath: /sys
      containers:
        - name: redis
          image: redis:5.0.8
          command:
            - "sh"
            - "-c"
            - "redis-server /usr/local/etc/redis/redis.conf"
          ports:
            - containerPort: 6379
          resources:
            limits:
              cpu: 1000m
              memory: 1024Mi
            requests:
              cpu: 1000m
              memory: 1024Mi
          livenessProbe:
            tcpSocket:
              port: 6379
            initialDelaySeconds: 300
            timeoutSeconds: 1
            periodSeconds: 10
            successThreshold: 1
            failureThreshold: 3
          readinessProbe:
            tcpSocket:
              port: 6379
            initialDelaySeconds: 5
            timeoutSeconds: 1
            periodSeconds: 10
            successThreshold: 1
            failureThreshold: 3
          volumeMounts:
            - name: data
              mountPath: /data
            - name: config
              mountPath: /usr/local/etc/redis/redis.conf
              subPath: redis.conf
      volumes:
        - name: data
          persistentVolumeClaim:
            claimName: redis-pvc
        - name: config
          configMap:
            name: redis-config
        - name: sys
          hostPath:
            path: /sys

执行kubectl apply -f 应用资源

查看资源

[user@vm-centos-7 ~]$ kubectl get all -n redis
NAME                             READY   STATUS    RESTARTS   AGE
pod/redis-dep-77f876647c-g42vd   1/1     Running   1          23d

NAME                TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)    AGE
service/redis-svc   ClusterIP   10.97.105.16   <none>        6379/TCP   23d

NAME                        READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/redis-dep   1/1     1            1           23d

NAME                                   DESIRED   CURRENT   READY   AGE
replicaset.apps/redis-dep-77f876647c   1         1         1       23d

测试

使用如下步骤测试 redis 是否正常运行

[user@vm-centos-7 ~]$ kubectl -it exec redis-dep-77f876647c-g42vd -n redis -- sh
# redis-cli
127.0.0.1:6379> auth yourpassword
OK
127.0.0.1:6379> config get requirepass
1) "requirepass"
2) "yourpassword"
127.0.0.1:6379>

 

SLB实例压测请求504超时


2022年6月02日 15:06:49   1,170 次浏览

问题描述

对SLB实例进行压测,出现504状态码、请求超时的现象。压测的URL配置了HTTPS监听的URL转发策略,且该转发策略并没有启用健康检查。

问题原因

  1. 查看日志服务中的SLB实例日志,发现大部分请求都出现504状态码,但是upstream_response_time值都非常有规律,响应时间都是5秒,该情况是SLB与后端服务器TCP三次握手失败,导致连接超时抛出504状态码。

    说明:查看该日志需要您开通日志服务。

  2. 登录后端服务器,排查发现Nginx日志没有异常,但是messages日志存在“nf_conntrack: table full, dropping packet”错误。该信息是因为Linux系统为每个经过内核网络栈的数据包,都生成一个新的连接记录项,当服务器处理的连接过多时,连接跟踪表无法记录新的连接记录项,服务器会丢弃新建连接的数据包。所以导致SLB和后端服务器TCP三次握手失败,出现504状态码。

解决方案

  1. 建议调整nf_conntrack参数,调整命令如下所示,参数值请以实际情况为准。

    说明:该方法会临时修改参数,重启实例后配置会不生效。

    sysctl -w net.netfilter.nf_conntrack_max=1048576
    sysctl -w net.netfilter.nf_conntrack_buckets=262144
    sysctl -w net.netfilter.nf_conntrack_tcp_timeout_established=3600
  2. 确认压测正常即可。

kubeadm 更新证书


2022年3月03日 11:33:35   1,568 次浏览

安装 go 语言环境

Golang 官网下载地址:golang官网
打开官网下载地址选择对应的系统版本, 复制下载链接
这里我选择的是
go1.16.5.linux-amd64.tar.gz

下载解压

下载安装包

wget https://dl.google.com/go/go1.16.5.linux-amd64.tar.gz

解压到/usr/loacl目录下

tar -C /usr/local -zxvf  go1.16.5.linux-amd64.tar.gz

添加环境变量

添加/usr/loacl/go/bin 目录到 PATH 变量中。添加到 /etc/profile

vim /etc/profile 
# 在最后一行添加 
export GOROOT=/usr/local/go 
export PATH=$PATH:$GOROOT/bin 
# 保存退出后source一下 
source /etc/profile

验证

执行go version,如果现实版本号,则Go环境安装成功。

[root@master ~]# go version 
go version go1.16.5 linux/amd64

查看当前的证书时间

执行命令 查看当前证书时间

kubeadm alpha certs check-expiration

 

下载源码

打开github kubernetes 选择对应的版本下载
下载并解压
因为我是 v1.20.6 版本所以下载对应的

wget https://github.com/kubernetes/kubernetes/archive/refs/tags/v1.20.6.zip 
unzip v1.20.6.zip

修改 constants.go 文件
vim cmd/kubeadm/app/constants/constants.go 找到 CertificateValidity ,修改如下

cd kubernetes-1.20.6
vim cmd/kubeadm/app/constants/constants.go
....
const (
        // KubernetesDir is the directory Kubernetes owns for storing various configuration files
        KubernetesDir = "/etc/kubernetes"
        // ManifestsSubDirName defines directory name to store manifests
        ManifestsSubDirName = "manifests"
        // TempDirForKubeadm defines temporary directory for kubeadm
        // should be joined with KubernetesDir.
        TempDirForKubeadm = "tmp"

        // CertificateValidity defines the validity for all the signed certificates generated by kubeadm
        CertificateValidity = time.Hour * 24 * 365 * 100  #  修改此内容
....

编译 kubeadm

make WHAT=cmd/kubeadm

返回如下

[root@master kubernetes-1.20.6]# make WHAT=cmd/kubeadm

+++ [0624 10:59:21] Building go targets for linux/amd64:
    ./vendor/k8s.io/code-generator/cmd/prerelease-lifecycle-gen
+++ [0624 10:59:25] Building go targets for linux/amd64:
    ./vendor/k8s.io/code-generator/cmd/deepcopy-gen
+++ [0624 10:59:33] Building go targets for linux/amd64:
    ./vendor/k8s.io/code-generator/cmd/defaulter-gen
+++ [0624 10:59:44] Building go targets for linux/amd64:
    ./vendor/k8s.io/code-generator/cmd/conversion-gen
+++ [0624 11:00:04] Building go targets for linux/amd64:
    ./vendor/k8s.io/kube-openapi/cmd/openapi-gen
+++ [0624 11:00:19] Building go targets for linux/amd64:
    ./vendor/github.com/go-bindata/go-bindata/go-bindata
+++ [0624 11:00:20] Building go targets for linux/amd64:
    cmd/kubeadm

编译完生成如下目录和二进制文件

[root@master kubernetes-1.20.6]#  ll _output/bin/
总用量 75680
-rwxr-xr-x. 1 root root  5943296 6月  24 10:59 conversion-gen
-rwxr-xr-x. 1 root root  5689344 6月  24 10:59 deepcopy-gen
-rwxr-xr-x. 1 root root  5709824 6月  24 10:59 defaulter-gen
-rwxr-xr-x. 1 root root  3555111 6月  24 10:59 go2make
-rwxr-xr-x. 1 root root  1966080 6月  24 11:00 go-bindata
-rwxr-xr-x. 1 root root 39325696 6月  24 11:01 kubeadm
-rwxr-xr-x. 1 root root  9650176 6月  24 11:00 openapi-gen
-rwxr-xr-x. 1 root root  5656576 6月  24 10:59 prerelease-lifecycle-gen

备份文件

备份 kubeadm 和证书文件

cp /usr/bin/kubeadm{,.bak20210624} 
cp -r /etc/kubernetes/pki{,.bak20210624}

查看备份文件

[root@master kubernetes-1.20.6]# ll /usr/bin/kubeadm*
-rwxr-xr-x. 1 root root 39325696 6月  24 11:05 /usr/bin/kubeadm
-rwxr-xr-x. 1 root root 39210880 6月  24 11:02 /usr/bin/kubeadm.bak20210624

[root@master kubernetes-1.20.6 ll /etc/kubernetes/pki*
/etc/kubernetes/pki:
总用量 56
-rw-r--r--. 1 root root 1289 6月  24 11:05 apiserver.crt
-rw-r--r--. 1 root root 1139 6月  24 11:05 apiserver-etcd-client.crt
-rw-------. 1 root root 1675 6月  24 11:05 apiserver-etcd-client.key
-rw-------. 1 root root 1679 6月  24 11:05 apiserver.key
-rw-r--r--. 1 root root 1147 6月  24 11:05 apiserver-kubelet-client.crt
-rw-------. 1 root root 1675 6月  24 11:05 apiserver-kubelet-client.key
-rw-r--r--. 1 root root 1066 6月  22 15:01 ca.crt
-rw-------. 1 root root 1675 6月  22 15:01 ca.key
drwxr-xr-x. 2 root root  162 6月  22 15:01 etcd
-rwxr-xr-x. 1 root root 1078 6月  22 15:01 front-proxy-ca.crt
-rw-------. 1 root root 1675 6月  22 15:01 front-proxy-ca.key
-rw-r--r--. 1 root root 1103 6月  24 11:05 front-proxy-client.crt
-rw-------. 1 root root 1679 6月  24 11:05 front-proxy-client.key
-rw-------. 1 root root 1675 6月  22 15:01 sa.key
-rw-------. 1 root root  451 6月  22 15:01 sa.pub

/etc/kubernetes/pki.bak20210624:
总用量 56
-rw-r--r--. 1 root root 1289 6月  24 11:04 apiserver.crt
-rw-r--r--. 1 root root 1135 6月  24 11:04 apiserver-etcd-client.crt
-rw-------. 1 root root 1675 6月  24 11:04 apiserver-etcd-client.key
-rw-------. 1 root root 1679 6月  24 11:04 apiserver.key
-rw-r--r--. 1 root root 1143 6月  24 11:04 apiserver-kubelet-client.crt
-rw-------. 1 root root 1675 6月  24 11:04 apiserver-kubelet-client.key
-rw-r--r--. 1 root root 1066 6月  24 11:04 ca.crt
-rw-------. 1 root root 1675 6月  24 11:04 ca.key
drwxr-xr-x. 2 root root  162 6月  24 11:04 etcd
-rwxr-xr-x. 1 root root 1078 6月  24 11:04 front-proxy-ca.crt
-rw-------. 1 root root 1675 6月  24 11:04 front-proxy-ca.key
-rw-r--r--. 1 root root 1103 6月  24 11:04 front-proxy-client.crt
-rw-------. 1 root root 1679 6月  24 11:04 front-proxy-client.key
-rw-------. 1 root root 1675 6月  24 11:04 sa.key
-rw-------. 1 root root  451 6月  24 11:04 sa.pub

替换 kubeadm

将新生成的 kubeadm 进行替换

cp _output/bin/kubeadm /usr/bin/kubeadm

生成新的证书

cd /etc/kubernetes/pki kubeadm alpha certs renew all

返回内容

[root@master pki]# kubeadm alpha certs renew all
Command "all" is deprecated, please use the same command under "kubeadm certs"
[renew] Reading configuration from the cluster...
[renew] FYI: You can look at this config file with 'kubectl -n kube-system get cm kubeadm-config -o yaml'

certificate embedded in the kubeconfig file for the admin to use and for kubeadm itself renewed
certificate for serving the Kubernetes API renewed
certificate the apiserver uses to access etcd renewed
certificate for the API server to connect to kubelet renewed
certificate embedded in the kubeconfig file for the controller manager to use renewed
certificate for liveness probes to healthcheck etcd renewed
certificate for etcd nodes to communicate with each other renewed
certificate for serving etcd renewed
certificate for the front proxy client renewed
certificate embedded in the kubeconfig file for the scheduler manager to use renewed

Done renewing certificates. You must restart the kube-apiserver, kube-controller-manager, kube-scheduler and etcd, so that they can use the new certificates.

 

验证结果

到这里,证书就替换完成了。接下来验证下证书时间是否延长。

kubeadm alpha certs check-expiration

返回信息

[root@master pki]# kubeadm alpha certs check-expiration
Command "check-expiration" is deprecated, please use the same command under "kubeadm certs"
[check-expiration] Reading configuration from the cluster...
[check-expiration] FYI: You can look at this config file with 'kubectl -n kube-system get cm kubeadm-config -o yaml'

CERTIFICATE                EXPIRES                  RESIDUAL TIME   CERTIFICATE AUTHORITY   EXTERNALLY MANAGED
admin.conf                 May 31, 2121 03:05 UTC   99y                                     no      
apiserver                  May 31, 2121 03:05 UTC   99y             ca                      no      
apiserver-etcd-client      May 31, 2121 03:05 UTC   99y             etcd-ca                 no      
apiserver-kubelet-client   May 31, 2121 03:05 UTC   99y             ca                      no      
controller-manager.conf    May 31, 2121 03:05 UTC   99y                                     no      
etcd-healthcheck-client    May 31, 2121 03:05 UTC   99y             etcd-ca                 no      
etcd-peer                  May 31, 2121 03:05 UTC   99y             etcd-ca                 no      
etcd-server                May 31, 2121 03:05 UTC   99y             etcd-ca                 no      
front-proxy-client         May 31, 2121 03:05 UTC   99y             front-proxy-ca          no      
scheduler.conf             May 31, 2121 03:05 UTC   99y                                     no      

CERTIFICATE AUTHORITY   EXPIRES                  RESIDUAL TIME   EXTERNALLY MANAGED
ca                      Jun 20, 2031 07:01 UTC   9y              no      
etcd-ca                 Jun 20, 2031 07:01 UTC   9y              no      
front-proxy-ca          Jun 20, 2031 07:01 UTC   9y              no      

查看 node 状态

[root@master pki]#  kubectl get node
NAME     STATUS   ROLES                  AGE   VERSION
master   Ready    control-plane,master   44h   v1.20.6
node1    Ready    <none>                 43h   v1.20.6

 

Zabbix历史数据处理


2022年3月03日 11:19:55   1,917 次浏览

问题描述

zabbix server 平稳运行有一段时间了,但是最近问题却来了,今天早上收到zabbixserver磁盘空间不足的告警。通过查看之后发现是大部分数据是zabbix 库的的数据 在进一步查看发现是history表和history_uint数据太多导致磁盘占用过多。

问题分析

history_uint

该表存储的是监控项的无符号整型的数据。
该数据的保存时长,取决于在监控项设置的 历史数据保留时长。

history

这个表保存的是浮点型的。
像 history_str 等保存的是 字符型数据。这些都是我们在设置监控项的对应的信息类型决定的。
该数据的保存时长,取决于在监控项设置的 历史数据保留时长

针对这个问题,我打算删除 history_uint 和 history 的一些历史数据。
要删除history_uint里的数据,还需要注意一点,由于数据量比较多,我建议可以分多次少量数据进行删除,因为我一次删除90天的时候CPU已经吃不消了…
这样可以避免一次性删除数据过多导致数据库的负载比较大。(或者可以使用limit 10000)

处理过程

我这里需要删除90天以前的数据下面是我的操作过程

获取时间戳

#通过如下命令进行获取90天以前的时间戳

[root@zabbix-server ~]# date -d $(date -d "-90 day" +%Y%m%d) +%s 1590105600

登录数据库操作

[root@zabbix-server ~]# mysql -uzabbix -p 
Enter password: mysql> use zabbix; 
Database changed 

#delete history_uint 
mysql> delete from history_uint where clock < 1590105600 LIMIT 10000; 
Query OK, 1653 rows affected (1 min 45.42 sec) 

#delete history 
mysql> delete from history where clock < 1590105600 LIMIT 10000; 
Query OK, 0 rows affected (24.72 sec)

释放空间

上面执行删除后,数据的存储空间是没有减少的,因为对于delete from table_name where xxx 带条件的删除,不管是innodb还是MyISAM都不会释放空间,需要进行OPTIMIZE TABLE操作,进行释放空间。

注意:在optimize table ‘表名’ 运行过程中,MySQL会进行锁表。
optimize table history_uin

mysql>  optimize table history_uint;
+---------------------+----------+----------+-------------------------------------------------------------------+
| Table               | Op       | Msg_type | Msg_text                                                          |
+---------------------+----------+----------+-------------------------------------------------------------------+
| zabbix.history_uint | optimize | note     | Table does not support optimize, doing recreate + analyze instead |
| zabbix.history_uint | optimize | status   | OK                                                                |
+---------------------+----------+----------+-------------------------------------------------------------------+
2 rows in set (5 min 33.76 sec)

optimize table history

mysql>  optimize table history;
+----------------+----------+----------+-------------------------------------------------------------------+
| Table          | Op       | Msg_type | Msg_text                                                          |
+----------------+----------+----------+-------------------------------------------------------------------+
| zabbix.history | optimize | note     | Table does not support optimize, doing recreate + analyze instead |
| zabbix.history | optimize | status   | OK                                                                |
+----------------+----------+----------+-------------------------------------------------------------------+
2 rows in set (1 min 39.51 sec)

问题解决

待以上步骤都完成以后,检查磁盘可以看到问题解决 。
不过想要一劳永益的话的话 还是需要写一个脚本来处理这个问题

#!/bin/bash
User="zabbix"
Passwd="zabbix"
Date=`date -d $(date -d "-90 day" +%Y%m%d) +%s`
$(which mysql) -u${User} -p${Passwd} -e "
use zabbix;
DELETE FROM history WHERE 'clock' < '$Date' LIMIT 10000;
optimize table history;
DELETE FROM history_str WHERE 'clock' < '$Date' LIMIT 10000;
optimize table history_str;
DELETE FROM history_uint WHERE 'clock' < '$Date' LIMIT 10000;
optimize table history_uint;
DELETE FROM history_text WHERE 'clock' < $Date' LIMIT 10000;
optimize table history_text;
DELETE FROM  trends WHERE 'clock' < '$Date' LIMIT 10000;
optimize table  trends;
DELETE FROM trends_uint WHERE 'clock' < '$Date' LIMIT 10000;
optimize table trends_uint;
DELETE FROM events WHERE 'clock' < '$Date' LIMIT 10000;
optimize table events;
"

 

另外历史数据过多是由于我们保存的历史数据的时间所致,我们可以根据需求设置历史数据的保留时长,例如一些相对不太重要的数据,我们可以将该值设置的更短一些,这样数据量也就随着减少了。

CentOS 7 OpenVPN一键安装脚本


2022年3月03日 11:06:46   6,102 次浏览

为了避免数据库服务器等内网应用资源暴露到公网中,打算利用VPN 技术实现链接到内网。
本文主要介绍CentOS 7 服务器上安装与配置OpenVPN服务器,以及如何编写客户端连接到新建立的OpenVPN服务器上所需的配置文件

OpenVPN的介绍

OpenVPN是一个开源的应用程序,它允许您通过公共互联网创建一个安全的专用网络。OpenVPN实现一个虚拟专用网(VPN)来创建一个安全连接。OpenVPN使用OpenSSL库提供加密,它提供了几种身份验证机制,如基于证书的、预共享密钥和用户名/密码身份验证。

openvpn 有两种模式

数据包(TUN模式)或数据帧(TAP模式)

  • TUN模式:TUN模拟了网络层设备,第三层数据包如IP封包,底层数据隧道数据
  • TAP模式等同于一个设备,第二操作层数据包如扩展数据帧,创建一个相对桥接接,复杂T

接口接口的好处可见,客户端优化VPN服务子网的IP(忽然忽隐忽现)物理上的区别,可以完全将客户端看做完全与VPN服务器相关的时间,而TUN接口下所有的客户端则出现一个独立的子网内,与VPN服务器相关的子网没有关系,这种使用比较好,和公司的网络区分开,完全是一个虚拟的网络

脚本内容

openvpn 一键安装脚本

#!/bin/bash
#
# https://github.com/Nyr/openvpn-install
#
# Copyright (c) 2013 Nyr. Released under the MIT License.


# Detect Debian users running the script with "sh" instead of bash
if readlink /proc/$$/exe | grep -q "dash"; then
    echo 'This installer needs to be run with "bash", not "sh".'
    exit
fi

# Discard stdin. Needed when running from an one-liner which includes a newline
read -N 999999 -t 0.001

# Detect OpenVZ 6
if [[ $(uname -r | cut -d "." -f 1) -eq 2 ]]; then
    echo "The system is running an old kernel, which is incompatible with this installer."
    exit
fi

# Detect OS
# $os_version variables aren't always in use, but are kept here for convenience
if grep -qs "ubuntu" /etc/os-release; then
    os="ubuntu"
    os_version=$(grep 'VERSION_ID' /etc/os-release | cut -d '"' -f 2 | tr -d '.')
    group_name="nogroup"
elif [[ -e /etc/debian_version ]]; then
    os="debian"
    os_version=$(grep -oE '[0-9]+' /etc/debian_version | head -1)
    group_name="nogroup"
elif [[ -e /etc/centos-release ]]; then
    os="centos"
    os_version=$(grep -oE '[0-9]+' /etc/centos-release | head -1)
    group_name="nobody"
elif [[ -e /etc/fedora-release ]]; then
    os="fedora"
    os_version=$(grep -oE '[0-9]+' /etc/fedora-release | head -1)
    group_name="nobody"
else
    echo "This installer seems to be running on an unsupported distribution.
Supported distributions are Ubuntu, Debian, CentOS, and Fedora."
    exit
fi

if [[ "$os" == "ubuntu" && "$os_version" -lt 1804 ]]; then
    echo "Ubuntu 18.04 or higher is required to use this installer.
This version of Ubuntu is too old and unsupported."
    exit
fi

if [[ "$os" == "debian" && "$os_version" -lt 9 ]]; then
    echo "Debian 9 or higher is required to use this installer.
This version of Debian is too old and unsupported."
    exit
fi

if [[ "$os" == "centos" && "$os_version" -lt 7 ]]; then
    echo "CentOS 7 or higher is required to use this installer.
This version of CentOS is too old and unsupported."
    exit
fi

# Detect environments where $PATH does not include the sbin directories
if ! grep -q sbin <<< "$PATH"; then
    echo '$PATH does not include sbin. Try using "su -" instead of "su".'
    exit
fi

if [[ "$EUID" -ne 0 ]]; then
    echo "This installer needs to be run with superuser privileges."
    exit
fi

if [[ ! -e /dev/net/tun ]] || ! ( exec 7<>/dev/net/tun ) 2>/dev/null; then
    echo "The system does not have the TUN device available.
TUN needs to be enabled before running this installer."
    exit
fi

new_client () {
    # Generates the custom client.ovpn
    {
    cat /etc/openvpn/server/client-common.txt
    echo "<ca>"
    cat /etc/openvpn/server/easy-rsa/pki/ca.crt
    echo "</ca>"
    echo "<cert>"
    sed -ne '/BEGIN CERTIFICATE/,$ p' /etc/openvpn/server/easy-rsa/pki/issued/"$client".crt
    echo "</cert>"
    echo "<key>"
    cat /etc/openvpn/server/easy-rsa/pki/private/"$client".key
    echo "</key>"
    echo "<tls-crypt>"
    sed -ne '/BEGIN OpenVPN Static key/,$ p' /etc/openvpn/server/tc.key
    echo "</tls-crypt>"
    } > ~/"$client".ovpn
}

if [[ ! -e /etc/openvpn/server/server.conf ]]; then
    clear
    echo 'Welcome to this OpenVPN road warrior installer!'
    # If system has a single IPv4, it is selected automatically. Else, ask the user
    if [[ $(ip -4 addr | grep inet | grep -vEc '127(\.[0-9]{1,3}){3}') -eq 1 ]]; then
        ip=$(ip -4 addr | grep inet | grep -vE '127(\.[0-9]{1,3}){3}' | cut -d '/' -f 1 | grep -oE '[0-9]{1,3}(\.[0-9]{1,3}){3}')
    else
        number_of_ip=$(ip -4 addr | grep inet | grep -vEc '127(\.[0-9]{1,3}){3}')
        echo
        echo "Which IPv4 address should be used?"
        ip -4 addr | grep inet | grep -vE '127(\.[0-9]{1,3}){3}' | cut -d '/' -f 1 | grep -oE '[0-9]{1,3}(\.[0-9]{1,3}){3}' | nl -s ') '
        read -p "IPv4 address [1]: " ip_number
        until [[ -z "$ip_number" || "$ip_number" =~ ^[0-9]+$ && "$ip_number" -le "$number_of_ip" ]]; do
            echo "$ip_number: invalid selection."
            read -p "IPv4 address [1]: " ip_number
        done
        [[ -z "$ip_number" ]] && ip_number="1"
        ip=$(ip -4 addr | grep inet | grep -vE '127(\.[0-9]{1,3}){3}' | cut -d '/' -f 1 | grep -oE '[0-9]{1,3}(\.[0-9]{1,3}){3}' | sed -n "$ip_number"p)
    fi
    # If $ip is a private IP address, the server must be behind NAT
    if echo "$ip" | grep -qE '^(10\.|172\.1[6789]\.|172\.2[0-9]\.|172\.3[01]\.|192\.168)'; then
        echo
        echo "This server is behind NAT. What is the public IPv4 address or hostname?"
        # Get public IP and sanitize with grep
        get_public_ip=$(grep -m 1 -oE '^[0-9]{1,3}(\.[0-9]{1,3}){3}$' <<< "$(wget -T 10 -t 1 -4qO- "http://ip1.dynupdate.no-ip.com/" || curl -m 10 -4Ls "http://ip1.dynupdate.no-ip.com/")")
        read -p "Public IPv4 address / hostname [$get_public_ip]: " public_ip
        # If the checkip service is unavailable and user didn't provide input, ask again
        until [[ -n "$get_public_ip" || -n "$public_ip" ]]; do
            echo "Invalid input."
            read -p "Public IPv4 address / hostname: " public_ip
        done
        [[ -z "$public_ip" ]] && public_ip="$get_public_ip"
    fi
    # If system has a single IPv6, it is selected automatically
    if [[ $(ip -6 addr | grep -c 'inet6 [23]') -eq 1 ]]; then
        ip6=$(ip -6 addr | grep 'inet6 [23]' | cut -d '/' -f 1 | grep -oE '([0-9a-fA-F]{0,4}:){1,7}[0-9a-fA-F]{0,4}')
    fi
    # If system has multiple IPv6, ask the user to select one
    if [[ $(ip -6 addr | grep -c 'inet6 [23]') -gt 1 ]]; then
        number_of_ip6=$(ip -6 addr | grep -c 'inet6 [23]')
        echo
        echo "Which IPv6 address should be used?"
        ip -6 addr | grep 'inet6 [23]' | cut -d '/' -f 1 | grep -oE '([0-9a-fA-F]{0,4}:){1,7}[0-9a-fA-F]{0,4}' | nl -s ') '
        read -p "IPv6 address [1]: " ip6_number
        until [[ -z "$ip6_number" || "$ip6_number" =~ ^[0-9]+$ && "$ip6_number" -le "$number_of_ip6" ]]; do
            echo "$ip6_number: invalid selection."
            read -p "IPv6 address [1]: " ip6_number
        done
        [[ -z "$ip6_number" ]] && ip6_number="1"
        ip6=$(ip -6 addr | grep 'inet6 [23]' | cut -d '/' -f 1 | grep -oE '([0-9a-fA-F]{0,4}:){1,7}[0-9a-fA-F]{0,4}' | sed -n "$ip6_number"p)
    fi
    echo
    echo "Which protocol should OpenVPN use?"
    echo "   1) UDP (recommended)"
    echo "   2) TCP"
    read -p "Protocol [1]: " protocol
    until [[ -z "$protocol" || "$protocol" =~ ^[12]$ ]]; do
        echo "$protocol: invalid selection."
        read -p "Protocol [1]: " protocol
    done
    case "$protocol" in
        1|"") 
        protocol=udp
        ;;
        2) 
        protocol=tcp
        ;;
    esac
    echo
    echo "What port should OpenVPN listen to?"
    read -p "Port [1194]: " port
    until [[ -z "$port" || "$port" =~ ^[0-9]+$ && "$port" -le 65535 ]]; do
        echo "$port: invalid port."
        read -p "Port [1194]: " port
    done
    [[ -z "$port" ]] && port="1194"
    echo
    echo "Select a DNS server for the clients:"
    echo "   1) Current system resolvers"
    echo "   2) Google"
    echo "   3) 1.1.1.1"
    echo "   4) OpenDNS"
    echo "   5) Quad9"
    echo "   6) AdGuard"
    read -p "DNS server [1]: " dns
    until [[ -z "$dns" || "$dns" =~ ^[1-6]$ ]]; do
        echo "$dns: invalid selection."
        read -p "DNS server [1]: " dns
    done
    echo
    echo "Enter a name for the first client:"
    read -p "Name [client]: " unsanitized_client
    # Allow a limited set of characters to avoid conflicts
    client=$(sed 's/[^0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-]/_/g' <<< "$unsanitized_client")
    [[ -z "$client" ]] && client="client"
    echo
    echo "OpenVPN installation is ready to begin."
    # Install a firewall in the rare case where one is not already available
    if ! systemctl is-active --quiet firewalld.service && ! hash iptables 2>/dev/null; then
        if [[ "$os" == "centos" || "$os" == "fedora" ]]; then
            firewall="firewalld"
            # We don't want to silently enable firewalld, so we give a subtle warning
            # If the user continues, firewalld will be installed and enabled during setup
            echo "firewalld, which is required to manage routing tables, will also be installed."
        elif [[ "$os" == "debian" || "$os" == "ubuntu" ]]; then
            # iptables is way less invasive than firewalld so no warning is given
            firewall="iptables"
        fi
    fi
    read -n1 -r -p "Press any key to continue..."
    # If running inside a container, disable LimitNPROC to prevent conflicts
    if systemd-detect-virt -cq; then
        mkdir /etc/systemd/system/openvpn-server@server.service.d/ 2>/dev/null
        echo "[Service]
LimitNPROC=infinity" > /etc/systemd/system/openvpn-server@server.service.d/disable-limitnproc.conf
    fi
    if [[ "$os" = "debian" || "$os" = "ubuntu" ]]; then
        apt-get update
        apt-get install -y openvpn openssl ca-certificates $firewall
    elif [[ "$os" = "centos" ]]; then
        yum install -y epel-release
        yum install -y openvpn openssl ca-certificates tar $firewall
    else
        # Else, OS must be Fedora
        dnf install -y openvpn openssl ca-certificates tar $firewall
    fi
    # If firewalld was just installed, enable it
    if [[ "$firewall" == "firewalld" ]]; then
        systemctl enable --now firewalld.service
    fi
    # Get easy-rsa
    easy_rsa_url='https://github.com/OpenVPN/easy-rsa/releases/download/v3.0.8/EasyRSA-3.0.8.tgz'
    mkdir -p /etc/openvpn/server/easy-rsa/
    { wget -qO- "$easy_rsa_url" 2>/dev/null || curl -sL "$easy_rsa_url" ; } | tar xz -C /etc/openvpn/server/easy-rsa/ --strip-components 1
    chown -R root:root /etc/openvpn/server/easy-rsa/
    cd /etc/openvpn/server/easy-rsa/
    # Create the PKI, set up the CA and the server and client certificates
    ./easyrsa init-pki
    ./easyrsa --batch build-ca nopass
    EASYRSA_CERT_EXPIRE=3650 ./easyrsa build-server-full server nopass
    EASYRSA_CERT_EXPIRE=3650 ./easyrsa build-client-full "$client" nopass
    EASYRSA_CRL_DAYS=3650 ./easyrsa gen-crl
    # Move the stuff we need
    cp pki/ca.crt pki/private/ca.key pki/issued/server.crt pki/private/server.key pki/crl.pem /etc/openvpn/server
    # CRL is read with each client connection, while OpenVPN is dropped to nobody
    chown nobody:"$group_name" /etc/openvpn/server/crl.pem
    # Without +x in the directory, OpenVPN can't run a stat() on the CRL file
    chmod o+x /etc/openvpn/server/
    # Generate key for tls-crypt
    openvpn --genkey --secret /etc/openvpn/server/tc.key
    # Create the DH parameters file using the predefined ffdhe2048 group
    echo '-----BEGIN DH PARAMETERS-----
MIIBCAKCAQEA//////////+t+FRYortKmq/cViAnPTzx2LnFg84tNpWp4TZBFGQz
+8yTnc4kmz75fS/jY2MMddj2gbICrsRhetPfHtXV/WVhJDP1H18GbtCFY2VVPe0a
87VXE15/V8k1mE8McODmi3fipona8+/och3xWKE2rec1MKzKT0g6eXq8CrGCsyT7
YdEIqUuyyOP7uWrat2DX9GgdT0Kj3jlN9K5W7edjcrsZCwenyO4KbXCeAvzhzffi
7MA0BM0oNC9hkXL+nOmFg/+OTxIy7vKBg8P+OxtMb61zO7X8vC7CIAXFjvGDfRaD
ssbzSibBsu/6iGtCOGEoXJf//////////wIBAg==
-----END DH PARAMETERS-----' > /etc/openvpn/server/dh.pem
    # Generate server.conf
    echo "local $ip
port $port
proto $protocol
dev tun
ca ca.crt
cert server.crt
key server.key
dh dh.pem
auth SHA512
tls-crypt tc.key
topology subnet
server 10.8.0.0 255.255.255.0" > /etc/openvpn/server/server.conf
    # IPv6
    if [[ -z "$ip6" ]]; then
        echo 'push "redirect-gateway def1 bypass-dhcp"' >> /etc/openvpn/server/server.conf
    else
        echo 'server-ipv6 fddd:1194:1194:1194::/64' >> /etc/openvpn/server/server.conf
        echo 'push "redirect-gateway def1 ipv6 bypass-dhcp"' >> /etc/openvpn/server/server.conf
    fi
    echo 'ifconfig-pool-persist ipp.txt' >> /etc/openvpn/server/server.conf
    # DNS
    case "$dns" in
        1|"")
            # Locate the proper resolv.conf
            # Needed for systems running systemd-resolved
            if grep -q '^nameserver 127.0.0.53' "/etc/resolv.conf"; then
                resolv_conf="/run/systemd/resolve/resolv.conf"
            else
                resolv_conf="/etc/resolv.conf"
            fi
            # Obtain the resolvers from resolv.conf and use them for OpenVPN
            grep -v '^#\|^;' "$resolv_conf" | grep '^nameserver' | grep -oE '[0-9]{1,3}(\.[0-9]{1,3}){3}' | while read line; do
                echo "push \"dhcp-option DNS $line\"" >> /etc/openvpn/server/server.conf
            done
        ;;
        2)
            echo 'push "dhcp-option DNS 8.8.8.8"' >> /etc/openvpn/server/server.conf
            echo 'push "dhcp-option DNS 8.8.4.4"' >> /etc/openvpn/server/server.conf
        ;;
        3)
            echo 'push "dhcp-option DNS 1.1.1.1"' >> /etc/openvpn/server/server.conf
            echo 'push "dhcp-option DNS 1.0.0.1"' >> /etc/openvpn/server/server.conf
        ;;
        4)
            echo 'push "dhcp-option DNS 208.67.222.222"' >> /etc/openvpn/server/server.conf
            echo 'push "dhcp-option DNS 208.67.220.220"' >> /etc/openvpn/server/server.conf
        ;;
        5)
            echo 'push "dhcp-option DNS 9.9.9.9"' >> /etc/openvpn/server/server.conf
            echo 'push "dhcp-option DNS 149.112.112.112"' >> /etc/openvpn/server/server.conf
        ;;
        6)
            echo 'push "dhcp-option DNS 94.140.14.14"' >> /etc/openvpn/server/server.conf
            echo 'push "dhcp-option DNS 94.140.15.15"' >> /etc/openvpn/server/server.conf
        ;;
    esac
    echo "keepalive 10 120
cipher AES-256-CBC
user nobody
group $group_name
persist-key
persist-tun
verb 3
crl-verify crl.pem" >> /etc/openvpn/server/server.conf
    if [[ "$protocol" = "udp" ]]; then
        echo "explicit-exit-notify" >> /etc/openvpn/server/server.conf
    fi
    # Enable net.ipv4.ip_forward for the system
    echo 'net.ipv4.ip_forward=1' > /etc/sysctl.d/99-openvpn-forward.conf
    # Enable without waiting for a reboot or service restart
    echo 1 > /proc/sys/net/ipv4/ip_forward
    if [[ -n "$ip6" ]]; then
        # Enable net.ipv6.conf.all.forwarding for the system
        echo "net.ipv6.conf.all.forwarding=1" >> /etc/sysctl.d/99-openvpn-forward.conf
        # Enable without waiting for a reboot or service restart
        echo 1 > /proc/sys/net/ipv6/conf/all/forwarding
    fi
    if systemctl is-active --quiet firewalld.service; then
        # Using both permanent and not permanent rules to avoid a firewalld
        # reload.
        # We don't use --add-service=openvpn because that would only work with
        # the default port and protocol.
        firewall-cmd --add-port="$port"/"$protocol"
        firewall-cmd --zone=trusted --add-source=10.8.0.0/24
        firewall-cmd --permanent --add-port="$port"/"$protocol"
        firewall-cmd --permanent --zone=trusted --add-source=10.8.0.0/24
        # Set NAT for the VPN subnet
        firewall-cmd --direct --add-rule ipv4 nat POSTROUTING 0 -s 10.8.0.0/24 ! -d 10.8.0.0/24 -j SNAT --to "$ip"
        firewall-cmd --permanent --direct --add-rule ipv4 nat POSTROUTING 0 -s 10.8.0.0/24 ! -d 10.8.0.0/24 -j SNAT --to "$ip"
        if [[ -n "$ip6" ]]; then
            firewall-cmd --zone=trusted --add-source=fddd:1194:1194:1194::/64
            firewall-cmd --permanent --zone=trusted --add-source=fddd:1194:1194:1194::/64
            firewall-cmd --direct --add-rule ipv6 nat POSTROUTING 0 -s fddd:1194:1194:1194::/64 ! -d fddd:1194:1194:1194::/64 -j SNAT --to "$ip6"
            firewall-cmd --permanent --direct --add-rule ipv6 nat POSTROUTING 0 -s fddd:1194:1194:1194::/64 ! -d fddd:1194:1194:1194::/64 -j SNAT --to "$ip6"
        fi
    else
        # Create a service to set up persistent iptables rules
        iptables_path=$(command -v iptables)
        ip6tables_path=$(command -v ip6tables)
        # nf_tables is not available as standard in OVZ kernels. So use iptables-legacy
        # if we are in OVZ, with a nf_tables backend and iptables-legacy is available.
        if [[ $(systemd-detect-virt) == "openvz" ]] && readlink -f "$(command -v iptables)" | grep -q "nft" && hash iptables-legacy 2>/dev/null; then
            iptables_path=$(command -v iptables-legacy)
            ip6tables_path=$(command -v ip6tables-legacy)
        fi
        echo "[Unit]
Before=network.target
[Service]
Type=oneshot
ExecStart=$iptables_path -t nat -A POSTROUTING -s 10.8.0.0/24 ! -d 10.8.0.0/24 -j SNAT --to $ip
ExecStart=$iptables_path -I INPUT -p $protocol --dport $port -j ACCEPT
ExecStart=$iptables_path -I FORWARD -s 10.8.0.0/24 -j ACCEPT
ExecStart=$iptables_path -I FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT
ExecStop=$iptables_path -t nat -D POSTROUTING -s 10.8.0.0/24 ! -d 10.8.0.0/24 -j SNAT --to $ip
ExecStop=$iptables_path -D INPUT -p $protocol --dport $port -j ACCEPT
ExecStop=$iptables_path -D FORWARD -s 10.8.0.0/24 -j ACCEPT
ExecStop=$iptables_path -D FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT" > /etc/systemd/system/openvpn-iptables.service
        if [[ -n "$ip6" ]]; then
            echo "ExecStart=$ip6tables_path -t nat -A POSTROUTING -s fddd:1194:1194:1194::/64 ! -d fddd:1194:1194:1194::/64 -j SNAT --to $ip6
ExecStart=$ip6tables_path -I FORWARD -s fddd:1194:1194:1194::/64 -j ACCEPT
ExecStart=$ip6tables_path -I FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT
ExecStop=$ip6tables_path -t nat -D POSTROUTING -s fddd:1194:1194:1194::/64 ! -d fddd:1194:1194:1194::/64 -j SNAT --to $ip6
ExecStop=$ip6tables_path -D FORWARD -s fddd:1194:1194:1194::/64 -j ACCEPT
ExecStop=$ip6tables_path -D FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT" >> /etc/systemd/system/openvpn-iptables.service
        fi
        echo "RemainAfterExit=yes
[Install]
WantedBy=multi-user.target" >> /etc/systemd/system/openvpn-iptables.service
        systemctl enable --now openvpn-iptables.service
    fi
    # If SELinux is enabled and a custom port was selected, we need this
    if sestatus 2>/dev/null | grep "Current mode" | grep -q "enforcing" && [[ "$port" != 1194 ]]; then
        # Install semanage if not already present
        if ! hash semanage 2>/dev/null; then
            if [[ "$os_version" -eq 7 ]]; then
                # Centos 7
                yum install -y policycoreutils-python
            else
                # CentOS 8 or Fedora
                dnf install -y policycoreutils-python-utils
            fi
        fi
        semanage port -a -t openvpn_port_t -p "$protocol" "$port"
    fi
    # If the server is behind NAT, use the correct IP address
    [[ -n "$public_ip" ]] && ip="$public_ip"
    # client-common.txt is created so we have a template to add further users later
    echo "client
dev tun
proto $protocol
remote $ip $port
resolv-retry infinite
nobind
persist-key
persist-tun
remote-cert-tls server
auth SHA512
cipher AES-256-CBC
ignore-unknown-option block-outside-dns
block-outside-dns
verb 3" > /etc/openvpn/server/client-common.txt
    # Enable and start the OpenVPN service
    systemctl enable --now openvpn-server@server.service
    # Generates the custom client.ovpn
    new_client
    echo
    echo "Finished!"
    echo
    echo "The client configuration is available in:" ~/"$client.ovpn"
    echo "New clients can be added by running this script again."
else
    clear
    echo "OpenVPN is already installed."
    echo
    echo "Select an option:"
    echo "   1) Add a new client"
    echo "   2) Revoke an existing client"
    echo "   3) Remove OpenVPN"
    echo "   4) Exit"
    read -p "Option: " option
    until [[ "$option" =~ ^[1-4]$ ]]; do
        echo "$option: invalid selection."
        read -p "Option: " option
    done
    case "$option" in
        1)
            echo
            echo "Provide a name for the client:"
            read -p "Name: " unsanitized_client
            client=$(sed 's/[^0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-]/_/g' <<< "$unsanitized_client")
            while [[ -z "$client" || -e /etc/openvpn/server/easy-rsa/pki/issued/"$client".crt ]]; do
                echo "$client: invalid name."
                read -p "Name: " unsanitized_client
                client=$(sed 's/[^0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-]/_/g' <<< "$unsanitized_client")
            done
            cd /etc/openvpn/server/easy-rsa/
            EASYRSA_CERT_EXPIRE=3650 ./easyrsa build-client-full "$client" nopass
            # Generates the custom client.ovpn
            new_client
            echo
            echo "$client added. Configuration available in:" ~/"$client.ovpn"
            exit
        ;;
        2)
            # This option could be documented a bit better and maybe even be simplified
            # ...but what can I say, I want some sleep too
            number_of_clients=$(tail -n +2 /etc/openvpn/server/easy-rsa/pki/index.txt | grep -c "^V")
            if [[ "$number_of_clients" = 0 ]]; then
                echo
                echo "There are no existing clients!"
                exit
            fi
            echo
            echo "Select the client to revoke:"
            tail -n +2 /etc/openvpn/server/easy-rsa/pki/index.txt | grep "^V" | cut -d '=' -f 2 | nl -s ') '
            read -p "Client: " client_number
            until [[ "$client_number" =~ ^[0-9]+$ && "$client_number" -le "$number_of_clients" ]]; do
                echo "$client_number: invalid selection."
                read -p "Client: " client_number
            done
            client=$(tail -n +2 /etc/openvpn/server/easy-rsa/pki/index.txt | grep "^V" | cut -d '=' -f 2 | sed -n "$client_number"p)
            echo
            read -p "Confirm $client revocation? [y/N]: " revoke
            until [[ "$revoke" =~ ^[yYnN]*$ ]]; do
                echo "$revoke: invalid selection."
                read -p "Confirm $client revocation? [y/N]: " revoke
            done
            if [[ "$revoke" =~ ^[yY]$ ]]; then
                cd /etc/openvpn/server/easy-rsa/
                ./easyrsa --batch revoke "$client"
                EASYRSA_CRL_DAYS=3650 ./easyrsa gen-crl
                rm -f /etc/openvpn/server/crl.pem
                cp /etc/openvpn/server/easy-rsa/pki/crl.pem /etc/openvpn/server/crl.pem
                # CRL is read with each client connection, when OpenVPN is dropped to nobody
                chown nobody:"$group_name" /etc/openvpn/server/crl.pem
                echo
                echo "$client revoked!"
            else
                echo
                echo "$client revocation aborted!"
            fi
            exit
        ;;
        3)
            echo
            read -p "Confirm OpenVPN removal? [y/N]: " remove
            until [[ "$remove" =~ ^[yYnN]*$ ]]; do
                echo "$remove: invalid selection."
                read -p "Confirm OpenVPN removal? [y/N]: " remove
            done
            if [[ "$remove" =~ ^[yY]$ ]]; then
                port=$(grep '^port ' /etc/openvpn/server/server.conf | cut -d " " -f 2)
                protocol=$(grep '^proto ' /etc/openvpn/server/server.conf | cut -d " " -f 2)
                if systemctl is-active --quiet firewalld.service; then
                    ip=$(firewall-cmd --direct --get-rules ipv4 nat POSTROUTING | grep '\-s 10.8.0.0/24 '"'"'!'"'"' -d 10.8.0.0/24' | grep -oE '[^ ]+$')
                    # Using both permanent and not permanent rules to avoid a firewalld reload.
                    firewall-cmd --remove-port="$port"/"$protocol"
                    firewall-cmd --zone=trusted --remove-source=10.8.0.0/24
                    firewall-cmd --permanent --remove-port="$port"/"$protocol"
                    firewall-cmd --permanent --zone=trusted --remove-source=10.8.0.0/24
                    firewall-cmd --direct --remove-rule ipv4 nat POSTROUTING 0 -s 10.8.0.0/24 ! -d 10.8.0.0/24 -j SNAT --to "$ip"
                    firewall-cmd --permanent --direct --remove-rule ipv4 nat POSTROUTING 0 -s 10.8.0.0/24 ! -d 10.8.0.0/24 -j SNAT --to "$ip"
                    if grep -qs "server-ipv6" /etc/openvpn/server/server.conf; then
                        ip6=$(firewall-cmd --direct --get-rules ipv6 nat POSTROUTING | grep '\-s fddd:1194:1194:1194::/64 '"'"'!'"'"' -d fddd:1194:1194:1194::/64' | grep -oE '[^ ]+$')
                        firewall-cmd --zone=trusted --remove-source=fddd:1194:1194:1194::/64
                        firewall-cmd --permanent --zone=trusted --remove-source=fddd:1194:1194:1194::/64
                        firewall-cmd --direct --remove-rule ipv6 nat POSTROUTING 0 -s fddd:1194:1194:1194::/64 ! -d fddd:1194:1194:1194::/64 -j SNAT --to "$ip6"
                        firewall-cmd --permanent --direct --remove-rule ipv6 nat POSTROUTING 0 -s fddd:1194:1194:1194::/64 ! -d fddd:1194:1194:1194::/64 -j SNAT --to "$ip6"
                    fi
                else
                    systemctl disable --now openvpn-iptables.service
                    rm -f /etc/systemd/system/openvpn-iptables.service
                fi
                if sestatus 2>/dev/null | grep "Current mode" | grep -q "enforcing" && [[ "$port" != 1194 ]]; then
                    semanage port -d -t openvpn_port_t -p "$protocol" "$port"
                fi
                systemctl disable --now openvpn-server@server.service
                rm -rf /etc/openvpn/server
                rm -f /etc/systemd/system/openvpn-server@server.service.d/disable-limitnproc.conf
                rm -f /etc/sysctl.d/99-openvpn-forward.conf
                if [[ "$os" = "debian" || "$os" = "ubuntu" ]]; then
                    apt-get remove --purge -y openvpn
                else
                    # Else, OS must be CentOS or Fedora
                    yum remove -y openvpn
                fi
                echo
                echo "OpenVPN removed!"
            else
                echo
                echo "OpenVPN removal aborted!"
            fi
            exit
        ;;
        4)
            exit
        ;;
    esac
fi

 

微信小程序登录和鉴权 django jwt


2021年12月18日 17:12:32   2,166 次浏览

1|什么是JWT ?

JWT,全称Json Web Token,用于作为JSON对象在各方之间安全地传输信息。该信息可以被验证和信任,因为它是数字签名的。

1|与Session的区别

一、Session是在服务器端的,而JWT是在客户端的,这点很重要。
二、流程不同

1|JWT使用场景

  • 大量需要进行跨域的站点
  • 服务器运算能力较差、存储空间较小

1|JWT的原理

JWT 的原理是,服务器认证以后,生成一个 JSON 对象,发回给用户,就像下面这样。





{
  "姓名": "张三",
  "角色": "管理员",
  "到期时间": "2018年7月1日0点0分"
}

以后,用户与服务端通信的时候,都要发回这个 JSON 对象。服务器完全只靠这个对象认定用户身份。为了防止用户篡改数据,服务器在生成这个对象的时候,会加上签名(详见后文)。

服务器就不保存任何 session 数据了,也就是说,服务器变成无状态了,从而比较容易实现扩展。

1|JWT数据的格式

实际的 JWT 大概就像下面这样。

它是一个很长的字符串,中间用点(.)分隔成三个部分。注意,JWT 内部是没有换行的,这里只是为了便于展示,将它写成了几行。

JWT 的三个部分依次如下。

  • Header(头部)
  • Payload(负载)
  • Signature(签名)

Header 部分是一个 JSON 对象,描述 JWT 的元数据,通常是下面的样子。





{
  "alg": "HS256",
  "typ": "JWT"
}

上面代码中,alg属性表示签名的算法(algorithm),默认是 HMAC SHA256(写成 HS256);typ属性表示这个令牌(token)的类型(type),JWT 令牌统一写为JWT。

最后,将上面的 JSON 对象使用 Base64URL 算法(详见后文)转成字符串。

Payload

Payload 部分也是一个 JSON 对象,用来存放实际需要传递的数据。JWT 规定了7个官方字段,供选用。

  • iss (issuer):签发人
  • exp (expiration time):过期时间
  • sub (subject):主题
  • aud (audience):受众
  • nbf (Not Before):生效时间
  • iat (Issued At):签发时间
  • jti (JWT ID):编号

除了官方字段,你还可以在这个部分定义私有字段,下面就是一个例子。





{
  "sub": "1234567890",
  "name": "John Doe",
  "admin": true
}


注意,JWT 默认是不加密的,任何人都可以读到,所以不要把秘密信息放在这个部分。

这个 JSON 对象也要使用 Base64URL 算法转成字符串。

Signature

Signature 部分是对前两部分的签名,防止数据篡改。

首先,需要指定一个密钥(secret)。这个密钥只有服务器才知道,不能泄露给用户。然后,使用 Header 里面指定的签名算法(默认是 HMAC SHA256),按照下面的公式产生签名。





HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  secret)

算出签名以后,把 Header、Payload、Signature 三个部分拼成一个字符串,每个部分之间用”点”(.)分隔,就可以返回给用户。

Base64URL

前面提到,Header 和 Payload 串型化的算法是 Base64URL。这个算法跟 Base64 算法基本类似,但有一些小的不同。

JWT 作为一个令牌(token),有些场合可能会放到 URL(比如 api.example.com/?token=xxx)。Base64 有三个字符+、/和=,在 URL 里面有特殊含义,所以要被替换掉:=被省略、+替换成-,/替换成_ 。这就是 Base64URL 算法。

1|JWT的使用方式

客户端收到服务器返回的 JWT,可以储存在 Cookie 里面,也可以储存在 localStorage。

此后,客户端每次与服务器通信,都要带上这个 JWT。你可以把它放在 Cookie 里面自动发送,但是这样不能跨域,所以更好的做法是放在 HTTP 请求的头信息Authorization字段里面。





Authorization: Bearer <token>

另一种做法是,跨域的时候,JWT 就放在 POST 请求的数据体里面。

1|JWT 的几个特点

(1)JWT 默认是不加密,但也是可以加密的。生成原始 Token 以后,可以用密钥再加密一次。

(2)JWT 不加密的情况下,不能将秘密数据写入 JWT。

(3)JWT 不仅可以用于认证,也可以用于交换信息。有效使用 JWT,可以降低服务器查询数据库的次数。

(4)JWT 的最大缺点是,由于服务器不保存 session 状态,因此无法在使用过程中废止某个 token,或者更改 token 的权限。也就是说,一旦 JWT 签发了,在到期之前就会始终有效,除非服务器部署额外的逻辑。

(5)JWT 本身包含了认证信息,一旦泄露,任何人都可以获得该令牌的所有权限。为了减少盗用,JWT 的有效期应该设置得比较短。对于一些比较重要的权限,使用时应该再次对用户进行认证。

(6)为了减少盗用,JWT 不应该使用 HTTP 协议明码传输,要使用 HTTPS 协议传输。

1|内容说明

以上主要内容转载于廖雪峰的网络日志

2|0实例:使用Django完成微信小程序的JWT登录及鉴权

网上目前已经有了一些文章来说明了,可是我在查阅的时候发现大多讲得不是很清楚。

2|1基本了解

通过之前的内容铺垫,相信读者对于JWT都有了一定的了解,总的来说,便是JWT是保存在用户端的Token机制。

需要注意的是:JWT默认是无加密的,只是使用了一层Base64编码,所以我们不能将重要信息,如密码等放入header和payload字段中。

2|2开始

对于Django来说,这里我们使用djangorestframework-jwt库

安装命令:





pip install djangorestframework-jwt

注意djangorestframework-jwt库默认将settings里的SECRET_KEY当中jwt加密秘钥。

首先我们先去我们的project下的settings文件内设置jwt库的一些参数





import datetime

# 在末尾添加上
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework_jwt.authentication.JSONWebTokenAuthentication',# JWT认证,在前面的认证方案优先
        'rest_framework.authentication.SessionAuthentication',
        'rest_framework.authentication.BasicAuthentication',
    ),
}
JWT_AUTH = {
    'JWT_EXPIRATION_DELTA': datetime.timedelta(days=1), #JWT_EXPIRATION_DELTA 指明token的有效期
}


登录函数的实现:





'''
登录函数:
'''
def get_user_info_func(user_code):
    api_url = 'https://api.weixin.qq.com/sns/jscode2session?appid={0}&secret={1}&js_code={2}&grant_type=authorization_code'
    get_url = api_url.format(App_id,App_secret,user_code)
    r = requests.get(get_url)
    return r.json()


@require_http_methods(['POST'])
def user_login_func(request):
    try:
        user_code = request.POST.get('user_code')
        print(user_code)
        if user_code == None:
            print(request.body)
            json_data =  json.loads(request.body)
            user_code = json_data['user_code']
            print(user_code)
    except:
        return JsonResponse({'status':500,'error':'请输入完整数据'})
    try:
        json_data = get_user_info_func(user_code)
        #json_data = {'errcode':0,'openid':'111','session_key':'test'}
        if 'errcode' in json_data:
            return JsonResponse({'status': 500, 'error': '验证错误:' + json_data['errmsg']})
        res = login_or_create_account(json_data)
        return JsonResponse(res)
    except:
        return JsonResponse({'status':500,'error':'无法与微信验证端连接'})


def login_or_create_account(json_data):
    openid = json_data['openid']
    session_key = json_data['session_key']

    try:
        user = User.objects.get(username=openid)
    except:
        user = User.objects.create(
            username=openid,
            password=openid,
        )
    user.session_key = session_key
    user.save()

    try:
        jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
        jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
        payload = jwt_payload_handler(user)
        token = jwt_encode_handler(payload)
        res = {
            'status': 200,
            'token': token
        }
    except:
        res = {
            'status': 500,
            'error': 'jwt验证失败'
        }
    return res

视图函数:





'''
视图样例:
'''
from django.http import JsonResponse
from account.models import *
from rest_framework_jwt.views import APIView
from rest_framework import authentication
from rest_framework.permissions import IsAuthenticated
from rest_framework_jwt.authentication import JSONWebTokenAuthentication
from rest_framework import permissions

class IsOwnerOrReadOnly(permissions.BasePermission):

    def has_object_permission(self, request, view, obj):
        if request.method in ['GET','POST']:
            return True
        return obj.user == request.user

class test_view(APIView):
    http_method_names = ['post']  #限制api的访问方式
    authentication_classes = (authentication.SessionAuthentication,JSONWebTokenAuthentication)
    permission_classes = (IsAuthenticated,IsOwnerOrReadOnly)       #权限管理

    def post(self,request):                                        #视图函数
        user = request.user.username
        U = User.objects.get(username=user)
        json_data = json.loads(request.body)
        try:
            test = json_data['']
        except:
            return JsonResponse({'status':500,'errmsg':'参数不全'})
        try:
            U.sex = sex
            U.weight = weight
            U.height = height
            U.save()
        except:
            return JsonResponse({'status': 500, 'errmsg': '数据库错误'})
        return JsonResponse({'status':200})

urls.py:





urlpatterns = [
    re_path('^$', index),
    re_path('^login$',login),                                 # 登录
    re_path('^test$',test_view.as_view())
]