X-pack 6.4.0 破解

X-package 6.4.0 破解

获取x-pack-core-6.4.0.jar

下载 elasticsearch
下载页面:https://www.elastic.co/downloads/elasticsearch

下载下来,解压压缩包,x-pack-core-6.4.0.jar 就位于 elasticsearch-6.4.0/modules/x-pack-core 目录下面

使用luyten反编译x-pack-core-6.4.0.jar

下载 luyten
下载页面:https://github.com/deathmarine/Luyten/releases

软件下载下来,打开软件,把x-pack-core-6.4.0.jar 丢进去,就能看到我们jar包的源代码了。
我们需要把2个文件提取出来进行修改。
org.elasticsearch.license.LicenseVerifier
org.elasticsearch.xpack.core.XPackBuild

1、修改LicenseVerifier
LicenseVerifier 中有两个静态方法,这就是验证授权文件是否有效的方法,我们把它修改为全部返回true.

package org.elasticsearch.license;

import java.nio.*;
import org.elasticsearch.common.bytes.*;
import java.util.*;
import java.security.*;
import org.elasticsearch.common.xcontent.*;
import org.apache.lucene.util.*;
import org.elasticsearch.core.internal.io.*;
import java.io.*;

public class LicenseVerifier
{
    public static boolean verifyLicense(final License license, final byte[] encryptedPublicKeyData) {
        return true;
    }

    public static boolean verifyLicense(final License license) {
        return true;
    }
}

2、修改XPackBuild
XPackBuild 中最后一个静态代码块中 try的部分全部删除,这部分会验证jar包是否被修改.

package org.elasticsearch.xpack.core;

import org.elasticsearch.common.io.*;
import java.net.*;
import org.elasticsearch.common.*;
import java.nio.file.*;
import java.io.*;
import java.util.jar.*;

public class XPackBuild
{
    public static final XPackBuild CURRENT;
    private String shortHash;
    private String date;

    @SuppressForbidden(reason = "looks up path of xpack.jar directly")
    static Path getElasticsearchCodebase() {
        final URL url = XPackBuild.class.getProtectionDomain().getCodeSource().getLocation();
        try {
            return PathUtils.get(url.toURI());
        }
        catch (URISyntaxException bogus) {
            throw new RuntimeException(bogus);
        }
    }

    XPackBuild(final String shortHash, final String date) {
        this.shortHash = shortHash;
        this.date = date;
    }

    public String shortHash() {
        return this.shortHash;
    }

    public String date() {
        return this.date;
    }

    static {
        final Path path = getElasticsearchCodebase();
        String shortHash = null;
        String date = null;
        Label_0157: {
            shortHash = "Unknown";
            date = "Unknown";
        }
        CURRENT = new XPackBuild(shortHash, date);
    }
}

编译修改后的java文件

javac -cp ".:./x-pack-core-6.3.2.jar:./elasticsearch-6.4.0/lib/*" LicenseVerifier.java
javac -cp ".:./x-pack-core-6.3.2.jar:./elasticsearch-6.4.0/lib/*" XPackBuild.java

需要注意的是,编译这两个文件的时候 需要指定依赖包的位置 如果你的位置和我的有区别,注意修改。

将编译好的class文件重新压回x-pack-core-6.4.0.jar

解压x-pack-core-6.4.0.jar 会得到一个 x-pack-core-6.4.0目录,按照其位置将编译好的2个 class文件放到我们目录里面,替换老的。

将修改过的 x-pack-core-6.4.0目录重新压成jar包。
https://stackoverflow.com/questions/18146361/how-to-create-jar-file-with-package-structure

jar -cvf x-pack-core-6.4.0_new.jar -C x-pack-core-6.4.0 .

导入授权文件

1、 先从官网申请basic授权文件
https://license.elastic.co/registration

2、 授权文件修改

{

   "uid": "6fb96d6b-938c-45ff-9ce7-6b53b39cd7dd",

   "type": "platinum", # 修改授权为白金版本

   "issue_date_in_millis": 1530489600000,

   "expiry_date_in_millis": 2855980923000, #修改到期时间为2060-07-02

   "max_nodes": 100,  # 修改最大节点数

   "issued_to": "xxxx",

   "issuer": "Web Form",

   "signature":"AAAAAwAAAA3PP60wKNtAvRmuCGdSAAABmC9ZN0hjZDBGYnVyRXpCOW5Bb3FjZDAxOWpSbTVoMVZwUzRxVk1PSmkxaktJRVl5MUYvUWh3bHZVUTllbXNPbzBUemtnbWpBbmlWRmRZb25KNFlBR2x0TXc2K2p1Y1VtMG1UQU9TRGZVSGRwaEJGUjE3bXd3LzRqZ05iLzRteWFNekdxRGpIYlFwYkJiNUs0U1hTVlJKNVlXekMrSlVUdFIvV0FNeWdOYnlESDc3MWhlY3hSQmdKSjJ2ZTcvYlBFOHhPQlV3ZHdDQ0tHcG5uOElCaDJ4K1hob29xSG85N0kvTWV3THhlQk9NL01V",

   "start_date_in_millis": 1530489600000

} 

时间戳、时间转换

https://tool.lu/timestamp

3.通过API接口上传
curl -u elastic:elastic -XPUT ‘http://es-ip:port/_xpack/license’ -H “Content-Type: application/json” -d @/tmp/license.json

Ceph监控管理平台Calamari搭建

环境

已有: CentOS 7.3(基础设施服务器) Ceph 10.2.10(jewel)
后部署:Calamari 1.3.3

介绍

Calamari包含的组件主要有 calamari-server;romana;salt-minion;salt-master;diamond。

这些模块各自的作用:
– calamari-server 这个是提供一个与集群进行交互,并且自己封装了一个自己的API,做集中管理的地方,这个只需要在集群当中的某一台机器上安装,也可以独立安装
– romana 就是原来的calamari-client,这个叫client,其实是一个web的界面,这个叫calamari-web更好,现在已经更名为romana,这个也是只需要在集群当中的某一台机器上安装,也可以独立安装,这个需要跟calamari-server安装在一台机器上
– salt-master 是一个管理的工具,可以批量的管理其他的机器,可以对安装了salt-minion的机器进行管理,在集群当中,这个也是跟calamari-server安装在一起的
– salt-minion 是安装在集群的所有节点上的,这个是接收salt-master的指令对集群的机器进行操作,并且反馈一些信息到salt-master上
– diamond 这个是系统的监控信息的收集控件,提供集群的硬件信息的监控和集群的信息的监控,数据是发送到romana的机器上的,是由romana上的carbon来收取数据并存储到机器当中的数据库当中的

准备

防火墙配置

firewall-cmd --add-port=4505/tcp --zone=public --permanent
firewall-cmd --add-port=4506/tcp --zone=public --permanent
firewall-cmd --add-port=2003/tcp --zone=public --permanent
firewall-cmd --add-port=2004/tcp --zone=public --permanent
firewall-cmd --add-port=80/tcp --zone=public --permanent
firewall-cmd --reload

安装

节点情况 需要安装软件
Calamari服务端 calamri-server romana salt-master
集群节点 salt-minion diamond

如果在某集群节点安装calamari,则以上都需要安装

以下涉及的swarm1均为calamari 和salt-master服务端的hostname

本文不采用ceph-deploy calamari connect node 方式安装。

==============================calamari端=================================
1、软件包解压
下载,并上传到服务器。链接: https://pan.baidu.com/s/1uCcRIhDwGX1OSeK1aKCnGQ 密码: 6mut

unzip centosjewel.zip
cd centosjewel/

2、添加saltstack 的repo文件

# mv saltstack-rhel7.repo /etc/yum.repos.d/
# yum clean all; yum repolist

3、本地安装calamari romana

# yum localinstall calamari-server-1.3.3-jewel.el7.centos.x86_64.rpm romana-1.2.2-36_gc62bb5b.el7.centos.x86_64.rpm

4、调整目录权限

# chmod 777 -R /var/log/calamari/
# chmod 777 -R /opt/calamari/

需要先配置被监控端再继续calamari的相关操作!

===========================被监控端(集群节点)==============================

1、安装salt-minion diamond

1.1、添加saltstack 的repo文件

# mv saltstack-rhel7.repo /etc/yum.repos.d/
# yum clean all; yum repolist

1.2、安装

# yum localinstall salt salt-minion

2、配置salt-minion

2.1、修改minion文件(admin均为calamari的hostname)

# sed -i 's/#master: salt/swarm1: admin/' /etc/salt/minion

2.2、添加minion.d附加目录及calamari.conf文件

# mkdir /etc/salt/minion.d/
# echo 'master: swarm1'> /etc/salt/minion.d/calamari.conf
# systemctl restart salt-minion
# systemctl enable salt-minion

3、配置diamond

将host指向calamari服务端hostname

# cd /etc/diamond/ && cp diamond.conf.example diamond.conf
# sed -i '/^host/s/graphite/swarm1/' /etc/diamond/diamond.conf
# systemctl restart diamond
# systemctl enable diamond

注:某些机子上面出现了systemctl无法识别diamond服务,在确保已经安装后添加即可

chkconfig --add diamond

============================calamari端(续)===============================

6、salt-master相关

!!!被监控端(集群节点)部署完成后再执行以下命令!!!

6.1重启

# systemctl restart salt-master

6.2 管理salt-minion节点

# salt-key -L               列出已发现的节点
# salt-key -A                允许所有节点加入

成功后你将观察到注册节点由Unaccepted Keys 转为 Accepted keys下

6.3 检测节点

# salt '*' test.ping           检测链接性

如果出现如下报错,检查client端/etc/salt/minion.d/calamari.conf里的master项是否为calamari端的hostname

Minion did not return. (No response)
# salt '*' ceph.get_heartbeats      输出ceph集群信息(过多不贴了)

7、初始化calamari配置

# calamari-ctl initialize

根据提示输入管理界面的用户名和密码

# chmod 777 -R /var/log/calamari/
# chmod 777 -R /opt/calamari/

8、web访问及问题

以上步骤均成功后在浏览器地址栏输入calamari server端IP登录(默认80端口)即可

故障排除

如果提示 服务器内部错误
解决办法:清理配置再初始化一次

# calamari-ctl clear --yes-i-am-sure
# calamari-ctl initialize (无交互)

如果提示 New Calamari Installation

解决办法:
重启 被监控端的salt-minion和 diamond
以及 server 端的salt-master

注:salt-minion 必须有一个mon节点,否则也会报这个错。

参考资料1
参考资料2
参考资料3

Docker Swarm + CEPH 搭建过程

准备工作

硬件准备

我准备了3台虚拟化机器, 安装的是centos7系统(CentOS-7-x86_64-Everything-1708.iso)的最精简安装。

网络规划

服务器的 IP地址规划为:
swarm1:10.255.255.61
swarm2:10.255.255.62
swarm3:10.255.255.63

主机名和IP地址的映射我在路由器上配置好的,如果你没有配置dns,最好在本地hosts文件上配置好,以免发生意想不到的状况。

搭建目标

我的搭建目标是搭建一套docker swarm平台, 网络用 swarm的 overlay网络,存储使用ceph。

一些说明

由于之前没用接触过ceph存储,在百度 google一番之后,搜到很多 使用docker搭建ceph的教程(可能我搜索关键词有毛病。。汗)。
然后我就顺着教程进行搭建,然后其过程痛苦不堪,搞了2天,反正最终是没搞起来。
将要放弃之际,看了看ceph的官方文档(http://docs.ceph.org.cn/start/) ,发现居然有ceph-deploy这么神奇的东西。
然后就顺着教程搭建了一下,第一把搭建是比较坑的。后来查了下网络,有人说需要配置网络代理,有些包不用代理下不下来的,
然后我配置了下代理,顺利多了,根据教程就能很容易的搭建起来。代理配置

vim /etc/environment

在里面输入
http_proxy="http://username:password@proxy_server:port"

然后在命令行上
export http_proxy="http://username:password@proxy_server:port"

每个节点都需要配置

CEPH搭建

主要参考CEPH官方教程

预检

http://docs.ceph.org.cn/start/quick-start-preflight/

安装 CEPH 部署工具

由于我们用的centos7系统,使用RPM包的形式安装。

sudo yum install -y yum-utils && sudo yum-config-manager --add-repo https://dl.fedoraproject.org/pub/epel/7/x86_64/ && sudo yum install --nogpgcheck -y epel-release && sudo rpm --import /etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL-7 && sudo rm /etc/yum.repos.d/dl.fedoraproject.org*

把软件包源加入软件仓库。用文本编辑器创建一个 YUM (Yellowdog Updater, Modified) 库文件,其路径为 /etc/yum.repos.d/ceph.repo

vim /etc/yum.repos.d/ceph.repo

[Ceph-noarch]
name=Ceph noarch packages
baseurl=http://download.ceph.com/rpm-jewel/el7/noarch
enabled=1
gpgcheck=1
type=rpm-md
gpgkey=https://download.ceph.com/keys/release.asc
priority=1

更新软件库并安装 ceph-deploy

sudo yum update && sudo yum install ceph-deploy

NPT&用户

官方教程建议安装NTP(http://docs.ceph.org.cn/start/quick-start-preflight/#ntp) ,防止主机时间问题导致毛病。我检查下我的主机时间,发现没毛病,偷懒就没配置。
官方教程建议使用非root用户来安装,需建用户(http://docs.ceph.org.cn/start/quick-start-preflight/#ntp) 我偷懒直接用了root用户,就没进行配置。

允许无密码 SSH 登录

正因为 ceph-deploy 不支持输入密码,你必须在管理节点上生成 SSH 密钥并把其公钥分发到各 Ceph 节点。 ceph-deploy 会尝试给初始 monitors 生成 SSH 密钥对。
生成 SSH 密钥对,但不要用 sudo 或 root 用户。提示 “Enter passphrase” 时,直接回车,口令即为空:

ssh-keygen

Generating public/private key pair.
Enter file in which to save the key (/ceph-admin/.ssh/id_rsa):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /ceph-admin/.ssh/id_rsa.
Your public key has been saved in /ceph-admin/.ssh/id_rsa.pub.

把公钥拷贝到各 Ceph 节点,把下列命令中的 {username} 替换成前面创建部署 Ceph 的用户里的用户名。

ssh-copy-id root@swarm1
ssh-copy-id root@swarm2
ssh-copy-id root@swarm3

开放所需端口

Ceph Monitors 之间默认使用 6789 端口通信, OSD 之间默认用 6800:7300 这个范围内的端口通信。详情见网络配置参考。 Ceph OSD 能利用多个网络连接进行与客户端、monitors、其他 OSD 间的复制和心跳的通信。
某些发行版(如 RHEL )的默认防火墙配置非常严格,你可能需要调整防火墙,允许相应的入站请求,这样客户端才能与 Ceph 节点上的守护进程通信。
对于 RHEL 7 上的 firewalld ,要对公共域开放 Ceph Monitors 使用的 6789 端口和 OSD 使用的 6800:7300 端口范围,并且要配置为永久规则,这样重启后规则仍有效。例如:
三台机器都必须要配置。

sudo firewall-cmd --zone=public --add-port=6789/tcp --permanent
sudo firewall-cmd --zone=public --add-port=6800-7300/tcp --permanent
sudo firewall-cmd --reload

SELINUX

在 CentOS 和 RHEL 上, SELinux 默认为 Enforcing 开启状态。为简化安装,我们建议把 SELinux 设置为 Permissive 或者完全禁用,也就是在加固系统配置前先确保集群的安装、配置没问题。用下列命令把 SELinux 设置为 Permissive :

sudo setenforce 0

永久关闭 vim /etc/selinux/config

SELINUX=disabled

这是我的准备工作配置,如果你在配置过程中遇到问题,最好还是看看官方的文档

存储集群快速安装

参考的文档

创建配置目录

我们创建一个 Ceph 存储集群,它有一个 Monitor 和两个 OSD 守护进程。一旦集群达到 active + clean 状态,再扩展它:增加第三个 OSD 、增加元数据服务器和两个 Ceph Monitors。为获得最佳体验,先在管理节点上创建一个目录,用于保存 ceph-deploy 生成的配置文件和密钥对。

mkdir /opt/my-cluster
cd /opt/my-cluster

ceph-deploy 会把文件输出到当前目录,所以请确保在此目录下执行 ceph-deploy

创建集群

如果在某些地方碰到麻烦,想从头再来,可以用下列命令清除配置:

ceph-deploy purge {ceph-node} [{ceph-node}]
ceph-deploy forgetkeys

在管理节点上,进入刚创建的放置配置文件的目录,用 ceph-deploy 执行如下步骤。

ceph-deploy new swarm1

把 Ceph 配置文件里的默认副本数从 3 改成 2 ,这样只有两个 OSD 也可以达到 active + clean 状态。把下面这行加入 [global] 段:

osd pool default size = 2

安装 Ceph

ceph-deploy install swarm1 swarm2 swarm3

配置初始 monitor(s)、并收集所有密钥:

ceph-deploy mon create-initial

完成上述操作后,当前目录里应该会出现这些密钥环:
{cluster-name}.client.admin.keyring
{cluster-name}.bootstrap-osd.keyring
{cluster-name}.bootstrap-mds.keyring
{cluster-name}.bootstrap-rgw.keyring

添加两个 OSD 。为了快速地安装,这篇快速入门把目录而非整个硬盘用于 OSD 守护进程。如何为 OSD 及其日志使用独立硬盘或分区,请参考 ceph-deploy osd 。登录到 Ceph 节点、并给 OSD 守护进程创建一个目录。

ssh swarm2
sudo mkdir /var/local/osd2
exit

ssh swarm3
sudo mkdir /var/local/osd3
exit

然后,从管理节点执行 ceph-deploy 来准备 OSD 。

ceph-deploy osd prepare swarm2:/var/local/osd2 swarm3:/var/local/osd3

最后,激活 OSD 。

ceph-deploy osd activate swam2:/var/local/osd2 swarm3:/var/local/osd3

用 ceph-deploy 把配置文件和 admin 密钥拷贝到管理节点和 Ceph 节点,这样你每次执行 Ceph 命令行时就无需指定 monitor 地址和 ceph.client.admin.keyring 了。

ceph-deploy admin swarm1 swarm2 swarm3

确保你对 ceph.client.admin.keyring 有正确的操作权限。

chmod +r /etc/ceph/ceph.client.admin.keyring

检查集群的健康状况。

ceph health

至此,CEPH集群搭建完毕,如果一切OK的话,最好给系统打个快照。

Docker Swarm集群搭建

docker安装

在3台机器上执行安装操作(参考地址

sudo yum remove docker \
                  docker-client \
                  docker-client-latest \
                  docker-common \
                  docker-latest \
                  docker-latest-logrotate \
                  docker-logrotate \
                  docker-selinux \
                  docker-engine-selinux \
                  docker-engine

sudo yum install -y yum-utils \
  device-mapper-persistent-data \
  lvm2

sudo yum-config-manager \
    --add-repo \
    https://download.docker.com/linux/centos/docker-ce.repo

sudo yum install docker-ce

sudo systemctl start docker
sudo systemctl enable docker

docker就安装好了。

docker swarm初始化

参考教程 参考教程2
先把网络配置好
You need the following ports open to traffic to and from each Docker host participating on an overlay network:
* TCP port for cluster management communications
* TCP and UDP port 7946 for communication among nodes
* UDP port 4789 for overlay network traffic

firewall-cmd --add-port=2377/tcp --zone=public --permanent
firewall-cmd --add-port=7946/tcp --zone=public --permanent
firewall-cmd --add-port=7946/udp --zone=public --permanent
firewall-cmd --add-port=4789/udp --zone=public --permanent
firewall-cmd --reload

在swarm1节点上执行

docker swarm init --advertise-addr 10.255.255.61

docker swarm join --token SWMTKN-1-3i2e61fn9hgue070k2qnmsd7mcaapc9jwa2r1wyuao6rlyytr9-7wim3etfq5imqsywklutstacx 10.255.255.61:2377

他会给你一个其他节点加入集群的命令, 然后在其他节点上执行这条命令就可以把 swarm2 swarm3 加入到集群了。
这里有个超级巨坑要注意下, swarm初始化完了会创建一个ingress网络,我当时配置好,启动服务之后外部怎么都访问不了,郁闷死我了。后来查了半天,原来是ingress的网段和我服务器的网段冲突了。
我主机的默认网络是 10.255.255.0/24的, 这个ingress的默认网络是10.255.0.0/16的,是冲突的,真是晕死。
如果你在实践的时候,最好检查下网络,如果发现冲突,或者想重新规划,只需要删除这个网络,再重新创建就行了。参考这里

docker network ls

找到冲突的网络id

docker network rm network-id

docker network create \
  --driver overlay \
  --ingress \
  --subnet=10.200.0.0/24 \
  --gateway=10.200.0.2 \
  ingress

初始化一个你自己的服务

docker service create --name=nginx --publish 80:80  nginx

我在初始化集群的时候还遇到个服务不能转发的问题,搞了半天,也很郁闷, 然后我创建一个简单docker应用,暴露端口,然后docker提示我 网络转发没有开,服务可能不能正常访问。
如果你也遇到这个问题,尝试这样操作:参考这里

vim /etc/sysctl.conf
net.ipv4.ip_forward=1

systemctl restart network
sysctl -p

至此你的docker swarm集群应该初始化完毕了。

Portainer安装

portainer是一个用来管理docker swarm集群的可视化工具,非常好用。
先创建个网络

docker network create --driver overlay portainer_agent_network

配置portainer代理,这是全局的,所有节点上都会运行这个代理

docker service create \
    --name portainer_agent \
    --network portainer_agent_network \
    -e AGENT_CLUSTER_ADDR=tasks.portainer_agent \
    --mode global \
    --mount type=bind,src=//var/run/docker.sock,dst=/var/run/docker.sock \
    portainer/agent

创建一个portainer持久化目录

mkdir /opt/portainer_data

创建portainer服务

docker service create \
    --name portainer \
    --network portainer_agent_network \
    --publish 9000:9000 \
    --replicas=1 \
    --constraint 'node.role == manager' \
    --mount type=bind,source=/opt/portainer_data,destination=/data \
    portainer/portainer:latest -H "tcp://tasks.portainer_agent:9001" --tlsskipverify

你可以通过访问任意节点的 9000端口来访问portainer,初始化个密码就可以进行管理了。

rexray/rbd安装

ceph装好了,docker swarm也装好了,如何来使用呢。
你需要装个docker插件 具体参考

docker plugin install rexray/rbd RBD_DEFAULTPOOL=rbd

装好之后,你就可以创建你的存储卷了

docker volume create --driver rexray/rbd:latest cephvol1

你也可以在portainer界面上进行创建。
Requirements

The RBD plug-in requires that the host has a fully working environment for mapping Ceph RBDs, including having the RBD kernel module already loaded. The cluster configuration and authentication files must be present in /etc/ceph.

至此,全部完成了。后面的我还没实践,等实践了再来更新。

——————–分割线—————-

我又来了, 上面其实还没搞完。

在上面的基础上,我发现可以创建 基于rexray/rbd的存储卷, 然后在我尝试把存储挂到我容器上面的时候,容器怎么都启动不了。
搞了半天,看系统日志有报错,看到 docker 有尝试 和 “unix:///var/run/rexray/998287049.sock” 联系,但是联系不上。
查了些资料,思考了下,感觉 应该是这样玩的。
我前面装了个 rexray/rbd 只是个 rexray给rbd存储写的插件, 我们还需要装个rexray才可以,看了rexray官网文档,也确实是这样。

那么开干吧。参考文档 (IBM) (REXRAY

安装 rexray

curl -sSL https://dl.bintray.com/emccode/rexray/install | sh -

在每个节点上面都要安装。

然后启动rexray

rexray start

也可以debug模式启动
REXRAY_DEBUG=true rexray start

然后再尝试 创建卷,挂载卷, 好像可以玩了。 噢耶。

配置rexray为服务

Install as a service with

rexray install

This will register itself with SystemD or SystemV for proper initialization.

Once configured the REX-Ray service can be started with the command ‘sudo systemctl start rexray’.

增加rexray的配置

配置生成器

Rexray 官方文档

再次更新,关于 权限问题。
在我尝试给NGINX挂载一个卷之后,访问发现nginx返回的是403,看了下日志,报的是 permission deny,没有读文件权限。
查看了文件权限之后 是 700, root的权限,nginx没权限读取文件的。
然后研究了很多文章,查了很多文档。然后目前可以通过这样配置来修改权限。

docker plugin install rexray/rbd RBD_DEFAULTPOOL=rbd LINUX_VOLUME_FILEMODE=0777

然后我把插件卸载了,重新安装了下,目前看来权限是OK的,可以控制。 参考文档
参考文档

portainer的二次开发

一、准备环境
依赖:Docker, Node.js >= 0.8.4 和 npm

[root@dev_08 ~]# curl --silent --location https://rpm.nodesource.com/setup_7.x | sudo bash -
[root@dev_08 ~]# yum install -y nodejs
[root@dev_08 ~]# npm install -g grunt-cli

二、构建
1、checkout
[root@dev_08 ~]# cd /opt
先 fork 一个 portainer的分支,然后 clone 到本地, 然后在 branch 上开发,例如:
[root@dev_08 opt]# git clone https://github.com/opera443399/portainer.git
[root@dev_08 opt]# cd portainer
[root@dev_08 portainer]# git checkout -b feat-add-container-console-on-task-details
Switched to a new branch 'feat-add-container-console-on-task-details'
[root@dev_08 portainer]# git branch
  develop
* feat-add-container-console-on-task-details


2、使用 npm 安装依赖包
[root@dev_08 portainer]# npm install -g bower && npm install

3、根目录没有这个目录: bower_components 的话则执行
[root@dev_08 portainer]# bower install --allow-root


4、针对 centos 执行
[root@dev_08 portainer]# ln -s /usr/bin/sha1sum /usr/bin/shasum


5、构建 app
[root@dev_08 portainer]# grunt build

如果遇到这样的错误:
Building portainer for linux-amd64
/go/src/github.com/portainer/portainer/crypto/crypto.go:4:2: cannot find package "golang.org/x/crypto/bcrypt" in any of:
    /usr/local/go/src/golang.org/x/crypto/bcrypt (from $GOROOT)
    /go/src/golang.org/x/crypto/bcrypt (from $GOPATH)
/go/src/github.com/portainer/portainer/http/handler/websocket.go:21:2: cannot find package "golang.org/x/net/websocket" in any of:
    /usr/local/go/src/golang.org/x/net/websocket (from $GOROOT)
    /go/src/golang.org/x/net/websocket (from $GOPATH)
mv: cannot stat ‘api/cmd/portainer/portainer-linux-amd64’: No such file or directory
Warning: Command failed: build/build_in_container.sh linux amd64
mv: cannot stat ‘api/cmd/portainer/portainer-linux-amd64’: No such file or directory
 Use --force to continue.

Aborted due to warnings.

那是因为网络可达性问题,国内访问 golang.org 异常。
[root@dev_08 portainer]# host golang.org
golang.org is an alias for golang-consa.l.google.com.
golang-consa.l.google.com has address 216.239.37.1


导致这2个依赖下载失败:
golang.org/x/crypto/bcrypt
golang.org/x/net/websocket


解决方法:
[root@dev_08 portainer]# go get github.com/golang/crypto/tree/master/bcrypt
[root@dev_08 portainer]# go get github.com/golang/net/tree/master/websocket

[root@dev_08 portainer]# cd $GOPATH/src
[root@dev_08 src]# mkdir golang.org/x -p
[root@dev_08 src]# mv github.com/golang/* golang.org/x/


然后再切换到源码目录,调整构建脚本: 
[root@dev_08 src]# cd /opt/portainer
[root@dev_08 portainer]# vim build/build_in_container.sh
挂载本地的 $GOPATH/src/golang.org 到容器路径:/go/src/golang.org

docker run --rm -tv $(pwd)/api:/src -e BUILD_GOOS="$1" -e BUILD_GOARCH="$2" portainer/golang-builder:cross-platform /src/cmd/portainer

调整为:

docker run --rm -tv $(pwd)/api:/src -v $GOPATH/src/golang.org:/go/src/golang.org -e BUILD_GOOS="$1" -e BUILD_GOARCH="$2" portainer/golang-builder:cross-platform /src/cmd/portainer


最后重新构建一次:
[root@dev_08 portainer]# grunt build
(略)
Cleaning "dist/js/angular.37dfac18.js"...OK
Cleaning "dist/js/portainer.cab56db9.js"...OK
Cleaning "dist/js/vendor.4edc9b0f.js"...OK
Cleaning "dist/css/portainer.e7f7fdaa.css"...OK

Done, without errors.


看到上述输出,表示符合预期。




6、运行(可以自动重启)
[root@dev_08 portainer]# grunt run-dev

访问 UI 地址: http://localhost:9000



7、不要忘记 lint 代码
[root@dev_08 portainer]# grunt lint



8、release(通常我们使用 linux-amd64 这个平台,具体过程请参考脚本 build.sh)
[root@dev_08 portainer]# grunt "release:linux:amd64"
(略)
Done, without errors.
[root@dev_08 portainer]# ls dist/
css  fonts  ico  images  index.html  js  portainer-linux-amd64
[root@dev_08 portainer]# mv dist/portainer-linux-amd64 dist/portainer



9、打包成镜像
[root@dev_08 portainer]# docker build -t 'opera443399/portainer:dev' -f build/linux/Dockerfile .



10、测试上述镜像
[root@dev_08 portainer]# mkdir -p /data/portainer_dev
[root@dev_08 portainer]# docker run -d -p 9001:9000 -v /var/run/docker.sock:/var/run/docker.sock -v /data/portainer_dev:/data --name portainer_dev opera443399/portainer:dev
[root@dev_08 portainer]# docker ps -l
CONTAINER ID        IMAGE                                   COMMAND             CREATED             STATUS              PORTS                    NAMES
cbd986df765b        opera443399/portainer:dev               "/portainer"        8 seconds ago       Up 7 seconds        0.0.0.0:9001->9000/tcp   portainer_dev

首次使用时将初始化一个管理员账户(本例使用 httpie 来提交)
[root@dev_08 portainer]# http POST :9001/api/users/admin/init Username="admin" Password="Develop"
HTTP/1.1 200 OK
Content-Length: 0
Content-Type: text/plain; charset=utf-8
Date: Tue, 10 Oct 2017 08:18:19 GMT
X-Content-Type-Options: nosniff
X-Frame-Options: DENY

访问页面:your_dev_ip:9001
验证功能:符合预期

清理:
[root@dev_08 portainer]# docker rm -f portainer_dev




三、开发需求示例
可结合 github 的搜索功能来查找关键字。

1、需求:在 'Container list' 页面,设定初始化时,默认不显示所有容器(即默认不勾选 'Show all containers' 这个复选框)
注1:从 1.14.1 版本开始使用 cookie 来记录是否显示所有的状态(Persist the status of the show all containers filter: #1198),其实完全可以不更改代码,去掉 checkbox 的选择后,下次登录还是 unchecked 的状态,本例仅作为修改代码的一个 howto 来展示。
注2:从 1.14.1 版本开始,新增了针对资源的限制(Add the ability to manage CPU/MEM limits & reservations for Swarm services: #516),不妨一试。
https://github.com/portainer/portainer/releases



为了达到我们的小目标,需要调整 filter_containerShowAll 在初始化时的默认值为 false 即可。

[root@dev_08 portainer]# diff -u /tmp/localStorage.js app/services/localStorage.js
--- /tmp/localStorage.js   2017-09-26 15:11:41.062167776 +0800
+++ app/services/localStorage.js   2017-09-26 15:21:11.604992708 +0800
@@ -50,7 +50,7 @@
     getFilterContainerShowAll: function() {
       var filter = localStorageService.cookie.get('filter_containerShowAll');
       if (filter === null) {
-        filter = true;
+        filter = false;
       }
       return filter;
     }



2、需求:在 'Service details' 页面的 'Tasks' 标签页中增加一个过滤器
当前操作列出了所有的 tasks
而我有时候只需要查看处于 running 状态的 tasks 即可,因而需要一个过滤器,改动代码如下:

[root@dev_08 portainer]# diff -u /tmp/tasks.html app/components/service/includes/tasks.html
--- /tmp/tasks.html    2017-09-26 18:00:45.893169748 +0800
+++ app/components/service/includes/tasks.html 2017-09-26 18:22:10.188208664 +0800
@@ -12,6 +12,11 @@
       </select>
       </div>
     </rd-widget-header>
+    <rd-widget-taskbar classes="col-lg-12">
+      <div class="pull-right">
+        <input type="text" id="filter" ng-model="state.filter" placeholder="Filter..." class="form-control input-sm" />
+      </div>
+    </rd-widget-taskbar>
     <rd-widget-body classes="no-padding">
       <table class="table">
       <thead>
@@ -48,7 +53,7 @@
         </tr>
       </thead>
       <tbody>
-        <tr dir-paginate="task in (filteredTasks = ( tasks | orderBy:sortType:sortReverse | itemsPerPage: state.pagination_count))">
+        <tr dir-paginate="task in (filteredTasks = ( tasks | filter:state.filter | orderBy:sortType:sortReverse | itemsPerPage: state.pagination_count))">
         <td><a ui-sref="task({ id: task.Id })" class="monospaced">{{ task.Id }}</a></td>
         <td><span class="label label-{{ task.Status.State|taskstatusbadge }}">{{ task.Status.State }}</span></td>
         <td ng-if="service.Mode !== 'global'">{{ task.Slot }}</td>


具体请参考:
https://github.com/portainer/portainer/pull/1242



3、需求:在 'Service details > Tasks > Task details' 页面增加一个 Container Console 按钮
当前操作要找到 service 下所有容器的 Console 并打开,则要执行下述步骤:
1 个 service 对应 N 个 tasks(containers) (运行在 M nodes 中)

    -> 打开 'Service Details' 页面,过滤处于 running 状态的 tasks 并注意对应的 node 名称  
    -> 选择 node N1 并切换到 endpoint N1
    -> 在 N1 的 'Container List' 页面上通过过滤器找到 container C1 
    -> 在 'Container Details' 页面打开 Console


为了简化操作,实现咱们的小目标,改动代码如下:
    -> 在 'Service details > tasks' 页面,打开 'Task details' 页面时跳转到新的标签页
    -> 在 'Task details' 页面,新增 container console 按钮,新增一个提示框,提示后续步骤遇到错误时如何处理
    -> 点击 Console 按钮打开 'Container console' 页面时,跳转到新的标签页
            如果该 Container 不在当前 endpoint 中运行,则有一个错误通知:
                'Failure: Docker container identifier not found'
            此时,我们要做的就是一个 endpoint 切换的操作
    -> 当我们切换 endpoint 时,不是跳转页面到 dashboard 页面,而仅仅使用一个消息通知,然后等待页面刷新后,得到的页面即我们想要的。
    -> 如果我们切换到 swarm worker node 则后续需要一个切换回 swarm manager node 的操作
    -> 小结:用户体验有点糟糕,但能工作。




[root@dev_08 portainer]# diff -u /tmp/tasks.html app/components/service/includes/tasks.html
--- /tmp/tasks.html    2017-10-12 11:55:23.247181711 +0800
+++ app/components/service/includes/tasks.html 2017-10-12 10:12:52.653767466 +0800
@@ -54,7 +54,7 @@
       </thead>
       <tbody>
         <tr dir-paginate="task in (filteredTasks = ( tasks | filter:state.filter | orderBy:sortType:sortReverse | itemsPerPage: state.pagination_count))">
-        <td><a ui-sref="task({ id: task.Id })" class="monospaced">{{ task.Id }}</a></td>
+        <td><a ui-sref="task({ id: task.Id })" class="monospaced" target="_blank">{{ task.Id }}</a></td>
         <td><span class="label label-{{ task.Status.State|taskstatusbadge }}">{{ task.Status.State }}</span></td>
         <td ng-if="service.Mode !== 'global'">{{ task.Slot }}</td>
         <td>{{ task.NodeId | tasknodename: nodes }}</td>



[root@dev_08 portainer]# diff -u /tmp/task.html app/components/task/task.html 
--- /tmp/task.html 2017-10-11 14:22:29.782178036 +0800
+++ app/components/task/task.html  2017-10-12 10:13:28.367767064 +0800
@@ -44,7 +44,12 @@
             </tr>
             <tr ng-if="task.Status.ContainerStatus.ContainerID">
               <td>Container ID</td>
-              <td>{{ task.Status.ContainerStatus.ContainerID }}</td>
+              <td title="Notify 'Failure: Docker container identifier not found' -> Try 'Switch endpoint to the node where the container is running'">
+                {{ task.Status.ContainerStatus.ContainerID }}
+                <a class="btn btn-outline-secondary" target="_blank" type="button" ui-sref="console({id: task.Status.ContainerStatus.ContainerID})">
+                  <i class="fa fa-terminal space-right" aria-hidden="true"></i>Console
+                </a>
+              </td>
             </tr>
           </tbody>
         </table>



[root@dev_08 portainer]# diff -u /tmp/sidebarController.js app/components/sidebar/sidebarController.js 
--- /tmp/sidebarController.js  2017-10-12 11:57:10.063180508 +0800
+++ app/components/sidebar/sidebarController.js    2017-10-12 09:37:32.314244435 +0800
@@ -14,7 +14,7 @@
     EndpointProvider.setEndpointPublicURL(endpoint.PublicURL);
     StateManager.updateEndpointState(true)
     .then(function success() {
-      $state.go('dashboard');
+      Notifications.success('switch endpoint to: ', endpoint.Name);
     })
     .catch(function error(err) {
       Notifications.error('Failure', err, 'Unable to connect to the Docker endpoint');


具体请参考:
https://github.com/portainer/portainer/pull/1271


四、源码分析示例
1、在 UI 界面中是如何请求 docker api 来完成 container list 之类的请求的?
分析过程:

1)例如,在 Container list 页面,有如下请求:
/api/endpoints/1/docker/containers/json?all=0

列出了处于 running 状态的容器,我们去找找相关的代码。


2)找找 js 代码
[root@dev_08 portainer]# find ./app -type f -name '*.js' -exec grep -l 'docker\/containers' {} \;
./app/rest/docker/container.js
./app/rest/docker/containerLogs.js
[root@dev_08 portainer]# 

[root@dev_08 portainer]# vim app/rest/docker/container.js

angular.module('portainer.rest')
.factory('Container', ['$resource', 'API_ENDPOINT_ENDPOINTS', 'EndpointProvider', function ContainerFactory($resource, API_ENDPOINT_ENDPOINTS, EndpointProvider) {
  'use strict';
  return $resource(API_ENDPOINT_ENDPOINTS + '/:endpointId/docker/containers/:id/:action', {
    name: '@name',
    endpointId: EndpointProvider.endpointID
  },



3)找找 go 代码
[root@dev_08 portainer]# find ./api -type f -name '*.go' -exec grep -l '\/api\/endpoints' {} \;
./api/http/handler/handler.go
[root@dev_08 portainer]# 


[root@dev_08 portainer]# vim api/http/handler/handler.go
func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {

        case strings.HasPrefix(r.URL.Path, "/api/endpoints"):                                   // 该请求的 URL 是 /api/endpoints 开头的
                if strings.Contains(r.URL.Path, "/docker") {                                    // 且URL 包含 /docker
                        http.StripPrefix("/api/endpoints", h.DockerHandler).ServeHTTP(w, r)     // 去掉 /api/endpoints 变成 /1/docker/containers/json?all=0  然后交给 DockerHandler 来处理


[root@dev_08 portainer]# vim api/http/handler/docker.go
func (handler *DockerHandler) proxyRequestsToDockerAPI(w http.ResponseWriter, r *http.Request) {

        http.StripPrefix("/"+id+"/docker", proxy).ServeHTTP(w, r)                               // 去掉 /1/docker 变成 /containers/json?all=0  (这就是最终请求 docker api 的路径)


通过上述代理行为,最终变成请求类似这样的 docker api (具体的请求是走的本地 socket 还是 tcp 的行为尚未分析)

curl -s \
    --unix-socket /var/run/docker.sock \
    http:/containers/json?all=0 

或者:

curl -s \
    http://endpoint_ip:port/containers/json?all=0 




五、FAQ
1、为何不能统一管理 swarm mode 集群下的资源?
状态:
我理解,这个UI的开发理念是一个简单通用的docker UI
目前的方式是:每个 node 都是一个 endpoint,要切换 endpoint 来执行 docker API 指令获取该 node 上的容器等信息。
未来计划增加 swarm mode 集群管理的功能。

具体的讨论,还请跟踪下这个 issue 相关的内容:
[FEATURE REQUEST] Be able to use all the Portainer built-in functionalities in all the containers running in a swarm cluster #461
https://github.com/portainer/portainer/issues/461



2、如何提交 github PR 来为本项目共享代码?
请先阅读:https://github.com/portainer/portainer/blob/develop/CONTRIBUTING.md

贡献代码的指引
1)通用
先看看有没有相关的 PR/issues 已经存在,如果没有则进入下一步
打开一个 issue 讨论你要带来的一些变化(也可以在已经存在 的 issue 上讨论),包括功能合理性和技术可行性等方面,在解决方案上讨论出一个解决,一致同意后,再去开发【这一步很重要,不能跨过】
在一个特定主题的分支上开发,不要在 master/develop 分支开发!
新建分支时,有一些命名规则,例如:
(type)(issue number)-text-desc

举例:
你在提交一个 bugfix 来解决 issue #361 则分支可以命名为: fix361-template-selection.
参考这里的讨论:
https://github.com/portainer/portainer/issues/1271#issuecomment-336033282

2)为已经打开的 issues 贡献代码
想做却不知道如何开始?

有一些 issues 的标签是 exp/ 开头的,意味着可以供大家去开发,但有难度的区分:
beginner: 针对不太熟系该项目代码的开发者
intermediate: 针对该项目代码有一定理解,或对 AngularJS or Golang 有使用经验的开发者
advanced: 针对该项目代码有深入理解的开发者

可以使用 Github filters 来过滤出 issues:

beginner labeled issues: https://github.com/portainer/portainer/labels/exp%2Fbeginner
intermediate labeled issues: https://github.com/portainer/portainer/labels/exp%2Fintermediate
advanced labeled issues: https://github.com/portainer/portainer/labels/exp%2Fadvanced

3)代码 Linting

检查代码使用 grunt lint 然后再提交 PR


4)提交的信息格式
 <type>(<scope>): <subject>
不要超过 100 字符,整洁的 commit log 示例:

 #271 feat(containers): add exposed ports in the containers view
 #270 fix(templates): fix a display issue in the templates view
 #269 style(dashboard): update dashboard with new layout


有如下几类 type

feat: A new feature
fix: A bug fix
docs: Documentation only changes
style: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)
refactor: A code change that neither fixes a bug or adds a feature
test: Adding missing tests
chore: Changes to the build process or auxiliary tools and libraries such as documentation generation
Scope

关于 scope :表示
提交的代码是改动了哪一块的内容,例如:networks, containers, images 可以使用 area 标签来关联 issue (例如:标签 area/containers 使用 containers 作为a scope)

关于 subject :表示
简明的描述本次提交做了那些改变

使用祈使句和现在时: "change" not "changed" nor "changes"
不要大些第一个字母
语句最后不要使用点 (.) 

使用Netcat在两台服务器之间传输文件

To send a directory, cd to inside the directory whose contents you want to send on the computer doing the sending and do:

tar -cz . | nc -q 10 -l -p 45454

On the computer receiving the contents, cd to where you want the contents to appear and do:

nc -w 10 $REMOTE_HOST 45454 | tar -xz

Replace $REMOTE_HOST with ip / hostname of computer doing the sending. You can also use a different port instead of 45454.

What’s actually happening here is that the ‘receiving’ computer is connecting to the sending computer on port 45454 and receiving the tar’d and gzip’d contents of the directory, and is passing that directly to tar (and gzip) to extract it into the current directory.

Quick example (using localhost as a remote host)

Computer 1

caspar@jumpy:~/nctest/a/mydir$ ls
file_a.txt file_b.log
caspar@jumpy:~/nctest/a/mydir$ tar -cz . | nc -q 10 -l -p 45454
Computer 2

caspar@jumpy:~/nctest/b$ ls
caspar@jumpy:~/nctest/b$ nc -w 10 localhost 45454 | tar -xz
caspar@jumpy:~/nctest/b$ ls
file_a.txt file_b.log

Linux CentOS 7 OpenVPN Server 安装配置

OpenVPN是一个开源应用程序,可以通过公共Internet创建一个专用网络。在我们开始之前,我们首先需要安装Enterprise Linux(EPEL)存储库的额外包。下面就是关于如何在CentOS 7上设置Client和OpenVPN服务器的教程。

1.安装EPEL套件库,顺道更新
yum -y install epel-release && yum -y update
2.安装 OpenVPN

首先我们需要安装OpenVPN。我们还将安装Easy RSA来生成我们的SSL密钥对,这将保护我们的VPN连接。

yum install -y openvpn easy-rsa
3.生成密钥及证书的前置条件
3.1.创建密钥与证书储存目录,并且准备好要产生密钥与证书的相关文件:
mkdir -p /etc/openvpn/easy-rsa/keys
cp -rf /usr/share/easy-rsa/3.0.3/* /etc/openvpn/easy-rsa
cp /etc/openvpn/easy-rsa/openssl-1.0.0.cnf /etc/openvpn/easy-rsa/openssl.cnf
3.2.修改凭证的默认资料,之后产生凭证时就会自动填写,不必再手动输入。
vim /etc/openvpn/easy-rsa/vars

可以在https://github.com/OpenVPN/easy-rsa 上面找到模板vars

    wget -c https://github.com/OpenVPN/easy-rsa/archive/master.zip

这里提供一个模板

set_var EASYRSA                 "$PWD"
set_var EASYRSA_PKI             "$EASYRSA/pki"
set_var EASYRSA_DN              "cn_only"
set_var EASYRSA_REQ_COUNTRY     "CN"
set_var EASYRSA_REQ_PROVINCE    "Jiangsu"
set_var EASYRSA_REQ_CITY        "Nanjing"
set_var EASYRSA_REQ_ORG         "XIAOCAICAI"
set_var EASYRSA_REQ_EMAIL       "admin@xiaocaicai.com.com"
set_var EASYRSA_REQ_OU          "XIAOCAICAI EASY CA"
set_var EASYRSA_KEY_SIZE        2048
set_var EASYRSA_ALGO            rsa
set_var EASYRSA_CA_EXPIRE       7500
set_var EASYRSA_CERT_EXPIRE     3650
set_var EASYRSA_NS_SUPPORT      "no"
set_var EASYRSA_NS_COMMENT      "XIAOCAICAI CERTIFICATE AUTHORITY"
set_var EASYRSA_EXT_DIR         "$EASYRSA/x509-types"
set_var EASYRSA_SSL_CONF        "$EASYRSA/openssl-1.0.cnf"
set_var EASYRSA_DIGEST          "sha256"
3.3.初始化PKI

首先需要把pki的目录创建好,以前的版本是会要求执行一个clean-all脚本。

$ ./easyrsa init-pki

Note: using Easy-RSA configuration from: ./vars

init-pki complete; you may now create a CA or requests.
Your newly created PKI dir is: /Users/sskaje/Work/CA/Easy/pki
3.4.创建CA

由于设置了 EASYRSA_DN 为 cn_only,所以创建CA时比较简单。
如果设置成 org 则会要求输入很多项目,或者从 vars 文件里加载。

$ ./easyrsa build-ca

Note: using Easy-RSA configuration from: ./vars
Generating a 2048 bit RSA private key
...................................................................................................................................+++
........+++
writing new private key to '/Users/sskaje/Work/CA/Easy/pki/private/ca.key.NoYqS71N95'
Enter PEM pass phrase:
Verifying - Enter PEM pass phrase:
-----
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Common Name (eg: your user, host, or server name) [Easy-RSA CA]:SSKAJE CERTIFICATE AUTHORITY 

CA creation complete and you may now import and sign cert requests.
Your new CA certificate file for publishing is at:
3.5.创建DH参数

Diffie hellman参数使用的是 openssl dhparam 创建的,openvpn文档里提到的size只有1024和2048,所以vars文件里的 EASYRSA_KEY_SIZE 我没设成4096。
easyrsa 3 的命令是
$ ./easyrsa gen-dh

Note: using Easy-RSA configuration from: ./vars
Generating DH parameters, 2048 bit long safe prime, generator 2
This is going to take a long time
...
DH parameters of size 2048 created at /Users/sskaje/Work/CA/Easy/pki/dh.pem
3.6.生成证书

生成证书的操作步骤就两步,生成请求文件,根据请求文件签发证书。
easy-rsa 3.0签发证书时要求制定type,可选的值参考x509-types目录下的文件名,包括

server:TLS服务端,适用于https服务端和vpn服务端
client:TLS客户端,适用于web浏览器和vpn客户端
ca:签发子CA证书
gen-req, build-client-full, build-server-full 可以使用 nopass 参数生成不加密的私钥。
可以使用 build-client-full 和 build-server-full 直接完成 gen-req 和 sign-req 的过程。

$ ./easyrsa build-server-full xiaocaicai-server nopass
$ ./easyrsa build-client-full xiaocaicai-client nopass
3.7.撤销证书

使用 revoke 撤销证书,命令格式如下:
./easyrsa revoke NAME

3.8.生成CRL文件

CRL是证书撤销列表,有些软件会需要,包括OpenVPN。

$ ./easyrsa gen-crl
3.9.查看证书/查看请求文件

使用 show-cert 和 show-req 查看请求文件,参数是请求时的名字。

3.10.更新数据库
$ ./easyrsa update-db
3.11.将Server的密钥及凭证放到主目录里:
cp ./pki/dh.pem ./pki/ca.crt ./pki/issued/xiaocaicai-server.crt ./pki/private/xiaocaicai-server.key /etc/openvpn
4.配置 OpenVPN server.conf

OpenVPN安装目录默认存在 server.conf 配置文件,我们拷贝一份并修改使用:
# cp /usr/share/doc/openvpn-*/sample/sample-config-files/server.conf /etc/openvpn

我们进入 /etc/openvpn/ 目录编辑 server.conf 文件。

配置完成后的内容如下:

# egrep -v "^$|^#|^;" server.conf

local 10.10.204.62
port 1194
proto udp
dev tun
ca ca.crt
cert renwoleserver.crt
key renwoleserver.key # This file should be kept secret
dh dh2048.pem
server 10.8.0.0 255.255.255.0
ifconfig-pool-persist ipp.txt
push "redirect-gateway def1 bypass-dhcp"
push "dhcp-option DNS 223.5.5.5"
client-to-client
keepalive 10 120
tls-auth ta.key 0 # This file is secret
cipher AES-256-CBC
compress lz4-v2
push "compress lz4-v2"
user nobody
group nobody
persist-key
persist-tun
status /var/log/openvpn-status.log
log /var/log/openvpn.log
verb 3
explicit-exit-notify 1
5.启动IP转发功能
# echo "net.ipv4.ip_forward = 1" >> /etc/sysctl.conf
# sysctl -p
6.配置firewall转发规则

确保服务器可以转发数据包到服务器内外网,开启必要的通讯服务,并启用IP伪装(Masquerade)功能。

# firewall-cmd --permanent --add-service openvpn
# firewall-cmd --permanent --add-masquerade
# DEV=$(ip route get 223.5.5.5 | awk 'NR==1 {print $(NF-2)}' )
# firewall-cmd --permanent --direct --passthrough ipv4 -t nat -A POSTROUTING -s 10.8.0.0/24 -o $DEV -j MASQUERADE
# firewall-cmd --reload
# firewall-cmd --list-all
7.启动 OpenVPN

重启网络与OpenVPN服务,并且让OpenVPN开机后自动启动:

# systemctl restart network
# systemctl restart openvpn@server
# systemctl enable openvpn@server