feat: distributed systems

The implementation part is settled, I'll use the serf library.

Edit: On Rust there is an equivalent ruserf library, but it's a one person project, so I'm unsure if I should use it.

Edit: I may implement this with https://iroh.computer instead.

However, I'm really open to suggestions about the configuration on the user side. I'd like to have the more versatile, easy to grasp configuration interface for cluster configuration.

Here are the changes to reaction I thought of to enable clustering:

Stream could accept a cmd []string or a cluster type Cluster struct { Bootstrap [][]string, Key string }

Action could accept either a cmd []string or cluster name string

Share bans:

{
  streams: {
    ssh: {
      cmd: ['journalctl', '-n0', '-fu', 'sshd.service'],
      filters: {
        login: {
          regex: [ 'authentication failed: <ip>' ],
          retry: 3,
          retryperiod: "6h",
          actions: {
            ban: "[...]",
            send: {
              cluster: "picasoft",
              msg: "ban <ip>",
            },
            unban: "[...]",
            send: {
              cluster: "picasoft",
              msg: "unban <ip>",
              after: "3h"
            },
          },
        },
      },
    },
    picasoft: {
      cluster: {
        bootstrap: [
          "pica01.picasoft.net:8484",
          "pica02.picasoft.net:8484",
        ],
        //key: "picasoft.keyring",
      },
      filters: {
        // How do we set the formalism for message structure? string-only may be perfect for most use-cases.
        ban: {
          // Here we may need support for multiple patterns (feature not yet asked nor implemented)
          regex: ['ban: <ip>'],
          actions: {
            ban: "[...]",
          },
        },
        unban: {
          // Here we may need support for multiple patterns (feature not yet asked nor implemented)
          regex: ['unban: <ip>'],
          actions: {
            unban: "[...]",
          },
        },
      },
    },
  },
}

Share matches:

{
  streams: {
    local sshregexes = [ 'authentication failed: <ip>' ];
    ssh: {
      cmd: ['journalctl', '-n0', '-fu', 'sshd.service'],
      filters: {
        loginsend: {
          regex: sshregexes,
          actions: {
            send: {
              cluster: "picasoft",
              // self means this instance receives the message as well on the cluster stream below ↓
              self: true,
              msg: "ssh.match <ip>",
            },
          },
        },
      },
    },
    picasoft: {
      cluster: {
        bootstrap: [
          "pica01.picasoft.net:8484",
          "pica02.picasoft.net:8484",
        ],
        // key: "picasoft.keyring",
      },
      filters: {
        sshmatches: {
          regex: ['ssh.match: <ip>'],
          retry: 4,
          retryperiod: "8h",
          actions: {
            ban: "[...]",
            unban: "[...]",
          },
        },
      },
    },
  },
}
Edited by ppom