Home | 简体中文 | 繁体中文 | 杂文 | Github | 知乎专栏 | 51CTO学院 | CSDN程序员研修院 | OSChina 博客 | 腾讯云社区 | 阿里云栖社区 | Facebook | Linkedin | Youtube | 打赏(Donations) | About
知乎专栏多维度架构

6.4. Spring cloud 安全部署与性能优化

6.4.1. 环境安装

6.4.1.1. 操作系统初始化

操作系统按完成后,使用下面脚本做一次初始化

			
curl -s https://raw.githubusercontent.com/oscm/shell/master/os/personalise.sh | bash
			
			

6.4.1.2. 安装 Nginx

			
curl -s https://raw.githubusercontent.com/oscm/shell/master/web/nginx/stable/nginx.sh | bash
			
			

6.4.1.3. 安装数据库

MySQL

5.7

						
curl -s https://raw.githubusercontent.com/oscm/shell/master/database/mysql/5.7/mysql.server.sh | bash
			
				

安装完成会看到下面输出

			
2019-06-04T08:54:24.419092Z 1 [Note] A temporary password is generated for root@localhost: 8X!pE(o+urv;			
			
				

8.0

			
curl -s https://raw.githubusercontent.com/oscm/shell/master/database/mysql/8.0/server.sh | bash

curl -s https://raw.githubusercontent.com/oscm/shell/master/database/mysql/8.0/client.sh | bash			
			
				
Redis
			
curl -s https://raw.githubusercontent.com/oscm/shell/master/database/redis/source/redis-5.0.5.sh | bash			
			
				

6.4.1.4. RabbitMQ

		
curl -s https://raw.githubusercontent.com/oscm/shell/master/queue/rabbitmq/rabbitmq-server-3.7.15.sh | bash		
		
			

6.4.1.5. OpenJDK

			
curl -s https://raw.githubusercontent.com/oscm/shell/master/lang/java/openjdk/java-1.8.0-openjdk.sh | bash
			
			
Maven
				
curl -s https://raw.githubusercontent.com/oscm/shell/master/lang/java/maven/apache-maven-3.6.1.sh | bash				
				
				
Gradle
				
curl -s https://raw.githubusercontent.com/oscm/shell/master/lang/java/gradle/gradle-5.4.1.sh | bash				
				
				

6.4.1.6. Node

前端开发会用到Node.js

			
curl -s https://raw.githubusercontent.com/oscm/shell/master/lang/node.js/binrary/node-v12.4.0.sh | bash			
			
			

6.4.1.7. Gitlab

			
curl -s https://raw.githubusercontent.com/oscm/shell/master/project/gitlab/gitlab.centos7.sh | bash			
			
			

Gitlab 配置请参考 《Netkiller Project 手札》

Gitlab runner
				
curl -s https://raw.githubusercontent.com/oscm/shell/master/project/gitlab/gitlab-runner.sh | bash				
				
				

java 编译环境

去 Oracle 网站下载 java 8 jdk-8u212-linux-x64.rpm

				
yum localinstall jdk-8u212-linux-x64.rpm

curl -s https://raw.githubusercontent.com/oscm/shell/master/project/git.sh | bash
curl -s https://raw.githubusercontent.com/oscm/shell/master/lang/java/maven/apache-maven-3.6.1.sh | bash
				
				

6.4.2. 操作系统配置与优化

操作系统要求 CentOS 7 Minimal ISO

6.4.2.1. 域名

配置 DNS

修改 /etc/resolv.conf 配置 DNS 确保域名解析正确

				
echo -ne "
search example.com
nameserver 208.67.222.222
nameserver 202.67.220.220
nameserver 8.8.8.8
nameserver 4.4.4.4
" > /etc/resolv.conf
				
				
/etc/hosts

DNS 解析有延迟,很多不可控因素,将域名写入 /etc/hosts 更为保险

				
192.168.1.1		www.netkiller.cn
192.168.1.2		api.netkiller.cn
192.168.1.3		db.netkiller.cn
				
				

使用域名链接数据的例子

				
spring.datasource.url=jdbc:mysql://db.netkiller.cn:3306/neo				
				
				

DNS 不可靠的原因分析,因为DNS使用UDP传输,网络拥堵,电磁干扰等等都能导致丢包。

6.4.2.2. 历史记录操作留痕

定义 .history 文件格式,记录每一步操作,便于查看什么时间执行了什么命令

通过~/.bash_history文件记录系统管理员的操作记录,定制.bash_history格式

			
HISTSIZE=1000
HISTFILESIZE=2000
HISTTIMEFORMAT="%Y-%m-%d-%H:%M:%S "
export HISTTIMEFORMAT
			
			

看看实际效果

			
$ history | head
1 2012-02-27-09:10:45 do-release-upgrade
2 2012-02-27-09:10:45 vim /etc/network/interfaces
3 2012-02-27-09:10:45 vi /etc/network/interfaces
4 2012-02-27-09:10:45 ping www.163.com
			
			

6.4.2.3. 临时文件安全

临时文件不应该有执行权限

/tmp

			
/dev/sda3 /tmp ext4 nosuid,noexec,nodev,rw 0 
			
			

同时使用符号连接将/var/tmp 指向 /tmp

/dev/shm

			
none /dev/shm tmpfs defaults,nosuid,noexec,rw 0 0
			
			

6.4.2.4. 执行权限

以数据库为例,从安全角度考虑我们需要如下更改

			
chown mysql:mysql /usr/bin/mysql*
chmod 700 /usr/bin/mysql*
			
			

mysql用户是DBA专用用户, 其他用户将不能执行mysql等命令。

6.4.2.5. Linux 系统资源调配

/etc/security/limits.conf

很多资料上是这么写的

				
* soft nofile 65535
* hard nofile 65535
				
				

这样做是偷懒,会带来很多问题,如果你的服务器被攻击,由于你的设置,系统将耗光你的资源,直到没有任何响应为止,你可能键盘输入都成问题,你不得不重启服务器,但你会发现重启只能维持短暂几分钟,又会陷入无响应状态。

			
nobody soft nofile 4096
nobody hard nofile 8192
			
				

为什么会设置为nobody用户呢?因为root用户启动系统后web 服务器会使用nobody用户创建子进程,socket连接实际上是nobody用户在处理。root 仅仅是守护父进程。

				
mysql soft nofile 2048
mysql hard nofile 2048
				
				

针对 mysql 做限制

[提示]提示

关于 nofile 即打开文件数,这个跟socket有非常紧密的关系,在linux系统中任何设备都被看做是一个文件(字符设备),你连接一个鼠标,键盘,摄像头,硬盘等等都被看作打开一个设备文件,所以默认1024是远远不够的。

				
cat >> /etc/security/limits.conf <<EOF
root 	soft nofile 65536
root 	hard nofile 65536
www		soft nofile 65536
www 	hard nofile 65536
mysql	soft nofile 65536
mysql 	hard nofile 65536
EOF
				
				
/etc/sysctl.conf

内核参数调整

			
cat >> /etc/sysctl.conf <<EOF

net.ipv4.ip_local_port_range = 1024 65500
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_tw_recycle = 1
net.ipv4.tcp_fin_timeout = 60
net.ipv4.tcp_keepalive_time = 1200
net.ipv4.tcp_max_syn_backlog = 8192
net.ipv4.tcp_max_tw_buckets = 4096
EOF
			
				

6.4.2.6. 关闭 SELINUX

			
curl -s https://raw.githubusercontent.com/oscm/shell/master/os/selinux.sh | bash				
			
			

6.4.2.7. 关闭写磁盘I/O功能

对于某些文件没必要记录文件的访问时间,由其是在高并发的IO密集操作的环境下,通过两个参数可以实现noatime,nodiratime减少不必要的系统IO资源。

编辑/etc/fstab 添加 noatime,nodiratime 参数

			
/dev/sdb1 /www ext4 noatime,nodiratime 0 0
			
			

我一般分区规划是,/系统根分区,swap交换分区,/www数据分区,同时 禁止写入atime时间,因为/www频繁请求会影响IO

临时mount

			
mount -o remount,noatime,nodiratime /dev/sda3 /mnt/your
			
			

LABEL 方式

			
LABEL=/www /www ext3 defaults,noatime,nodiratime 1 1
			
			

UUID 方式

			
UUID=eeff3e86-7964-4a48-ac02-51ea167ea6b2 /www ext4 defaults,noatime,nodiratime 1 2
			
			

至此,Linux 的OS部分安装配置与优化完成。

6.4.2.8. 放弃 LVM 使用 Btrfs

btrfa 的快照功能非常适合快速备份

子卷功能比物理分区更灵活

			
[root@netkiller ~]# cat /etc/fstab 

#
# /etc/fstab
# Created by anaconda on Fri Nov 21 18:16:53 2014
#
# Accessible filesystems, by reference, are maintained under '/dev/disk'
# See man pages fstab(5), findfs(8), mount(8) and/or blkid(8) for more info
#
UUID=6634633e-001d-43ba-8fab-202f1df93339 / ext4 defaults,barrier=0 1 1
UUID=786f570d-fe5c-4d5f-832a-c1b0963dd4e6 /srv btrfs defaults 1 1
UUID=786f570d-fe5c-4d5f-832a-c1b0963dd4e6 /var/lib/mongo  btrfs   noatime,nodiratime,subvol=@mongo 0 2
UUID=786f570d-fe5c-4d5f-832a-c1b0963dd4e6 /var/lib/mysql  btrfs   noatime,nodiratime,subvol=@mysql 0 2
UUID=786f570d-fe5c-4d5f-832a-c1b0963dd4e6 /www  btrfs   noatime,nodiratime,subvol=www 0 2			
			
			

6.4.2.9. Openssh 安全配置

这节主要讲与SSH有关的安全配置

禁止root用户登录

只允许普通用户登陆,然后通过su命令切换到root用过。后面还会将怎样限制su命令

			
PermitRootLogin no
			
				
限制SSH验证重试次数

超过3次socket连接会断开,效果不明显,有一点点用。

			
MaxAuthTries 3
			
				
禁止证书登陆

证书登陆非常安全,但是很有可能正常用户在你不知道情况下,给你安装了一个证书,他随时都可能进入你的系统

任何一个有权限的用户都能很方便的植入一个证书到 .ssh/authorized_keys 文件中

				
PubkeyAuthentication no
AuthorizedKeysFile /dev/null
				
				
使用证书替代密码认证

是不是自相矛盾? 这个跟上面讲的正好相反,这里只允许使用key文件登陆。

			
PasswordAuthentication no
			
				

这种方式比起密码要安全的多,唯一要注意的地方就是证书被拷贝 ,建议你给证书加上 passphrase。

证书的 passphrase 是可以通过openssl工具将其剥离的,SSH证书我没有试过,但是原理都差不多。

图形窗口客户端记忆密码的问题

当你使用XShell, Xftp, WinSCP, SecureCRT, SecureFX ......等等软件登录时,该软件都提供记住密码的功能,使你下次再登陆的时候无须输入密码就可以进入系统。这样做的确非常方便,

但是你是否想过你的电脑一旦丢失或者被其他人进入,那有多么危险。我之前每天背着笔记本电脑上班,上面安装着XShell并且密码全部记忆在里面。这使我意识到一点电脑丢失,有多么可怕。

禁止SSH客户端记住密码,你不要要求别人那么做。你也无法控制,最终我找到了一种解决方案。

			
ChallengeResponseAuthentication yes
			
				

每次登陆都回提示你输入密码。密码保存也无效。

关闭 GSSAPI
				
GSSAPIAuthentication no
#GSSAPIAuthentication yes
#GSSAPICleanupCredentials yes
#GSSAPICleanupCredentials yes
#GSSAPIStrictAcceptorCheck yes
#GSSAPIKeyExchange no
				
				
禁止SSH端口映射

禁止使用SSH映射Socks5翻墙等等

			
AllowTcpForwarding no
			
				
IP地址限制

只允许通过192.168.2.1,192.168.2.2 访问本机

					# vim /etc/hosts.allow
					sshd:192.168.2.1,192.168.2.2

				

禁止所有人访问本机

					# vim /etc/hosts.deny
					sshd:ALL
				

上面使白名单策略,你也可以采用黑名单策略。

禁止SSH密码穷举

骇客常常使用骇客字典穷举你的SSH密码,使用下面脚本可以封杀频繁链接的IP地址

			
#!/bin/bash
########################################
# Homepage: http://netkiller.github.io
# Author: neo <netkiller@msn.com>
########################################
PIPE=/var/tmp/pipe
pidfile=/var/tmp/$0.pid
BLACKLIST=/var/tmp/black.lst
WHITELIST=/var/tmp/white.lst

LOGFILE=/var/log/secure
DAY=5
########################################

if [ -z "$( egrep "CentOS|7." /etc/centos-release)" ]; then
	echo 'Only for CentOS 7.x'
	exit
fi

if [ -f $BLACKLIST ]; then
	find $BLACKLIST -type f -mtime +${DAY} -delete
fi

if [ ! -f ${BLACKLIST} ]; then
    touch ${BLACKLIST}
fi

if [ ! -f ${WHITELIST} ]; then
    touch ${WHITELIST}
fi

for ipaddr in $(grep rhost ${LOGFILE} | grep -oE "\b([0-9]{1,3}\.){3}[0-9]{1,3}\b" | sort | uniq -c | sort -r -n | head -n 10| awk '{print $2}')
do

    if [ $(grep -c $ipaddr ${WHITELIST}) -gt 0 ]; then
		continue
    fi

    if [ $(grep -c $ipaddr ${BLACKLIST}) -eq 0 ] ; then
		echo $ipaddr >> ${BLACKLIST}
        iptables -I INPUT -p tcp --dport 22 -s $ipaddr -j DROP
        #iptables -I INPUT -s $ipaddr -j DROP
    fi
done			
			
				
解决 SSH 超时退出的问题

很多云主机 SSH 登录后,会自动断开。执行下面脚本可以解决。

				
curl -s https://raw.githubusercontent.com/oscm/shell/master/os/ssh/config.sh | bash				
				
				

6.4.2.10. PAM 插件认证加固配置

配置文件

		
ls  /etc/pam.d/
chfn         crond                login    passwd            remote    runuser-l          smtp          ssh-keycat  sudo-i       system-auth-ac
chsh         fingerprint-auth     newrole  password-auth     run_init  smartcard-auth     smtp.postfix  su          su-l
config-util  fingerprint-auth-ac  other    password-auth-ac  runuser   smartcard-auth-ac  sshd          sudo        system-auth
		
			

认证插件

		
ls /lib64/security/
		
			
pam_tally2.so

此模块的功能是,登陆错误输入密码3次,5分钟后自动解禁,在未解禁期间输入正确密码也无法登陆。

在配置文件 /etc/pam.d/sshd 顶端加入

			
auth required pam_tally2.so deny=3 onerr=fail unlock_time=300
			
				

查看失败次数

			
# pam_tally2
Login           Failures Latest failure     From
root               14    07/12/13 15:44:37  192.168.6.2
neo                 8    07/12/13 15:45:36  192.168.6.2
			
				

重置计数器

			
# pam_tally2 -r -u root
Login           Failures Latest failure     From
root               14    07/12/13 15:44:37  192.168.6.2

# pam_tally2 -r -u neo
Login           Failures Latest failure     From
neo                 8    07/12/13 15:45:36  192.168.6.2
			
				

pam_tally2 计数器日志保存在 /var/log/tallylog 注意,这是二进制格式的文件

例 6.3. /etc/pam.d/sshd - pam_tally2.so

				
# cat  /etc/pam.d/sshd
#%PAM-1.0
auth required pam_tally2.so deny=3 onerr=fail unlock_time=300

auth	   required	pam_sepermit.so
auth       include      password-auth
account    required     pam_nologin.so
account    include      password-auth
password   include      password-auth
# pam_selinux.so close should be the first session rule
session    required     pam_selinux.so close
session    required     pam_loginuid.so
# pam_selinux.so open should only be followed by sessions to be executed in the user context
session    required     pam_selinux.so open env_params
session    optional     pam_keyinit.so force revoke
session    include      password-auth
				
					

以上配置root用户不受限制, 如果需要限制root用户,参考下面

			
auth required pam_tally2.so deny=3 unlock_time=5 even_deny_root root_unlock_time=1800
			
				
pam_listfile.so
用户登陆限制

将下面一行添加到 /etc/pam.d/sshd 中,这里采用白名单方式,你也可以采用黑名单方式

			
auth       required     pam_listfile.so item=user sense=allow file=/etc/ssh/whitelist onerr=fail
			
				

将允许登陆的用户添加到 /etc/ssh/whitelist,除此之外的用户将不能通过ssh登陆到你的系统

			
# cat /etc/ssh/whitelist
neo
www
			
				

例 6.4. /etc/pam.d/sshd - pam_listfile.so

				
# cat /etc/pam.d/sshd
#%PAM-1.0
auth       required     pam_listfile.so item=user sense=allow file=/etc/ssh/whitelist onerr=fail
auth       required     pam_tally2.so deny=3 onerr=fail unlock_time=300

auth	   required	pam_sepermit.so
auth       include      password-auth
account    required     pam_nologin.so
account    include      password-auth
password   include      password-auth
# pam_selinux.so close should be the first session rule
session    required     pam_selinux.so close
session    required     pam_loginuid.so
# pam_selinux.so open should only be followed by sessions to be executed in the user context
session    required     pam_selinux.so open env_params
session    optional     pam_keyinit.so force revoke
session    include      password-auth
				
					

sense=allow 白名单方式, sense=deny 黑名单方式

			
auth       required     pam_listfile.so item=user sense=deny file=/etc/ssh/blacklist onerr=fail
			
				

更多细节请查看手册 $ man pam_listfile

pam_access.so

编辑 /etc/pam.d/sshd 文件,加入下面一行

			
account required pam_access.so
			
				

保存后重启sshd进程

编辑 /etc/security/access.conf 文件

			
cat >>  /etc/security/access.conf << EOF

- : root : ALL EXCEPT 192.168.6.1
EOF
			
				

只能通过 192.168.6.1 登陆, 添加多个IP地址

			
- : root : ALL EXCEPT 192.168.6.1 192.168.6.2
			
				

测试是否生效

pam_wheel.so

限制普通用户通过su命令提升权限至root. 只有属于wheel组的用户允许通过su切换到root用户

编辑 /etc/pam.d/su 文件,去掉下面的注释

			
auth		required	pam_wheel.so use_uid
			
				

修改用户组别,添加到wheel组

			
# usermod -G wheel www

# id www
uid=501(www) gid=501(www) groups=501(www),10(wheel)
			
				

没有加入到wheel组的用户使用su时会提示密码不正确。

			
$ su - root
Password:
su: incorrect password
			
				

6.4.2.11. NTP 服务

每个服务器必须安装 NTP,以保证服务器的时间准确。

			
curl -s https://raw.githubusercontent.com/oscm/shell/master/os/ntpd/ntpdate.sh | bash			
			
			

6.4.3. Java 虚拟机

6.4.3.1. 安装脚本

OpenJDK 1.8

			
curl -s https://raw.githubusercontent.com/oscm/shell/master/lang/java/openjdk/java-1.8.0-openjdk.sh | bash			
			
			

OpenJDK 11

			
curl -s https://raw.githubusercontent.com/oscm/shell/master/lang/java/openjdk/java-11-openjdk.sh | bash			
			
			

Oracle Server JRE 1.8

http://java.oracle.com 下载 server-jre-8u212-linux-x64.tar.gz 然后运行下面安装脚本

			
#!/bin/bash
tar zxf server-jre-8u212-linux-x64.tar.gz*
mv jdk1.8.0_212 /srv/
ln -s /srv/jdk1.8.0_212 /srv/java

cat >> /etc/profile.d/java.sh <<'EOF'
export JAVA_HOME=/srv/java
export JAVA_OPTS="-server -Xms512m -Xmx8192m"
export CLASSPATH=$JAVA_HOME/lib:$JAVA_HOME/jre/lib:.
export PATH=$PATH:$JAVA_HOME/bin:$JAVA_HOME/jre/bin:
EOF

sed -i '117s/securerandom.source/#securerandom.source/' /srv/java/jre/lib/security/java.security
sed -i '117isecurerandom.source=file:/dev/./urandom' /srv/java/jre/lib/security/java.security

cat >> /etc/man.config <<EOF
MANPATH  /srv/java/man
EOF

alternatives --install /usr/bin/java java /srv/jdk1.8.0_212/bin/java 100 \
	--family java-1.8.0-server-jre \
	--slave /usr/bin/javac javac /srv/jdk1.8.0_212/bin/javac			
			
			

如果服务器上安装了多个 JAVA 版本,切换 JAVA 版本使用下面命令

			
alternatives --config java			
			
			

6.4.3.2. 使用 Server JRE 替代JDK。

服务器上不要安装JDK,请使用 Server JRE. 服务器上根本不需要编译器,代码应该在Release服务器上完成编译打包工作。

理由:一旦服务器被控制,可以防止在其服务器上编译其他恶意代码并植入到你的程序中。

6.4.3.3. JAVA_OPTS

加入 -server 选项使 JRE 工作在服务器模式。

			
export JAVA_OPTS="-server -Xms512m -Xmx4096m  -XX:PermSize=64M -XX:MaxPermSize=512m"
			
			

-Xms 指定初始化时化的栈内存

-Xmx 指定最大栈内存

[提示]提示

Java 8 以后 -XX:PermSize 与 -XX:MaxPermSize 两个配置项被废弃

			
export JAVA_OPTS="-server -Xms512m -Xmx4096m"			
			
			

6.4.3.4. java.security 优化

打开$JAVA_HOME/jre/lib/security/java.security文件,找到下面的内容:

			
securerandom.source=file:/dev/urandom
替换成
securerandom.source=file:/dev/./urandom
			
			

6.4.3.5. jconsole 配置

			
java -jar -Djava.rmi.server.hostname=192.168.0.1 -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=911 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false netkiller-1.0-SNAPSHOT.jar			
			
			

如果是云主机,配置 java.rmi.server.hostname=192.168.0.1 为内网IP地址,这样只能从内网监控 JVM

启动 jconsole

			
jconsole localhost:911
			
			

6.4.4. 配置 Spring boot

下面以 Springboot 2.0 为例。

6.4.4.1. 指定配置文件

配置文件位置,默认application.properties是放在jar包中的,这样对于运维并不友好。通过spring.config.location可以制定外部配置文件,这样更便于运维。

--spring.config.location 指定配置文件

			
java -jar demo.jar --spring.config.location=/opt/config/application.properties
			
			

这样运维人员便于配置数据库链接,切换服务器等操作

6.4.4.2. 日志文件

默认日志文件

			
logging.path=/tmp 		# 日志目录默认为 /tmp
logging.file=spring.log # 日志文件名称,默认为spring.log
			
			

需要需要根据每个服务器的情况来指定日志存储的目录

			
java -jar spring-boot-app.jar --logging.file=/tmp/spring-2018-12-10.log			
			
			

6.4.4.3. 如何启动 Springboot 程序

systemd

/etc/systemd/system/spring.service

							
####################################################
# Homepage: http://netkiller.github.io
# Author: netkiller<netkiller@msn.com>
# Script: https://github.com/oscm/shell
# Date: 2015-11-03
####################################################
[Unit]
Description=Spring Boot Application
After=network.target

[Service]
User=www
Group=www
Type=oneshot
WorkingDirectory=/www/netkiller.cn/api.netkiller.cn
ExecStart=/usr/bin/java -jar -Xms256m -Xmx4G  -Dlog.level.console=warn your_jar_file.jar --spring.config.location=appliction-production.properties --spring.profiles.active=profile
#ExecStop=pkill -9 -f  
RemainAfterExit=yes

[Install]
WantedBy=multi-user.target
							
				
				
传统 init.d 脚本
				
#!/bin/bash
##############################################
# Author: netkiller<netkiller@msn.com>
# Homepage: http://www.netkiller.cn
# Date: 2017-02-08
# $Author$
# $Id$
##############################################
# chkconfig: 345 100 02
# description: Spring boot application
# processname: springbootd
# File : springbootd
##############################################
BASEDIR="/www/netkiller.cn/api.netkiller.cn"
JAVA_HOME=/srv/java
JAVA_OPTS="-server -Xms2048m -Xmx8192m -Dlog.level.console=warn -Djava.security.egd=file:/dev/./urandom"
PACKAGE="api.netkiller.cn-0.0.2-release.jar"
CONFIG="--spring.config.location=$BASEDIR/application.properties"
USER=www
##############################################
NAME=springbootd
PROG="$JAVA_HOME/bin/java $JAVA_OPTS -jar $BASEDIR/$PACKAGE $CONFIG"
LOGFILE=/var/tmp/$NAME.log
PIDFILE=/var/tmp/$NAME.pid
ACCESS_LOG=/var/tmp/$NAME.access.log
##############################################

function log(){
	echo "$(date -d "today" +"%Y-%m-%d %H:%M:%S") $1	$2" >> $LOGFILE
}

function start(){
	if [ -f "$PIDFILE" ]; then
		echo $PIDFILE
		exit 2
	fi

	su - $USER -c "$PROG & echo \$! > $PIDFILE"
	log info start
}
function stop(){
	[ -f $PIDFILE ] && kill `cat $PIDFILE` && rm -rf $PIDFILE
	log info stop
}
function status(){
	ps aux | grep $PACKAGE | grep -v grep | grep -v status
	log info status
}
function reset(){
	pkill -f $PACKAGE
  	[ -f $PIDFILE ] && rm -rf $PIDFILE
	log info reset
}

case "$1" in
	start)
		start
		;;
	stop)
		stop
		;;
	status)
		status
		;;
	restart)
		stop
		start
		;;
	log)
		tail -f $LOGFILE
		;;
	reset)
		reset
		;;
	*)
		echo $"Usage: $0 {start|stop|status|restart|log|reset}"
esac
exit $?				
				
				

6.4.4.4. 监听端口

不要使用root用户启动springboot,Java程序与C程序不同。nginx,httpd 使用root用户启动守护80端口,子进程/线程会通过setuid(),setgid()两个函数切换到普通用户。即父进程所有者是root用户,子进程与多线程所有者是一个非root用户,这个用户没有shell权限,无法通过ssh与控制台登陆系统。

Java 的JVM 是与系统无关的,是建立在OS之上的,你使用什么用户启动Springboot,那麽Springboot 就会继承该所有者的权限。

一旦 springboot 被攻击,黑客将会获得 root 权限,控制整个系统。

			
server.port=8080 # 监听端口			
			
			

服务器上有多个网卡的情况,需要指定一个网络适配器监听端口。

			
server.address=192.168.0.1 # 绑定的地址			
			
			
使用 80 端口

Linux系统小于1024的端口只有root可以使用,这也是为什么Springboot默认端口是8080。如果你想使用80端口只能使用root启动Springboot,不建议这样做,这会带来了很多安全问题。建议使用iptables端口转发。

解决方案是创建一个普通用户,如:

				
groupadd -g 80 daemon
adduser -o --home /daemon --shell /sbin/nologin --uid 80 --gid 80 -c "Web Server" daemon
				
				

注意 /sbin/nologin , 意味着该用户不能登录,同时我也没有给它指定密码,这个用户只能用于启动tomcat,没有Shell权限就以为只被注入后无法运行linux命令。

				
chown daemon:daemon -R /srv/*
su - daemon -c "/srv/apache-tomcat/bin/startup.sh"
				
				

接下来解决80端口问题, 思路就是80去调用8080,或者映射端口。

下面是影射方案,80 跳转 8080

				
iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 8080

取消跳转
iptables -t nat -D PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 8080

查看规则
iptables -t nat -L
				
				

另一个就是从80请求去调用8080的方案

这个方案可以在 Tomcat 前段增加反向代理,例如:Nginx,Apache,Squid,Varnish或者F5, Array这类设备等等

6.4.4.5. 优雅重启

			
server:
  shutdown: graceful			
			
			

6.4.4.6. 连接数配置

系统默认 2048,这里 max-threads 是 4096 表示服务器最大连接数是 4096

							
server.tomcat.max-threads=4096 # 最大线程数				
			
			

空闲线程数,静止没有访问时,服务器保留的线程数。

			
server:
  tomcat:
    min-spare-threads: 20
    max-threads: 100
  connection-timeout: 20000		
			
			

配置 min-spare-threads 可以防止服务器静止的状态下,突然发起大量的链接请求导致系统没有响应。

			
server.tomcat.connection-timeout=20000
			
			

链接超时时间建议设置 60~20秒之间。

			
server:
  tomcat:
    connection-timeout: 300000
    accept-count: 1000
    max-threads: 4000
    min-spare-threads: 100
    max-connections: 10000
			
			

6.4.4.7. 超时时间

配置Openfeign超时时间

			
feign:
  httpclient:
    enabled: true
    connection-timeout: 120000  
  compression:
    request:
      enabled: true
      min-request-size: 1024   
			
			

6.4.4.8. 编码配置

有些版本的 Spring boot 默认并非 UTF-8 建议强制配置编码

			
spring.messages.encoding=UTF-8
server.tomcat.uri-encoding=UTF-8
spring.http.encoding.charset=UTF-8
spring.http.encoding.enabled=true
spring.http.encoding.force=true			
			
			

6.4.4.9. Session 配置

修改 Cookie 变量 JSESSIONID, 这个cookie 是用于维持Session关系。建议你改为PHPSESSID,防止黑客骚猫。

			
server.session.cookie.name=PHPSESSID
server.session.cookie.domain=.example.com
server.session.cookie.http-only=true
server.session.cookie.path=/				
			
			

6.4.4.10. 压缩传输配置

注意不仅仅压缩 HTML 页面,配置 application/json 也可以压缩传输 JSON 数据

			
server.compression.enabled=true
server.compression.mime-types=application/json,application/xml,text/html,text/xml,text/plain,text/css,application/javascript
server.compression.min-response-size=1024	
			
			

feign 压缩传输

			 
feign:
  httpclient:
    enabled: true
    connection-timeout: 120000  
  compression:
    request:
      enabled: true
      min-request-size: 1024   			
			
			

6.4.4.11. 上传限制

上传文件尺寸限制,注意如果使用了 Nginx 代理 Springboot 同时也需要配置 Nginx 上传限制。

						
spring.servlet.multipart.max-file-size=2MB
spring.servlet.multipart.max-request-size=10MB
spring.http.multipart.enabled=false			
			
			

6.4.4.12. Redis 配置

					
# REDIS (RedisProperties)
# Redis数据库索引(默认为0)
spring.redis.database=0
# Redis服务器地址
spring.redis.host=192.168.30.103
# Redis服务器连接端口
spring.redis.port=6379
# Redis服务器连接密码(默认为空)
spring.redis.password=
# 连接超时时间(毫秒)
spring.redis.timeout=0
# 连接池最大连接数(使用负值表示没有限制)
spring.redis.jedis.pool.max-active=8
# 连接池最大阻塞等待时间(使用负值表示没有限制)
spring.redis.jedis.pool.max-wait=-1
# 连接池中的最大空闲连接
spring.redis.jedis.pool.max-idle=8
# 连接池中的最小空闲连接
spring.redis.jedis.pool.min-idle=0			
			
			

6.4.4.13. 数据库配置

PostgreSQL
				
spring.datasource.driver-class-name=org.postgresql.Driver
spring.datasource.platform=postgres
spring.datasource.url=jdbc:postgresql://192.168.1.240:5432/saas_data?currentSchema=public&useUnicode=true&characterEncoding=UTF-8
spring.datasource.username=ctsuser
spring.datasource.password=saas
				
				
MySQL
				
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://主机地址:端口号/数据库
spring.datasource.username=用户名
spring.datasource.password=密码
spring.jpa.database=MYSQL # 启用JPA支持
				
				
Oracle
				
spring.datasource.driver-class-name=oracle.jdbc.OracleDriver
spring.datasource.url=jdbc:oracle:thin:@//odb.netkiller.cn:1521/orcl
spring.datasource.username=orcl
spring.datasource.password=passw0rd
spring.datasource.connection-test-query="SELECT 1 FROM DUAL"
spring.jpa.database-platform=org.hibernate.dialect.Oracle10gDialect
				
				
链接池
				
spring.jpa.show-sql=true
#spring.jpa.hibernate.ddl-auto=none
#spring.jpa.hibernate.ddl-auto=create-drop
spring.datasource.max-active=50
spring.datasource.initial-size=5
spring.datasource.max-idle=10
spring.datasource.min-idle=5
spring.datasource.test-while-idle=true
spring.datasource.test-on-borrow=false
spring.datasource.validation-query=SELECT 1 FROM DUAL
spring.datasource.time-between-eviction-runs-millis=5000
spring.datasource.min-evictable-idle-time-millis=60000				
				
				

6.4.5. Spring Cloud 配置

6.4.5.1. Config Server

下面是给配置服务器增加用户认证,访问配置服务器用户名config, 密码 s3cr3t

			
server.port=8888
spring.cloud.config.server.git.uri=/opt/config
security.user.name=config
security.user.password=s3cr3t			
			
			

6.4.5.2. Eureka 安全配置

为 eureka 增加HTTP认证,周泽任何人都能注册进来。

			
security.user.name=eureka
security.user.password=s3cr3t			
			
			

6.4.5.3. Openfeign 优化

开发压缩传输

			
feign.compression.response.enabled=true
feign.compression.request.enabled=true
feign.compression.request.mime-types=text/xml,application/xml,application/json
feign.compression.request.min-request-size=2048
			
			

Feign metrics

			
feign.metrics.enabled=true			
			
			

超时时间

			
feign:
  client:
    config:
      default:
        connectTimeout: 300000
        readTimeout: 300000
  httpclient:
    enabled: true
    connection-timeout: 300000
  compression:
    request:
      enabled: true
      min-request-size: 1024			
			
			

6.4.6. 配置 Nginx

6.4.6.1. 处理器数量和连接数配置

worker_processes = CPU 数量

			
user www;
worker_processes 1;

error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;				
			
			

连接数配置

			
events {
    worker_connections  4096;
}				
			
			

6.4.6.2. 隐藏nginx版本号

隐藏nginx版本号可以有效的增加黑客攻击难度

			
vim /etc/nginx/nginx.conf

http {
...
server_tokens off;
}
			
			

6.4.6.3. HTTP2

			
	server {
		listen 443 ssl http2;
	
		ssl_certificate server.crt;
		ssl_certificate_key server.key;
	}
			
			

6.4.6.4. 用户访问 HTTP时强制跳转到 HTTPS

497 - normal request was sent to HTTPS

			
				#让http请求重定向到https请求

	server {
		listen 80;
		error_page 497 https://$host$uri?$args;
		rewrite ^(.*)$ https://$host$1 permanent;
	}
			
			
			
	server {
		listen 80
		listen 443 ssl http2;
	
		ssl_certificate server.crt;
		ssl_certificate_key server.key;
	
		error_page 497 https://$host$uri?$args;
	
		if ($scheme = http) {
			return 301 https://$server_name$request_uri;
		}
	}
			
			

6.4.6.5. 开启 gzip 压缩传输

gzip_types 压缩类型

				
	gzip_types text/plain text/css application/javascript text/javascript application/x-javascript text/xml application/xml application/xml+rss application/json;			
			
			

text/html 是 gzip_types 默认值,所以不要将text/html加入到gzip_types

6.4.6.6. 缓存过期时间

			
	location ~ .*\.(htm|html|gif|jpg|jpeg|png|bmp|swf|ioc|rar|zip|txt|flv|mid|doc|ppt|pdf|xls|mp3|wma)$
	{
		expires 30d;
	}
	location ~ .*\.(js|css)$
	{
		expires 1h;
	}
			
			

6.4.6.7. 上传文件尺寸限制

client_max_body_size 上传文件尺寸限制

						
client_max_body_size 2M;				
			
			

6.4.6.8. 实现相互备份

两台服务器 server1,server2 相互备份

			
	upstream backend {
		server server1.example.com:8080;
		server server2.example.com:8080 backup;
	}
	server {
		location / {
			proxy_pass http://backend;
		}
	}
					
			
			
			
	upstream frontend {
		server server1.example.com:8080 backup;
		server server2.example.com:8080;
	}
	server {
		location / {
			proxy_pass http://frontend;
		}
	}
			
			

6.4.6.9. Nginx 状态信息

允许监控软件从 localhost 收集 nginx 状态信息

						
server {
    listen       80;
    server_name  localhost;

    location /status {
        stub_status on;
        access_log off;
        allow 127.0.0.1;
        deny all;
    }
}			
			
			

6.4.6.10. 禁止公网访问 Spring Boot Actuator

Spring Boot Actuator 是会泄漏一些环境变量等敏感信息,甚至可以通过 localhost:8080/actuator/shutdown 远程关闭服务器。所以需要禁止公网访问,只允许内部服务器采集信息。

			
	location ~ ^/actuator
	{
		allow 172.16.0.10;
		deny all;
	}
			
			

172.16.0.10 是监控服务器

6.4.7. 部署应用程序

6.4.7.1. 服务器软件安装目录

			
/srv		服务器软件安装目录,例如 jdk
/opt		系统保留,可以用于安装软件,存储备份文件
			
			

6.4.7.2. 应用程序部署目录

应用程序放在/www目录下,一般是这样的结构。

			
/www/example.com/www.example.com
			
			

每次升级将压错包解压到 /www/example.com/目录下,www.example.com 是符号连接,连接到刚刚解压的目录。

这个可以实现通过符号连接在多个版本之间快速切换。

6.4.7.3. 数据盘

数据盘是指云主机额外增加一个磁盘,用于数据存储。

如果是阿里云可以使用下面脚本,自动化分区格式化为 Btrfs 格式的文件系统。

			
curl https://github.com/oscm/shell/blob/master/cloud/aliyun/xvdb.exp.sh | bash
			
			

6.4.7.4. 用户与权限

禁止使用 root 用户启动服务

禁止使用 root 用户启动应用,每个应用拥有自己的用户。例如:

					
nginx		Nginx Web 服务器用户
redis 		Redis 缓存用户
mysql		MySQL 数据库用户
				
				

我门常常会看到有人编译安装了nginx,并没有为nginx创建用户,也没有使用 nobody,daemon 等用户启动nginx,而是使用root启动了 nginx.

通过 ps ax 可以看到 nginx 所有者是 root,一旦 nginx 被入侵,黑客将获得 root 权限。

Springboot 应用程序启动用户

为 Springboot 进程创建启动用户

应用程序放在/www目录下www所有者是www用户。下面是创建www用户的命令

				
adduser --home /www -c "Web Application" www
				
				
用户隔离更安全

应用程序部署与应用程序启动不能使用同一个用户。

上传权限与应用启动不使用一个用户。

				
用户 --> nginx (nginx 用户) --> springboot (www 用户)	--> xxxx.jar (root 用户)
               |
               V
      HTML 文件 (nobody 用户 644)			
				
				

例如 nginx 被黑客攻击,黑客想修改 HTML文件,但HTML权限是 nobody,所以只能查看无法修改。

同理 nginx 也没有权限修改 xxxx.jar 文件。

6.4.7.5. 进程监控与重启

程序运行一段时间后,可能会出现崩溃,异常退出,没有任何日志可以排查。

所以我们需要监控程序,一旦退出就重新启动。

6.4.8. 持续集成

6.4.8.1. Gitlab CI/CD

保存到 master 分支根目录,命名为 .gitlab-ci.yml

			
#image: java:8
#image: maven:latest
#image: maven:3.5.0-jdk-8

variables:
  MAVEN_OPTS: "-Dmaven.repo.local=.m2/repository"

cache:
  paths:
    - .m2/repository/
    - target/

stages:
  - build
  - test
  - package

build:
  stage: build
  script: mvn compile

unittest:
  stage: test
  script: mvn test
  
  
package:
  stage: package
  script: mvn package
  artifacts:
    paths:
      - target/*.jar

			
			

6.4.8.2. Jenkins

保存在 master 分支根目录,命名为 Jenkinsfile

			
pipeline {
    agent {
        label "java-8"
    }
    stages  {
        stage("初始化") {
        	steps{
        	    sh 'apt install -y sshpass'
                sh 'sshpass -v'
        	}
        }
        stage("检出") {
            steps {
                checkout(
                  [$class: 'GitSCM', branches: [[name: env.GIT_BUILD_REF]], 
                  userRemoteConfigs: [[url: env.GIT_REPO_URL]]]
                )
            }
        }

        stage("编译") {
            steps {
                sh "mvn package -Dmaven.test.skip=true"
            }
        }

        stage("测试") {
            steps {
              	sh "mvn test"
            }
        }
        
        stage("保存构建产物") {
            steps {
                archiveArtifacts artifacts: 'target/*.jar', fingerprint: true
              	sh "ls -l target/*.jar"
            }
        }
        stage("部署"){
            parallel{
            	stage('stop') {
			         steps {
			            sh 'sshpass -p 123456 ssh -f www@www.netkiller.cn pkill -f java-project-0.0.2-SNAPSHOT'
			         }
			    }
			    stage('start') {
			         steps {
			            sh 'sshpass -p 123456 scp target/*.jar www@www.netkiller.cn:/opt/'
                	    sh 'sshpass -p 123456 ssh -f www@www.netkiller.cn java -jar /opt/java-project-0.0.2-SNAPSHOT.jar'
			          }
			    }	                
    	    }
    	}
    }
}
			
			

6.4.9. 监控

Spring Boot Actuator 健康检查、审计、统计和监控

常规接口

		
neo@MacBook-Pro ~ % curl -s http://www.netkiller.cn:8080/actuator | jq
{
  "_links": {
    "self": {
      "href": "http://www.netkiller.cn:8080/actuator",
      "templated": false
    },
    "health": {
      "href": "http://www.netkiller.cn:8080/actuator/health",
      "templated": false
    },
    "health-component": {
      "href": "http://www.netkiller.cn:8080/actuator/health/{component}",
      "templated": true
    },
    "health-component-instance": {
      "href": "http://www.netkiller.cn:8080/actuator/health/{component}/{instance}",
      "templated": true
    },
    "info": {
      "href": "http://www.netkiller.cn:8080/actuator/info",
      "templated": false
    }
  }
}
		
		
		

暴漏所有接口

		
management:
  endpoints:
    web:
      exposure:
        include: "*"		
		
		

再次访问 /actuator

		
neo@MacBook-Pro-Neo ~/w/Architect (master)> curl -s https://www.netkiller.cn/actuator/ | jq
{
  "_links": {
    "self": {
      "href": "http://pre.ejiayou.com/ensd-channel-service/monitor",
      "templated": false
    },
    "archaius": {
      "href": "https://www.netkiller.cn/actuator/archaius",
      "templated": false
    },
    "nacosconfig": {
      "href": "https://www.netkiller.cn/actuator/nacosconfig",
      "templated": false
    },
    "nacosdiscovery": {
      "href": "https://www.netkiller.cn/actuator/nacosdiscovery",
      "templated": false
    },
    "auditevents": {
      "href": "https://www.netkiller.cn/actuator/auditevents",
      "templated": false
    },
    "beans": {
      "href": "https://www.netkiller.cn/actuator/beans",
      "templated": false
    },
    "caches-cache": {
      "href": "https://www.netkiller.cn/actuator/caches/{cache}",
      "templated": true
    },
    "caches": {
      "href": "https://www.netkiller.cn/actuator/caches",
      "templated": false
    },
    "health-component": {
      "href": "https://www.netkiller.cn/actuator/health/{component}",
      "templated": true
    },
    "health": {
      "href": "https://www.netkiller.cn/actuator/health",
      "templated": false
    },
    "health-component-instance": {
      "href": "https://www.netkiller.cn/actuator/health/{component}/{instance}",
      "templated": true
    },
    "conditions": {
      "href": "https://www.netkiller.cn/actuator/conditions",
      "templated": false
    },
    "configprops": {
      "href": "https://www.netkiller.cn/actuator/configprops",
      "templated": false
    },
    "env-toMatch": {
      "href": "https://www.netkiller.cn/actuator/env/{toMatch}",
      "templated": true
    },
    "env": {
      "href": "https://www.netkiller.cn/actuator/env",
      "templated": false
    },
    "info": {
      "href": "https://www.netkiller.cn/actuator/info",
      "templated": false
    },
    "loggers-name": {
      "href": "https://www.netkiller.cn/actuator/loggers/{name}",
      "templated": true
    },
    "loggers": {
      "href": "https://www.netkiller.cn/actuator/loggers",
      "templated": false
    },
    "heapdump": {
      "href": "https://www.netkiller.cn/actuator/heapdump",
      "templated": false
    },
    "threaddump": {
      "href": "https://www.netkiller.cn/actuator/threaddump",
      "templated": false
    },
    "metrics": {
      "href": "https://www.netkiller.cn/actuator/metrics",
      "templated": false
    },
    "metrics-requiredMetricName": {
      "href": "https://www.netkiller.cn/actuator/metrics/{requiredMetricName}",
      "templated": true
    },
    "scheduledtasks": {
      "href": "https://www.netkiller.cn/actuator/scheduledtasks",
      "templated": false
    },
    "httptrace": {
      "href": "https://www.netkiller.cn/actuator/httptrace",
      "templated": false
    },
    "mappings": {
      "href": "https://www.netkiller.cn/actuator/mappings",
      "templated": false
    },
    "refresh": {
      "href": "https://www.netkiller.cn/actuator/refresh",
      "templated": false
    },
    "features": {
      "href": "https://www.netkiller.cn/actuator/features",
      "templated": false
    },
    "service-registry": {
      "href": "https://www.netkiller.cn/actuator/service-registry",
      "templated": false
    }
  }
}		
		
		

6.4.9.1. 健康状态

curl www.netkiller.cn:8080/actuator/health

				
neo@MacBook-Pro ~ % curl -s http://www.netkiller.cn:8080/actuator/health | jq
{
  "status": "UP"
}
		
			
健康状态

详细的健康状态信息

			
management.endpoint.health.show-details=always			
			
				

			
neo@MacBook-Pro ~ % curl -s http://www.netkiller.cn:8080/actuator/health | jq
{
  "status": "UP",
  "details": {
    "diskSpace": {
      "status": "UP",
      "details": {
        "total": 250790436864,
        "free": 23556112384,
        "threshold": 10485760
      }
    }
  }
}			
			
				

6.4.10. 容器部署

		
metadata:
  name: netkiller
  labels:
    app: netkiller
    group: finance
  namespace: neo
  annotations:
    description: 服务
spec:
  replicas: 1
  progressDeadlineSeconds: 600
  revisionHistoryLimit: 10
  selector:
    matchLabels:
      app: netkiller
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 0
  template:
    metadata:
      labels:
        app: netkiller
    spec:
      hostAliases:
        - ip: 172.18.200.10
          hostnames:
            - log.netkiller.cn
      containers:
        - name: netkiller
          image: registry.netkiller.cn/project/netkiller
          ports:
            - name: http
              containerPort: 8080
          imagePullPolicy: IfNotPresent
          livenessProbe:
            failureThreshold: 3
            httpGet:
              path: /netkiller-service/monitor/health
              port: 8080
              scheme: HTTP
            initialDelaySeconds: 60
            periodSeconds: 10
            successThreshold: 1
            timeoutSeconds: 5
          readinessProbe:
            failureThreshold: 3
            httpGet:
              path: /netkiller-service/monitor/health
              port: 8080
              scheme: HTTP
            initialDelaySeconds: 30
            periodSeconds: 10
            successThreshold: 1
            timeoutSeconds: 5
          env:
            - name: JAVA_OPTS
              value: -Duser.timezone=Asia/Shanghai -Xmx2g -Xms1g -Dspring.application.name=netkiller-service
                -Dspring.profiles.active=neo -Dserver.port=8080 -Dlog.level=info -Dspring.cloud.nacos.config.server-addr=nacos.default.svc.cluster.local:8848
                -Dspring.cloud.nacos.discovery.server-addr=nacos.default.svc.cluster.local:8848
          volumeMounts:
            - mountPath: /tmp
              name: tmp
        - name: filebeat
          image: docker.elastic.co/beats/filebeat:8.6.1
          args:
            - -c
            - /usr/share/filebeat/filebeat.yml
            - -e
          imagePullPolicy: IfNotPresent
          env:
            - name: environment
              value: prod
            - name: service
              value: netkiller-service
          volumeMounts:
            - mountPath: /tmp
              name: tmp
            - mountPath: /usr/share/filebeat/data
              name: data
            - mountPath: /usr/share/filebeat/filebeat.yml
              name: config
              subPath: filebeat.yml
      imagePullSecrets:
        - name: registry
      restartPolicy: Always
      dnsPolicy: ClusterFirst
      volumes:
        - emptyDir: {}
          name: data
        - emptyDir: {}
          name: tmp
        - configMap:
            defaultMode: 420
            name: filebeat
          name: config
apiVersion: apps/v1
kind: Deployment
---
metadata:
  name: netkiller
  namespace: neo
spec:
  ports:
    - name: http-80
      protocol: TCP
      port: 80
      targetPort: 8080
  selector:
    app: netkiller
apiVersion: v1
kind: Service
---
metadata:
  name: netkiller
  namespace: neo
  annotations:
    kubernetes.io/ingress.class: nginx
    nginx.ingress.kubernetes.io/proxy-connect-timeout: '300'
    nginx.ingress.kubernetes.io/proxy-read-timeout: '300'
    nginx.ingress.kubernetes.io/proxy-send-timeout: '300'
spec:
  rules:
    - host: neo.netkiller.cn
      http:
        paths:
          - path: /netkiller-service
            pathType: ImplementationSpecific
            backend:
              service:
                name: netkiller
                port:
                  number: 80
  tls:
    - hosts:
        - neo.netkiller.cn
      secretName: tls
apiVersion: networking.k8s.io/v1
kind: Ingress