diff --git a/app/services/ldap_service.rb b/app/services/ldap_service.rb index ab7dd94..8364eb5 100644 --- a/app/services/ldap_service.rb +++ b/app/services/ldap_service.rb @@ -101,7 +101,7 @@ class LdapService < ApplicationService dn = "ou=#{ou},cn=users,#{ldap_suffix}" aci = <<-EOS -(target="ldap:///cn=*,ou=#{ou},cn=users,#{ldap_suffix}")(targetattr="cn || sn || uid || mail || userPassword || nsRole || objectClass") (version 3.0; acl "service-#{ou.gsub(".", "-")}-read-search"; allow (read,search) userdn="ldap:///uid=service,ou=#{ou},cn=applications,#{ldap_suffix}";) +(target="ldap:///cn=*,ou=#{ou},cn=users,#{ldap_suffix}")(targetattr="cn || sn || uid || userPassword || mail || mailRoutingAddress || serviceEnabled || nostrKey || nsRole || objectClass") (version 3.0; acl "service-#{ou.gsub(".", "-")}-read-search"; allow (read,search) userdn="ldap:///uid=service,ou=#{ou},cn=applications,#{ldap_suffix}";) EOS attrs = { diff --git a/docker-compose.yml b/docker-compose.yml index ec7069f..d38ed17 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -107,16 +107,24 @@ services: - minio - redis - nostr-relay: - image: pluja/strfry:latest + strfry: + image: gitea.kosmos.org/kosmos/strfry-deno:1.1.1 volumes: - ./docker/strfry/strfry.conf:/etc/strfry.conf - - strfry-data:/app/strfry-db + - ./extras/strfry/ldap-policy.ts:/opt/ldap-policy.ts + - ./extras/strfry/strfry-policy.ts:/opt/strfry-policy.ts + - ./extras/strfry/strfry-sync.ts:/opt/strfry-sync.ts + - strfry-data:/var/lib/strfry networks: - external_network - internal_network ports: - "4777:7777" + environment: + LDAP_URL: 'ldap://ldap:3389' + LDAP_BIND_DN: 'cn=Directory Manager' + LDAP_PASSWORD: passthebutter + LDAP_SEARCH_DN: 'ou=kosmos.org,cn=users,dc=kosmos,dc=org' # phpldapadmin: # image: osixia/phpldapadmin:0.9.0 diff --git a/docker/strfry/strfry.conf b/docker/strfry/strfry.conf index 9342387..2a2bd09 100644 --- a/docker/strfry/strfry.conf +++ b/docker/strfry/strfry.conf @@ -3,7 +3,7 @@ ## # Directory that contains the strfry LMDB database (restart required) -db = "./strfry-db/" +db = "/var/lib/strfry/" dbParams { # Maximum number of threads/processes that can simultaneously have LMDB transactions open (restart required) @@ -54,7 +54,7 @@ relay { info { # NIP-11: Name of this server. Short/descriptive (< 30 characters) - name = "akkounts-nostr-relay" + name = "Akkounts Nostr Relay" # NIP-11: Detailed information about relay, free-form description = "Local strfry instance for akkounts development" @@ -86,7 +86,7 @@ relay { writePolicy { # If non-empty, path to an executable script that implements the writePolicy plugin logic - plugin = "" + plugin = "/opt/strfry-policy.ts" } compression { diff --git a/extras/strfry/ldap-policy.ts b/extras/strfry/ldap-policy.ts new file mode 100644 index 0000000..a200639 --- /dev/null +++ b/extras/strfry/ldap-policy.ts @@ -0,0 +1,46 @@ +import type { Policy } from 'https://gitlab.com/soapbox-pub/strfry-policies/-/raw/develop/mod.ts'; +import { Client } from 'npm:ldapts'; + +interface LdapConfig { + url: string; + bindDN: string; + password: string; + searchDN: string; +} + +const ldapPolicy: Policy = async (msg, opts) => { + const client = new Client({ url: opts.url }); + const { pubkey, kind, tags } = msg.event; + let out = { id: msg.event.id } + + try { + await client.bind(opts.bindDN, opts.password); + + const { searchEntries } = await client.search(opts.searchDN, { + filter: `(nostrKey=${pubkey})`, + attributes: ['nostrKey'] + }); + + const memberKey = searchEntries[0]?.nostrKey; + + const accepted = (memberKey === pubkey); + // TODO if kind is 9735, check that "description" tag contains valid 9734 event, + // signed by memberKey and with "p" tag being the same as pubkey (receipt sender) + + if (accepted) { + out['action'] = 'accept'; + out['msg'] = ''; + } else { + out['action'] = 'reject'; + out['msg'] = 'Only members can publish notes on this relay'; + } + } catch (ex) { + out['action'] = 'reject'; + out['msg'] = 'Auth service temporarily unavailable'; + } finally { + await client.unbind(); + return out; + } +}; + +export default ldapPolicy; diff --git a/extras/strfry/strfry-policy.ts b/extras/strfry/strfry-policy.ts new file mode 100755 index 0000000..15fafe9 --- /dev/null +++ b/extras/strfry/strfry-policy.ts @@ -0,0 +1,33 @@ +#!/bin/sh +//bin/true; exec deno run -A "$0" "$@" +import { + antiDuplicationPolicy, + hellthreadPolicy, + pipeline, + rateLimitPolicy, + readStdin, + writeStdout, +} from 'https://gitlab.com/soapbox-pub/strfry-policies/-/raw/develop/mod.ts'; +import ldapPolicy from './ldap-policy.ts'; +import { load } from "https://deno.land/std@0.224.0/dotenv/mod.ts"; + +const dirname = new URL('.', import.meta.url).pathname; +await load({ envPath: `${dirname}/.env`, export: true }); + +const ldapConfig = { + url: Deno.env.get("LDAP_URL"), + bindDN: Deno.env.get("LDAP_BIND_DN"), + password: Deno.env.get("LDAP_PASSWORD"), + searchDN: Deno.env.get("LDAP_SEARCH_DN"), +} + +for await (const msg of readStdin()) { + const result = await pipeline(msg, [ + [hellthreadPolicy, { limit: 10 }], + [antiDuplicationPolicy, { ttl: 60000, minLength: 50 }], + [rateLimitPolicy, { whitelist: ['127.0.0.1'] }], + [ldapPolicy, ldapConfig], + ]); + + writeStdout(result); +} diff --git a/extras/strfry/strfry-sync.ts b/extras/strfry/strfry-sync.ts new file mode 100644 index 0000000..4b24540 --- /dev/null +++ b/extras/strfry/strfry-sync.ts @@ -0,0 +1,39 @@ +import { load } from "https://deno.land/std@0.224.0/dotenv/mod.ts"; +import { Client } from 'npm:ldapts'; + +const dirname = new URL('.', import.meta.url).pathname; +await load({ envPath: `${dirname}/.env`, export: true }); + +const opts = { + url: Deno.env.get("LDAP_URL"), + bindDN: Deno.env.get("LDAP_BIND_DN"), + password: Deno.env.get("LDAP_PASSWORD"), + searchDN: Deno.env.get("LDAP_SEARCH_DN"), + relayUrl: Deno.args[0] +} + +const client = new Client({ url: opts.url }); + +try { + await client.bind(opts.bindDN, opts.password); + + const { searchEntries } = await client.search(opts.searchDN, { + filter: `(nostrKey=*)`, + attributes: ['nostrKey'] + }); + + const pubkeys = searchEntries.map(e => e.nostrKey); + const filter = JSON.stringify({ authors: pubkeys }); + + const p = Deno.run({ cmd: [ + "strfry", "sync", opts.relayUrl, + "--dir", "down", "--filter", filter + ]}); + + const result = await p.status(); + + Deno.exit(result.code); +} catch (ex) { + console.error(ex); + Deno.exit(1); +}