Remove complex rootless container setup that was causing dependency issues with user-runtime-dir services. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
214 lines
5.9 KiB
Nix
214 lines
5.9 KiB
Nix
# SearXNG Module (Podman)
|
|
# Provides: Private meta-search engine running in containers
|
|
#
|
|
# Usage:
|
|
# myModules.searxng = {
|
|
# enable = true;
|
|
# port = 8888;
|
|
# domain = "search.example.com";
|
|
# };
|
|
|
|
{
|
|
config,
|
|
lib,
|
|
pkgs,
|
|
...
|
|
}:
|
|
|
|
let
|
|
cfg = config.myModules.searxng;
|
|
anubisPolicy = pkgs.writeText "anubis-policy.yml" ''
|
|
bots:
|
|
- name: "Allow OpenSearch"
|
|
action: ALLOW
|
|
path_regex: ".*opensearch\\.xml.*"
|
|
- name: "Catch-All"
|
|
user_agent_regex: ".*"
|
|
action: CHALLENGE
|
|
'';
|
|
|
|
faviconsConfig = pkgs.writeText "favicons.toml" ''
|
|
[favicons]
|
|
cfg_schema = 1
|
|
|
|
[favicons.cache]
|
|
db_url = "/var/cache/searxng/faviconcache.db"
|
|
LIMIT_TOTAL_BYTES = 2147483648
|
|
|
|
[favicons.proxy.resolver_map]
|
|
google = "searx.favicons.resolver.google_resolver"
|
|
duckduckgo = "searx.favicons.resolver.duckduckgo_resolver"
|
|
'';
|
|
in
|
|
{
|
|
options.myModules.searxng = {
|
|
enable = lib.mkEnableOption "SearXNG meta-search engine";
|
|
|
|
port = lib.mkOption {
|
|
type = lib.types.port;
|
|
default = 8888;
|
|
description = "Port to expose SearXNG on localhost";
|
|
};
|
|
|
|
domain = lib.mkOption {
|
|
type = lib.types.str;
|
|
example = "search.example.com";
|
|
description = "Public domain name for SearXNG";
|
|
};
|
|
|
|
instanceName = lib.mkOption {
|
|
type = lib.types.str;
|
|
default = "SearXNG";
|
|
description = "Name displayed in the search interface";
|
|
};
|
|
|
|
donations = lib.mkOption {
|
|
type = lib.types.attrsOf lib.types.str;
|
|
default = { };
|
|
description = "Map of donation platform names to URLs";
|
|
};
|
|
};
|
|
|
|
config = lib.mkIf cfg.enable {
|
|
# Ensure Podman is enabled
|
|
myModules.podman.enable = true;
|
|
|
|
# Create bridge network
|
|
systemd.services.create-searxng-network = {
|
|
description = "Create SearXNG podman network";
|
|
after = [ "network-online.target" ];
|
|
requires = [ "network-online.target" ];
|
|
serviceConfig = {
|
|
Type = "oneshot";
|
|
RemainAfterExit = true;
|
|
};
|
|
path = [ pkgs.podman ];
|
|
script = ''
|
|
if ! podman network exists searxng-net 2>/dev/null; then
|
|
podman network create searxng-net --subnet 10.89.2.0/24
|
|
fi
|
|
'';
|
|
};
|
|
|
|
# Valkey Container (Cache)
|
|
virtualisation.oci-containers.containers."searxng-valkey" = {
|
|
image = "docker.io/valkey/valkey:alpine";
|
|
cmd = [ "valkey-server" "--save" "" "--appendonly" "no" ];
|
|
extraOptions = [
|
|
"--network=searxng-net"
|
|
"--network-alias=valkey"
|
|
];
|
|
};
|
|
|
|
# SearXNG Container
|
|
virtualisation.oci-containers.containers."searxng" = {
|
|
image = "docker.io/searxng/searxng:latest";
|
|
environment = {
|
|
SEARXNG_BASE_URL = "https://${cfg.domain}";
|
|
SEARXNG_REDIS_URL = "valkey://valkey:6379";
|
|
SEARXNG_URL_BASE = "https://${cfg.domain}";
|
|
GRANIAN_HOST = "0.0.0.0";
|
|
};
|
|
environmentFiles = [
|
|
config.sops.templates."searxng.env".path
|
|
];
|
|
extraOptions = [
|
|
"--network=searxng-net"
|
|
"--network-alias=searxng"
|
|
"--cap-drop=ALL"
|
|
"--cap-add=CHOWN"
|
|
"--cap-add=SETGID"
|
|
"--cap-add=SETUID"
|
|
"--cap-add=DAC_OVERRIDE"
|
|
"--dns=9.9.9.9"
|
|
"--dns=1.1.1.1"
|
|
];
|
|
volumes = [
|
|
"${config.sops.templates."searxng_settings.yml".path}:/etc/searxng/settings.yml:ro"
|
|
"${faviconsConfig}:/etc/searxng/favicons.toml:ro"
|
|
"searxng-cache:/var/cache/searxng"
|
|
];
|
|
dependsOn = [ "searxng-valkey" ];
|
|
};
|
|
|
|
# Anubis Container (AI Firewall)
|
|
virtualisation.oci-containers.containers."searxng-anubis" = {
|
|
image = "ghcr.io/techarohq/anubis:latest";
|
|
ports = [ "127.0.0.1:${toString cfg.port}:8080" ];
|
|
environment = {
|
|
TARGET = "http://searxng:8080";
|
|
BIND = ":8080";
|
|
POLICY_FNAME = "/etc/anubis/policy.yml";
|
|
};
|
|
extraOptions = [
|
|
"--network=searxng-net"
|
|
"--network-alias=searxng-anubis"
|
|
];
|
|
volumes = [
|
|
"${anubisPolicy}:/etc/anubis/policy.yml:ro"
|
|
];
|
|
dependsOn = [ "searxng" ];
|
|
};
|
|
|
|
# SOPS templates
|
|
sops.templates."searxng.env" = {
|
|
content = ''
|
|
SEARXNG_SECRET_KEY=${config.sops.placeholder.searxng_secret_key}
|
|
'';
|
|
};
|
|
|
|
sops.templates."searxng_settings.yml" = {
|
|
content = ''
|
|
use_default_settings: true
|
|
|
|
general:
|
|
debug: false
|
|
instance_name: "${cfg.instanceName}"
|
|
contact_url: false
|
|
issue_url: false
|
|
donation_url: ${if cfg.donations ? "Monero" then "\"${cfg.donations.Monero}\"" else "false"}
|
|
donations:
|
|
${lib.concatStringsSep "\n " (
|
|
lib.mapAttrsToList (name: url: "${name}: \"${url}\"") cfg.donations
|
|
)}
|
|
|
|
outgoing:
|
|
request_timeout: 10.0
|
|
connect_timeout: 6.0
|
|
max_retry_count: 3
|
|
enable_ipv6: false
|
|
|
|
search:
|
|
safe_search: 0
|
|
favicon_resolver: "google"
|
|
autocomplete: "google"
|
|
default_lang: "en-US"
|
|
formats:
|
|
- html
|
|
- json
|
|
|
|
server:
|
|
port: 8080
|
|
bind_address: "0.0.0.0"
|
|
secret_key: "${config.sops.placeholder.searxng_secret_key}"
|
|
limiter: true
|
|
image_proxy: true
|
|
public_instance: true
|
|
|
|
default_http_headers:
|
|
Content-Security-Policy: "upgrade-insecure-requests; default-src 'none'; script-src 'self'; style-src 'self' 'sha256-/ldGxQqxNIMRftg3AGsPF+F281wiBPECUDcL2RJkxdU='; form-action 'self' https://github.com/searxng/searxng/issues/new; font-src 'self'; frame-ancestors 'self'; img-src 'self' data:; connect-src 'self' https://overpass-api.de; manifest-src 'self'"
|
|
|
|
ui:
|
|
default_theme: simple
|
|
default_theme_style: dark
|
|
static_use_hash: true
|
|
|
|
redis:
|
|
url: valkey://valkey:6379/0
|
|
'';
|
|
};
|
|
|
|
# Secret definitions
|
|
sops.secrets.searxng_secret_key = { };
|
|
};
|
|
}
|