Compare commits

..

12 Commits

Author SHA1 Message Date
Xe Iaso
64585dc2bd chore: spelling
Signed-off-by: Xe Iaso <me@xeiaso.net>
2025-07-24 18:05:10 -04:00
Xe Iaso
ca13b6c3f4 test(palemoon): disable i386 for now
Signed-off-by: Xe Iaso <me@xeiaso.net>
2025-07-24 17:38:44 -04:00
Xe Iaso
449e684993 fix: disable tmate
Signed-off-by: Xe Iaso <me@xeiaso.net>
2025-07-24 17:31:32 -04:00
Xe Iaso
89230f7678 test(palemoon): add some variables
Signed-off-by: Xe Iaso <me@xeiaso.net>
2025-07-24 17:27:18 -04:00
Xe Iaso
b2f3f99b52 ci: enable palemoon tests
Signed-off-by: Xe Iaso <me@xeiaso.net>
2025-07-24 15:52:33 -04:00
Xe Iaso
50cfe72cef test(palemoon): rewrite to use ci-images
Signed-off-by: Xe Iaso <me@xeiaso.net>
2025-07-24 15:52:16 -04:00
Xe Iaso
7d60a0a77a test: start work on Pale Moon tests
Signed-off-by: Xe Iaso <me@xeiaso.net>
2025-07-24 09:32:16 -04:00
Moonchild
21f570962c Pass forward X-Real-IP to nginx backend server (#901)
The TLS termination server sets X-Real-IP to be used by the back-end, but the back-end configuration example doesn't actually extract it so nginx logs (and back-end processing) fails to log or use the visiting IP in any way (it just states `unix:` if using a unix socket like in the example given, or the local IP if forwarded over TCP).

Adding real_ip_header to the config will fix this.

Signed-off-by: Moonchild <moonchild@palemoon.org>
2025-07-24 12:11:53 +00:00
Xe Iaso
1cb1352a44 fix(blog/v1.21.1): we avoid breaking changes
Signed-off-by: Xe Iaso <me@xeiaso.net>
2025-07-22 22:54:22 +00:00
Xe Iaso
a4c08687cc docs: add blogpost for announcing v1.21.1 (#886)
* docs: add release announcement post for v1.21.1

Signed-off-by: Xe Iaso <me@xeiaso.net>

* docs(v1.21.1): small fixups

Signed-off-by: Xe Iaso <me@xeiaso.net>

* docs(v1.21.1): spelling fixes

Signed-off-by: Xe Iaso <me@xeiaso.net>

* docs(v1.21.1): clarify that Bell is trash

Signed-off-by: Xe Iaso <me@xeiaso.net>

* chore: spelling

check-spelling run (pull_request) for Xe/v1.21.1-blogpost

Signed-off-by: check-spelling-bot <check-spelling-bot@users.noreply.github.com>
on-behalf-of: @check-spelling <check-spelling-bot@check-spelling.dev>

---------

Signed-off-by: Xe Iaso <me@xeiaso.net>
Signed-off-by: check-spelling-bot <check-spelling-bot@users.noreply.github.com>
2025-07-22 16:42:58 -04:00
Xe Iaso
1a19d7eee4 chore: release v1.21.1 (#887)
Signed-off-by: Xe Iaso <me@xeiaso.net>
2025-07-22 16:32:06 -04:00
Sunniva Løvstad
25af5a232f feat(localization): Add in Bokmål and Nynorsk translations (#855)
* feat(localization): add bokmål and nynorsk translations

* feat(localization): update tests for Bokmål and Nynorsk

* docs(localization): document bokmål and nynorsk locales

* fix(locales/nb,nn): remove unicode ellipsis to make tests pass

Signed-off-by: Xe Iaso <me@xeiaso.net>

* style(localization): sort languages to make test output stable

Signed-off-by: Xe Iaso <me@xeiaso.net>

* chore: spelling

check-spelling run (pull_request) for main

Signed-off-by: check-spelling-bot <check-spelling-bot@users.noreply.github.com>
on-behalf-of: @check-spelling <check-spelling-bot@check-spelling.dev>

---------

Signed-off-by: Xe Iaso <me@xeiaso.net>
Signed-off-by: Xe Iaso <xe.iaso@techaro.lol>
Signed-off-by: check-spelling-bot <check-spelling-bot@users.noreply.github.com>
Co-authored-by: Xe Iaso <me@xeiaso.net>
Co-authored-by: Xe Iaso <xe.iaso@techaro.lol>
2025-07-21 22:37:49 -04:00
29 changed files with 807 additions and 18 deletions

View File

@@ -23,6 +23,7 @@ bitrate
Bluesky
blueskybot
boi
Bokm
botnet
botstopper
BPort

View File

@@ -18,7 +18,9 @@ jobs:
- git-push
- healthcheck
- i18n
runs-on: ubuntu-24.04
- palemoon/amd64
#- palemoon/i386
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
@@ -43,3 +45,14 @@ jobs:
run: |
cd test/${{ matrix.test }}
backoff-retry --try-count 10 ./test.sh
- name: Sanitize artifact name
if: always()
run: echo "ARTIFACT_NAME=${{ matrix.test }}" | sed 's|/|-|g' >> $GITHUB_ENV
- name: Upload artifact
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02
if: always()
with:
name: ${{ env.ARTIFACT_NAME }}
path: test/${{ matrix.test }}/var

View File

@@ -1 +1 @@
1.21.0
1.21.1

View File

@@ -43,7 +43,7 @@ And this release also fixes the following bugs:
- In certain cases, a user could be stuck with a test cookie that is invalid, locking them out of the service for up to half an hour. This has been fixed with better validation of this case and clearing the cookie.
- "Proof of work" has been removed from the branding due to some users having extremely negative connotations with it.
We try to introduce breaking changes as much as possible, but these are the changes that may be relevant for you as an administrator:
We try to avoid introducing breaking changes as much as possible, but these are the changes that may be relevant for you as an administrator:
- The [challenge format](#challenge-format-change) has been changed in order to account for [the new challenge issuance flow](#challenge-flow-v2).
- The [systemd service `RuntimeDirectory` has been changed](#breaking-change-systemd-runtimedirectory-change).

View File

@@ -13,19 +13,25 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
<!-- This changes the project to: -->
## v1.21.1: Minfilia Warde - Echo 1
- Expired records are now properly removed from bbolt databases ([#848](https://github.com/TecharoHQ/anubis/pull/848)).
- Fix hanging on service restart ([#853](https://github.com/TecharoHQ/anubis/issues/853))
### Added
Anubis now supports the [`missingHeader`](./admin/configuration/expressions.mdx#missingHeader) to assert the absence of headers in requests.
#### New locales
Anubis now supports these new languages:
- [Czech](https://github.com/TecharoHQ/anubis/pull/849)
- [Finnish](https://github.com/TecharoHQ/anubis/pull/863)
- [Norwegian Bokmål](https://github.com/TecharoHQ/anubis/pull/855)
- [Norwegian Nynorsk](https://github.com/TecharoHQ/anubis/pull/855)
- [Russian](https://github.com/TecharoHQ/anubis/pull/882)
Anubis now supports the [`missingHeader`](./admin/configuration/expressions.mdx#missingHeader) to assert the absence of headers in requests.
### Fixes
#### Fix ["error: can't get challenge"](https://github.com/TecharoHQ/anubis/issues/869) when details about a challenge can't be found in the server side state

View File

@@ -79,6 +79,10 @@ server {
root "/srv/http/anubistest.techaro.lol";
index index.html;
# Get the visiting IP from the TLS termination server
set_real_ip_from unix:;
real_ip_header X-Real-IP;
# Your normal configuration can go here
# location .php { fastcgi...} etc.
}

View File

@@ -11,6 +11,8 @@
"is",
"it",
"ja",
"nb",
"nn",
"pt-BR",
"ru",
"tr",

View File

@@ -0,0 +1,64 @@
{
"loading": "Laster inn...",
"why_am_i_seeing": "Hvorfor ser jeg dette?",
"protected_by": "Beskyttet av",
"protected_from": "Fra",
"made_with": "Laget med ❤️ i 🇨🇦",
"mascot_design": "Maskotdesign av",
"ai_companies_explanation": "Du ser dette fordi administratoren av dette nettstedet har satt opp Anubis for å beskytte sørveren mot plagen av KI-selskaper som aggressivt skraper nettsteder. Dette kan, og fortsetter med å, forårsake driftstans for nettstedene, som gjør ressursene deres utilgjengelige for alle.",
"anubis_compromise": "Anubis er et kompromiss. Anubis bruker et «Proof-of-Work»-skjema som ligner på Hashcash, et lignende skjema for å redusere søppel-e-post. Idéen er at ved småstilte tilfeller er den ytterligere belastningen ignorerbar, men ved storstilt skraping samler den på seg fart og gjør det å skrape mye mer dyrt.",
"hack_purpose": "Til syvende og sist er dette en hack som har som formål å gi en «god nok» plassholderløsning slik at mer tid kan brukes på å fingeravtrykke og gjenkjenne hodeløse nettlesere (f.eks. hvordan de gjengir skrifttyper) slik at utfordringssiden ikke må bli vist til brukere som er mer sannsynligvis ekte.",
"jshelter_note": "NB: Anubis krever bruk av moderne JavaScript-funksjoner som tillegg som JShelter slår av. Vennligst slå av JShelter eller lignende tillegg for dette domenet.",
"version_info": "Dette nettstedet kjører Anubis-utgave",
"try_again": "Prøv igjen",
"go_home": "Gå hjem",
"contact_webmaster": "eller om du synes at du ikke burde være blokkert, vennligst ta kontakt med administratoren på",
"connection_security": "Vennligst vent mens vi bekrefter tryggheten av tilkoblingen din.",
"javascript_required": "Du må dessverre slå på JavaScript for å komme deg forbi denne utfordringen. Dette kreves fordi KI-selskaper har endret sosialkontrakten om hvordan nettstedsverting fungerer. En ikke-JS-løsning er i gang med å skapes.",
"benchmark_requires_js": "JavaScript må være påslått for å kjøre sammenligningsverktøyet.",
"difficulty": "Vanskelighetsnivå:",
"algorithm": "Algoritme:",
"compare": "Jevnfør:",
"time": "Tid",
"iters": "Gjentakelser",
"time_a": "Tid A",
"iters_a": "Gjentakelser A",
"time_b": "Tid B",
"iters_b": "Gjentakelser B",
"static_check_endpoint": "Dette er bare et sjekkeendepunkt for din omvendte proxy å bruke.",
"authorization_required": "Legitimasjon kreves",
"cookies_disabled": "Nettleseren din er konfigurert for å avslå informasjonskapsler. Anubis krever informasjonskapsler for å bekrefte at du er en ekte bruker. Vennligst slå på informasjonskapsler på dette domenet.",
"access_denied": "Adgang nektet: feilkode",
"dronebl_entry": "DroneBL rapporterte em oppføring.",
"see_dronebl_lookup": "se",
"internal_server_error": "Intern serverfeil: administratoren har feilkonfigurert Anubis. Vennligst ta kontakt med hen og spør hen om å se gjennom loggene om",
"invalid_redirect": "Ugyldig omdirigering",
"redirect_not_parseable": "Omdirigerings-URL-en kunne ikkj tolkes",
"redirect_domain_not_allowed": "Omdirigeringsdomenet er ikke tillatt",
"failed_to_sign_jwt": "mislyktes i å signere JWT",
"invalid_invocation": "Ugyldig fremkalling av MakeChallenge",
"client_error_browser": "Klientfeil: Vennligst sørg for at at nettleseren din er oppdatert og prøv igjen senere.",
"oh_noes": "Å nei!",
"benchmarking_anubis": "Sammenligner Anubis!",
"you_are_not_a_bot": "Du er ikke en bot!",
"making_sure_not_bot": "Bekrefter at du ikke er en bot!",
"celphase": "CELPHASE",
"js_web_crypto_error": "Nettleseren din har ikke et fungerande web.crypto-element. Ser du dette med ei sikker tilkopling?",
"js_web_workers_error": "Nettleseren din støtter ikke nettarbeidere (Anubis bruker dette for å unngå å fryse nettleseren din). Har du et tillegg som JShelter installert?",
"js_cookies_error": "Nettleseren lagrer ikke informasjonskapsler. Anubis bruker informasjonskapsler for å avgjøre hvilke klienter har lyktes i utfordringen ved å lagre en signert token i en informasjonskapsel. Vennligst slå på informasjonskapsler på dette domenet. Navnene på informasjonskapslene Anubis lagrer, kan variere uten varsel. Informasjonskapselnavn og -verdier er ikke en del av det offentlege API-et.",
"js_context_not_secure": "Du bruker ikke en sikker tilkobling!",
"js_context_not_secure_msg": "Prøv å koble til over HTTPS eller fortell administratoren å opprette HTTPS. Se <a hreflang=\"en\" href=\"https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts#when_is_a_context_considered_secure\">MDN</a> for mer informasjon.",
"js_calculating": "Beregner…",
"js_missing_feature": "Mangler funksjon",
"js_challenge_error": "Utfordringsfeil!",
"js_challenge_error_msg": "Mislyktes i å tolke sjekkalgoritmen. Du burde laste inn denne siden på nytt.",
"js_calculating_difficulty": "Beregner…<br/>Vanskelighetsnivå:",
"js_speed": "Hastighet:",
"js_verification_longer": "Verifisering tar lengre enn forventet. Vennligst ikke last inn denne siden på nytt.",
"js_success": "Vellykket!",
"js_done_took": "Ferdig! Tok",
"js_iterations": "gjentakelser",
"js_finished_reading": "Jeg har sluttet å lese, fortsett →",
"js_calculation_error": "Beregningsfeil!",
"js_calculation_error_msg": "Mislyktes i å beregne utfordring:"
}

View File

@@ -0,0 +1,64 @@
{
"loading": "Lastar inn...",
"why_am_i_seeing": "Kvifor ser eg dette?",
"protected_by": "Verna av",
"protected_from": "Frå",
"made_with": "Laga med ❤️ i 🇨🇦",
"mascot_design": "Maskotdesign av",
"ai_companies_explanation": "Du ser dette av di administratoren av denne nettstaden har sett opp Anubis for å verne sørvaren mot plaga av KI-selskap som aggressivt skrapar nettstader. Dette kan, og held frem med å, forårsake driftstans for nettstadene, som gjer ressursane deira utilgjengelege for alle.",
"anubis_compromise": "Anubis er eit kompromiss. Anubis brukar eit «Proof-of-Work»-skjema som liknar på Hashcash, eit liknande skjema for å redusere søppel-e-post. Idéen er at ved småstilte tilfelle er den ytterlegare lastinga ignorerbar, men ved storstilt skraping samlar ho på seg fart og gjer det å skrapa mykje meir dyrt.",
"hack_purpose": "Til sjuande og sist er dette ein hack som har som formål å gje ei «god nok» plasshaldarløysing slik at meir tid kan brukast på å fingeravtrykkje og attkjenne hovudlause nettlesarar (f.eks. korleis dei attgjev skrifttypar) slik at utfordringssida ikkje må verte synt til brukarar som er meir sannsynlegvis ekte.",
"jshelter_note": "NB: Anubis krev bruk av moderne JavaScript-funksjonar som tillegg som JShelter slår av. Venlegast slå av JShelter eller liknande tillegg for dette domenet.",
"version_info": "Denne nettstaden køyrer Anubis-utgåve",
"try_again": "Prøv att",
"go_home": "Gå heim",
"contact_webmaster": "eller om du synest at du ikkje burde vera blokkert, venlegast tak kontakt med administratoren på",
"connection_security": "Venlegast vent medan vi stadfestar tryggleiken av tilkoplinga di.",
"javascript_required": "Du lyt diverre slå på JavaScript for å koma deg forbi denne utfordringa. Dette krevst av di KI-selskap har endra sosialkontrakten om korleis nettstadsverting fungerer. Ei ikkje-JS-løysing er i gang med å skapast.",
"benchmark_requires_js": "JavaScript må vera slegen på for å køyre samanlikningsverktøyet.",
"difficulty": "Vanskenivå:",
"algorithm": "Algoritme:",
"compare": "Jamfør:",
"time": "Tid",
"iters": "Oppattakingar",
"time_a": "Tid A",
"iters_a": "Oppattakingar A",
"time_b": "Tid B",
"iters_b": "Oppattakingar B",
"static_check_endpoint": "Dette er berre eit sjekkeendepunkt for din omvende proxy å bruke.",
"authorization_required": "Legitimasjon krevst",
"cookies_disabled": "Nettlesaren din er konfigurert for å avslå informasjonskapslar. Anubis krev informasjonskapslar for å stadfeste at du er ein ekte brukar. Venlegast slå på informasjonskapslar på dette domenet.",
"access_denied": "Tilgang nekta: feilkode",
"dronebl_entry": "DroneBL rapporterte ei oppføring.",
"see_dronebl_lookup": "sjå",
"internal_server_error": "Intern serverfeil: administratoren har feilkonfigurert Anubis. Venlegast tak kontakt med hen og spør hen om å sjå gjennom loggane om",
"invalid_redirect": "Ugyldig omdirigering",
"redirect_not_parseable": "Omdirigerings-URL-en kunne ikkje tolkast",
"redirect_domain_not_allowed": "Omdirigeringsdomenet er ikkje tillate",
"failed_to_sign_jwt": "mislukkast i å signere JWT",
"invalid_invocation": "Ugyldig framkalling av MakeChallenge",
"client_error_browser": "Klientfeil: Venlegast stadfest at nettlesaren din er oppdatert og prøv att seinare.",
"oh_noes": "Å nei!",
"benchmarking_anubis": "Samanliknar Anubis!",
"you_are_not_a_bot": "Du er ikkje ein bot!",
"making_sure_not_bot": "Stadfestar at du ikkje er ein bot!",
"celphase": "CELPHASE",
"js_web_crypto_error": "Nettlesaren din har ikkje eit fungerande web.crypto-element. Ser du dette med ei sikker tilkopling?",
"js_web_workers_error": "Nettlesaren din støttar ikkje nettarbeidarar (Anubis brukar dette for å unngå å fryse nettlesaren din). Har du eit tillegg som JShelter installert?",
"js_cookies_error": "Nettlesaren lagrar ikkje informasjonskapslar. Anubis brukar informasjonskapslar for å avgjera kva klientar har lukkast i utfordringa ved å lagra ein signert token i ein informasjonskapsel. Venlegast slå på informasjonskapslar på dette domenet. Namna på informasjonskapslane Anubis lagrar, kan variere utan varsel. Informasjonskapselnamn og -verdiar er ikkje ein del av det offentlege API-et.",
"js_context_not_secure": "Du brukar ikkje ei sikker tilkopling!",
"js_context_not_secure_msg": "Prøv å kople til over HTTPS eller fortel administratoren å opprette HTTPS. Sjå <a hreflang=\"en\" href=\"https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts#when_is_a_context_considered_secure\">MDN</a> for fleire opplysingar.",
"js_calculating": "Reknar…",
"js_missing_feature": "Manglar funksjon",
"js_challenge_error": "Utfordringsfeil!",
"js_challenge_error_msg": "Mislukkast i å tolke sjekkalgoritmen. Du burde laste inn denne sida på nytt.",
"js_calculating_difficulty": "Reknar…<br/>Vanskenivå:",
"js_speed": "Fart:",
"js_verification_longer": "Verifisering tek lengre enn forventa. Venlegast ikkje last inn denne sida på nytt.",
"js_success": "Vellykka!",
"js_done_took": "Ferdig! Tok",
"js_iterations": "oppattakingar",
"js_finished_reading": "Eg har slutta å lesa, hald fram →",
"js_calculation_error": "Rekningsfeil!",
"js_calculation_error_msg": "Mislukkast i å rekne utfordring:"
}

View File

@@ -21,6 +21,8 @@ func TestLocalizationService(t *testing.T) {
"fr": "Chargement...",
"ja": "ロード中...",
"is": "Hleður...",
"nb": "Laster inn...",
"nn": "Lastar inn...",
"pt-BR": "Carregando...",
"tr": "Yükleniyor...",
"ru": "Загрузка...",
@@ -28,7 +30,16 @@ func TestLocalizationService(t *testing.T) {
"zh-TW": "載入中...",
}
for lang, expected := range loadingStrMap {
var keys []string
for lang := range loadingStrMap {
keys = append(keys, lang)
}
sort.Strings(keys)
for _, lang := range keys {
expected := loadingStrMap[lang]
t.Run(fmt.Sprintf("%s localization", lang), func(t *testing.T) {
localizer := service.GetLocalizer(lang)
result := localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "loading"})
@@ -44,7 +55,7 @@ func TestLocalizationService(t *testing.T) {
"mascot_design", "try_again", "go_home", "javascript_required",
}
for lang := range loadingStrMap {
for _, lang := range keys {
t.Run(fmt.Sprintf("All required keys exist in %s", lang), func(t *testing.T) {
loc := service.GetLocalizer(lang)
for _, key := range requiredKeys {

4
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "@techaro/anubis",
"version": "1.21.0",
"version": "1.21.1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@techaro/anubis",
"version": "1.21.0",
"version": "1.21.1",
"license": "ISC",
"devDependencies": {
"cssnano": "^7.1.0",

View File

@@ -1,6 +1,6 @@
{
"name": "@techaro/anubis",
"version": "1.21.0",
"version": "1.21.1",
"description": "",
"main": "index.js",
"scripts": {

View File

@@ -0,0 +1,33 @@
package internal
import (
"context"
"fmt"
"github.com/docker/docker/client"
)
// GetContainerIPAddress returns the first non-empty IP address of the container with the given name.
// It returns the IP address as a string or an error.
func GetContainerIPAddress(containerName string) (string, error) {
ctx := context.Background()
cli, err := client.NewClientWithOpts(client.FromEnv)
if err != nil {
return "", err
}
// Get container details
containerJSON, err := cli.ContainerInspect(ctx, containerName)
if err != nil {
return "", err
}
// Loop through all networks and return the first IP address found
for _, net := range containerJSON.NetworkSettings.Networks {
if net.IPAddress != "" {
return net.IPAddress, nil
}
}
return "", fmt.Errorf("no IP address found for container %q", containerName)
}

View File

@@ -0,0 +1,50 @@
package internal
import (
"fmt"
"net"
)
// GetLANIP returns the first non-loopback IPv4 LAN IP address.
func GetLANIP() (net.IP, error) {
ifaces, err := net.Interfaces()
if err != nil {
return nil, err
}
for _, iface := range ifaces {
// Skip down or loopback interfaces
if iface.Flags&(net.FlagUp|net.FlagLoopback) != net.FlagUp {
continue
}
addrs, err := iface.Addrs()
if err != nil {
continue // skip interfaces we can't query
}
for _, addr := range addrs {
var ip net.IP
switch v := addr.(type) {
case *net.IPNet:
ip = v.IP
case *net.IPAddr:
ip = v.IP
}
if ip == nil || ip.IsLoopback() {
continue
}
ip = ip.To4()
if ip == nil {
continue // not an IPv4 address
}
return ip, nil
}
}
return nil, fmt.Errorf("no connected LAN IPv4 address found")
}

View File

@@ -0,0 +1,34 @@
package internal
import (
"context"
"log"
"os"
"github.com/docker/docker/api/types/network"
"github.com/docker/docker/client"
)
// UnbreakDocker connects the container named after the current hostname
// to the specified Docker network.
func UnbreakDocker(networkName string) error {
ctx := context.Background()
cli, err := client.NewClientWithOpts(client.FromEnv)
if err != nil {
return err
}
hostname, err := os.Hostname()
if err != nil {
return err
}
err = cli.NetworkConnect(ctx, networkName, hostname, &network.EndpointSettings{})
if err != nil {
return err
}
log.Printf("Connected container %q to network %q\n", hostname, networkName)
return nil
}

114
test/cmd/cipra/main.go Normal file
View File

@@ -0,0 +1,114 @@
package main
import (
"context"
"errors"
"flag"
"fmt"
"log"
"net/http"
"os"
"os/exec"
"strings"
"time"
"github.com/TecharoHQ/anubis/test/cmd/cipra/internal"
"github.com/facebookgo/flagenv"
)
var (
bind = flag.String("bind", ":9090", "TCP host:port to bind HTTP on")
browserBin = flag.String("browser-bin", "palemoon", "browser binary name")
browserContainerName = flag.String("browser-container-name", "palemoon", "browser container name")
composeName = flag.String("compose-name", "", "docker compose base name for resources")
vncServerContainer = flag.String("vnc-container-name", "display", "VNC host:port (NOT a display number)")
)
func main() {
flagenv.Parse()
flag.Parse()
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
defer cancel()
lanip, err := internal.GetLANIP()
if err != nil {
log.Panic(err)
}
os.Setenv("TARGET", fmt.Sprintf("%s%s", lanip.String(), *bind))
http.HandleFunc("/{$}", func(w http.ResponseWriter, r *http.Request) {
http.Error(w, "OK", http.StatusOK)
log.Println("got termination signal", r.RequestURI)
go func() {
time.Sleep(2 * time.Second)
cancel()
}()
})
srv := &http.Server{
Handler: http.DefaultServeMux,
Addr: *bind,
}
go func() {
if err := srv.ListenAndServe(); !errors.Is(err, http.ErrServerClosed) {
log.Panic(err)
}
}()
if err := RunScript(ctx, "docker", "compose", "up", "-d"); err != nil {
log.Fatalf("can't start project: %v", err)
}
defer RunScript(ctx, "docker", "compose", "down", "-t", "1")
defer RunScript(ctx, "docker", "compose", "rm", "-f")
internal.UnbreakDocker(*composeName + "_default")
if err := RunScript(ctx, "docker", "exec", fmt.Sprintf("%s-%s-1", *composeName, *browserContainerName), "bash", "/hack/scripts/install-cert.sh"); err != nil {
log.Panic(err)
}
if err := RunScript(ctx, "docker", "exec", fmt.Sprintf("%s-%s-1", *composeName, *browserContainerName), *browserBin, "https://relayd"); err != nil {
log.Panic(err)
}
<-ctx.Done()
srv.Close()
time.Sleep(2 * time.Second)
}
func RunScript(ctx context.Context, args ...string) error {
var err error
backoff := 250 * time.Millisecond
for attempt := 0; attempt < 5; attempt++ {
select {
case <-ctx.Done():
return nil
default:
}
log.Printf("Running command: %s", strings.Join(args, " "))
cmd := exec.CommandContext(ctx, args[0], args[1:]...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err = cmd.Run()
if exitErr, ok := err.(*exec.ExitError); ok {
log.Printf("attempt=%d code=%d", attempt, exitErr.ExitCode())
}
if err == nil {
return nil
}
log.Printf("Attempt %d failed: %v %T", attempt+1, err, err)
log.Printf("Retrying in %v...", backoff)
time.Sleep(backoff)
backoff *= 2
}
return fmt.Errorf("script failed after 5 attempts: %w", err)
}

View File

@@ -6,6 +6,7 @@ replace github.com/TecharoHQ/anubis => ..
require (
github.com/TecharoHQ/anubis v1.19.1
github.com/docker/docker v28.0.1+incompatible
github.com/facebookgo/flagenv v0.0.0-20160425205200-fcd59fca7456
github.com/google/uuid v1.6.0
)
@@ -13,27 +14,38 @@ require (
require (
buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.6-20250425153114-8976f5be98c1.1 // indirect
cel.dev/expr v0.24.0 // indirect
github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/TecharoHQ/thoth-proto v0.4.0 // indirect
github.com/a-h/templ v0.3.906 // indirect
github.com/antlr4-go/antlr/v4 v4.13.1 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/distribution/reference v0.6.0 // indirect
github.com/docker/go-connections v0.5.0 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/ebitengine/purego v0.8.4 // indirect
github.com/facebookgo/ensure v0.0.0-20200202191622-63f1cf65ac4c // indirect
github.com/facebookgo/subset v0.0.0-20200203212716-c811ad88dec4 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/gaissmai/bart v0.20.5 // indirect
github.com/go-logr/logr v1.4.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang-jwt/jwt/v5 v5.2.2 // indirect
github.com/google/cel-go v0.25.0 // indirect
github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.1.0 // indirect
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.2 // indirect
github.com/joho/godotenv v1.5.1 // indirect
github.com/jsha/minica v1.1.0 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/lum8rjack/go-ja4h v0.0.0-20250606032308-3a989c6635be // indirect
github.com/moby/docker-image-spec v1.3.1 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/nicksnyder/go-i18n/v2 v2.6.0 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.1.1 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
github.com/prometheus/client_golang v1.22.0 // indirect
github.com/prometheus/client_model v0.6.2 // indirect
@@ -45,15 +57,22 @@ require (
github.com/stoewer/go-strcase v1.3.0 // indirect
github.com/yusufpapurcu/wmi v1.2.4 // indirect
go.etcd.io/bbolt v1.4.2 // indirect
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect
go.opentelemetry.io/otel v1.37.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.37.0 // indirect
go.opentelemetry.io/otel/metric v1.37.0 // indirect
go.opentelemetry.io/otel/trace v1.37.0 // indirect
go.yaml.in/yaml/v2 v2.4.2 // indirect
golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 // indirect
golang.org/x/net v0.42.0 // indirect
golang.org/x/sys v0.34.0 // indirect
golang.org/x/text v0.27.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20250528174236-200df99c418a // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250528174236-200df99c418a // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20250603155806-513f23925822 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822 // indirect
google.golang.org/grpc v1.73.0 // indirect
google.golang.org/protobuf v1.36.6 // indirect
gotest.tools/v3 v3.5.2 // indirect
k8s.io/apimachinery v0.33.2 // indirect
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect
sigs.k8s.io/yaml v1.5.0 // indirect

View File

@@ -24,6 +24,8 @@ github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
github.com/cenkalti/backoff/v5 v5.0.2 h1:rIfFVxEf1QsI7E1ZHfp/B4DF/6QBAUhmgkxc0H7Zss8=
github.com/cenkalti/backoff/v5 v5.0.2/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
@@ -32,7 +34,6 @@ github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpS
github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw=
github.com/cpuguy83/dockercfg v0.3.2 h1:DlJTyZGBDlXqUZ2Dk2Q3xHs/FtnooJJVaad2S9GKorA=
github.com/cpuguy83/dockercfg v0.3.2/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
@@ -61,6 +62,7 @@ github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/gaissmai/bart v0.20.5 h1:ehoWZWQ7j//qt0K0Zs4i9hpoPpbgqsMQiR8W2QPJh+c=
github.com/gaissmai/bart v0.20.5/go.mod h1:cEed+ge8dalcbpi8wtS9x9m2hn/fNJH5suhdGQOHnYk=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
@@ -83,10 +85,14 @@ github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.1.0 h1:QGLs
github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.1.0/go.mod h1:hM2alZsMUni80N33RBe6J0e423LB+odMj7d3EMP9l20=
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.2 h1:sGm2vDRFUrQJO/Veii4h4zG2vvqG6uWNkBHSTqXOZk0=
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.2/go.mod h1:wd1YpapPLivG6nQgbf7ZkG1hhSOXDhhn4MLTknx2aAc=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1 h1:X5VWvz21y3gzm9Nw/kaUeku/1+uBhcekkmy4IkffJww=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1/go.mod h1:Zanoh4+gvIgluNqcfMVTJueD4wSS5hT7zTt4Mrutd90=
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/jsha/minica v1.1.0 h1:O2ZbzAN75w4RTB+5+HfjIEvY5nxRqDlwj3ZlLVG5JD8=
github.com/jsha/minica v1.1.0/go.mod h1:dxC3wNmD+gU1ewXo/R8jB2ihB6wNpyXrG8aUk5Iuf/k=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
@@ -164,6 +170,8 @@ github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFA
github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk=
github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
go.etcd.io/bbolt v1.4.2 h1:IrUHp260R8c+zYx/Tm8QZr04CX+qWS5PGfPdevhdm1I=
@@ -174,6 +182,10 @@ go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw=
go.opentelemetry.io/otel v1.37.0 h1:9zhNfelUvx0KBfu/gb+ZgeAfAgtWrfHJZcAqFC228wQ=
go.opentelemetry.io/otel v1.37.0/go.mod h1:ehE/umFRLnuLa/vSccNq9oS1ErUlkkK71gMcN34UG8I=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.37.0 h1:Ahq7pZmv87yiyn3jeFz/LekZmPLLdKejuO3NcK9MssM=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.37.0/go.mod h1:MJTqhM0im3mRLw1i8uGHnCvUEeS7VwRyxlLC78PA18M=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.37.0 h1:bDMKF3RUSxshZ5OjOTi8rsHGaPKsAt76FaqgvIUySLc=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.37.0/go.mod h1:dDT67G/IkA46Mr2l9Uj7HsQVwsjASyV9SjGofsiUZDA=
go.opentelemetry.io/otel/metric v1.37.0 h1:mvwbQS5m0tbmqML4NqK+e3aDiO02vsf/WgbsdpcPoZE=
go.opentelemetry.io/otel/metric v1.37.0/go.mod h1:04wGrZurHYKOc+RKeye86GwKiTb9FKm1WHtO+4EVr2E=
go.opentelemetry.io/otel/sdk v1.37.0 h1:ItB0QUqnjesGRvNcmAcU0LyvkVyGJ2xftD29bWdDvKI=
@@ -182,28 +194,57 @@ go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5J
go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w=
go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4=
go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0=
go.opentelemetry.io/proto/otlp v1.7.0 h1:jX1VolD6nHuFzOYso2E73H85i92Mv8JQYk0K9vz09os=
go.opentelemetry.io/proto/otlp v1.7.0/go.mod h1:fSKjH6YJ7HDlwzltzyMj036AJ3ejJLCgCSHGj4efDDo=
go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI=
go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU=
go.yaml.in/yaml/v3 v3.0.3 h1:bXOww4E/J3f66rav3pX3m8w6jDE4knZjGOw8b5Y6iNE=
go.yaml.in/yaml/v3 v3.0.3/go.mod h1:tBHosrYAkRZjRAOREWbDnBXUf08JOwYq++0QNwQiWzI=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM=
golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY=
golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 h1:y5zboxd6LQAqYIhHnB48p0ByQ/GnQx2BE33L8BOHQkI=
golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6/go.mod h1:U6Lno4MTRCDY+Ba7aCcauB9T60gsv5s4ralQzP72ZoQ=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs=
golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4=
golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU=
google.golang.org/genproto/googleapis/api v0.0.0-20250528174236-200df99c418a h1:SGktgSolFCo75dnHJF2yMvnns6jCmHFJ0vE4Vn2JKvQ=
google.golang.org/genproto/googleapis/api v0.0.0-20250528174236-200df99c418a/go.mod h1:a77HrdMjoeKbnd2jmgcWdaS++ZLZAEq3orIOAEIKiVw=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250528174236-200df99c418a h1:v2PbRU4K3llS09c7zodFpNePeamkAwG3mPrAery9VeE=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250528174236-200df99c418a/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY=
golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/genproto/googleapis/api v0.0.0-20250603155806-513f23925822 h1:oWVWY3NzT7KJppx2UKhKmzPq4SRe0LdCijVRwvGeikY=
google.golang.org/genproto/googleapis/api v0.0.0-20250603155806-513f23925822/go.mod h1:h3c4v36UTKzUiuaOKQ6gr3S+0hovBtUrXzTG/i3+XEc=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822 h1:fc6jSaCT0vBduLYZHYrBBNY4dsWuvgyff9noRNDdBeE=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
google.golang.org/grpc v1.73.0 h1:VIWSmpI2MegBtTuFt5/JWy2oXxtjJ/e89Z70ImfD2ok=
google.golang.org/grpc v1.73.0/go.mod h1:50sbHOUqWoCQGI8V2HQLJM0B+LMlIUjNSZmow7EVBQc=
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
@@ -214,6 +255,8 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EV
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q=
gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA=
k8s.io/apimachinery v0.33.2 h1:IHFVhqg59mb8PJWTLi8m1mAoepkUNYmptHsV+Z1m5jY=
k8s.io/apimachinery v0.33.2/go.mod h1:BHW0YOu7n22fFv/JkYOEfkUYNRN0fj0BlvMFWA7b+SM=
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 h1:gBQPwqORJ8d8/YNZWEjoZs7npUVDpVXUUOFfW6CgAqE=

2
test/i18n/var/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
*
!.gitignore

51
test/lib/lib.sh Normal file
View File

@@ -0,0 +1,51 @@
REPO_ROOT=$(git rev-parse --show-toplevel)
(cd $REPO_ROOT && go install ./utils/cmd/...)
function cleanup() {
pkill -P $$
if [ -f "docker-compose.yaml" ]; then
docker compose down -t 1 || :
docker compose rm -f || :
fi
}
trap cleanup EXIT SIGINT
function build_anubis_ko() {
(
cd $REPO_ROOT && npm ci && npm run assets
)
(
cd $REPO_ROOT &&
VERSION=devel ko build \
--platform=all \
--base-import-paths \
--tags="latest" \
--image-user=1000 \
--image-annotation="" \
--image-label="" \
./cmd/anubis \
--local
)
}
function mint_cert() {
if [ "$#" -ne 1 ]; then
echo "Usage: mint_cert <domain.name>"
fi
domainName="$1"
# If the transient local TLS certificate doesn't exist, mint a new one
if [ ! -f "${REPO_ROOT}/test/pki/${domainName}/cert.pem" ]; then
# Subshell to contain the directory change
(
cd ${REPO_ROOT}/test/pki &&
mkdir -p "${domainName}" &&
go tool minica -domains "${domainName}" &&
cd "${domainName}" &&
chmod 666 *
)
fi
}

5
test/palemoon/README.md Normal file
View File

@@ -0,0 +1,5 @@
# Pale Moon CI tests
Pale Moon has exposed [some pretty bad bugs](https://anubis.techaro.lol/blog/release/v1.21.1#fix-event-loop-thrashing-when-solving-a-proof-of-work-challenge) in Anubis. As such, we're running Pale Moon against Anubis in CI to ensure that it keeps working.
This test is a fork of [dtinth/xtigervnc-docker](https://github.com/dtinth/xtigervnc-docker) but focused on Pale Moon.

View File

@@ -0,0 +1,50 @@
services:
display:
image: ghcr.io/techarohq/ci-images/xserver:latest
pull_policy: always
ports:
- 5900:5900
anubis:
image: ko.local/anubis
environment:
BIND: ":3000"
TARGET: http://$TARGET
POLICY_FNAME: /cfg/anubis.yaml
SLOG_LEVEL: DEBUG
volumes:
- ../anubis:/cfg
depends_on:
- relayd
relayd:
image: ghcr.io/xe/x/relayd
environment:
BIND: :443
CERT_DIR: /techaro/pki
CERT_FNAME: cert.pem
KEY_FNAME: key.pem
PROXY_TO: http://anubis:3000
volumes:
- ../../pki/relayd:/techaro/pki:ro
# novnc:
# image: geek1011/easy-novnc
# command: -a :5800 -h display --no-url-password
# ports:
# - 5800:5800
palemoon:
platform: linux/amd64
init: true
image: ghcr.io/techarohq/ci-images/palemoon:latest
command: sleep inf
environment:
DISPLAY: display:0
volumes:
- ../../pki:/usr/local/share/ca-certificates/minica:ro
- ../scripts:/hack/scripts:ro
depends_on:
- anubis
- relayd
- display

30
test/palemoon/amd64/test.sh Executable file
View File

@@ -0,0 +1,30 @@
#!/usr/bin/env bash
export VERSION=$GITHUB_COMMIT-test
export KO_DOCKER_REPO=ko.local
function capture_vnc_snapshots() {
sudo apt-get update && sudo apt-get install -y gvncviewer
mkdir -p ./var
while true; do
timestamp=$(date +"%Y%m%d%H%M%S")
gvnccapture localhost:0 ./var/snapshot_$timestamp.png 2>/dev/null
sleep 1
done
}
source ../../lib/lib.sh
if [ "$GITHUB_ACTIONS" = "true" ]; then
capture_vnc_snapshots &
fi
set -euo pipefail
build_anubis_ko
mint_cert relayd
go run ../../cmd/cipra/ --compose-name $(basename $(pwd))
docker compose down -t 1 || :
docker compose rm -f || :

2
test/palemoon/amd64/var/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
*
!.gitignore

View File

@@ -0,0 +1,12 @@
bots:
- name: palemoon
user_agent_regex: PaleMoon
action: CHALLENGE
challenge:
difficulty: 2
report_as: 2
algorithm: fast
status_codes:
CHALLENGE: 401
DENY: 403

View File

@@ -0,0 +1,44 @@
services:
display:
image: ghcr.io/techarohq/ci-images/xserver:latest
pull_policy: always
ports:
- 5900:5900
anubis:
image: ko.local/anubis
environment:
BIND: ":3000"
TARGET: http://$TARGET
POLICY_FNAME: /cfg/anubis.yaml
SLOG_LEVEL: DEBUG
volumes:
- ../anubis:/cfg
relayd:
image: ghcr.io/xe/x/relayd
environment:
BIND: :443
CERT_DIR: /techaro/pki
CERT_FNAME: cert.pem
KEY_FNAME: key.pem
PROXY_TO: http://anubis:3000
volumes:
- ../../pki/relayd:/techaro/pki:ro
# novnc:
# image: geek1011/easy-novnc
# command: -a :5800 -h display --no-url-password
# ports:
# - 5800:5800
palemoon:
platform: linux/386
init: true
image: ghcr.io/techarohq/ci-images/palemoon:latest
command: sleep inf
environment:
DISPLAY: display:0
volumes:
- ../../pki:/usr/local/share/ca-certificates/minica:ro
- ../scripts:/hack/scripts:ro

30
test/palemoon/i386/test.sh Executable file
View File

@@ -0,0 +1,30 @@
#!/usr/bin/env bash
export VERSION=$GITHUB_COMMIT-test
export KO_DOCKER_REPO=ko.local
function capture_vnc_snapshots() {
sudo apt-get update && sudo apt-get install -y gvncviewer
mkdir -p ./var
while true; do
timestamp=$(date +"%Y%m%d%H%M%S")
gvnccapture localhost:0 ./var/snapshot_$timestamp.png 2>/dev/null
sleep 1
done
}
source ../../lib/lib.sh
if [ "$GITHUB_ACTIONS" = "true" ]; then
capture_vnc_snapshots &
fi
set -euo pipefail
build_anubis_ko
mint_cert relayd
go run ../../cmd/cipra/ --compose-name $(basename $(pwd))
docker compose down -t 1 || :
docker compose rm -f || :

2
test/palemoon/i386/var/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
*
!.gitignore

View File

@@ -0,0 +1,103 @@
#!/usr/bin/env bash
set -euo pipefail
CERT_PATH="/usr/local/share/ca-certificates/minica/minica.pem"
CERT_NAME="minica"
TRUST_FLAGS="C,,"
FIREFOX_DIR="$HOME/.mozilla/firefox"
PALEMOON_DIR="$HOME/.moonchild productions/pale moon"
echo "🔄 Updating system CA certificates..."
update-ca-certificates
# 🌀 Trigger Pale Moon to create its profile if needed
if command -v palemoon &>/dev/null; then
echo "🚀 Launching Pale Moon to initialize profile..."
palemoon &>/dev/null &
PALEMOON_PID=$!
# Wait up to 20 seconds for prefs.js to be created
for i in {1..20}; do
set +e
PROFILE_DIR=$(grep Path ~/.moonchild\ productions/pale\ moon/profiles.ini | cut -d= -f2)
PREFS_FILE="$HOME/.moonchild productions/pale moon/$PROFILE_DIR/prefs.js"
if [[ -f "$PREFS_FILE" ]]; then
set -e
echo "✅ prefs.js found at: $PREFS_FILE"
break
fi
sleep 5
done
kill $PALEMOON_PID 2>/dev/null || true
wait $PALEMOON_PID 2>/dev/null || true
if [[ ! -f "$PREFS_FILE" ]]; then
echo "❌ prefs.js not found. Pale Moon did not fully initialize."
exit 1
fi
else
echo "⚠️ Pale Moon is not installed or not in PATH. Skipping profile bootstrap."
fi
echo 'user_pref("security.cert_pinning.enforcement_level", 0);' >>"$PREFS_FILE"
echo "✅ TLS cert validation disabled in Pale Moon profile: $PROFILE_DIR"
# 🔧 Ensure certutil is installed
if ! command -v certutil &>/dev/null; then
if [ -f /etc/debian_version ]; then
echo "🔧 'certutil' not found. Installing via apt..."
apt-get update
apt-get install -y libnss3-tools
else
echo "❌ 'certutil' not found and install is only supported on Debian-based systems."
exit 1
fi
fi
import_cert_to_profiles() {
local base_dir="$1"
local browser_name="$2"
local profile_glob="$3"
if [ ! -d "$base_dir" ]; then
echo "⚠️ $browser_name profile directory not found: $base_dir"
return
fi
echo "📌 Searching for $browser_name profiles in: $base_dir"
local found=0
for profile in "$base_dir"/$profile_glob; do
if [ ! -d "$profile" ]; then
continue
fi
found=1
local db_path="sql:$profile"
echo "🔍 Processing $browser_name profile: $profile"
if certutil -L -d "$db_path" | grep -q "^$CERT_NAME"; then
echo " ✅ Certificate '$CERT_NAME' already exists in profile."
continue
fi
certutil -A -n "$CERT_NAME" -t "$TRUST_FLAGS" -i "$CERT_PATH" -d "$db_path"
echo " Added certificate '$CERT_NAME' to $browser_name profile."
done
if [ "$found" -eq 0 ]; then
echo "⚠️ No $browser_name profiles found in: $base_dir"
fi
}
import_cert_to_profiles "$FIREFOX_DIR" "Firefox" "*.default*"
import_cert_to_profiles "$PALEMOON_DIR" "Pale Moon" "*.*"
echo "✅ Done. Firefox and Pale Moon profiles updated with '$CERT_NAME' certificate."