Minulla on parikin sivusto WordPressillä, jotka ovat vahvasti asiapohjaisia. Ja yksi blogimaisempi. Toki ne löytyvät, enemmän tai vähemmän, perinteisilläkin hakukoneilla, mutta joskus olisi tarve saada ne osumaan SearXNG-hakuihin suorempaa ja ilman riippuvuutta muiden indeksoinneista. Se onnistuu. Ei täydellisestä, mutta riittävästi ja mikä tärkeintä, niin helposti.
Minulla ei ole hajuakaan miten engine koodataan. Onneksi ei tarvitsekaan, koska SearXNG tarjoaa valmiina hyödynnettävän moottorin: JSON engine.
WordPressillä on omat REST API:n endpointinsa polussa /wp-json/wp/v2/
ja niitä pääsee hyödyntämään. WordPressin API ei ehkä ole laajin mahdollinen, mutta se on riittävän monipuolinen. Sen verran monipuolinen, että se voidaan tulkita turvallisuusongelmaksi, ainakin sivuston rekisteröityneiden käyttäjien suhteen.
Vaikka puoltajat perustelevatkin kantaansa sillä, että se ei paljasta muuta kuin sen mikä olisi muutoinkin julkista tietoa, niin ehkä ei kuitenkaan ole tarkoituksenmukaista tarjota boteille helpointa mahdollista reittiä saada koottu ja konelukemiseen optimoitu lista aivan kaikesta mitä sivustolta löytyy. Mukaanluettuna artikkelit, kommentit ja käyttäjät. Joten normaali käytäntö on laittaa /wp-json/
kirjautumispakon taakse.
SearXNG-instanssin suhteen hyödynnetään API:a ja esitetään hakutuloksissa artikkelikuva, otsikko ja lyhennelmä. Haku itsessään on sama kuin WordPressin oma, joten se palauttaa osumat myös sisällöstä — vakiona joko posteista, sivuista tai molemmista. Sen sijaan se ei löydä custom-postauksia, muita kenttiä tai kommentteja.
Huomaa:
Nämä ohjeet on tehty tilanteeseen, jossa on backend, eikä vain yksi web-serveri. Minulla rakenne on Nginx – Varnish – Apache2. Minä en tiedä miten pitäisi rakentaa tavanomainen yhden serverin ratkaisu.
Kunhan sanon, että Varnish kannattaisi silloinkin asentaa. Se on se tekijä, joka aidosti nopeuttaa WordPressiä. Ei Valkey/Redis tai edes sivu-cachet malliin WP Rocket. Kyllä, sivu-cachet nopeuttavat myös, mutta ne ovat erittäin rajoittuneita sekä todella herkkiä menemään rikki.
JSON engine WordPressille
Jos haluaa nähdä miten ja mitä WordPress palauttaa REST API:lla, kun kysellään vastaavia oleellisia asioita kuin enginekin tekisi, niin sitä voi kokeilla. Esimerkiksi näin:
❯ Näytä koodi
curl -s "https://www.eksis.one/wp-json/wp/v2/posts?search=searxng&per_page=1&_fields=id,link,title.rendered,excerpt.rendered" | jq
Muuttamalla per_page
arvoa saa enemmän osumia.
Ilman jq
:ta saa tylsemmän json-vastauksen, mutta tämä on ihmisystävällisempi.
❯ Näytä koodi
[
{
"title": {
"rendered": "SearXNG ja ValkeyDB:n asennus"
},
"excerpt": {
"rendered": "<p>SearXNG vaihtoi Redisistä Valkeyhin. Nuo ovat periaattessa samaa, ja samantyyppisestä asiasta on kyse kuin mikä on tilanne MySQL:n ja MariaDB:n kanssa. Valkey taitaa olla Redisin forkki. Valkey ymmärtääkseni syntyi lisenssiriidoista, kun Redis muutti omaansa sellaiseksi, että se ei nää mahtunut open source filosofiaan. Kyllä, käytin lauseessa aika paljon epävarmuuksia, koska en edes väitä ymmärtäväni […]</p>\n"
}
}
]
Enginen asetus SearXNG:een
Käyttöönotto SearXNG:ssä on siedettävän yksinkertaista. Rakennetaan API-pyyntö ja laitetaan json engineen muutkin asiat mitä dokumentointi haluaa. Se siitä.
Mahdollisia ratkaisuja on varmasti useita, mutta minä tein näin.
❯ Näytä koodi
- name: eksis.one
engine: json_engine
shortcut: eksis
categories: [general, it]
paging: true
first_page_num: 1
# pelkkä posts, tai pelkkä pages; molemmat posts|pages
search_url: https://www.eksis.one/wp-json/wp/v2/posts?_embed=1&search={query}&per_page=10&page={pageno}
results_query: ''
url_query: link
title_query: title/rendered
content_query: excerpt/rendered
title_html_to_text: true
content_html_to_text: true
thumbnail_query: _embedded/wp:featuredmedia/0/media_details/sizes/thumbnail/source_url
Kuten aina, niin tarvitaan docker compose down && docker compose up -d
.
Tuossa ei ole ratkaistu mahdollisia rajoituksia sen suhteen kuka saa API:a kysellä. Koska minä hoidan sen Varnishissa, niin siellä se hoituu esimerkiksi acl:ssä olevalla searxng-containerin julkisella IP:llä.
Minä selvitin sallittavan IP:n tylsällä maalaistekniikalla. Tein haun käyttämällä shortcutia malliin !bang ja vilkaisin sitten logeista kuka juuri sai error 403. Mutta riippuen miten yhteydet muodostetaan, niin se on joko serverin IP, 172.20.0.3, 172.18.02 tai jotain sen kaltaista riippuen miten docker/container on säädetty.
Relevanssi ja REST API salliminen
SearXNG:n json engine hakee käyttäen WordPressin omaa search-toimintoa. Jos on ottanut käyttöön lisäosan Relevanssi, kuten jokainen hakutoimintaa kunnioittava lienee tehnyt, niin se täytyy erikseen sallia API-polulle. WordPress lähtökohtaisesti estää REST API:n plugineilta ja se täytyy erikseen sallia. Toisin sanoen sivusto tarjoaa Relevanssin huolehtimat hakutulokset, mutta API:n kautta SearXNG antaisi WordPressin vaatimattoman vakiohaun.
Relevanssin saa toimimaan REST API pyynnöissä, jolloin SearXNG:n hakutulokset ovat samanlaiset kuin sivustollakin, ja esimerkiksi stop-sanojen etsiminen ei palauttaisi mitään. Tarvitset siihen mu-pluginin — voi sen tavallisenakin asentaa, mutta must use käynnistyy varmasti riittävän ajoissa.
nano wp-content/mu-plugins/relevanssi-rest.php
❯ Näytä koodi
<?php
/**
* Relevanssi käyttöön kaikissa REST-kokoelmareiteissä (posts, pages, CPT:t)
* sekä /wp/v2/search -reitissä.
*/
if ( ! defined( 'ABSPATH' ) ) exit;
add_action( 'init', function () {
remove_filter( 'rest_api_init', 'relevanssi_rest_api_disable' );
}, 11 );
add_filter( 'rest_post_search_query', function( $args, WP_REST_Request $request ) {
if ( $request->get_param( 'search' ) ) {
$args['relevanssi'] = true;
if ( empty( $request->get_param( 'orderby' ) ) ) {
$args['orderby'] = 'relevance';
}
}
return $args;
}, 10, 2 );
add_action( 'init', function() {
$types = get_post_types( [
'public' => true,
'show_in_rest' => true,
], 'names' );
foreach ( $types as $type ) {
add_filter( "rest_{$type}_query", function( $args, WP_REST_Request $request ) {
if ( $request->get_param( 'search' ) ) {
$args['relevanssi'] = true;
if ( empty( $request->get_param( 'orderby' ) ) ) {
$args['orderby'] = 'relevance';
}
}
return $args;
}, 10, 2 );
}
}, 12 );
Nyt Relevanssia käytetään hakuihin aina REST API:ssa, joten tämä ei ole pelkästään SearXNG-riippuvainen asia. Tuo mahdollistaa myös custom post typet, jos ne on sallittu Relevanssissa.
Siinä on kuitenkin koukku. Relevanssi kykenee esittämään custom post typet WordPressin sisäisessä haussa yhdessä tavallisten artikkelien ja sivujen kanssa, mutta ei REST API:ssa. Ne täytyy kysellä erikseen omasta polustaan, ja se polku riippuu custom post typestä.
Tällä komennolla saat selattua mitä polkuja WP:n REST API julkaisee maailmalle:
❯ Näytä koodi
curl -s https://www.eksis.one/wp-json | jq '.routes | keys[]' | grep "/wp/v2/"
Jos käytät WP CLI:tä, niin tarjolla on miellyttävämpi esitystapa:
❯ Näytä koodi
wp post-type list --fields=name,public,show_in_rest
Koska API-pyyntö tehdään vain yhdelle tyypille, niin joudut tekemään useamman enginen samalle hostille, jos käytössä on useampi kuin yksi tyyppi.
Tämä hakee posteista:
❯ Näytä koodi
http://host.docker.internal:9000/wp-json/wp/v2/posts?site=www.eksis.one&_embed=1&search={query}&per_page=10&page={pageno}&orderby=relevance
Ja tämä hakee Knowledge Basen slugista:
❯ Näytä koodi
http://host.docker.internal:9000/wp-json/wp/v2/knowledgebase?site=www.eksis.one&_embed=1&search={query}&per_page=10&page={pageno}&orderby=relevance
- domain on tuon näköinen siksi, että minulla haut kulkevat serverin sisäverkossa dockerin containerista loppujen lopuksi Apachelle.
Usemman enginen käyttöönotto ei ole ongelma hakutulosten suhteen. SearXNG kuitenkin yhdistää ne. Jos haluat painottaa jompaa kumpaa, niin sehän onnistuu weight
asetuksella. Laita toiselle esimerkiksi weight: 1.5
niin saat sille enemmän painoarvoa.
Hakukyselyt serverin sisäisenä yhdelle hostille
Minulla liikenne tulee Nginxiin, menee siitä reverse proxynä hääräävälle Varnishille, joka juttelee tarpeen vaatiessa backendinä olevan Apache2 kanssa, joka pitää huolen WordPresseistä. Lisäksi Nginx keskustelee SearXNG-instanssin kanssa.
Joten Nginx on niin sanotusti portivartijana sisäverkon tapaiselle. Kukaan Nginxin takana oleva ei näe ulkomaailmaa. Joten tuntui hölmöltä kierrättää SearXNG:n tekemä API-kysely Nginxin kautta ikäänkuin ulos 443 portista, käydä selvittämässä DNS, ja palata heti samantien takaisin 443 portista ja proxytettuna Varnishin läpi Apachelle ja WordPresseille. Ja niiden rakentama JSON-kysely palasi samaa tietä SearXNG:lle ja uudestaan Nginxin kautta kyselijän selaimelle.
Noinhan se liikenne menee.
Joten halusin, että SearXNG juttelee suoraan Apachelle ja WordPresseille hostin sisällä. Silloin ei tarvitsisi vahtia API:n käyttölupaakaan.
Minulla meni noin tunti, että sain alussa esitetyn perustasoisen JSON-jutun tehtyä enginellä ja WordPressiltä vastauksen kyselijän selaimella. Olin varma, että ei tuo sen kummallisemmaksi muutu, kun pelataan julkisten IP-osoitteiden sijaan 127.0.0.1 maailmassa. Hiukan se muuttui… minulla meni 10 tuntia sen virittämiseen.
Muistutan edelleen, että en ihan ymmärrä miten docker ja containerit toimivat. Siksi kuvittelin, että kun käsken searxng:n ja enginen jutella suuntaan 127.0.0.1:8282 jossa minulla Apache2 kuuntelee, niin kaikki on hyvin.
Ei ollut hyvin, vaan sain pelkästään timeoutin. Jonkun ajan kuluttua minulle selvisi, että containerissa 127.0.0.1 tarkoittaa containerin localhostia. Se taasen on täysin erillään hostin localhostista.
Yritin siis saada containerin 127.0.0.1:8080 juttelemaan hostin 127.0.0.1:8282 kanssa tilanteessa, jossa kumpikaan 127.0.0.1 ei ole sama asia eivätkä näe toisiaan.
Oma SearXNG-instanssi haku.eksis.eu
sai urakoida hakuja minulle kunnolla — ja nyt sain aika vahvankin kuvan siitä kuinka paljon parempia, tai käyttökelpoisempia, tuloksia sain sillä kuin Googlella. Ja paremmuudella tarkoitan sitä, että sain keskittyä etsimään tietoa paremmin kohdennetuista osumista ja hommaa eivät sotkeneet täysin asiaan liittymättömät linkkiehdotukset, pielessä olevat AI-koosteet (niitä saa muuta kautta tehokkaammin) sekä mainokset.
Luulin jossain vaiheessa hieman ymmärtäneeni mistä on kyse, mutta… ei, en ymmärrä. Siellä häärää välissä joku bridge, joka toimii sananmukaisesti siltana containerin privaattimaailman ja hostin välissä. Se tekee niin containerille kuin searxng:lle (ne eivät ilmeisesti olekaan sama asia, kuten olen luullut, tai sitten ovat) sellaiset IP-osoitteet, jotka näkyvät hostin puolelle.
Ja koska container on oma maailmansa, niin se ei pysty juttelemaan suoraan hostin 127.0.0.1 maailman kanssa, kuten ei pysty aitokaan virtuaalimaailma — siihen tarvitaan proxy väliin: Nginx. Se saattaisi onnistua ilman Nginxiä sillä, että Apachella olisi listen
myös siihen IP-osoitteeseen, jota SearXNG ja sen container käyttää hostin suuntaan. Mutta tuota en kokeillut.
Joten se, että SearXNG tekisi API-kyselynsä suoraan localhostia kuuntelevalla Apache2:lle ei onnistu (paitsi jos se containerin hostin puoleisen IP:n kuuntelu toimii Apachessa). Tarvitaan Nginx väliin tulkkaamaan IP:t, eli tekemään proxyn. Mutta koska silti operoidaan koko ajan sisäverkossa, niin saan ohitettua API-suojani ja siten eräällä tavalla kuitenkin juttelu Apachelle suoraan onnistuu. Vaikka en juttelekaan suoraan.
Ja tein kaiken vain siksi, että saan ohitettua maailmassa käynnin ja pysymään ikäänkuin samojen seinien sisällä.
Tämä on minusta aika… jopa jossain määrin skitsofreenista. Jos minulta nyt kysytäään, että oliko tässä mitään järkeä, niin vastaus on, että ei ollut.
Tässä on kaksi asiaa vastakkain:
- tehdä normaali https-pyyntö hostille ja jotenkin ratkaista REST API:n käyttöoikeus, jos sellaista käyttää; tämä onnistuu saman serverin useammankin hostin kanssa
- laittaa engineä käyttävään vhostiin containerin IP:tä esim. 172.18.0.1:9000 kuunteleva serveriblokki, joka sitten tekee
proxy_pass
Apachelle; tämä onnistuu pakasta vedettynä vain yhdellä hostilla
Kyse on oikeastaan vain siitä kuinka työlästä REST API:lle pääsy on. Jos käyttöoikeudet ovat hankalia ratkaista, niin sen saa hoidettua ohittamalla moiset pysymällä hostin sisällä.
Sitten on teoreettisempi kysymys, että kannattaako mennä julkisen https://www.example.com
kautta vai sisäverkon puolella http://172.18.0.1:9000
. En minä tiedä kannattaako. Minun maailmassani ulkokautta kiertäminen tuntui vain oudolta, kun voisi mennä lyhempääkin tietä.
Sitä en sitten tiedä miten tuon hoitaisi, jos ei ole mitään varsinaista backendiä, vaan web-serveri tarjoilee suoraan sivustot. Kai sitten kannattaa tai täytyy mennä https-reittiä. Tai sitten sisäverkon http-serveri tekee samat temput kuin porttia 443 kuunteleva sillä erolla, että SSL:ää ei tarvitse terminoida — käyttää SSL:ää serverin sisällä on pahimman luokan vainoharhaa.
Tehdyt muutokset SearXNG:lle https-versioon verrattuna
docker-compose.yml
laitetaan samalle tasolle sisennettynä (kaksi välilyöntiä) kuin missä on searxng:
ja environment:
:
❯ Näytä koodi
extra_hosts:
- "host.docker.internal:172.18.0.1"
IP-osoite kovakoodataan, eikä käytetä dokumenttien tarjoamahost.docker.internal:host-gateway
siksi, että host-gateway
on käytännössä aina dockerin bridgen IP, ei containerin IP. Tuo on kohtuullisen yleinen päänsärky dockerissa.
Tässä on pakko käyttää containerin IP-osoitetta, koska bridge tarjosi aina väärää. Sen takia minulla se ensimmäinen 10 tuntia hukkaan meni. Ja tuo ei ole sama kuin vaikkapa Varnishin logeissa näkyvä 172.18.0.3. Se on SearXNG:n IP, eli ikäänkuin käyttäjän IP-osoite. Jep, tämä on mahdottoman sekavaa, ainakin minulle.
Json enginessä vaihdetaan:
❯ Näytä koodi
enable_http: true
search_url: http://host.docker.internal:9000/wp-json/...
Yhden hostin serveriblokki Nginxiin:
Alun esimerkki pätee normaalille liikenteelle ulkomaailman kautta. Tässä pysytään serverin sisäverkossa.
❯ Näytä koodi
# Searxngc
server {
listen 172.18.0.1:9000;
server_name www.eksis.one;
# Lokit erilliseen tiedostoon (helpottaa debugia)
access_log /var/log/nginx/xng-access.log main;
error_log /var/log/nginx/xng-error.log;
# Salli vain nämä REST-polut (minulla vain postit)
location ~ ^/wp-json/wp/v2/(posts)(/.*)?$ {
# Proxy Apache-backendiin
proxy_pass http://127.0.0.1:8282;
# WordPress tarvitsee oikean Hostin
#proxy_set_header Host $host;
# Perusotsakkeet ja keepalive
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_set_header Host www.eksis.one;
proxy_set_header X-Forward-Proto https;
proxy_set_header X-Forwarded-For $remote_addr;
# Sisäinen tunniste (valinnainen)
#proxy_set_header X-Internal-Caller "searxng";
}
# Kaikki muu 403:lla kiinni, aika turha
location / {
return 403;
}
}
Hakukyselyt serverin sisäisenä useammalla hostilla
Mutta minulla oli useampi WordPress, jotka halusin hakuihin mukaan, viisi tarkkaan ottaen. Ongelman tynkää on siinä, että engine ei lähetä tietoa hostista. Tai tuo on siis ongelma, jos pysytään sisäverkon puolella, ei jos mennään https-osoitteella, koska sehän menee aina oikealle hostille — siksi meillä on sellainen hassu asia kuin domain urlissa.
Joten host täytyy välittää API-pyynnön mukana sille, joka kuuntelee 172.18.0.1:9000 porttia — portin voi valita miten haluaa, mutta IP on oltava tuo tai minkä sitten docker kertoo bridgen päättäneen oleva containerin ns. julkinen IP.
Tuohon tarvitaan ikioma virtuaali host, joka tekee proxyt samalla tavalla kuin julkisen puolen vhost.
Host-tieto täytyy saada jollain tavalla käyttöön niin, että saadaan tehtyä oikea proxy_pass
.
Sain lopulta useamman hostin hoidettua sisäverkon puolella. Ensin tein sen map-tempulla. Pelkästään siksi, että oli sanottu sen olevan oleellisen tärkeä turvallisuusjuttu. arg_
asioidem oivaltamiseen ja ratkaisun tekemiseen upposi 6 tuntia ja minulla alkoi näppäimistöstä painatukset kulumaan ctrl-, C- ja V-hatuista. Jonkun verran jouduin pyytämään apua myös tekoälyltä, kun en ymmärtänyt jotain yhtään. Välillä sain apuja, ja välillä minut johdatettiin aivan väärään suuntaan — sellaista se on tekoälyjen kanssa.
Lopulta yksinkertaistin asioita, heivasin map-rakenteen ja käytin $arg_
suoraan Nginxissä laittaen samantien oikean hostin API-kyselyyn.
Kyllä, nyt ainoastaan tyhjä site-tieto antaa error 403. Turvallisuutta tai ei, mutta kuinka moni sellainen, jolla olisi tarkoitus rikkoa tuo haku laittamalla siihen jonkun höpöhostin, jota web-serverini ei osaa proxyta, edes kykenee pääsemään palomuurin läpi serverin puolelle injektoimaan väärän hostin? Kun saan tuohon vastauksen, ja se on jokin muu kuin admin-kirjautumisen SSH:n murtaminen, niin palautan mapin.
Kun kaikki hostit oli asetettu toimimaan serverin paikallisella puolella, niin minulta meni 5 minuuttia, kun vaihdoin takaisin jokaisen toimimaan taas https-osoitteilla.
Miksikö? Se on yksinkertaisempi. Sisäverkkohöpsötyksissä on aivan liian monta liikkuvaa osaa, jotka voivat hajota joskus. Käytännössä pelkäsin dockerin/composerin IP-osoitteen vaihtumista. En minä tiedä voiko se edes vaihtua, ja jos voi, niin missä tilanteessa. Siksi sitä pelkäänkin.
Tämä on taas niitä hetkiä, kun kyseenalaistan valintojani elämässä. Ja ajankäytön prioriteetteja.
En minä kuitenkaan mitään oppinut ja palautin uudemman kerran enginet käyttämään sisäverkkoa. Syynä käytin sitä, että se on einen nopeampi. Aito syy oli kylläkin niinkin tylsä kuin että haut saastuttavat liikaa Nginxin JA Varnishin logeja, eivätkä ne ole minulle millään tavalla informatiivisia. Joten siirtää kaikki takaisin localhostin ja containerin väliseksi oli helpoin tapa saada vain yksi logi mahdollisia debuggauksia varten.
json engine
Muutokset ovat docker-compose.yml
tiedostossa samat kuin yhden hostin ratkaisussa.
docker-compose.yml
laitetaan samalle tasolle sisennettynä (kaksi välilyöntiä) kuin missä on searxng:
ja environment:
:
❯ Näytä koodi
extra_hosts:
- "host.docker.internal:172.18.0.1"
Json enginessä sallitaan http käyttö, sekä vaihdetaan url — aivan samoin kuin yhdelläkin hostilla. Mutta queryn alkuun, keskelle tai loppuun täytyy laittaa site=<hostin_nimi>&
Esimerkiksi näin:
❯ Näytä koodi
enable_http: true
search_url: http://host.docker.internal:9000/wp-json/wp/v2/posts?site=www.eksis.one&_embed=1&search=query&per_page=10&page=pageno
- käytän aina www-muotoa, koska käskystäni Apache2 odottaa sitä
Nginx
Nginxissä tarvitaan erillinen virtual host hoitamaan proxyt, esimerkiksi searxng-internal.conf
❯ Näytä koodi
# Searxngc
server {
listen 172.18.0.1:9000;
server_name _;
# Lokit erilliseen tiedostoon (helpottaa debugia)
access_log /var/log/nginx/xng-access.log main;
error_log /var/log/nginx/xng-error.log;
# Salli vain nämä REST-polut (tässä postit, pagesit ja yksi CPT.
# Muuta slug oikeaksi. Jos useampi, laita kaikki mukaan
location ~ ^/wp-json/wp/v2/(posts|pages|knowledgebase)(/.*)?$ {
# jos site puuttuu tai on tyhjä, niin 403
if ($arg_site = "") { return 403; }
# pyyntö lähtee Apachelle
proxy_pass http://127.0.0.1:8282;
# kerro WP:lle oikea vhost + schema, ettei tule redirect/403
proxy_set_header Host $arg_site;
proxy_set_header X-Forwarded-Host $arg_site;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_http_version 1.1;
proxy_set_header Connection "";
}
# Kaikki muu 403:lla estetty, ehkä turha
location / {
return 403;
}
}
Jos haluat varmistaa, että vain sallittuja hosteja käytetään, niin tarvitaan map. Muuta vhostin konffista kaikki kolme $arg_site
muotoon $target_host
.
Laita tämä /etc/nginx/nginx.conf
tiedoston http {}
osioon (kaipa sen voisi laittaa myös vhostiin internal.conf
ensimmäiseksi ennen ensimmäistä server-lohkoa).
❯ Näytä koodi
# site-parametri -> kohdedomain
map $arg_site $target_host {
default "";
katiska www.eksis.one;
toinen www.example.tld;
# lisää tänne muita saitteja...
}
Joten nyt tehdään tulkkaus queryn site-tiedosta. Aidon hostin nimi täytyy olla sellainen, jonka vastaanottava ymmärtää.
Sitten on kysymysmerkkinä ylimääräinen site=jotain
API-kyselyssä. Jos sen haluaa siivota pois API-kyselystä ennen Apachea, niin siihenkin tarvitaan map.
Koska en osaa regex-asioita, lähes yhtään, niin nakitin tekoälyn koostamaan tämän minulle. Omissa kokeiluissa se ei ainakaan kaatanut mitään ja haut onnistuivat.
❯ Näytä koodi
# Poistaa "site=" kyselyn stringistä, jotta backend ei koskaan näe sitä
# match: (alku)(site=...)(loppu) -> yhdistä alku+loppu
map $query_string $qs_wo_site {
~^(?<pfx>(?:^|.*?&))site=[^&]*&(?<sfx>.*)$ $pfx$sfx; # site välissä
~^(?<only>site=[^&]*)$ ""; # site ainoana
~^(?<beg>site=[^&]*&)(?<rest>.*)$ $rest; # site alussa
default $query_string;
}
Tämä sitten laitetaan samaan paikkaan kuin edellinen map. Mutta hyvin minulla on toiminut ilman tuota.
Error 403
Jos saa vastauksen error 403, niin API-pyyntö ei mene läpi tai pyydetään laitonta polkua, ja jos kaikki muu on kunnossa, niin yleisin syy on se, että API:n käyttö on estetty. Se on rajoitettu joko sivustolle itselleen, kirjautumisen taakse tai pelkästään adminille. Jos tuo tapahtuu omalla sivustollasi, niin tietänet syyn.
Vieraalla sivustolla et voi API:a käyttää, ellei sen sivuston admin sitä sinulle tarjoa. Omalla sivustollasi pystyt sen ratkaisemaan — minä en vaan tiedä miten. Riippuu huomattavan paljon miten olet estänyt tai rajoittanut REST API:n käyttöä. SearXNG auttaa (tai Google…) kun lähdet selvittämään mitä täytyy tehdä. Mutta älä lähetä salasanaa API-pyynnön mukana. Se on melkoisen tietoturvaton tapa.
Varnish
Minä olen rajoittanut pääsyä Varnishissa ja sallinnut /wp-json/wp/
autentikoiduille, jos on kirjautuneena (koska minulla WordPress ei ole vapaalla rekisteröitymisellä, koska siellä ei ole mitään käyttäjille, ja silloin olen ainoa käyttäjä) tai mikäli serveri lähettää headereissa acl-tiedon IP-osoitteen mukaan.
Salasanasalliminen on vaan selvyyden takia, ei se käytössä ole. Kirjautumisen juoruava cookie on myös aika turha ja normaalikäytössä vain estämässä cachen, vaikka tarjoaa myös käyttöeston. Aito rajoitus tulee Nginxin IP-osoitteiden mukaan tekemästä acl-headerista.
❯ Näytä koodi
vcl_recv:
## WordPress REST API
if (req.url ~ "^/wp-json/wp/") {
if (req.http.Authorization||
req.http.Cookie ~ "wordpress_logged_in" ||
# Nginxissä tehty acl
req.http.X-Bypass == "true" ||
# SearXNG:n oma IP; aika turha, koska se on acl:ssä
req.http.X-Real-IP == "172.18.0.3"
) {
# Lyhyt cache SearXNG kyselyille
# vain rauhoittamaan Valkeyn elämää
if (req.url ~ "[?&]search=") {
return (hash);
}
# everything else will pass
return (pass);
}
# others: go away
return (synth(403, "Unauthorized request"));
}
vcl_backend_response:
## Lyhyt cache SearXNG API-kutsuille.
# Lisää custom post typet posts|pages jälkeen (Relevanssi)
if (bereq.url ~ "^/wp-json/wp/v2/(posts|pages|knowledge|podcasts)(/|\\?|$)" &&
bereq.url ~ "(\\?|&)search=") {
set beresp.uncacheable = false;
unset beresp.http.Cache-Control;
set beresp.ttl = 30s;
return(deliver);
}
Hieman asian vierestä dockerille
Taistelin melkoisen paljon löytääkseni oikean IP:n, jota minulla container käytti. Lähes kaikki ohjeet käyttivät dockerin host-bridgen IP-osoitetta tai muuta defaulttia, ja ne antoivat timeoutin, koska kukaan ei kuunnellut sitä.
En enää edes muista mitä kaikkea kokeilin. Puolessa tapauksissa en edes ymmärtänyt syötettä. Jälkeenpäin ihmettelin, että eikö voi olla sellaista työkalua, jonka tavallinenkin ihminen ymmärtää ja joka kertoo samantien mikä se hemmetin IP-osoite on. Tällaisen scriptin sain aikaiseksi:
❯ Näytä koodi
nano /usr/local/bin/docker-ips
❯ Näytä koodi
#!/bin/bash
# Näytä konttien gatewayn IP-osoitteet ja verkkojen ID:t
if [ $# -eq 0 ]; then
containers=$(docker ps -q)
else
containers=$(docker ps -qf "name=$1")
fi
for cid in $containers; do
cname=$(docker inspect --format '{{.Name}}' "$cid" | sed 's|/||')
echo "=== $cname ==="
docker inspect -f '{{range $net,$v := .NetworkSettings.Networks}}Verkko: {{$net}}
Kontin IP: {{$v.IPAddress}}
Gateway: {{$v.Gateway}}
VerkkoID: {{printf "%.12s" $v.NetworkID}}{{"\n"}}{{end}}' "$cid"
done
Ja tietysti tuo tarvitsee chmod +x
Minulla syöte oli tällainen:
❯ Näytä koodi
=== searxng ===
Verkko: searxng_default
Kontin IP: 172.18.0.3
Gateway: 172.18.0.1
VerkkoID: e2450523996f
=== searxng-valkey-1 ===
Verkko: searxng_default
Kontin IP: 172.18.0.2
Gateway: 172.18.0.1
VerkkoID: e2450523996f
=== qr-service ===
Verkko: qr_default
Kontin IP: 172.19.0.2
Gateway: 172.19.0.1
VerkkoID: 27345adc15d4
Valkey on tietokanta hoitamassa mm. limiteriä ja qr-service on simppeli appi, joka tekee QR-koodeja (ei kovinkaan näppärä), joten searxng_default on se mikä kiinnostaa.
Gateway on se mitä Nginx portissa 9000 kuuntelee ja mikä tarvitaan, jos IP:t joskus jostain oudosta syystä vaihtuvat. Containerin IP on se mikä näkyy käyttäjänä logeissa. Verkon ID:tä tarvitaan siinä tilanteessa kun aletaan miettimään onko verkko UP vai DOWN. Joskus voi törmätä sellaiseen hervottoman pitkään ID:hent — tuo on sen 12 ensimmäistä merkkiä, ja sitä käytetään joka paikassa.
Löytää ID:n myös suoraan komennolla docker network ls
mutta scriptillä saa IP:kin. Gatewayn ja searxng:n IP:t sekä sen hervottoman pitkän ID:n saa tälläkin, mutta kuka tuollaisia muistaa:
❯ Näytä koodi
docker inspect searxng --format '{{range $k,$v := .NetworkSettings.Networks}}{{println $k "gw" $v.Gateway "ip" $v.IPAddress "netid" $v.NetworkID}}{{end}}'
Tällä komennolla näkee onko verkko pystyssä; UP on, DOWN on alhaalla.
❯ Näytä koodi
ip -br addr show br-e2450523996f
Tähän tarvitaan sitä verkon ID:tä. Kyllä, br-
täytyy olla edessä — lyhenteenä viittaa bridgeen, luultavasti oikeammin bridge interface.
Jos se oli alhaalla, niin tämä auttaa:
❯ Näytä koodi
ip link set br-3e41aa1bf153 up
Keskustele foorumilla Katiskan foorumi