Add strfry policies and members-only LDAP policy #196

Merged
raucao merged 5 commits from feature/strfry_policies into master 2024-06-11 20:10:34 +00:00
6 changed files with 133 additions and 7 deletions

View File

@ -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 = {

View File

@ -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

View File

@ -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 {

View File

@ -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<LdapConfig> = 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;

33
extras/strfry/strfry-policy.ts Executable file
View File

@ -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);
}

View File

@ -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);
}