Įkvėptas komentarų: žmonės turi tvirtą ryšį tarp Perl kaip kalbos ir CGI kaip technologijos, naudotos pirmosiomis interneto dienomis.

Tai logiška: tuo metu Perl buvo viena iš nedaugelio plačiai paplitusių scenarijų kalbų ir, žinoma, CGI scenarijus buvo patogiau rašyti Perl, o ne, pavyzdžiui, apvalkalu. Tačiau tai nereiškia, kad vienas buvo prikaltas prie kito.

Apskritai, technologija buvo savaip gera: įdiegėte žiniatinklio serverį (dažniausiai Apache), sukonfigūravote katalogą, iš kurio buvo galima vykdyti scenarijus, ir galėjote paleisti viską, ką norite.

Skirtingai nuo vėlesnių sudėtingų žiniatinklio sistemų, tai tiesiogine prasme paleido programą serveryje, lygiai taip pat, kaip paleistumėte ją per terminalą ar konsolę. Viskas, kas buvo perduota programai per HTTP, buvo gauta per STDIN, o viskas, kas programos išvestis į STDOUT, buvo išsiųsta į kliento naršyklę.

Iš esmės tai buvo įprasta neinteraktyvi konsolinė programa, grynas REST API – serveris visada paleidžia programą nuo nulio, o programa veikia su tuo, kas jai buvo duota.

Neigiamas dalykas buvo tas, kad programos paleidimas visada užtrukdavo, ypač jei ji buvo parašyta scenarijų kalba, kuriai reikalingas vertimas. Vienkartinėms užklausoms tai nebuvo svarbu, tačiau kai programa paleidžiama per 0,5 sekundės ir turite 100 užklausų per sekundę, viskas pradeda šiek tiek lėtėti.

Štai kodėl vėliau buvo pristatytas FastCGI, kur programa buvo „iš anksto paleista“ ir laukė duomenų, o galiausiai perėjome prie integruotų serverių su kelių gijų tinkle.

Dabar parodysiu, kaip tai galima naudoti šiandien:

Kaip jau minėjau ankstesniame straipsnyje, sukūriau „universalios prieigos bloką“: vienos plokštės kompiuterį su maršruto parinkimo tarpiniu serveriu, kuris atskiria avis nuo ožkų, muses nuo kotletų ir ligonius nuo sveikųjų.

Tačiau, kaip ir bet kurią programą, visas šias funkcijas gali reikėti paleisti iš naujo. Negana to, namų internetas iš prigimties yra nestabilus – ant laidų užvirsta medis, ekskavatorius iškasa kažką ne taip ir staiga nustoja krauti kačių nuotraukos. Turite suprasti, kas atsitiko.

Žinoma, visada galite įvesti SSH, įvesti kelias komandas konsolėje ir sužinoti, bet ar galime tai padaryti lengviau? Rašyti atskirą žiniatinklio sistemą, kad būtų galima viską patikrinti, atrodo per daug.

Taigi, užduotis:

  • Fone veikia kelios programos.
  • Yra būdų, kaip patikrinti, ar programa veikia, ar „pakabinta“.
  • Turime sukurti paprastą tinklalapį su dabartine būsena ir galimybe iš naujo paleisti programas.
  • Kadangi tai yra giliai vietiniame tinkle, leidimo nereikia, tačiau niekas neturėtų nieko sulaužyti gremėzdiškomis rankomis.

Šiame serveryje įdiegiame „Apache“ („Nginx“ negali susidoroti su paprastu CGI be šokinėjimo).

apt install apache2

Po įdiegimo viskas veikia „iš dėžutės“, tačiau turime įjungti CGI modulį, kuris pagal numatytuosius nustatymus yra išjungtas:

cd /etc/apache2/mods-enabled
ln -s ../mods-available/cgi.load .
/etc/init.d/apache2 restart

Atlikta. Pagal numatytuosius nustatymus CGI scenarijai turėtų būti /usr/lib/cgi-bin/ir pasiekiama per žiniatinklį kaip /cgi-bin/*.cgi. Žinoma, tai galima pakeisti, ypač jei jus vargina pasenęs „cgi-bin“, bet dabar to nepamatysime. Ir, žinoma, šis katalogas iš pradžių tuščias.

Sukuriame pirmąjį failą, env.cgi:

#!/bin/sh

echo "Content-Type: text/html"
echo ""

echo "<pre>"
env
echo "</pre>"

id
chmod 755 env.cgi

Prie serverio pasiekiame per naršyklę:

http://xx.xx.xx.xx/cgi-bin/env.cgi

Išvestis:

GATEWAY_INTERFACE=CGI/1.1
REMOTE_ADDR=XX.XX.XX.XX
QUERY_STRING=
HTTP_USER_AGENT=Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36
DOCUMENT_ROOT=/var/www/html
REMOTE_PORT=40472
HTTP_UPGRADE_INSECURE_REQUESTS=1
HTTP_ACCEPT=text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
SERVER_SIGNATURE=
Apache/2.4.62 (Debian) Server at 10.1.0.4 Port 80

....

uid=33(www-data) gid=33(www-data) groups=33(www-data)

Jokių sudėtingų programų, naujų ar senų kalbų, struktūrų, nieko – tik paprastas apvalkalo scenarijus.

Čia yra įdomios eilutės QUERY_STRING= ir apatinė eilutė uid=33(www-data) gid=33(www-data) groups=33(www-data).

The QUERY_STRING= yra eilutė, kuri bus įvesta adresu po scenarijaus pavadinimo:

http://XX.XX.XX.XX/cgi-bin/env.cgi?blablabla -> QUERY_STRING=blablabla

Idealiu atveju tai turėtų būti URL užkoduoti GET parametrai, tačiau praktiškai nesvarbu, kokia eilutė yra, jei ji nepažeidžia simbolių konvencijų. Tokiu atveju galime nusiųsti vieną eilutę, nurodant, su kokia programa mūsų scenarijus turėtų veikti.

Apatinė eilutė, rezultatas id komanda rodo, kad žiniatinklio serveris veikia pagal www-data naudotojas.

Norėdami patikrinti internetą ir įvairias tarpinio serverio parinktis, galime naudoti standartą curl:

Tiesioginė užklausa suteiks mums išorinį IP:

curl http://v4v6.ipv6-test.com/api/myip.php --silent

Užklausa per SOCKS5 tarpinį serverį suteiks mums išorinį IP, matomą per tarpinį serverį, ir taip pat patikrins tarpinio serverio funkcionalumą:

curl -x socks5h://127.0.0.1:1080 http://v4v6.ipv6-test.com/api/myip.php --silent

Kiti panašūs.

Galėtume atlikti visas patikras vienu scenarijumi ir grąžinti rezultatus JSON formatu, tačiau kai kurios užklausos gali užtrukti ilgai, o scenarijus jų visų lauktų. Tai nėra labai patogu, todėl scenarijus bus vienas, bet ką jis patikrins, priklausys nuo užklausos parametro.

Taigi, mes gauname scenarijų check_one.cgi:

#!/bin/sh
q=$QUERY_STRING
# remove everything else
unset $(env | cut -d= -f1)

PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin; export PATH

# mandatory header
echo "Content-Type: application/json"
echo ""

# add "x" to the parameter to avoid empty strings
if ( "x$q" = "xxray" ) ; then

    # here we request the server's response, it will give us the IP address
	INETIP=$( curl http://v4v6.ipv6-test.com/api/myip.php --silent )
	PROXYIP=$( curl -x socks5h://127.0.0.1:1080 http://v4v6.ipv6-test.com/api/myip.php --silent )

	echo "{\"extip\":\"${INETIP}\",\"proxyip\":\"${PROXYIP}\"}"

elif ( "x$q" = "xi2p" ) ; then

    X=1
    # here and below - the fact of the response is important, so only headers and error code
	curl -x socks5h://127.0.0.1:4447 http://flibusta.i2p -I --silent -o /dev/null
	if ( $? -ne 0 ) ; then
		X=0
	fi
	echo "{\"i2p\":${X}}";

elif ( "x$q" = "xnodpi" ) ; then

    X=1
	curl -x socks5h://127.0.0.1:1081 https://jnn-pa.googleapis.com -I --silent -o /dev/null
	if ( $? -ne 0 ) ; then
		X=0
	fi
	echo "{\"nodpi\":${X}}";

elif ( "x$q" = "xproxy" ) ; then
	
    X=1
	curl -x socks5h://127.0.0.1:6007 http://v4v6.ipv6-test.com/api/myip.php -I --silent -o /dev/null
	if ( $? -ne 0 ) ; then
		X=0
	fi
	echo "{\"proxy\":${X}}";

else
    # to not forget what options are available
	echo "{\"options\":(\"proxy\",\"xray\",\"i2p\",\"nodpi\")}"
fi

Dabar, nurodę tam tikras parinktis, galime gauti atsakymą, ar konkretus maršrutas veikia, ar ne.

Paslaugas iš naujo paleisti galima taip: Kadangi žiniatinklio serveris veikia pagal www-data vartotojas, jis neturi teisių iš naujo paleisti visko, ko norime, ir mes jai tų teisių nesuteiksime. Vietoj to, mes vykdysime paslaugas specialiai pagal ją ir taip, kad jos pačios paleistųsi iš naujo. Mes kuriame tipinius scenarijus, tokius kaip:

#!/bin/sh
#
exec > /dev/null
exec 2>&1

cd /tmp

while ( 1 ) ; do
  /usr/local/etc/xray/xray -c /usr/local/etc/xray/config.json
done

The xray programa nepaleidžiama fone, taigi, jei procesas užmuštas arba miršta savaime, begalinis scenarijaus ciklas jį paleis iš naujo. Taip apsisaugome nuo netikėtų gedimų, o pakibimo atveju užtenka užmušti pakibimo procesą – tam užtenka teisių.

Tas pats pasakytina ir apie kitus procesus.

Dabar turime pradėti procesus. Tam patogu naudoti /etc/rc.local scenarijus (šiuo atveju jis veikia; jei ne, pridėkite jį kur nors kitur, pvz /etc/init.d):

su www-data -s /bin/sh -c 'setsid /usr/local/bin/start_xray &'

The -s /bin/sh parametras reikalingas, nes www-data vartotojas neturi savo apvalkalo (žr vipw). The -c parametras paleidžia komandą, šiuo atveju setsidkuris paleidžia paleisties scenarijų (šiek tiek perteklinį) kaip demoną.

Kiti panašūs. Iš naujo paleidus kompiuterį, paleidžiami paleidimo scenarijai, kurie paleidžia programas, kurios dabar veikia pagal vartotoją, o joms sugedus ar žuvus, jos automatiškai paleidžiamos iš naujo.

Vienintelis niuansas su i2pd yra tai, kad jis ieško konfigūracijų pagrindiniame kataloge ~/.i2pdo šiam vartotojui tai yra /var/www katalogas (dar kartą žr vipw), todėl nustatymai turėtų būti ten ir turėtų būti rašymo teisės į konfigūracijos katalogą.

Dabar rašome žudiko scenarijų:

#!/bin/sh
q=$QUERY_STRING
# remove everything else
unset $(env | cut -d= -f1)

PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin; export PATH

echo "Content-Type: text/html"
echo ""

pid=

if ( "x$q" = "xxray" ) ; then
	
  pid=$(ps ax| grep -v grep | grep 'xray/xray' | awk '{ print $1 }')

  if ( -n "$pid" ) ; then
	  echo $pid
    kill $pid
    sleep 3
  fi

elif ( "x$q" = "xi2p" ) ; then

  pid=$(ps ax| grep -v grep | grep '/i2pd' | awk '{ print $1 }')

  if ( -n "$pid" ) ; then
	  echo $pid
    kill $pid
    sleep 3
  fi

elif ( "x$q" = "xnodpi" ) ; then

  pid=$(ps ax| grep -v grep | grep '/ciadpi' | awk '{ print $1 }')

  if ( -n "$pid" ) ; then
	  echo $pid
    kill $pid
    sleep 3
  fi

else
	echo "{\"options\":(\"xray\",\"i2p\",\"nodpi\")}"
fi

ps ax pateikia procesų sąrašą, pirmasis grep pašalina grep pati iš išvesties, antra grep ieško norimos programos, awk ištraukia PID, kill užmuša jį ir sleep šiek tiek laukia, kol procesas baigsis.

Apskritai tai beveik viskas. Dabar sukurkime puslapį:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <title>HTML gate</title>

<style type="text/css">
  body, html {
    margin: 0;
    padding: 0;
    font-family: Arial, sans-serif;
    height: 100%;
  }
  .placeholder {
    display:inline-block;
    width:10em;
  }
  .txt_error {
    color:#ff0000;
    font-weight:bold;
  }
  .txt_ok {
    color:#00ff00;
    font-weight:bold;
  }
  .hero {
    height: 100vh;
    background-image: url('bg-image.jpeg');
    background-color: black;
    background-position: center;
    background-repeat: no-repeat;
    background-size:cover;
    color: white;
    box-sizing: border-box;
  }
  #status {
    padding:2em;
  }
  #buttons {
    display:flex;
    align-items: center;
    justify-content: center;
    gap: 5em;
    padding-top: 300px;
  }
  #buttons button, #check {
    padding: 15px 35px;
    font-size: 20px;
    border: solid 3px #ff8b00;
    background: #00003370;
    color: #ffc800;
    border-radius: 10px;
    cursor:pointer;
  }
  .disabled {
    color:#736e5f!important;
    border: solid 3px #736e5f!important;
    cursor:not-allowed!important;
  }

</style>
</head>
<body>

  <div class="hero" id="head">

   <div id="status">
	  <table>
		  <tr>
			  <td>External IP</td>
			  <td><span id="extip" class="placeholder"><span class="loading">...</span></span></td>
		  </tr>
		  <tr>
			  <td>Proxy IP</td>
			  <td><span id="proxyip" class="placeholder"><span class="loading">...</span></span></td>
		  </tr>
		  <tr>
			  <td>I2P network</td>
			  <td><span id="i2p" class="placeholder"><span class="loading">...</span></span></td>
		  </tr>
		  <tr>
			  <td>Proxy</td>
			  <td><span id="proxy" class="placeholder"><span class="loading">...</span></span></td>
		  </tr>
		  <tr>
			  <td>NoDPI</td>
			  <td><span id="nodpi" class="placeholder"><span class="loading">...</span></span></td>
		  </tr>
	  </table>
    <button onclick="check()" id="check">Check</button>
   </div>
   <div id="buttons">
    <button onclick="kill(this,'xray')">Restart Xray</button>
    <button onclick="kill(this,'i2p')">Restart i2p</button>
    <button onclick="kill(this,'nodpi')">Restart NoDPI</button>
   </div>
  </div>
  <script>
    function kill(ctl,id){
      console.log(ctl, id);
      ctl.disabled=true;
      ctl.classList.add('disabled');
      fetch('/cgi-bin/kill_one.cgi?'+id)
        .then(response => {
          if (!response.ok) {
            throw new Error(`HTTP error! status: ${response.status}`);
          }
          return response.json();
        })
        .then(data => {
          window.setTimeout(check,10000);
          ctl.disabled=false;
          ctl.classList.remove('disabled');
          check();
        })
        .catch(error => {
          console.error('Error fetching data:', error);
          ctl.disabled=false;
          ctl.classList.remove('disabled');
        });
    }

    function check(){
      const urls = (
        'xray',
        'i2p',
        'proxy',
        'nodpi'
      );

      const labels = document.querySelectorAll('.placeholder');
      labels.forEach(item => {
        item.innerHTML = '<span class="loading">...</span>';
      });

      urls.forEach(url => {
        fetch('/cgi-bin/check_one.cgi?'+url)
          .then(response => {
            if (!response.ok) {
              throw new Error(`HTTP error! status: ${response.status}`);
            }
            return response.json();
          })
          .then(data => {
            Object.entries(data).forEach(((key, value)) => {
              const element = document.getElementById(key);
              if (element) {
                if(value == 1){
                  element.classList.remove('txt_error');
                  element.classList.add('txt_ok');
                  element.textContent="OK";
                }else if(value == 0){
                  element.classList.add('txt_error');
                  element.classList.remove('txt_ok');
                  element.textContent="ERROR";
                }else{
                  element.classList.remove('txt_error');
                  element.classList.add('txt_ok');
                  element.textContent = value;
                }
              }
            });
          })
          .catch(error => {
            console.error('Error fetching data:', error);
        });
      });
    }

    check();
    </script>
  </body>
</html>

Rezultatas:

tiek. Grynas administratoriaus darbas, apvalkalas ir šiek tiek

Jei tekste radote klaidą, siųskite pranešimą autoriui pažymėdami klaidą ir paspausdami Ctrl-Enter.



Source link

By admin

Draugai: - Marketingo paslaugos - Teisinės konsultacijos - Skaidrių skenavimas - Fotofilmų kūrimas - Karščiausios naujienos - Ultragarsinis tyrimas - Saulius Narbutas - Įvaizdžio kūrimas - Veidoskaita - Nuotekų valymo įrenginiai -  Padelio treniruotės - Pranešimai spaudai -