运维杂记

可能比较爱折腾吧,不知不觉中,好像就成为校队的运维成员之一了。

感觉运维还是挺有意思的,就像CTF做题一样。

装东西报错了,就会拿报错信息去google上搜,运气好的话,能直接找到相应的解决方案;运气差的话,可能找了很久都无法解决。在搜索解决方案的过程中,也会去学习一下相关的原理知识,理解为什么会报错。这其实跟打CTF是很类似的,拿到一个题目,考查的知识点是未曾学过的,这个时候往往就需要自己上网找相关的学习资料,然后现学,理解了之后,再去做。

这种通过解决问题来促进学习的方式,感觉还是挺适合我的。

下面就是我在运维过程中遇到的一些问题,以及我是如何解决这些问题的。可能会比较杂,2333

通过ssh隧道(以及frp)来实现内网穿透 (2020.08.13)

2020.11.11更新:当时写的属实不太行,推荐: 实战 SSH 端口转发

Introduction

学校内网有服务器,配置挺好的,传代下来给我了。

配置其实很垃圾:CPU(s) 24 x Intel(R) Xeon(R) CPU X5670 @ 2.93GHz (2 Sockets) + 32GiB RAM. 属于10年前用完的电子垃圾,某宝不到100块钱就能买到一个6核的X5670。。。

但是!!!外网连不进去学校内网,无法通过ssh连接到服务器。。

好在可以通过某个网页管理平台里的webshell连进去,但是webshell用的不是很爽,还是很想要通过ssh连进去。

Screen Shot 2020-08-05 at 1.21.37 AM

注意,学校内网的机器是可以访问到外面公网上的机器的。

另一方面,也是由于今天上班的时候,也遇到了类似的问题:内网的A机器可以ssh连到同样在内网的B机器,但是B机器就是ping不通A机器,估计是管理员把路由规则改了,可恶啊。。其中,A机器作为服务端,提供rpc服务,B机器需要去访问A机器的rpc接口才能被服务。

Screen Shot 2020-08-05 at 1.40.20 AM

此时,隔壁安研组的大哥exp说,能用ssh隧道(草,没听说过,好高大上啊)解决这个问题,随之,就在他的神船上操作了起来,不到几分钟就ok了,完美地解决了这个问题。我们在B机器上访问映射过来的某端口,就直接相当于访问A机器提供的rpc端口 。

所以,想学习一下这个ssh隧道到底是个多么高大上的东西,看看能不能通过同样的方式,也能让外网能够通过ssh连到学校内网的那台服务器。

Local Forward and Remote Forward via SSH Tunnel

学习资料:https://www.zsythink.net/archives/2450

学习资料里的mysql是5.x.x的,所以mysql流量是没有被加密的;但新版本的mysql的流量(8.x.x)是有TLS加密的

简单来说,就是通过ssh完成了一个转发(forward)的操作。

-L表示本地转发(local forward)。

ssh的文档中也能看出来这个-L参数的功能:

Screen Shot 2020-08-10 at 10.34.01 PM

画一张图表示一下我的理解:

Screen Shot 2020-08-10 at 11.28.45 PM

ssh -L 10fang wen dao000:B:9999 user@B就是在A机器的10000端口开启了一个监听,任何访问到该端口的流量,都会经过ssh提供的安全隧道,被转发至B机器的9999端口上。

因此,访问A的10000端口实际上就是访问B的9999端口。

这是建立在A能够ssh连接到B的前提下。(此时B可能无法ssh连接到A)


-R表示远程转发(remote forward),文档:

Screen Shot 2020-08-10 at 11.17.37 PM

再画一张图:

Screen Shot 2020-08-10 at 11.30.51 PM

可以发现其实改动很少,只不过就是ssh的发起者和接受者不同了而已。

还有一个不同点是,远程转发在A上监听的这个端口,只有A自己(127.0.0.1)能够连接,其他的ip是无法连接的;而本地转发在本地监听的那个端口,是可以设置为让任何ip连接的。

远程转发,就很贴近于我们的实际场景了。

ssh -R 20000:localhost:22 root@A

外网机器A无法访问到学校内网里的机器B,但是B可以访问A。B在通过ssh连接到A后,可以把B自己本地的一个端口(例如用于ssh服务的22端口)映射到A机器上的某个端口(例如20000);此时,A只需要访问自己本地的20000端口,就直接相当于在访问B的22端口。

可以看出来,问题的根源在于A和B之间的通信并不是双向的;但是B通过ssh连接到A后,建立起来的ssh隧道的通信却是双向的,所以,可以借助于ssh隧道来实现双方的相互通信。

Forwarding Port 22

学习资料:https://www.cnblogs.com/kwongtai/p/6903420.html

ok,我们现在要解决问题的是,如何让外网可以ssh连上内网的机器B。

按照上述方法,我们只需要通过B开启一个远程转发即可。但是问题是,我们每次ssh连接,都需要先上B机器去执行一下ssh -fCNR 20000:localhost:22 root@A???这样还不如直接用webshell。。。

此时,就需要一个==中转站C==,我们可以通过远程转发的方式,把B的22端口固定地(仅需一次即可)映射到C的20000端口上,然后我们再去访问C的20000端口,就能访问到B的22端口。

但问题又来了,远程转发到C的那个20000端口,又只能C自己去访问,不能让外网的其他的ip去访问。。

解决方案其实很简单:再在C上通过本地转发,将C本地的20000端口映射到另外一个30000端口上。C的30000端口外网ip可以任意访问,访问C的30000端口 == 访问C的20000端口 ==访问B的22端口。

因此,外网机器A,只需要ssh -p 30000 root@C即可ssh连接到学校内网中的B。

再画一个图:

Screen Shot 2020-08-10 at 11.57.24 PM

中转站C是需要公网ip的,否则A连不到C。这其实很好办,用一个学生机就可以了(反正学生机租着也没其他用处)。

其实从图中可以看到,实际上一通操作(一次远程转发+一次本地转发)的最终效果是:将B的22端口映射到了C的30000端口上,使得外网访问C的30000端口即可ssh连接到B。

FRP

ssh隧道转发看起来未免有些太麻烦了。

事实上,有一些比较优秀的工具(软件)就是专门用来内网穿透的。

其中一个比较给力的就是golang写的frp

按照官方文档里的配置操作一下,就OK了。

大致原理就是,需要一台类似于中转站C的frp server,用于转发frp client的各种流量。frp client只需要将自己的某个端口映射到frp server上,即可实现内网穿透、反向代理等等的功能。


贴一下我的配置。

学生机(frp server)frps.ini

1
2
3
4
5
6
7
[common]
bind_addr = 0.0.0.0
bind_port = 20000
authenticate_method = token
token = xxx # your password

vhost_http_port = 10005

nohup frps -c frps.ini &开启frp服务。

某个内网机器,将ssh的22端口映射出来,frpc.ini

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
[common]
server_addr = xxx.xxx.xxx.xxx # frp server's ip
server_port = 20000
authentication_method = token
token = xxx # your password

[ssh]
type = tcp
local_ip = 127.0.0.1
local_port = 22
remote_port = 20006

nohup frpc -c frpc.in &将端口映射到frp server上。

这样,我们只需要ssh -p 20006 root@xxx.xxx.xxx.xxx即可访问到内网的那台机器。

Summary

出于某些现实需求,去学习了一下内网穿透相关的东西,并成功利用所学的ssh隧道和frp,解决了外网无法ssh连接到学校内网机器的问题。

Proxmox虚拟机连不上外网 (2020.10.21)

Proxmox VE是一个开源的基于Debian的虚拟化平台。

传下来的那台服务器配置(24core + 32GB RAM)还可以,如果只是用来跑一个系统(例如Ubuntu 20.04),可能就会显得有点浪费了。这个时候就可以考虑使用Proxmox这种虚拟化平台,通过Proxmox,我们可以创建多个自定义配置的虚拟机,这样就可以用其中的某个虚拟机来跑一些小一些的服务(例如博客网站之类的)。这样不光光可以充分利用资源,还能将不同的服务隔离开来,方便管理。

Proxmox架构大概就是:一台Host机(母鸡)+ n台Guest虚拟机(小鸡)。可以通过母鸡生成多个小鸡,并对小鸡进行管理;小鸡可以用来搭建各种服务。

我们的那台服务器就是用的这个Proxmox虚拟化平台,服务器托管在学校的机房,信息办给我们分配了一个公网ip。

现在的情况是:

  • 母鸡可以访问公网,也可以访问所有的小鸡。母鸡出网用的是vmbr1虚拟网卡,gateway是信息办给的一个。
  • 小鸡通过桥接的方式配置的网络,有两个桥接虚拟网卡vmbr0vmbr1,分别在192.168.100.1和192.168.20.1,划分了两个子网网段:192.168.100.0、192.168.20.0;
  • 小鸡可以访问到对应子网内的所有其他小鸡,也可以访问到母鸡,但是访问不到母鸡的gateway,也访问不到公网。
  • 小鸡ping某个公网ip,在母鸡上能够看到从小鸡到公网

母鸡上的/etc/network/interfaces的配置为:

source /etc/network/interfaces.d/*

auto lo
iface lo inet loopback

iface enp3s0f0 inet manual

iface enp3s0f1 inet manual

iface enp4s0f0 inet manual

iface enp4s0f1 inet manual

auto vmbr1
iface vmbr1 inet static
	address  xxx.xxx.xxx.xxx
	netmask  255.255.255.0
	gateway  xxx.xxx.xxx.xxx
	bridge-ports enp3s0f0
	bridge-stp off
	bridge-fd 0
	dns-nameservers 101.7.8.9

auto vmbr0
iface vmbr0 inet static
	address  192.168.100.1
	netmask  255.255.255.0
	bridge-ports none
	bridge-stp off
	bridge-fd 0


auto vmbr2
iface vmbr2 inet static
	address  192.168.20.1
	netmask  255.255.255.0
	bridge-ports none
	bridge-stp off
	bridge-fd 0

这个问题找了将近2天的下午,都未能解决,但是在查找解决方案的过程中也学到了不少东西。

后来,把情况告诉给了校队里的一位web师傅,他说可能是NAT的问题。

确实,之前在找资料的过程中,也看到过只有一个公网ip的配置方案就是:桥接网卡+NAT转发。

通过NAT转发,可以把小鸡数据包中的私网地址转换为公网地址,也能将从公网回来的数据包中的公网地址转换为私网地址,以实现小鸡对公网的访问。

其实只需要在/etc/network/interfaces配置文件中添加一下相关配置即可:

source /etc/network/interfaces.d/*

auto lo
iface lo inet loopback

iface enp3s0f0 inet manual

iface enp3s0f1 inet manual

iface enp4s0f0 inet manual

iface enp4s0f1 inet manual

auto vmbr1
iface vmbr1 inet static
	address  180.209.64.78
	netmask  255.255.255.0
	gateway  180.209.64.20
	bridge-ports enp3s0f0
	bridge-stp off
	bridge-fd 0
	dns-nameservers 101.7.8.9

auto vmbr0
iface vmbr0 inet static
	address  192.168.100.1
	netmask  255.255.255.0
	bridge-ports en3s0f1
	bridge-stp off
	bridge-fd 0
	post-up echo 1 > /proc/sys/net/ipv4/ip_forward
	post-up iptables -t nat -A POSTROUTING -s '192.168.100.0/24' -o vmbr1  -j MASQUERADE
	post-down iptables -t nat -D POSTROUTING -s '192.168.100.0/24' -o vmbr1  -j MASQUERADE


auto vmbr2
iface vmbr2 inet static
	address  192.168.20.1
	netmask  255.255.255.0
	bridge-ports none
	bridge-stp off
	bridge-fd 0
	post-up echo 1 > /proc/sys/net/ipv4/ip_forward
	post-up iptables -t nat -A POSTROUTING -s '192.168.20.0/24' -o vmbr1 -j MASQUERADE
	post-down iptables -t nat -D POSTROUTING -s '192.168.20.0/24' -o vmbr1 -j MASQUERADE

这样就可以把两个私网网段内所有小鸡的数据包通过NAT转发,发送到gateway,从而出网。

修改完配置后,/etc/init.d/networking restart重启一下网络服务。

将所有小鸡的Network在Web GUI界面重新配置一下,即可实现访问外网。

迁移CTFd比赛平台(2020.10.22)

我们又新申请到了一台服务器,16核32线程,64GB内存。

上面只跑着我们的一个0xGame比赛平台

感觉有点浪费资源,所以打算把这台新服务器也做一下虚拟化处理,也就是在这台新服务器上也起一个Proxmox平台。

鉴于可能会影响到当前比赛平台(比赛时间持续一个月),万一中途没整成功,比赛平台就也没了,所以我们打算在原来那台服务器上弄一个配置比较高的小鸡,然后把平台先迁移到这台小鸡上,再对新服务器进行操作。

比赛平台是使用的是CTFd框架,所有内容都在CTFd-2.5.0这个目录中,通过docker起的服务(在目录下有docker-compose.yml等配置文件)。

怎么迁移呢?其实很简单,只需要把整个目录拷贝到小鸡上即可。

tar -zcvf 0xGame.tar.gz ./CTFd-2.5.0将整个目录压缩到0xGame.tar.gz这个文件中,然后通过scp传到小鸡上。

在小鸡上执行tar -zxvf 0xGame.tar.gz将文件进行解压,得到CTFd-2.5.0目录。

cd ./CTFd-2.5.0进入到目录中,然后执行docker-compose up来起服务。

下载镜像比较慢,因为校园网是通的,而我本机mbp是通过ClashX 起的代理,而且在ClashX里设置了Allow connect from Lan,socks5的端口是1080。

Screen Shot 2020-10-22 at 9.33.55 PMClashX Config

先找一下mbp的路由器内网ip:192.168.0.103,把mbp的1080端口转发出去:

image-20201022214856140Forward 192.168.0.103:1080 to outside

再去路由器192.168.0.1找到路由器的校园网ip:10.160.106.21,然后在小鸡上设置一下代理:export all_proxy='socks5://10.160.106.21:1080',再去下载所需的镜像,速度飞快。

但是问题来了,下载完镜像后,服务在启动的时候有报错:

AFF392D032425BBE001FB9749A4AA2DAdocker-compose up 报错

上网搜了一下,是数据库相关的问题。

想了一下,docker-compose会起3个容器:CTFd平台、mariadb数据库、redis缓存,数据库容器里的数据肯定映射到了CTFd-2.5.0目录下的某个位置。由于在拷贝目录的时候,未停止docker的运行,那么拷贝的数据大概率上应该是会有一些问题的(数据库可能会有lock之类的)。所以正确的做法应该是:先把docker服务停止一下,然后再去拷贝,这样就可以避免这种问题。

在新机器上,先docker-compose down将3个服务停止,然后再去拷贝目录,拷贝之后,再docker-compose up -d重新启动。

然后再到小鸡上,解压拷贝过来的目录,执行docker-compose up -d起服务,这次就没有问题了。

-d参数表示detach,即后台运行。

服务器外网暂时还无法访问,我们是通过frp把新机器的80端口映射到有公网ip的vps上的,需要再在小鸡上配置一下frp才行。

最后把新机器上的frp和docker服务都关了,成功实现了比赛平台的迁移。

时间不早了,明天再去新机器上装Proxmox。

Proxmox需要拿着U盘去机房装,先暂时咕一会儿。。

搭建校赛NCTF2020平台(2020.11.1)

一年一度的NCTF马上又要开始了。

我校的校赛平台是自研的,开源在 https://github.com/NJUPT-coding-gay/CGCTF_competition_ver

开发这个平台的学长们,已经毕业了,所以今年的平台需要我们自己来搭建。


开了一个服务器,跟着文档里的搭建步骤走了一下。

先安装组件:

1
2
3
sudo apt update
sudo apt upgrade
sudo apt install nginx mysql-server mysql-client php nodejs npm composer redis

然后git clone源码:

1
git clone https://github.com/NJUPT-coding-gay/CGCTF_competition_ver

来到CGCTF_competition_ver/CG-CTF目录下

cd CGCTF_competition_ver/CG-CTF

安装组件:

1
composer install --ignore-platform-reqs

但是有报错:

In Route.php line 917:

  Unable to prepare route [/] for serialization. Uses Closure.


Script php artisan optimize handling the post-install-cmd event returned with error code 1

上网搜了很久,发现是php代码的问题,在./routes/web.php中有好几段路由的设置是用的闭包函数来实现,但是php无法序列化闭包函数,所以GG。不会改php代码,因为这个问题浪费了很多时间。

关于这问题的一个issue:https://github.com/laravel/framework/issues/22034

想到去年校赛的平台是可以正常使用的,好在去年校赛服务器的环境都还在。那么就可以直接在旧服务器上先install好,然后把整个目录打包到新服务器上,这样就可以在新服务器上正常composer install了。

然后继续操作:

1
composer update

继续有报错,说是缺少php组件,那么补上就行:

1
sudo apt install php-xml

然后再composer update就行。

接着就是要对CGCTF_competition_ver/CG-CTF目录下的.env配置文件进行修改。

照着去年平台上的配置,稍微改了一下:

  • APP_NAME=NCTF2020
  • APP_DEBUG=false
  • APP_URL=http://nctf.x1ct34m.com
  • DB_DATABASE=“nctf2020”
  • DB_USERNAME=“root”
  • DB_PASSWORD=“xxxxxxxxxx”
  • ADMIN-CODE=“xxxxxxxxxxx”
  • DYN_FLAG=true

配置了一下mysql数据库:

  • sudo mysql_secure_installation设置mysql数据库root密码
  • mysql -u root -p进入到mysql数据库,CREATE DATABASE nctf2020;创建数据库。

然后根据文档上的,php artisan key:generate创建用于加密的项目key

然后php artisan migrate:install初始化数据库,但是也有报错。

大致有这么几种:

  • Could not find driver / Unable to load dynamic library ‘pdo_mysql’

    php的mysql插件没装:sudo apt-get install php-common php-mysql php-cli

  • Access denied for user ‘root’@’localhost’

    不能是root用户,需要就再进入mysql数据库创建一个普通用户:

    1
    2
    3
    
    mysql> CREATE USER 'newuser'@'localhost' IDENTIFIED BY 'password';
    mysql> GRANT ALL PRIVILEGES ON * . * TO 'newuser'@'localhost';
    mysql> FLUSH PRIVILEGES;
    

    重新登陆,修改.env文件中的DB_USERNAME

    修改了东西后,记得php artisan config:clear

继续php artisan migrate:install,然后php artisan migrate迁移表中的内容。

再生成一下前端:

1
2
npm install
npm run dev

最后起服务:

1
php artisan serve --host 0.0.0.0 --port 8000

直接访问ip,ok没问题。

然后可以访问ip/IN1t4dmin_Cg_c7f_X1c_+1s来创建管理员账号。

Admin Confirm就是.env文件中的ADMIN-CODE

再把nctf.x1ct34m.com这个域名解析到这个ip上,去FreeSSL 上申请了一下证书。

域名没备案,所以租的服务器是香港的。

通过nginx接受80和443端口的流量,并转发到本地的8000端口,配置了http转https。

blocked mixed content解决方法:https://stackoverflow.com/questions/35827062/how-to-force-laravel-project-to-use-https-for-all-routes

routes/web.phproutes/api.php的末尾加上一行URL::forceScheme('https');

 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
$ cat /etc/nginx/sites-enabled/nctf-x1ct34m-com
server
{
    listen       443 ssl;

    server_name  "nctf.x1ct34m.com";

    ssl_certificate /var/cert/nctf.x1ct34m.com_chain.crt;
    ssl_certificate_key /var/cert/nctf.x1ct34m.com_key.key;
    ssl_session_timeout 5m;
    ssl_protocols TLSv1.1 TLSv1.2;
    ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384';
    ssl_prefer_server_ciphers off;

    location / {
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    	proxy_set_header X-Forwarded-Proto $scheme;
        proxy_pass http://127.0.0.1:8000;
    }
}


server
{
    listen 80;

    server_name "nctf.x1ct34m.com";

    return 301 https://nctf.x1ct34m.com$request_uri;
}

找了一个图标,放到了CGCTF_competition_ver/CG-CTF/Public目录下。

试了一下平台功能,算是能用吧。好像还差一个Scoreboard,这玩意儿似乎是另外一个项目??暂时没找到,先不整了。

期间可能漏了一些报错。不得不说,这平台真难搭。。。。

还是挺期待11月中旬的NCTF2020的,虽然那个时间段也有很多其他比赛,真的很忙很忙。。。。