Nginx支持web界面執行bash|python等系統命令和腳本,可以傳遞參數


文章轉載自:https://me.jinchuang.org/archives/114.html ,有修改

步驟總結

1.安裝好nginx,假設其html根路徑為/usr/share/nginx/html
2.准備工作:關閉防火牆,關閉selinux,安裝epel源
3.yum安裝依賴包:dh-autoreconf fcgi fcgi-devel
4.源碼安裝spawn-fcgi和fcgiwrap

(這倆其實也可以直接通過yum方式安裝,不過安裝后沒法通過systemctl的方式啟動fcgiwrap,還是得用腳本文件,啟動的時候會報錯:spawn-fcgi: child exited with: 127,因此還是使用源碼安裝比較穩妥)

5.配置nginx的location路徑
把下載好的linux-shell模板放在nginx的html根路徑下,設置用戶和用戶組為nginx,然后腳本目錄文件增加可執行權限,添加location規則

location規則中配置的root是nginx的html根路徑,並不是實際程序所在路徑,因為執行的時候會在設置root路徑也就是nginx的html根路徑下查找linux-shell/page/script/(.*)$路徑里的文件進行執行。

chown -R nginx:nginx /usr/share/nginx/html/linux-shell
cd /usr/share/nginx/html/linux-shell/page/script/
chmod +x */*
    location ~ ^/linux-shell/page/script/(.*)$ {
        gzip off;
        root /usr/share/nginx/html;
        fastcgi_pass unix:/var/run/fcgiwrap.socket;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root/$fastcgi_script_name;
    }

路徑:/usr/share/nginx/html下有api文件夾,api文件夾下有一個disk的文件

cd `/usr/share/nginx/html/
chown -R nginx:nginx api
chmod a+x api/disk

訪問路徑是:http://localhost/api/disk,就會在設置的root路徑下,也就是/usr/share/nginx/html路徑下找api開頭的文件夾,進而找到disk文件進行執行

    location ~ ^/api/(.*)$ {
        gzip off;
        default_type  text/plain;
        root /usr/share/nginx/html;
        fastcgi_pass unix:/var/run/fcgiwrap.socket;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root/$fastcgi_script_name;
    }

如上倆,模板和參數可以合並起來,linux-shell模板可以參考disk里的文件內容寫法接收傳遞過來的參數

使用說明

1,shell命令 | python命令 | 系統支持的都可以
2,不支持交互式顯示 | 不支持動態內容顯示
3,傻瓜式操作(頁面點擊鏈接一次,執行一次腳本內容)|可以設置頁面自動刷新,實現重復執行腳本

准備工作

##關閉防火牆
##(centos6)
service iptables stop
chkconfig iptables off
##(centos7)
systemctl stop firewalld
systemctl disable firewalld

#關閉selinux
sed -i 's/SELINUX=enforcing/SELINUX=disabled/' /etc/selinux/config
setenforce 0

#已有epel源的跳過此步驟,直接安裝依賴開始
#centos6 添加epel yum源
wget -O /etc/yum.repos.d/epel-6.repo http://mirrors.aliyun.com/repo/epel-6.repo

#centos7 添加epel yum源 
wget -O /etc/yum.repos.d/epel-7.repo http://mirrors.aliyun.com/repo/epel-7.repo

#清除緩存 重新生成緩存
yum clean all
yum makecache

安裝依賴包

#安裝開發包組和相關的依賴包
yum install dh-autoreconf fcgi fcgi-devel -y

安裝spawn-fcgi 和 fcgiwrap

#創建存放包的目錄(包下載到哪個目錄都可以,這里放在/source/目錄下)
mkdir /source/ && cd /source/

#安裝spawn-fcgi
#github下載最新代碼 https://github.com/lighttpd/spawn-fcgi

本地下載:wget https://www.jinchuang.org/novel/lnmp/spawn-fcgi.zip 
最新版:wget https://github.com/lighttpd/spawn-fcgi/archive/refs/tags/spawn-fcgi-1.6.4.zip
解壓:unzip spawn-fcgi-1.6.4.zip
安裝:
cd spawn-fcgi-spawn-fcgi-1.6.4
./autogen.sh
./configure
make && make install

#安裝fcgiwrap
#github下載最新代碼 https://github.com/gnosek/fcgiwrap

本地下載:wget https://www.jinchuang.org/novel/lnmp/fcgiwrap.zip
最新版: wget https://github.com/gnosek/fcgiwrap/archive/refs/tags/1.1.0.zip
解壓: unzip 1.1.0.zip 
安裝:
cd fcgiwrap-1.1.0
autoreconf -i
./configure
make && make install

創建fcgiwrap啟動腳本

【nginx通過轉發請求到這里來執行腳本命令】,腳本啟動用戶要和你nginx啟動用戶一致,注意下腳本中2個命令的路徑是否和你的一致

vim /etc/init.d/fcgiwrap

#! /bin/bash
### BEGIN INIT INFO
# Provides:          fcgiwrap
# Required-Start:    $remote_fs
# Required-Stop:     $remote_fs
# Should-Start:
# Should-Stop:
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: FastCGI wrapper
# Description:       Simple server for running CGI applications over FastCGI
### END INIT INFO

PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
SPAWN_FCGI="/usr/local/bin/spawn-fcgi"
DAEMON="/usr/local/sbin/fcgiwrap"
NAME="fcgiwrap"

PIDFILE="/var/run/$NAME.pid"

FCGI_SOCKET="/var/run/$NAME.socket" # 注意:這里跟原文不一樣
FCGI_USER="nginx"
FCGI_GROUP="nginx"
FORK_NUM=5
SCRIPTNAME=/etc/init.d/$NAME

case "$1" in
    start)
        echo -n "Starting $NAME... "

        PID=`pidof $NAME`
        if [ ! -z "$PID" ]; then
            echo " $NAME already running"
            exit 1
        fi

        $SPAWN_FCGI -u $FCGI_USER -g $FCGI_GROUP -s $FCGI_SOCKET -P $PIDFILE -F $FORK_NUM -f $DAEMON

        if [ "$?" != 0 ]; then
            echo " failed"
            exit 1
        else
            echo " done"
        fi
    ;;

    stop)
        echo -n "Stoping $NAME... "

        PID=`pidof $NAME`
        if [ ! -z "$PID" ]; then
            kill `pidof $NAME`
            if [ "$?" != 0 ]; then
                echo " failed. re-quit"
                exit 1
            else
                rm -f $pid
                echo " done"
            fi
        else
            echo "$NAME is not running."
            exit 1
        fi
    ;;

    status)
        PID=`pidof $NAME`
        if [ ! -z "$PID" ]; then
            echo "$NAME (pid $PID) is running..."
        else
            echo "$NAME is stopped"
            exit 0
        fi
    ;;

    restart)
        $SCRIPTNAME stop
        sleep 1
        $SCRIPTNAME start
    ;;

    *)
        echo "Usage: $SCRIPTNAME {start|stop|restart|status}"
        exit 1
    ;;
esac

啟動fcgiwrap服務

增加可執行權限
chmod +x /etc/init.d/fcgiwrap

#添加到服務里面(centos6系統執行,centos7跳過此步驟)
chkconfig --add fcgiwrap
chkconfig --level 2345 fcgiwrap on

#啟動服務
/etc/init.d/fcgiwrap start
Starting fcgiwrap... spawn-fcgi: child spawned successfully: PID: 22416
spawn-fcgi: child spawned successfully: PID: 22417
spawn-fcgi: child spawned successfully: PID: 22418
spawn-fcgi: child spawned successfully: PID: 22419
spawn-fcgi: child spawned successfully: PID: 22420
done

nginx配置轉發 (系統安裝好nginx)

這一步的含義是下載現成的模板放在一個目錄下,然后nginx中配置location路徑進行訪問

#注意下修改為你的目錄路徑
#location ~ ^/linux-shell/page/script/.*\.(cgi) {  #這里的cgi后綴匹配根據需要修改,后綴自定義即可

# linux-shell 為模板程序目錄,放在nginx網站根目錄下面
location ~ ^/linux-shell/page/script/ {  #我這里調用的文件是沒有后綴的就用這個配置
        gzip off;
        fastcgi_pass  unix:/var/run/fcgiwrap.socket; # 注意:這里跟原文不一樣
       root /usr/share/nginx/html;  # 注意:這里跟原文不一樣,nginx的html目錄
        include fastcgi_params;
        fastcgi_param  SCRIPT_NAME    $document_root/$fastcgi_script_name; # 注意:這里跟原文不一樣
      }

#重啟nginx:
nginx -s reload

模板目錄結構

# 下載模板,放在網站根目錄下
# 目錄結構:
linux-shell
├── css
│   ├── iconfont.css
│   ├── iconfont.eot
│   ├── iconfont.svg
│   ├── iconfont.ttf
│   ├── iconfont.woff
│   ├── iconfont.woff2
│   ├── page.css
│   └── style.css
├── favicon.ico
├── images
│   ├── b.jpg
│   ├── body.cur
│   ├── b.png
│   ├── content.jpg
│   ├── hua.gif
│   ├── link.cur
│   ├── logo.png
│   ├── nav.jpg
│   └── page.cur
├── index.html
├── js
│   ├── jquery-2.1.1.min.js
│   └── nav.js
└── page
    ├── content
    │   ├── index.html
    │   ├── script.js
    │   └── TweenMax.min.js
    ├── h5
    │   ├── 161
    │   │   └── 161.html
    │   ├── 188
    │   │   └── 188.html
    │   └── local
    │       └── local.html
    └── script
        ├── 161
        │   ├── disk
        │   ├── info
        │   ├── mem
        │   ├── ps
        │   ├── server
        │   ├── ssh
        │   └── uptime
        ├── 188
        │   ├── disk
        │   ├── info
        │   ├── mem
        │   ├── ps
        │   ├── server
        │   ├── ssh
        │   └── uptime
        └── local
            ├── disk
            ├── info
            ├── mem
            ├── ps
            ├── server
            ├── ssh
            └── uptime

shell代碼示例文件(查看磁盤使用情況):

#!/bin/bash
echo "Content-Type:text/html;charset=utf-8"
echo "" 

# 自動刷新
#echo "<script>window.setInterval(function(){
#    window.location.reload();
#},1000);</script>"
#echo "<meta http-equiv="refresh" content="60">"

# html頁面css樣式
echo '<style>
body{color:#cecece;}
.title{color: #FF9800;border-left: 4px solid;padding: 4px;}
pre{font-size:14px;border-left: 4px solid #4CAF50;padding: 5px;}
</style>'

# 定義變量
ip="192.168.x.x"

# 內容代碼(命令返回結果放在<pre>標簽中)
echo '<div style="padding-left:10px;">'
echo '<h1 class="title">硬盤使用情況</h1>'
echo '<h5 style="color:#848484;">'
dd=`date`
echo "統計時間: $dd 【當前機器ip: $ip】"
echo '</h5>'
echo '<pre>'
# 查看磁盤使用(本機)
df -hT
# 查看磁盤使用(遠程機器,可以使用ansible|sshpass等遠程工具)
sshpass -p "password" ssh root@$ip -o StrictHostKeyChecking=no  'df -hT'
echo '</pre>'

html模板下載

如果訪問彈出下載,先檢查fcgiwrap服務是否正常,再檢查nginx匹配規則

下載地址:https://files.cnblogs.com/files/sanduzxcvbnm/linux-shell.zip

# 程序html模板使用:(腳本文件要加執行權限,不然會提示403 錯誤)

cd linux-shell/page/script/
chmod +x */*

# 腳本文件說明:
# disk
查看硬盤使用情況

# info
提示信息內容

# mem
內存使用情況

# ps
系統進程概覽

# server
自定義服務進程查看

# ssh
ssh連接用戶情況

# uptime
系統負載cpu和內存使用概覽

傳遞參數

nginx配置

    location ~ ^/api/(.*)$ {
        gzip off;
        default_type  text/plain;
        root /usr/share/nginx/html;
        fastcgi_pass unix:/var/run/fcgiwrap.socket;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root/$fastcgi_script_name;
    }

/usr/share/nginx/html路徑下有api文件夾,api文件夾下有一個disk的文件,其內容如下:

#!/bin/sh
echo "Content-Type:text/html;charset=utf-8"
echo ""

for i in a b c; do
        echo $i
done
echo "$QUERY_STRING" | awk -F '=' '{print $1}'
echo "$QUERY_STRING" | awk -F '=' '{print $2}'

訪問測試,用瀏覽器訪問效果是一樣的

# curl http://localhost/api/disk
a
b
c
# curl http://10.16.16.101/api/disk?abc=123
a
b
c
abc
123

關於使用nginx用戶,不使用root用戶的問題

首先,在寫fcgiwrap啟動腳本文件,也就是/etc/init.d/fcgiwrap的時候里面指定的是nginx用戶,這個用戶是啟動nginx使用的用戶是一致的。

若是該腳本文件中使用root用戶,啟動的時候會報錯:

stderr, "spawn-fcgi: I will not set uid to 0

因為用spawn-fcgi 啟動不能使用 -u root 啟動FastCGI進程。若是想使用root用戶啟動,需要修改源代碼:
在文件src/spawn-fcgi.c中注釋掉一段代碼, 總共有三處需要注釋

  /*                                                                                                                                                       
  if (my_uid == 0) {                                                                                                                                       
          fprintf(stderr, "spawn-fcgi: I will not set uid to 0\n");                                                                                        
          return -1;                                                                                                                                       
  }                                                                                                                                                        
  */

然后再編譯源碼進行安裝

這樣一來spawn-fcgi就行使用root啟動了,修改啟動腳本,把nginx修改成root,刪除使用nginx用戶生成的fcgiwrap.socket文件。

還需要修改nginx配置文件中nginx啟動使用的用戶,也修改為root。

然后重新啟動fcgiwrap和nginx,就能實現使用root用戶了

寫好shell腳本訪問報錯:An error occurred while parsing CGI reply,nginx日志顯示是502 bad gateway

shell腳本開頭內容中加上如下:

echo "Content-Type:text/html;charset=utf-8"
echo ""

關於傳遞參數的進一步分析

在nginx的location配置文件中,比如如下:

    location ~ ^/api/(.*)$ {
        gzip off;
        root /usr/share/nginx/html;
        fastcgi_pass unix:/var/run/fcgiwrap.socket;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root/$fastcgi_script_name;
    }

里面有 include fastcgi_params;, fastcgi_params文件內容如下:

# cat fastcgi_params

fastcgi_param  QUERY_STRING       $query_string;
fastcgi_param  REQUEST_METHOD     $request_method;
fastcgi_param  CONTENT_TYPE       $content_type;
fastcgi_param  CONTENT_LENGTH     $content_length;

fastcgi_param  SCRIPT_NAME        $fastcgi_script_name;
fastcgi_param  REQUEST_URI        $request_uri;
fastcgi_param  DOCUMENT_URI       $document_uri;
fastcgi_param  DOCUMENT_ROOT      $document_root;
fastcgi_param  SERVER_PROTOCOL    $server_protocol;
fastcgi_param  REQUEST_SCHEME     $scheme;
fastcgi_param  HTTPS              $https if_not_empty;

fastcgi_param  GATEWAY_INTERFACE  CGI/1.1;
fastcgi_param  SERVER_SOFTWARE    nginx/$nginx_version;

fastcgi_param  REMOTE_ADDR        $remote_addr;
fastcgi_param  REMOTE_PORT        $remote_port;
fastcgi_param  SERVER_ADDR        $server_addr;
fastcgi_param  SERVER_PORT        $server_port;
fastcgi_param  SERVER_NAME        $server_name;

# PHP only, required if PHP was built with --enable-force-cgi-redirect
fastcgi_param  REDIRECT_STATUS    200;

看到這一行:fastcgi_param QUERY_STRING $query_string;,聯想到接收參數的腳本內容寫法,里面同樣有變量:$QUERY_STRING:

#!/bin/bash

echo "Content-Type:text/html;charset=utf-8"
echo ""

echo "$QUERY_STRING" | awk -F '=' '{print $1}'
echo "$QUERY_STRING" | awk -F '=' '{print $2}'

這就很好理解了, 傳遞的參數是給變量$QUERY_STRING賦值,然后進一步使用該變量進行其他操作。

從安全性上考慮,nginx的location配置文件中沒必要指定引用fastcgi_param文件,可以只配置其中需要使用到的變量參數。比如:

    location ~ ^/api/(.*)$ {
        gzip off;
        root /usr/share/nginx/html;
        fastcgi_pass unix:/var/run/fcgiwrap.socket;
        #include fastcgi_params;
        fastcgi_param QUERY_STRING $query_string;
        fastcgi_param SCRIPT_FILENAME $document_root/$fastcgi_script_name;
    }

此時,就只能傳遞參數使用,而不是獲取其他默認變量的值了。

舉例說明:
nginx的location配置如下:

    location ~ ^/api/(.*)$ {
        gzip off;
        root /usr/share/nginx/html;
        fastcgi_pass unix:/var/run/fcgiwrap.socket;
        include fastcgi_params;
        #fastcgi_param QUERY_STRING $query_string;
        fastcgi_param SCRIPT_FILENAME $document_root/$fastcgi_script_name;
    }

shell腳本內容如下:

#!/bin/bash

echo "Content-Type:text/html;charset=utf-8"
echo ""

for i in a b c; do
        echo $i
done
echo "$QUERY_STRING" | awk -F '=' '{print $1}'
echo "$QUERY_STRING" | awk -F '=' '{print $2}'

echo "$REQUEST_METHOD"

echo "$SCRIPT_NAME"

echo "$DOCUMENT_ROOT"

此時訪問該腳本內容,結果如下:

# curl http://10.16.16.101/api/disk?name=123
a
b
c
name
123
GET
/api/disk
/usr/share/nginx/html

若是修改nginx的location配置如下:

    location ~ ^/api/(.*)$ {
        gzip off;
        root /usr/share/nginx/html;
        fastcgi_pass unix:/var/run/fcgiwrap.socket;
        #include fastcgi_params;
        fastcgi_param QUERY_STRING $query_string;
        fastcgi_param SCRIPT_FILENAME $document_root/$fastcgi_script_name;
    }

shell腳本內容不變,此時訪問該腳本內容,結果如下:

# curl http://10.16.16.101/api/disk?name=123
a
b
c
name
123

可以看到,nginx的location中未被引用的變量則不再輸出變量值

nginx和fcgiwrap都配置啟動了,但是訪問報錯,nginx日志中找不到fcgiwrap.socket文件

解決辦法:如果把該文件放在/tmp路徑下,比如是:/tmp/fcgiwrap.socket,此時就算nginx和fcgiwrap都配置啟動了,訪問的時候查看nginx日志,會提示找不到/tmp/fcgiwrap.socket文件。
給該文件換個路徑就行了,比如上文中說的路徑:/var/run/fcgiwrap.socket


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM