Featured image of post helm部署OpenVPN 4.2.3

helm部署OpenVPN 4.2.3

k8s部署openvpn打通k8s网络将Kubernetes集群网络暴露给本地开发网络,目前OpenVPN能在Solaris、Linux、OpenBSD、FreeBSD、NetBSD、Mac OS X与Microsoft Windows以及Android和iOS上运行,并包含了许多安全性的功能。。。。。。。。

helm部署OpenVPN 4.2.3

k8s部署openvpn打通k8s网络将Kubernetes集群网络暴露给本地开发网络

简单介绍​:

VPN直译就是虚拟专用通道,是提供给企业之间或者个人与公司之间安全数据传输的隧道,OpenVPN无疑是Linux下开源VPN的先锋,提供了良好的性能和友好的用户GUI。

它大量使用了OpenSSL加密库中的SSLv3/TLSv1协议函数库。

目前OpenVPN能在Solaris、Linux、OpenBSD、FreeBSD、NetBSD、Mac OS X与Microsoft Windows以及Android和iOS上运行,并包含了许多安全性的功能。它并不是一个基于Web的VPN软件,也不与IPsec及其他VPN软件包兼容

官方仓库地址:Kubeapps Hub

客户端下载地址:下载地址

若该网址无法访问,可以到下面的链接进行下载

百度网盘:点击直达 提取码:l0qv

确认集群helm安装正确

1
2
[root@master01 ~]# helm version
version.BuildInfo{Version:"v3.8.2", GitCommit:"6e3701edea09e5d55a8ca2aae03a68917630e91b", GitTreeState:"clean", GoVersion:"go1.17.5"}

下载Chart包,修改values.yaml​文件

1
2
3
4
5
6
7
helm repo add stable https://kubernetes-charts.storage.googleapis.com
helm pull stable/openvpn

或者
wget http://mirror.azure.cn/kubernetes/charts/openvpn-4.2.5.tgz
tar -zxvf openvpn-4.2.5.tgz
cd openvpn

修改配置文件,详情参考stable/openvpn-github

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
cp  values.yaml openvpn-values.yaml



replicaCount: 1
updateStrategy: {}
imagePullSecretName:
image:
  repository: jfelten/openvpn-docker
  tag: 1.1.0
  pullPolicy: IfNotPresent
service:
  type: ClusterIP
  #externalPort: 443
  #internalPort: 443
  # hostPort: 443
  externalIPs: []
  nodePort: 32085
  # clusterIP: None
  # LoadBalancerSourceRanges: 0.0.0.0/0
  # loadBalancerIP: 10.0.0.1

  ## Here annotations can be added to the openvpn service
  # annotations:
  #   external-dns.alpha.kubernetes.io/hostname: vpn.example.com
  annotations: {}
## Here annotations can be added to the openvpn pod
# podAnnotations:
#   backup.ark.heptio.com/backup-volumes: certs
podAnnotations: {}

# Add privileged init container to enable IPv4 forwarding
ipForwardInitContainer: true

resources:
  limits:
    cpu: 300m
    memory: 128Mi
  requests:
    cpu: 300m
    memory: 128Mi

readinessProbe:
  initialDelaySeconds: 5
  periodSeconds: 5
  successThreshold: 2

persistence:
  enabled: true

  storageClass: "nfs-client"
  accessMode: ReadWriteOnce
  size: 2M
openvpn:
  # Network allocated for openvpn clients (default: 10.240.0.0).
  OVPN_NETWORK: 10.240.0.0
  # Network subnet allocated for openvpn client (default: 255.255.0.0).
  OVPN_SUBNET: 255.255.0.0
  # Protocol used by openvpn tcp or udp (default: udp).
  OVPN_PROTO: udp
  OVPN_K8S_POD_NETWORK: "172.20.0.0" #k8s pod地址
  OVPN_K8S_POD_SUBNET: "255.255.0.0"
  OVPN_K8S_SVC_NETWORK: "172.21.0.0"    #k8s svc地址
  OVPN_K8S_SVC_SUBNET: "255.255.0.0"

  DEFAULT_ROUTE_ENABLED: true

  dhcpOptionDomain: true
  # Redirect all client traffic through VPN
  redirectGateway: true

  useCrl: false
  taKey: true
  cipher: AES-256-CBC
  istio:
    enabled: false
    proxy:
      port: 15001
  iptablesExtra: []

  ccd:
    enabled: false
    config: {}

nodeSelector: {}
tolerations: []

相关文档参考:

www.1nth.com/post/k8s-op…

zhuanlan.zhihu.com/p/491…

pythontaotao.github.io/2…

部署并进行验证

可以直接helm部署,也可以生成yaml文件部署

方法一:

1
2
3
kubectl create ns openvpn
helm install openvpn -n openvpn ./ -f openvpn-values.yaml
 helm status openvpn -n openvpn

方法二:我才用此方法

1
2
3
helm install openvpn -n openvpn ./ -f openvpn-values.yaml  --dry-run --debug

生成yaml ,在进行部署

生成的文件

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
---
# Source: openvpn/templates/config-openvpn.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: openvpn
  labels:
    app: openvpn
    chart: openvpn-4.2.5
    release: openvpn
    heritage: Helm
data:
  setup-certs.sh: |-
    #!/bin/bash
    EASY_RSA_LOC="/etc/openvpn/certs"
    cd $EASY_RSA_LOC
    SERVER_CERT="${EASY_RSA_LOC}/pki/issued/server.crt"
    if [ -e "$SERVER_CERT" ]
    then
      echo "found existing certs - reusing"
        if [ ! -e ${EASY_RSA_LOC}/pki/ta.key ]
        then
          echo "generating missed ta.key"
          openvpn --genkey --secret ${EASY_RSA_LOC}/pki/ta.key
        fi
    else
      cp -R /usr/share/easy-rsa/* $EASY_RSA_LOC
      ./easyrsa init-pki
      echo "ca\n" | ./easyrsa build-ca nopass
      ./easyrsa build-server-full server nopass
      ./easyrsa gen-dh
      openvpn --genkey --secret ${EASY_RSA_LOC}/pki/ta.key
    fi


  newClientCert.sh: |-
      #!/bin/bash
      EASY_RSA_LOC="/etc/openvpn/certs"
      cd $EASY_RSA_LOC
      MY_IP_ADDR="$2"
      ./easyrsa build-client-full $1 nopass
      cat >${EASY_RSA_LOC}/pki/$1.ovpn <<EOF
      client
      nobind
      dev tun
      remote ${MY_IP_ADDR} 443 udp
    
      cipher AES-256-CBC
    
      redirect-gateway def1
    
      <key>
      `cat ${EASY_RSA_LOC}/pki/private/$1.key`
      </key>
      <cert>
      `cat ${EASY_RSA_LOC}/pki/issued/$1.crt`
      </cert>
      <ca>
      `cat ${EASY_RSA_LOC}/pki/ca.crt`
      </ca>
      <tls-auth>
      `cat ${EASY_RSA_LOC}/pki/ta.key`
      </tls-auth>
      key-direction 1
      EOF
      cat pki/$1.ovpn

  revokeClientCert.sh: |-
      #!/bin/bash
      EASY_RSA_LOC="/etc/openvpn/certs"
      cd $EASY_RSA_LOC
      ./easyrsa revoke $1
      ./easyrsa gen-crl
      cp ${EASY_RSA_LOC}/pki/crl.pem ${EASY_RSA_LOC}
      chmod 644 ${EASY_RSA_LOC}/crl.pem

  configure.sh: |-
      #!/bin/sh

      cidr2mask() {
         # Number of args to shift, 255..255, first non-255 byte, zeroes
         set -- $(( 5 - ($1 / 8) )) 255 255 255 255 $(( (255 << (8 - ($1 % 8))) & 255 )) 0 0 0
         [ $1 -gt 1 ] && shift "$1" || shift
         echo ${1-0}.${2-0}.${3-0}.${4-0}
      }

      cidr2net() {
          local i ip mask netOctets octets
          ip="${1%/*}"
          mask="${1#*/}"
          octets=$(echo "$ip" | tr '.' '\n')

          for octet in $octets; do
              i=$((i+1))
              if [ $i -le $(( mask / 8)) ]; then
                  netOctets="$netOctets.$octet"
              elif [ $i -eq  $(( mask / 8 +1 )) ]; then
                  netOctets="$netOctets.$((((octet / ((256 / ((2**((mask % 8)))))))) * ((256 / ((2**((mask % 8))))))))"
              else
                  netOctets="$netOctets.0"
              fi
          done

          echo ${netOctets#.}
      }

      /etc/openvpn/setup/setup-certs.sh




      iptables -t nat -A POSTROUTING -s 10.240.0.0/255.255.0.0 -o eth0 -j MASQUERADE
      mkdir -p /dev/net
      if [ ! -c /dev/net/tun ]; then
          mknod /dev/net/tun c 10 200
      fi

      if [ "$DEBUG" == "1" ]; then
          echo ========== ${OVPN_CONFIG} ==========
          cat "${OVPN_CONFIG}"
          echo ====================================
      fi

      intAndIP="$(ip route get 8.8.8.8 | awk '/8.8.8.8/ {print $5 "-" $7}')"
      int="${intAndIP%-*}"
      ip="${intAndIP#*-}"
      cidr="$(ip addr show dev "$int" | awk -vip="$ip" '($2 ~ ip) {print $2}')"

      NETWORK="$(cidr2net $cidr)"
      NETMASK="$(cidr2mask ${cidr#*/})"
      DNS=$(cat /etc/resolv.conf | grep -v '^#' | grep nameserver | awk '{print $2}')
      SEARCH=$(cat /etc/resolv.conf | grep -v '^#' | grep search | awk '{$1=""; print $0}')
      FORMATTED_SEARCH=""
      for DOMAIN in $SEARCH; do
        FORMATTED_SEARCH="${FORMATTED_SEARCH}push \"dhcp-option DOMAIN-SEARCH ${DOMAIN}\"\n"
      done
      cp -f /etc/openvpn/setup/openvpn.conf /etc/openvpn/
      sed 's|OVPN_K8S_SEARCH|'"${FORMATTED_SEARCH}"'|' -i /etc/openvpn/openvpn.conf
      sed 's|OVPN_K8S_DNS|'"${DNS}"'|' -i /etc/openvpn/openvpn.conf
      sed 's|NETWORK|'"${NETWORK}"'|' -i /etc/openvpn/openvpn.conf
      sed 's|NETMASK|'"${NETMASK}"'|' -i /etc/openvpn/openvpn.conf

      # exec openvpn process so it receives lifecycle signals
      exec openvpn --config /etc/openvpn/openvpn.conf
  openvpn.conf: |-
      server 10.240.0.0 255.255.0.0
      verb 3

      key /etc/openvpn/certs/pki/private/server.key
      ca /etc/openvpn/certs/pki/ca.crt
      cert /etc/openvpn/certs/pki/issued/server.crt
      dh /etc/openvpn/certs/pki/dh.pem

      tls-auth /etc/openvpn/certs/pki/ta.key 0



      cipher AES-256-CBC

      key-direction 0
      keepalive 10 60
      persist-key
      persist-tun

      proto udp
      port  443
      dev tun0
      status /tmp/openvpn-status.log

      user nobody
      group nogroup




      push "route NETWORK NETMASK"


      push "route 172.20.0.0 255.255.0.0"


      push "route 172.21.0.0 255.255.0.0"



      OVPN_K8S_SEARCH

      push "dhcp-option DNS 172.21.16.10"
---
# Source: openvpn/templates/certs-pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: openvpn
  labels:
    app: openvpn
    chart: openvpn-4.2.5
    release: openvpn
    heritage: Helm
spec:
  accessModes:
    - "ReadWriteOnce"
  resources:
    requests:
      storage: "2M"
  storageClassName: "nfs-client"
---
# Source: openvpn/templates/openvpn-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: openvpn
  labels:
    app: openvpn
    chart: openvpn-4.2.5
    release: openvpn
    heritage: Helm
spec:
  ports:
    - name: openvpn
      port: 443
      targetPort: 443
      protocol: UDP
  selector:
    app: openvpn
    release: openvpn
  type: ClusterIP
---
# Source: openvpn/templates/openvpn-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: openvpn
  labels:
    app: openvpn
    chart: openvpn-4.2.5
    release: openvpn
    heritage: Helm
spec:
  replicas: 1
  selector:
    matchLabels:
      app: openvpn
      release: openvpn
  template:
    metadata:
      labels:
        app: openvpn
        release: openvpn
      annotations:
        checksum/config: c6d7908b4a71d5de2ee5f823eac2522e8de7c15ab026fcc0f4149a270886df1f
    spec:
      initContainers:
        - args:
            - -c
            - sysctl -w net.ipv4.ip_forward=1
          command:
            - /bin/sh
          image: busybox:1.29
          imagePullPolicy: IfNotPresent
          name: sysctl
          resources:
            requests:
              cpu: 1m
              memory: 1Mi
          securityContext:
            privileged: true
      containers:
      - name: openvpn
        image: "jfelten/openvpn-docker:1.1.0"
        imagePullPolicy: IfNotPresent
        command: ["/etc/openvpn/setup/configure.sh"]
        ports:
        - containerPort: 443
          name: openvpn
        securityContext:
          capabilities:
            add:
              - NET_ADMIN
        readinessProbe:
          initialDelaySeconds: 5
          periodSeconds: 5
          successThreshold: 2
          exec:
            command:
            - nc
            - -u
            - -z
            - 127.0.0.1
            - "443"
        resources:
          requests:
            cpu: "300m"
            memory: "128Mi"
          limits:
            cpu: "300m"
            memory: "128Mi"
        volumeMounts:
          - mountPath: /etc/openvpn/setup
            name: openvpn
            readOnly: false
          - mountPath: /etc/openvpn/certs
            name: certs
            readOnly: false
      volumes:
      - name: openvpn
        configMap:
          name: openvpn
          defaultMode: 0775
      - name: certs
        persistentVolumeClaim:
          claimName: openvpn

账号管理脚本

下面整理一下生成clientkey的脚本,这里面的信息就是上面部署完成后输出的信息,需要修改参数KEY_NAME​​和SERVICE_IP​​,执行完成后会生成wangwu.ovpn​​的文件,客户端使用该文件作为连接凭证

create-client-key.sh​​​ 创建用户

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
$ cat create-client-key.sh

#!/bin/bash

POD_NAME=$(kubectl get pods --namespace "default" -l "app=openvpn,release=my-openvpn" -o jsonpath='{ .items[0].metadata.name }')
SERVICE_NAME=$(kubectl get svc --namespace "default" -l "app=openvpn,release=my-openvpn" -o jsonpath='{ .items[0].metadata.name }')
SERVICE_IP=10.169.68.142
KEY_NAME=wangwu
kubectl --namespace "default" exec -it "$POD_NAME" /etc/openvpn/setup/newClientCert.sh "$KEY_NAME" "$SERVICE_IP"
kubectl --namespace "default" exec -it "$POD_NAME" cat "/etc/openvpn/certs/pki/$KEY_NAME.ovpn" > "$KEY_NAME.ovpn"

$ chmod +x get_clientkey.sh && ./get_clientkey.sh

rm-client-key.sh​​​​ 注销用户

1
2
3
4
5
#!/usr/bin/env bash

KEY_NAME=$1
POD_NAME=$(kubectl get pods -n "openvpn" -l "app=openvpn,release=openvpn" -o jsonpath='{.items[0].metadata.name}')
kubectl -n "openvpn" exec -it "$POD_NAME" /etc/openvpn/setup/revokeClientCert.sh $KEY_NAME

创建客户端密钥

1
2
3
4
5
6
7
[root@iZwz98n1yl2yxjc2xpwt8dZ openvpn]#  kubectl --namespace "openvpn" get svc
NAME      TYPE           CLUSTER-IP    EXTERNAL-IP     PORT(S)         AGE
openvpn   LoadBalancer   172.19.77.8   119.23.xxx.xx   443:30924/TCP   8d

[root@iZwz98n1yl2yxjc2xpwt8dZ openvpn]# ./create-client-key.sh sunnyhaha
[root@iZwz98n1yl2yxjc2xpwt8dZ openvpn]# ll user-openvpn-20220402/sunnyhaha.ovpn 
-rw-r--r-- 1 root root 7445 Apr  2 09:50 user-openvpn-20220402/sunnyhaha.ovpn

上面的命令会生成client.ovpn配置文件,因为前面用Helm部署openvpn时,创建的Servie是ClusterIP类型的,生成的配置文件 中的VPN服务地址会有错误,需要修改这个配置文件,将remote配置修改如下地址为k8s边缘节点的ip,端口为前面暴露的1194端口,同时删除redirect-gateway def1这行内容

解析k8s内部svc

部署完成之后有个坑,不能解析k8s内部svc

1
2
3
4
5
6
7
nslookup kubernetes.default.svc.cluster.local
服务器:  UnKnown
Address:  192.168.1.1

DNS request timed out.
    timeout was 2 seconds.
DNS request timed out.

查看openvpn服务端配置

1
2
3
kubectl get cm -n openvpn openvpn -oyaml
......
push "dhcp-option DNS OVPN_K8S_DNS"

因为我用的是node-local-dns,pod的DNS地址会变成__PILLAR__LOCAL__DNS__的默认地址169.254.20.10所以导致不能解析

修改服务端配置

1
2
3
4
kubectl get svc -n kube-system -l k8s-app=kube-dns -o jsonpath='{$.items[*].spec.clusterIP}'
10.68.0.2
kubectl get cm -n openvpn openvpn -oyaml
push "dhcp-option DNS 10.68.0.2"

image

image

image

image