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和端口通讯所以不需要进行太多特殊的配置,所以我们可以进行下一步安装程序的准备工作。