120 lines
4.4 KiB
YAML
120 lines
4.4 KiB
YAML
app:
|
|||
|
|
id: nostr-vpn
|
||
|
|
name: Nostr VPN (paid exit)
|
||
|
|
version: 1.0.0
|
||
|
|
# Pinned commit, not a tag -- upstream has no release tags yet. Re-pin
|
||
|
|
# deliberately in docker/nostr-vpn/Dockerfile's NVPN_COMMIT build arg; see
|
||
|
|
# docs/nostr-vpn-integration-plan.md for the Phase 0 feasibility log this
|
||
|
|
# pin was verified against.
|
||
|
|
upstream:
|
||
|
|
kind: github
|
||
|
|
repo: mmalmi/nostr-vpn
|
||
|
|
description: |
|
||
|
|
Sells spare bandwidth as a Nostr-discovered, Cashu-metered paid exit
|
||
|
|
(github.com/mmalmi/nostr-vpn). Runs rootless in its own network
|
||
|
|
namespace (pasta) -- NET_ADMIN/NET_RAW are scoped to that netns, never
|
||
|
|
the host. Seller mode defaults OFF (upstream's own `paid_exit.enabled`
|
||
|
|
default); turning it on is a separate step (Phase 3 UI, not yet built).
|
||
|
|
|
||
|
|
This replaces the old root-mode integration (image-recipe's
|
||
|
|
nostr-vpn.service running `nvpn daemon` as root, auto-enabled on first
|
||
|
|
login via rpc/auth.rs) that broke the rootless/no-OS-reliance
|
||
|
|
invariant. That old path and its RPC TOML-rewriting code
|
||
|
|
(rpc/vpn.rs::handle_vpn_add_participant) are a separate, higher-risk
|
||
|
|
removal -- not done here, since it's wired into every node's login
|
||
|
|
flow today, not just this app.
|
||
|
|
category: money
|
||
|
|
|
||
|
|
container:
|
||
|
|
build:
|
||
|
|
context: /opt/archipelago/docker/nostr-vpn
|
||
|
|
dockerfile: Dockerfile
|
||
|
|
tag: localhost/nostr-vpn:local
|
||
|
|
network: pasta
|
||
|
|
# Image has no image-level ENTRYPOINT/CMD (see Dockerfile) -- both this
|
||
|
|
# app and nostr-vpn-web point the shared seed-config entrypoint at
|
||
|
|
# different binaries/args.
|
||
|
|
entrypoint: ["/usr/local/bin/archy-nvpn-entrypoint.sh"]
|
||
|
|
custom_args:
|
||
|
|
- /usr/local/bin/nvpn
|
||
|
|
- daemon
|
||
|
|
- --config
|
||
|
|
- /data/config/nvpn/config.toml
|
||
|
|
|
||
|
|
dependencies:
|
||
|
|
- storage: 1Gi
|
||
|
|
|
||
|
|
resources:
|
||
|
|
memory_limit: 256Mi
|
||
|
|
|
||
|
|
security:
|
||
|
|
# NET_ADMIN/NET_RAW: TUN device + the exit forwarding/NAT nvpn installs
|
||
|
|
# itself inside its own netns (nvpn-exit-forward-in/out, nvpn-exit-masq,
|
||
|
|
# the MSS clamp) -- confirmed working rootless in Phase 0 testing, with
|
||
|
|
# no capabilities beyond these two plus the sysctl below. Host iptables
|
||
|
|
# and routes were confirmed untouched.
|
||
|
|
capabilities: [NET_ADMIN, NET_RAW]
|
||
|
|
# false: not verified read-only-root-compatible in Phase 0 testing (the
|
||
|
|
# working run flags there didn't include --read-only). nvpn's own state
|
||
|
|
# (config/identity/wallet) lives on the /data volume either way.
|
||
|
|
readonly_root: false
|
||
|
|
no_new_privileges: true
|
||
|
|
network_policy: isolated
|
||
|
|
|
||
|
|
# Rootless /proc/sys is read-only, so forwarding can only be set at
|
||
|
|
# container-create time via this primitive (added for exactly this app --
|
||
|
|
# see commit e42bd26). nvpn only *reads* ip_forward and writes it when 0,
|
||
|
|
# so setting it here once at create is enough; nvpn's own cleanup path
|
||
|
|
# leaves it alone.
|
||
|
|
sysctls:
|
||
|
|
net.ipv4.ip_forward: "1"
|
||
|
|
|
||
|
|
devices:
|
||
|
|
- /dev/net/tun
|
||
|
|
|
||
|
|
ports:
|
||
|
|
# Paid-exit buyers dial this directly from the open internet to pay for
|
||
|
|
# bandwidth -- it's the whole point of the app, not an admin surface,
|
||
|
|
# and it speaks nvpn's own FIPS UDP wire protocol, not HTTP, so the app
|
||
|
|
# gate cannot front it. 51822, not upstream's default 51820: that
|
||
|
|
# collides with archipelago-wg (kernel WireGuard) on fleet nodes --
|
||
|
|
# found running both side by side in Phase 0 testing.
|
||
|
|
- host: 51822
|
||
|
|
container: 51822
|
||
|
|
protocol: udp
|
||
|
|
auth: none
|
||
|
|
auth_rationale: >-
|
||
|
|
FIPS UDP transport for paid-exit buyers. Anonymous by design (not
|
||
|
|
HTTP), and the seller is off by default (paid_exit.enabled=false)
|
||
|
|
until an operator explicitly turns on selling, so exposure here
|
||
|
|
alone grants no access to anything.
|
||
|
|
|
||
|
|
volumes:
|
||
|
|
# Adopts whatever a node already has under the old root-mode path
|
||
|
|
# (nostr-vpn.service wrote here too) -- an identity, wallet balance, or
|
||
|
|
# pending Cashu credit must survive this migration, not reset.
|
||
|
|
- type: bind
|
||
|
|
source: /var/lib/archipelago/nostr-vpn
|
||
|
|
target: /data
|
||
|
|
options: [rw]
|
||
|
|
|
||
|
|
environment:
|
||
|
|
- NVPN_LISTEN_PORT=51822
|
||
|
|
|
||
|
|
health_check:
|
||
|
|
type: exec
|
||
|
|
endpoint: nvpn status
|
||
|
|
interval: 30s
|
||
|
|
timeout: 10s
|
||
|
|
retries: 3
|
||
|
|
|
||
|
|
metadata:
|
||
|
|
category: money
|
||
|
|
tier: optional
|
||
|
|
author: mmalmi
|
||
|
|
repo: https://github.com/mmalmi/nostr-vpn
|
||
|
|
features:
|
||
|
|
- Sell spare bandwidth as a Cashu-metered Nostr paid exit
|
||
|
|
- Rootless: own network namespace, no host network access
|
||
|
|
- Seller mode off by default
|