1、一粒云二次封装与迁移 – 初步分析
因为CentOS系列的停服,需要将单位购买的商用云盘从该系统上迁移至其它发行版。但云盘官方只对CentOS7进行了适配,在别的系统上执行安装用的SetUp会提示不支持,所以需要按照该系统所用的软件架构来反向推导安装过程,重新编写安装程序。
首先需要了解该系统运行所需的基础软件,这个比较简单使用Htop或者Top命令即可。
1 [ 0.0%] Tasks: 43; 1 running 2 [ 0.0%] Load average: 0.08 0.24 0.15 3 [|| 1.3%] Uptime: 00:09:27 4 [ 0.0%] Mem[||||||||||||||||||||| 1.24G/3.70G] Swp[ 0K/5.87G] PID USER PRI NI VIRT RES SHR S CPU% MEM% TIME+ Command 2519 root 20 0 1036M 152M 12724 S 0.7 4.0 0:06.86 node /opt/yliyun/work/node/conv.js 2677 root 20 0 120M 2832 1512 R 1.3 0.1 0:02.80 htop 2492 root 20 0 898M 27160 11860 S 0.7 0.7 0:01.88 PM2 v3.0.3: God Daemon (/root/.pm2) 2364 root 20 0 143M 9636 1136 S 0.0 0.2 0:00.33 /opt/yliyun/redis/bin/redis-server 12 787 root 20 0 291M 6276 4940 S 0.0 0.2 0:00.54 /usr/bin/vmtoolsd 2526 root 20 0 945M 52576 12668 S 0.0 1.4 0:03.17 node /opt/yliyun/work/node/update.js 2502 root 20 0 1051M 159M 13444 S 0.0 4.2 0:07.88 node /opt/yliyun/work/node/app.js 2379 root 20 0 151M 5212 496 S 0.0 0.1 0:00.12 /opt/yliyun/fdfs/bin/fdfs_trackerd /o 2507 root 20 0 945M 53968 12648 S 0.0 1.4 0:03.43 node /opt/yliyun/work/node/socket.js 2514 root 20 0 1050M 162M 13396 S 0.0 4.3 0:07.67 node /opt/yliyun/work/node/app.js 1156 root 20 0 560M 19064 6040 S 0.0 0.5 0:00.42 /usr/bin/python -Es /usr/sbin/tuned - 2414 root 20 0 162M 129M 892 S 0.0 3.4 0:00.25 /opt/yliyun/fdfs/bin/fdfs_storaged /o 2337 root 20 0 3479M 269M 6576 S 0.0 7.1 0:00.75 /opt/yliyun/mysql/bin/mysqld --basedi 2625 root 20 0 157M 6124 4744 S 0.0 0.2 0:00.20 sshd: root@pts/0 1603 root 20 0 92232 21908 1624 S 0.0 0.6 0:00.05 /usr/bin/perl /usr/libexec/webmin/min 1 root 20 0 122M 3892 2568 S 0.0 0.1 0:01.91 /usr/lib/systemd/systemd --switched-r 583 root 20 0 39076 3092 2764 S 0.0 0.1 0:00.17 /usr/lib/systemd/systemd-journald 609 root 20 0 185M 3404 968 S 0.0 0.1 0:00.00 /usr/sbin/lvmetad -f 615 root 20 0 45060 2624 1320 S 0.0 0.1 0:00.34 /usr/lib/systemd/systemd-udevd 759 root 16 -4 55508 896 492 S 0.0 0.0 0:00.01 /sbin/auditd 782 root 20 0 21676 1304 976 S 0.0 0.0 0:00.04 /usr/sbin/irqbalance --foreground 784 polkitd 20 0 526M 12952 4924 S 0.0 0.3 0:00.08 /usr/lib/polkit-1/polkitd --no-debug 785 root 20 0 26376 1784 1460 S 0.0 0.0 0:00.01 /usr/lib/systemd/systemd-logind 786 root 20 0 99656 6120 4508 S 0.0 0.2 0:00.03 /usr/bin/VGAuthService -s 788 dbus 20 0 58204 2496 1828 S 0.0 0.1 0:00.12 /usr/bin/dbus-daemon --system --addre 800 root 20 0 123M 1600 984 S 0.0 0.0 0:00.00 /usr/sbin/crond -n 801 root 20 0 99152 2700 2016 S 0.0 0.1 0:00.06 login -- root 805 chrony 20 0 114M 1724 1300 S 0.0 0.0 0:00.02 /usr/sbin/chronyd 818 root 20 0 349M 29136 7176 S 0.0 0.8 0:01.26 /usr/bin/python -Es /usr/sbin/firewal 833 root 20 0 465M 9136 6880 S 0.0 0.2 0:00.17 /usr/sbin/NetworkManager --no-daemon 1157 root 20 0 213M 4656 3204 S 0.0 0.1 0:00.22 /usr/sbin/rsyslogd -n 1162 root 20 0 110M 4292 3268 S 0.0 0.1 0:00.02 /usr/sbin/sshd -D 1440 root 20 0 89620 2084 1072 S 0.0 0.1 0:00.03 /usr/libexec/postfix/master -w 1457 postfix 20 0 89724 4052 3056 S 0.0 0.1 0:00.00 pickup -l -t unix -u 1458 postfix 20 0 89792 4084 3080 S 0.0 0.1 0:00.01 qmgr -l -t unix -u 1664 root 20 0 112M 2204 1704 S 0.0 0.1 0:00.10 -bash 1747 root 20 0 110M 1600 1304 S 0.0 0.0 0:00.08 /bin/sh /opt/yliyun/mysql/bin/mysqld_ 2430 root 20 0 36860 1684 396 S 0.0 0.0 0:00.00 nginx: master process /opt/yliyun/ngi 2431 root 20 0 64144 29524 1000 S 0.0 0.8 0:00.05 nginx: worker process 2432 root 20 0 64144 29524 1000 S 0.0 0.8 0:00.02 nginx: worker process 2433 root 20 0 64144 29524 1000 S 0.0 0.8 0:00.03 nginx: worker process 2434 root 20 0 64144 29524 1000 S 0.0 0.8 0:00.04 nginx: worker process 2630 root 20 0 112M 2124 1644 S 0.0 0.1 0:00.02 -bash F1Help F2Setup F3SearchF4FilterF5Tree F6SortByF7Nice -F8Nice +F9Kill F10Quit
Htop显示,初步判断该系统主要使用nodejs,pm2,nginx,fastdfs,redis,mysql。整理下信息并写出推断
- nodejs -> 一门服务端语言,用来处理后端逻辑与数据库进行操作等功能。
- pm2 -> 用来管理nodejs进程,定时重启,负载均衡,崩溃重启等功能。
- nginx -> web中间件,可以实现负载均衡,Waf防火墙,转发等功能。
- fastdfs -> 分布式文件网络文件系统,用来实现文件的存储功能。
- redis -> 缓存数据库,将频繁读写的数据临时存储在其中,实现高速读写。
- mysql -> Sql数据库,用来存储长期数据。
使用pm2查看管理的nodejs进程
[root@yly yliyun]# pm2 list ┌──────────┬────┬─────────┬──────┬────────┬─────────┬────────┬──────┬────────────┬──────┬──────────┐ │ App name │ id │ mode │ pid │ status │ restart │ uptime │ cpu │ mem │ user │ watching │ ├──────────┼────┼─────────┼──────┼────────┼─────────┼────────┼──────┼────────────┼──────┼──────────┤ │ app │ 0 │ cluster │ 2502 │ online │ 0 │ 31m │ 0% │ 180.8 MB │ root │ disabled │ │ app │ 2 │ cluster │ 2514 │ online │ 0 │ 31m │ 0.1% │ 180.3 MB │ root │ disabled │ │ conv │ 3 │ cluster │ 2519 │ online │ 0 │ 31m │ 0.3% │ 179.0 MB │ root │ disabled │ │ socket │ 1 │ cluster │ 2507 │ online │ 0 │ 31m │ 0.1% │ 71.2 MB │ root │ disabled │ │ update │ 4 │ cluster │ 2526 │ online │ 0 │ 31m │ 0.1% │ 64.9 MB │ root │ disabled │ └──────────┴────┴─────────┴──────┴────────┴─────────┴────────┴──────┴────────────┴──────┴──────────┘ Use `pm2 show <id|name>` to get more details about an app
可以看出nodejs主要有app,conv,socket,update 4个进程,其中app生成2个进程用来负载均衡。
以上都为猜测,接下去进入/opt/yliyun目录进行更加细致的查看,使用tree命令进行目录结构查看
[root@yly yliyun]# tree -L 2 . ├── bin │ ├── centos7 │ ├── fdfs │ ├── fdfs_storaged │ ├── fdfs_trackerd │ ├── mysqld │ ├── nginx │ ├── node │ ├── redis │ └── yliyun ├── common │ ├── bin │ └── lib ├── conf │ ├── CentOS-Local-7.5.1804.repo │ ├── fdfs │ ├── fonts │ ├── rpm │ └── src ├── dailytemp │ ├── 30sql │ └── 7logs ├── data │ ├── client │ ├── g1_data0 │ ├── storage │ └── tracker ├── fdfs │ ├── bin │ ├── etc │ ├── include │ ├── lib │ └── lib64 ├── logs │ ├── mysql │ ├── nginx │ ├── node │ └── redis ├── mysql │ ├── bin │ ├── COPYING │ ├── data │ ├── docs │ ├── etc │ ├── include │ ├── lib │ ├── man │ ├── my.cnf │ ├── my-new.cnf │ ├── mysql-test │ ├── README │ ├── scripts │ ├── share │ ├── sql-bench │ └── support-files ├── nginx -> /opt/yliyun/openresty/nginx ├── node │ ├── bin │ ├── etc │ ├── include │ ├── lib │ └── share ├── openresty │ ├── bin │ ├── COPYRIGHT │ ├── luajit │ ├── lualib │ ├── nginx │ ├── pod │ ├── resty.index │ └── site ├── plugins │ ├── freetype │ ├── ghostscript │ ├── imagemagick │ ├── libjpeg │ ├── libpng │ ├── libreoffice │ ├── qcad │ └── uniconvertor ├── redis │ ├── bin │ ├── data │ ├── redis.conf │ ├── runtest │ ├── runtest-cluster │ ├── runtest-sentinel │ ├── sentinel.conf │ ├── src │ ├── tests │ └── utils ├── setup ├── temp │ └── upload ├── work │ ├── lua │ ├── nginx │ └── node └── yliyun.lic
从上面可以看出,前面使用Htop查询的结果出现了一些错误,这个系统并不是使用nginx而是使用的openresty,从这点我们可以得知信息,该云盘后端处理语言不止nodejs这一种,work下的lua目录也验证了我的猜想,openresty处理语言就是luajit。
从plugins目录下的名称可以得知这个目录下存放的应该是一些图片和office处理的库,fdfs目录是fastdfs的安装目录,conf下是配置目录,从该目录下可以看出为什么不支持CentOS7以外系统的安装😂。
bin是启动脚本,yliyun.lic是授权文件,work是程序目录,temp是临时目录,从下级的upload下也可以看出,上传文件会在该目录临时存放。logs是日志,data下是数据,common暂时无法得知用处,不过看下级的bin和lib应该是一些可执行程序。
目前系统使用为nodejs,pm2,openresty,fastdfs,redis,mysql。
接下去解析配置文件,首先从nodejs开始。从上面tree的目录结构下可以发现,work目录是程序目录,除了lua外,还存在一个node目录,该目录就极有可是nodejs的程序目录。
[root@yly node]# tree -L 1 . ├── app.js ├── config ├── conv.js ├── default.json ├── models ├── nas ├── node_modules ├── package.json ├── package-lock.json ├── plugin.js ├── pm2.json ├── scripts ├── socket.js ├── update.js └── versions.json
其中app.js,conv.js,socket.js,update.js就是pm2中管理的4个进程。
另外,其余文件结构也可以给我们提供不少信息,例如pm2.json是pm2的一个启动的配置文件。我们cat一下该文件。
[root@yly node]# cat pm2.json [ { "name": "app", "script": "/opt/yliyun/work/node/app.js", "cwd": "/opt/yliyun/work/node", "instances": 2, "instance_id_env": "NODE_APP_INSTANCE", "max_memory_restart": "1024M", "pid_file": "/opt/yliyun/logs/node/app.pid", "error_file": "/opt/yliyun/logs/node/app-err.log", "out_file": "/opt/yliyun/logs/node/app.log", "log_date_format": "YYYY-MM-DD HH:mm:ss" }, { "name": "socket", "script": "/opt/yliyun/work/node/socket.js", "cwd": "/opt/yliyun/work/node", "instances": 1, "instance_id_env": "NODE_APP_INSTANCE", "max_memory_restart": "256M", "pid_file": "/opt/yliyun/logs/node/socket.pid", "error_file": "/opt/yliyun/logs/node/socket-err.log", "out_file": "/opt/yliyun/logs/node/socket.log", "log_date_format": "YYYY-MM-DD HH:mm:ss" }, { "name": "conv", "script": "/opt/yliyun/work/node/conv.js", "cwd": "/opt/yliyun/work/node", "instances": 1, "instance_id_env": "NODE_APP_INSTANCE", "max_memory_restart": "256M", "pid_file": "/opt/yliyun/logs/node/conv.pid", "error_file": "/opt/yliyun/logs/node/conv-err.log", "out_file": "/opt/yliyun/logs/node/conv.log", "log_date_format": "YYYY-MM-DD HH:mm:ss" }, { "name": "update", "script": "/opt/yliyun/work/node/update.js", "cwd": "/opt/yliyun/work/node", "instances": 1, "instance_id_env": "NODE_APP_INSTANCE", "max_memory_restart": "256M", "pid_file": "/opt/yliyun/logs/node/update.pid", "error_file": "/opt/yliyun/logs/node/update-err.log", "out_file": "/opt/yliyun/logs/node/update.log", "log_date_format": "YYYY-MM-DD HH:mm:ss" } ]
更加确定了app.js,conv.js,socket.js,update.js是该云盘执行的重要进程。
接下去进入该目录下的config目录,该目录下应该存放一些配置文件。对这些文件进行一一查看。
[root@yly config]# ls app(1).js cmd.json other.json sms.json txmsg.json app.json oauth20-off.json plugin.json system.json wxPublic.json cas-off.json ordinary.json servers.json third_party.json
以下文件为一些关键文件。
[root@yly config]# cat app.json { "serverId": 1, "task": true, "env": "production", "log": "debug", "seqCache": 200, "seqFile": "/opt/yliyun/work/seq.id", "tmpdir": "/opt/yliyun/temp", "cloud": "www.yliyun.com", "web": "127.0.0.1", "mysql": { "host": "127.0.0.1", "username": "At4Bzqwek+mNiiEGZ4k26Q==", "password": "iVHkZtQ7W+wrOiXLvJ0urw==", "pool": { "max": 200, "min": 5, "idle": 10000 } }, "redis": { "host": "127.0.0.1", "port": 6379, "password": "yliyun@123" }, "fdfs": { "trackers": [ { "host": "127.0.0.1", "port": 22122 } ] } }
从该配置文件我们可以得知mysql,redis,fdfs的ip与运行端口,还有密码等。不过mysql的密码与用户名是加密状态。
[root@yly config]# cat cmd.json { "common": { "ping": "ping %s" }, "nas": { "mount":"mount -t cifs %s /%s -o username='%s',password='%s',domain='.com'", "umount" :"umount -v %s", "info" : "mountpoint %s", "updatedb" : "updatedb -o %s -U %s" }, "search" : "locate -d %s -b %s | grep %s" }
另一个配置文件cmd.json。执行一些系统命令,应该是用来挂载其它云盘的,移植时保证这些命令可用即可。
[root@yly config]# cat plugin.json { "mysql": { "host": "127.0.0.1", "username": "YbKX9GSeePfsg4j+Rdkm2w==", "password": "lCM2W4RRpxiDofnH/obnYg==", "pool": { "max": 200, "min": 5, "idle": 10000 } } }
plugin.json 同样是加密状态的mysql配置。
其余配置文件为扩展功能的配置文件,例如企业微信通知,邮件,钉钉等Api地址参数。
[root@yly config]# pm2 list ┌──────────┬────┬─────────┬──────┬────────┬─────────┬────────┬──────┬────────────┬──────┬──────────┐ │ App name │ id │ mode │ pid │ status │ restart │ uptime │ cpu │ mem │ user │ watching │ ├──────────┼────┼─────────┼──────┼────────┼─────────┼────────┼──────┼────────────┼──────┼──────────┤ │ app │ 0 │ cluster │ 2502 │ online │ 0 │ 105m │ 0.1% │ 192.9 MB │ root │ disabled │ │ app │ 2 │ cluster │ 2514 │ online │ 0 │ 105m │ 0.1% │ 191.9 MB │ root │ disabled │ │ conv │ 3 │ cluster │ 2519 │ online │ 0 │ 105m │ 0.4% │ 163.4 MB │ root │ disabled │ │ socket │ 1 │ cluster │ 2507 │ online │ 0 │ 105m │ 0.1% │ 83.6 MB │ root │ disabled │ │ update │ 4 │ cluster │ 2526 │ online │ 0 │ 105m │ 0.1% │ 84.2 MB │ root │ disabled │ └──────────┴────┴─────────┴──────┴────────┴─────────┴────────┴──────┴────────────┴──────┴──────────┘ Use `pm2 show <id|name>` to get more details about an app [root@yly config]# pm2 stop 0 1 2 3 4 [PM2] Applying action stopProcessId on app [0](ids: 0) [PM2] [app](0) ✓ [PM2] Applying action stopProcessId on app [1](ids: 1) [PM2] [socket](1) ✓ [PM2] Applying action stopProcessId on app [2](ids: 2) [PM2] [app](2) ✓ [PM2] Applying action stopProcessId on app [3](ids: 3) [PM2] [conv](3) ✓ [PM2] Applying action stopProcessId on app [4](ids: 4) [PM2] [update](4) ✓ ┌──────────┬────┬─────────┬─────┬─────────┬─────────┬────────┬─────┬────────┬──────┬──────────┐ │ App name │ id │ mode │ pid │ status │ restart │ uptime │ cpu │ mem │ user │ watching │ ├──────────┼────┼─────────┼─────┼─────────┼─────────┼────────┼─────┼────────┼──────┼──────────┤ │ app │ 0 │ cluster │ 0 │ stopped │ 0 │ 0 │ 0% │ 0 B │ root │ disabled │ │ app │ 2 │ cluster │ 0 │ stopped │ 0 │ 0 │ 0% │ 0 B │ root │ disabled │ │ conv │ 3 │ cluster │ 0 │ stopped │ 0 │ 0 │ 0% │ 0 B │ root │ disabled │ │ socket │ 1 │ cluster │ 0 │ stopped │ 0 │ 0 │ 0% │ 0 B │ root │ disabled │ │ update │ 4 │ cluster │ 0 │ stopped │ 0 │ 0 │ 0% │ 0 B │ root │ disabled │ └──────────┴────┴─────────┴─────┴─────────┴─────────┴────────┴─────┴────────┴──────┴──────────┘ Use `pm2 show <id|name>` to get more details about an app
关闭pm2中所有的nodejs服务,访问云盘主页面。
云盘页面并没有全部宕掉,这说明pm2所托管的4个服务的端口并不包括80,80被openresty监听,当pm2托管的nodejs服务宕机时,openresty来抛出此错误,也就说明openresty还担当了流量转发的任务。下一步就进入openresty的配置文件来查看具体情况。
[root@yly work]# tree -L 2 . ├── lua │ ├── common │ ├── conf │ ├── config.lua │ ├── download │ ├── fdfs │ ├── lib │ ├── nginx │ ├── redis │ ├── upload │ └── utils ├── nginx │ ├── admin.css │ ├── admin.html │ ├── app │ ├── client │ ├── commons.css │ ├── dl │ ├── download.css │ ├── download.html │ ├── favicon.ico │ ├── font │ ├── h5 │ ├── home.css │ ├── home.html │ ├── ie.html │ ├── ie-share.html │ ├── img │ ├── knbs.html │ ├── layui │ ├── links.css │ ├── links.html │ ├── login.css │ ├── login.html │ ├── oauth20_access.html │ ├── pdf │ ├── plugins │ ├── static │ ├── temp │ ├── vaildation.html │ ├── vendor.css │ ├── views.css │ └── views.html └── node ├── app.js ├── config ├── conv.js ├── default.json ├── models ├── nas ├── node_modules ├── package.json ├── package-lock.json ├── plugin.js ├── pm2.json ├── scripts ├── socket.js ├── update.js └── versions.json 28 directories, 31 files
在work下使用tree输出2级结构,该目录下的nginx目录其实是网页的静态资源目录。
[root@yly work]# find . -name nginx.conf ./lua/nginx/nginx.conf
find一下nginx.conf,使用vim直接打开查看。
user root; worker_processes 4; error_log /opt/yliyun/logs/nginx/error.log; error_log /opt/yliyun/logs/nginx/error.log notice; #error_log logs/error.log info; pid /opt/yliyun/logs/nginx/nginx.pid; worker_rlimit_nofile 65535; events { use epoll; worker_connections 102400; } http { include mime.types; default_type application/octet-stream; log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; access_log /opt/yliyun/logs/nginx/access.log main;
因为nginx.conf内容较多,不全部展示。如上的内容主要展示了日志文件的路径,运行用户,工作进程数。
proxy_connect_timeout 5; proxy_read_timeout 60; proxy_send_timeout 5; proxy_buffer_size 16k; proxy_buffers 4 64k; proxy_busy_buffers_size 128k; proxy_hide_header Vray; proxy_http_version 1.1; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $host; #gzip on; gzip on; gzip_min_length 1k; gzip_buffers 4 16k; gzip_http_version 1.0; gzip_comp_level 6; gzip_proxied any; gzip_types text/plain text/css application/json application/x-javascript application/xml application/javascript ; gzip_disable "MSIE [1-6]"; lua_package_path '/opt/yliyun/work/lua/?.lua;/opt/yliyun/openresty/lualib/resty/?.lua;;'; init_by_lua_file '/opt/yliyun/work/lua/common/init.lua';
http块中的这些内容,其中lua_package_path , init_by_lua_file是lua程序的路径。这个如果错误那么openresty找不到需要的lua模块。
server { listen 80; set $useHttps 'off'; # https ssl config # $http:system variate, when request is https, $http='on',otherwise $https='' ,$scheme current request 'http' or 'https'; #listen 443 default_server ssl; #set $useHttps 'on'; #add_header Strict-Transport-Security "max-age=31536000; includeSubDomains;preload" always; #ssl_certificate server.crt; #ssl_certificate_key server.key; #ssl_session_cache shared:SSL:10m; #ssl_session_timeout 10m; #ssl_prefer_server_ciphers on; server_name localhost; charset utf-8; #lua_code_cache off; root /opt/yliyun/work/nginx/; #access_log logs/host.access.log main; #if ($http_user_agent ~* '(iphone|ipod|ipad)') { # rewrite ^/$ /ios/; #} # upload settings upload_pass_args on; upload_limit_rate 1050k; upload_store /opt/yliyun/temp; upload_set_form_field $upload_field_name.name "$upload_file_name"; upload_set_form_field $upload_field_name.path "$upload_tmp_path"; upload_aggregate_form_field "$upload_field_name.md5" "$upload_file_md5"; upload_aggregate_form_field $upload_field_name.crc32 $upload_file_crc32; upload_aggregate_form_field "$upload_field_name.size" "$upload_file_size"; upload_pass_form_field "param"; upload_cleanup 200-299 400-499 500-505; #MSIE [6-8].[0-9] Trident location / { index index.html login.html; }
server块下比较重要的内容有 root /opt/yliyun/work/nginx/; 这个指定了页面的静态资源目录。upload_store /opt/yliyun/temp;是指定一个上传的临时目录。从这句配置内容中,我们可以得到一个非常重要的信息,openresty中启用了一个上传模块 nginx-upload-module 该模块nginx不自带,需要从github上下载。
location ~ ^/group1/M[0-9][0-9]/(.*)$ { access_by_lua_file /opt/yliyun/work/lua/download/anti_steal_check.lua; proxy_buffering off; limit_rate_after 50m; limit_rate 500k; ngx_fastdfs_module; }
继续往下看配置内容,其中遇到如上一段location配置,可以看见fastdfs的链接会交由anti_steal_check.lua校验,确定有权限下载后,在由 ngx_fastdfs_module 模块进行下载。由此又可以获得一个很重要的信息,openresty中启用了一个与fastdfs文件系统交互的模块 ngx_fastdfs_module 该模块nginx不自带,需要从github上fastdfs项目中去下载。
#nginx call md5sum search storage to call md5sum location ~ ^/broadcast/md5sum/(\w+)/(.*)$ { default_type 'text/html;charset=utf-8'; content_by_lua_file /opt/yliyun/work/lua/fdfs/md5sum.lua; } # storage's nginx recived location ~ ^/md5sum/\w+/(M[0-9][0-9])/(.*)$ { default_type 'text/html;charset=utf-8'; content_by_lua_file /opt/yliyun/work/lua/fdfs/storage_md5sum.lua; } #nginx call fsize search storage to call file size location ~ ^/broadcast/fsize/(\w+)/(.*)$ { default_type 'text/html;charset=utf-8'; content_by_lua_file /opt/yliyun/work/lua/fdfs/fsize.lua; } # storage's nginx recived location ~ ^/fsize/\w+/(M[0-9][0-9])/(.*)$ { default_type 'text/html;charset=utf-8'; content_by_lua_file /opt/yliyun/work/lua/fdfs/storage_fsize.lua; } # list all storage paths location = /fdfs/storage/path/list { default_type 'text/html;charset=utf-8'; content_by_lua_file /opt/yliyun/work/lua/fdfs/storage_path_list.lua; } # update storage paths location = /fdfs/storage/path/update { default_type 'text/html;charset=utf-8'; content_by_lua_file /opt/yliyun/work/lua/fdfs/storage_path_update.lua; } # to discard,at present for compatibility faulty lua signal file upload location = /lua/upload { proxy_set_header x-real-ip $remote_addr; upload_pass @lua-upload; }
继续往下可以看见如上内容,这些location匹配到对应的Url后,会将请求交给对应的lua程序进行处理。
location ~ ^/apps/(.*)$ { #proxy_set_header x-real-ip $remote_addr; #proxy_request_buffering off; proxy_pass http://127.0.0.1:3000/$1?$query_string; } location /socket.io/ { proxy_pass http://127.0.0.1:3001$request_uri; proxy_redirect off; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_http_version 1.1; }
而上面这两条配置,确定了nodejs与openresty之间的关系,openresty会将~ ^/apps/(.*)$的Url规则转发到本地端口3000,会将/socket.io/的Url规则转发到3001端口处理,3000对应app.js,3001对应socket。而conv.js 和 update.js也许承担定时任务和系统更新这些需要后期在进行验证。
由于我们不知道数据库初始结构,所以需要删除数据库中的部分数据来进行一个伪初始化。但数据库密码在参数中被加密,我们无法逆向,不过幸运的是,nodejs是一门脚本语言,虽然它可能被混淆。我们直接使用vim打开work/node/app.js文件,寻找可能的解密代码。
如何标记搜索点?
这时需要用上先前的app.json文件中的内容。
[root@yly ~]# cat /opt/yliyun/work/node/config/app.json { "serverId": 1, "task": true, "env": "production", "log": "debug", "seqCache": 200, "seqFile": "/opt/yliyun/work/seq.id", "tmpdir": "/opt/yliyun/temp", "cloud": "www.yliyun.com", "web": "127.0.0.1", "mysql": { "host": "127.0.0.1", "username": "At4Bzqwek+mNiiEGZ4k26Q==", "password": "iVHkZtQ7W+wrOiXLvJ0urw==", "pool": { "max": 200, "min": 5, "idle": 10000 } }, "redis": { "host": "127.0.0.1", "port": 6379, "password": "yliyun@123" }, "fdfs": { "trackers": [ { "host": "127.0.0.1", "port": 22122 } ] } }
json是javascript原生支持的数据格式,如上读取mysql中的加密密码时,读取结构为 变量.mysql.password 或 变量[“mysql”][“password”] 那么就在work/node/app.js中进行查询。
[root@yly ~]# vim /opt/yliyun/work/node/app.js !function(e){var t={};function r(n){if(t[n])return t[n].exports;var i=t[n]={i:n,l:!1,exports:{}};return e[n].call(i.exports,i,i.exports,r),i.l=!0,i.exports}r.m=e,r.c=t,r.d=function(e,t,n){r.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},r.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var i in e)r.d(n,i,function(t){return e[t]}.bind(null,i));return n},r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,"a",t),t},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r.p="",r(r.s=111)}([function(e,t,r){"use strict";const n=r(34),i=r(2),o=r(6),u=r(13),s=e.exports={};let a=function(e){i.debug(e)},d=u.decrypt(o.app.mysql.username,"ih9j8j!@$"),l=u.decrypt(o.app.mysql.password,"ih9j8j!@$"),c={host:o.app.mysql.host,port:3306,dialect:"mysql",timezone:"+08:00",logging:a,pool:o.app.mysql.pool||{max:200,min:5,idle:1e4}},f=new n("yliyun",d,l,c);console.log("\n\n\n\n密码",u.decrypt(o.app.mysql.password,"ih9j8j!@$"),"\n\n\n\n");s.mysql={username:d,pwd:l},s.sequelize=f,s.Sequence=f.import("./models/Sequence.js"),s.Department=f.import("./models/Department.js"),s.User=f.import("./models/User.js"),s.FsFile=f.import("./models/FsFile.js"),s.FsFileAttr=f.import("./models/FsFileAttr.js"),s.GroupFile=f.import("./models/GroupFile.js"),s.UserGroup=f.import("./models/UserGroup.js"),s.GroupMember=f.import("./models/GroupMember.js"),s.Msg=f.import("./models/Msg.js"),s.MsgSend=f.import("./models/MsgSend.js"),s.Enterprise=f.import("./models/Enterprise.js"),s.FileComment=f.import("./models/FileComment.js"),s.FilePraise=f.import("./models/FilePraise.js"),s.ShareFile=f.import("./models/ShareFile.js"),s.ShareFileInfo=f.import("./models/ShareFileInfo.js"),s.FileHistory=f.import("./models/FileHistory.js"),s.ConvFile=f.import("./models/ConvFile.js"),s.PublicFile=f.import("./models/PublicFile.js"),s.PersonalFile=f.import("./models/PersonalFile.js"),s.GroupTrashFile=f.import("./models/GroupTrashFile.js"),s.PersonalTrashFile=f.import("./models/PersonalTrashFile.js"),s.PublicTrashFile=f.import("./models/PublicTrashFile.js"),s.Ldap=f.import("./models/Ldap.js"),s.LdapOu=f.import("./models/LdapOu.js"),s.GroupUsedSize=f.import("./models/GroupUsedSize.js"),s.PersonalUsedSize=f.import("./models/PersonalUsedSize.js"),s.DeptPer=f.import("./models/DeptPer.js"),s.UserPer=f.import("./models/UserPer.js"),s.SysConfig=f.import("./models/SysConfig.js"),s.SpaceConfig=f.import("./models/SpaceConfig.js"),s.Plugin=f.import("./models/Plugin.js"),s.PersonalSyncFolder=f.import("./models/PersonalSyncFolder.js"),s.GroupSyncFolder=f.import("./models/GroupSyncFolder.js"),s.PublicSyncFolder=f.import("./models/PublicSyncFolder.js"),s.Propertys=f.import("./models/Propertys.js"),s.UserOnline=f.import("./models/UserOnline.js"),s.Versions=f.import("./models/Versions.js"),s.TransDepartment=f.import("./models/TransDepartment.js"),s.GroupAccess=f.import("./models/GroupAccess.js"),s.Rules=f.import("./models/Rules.js"),s.VUser=f.import("./models/VUser.js"),s.VUserGroup=f.import("./models/VUserGroup.js"),s.VJoinedGroup=f.import("./models/VJoinedGroup.js"),s.VGroupMember=f.import("./models/VGroupMember.js"),s.VGroupTrashFile=f.import("./models/VGroupTrashFile.js"),s.VPersonalTrashFile=f.import("./models/VPersonalTrashFile.js"),s.VPublicTrashFile=f.import("./models/VPublicTrashFile.js"),s.VGroupShareFile=f.import("./models/VGroupShareFile.js"),s.VPersonalShareFile=f.import("./models/VPersonalShareFile.js"),s.VPublicShareFile=f.import("./models/VPublicShareFile.js"),s.VGroupUsedSize=f.import("./models/VGroupUsedSize.js"),s.VPersonalUsedSize=f.import("./models/VPersonalUsedSize.js"),s.VDeptPer=f.import("./models/VDeptPer.js"),s.VUserPer=f.import("./models/VUserPer.js"),s.VFileDeptPer=f.import("./models/VFileDeptPer.js"),s.VFileUserPer=f.import("./models/VFileUserPer.js"),s.VFilePraise=f.import("./models/VFilePraise.js"),s.VPersonalSyncFolder=f.import("./models/VPersonalSyncFolder.js"),s.VGroupSyncFolder=f.import("./models/VGroupSyncFolder.js"),s.VPublicSyncFolder=f.import("./models/VPublicSyncFolder.js"),s.VDepartmentUsedSize=f.import("./models/VDepartmentUsedSize.js"),s.VEntPer=f.import("./models/VEntPer.js"),s.SysConfig.removeAttribute("id"),s.SpaceConfig.removeAttribute("id"),s.ShareFileInfo.removeAttribute("id"),s.VUser.removeAttribute("id"),s.VUserGroup.removeAttribute("id"),s.VJoinedGroup.removeAttribute("id"),s.VGroupMember.removeAttribute("id"),s.VGroupTrashFile.removeAttribute("id"),s.VPersonalTrashFile.removeAttribute("id"),s.VPublicTrashFile.removeAttribute("id"),s.VGroupShareFile.removeAttribute("id"),s.VPersonalShareFile.removeAttribute("id"),s.VPublicShareFile.removeAttribute("id"),s.VGroupUsedSize.removeAttribute("id"),s.VPersonalUsedSize.removeAttribute("id"),s.VDeptPer.removeAttribute("id"),s.VUserPer.removeAttribute("id"),s.VFileDeptPer.removeAttribute("id"),s.VFileUserPer.removeAttribute("id"),s.VFilePraise.removeAttribute("id"),s.VPersonalSyncFolder.removeAttribute("id"),s.VGroupSyncFolder.removeAttribute("id"),s.VPublicSyncFolder.removeAttribute("id"),s.VDepartmentUsedSize.removeAttribute("id"),s.VEntPer.removeAttribute("id"),s.User.hasMany(s.TransDepartment,{as:"TransDepartment",foreignKey:"userId"}),s.TransDepartment.belongsTo(s.Department,{foreignKey:"deptId"}),s.Department.hasMany(s.TransDepartment,{foreignKey:"deptId"}),s.User.belongsToMany(s.Department,{as:"Department",through:s.TransDepartment,foreignKey:"userId"}),s.Department.belongsToMany(s.User,{as:"User",through:s.TransDepartment,foreignKey:"deptId"}),s.KnowledgeItem=f.import("./models/KnowledgeItem"),s.KnowledgeLibrary=f.import("./models/KnowledgeLibrary"),s.KnowledgeTag=f.import("./models/KnowledgeTag"),s.Nas=f.import("./nas/models/Nas"),s.NasShare=f.import("./nas/models/NasShare"),s.N "/opt/yliyun/work/node/app.js" 1L, 591218C
如下我们使用 ?mysql.password 查找到了对应代码 d=u.decrypt(o.app.mysql.username,”ih9j8j!@$”) 1为了可以获得解密后的值,我们在后面使用 console.log(“\n\n\n\n密码”,u.decrypt(o.app.mysql.password,”ih9j8j!@$”),”\n\n\n\n”) 进行打印。
当pm2管理nodejs时,console不是那么容易查看,所以我们需要停止pm2使用node直接运行app.js
[root@yly node]# pm2 stop 0 1 2 3 4 [PM2] Applying action stopProcessId on app [0](ids: 0) [PM2] [app](0) ✓ [PM2] Applying action stopProcessId on app [1](ids: 1) [PM2] [socket](1) ✓ [PM2] Applying action stopProcessId on app [2](ids: 2) [PM2] [app](2) ✓ [PM2] Applying action stopProcessId on app [3](ids: 3) [PM2] [conv](3) ✓ [PM2] Applying action stopProcessId on app [4](ids: 4) [PM2] [update](4) ✓ ┌──────────┬────┬─────────┬─────┬─────────┬─────────┬────────┬─────┬────────┬──────┬──────────┐ │ App name │ id │ mode │ pid │ status │ restart │ uptime │ cpu │ mem │ user │ watching │ ├──────────┼────┼─────────┼─────┼─────────┼─────────┼────────┼─────┼────────┼──────┼──────────┤ │ app │ 0 │ cluster │ 0 │ stopped │ 0 │ 0 │ 0% │ 0 B │ root │ disabled │ │ app │ 2 │ cluster │ 0 │ stopped │ 0 │ 0 │ 0% │ 0 B │ root │ disabled │ │ conv │ 3 │ cluster │ 0 │ stopped │ 0 │ 0 │ 0% │ 0 B │ root │ disabled │ │ socket │ 1 │ cluster │ 0 │ stopped │ 0 │ 0 │ 0% │ 0 B │ root │ disabled │ │ update │ 4 │ cluster │ 0 │ stopped │ 0 │ 0 │ 0% │ 0 B │ root │ disabled │ └──────────┴────┴─────────┴─────┴─────────┴─────────┴────────┴─────┴────────┴──────┴──────────┘ Use `pm2 show <id|name>` to get more details about an app [root@yly node]# node app.js 密码 ***** [debug] instanceId------- 0 [info] [DailyStat] register stat space task [debug] oauth20: undefined [debug] ------------------weixin service [info] [app] Listening on port 3000 RedisClient ready
这时就会打印出数据库密码。这样我们就完成了迁移初步的分析,可以制作初始数据库模版。而且由于mysql,redis都是通过IP和端口通讯所以不需要进行太多特殊的配置,所以我们可以进行下一步安装程序的准备工作。