diff --git a/README.md b/README.md index 7f4fb4f..a2487ae 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,8 @@ User ⇄ XMPP client ⇄ XMPP Server ⇄ **XMPP Bot** ⇄ REST A ## Installation +An [Ansible role](/docs/ansible/xmpp-bot/README.md) is provided, but you can also use following commands: + - Install [Node.js](https://nodejs.org/): ```shell curl -sL https://deb.nodesource.com/setup_10.x | bash - @@ -127,6 +129,11 @@ User ⇄ XMPP client ⇄ XMPP Server ⇄ **XMPP Bot** ⇄ REST A - `action` among enumeration: - `outgoing_webhook` will execute a request to corresponding webhook with `args` as webhook code +## FAQ + +- *XMPP server is using a self signed certificate, how can i run service?* + You can allow insecure TLS connections and HTTPS requests by adding `Environment=NODE_TLS_REJECT_UNAUTHORIZED=0` in /usr/lib/systemd/system/xmpp-bot.service. + ## Credits - **[Nioc](https://github.com/nioc/)** - _Initial work_ diff --git a/docs/ansible/xmpp-bot/README.md b/docs/ansible/xmpp-bot/README.md new file mode 100644 index 0000000..a6aaef0 --- /dev/null +++ b/docs/ansible/xmpp-bot/README.md @@ -0,0 +1,57 @@ +Ansible Role: XMPP Bot +====================== + +Install XMPP Bot: + +- install [Node.js](https://nodejs.org/), +- install npm, +- download archive, +- install dependencies, +- create service user, +- set [configuration](https://github.com/nioc/xmpp-bot#configuration), +- add as a systemd service. + +Requirements +------------ + +- Ansible >= 2.9, +- a working XMPP server. + +Role Variables +-------------- + +These variables are installation related and should be checked/updated before use: + +- `xmppbot_install_nodejs`: Does NodeJS should be installed, set `false` if already present, default: `true`, +- `nodejs_repo`: NodeJS version to install, default: `node_12.x`. +- `domain`: your domain name (not a role variable but **must be set** in your playbook/host), no default, + +For variables in `webhooks config`, `XMPP server config`, `outgoing webhooks config` sections, please see [configuration](https://github.com/nioc/xmpp-bot#configuration). + + +Dependencies +------------ + +None. + +Example Playbook +---------------- + + - hosts: servers + vars: + domain: mydomain.ltd + roles: + - name: xmpp-bot + xmppbot_incoming_webhooks: + - path: /webhooks/alerting + action: send_xmpp_message + +License +------- + +AGPL-3.0-or-later + +Author Information +------------------ + +This role was created in 2020 by [Nioc](https://github.com/nioc). diff --git a/docs/ansible/xmpp-bot/defaults/main.yml b/docs/ansible/xmpp-bot/defaults/main.yml new file mode 100644 index 0000000..d79ce56 --- /dev/null +++ b/docs/ansible/xmpp-bot/defaults/main.yml @@ -0,0 +1,57 @@ +--- +# installation: +xmppbot_install_nodejs: true +nodejs_repo: node_12.x +xmppbot_version: HEAD +xmppbot_git_url: https://github.com/nioc/xmpp-bot.git +# global config: +xmppbot_dir: /usr/local/bin/xmpp-bot +xmppbot_user: xmpp-bot +xmppbot_log_dir: /var/log/xmpp-bot +# webhooks config: +xmppbot_webhook_path: '/webhooks' +xmppbot_webhook_port: '8000' +xmppbot_webhook_port_ssl: '8001' +xmppbot_webhook_certpath: /etc/ssl/certs/ssl-cert-snakeoil.pem +xmppbot_webhook_keypath: /etc/ssl/private/ssl-cert-snakeoil.key +xmppbot_webhook_users: + login1: 1pass + login2: 2pass +xmppbot_incoming_webhooks: +- path: /webhooks/w1 + action: send_xmpp_message +- path: /webhooks/grafana + action: send_xmpp_template + args: + destination: "grafana@conference.domain-xmpp.ltd" + type: "groupchat" + template: "${title}\r\n${message}\r\n${evalMatches[].metric}: ${evalMatches[].value}\r\n${imageUrl}" +# XMPP server config: +xmppbot_xmpp_server: + service: xmpps://domain-xmpp.ltd:5223 + domain: domain-xmpp.ltd + username: bot@domain-xmpp.ltd + password: botPass + rooms: + - id: roomname@conference.domain-xmpp.ltd + password: 'null' +xmppbot_xmpp_hooks: +- room: bot@domain-xmpp.ltd + action: outgoing_webhook + args: '["w1"]' +- room: roomname@conference.domain-xmpp.ltd + action: outgoing_webhook + args: '["w1"]' +xmppbot_xmpp_error_reply: Oops, something went wrong :( +xmppbot_xmpp_resource: botservice +# outgoing webhooks config: +xmppbot_outgoing_webhooks: +- code: 'w1' + url: 'https://domain.ltd:port/path/resource?parameter1=value1' + timeout: '500' + strictSSL: 'true' + contentType: 'application/json' + authMethod: 'basic' + user: 'user3' + password: '3pass' + bearer: 'null' \ No newline at end of file diff --git a/docs/ansible/xmpp-bot/meta/main.yml b/docs/ansible/xmpp-bot/meta/main.yml new file mode 100644 index 0000000..2e30339 --- /dev/null +++ b/docs/ansible/xmpp-bot/meta/main.yml @@ -0,0 +1,9 @@ +galaxy_info: + author: Nioc + description: Install XMPP bot + issue_tracker_url: https://github.com/nioc/xmpp-bot/issues + license: license (AGPL-3.0-or-later) + min_ansible_version: 2.9 + galaxy_tags: [] + +dependencies: [] diff --git a/docs/ansible/xmpp-bot/tasks/main.yml b/docs/ansible/xmpp-bot/tasks/main.yml new file mode 100644 index 0000000..0e46e83 --- /dev/null +++ b/docs/ansible/xmpp-bot/tasks/main.yml @@ -0,0 +1,85 @@ +--- +- name: Install and configure NodeJS LTS + include_tasks: nodejs.yml + when: xmppbot_install_nodejs + +- name: Install/update npm package globally + become: yes + npm: + name: npm + state: latest + global: yes + +- name: Get XMPP bot code from Git repo + become: yes + git: + repo: '{{xmppbot_git_url}}' + version: '{{xmppbot_version}}' + dest: '{{xmppbot_dir}}/' + force: yes + +- name: Install XMPP bot based on package.json + become: yes + npm: + path: '{{xmppbot_dir}}' + production: yes + +- name: Add XMPP bot user "{{xmppbot_user}}" + become: yes + user: + name: '{{xmppbot_user}}' + system: yes + shell: /bin/false + +- name: Set configuration file + become: yes + template: + src: config.json.j2 + dest: '{{xmppbot_dir}}/lib/config/config.json' + +- name: Set permissions + become: yes + file: + path: '{{xmppbot_dir}}/' + state: directory + owner: '{{xmppbot_user}}' + group: '{{xmppbot_user}}' + recurse: yes + +- name: Creates XMPP bot logs folder + become: yes + file: + path: '{{xmppbot_log_dir}}' + state: directory + owner: '{{xmppbot_user}}' + group: '{{xmppbot_user}}' + +- name: Create XMPP bot service + become: yes + copy: + src: '{{xmppbot_dir}}/docs/xmpp-bot.service' + dest: /usr/lib/systemd/system/xmpp-bot.service + remote_src: yes + +- name: Tune service (dir and running user) + become: yes + lineinfile: + path: /usr/lib/systemd/system/xmpp-bot.service + regexp: '{{item.regexp}}' + line: '{{item.line}}' + state: present + with_items: + - regexp: '^User=' + line: 'User={{xmppbot_user}}' + - regexp: '^WorkingDirectory=' + line: 'WorkingDirectory={{xmppbot_dir}}' + - regexp: '^ExecStart=' + line: 'ExecStart=/usr/bin/node {{xmppbot_dir}}/lib/server.js' + +- name: Enable XMPP bot service + become: yes + systemd: + name: xmpp-bot + enabled: yes + state: started + daemon_reload: yes \ No newline at end of file diff --git a/docs/ansible/xmpp-bot/tasks/nodejs.yml b/docs/ansible/xmpp-bot/tasks/nodejs.yml new file mode 100644 index 0000000..cd51899 --- /dev/null +++ b/docs/ansible/xmpp-bot/tasks/nodejs.yml @@ -0,0 +1,23 @@ +--- +- name: Add NodeSource signing key + become: yes + apt_key: + url: https://deb.nodesource.com/gpgkey/nodesource.gpg.key + state: present + +- name: Add NodeSource APT repository + become: yes + apt_repository: + repo: deb https://deb.nodesource.com/{{nodejs_repo}} {{distro}} main + filename: nodesource + update_cache: yes + state: present + vars: + - distro: "{{ ansible_distribution_release | default('buster') }}" + +- name: Install Node.js packages + become: yes + apt: + name: nodejs + state: present + cache_valid_time: 3600 \ No newline at end of file diff --git a/docs/ansible/xmpp-bot/templates/config.json.j2 b/docs/ansible/xmpp-bot/templates/config.json.j2 new file mode 100644 index 0000000..fa58a0a --- /dev/null +++ b/docs/ansible/xmpp-bot/templates/config.json.j2 @@ -0,0 +1,99 @@ +{ + "logger": { + "level": "debug", + "file": { + "active": false, + "pattern": "%d %p %m%n", + "path": "{{xmppbot_log_dir}}/", + "filename": "xmpp-bot.log" + }, + "console": { + "active": false, + "coloured": true + }, + "stdout": { + "active": true, + "pattern": "%p %m" + } + }, + "webhooksListener": { + "path": "{{xmppbot_webhook_path}}", + "port": {{xmppbot_webhook_port}}, + "ssl": { + "port": {{xmppbot_webhook_port_ssl}}, + "certPath": "{{xmppbot_webhook_certpath}}", + "keyPath": "{{xmppbot_webhook_keypath}}" + }, + "users": [{% for login, passwd in xmppbot_webhook_users.iteritems() %} + + { + "login": "{{login}}", + "password": "{{passwd}}" + }{% if not loop.last %},{% endif %}{% endfor %} + + ], + "accessLog": { + "active": true, + "path": "{{xmppbot_log_dir}}/", + "filename": "webhook.log" + } + }, + "xmppServer": { + "service": "{{xmppbot_xmpp_server.service}}", + "domain": "{{xmppbot_xmpp_server.domain}}", + "username": "{{xmppbot_xmpp_server.username}}", + "password": "{{xmppbot_xmpp_server.password}}", + "resource": "{{xmppbot_xmpp_resource}}", + "errorReply": "{{xmppbot_xmpp_error_reply}}", + "rooms": [{% for room in xmppbot_xmpp_server.rooms %} + + { + "id": "{{room.id}}", + "password": {{room.password}} + }{% if not loop.last %},{% endif %}{% endfor %} + + ] + }, + "incomingWebhooks": [{% for webhook in xmppbot_incoming_webhooks %} + + { + "path": "{{webhook.path}}", + {% if webhook.args is defined -%} + "args": { {% for key, value in webhook.args.iteritems() %} + + "{{key}}": "{{value}}"{% if not loop.last %},{% endif %}{% endfor %} + + }, + {% endif -%} + {% if webhook.template is defined -%} + "template": {{webhook.template|tojson}}, + {% endif -%} + "action": "{{webhook.action}}" + }{% if not loop.last %},{% endif %}{% endfor %} + + ], + "xmppHooks": [{% for xmpp_hook in xmppbot_xmpp_hooks %} + + { + "room": "{{xmpp_hook.room}}", + "action": "{{xmpp_hook.action}}", + "args": {{xmpp_hook.args}} + }{% if not loop.last %},{% endif %}{% endfor %} + + ], + "outgoingWebhooks": [{% for outgoing_webhook in xmppbot_outgoing_webhooks %} + + { + "code": "{{outgoing_webhook.code}}", + "url": "{{outgoing_webhook.url}}", + "timeout": {{outgoing_webhook.timeout}}, + "strictSSL": {{outgoing_webhook.strictSSL}}, + "contentType": "{{outgoing_webhook.contentType}}", + "authMethod": "{{outgoing_webhook.authMethod}}", + "user": "{{outgoing_webhook.user}}", + "password": "{{outgoing_webhook.password}}", + "bearer": {{outgoing_webhook.bearer}} + }{% if not loop.last %},{% endif %}{% endfor %} + + ] +}