Repositorium meum feci cum licentia et S3. Die Nginx

In hoc articulo velim experientiam meam communicare cum NJS, interprete JavaScript pro Nginx ab Nginx Inc evoluta, praecipuas eius facultates utendo exemplo reali describens. NJS est pars JavaScript quae sinit te extendere functionem Nginx. Ad quaestionem cur tuus interpres??? Dmitry Volyntsev singillatim respondit. In brevi: NJS est nginx-via, et JavaScript magis progressivus est, "patria" et sine GC dissimilis Lua.

Dudum…

Ad extremum officium meum gitlabum hereditavit cum pluribus canalibus CI/CD pipelines cum compositorio, cenaculo et aliis delitiis, quae ad cancellos kaniko translati sunt. Imagines quae antea in CI adhibitae erant in forma pristina motae sunt. Bene operati sunt usque ad diem quo gitlab IP noster mutatur et CI in cucurbita convertitur. Problema erat unum e phialas imagines, quae in CI participatae conti- git, quae Pythonis moduli per ssh evellebant. Nam ssh debes clavis privata et erat in imagine una cum hospitibus cognitis. Et quodvis CI defecit cum errore clave verificationis propter mis- junctionem inter realem IP et ad notis exercitiis definitum. Nova imago celeriter coacta ex Dockfiles existentium et optio addita est StrictHostKeyChecking no. Sed insulse residuum erat libs privatum PyPI repositorium movere. Additamentum bonus, post PyPI mutans privatam, simplicior fuit fistula et normali descriptione requisitorum

Electio facta est, viri!

Omnia in nubibus et Kubernetes currimus, et tandem parvam servitutem obtinere voluimus, quod erat vas sine repositione incorrupta. Bene, quoniam S3 utimur, prioratus est. Et, si fieri potest, cum authenticas in gitlab (tu, si opus est, addere potes).

Celeris inquisitionis plures eventus cesserunt: s3pypi, pypicloud et optio cum "manual" creationis HTML modorum pro rapa. Ultima optio per se evanuit.

s3pypi: Hoc cft ad usura S3 obnoxius. Fasciculos fasciculos imposuisti, html generare et eidem situlam imposuisti. Apta domus usus.

pypicloud: Visum est an interesting propositi, sed lectis documentis me fefellit. Quamvis bona documenta et facultas augendi ad usus tuos necessarios, re vera evenit ut superuacua ac difficilis configuraretur. Corrigendo codicem ad operas tuas aptandas, secundum temporis aestimationem, 3-5 dies adhibuisset. Ministerium database etiam eget. Dimisimus si aliud non invenimus.

Intensior quaesitio cessit modulum pro Nginx, ngx_aws_auth. Effectus probationis eius XML in navigatro monstratus est, qui contenta situlae S3 ostendit. Ultimum committere tempore inquisitionis erat ante annum. Repositorium vidi relicta.

Per ad fontem et ad legendi LABOR-I Intellexi XML converti posse ad HTML in muscam et ad pituitam dari. Post pauca plura de Nginx et S3, in S3 in JS pro Nginx scriptum inveni exemplum authenticitatis. Ita conveni NJS.

Hoc exemplo sumens fundamentum, post horam vidi in navigatro meo eundem XML ac cum ngx_aws_auth moduli utens, sed iam omnia in JS.

Ego vere probaverunt solutionem nginx. Primo, documenta bona et multa exempla, secundo omnia bona Nginx pro lima operando (ex arca), tertio qui scit quomodo scribere pro Nginx configs scribere poterit quid sit. Minimalismus etiam plus mihi est, cum Pythone vel Go (si a scabro scriptum), nexus ne dicam.

TL;DR Post 2 dies, test versio PyPi iam adhibita est in CI.

Quid opus est?

Modulus in Nginx oneratur ngx_http_js_moduleinclusa in officiali dotalicii imagine. Nos importare nostrum scriptum directivum js_importut Nginx configuratione. Munus directivum dicitur js_content. Directiva adhibetur ut variabilis js_setquae pro argumento obtinet tantum munus in scripto descriptum. Sed subqueries exequi possumus in NJS tantum utendo Nginx, non aliquo XMLHttpRequest. Ad hoc faciendum, addenda est figurae Nginx locus respondentis. Et necesse est scripturam huic loco requisitam describere. Ut functioni ab Nginx config accedere possit, munus nomen in ipso scripto exportandum est export default.

nginx.conf

load_module modules/ngx_http_js_module.so;
http {
  js_import   imported_name  from script.js;

server {
  listen 8080;
  ...
  location = /sub-query {
    internal;

    proxy_pass http://upstream;
  }

  location / {
    js_content imported_name.request;
  }
}

script.js

function request(r) {
  function call_back(resp) {
    // handler's code
    r.return(resp.status, resp.responseBody);
  }

  r.subrequest('/sub-query', { method: r.method }, call_back);
}

export default {request}

Cum rogatus in pasco http://localhost:8080/ nos in tempore location /in quo directivum js_content vocat munus request descriptus est in nostro scripto script.js. Rursus in munere request subquery factum est location = /sub-query, methodo (in exemplo currenti GET) ex argumento impetrato (r)hoc munus implicite nuncupatur. Responsio subrequata in functione discursum erit call_back.

conatur S3

Rogationem ut privatam S3 repono, opus est:

ACCESS_KEY

SECRET_KEY

S3_BUCKET

Ex usu http methodo, moderno/time, S3_NAME et URI, generatur quaedam chordae species, quae signatur (HMAC_SHA1) utens SECRET_KEY. Proxima est linea sicut AWS $ACCESS_KEY:$HASHadhiberi potest in auctoritate header. Eundem diem/tempus quo filum in praecedenti gradu generare solebat, ad caput addendum est X-amz-date. In codice hoc sic spectat:

nginx.conf

load_module modules/ngx_http_js_module.so;
http {
  js_import   s3      from     s3.js;

  js_set      $s3_datetime     s3.date_now;
  js_set      $s3_auth         s3.s3_sign;

server {
  listen 8080;
  ...
  location ~* /s3-query/(?<s3_path>.*) {
    internal;

    proxy_set_header    X-amz-date     $s3_datetime;
    proxy_set_header    Authorization  $s3_auth;

    proxy_pass          $s3_endpoint/$s3_path;
  }

  location ~ "^/(?<prefix>[w-]*)[/]?(?<postfix>[w-.]*)$" {
    js_content s3.request;
  }
}

s3.js(AWS Sign v2 auctoritate exempli, status in deprecationem mutati)

var crypt = require('crypto');

var s3_bucket = process.env.S3_BUCKET;
var s3_access_key = process.env.S3_ACCESS_KEY;
var s3_secret_key = process.env.S3_SECRET_KEY;
var _datetime = new Date().toISOString().replace(/[:-]|.d{3}/g, '');

function date_now() {
  return _datetime
}

function s3_sign(r) {
  var s2s = r.method + 'nnnn';

  s2s += `x-amz-date:${date_now()}n`;
  s2s += '/' + s3_bucket;
  s2s += r.uri.endsWith('/') ? '/' : r.variables.s3_path;

  return `AWS ${s3_access_key}:${crypt.createHmac('sha1', s3_secret_key).update(s2s).digest('base64')}`;
}

function request(r) {
  var v = r.variables;

  function call_back(resp) {
    r.return(resp.status, resp.responseBody);
  }

  var _subrequest_uri = r.uri;
  if (r.uri === '/') {
    // root
    _subrequest_uri = '/?delimiter=/';

  } else if (v.prefix !== '' && v.postfix === '') {
    // directory
    var slash = v.prefix.endsWith('/') ? '' : '/';
    _subrequest_uri = '/?prefix=' + v.prefix + slash;
  }

  r.subrequest(`/s3-query${_subrequest_uri}`, { method: r.method }, call_back);
}

export default {request, s3_sign, date_now}

A paulo circa explicandum _subrequest_urihaec variabilis est quae, initiali uri fretus, petitionem efformat ad S3. Si contenta "radix" obtinere debes, tum petitionem uri creare debes demonstrans delimiter delimiterquae indicem omnium elementorum Communium xml reddet, directoriis respondentium (in casu PyPI, indicem omnium fasciculorum). Si elenchum contentorum in certa presul (indicem omnium versionum sarcinarum accipere debes), postulatio uri debet praefixum campum continere cum nomine indicem (sarcina) necessario desinentem cum scapulae /. Alioquin collisiones fieri possunt cum contenta directorii petentes, exempli gratia. Sunt directoria aiohttp-petitionem et aiohttp-petitionem et si petitio specificat /?prefix=aiohttp-requesttunc responsio contenta utriusque directorii continebit. Si VULNUS in fine, /?prefix=aiohttp-request/tunc responsio tantum directorium requisitum continebit. Et si limam petimus, tunc uri non differunt ab originali.

Salvum ac sileo Nginx. In navigatro inscriptionis nostrae Nginx ingredimur, effectus rogationis XML erit, exempli gratia:

Index directoriis

<?xml version="1.0" encoding="UTF-8"?>
<ListBucketResult xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
  <Name>myback-space</Name>
  <Prefix></Prefix>
  <Marker></Marker>
  <MaxKeys>10000</MaxKeys>
  <Delimiter>/</Delimiter>
  <IsTruncated>false</IsTruncated>
  <CommonPrefixes>
    <Prefix>new/</Prefix>
  </CommonPrefixes>
  <CommonPrefixes>
    <Prefix>old/</Prefix>
  </CommonPrefixes>
</ListBucketResult>

E numero directorium elementorum tantum indigebis CommonPrefixes.

Addendo indicem ad inscriptionis nostrae in navigatro opus est, etiam contenta in XML forma recipiemus;

Index files in Directory

<?xml version="1.0" encoding="UTF-8"?>
<ListBucketResult xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
  <Name> myback-space</Name>
  <Prefix>old/</Prefix>
  <Marker></Marker>
  <MaxKeys>10000</MaxKeys>
  <Delimiter></Delimiter>
  <IsTruncated>false</IsTruncated>
  <Contents>
    <Key>old/giphy.mp4</Key>
    <LastModified>2020-08-21T20:27:46.000Z</LastModified>
    <ETag>&#34;00000000000000000000000000000000-1&#34;</ETag>
    <Size>1350084</Size>
    <Owner>
      <ID>02d6176db174dc93cb1b899f7c6078f08654445fe8cf1b6ce98d8855f66bdbf4</ID>
      <DisplayName></DisplayName>
    </Owner>
    <StorageClass>STANDARD</StorageClass>
  </Contents>
  <Contents>
    <Key>old/hsd-k8s.jpg</Key>
    <LastModified>2020-08-31T16:40:01.000Z</LastModified>
    <ETag>&#34;b2d76df4aeb4493c5456366748218093&#34;</ETag>
    <Size>93183</Size>
    <Owner>
      <ID>02d6176db174dc93cb1b899f7c6078f08654445fe8cf1b6ce98d8855f66bdbf4</ID>
      <DisplayName></DisplayName>
    </Owner>
    <StorageClass>STANDARD</StorageClass>
  </Contents>
</ListBucketResult>

Ex catalogo imaginum solum elementa tollemus Key.

Reliquum est ut XML parse consequens et ut HTML emittat, prius reposito contenti Type caput cum text/html.

function request(r) {
  var v = r.variables;

  function call_back(resp) {
    var body = resp.responseBody;

    if (r.method !== 'PUT' && resp.status < 400 && v.postfix === '') {
      r.headersOut['Content-Type'] = "text/html; charset=utf-8";
      body = toHTML(body);
    }

    r.return(resp.status, body);
  }
  
  var _subrequest_uri = r.uri;
  ...
}

function toHTML(xml_str) {
  var keysMap = {
    'CommonPrefixes': 'Prefix',
    'Contents': 'Key',
  };

  var pattern = `<k>(?<v>.*?)</k>`;
  var out = [];

  for(var group_key in keysMap) {
    var reS;
    var reGroup = new RegExp(pattern.replace(/k/g, group_key), 'g');

    while(reS = reGroup.exec(xml_str)) {
      var data = new RegExp(pattern.replace(/k/g, keysMap[group_key]), 'g');
      var reValue = data.exec(reS);
      var a_text = '';

      if (group_key === 'CommonPrefixes') {
        a_text = reValue.groups.v.replace(///g, '');
      } else {
        a_text = reValue.groups.v.split('/').slice(-1);
      }

      out.push(`<a href="/la/${reValue.groups.v}">${a_text}</a>`);
    }
  }

  return '<html><body>n' + out.join('</br>n') + 'n</html></body>'
}

conatur PyPI

Reprehendimus nihil usquam in fasciculis, qui labori noti sunt, frangere.

# Π‘ΠΎΠ·Π΄Π°Π΅ΠΌ для тСстов Π½ΠΎΠ²ΠΎΠ΅ ΠΎΠΊΡ€ΡƒΠΆΠ΅Π½ΠΈΠ΅
python3 -m venv venv
. ./venv/bin/activate

# Π‘ΠΊΠ°Ρ‡ΠΈΠ²Π°Π΅ΠΌ Ρ€Π°Π±ΠΎΡ‡ΠΈΠ΅ ΠΏΠ°ΠΊΠ΅Ρ‚Ρ‹.
pip download aiohttp

# Π—Π°Π³Ρ€ΡƒΠΆΠ°Π΅ΠΌ Π² ΠΏΡ€ΠΈΠ²Π°Ρ‚Π½ΡƒΡŽ Ρ€Π΅ΠΏΡƒ
for wheel in *.whl; do curl -T $wheel http://localhost:8080/${wheel%%-*}/$wheel; done

rm -f *.whl

# УстанавливаСм ΠΈΠ· ΠΏΡ€ΠΈΠ²Π°Ρ‚Π½ΠΎΠΉ Ρ€Π΅ΠΏΡ‹
pip install aiohttp -i http://localhost:8080

Itera cum nostris libs.

# Π‘ΠΎΠ·Π΄Π°Π΅ΠΌ для тСстов Π½ΠΎΠ²ΠΎΠ΅ ΠΎΠΊΡ€ΡƒΠΆΠ΅Π½ΠΈΠ΅
python3 -m venv venv
. ./venv/bin/activate

pip install setuptools wheel
python setup.py bdist_wheel
for wheel in dist/*.whl; do curl -T $wheel http://localhost:8080/${wheel%%-*}/$wheel; done

pip install our_pkg --extra-index-url http://localhost:8080

In CI, creando et onerando sarcinam similis est:

pip install setuptools wheel
python setup.py bdist_wheel

curl -sSfT dist/*.whl -u "gitlab-ci-token:${CI_JOB_TOKEN}" "https://pypi.our-domain.com/${CI_PROJECT_NAME}"

authenticas

In Gitlab potest uti JWT ad authenticas/auctoritatem servitiorum exteriorum. Utentes directivam in Nginx auth_requestionem, authenticationem datam ad interrogationem redigamus quae functionem vocationis in scripto continet. Scriptum aliam interrogationem Gitlab url faciet et si authenticas notitia recte designata est, tunc Gitlab codicem 200 reddet et fasciculi onerationis/download licebit. Cur non una subquisitione utimur et statim notitias ad Gitlabum mittimus? Quia tunc debebimus file configurationem Nginx recensere omni tempore, quascumque mutationes in concessione facimus, et hoc opus magis taedium est. Etiam, si Kubernetes legit solum consiliorum filesystem radicem, hoc etiam magis implicationem addit, cum reponat nginx.conf via configmap. Et omnino impossibile fit Nginx per configmap configurare, dum eodem tempore utens consilia prohibens connexionem voluminum (pvc) et solum radicum filesystem (hoc quoque fit).

Intermediis NJS adhibitis, occasionem nactus est ad determinatos ambitus in nginx config utendo variabilium ambituum mutandi, et nonnullas in scriptione compescendo (exempli gratia, URL non recte definitum).

nginx.conf

location = /auth-provider {
  internal;

  proxy_pass $auth_url;
}

location = /auth {
  internal;

  proxy_set_header Content-Length "";
  proxy_pass_request_body off;
  js_content auth.auth;
}

location ~ "^/(?<prefix>[w-]*)[/]?(?<postfix>[w-.]*)$" {
  auth_request /auth;

  js_content s3.request;
}

s3.js

var env = process.env;
var env_bool = new RegExp(/[Tt]rue|[Yy]es|[Oo]n|[TtYy]|1/);
var auth_disabled  = env_bool.test(env.DISABLE_AUTH);
var gitlab_url = env.AUTH_URL;

function url() {
  return `${gitlab_url}/jwt/auth?service=container_registry`
}

function auth(r) {
  if (auth_disabled) {
    r.return(202, '{"auth": "disabled"}');
    return null
  }

  r.subrequest('/auth-provider',
                {method: 'GET', body: ''},
                function(res) {
                  r.return(res.status, "");
                });
}

export default {auth, url}

Verisimile quaestio est medicandi: Cur non uteris paratis modulis? Omnia iam facta sunt! Exempli gratia, var AWS = require ('aws-sdk') et non opus est "bike" scribere cum S3 authenticas!

Transeamus ad cons

Mihi, non posse importare modulorum JS externorum, factus est ingratus, sed expectatus pluma. Descriptus est in exemplo supra require ('crypto') is constructum-in modulorum et opus est eis. Nullo etiam modo est codicem ex scriptorum reuse et in diversis fasciculis imitari et conglutinare habes. Spero fore ut aliquando haec officiatio impleatur.

Compressio etiam debilitari debet pro re praesenti in Nginx . gzip off;

Quia modulus gzip in NJS non est et eam connectere impossibile est, ergo in notitia compressa nullo modo laborandum est. Verum hoc non minus pro hac re est. Textus non multum est, et files translati iam compressi et compressiones additae eas multum adiuvabunt. Etiam, hoc non est tam onustum vel criticum servitium quod tibi molestum est cum paucis milliseconds citius tradendis contentus est.

Debugging scriptor longum tempus accipit et per "vestigia" in error.log tantum fieri potest. Secundum in statuto gradu logging info, moneo vel errore, uti potest 3 modos r.log, r.warn, r.error respective. Conor debug scripta quaedam in Chrome (v8) vel njs instrumentum consolatorium, sed non omnia ibi inhiberi possunt. Cum debugging codice, alias probationis functionis, historia aliquid simile hoc spectat:

docker-compose restart nginx
curl localhost:8080/
docker-compose logs --tail 10 nginx

Et possunt esse centies talium sequentium.

Scribens codicem utens subqueris et indeterminatibus illis in implexa vertitur. Interdum IDE fenestras diversas circumcurrere incipis, ordinem actionum codicis tui instare conatur. Non est difficile, sed interdum valde molestum est.

Nullum ES6 plenum subsidium.

Sunt alia quaedam vitia, sed nihil aliud non offendit. Partis notitias si experientiam negativam habes utens NJS.

conclusio,

NJS interpres fons apertus leve est, qui te permittit variis JavaScript scriptis in Nginx deducendi. In eius evolutione, magna agendi ratio est habenda. Scilicet, adhuc multum defuit, sed consilium augetur per parvas turmas et actuose addit novas lineas et cimices figendi. Spero fore ut aliquando NJS permittet te modulos externos coniungere, qui Nginx functionem paene infinitam efficies. Sed est NGINX Plus et verisimile nulla lineamenta erunt!

Repositorium cum pleno codice ad articulum

njs-pypi cum AWS Sign v4 firmamentum

Descriptio normarum moduli moduli ngx_http_js_moduli

NJS repositio officialis ΠΈ Litterarum

Exempla usus NJS ex Dmitry Volintsev

njs - native JavaScript scripting in nginx / Oratio a Dmitry Volnyev apud Sanctum HighLoad ++ 2019

NJS in productione / Oratio per Vasily Soshnikov ad HighLoad++ 2019

Signing and authenticating Reliqua Requests in AWS

Source: www.habr.com