diff --git a/group_vars/all b/group_vars/all index b5178a6..ba7b9dd 100644 --- a/group_vars/all +++ b/group_vars/all @@ -124,6 +124,9 @@ http_pfx_gitea: /git # URL prefix of LDAP-Account-Manager (web UI for LDAP). http_pfx_lam: /account +# URL prefix of Motion (video surveillance). +http_pfx_motion: /netcam + # URL prefix of Movim (XMPP web client). http_pfx_movim: /social @@ -308,6 +311,32 @@ media_minidlna_conf: | root_container=B friendly_name=HomeMedia +# Motion data directory +motion_data: /var/lib/motion +motion_cloud_url: 'https://www.mediafire.com/' +motion_cloud_login: login +motion_cloud_password: password +motion_cloud_id: app_id_xxxxx +motion_cloud_key: xxxxxxxxxx…xxxxxxxxxx +motion_email_recipient: hostmaster@localhost +motion_cameras: '[ + { + "id": 1, "name": "street door", + "url": "rtsp://user:password@street.example.org:554/videoMain", + "width": 640, "height": 360, + "mask_file": "example_mask_640_360.pgm", + "framerate": 5 + }, + { + "id": 2, "name": "garden door", + "url": "rtsp://user:password@garden.example.org:554/videoMain", + "width": 640, "height": 360, + "mask_file": null, + "framerate": 5 + } + ]' +motion_web_title: "Video surveillance" + # Name of the Movim database in PostgreSQL. movim_db: movim diff --git a/roles/dmz_motion_front/handlers/main.yml b/roles/dmz_motion_front/handlers/main.yml new file mode 100644 index 0000000..8f91f95 --- /dev/null +++ b/roles/dmz_motion_front/handlers/main.yml @@ -0,0 +1,10 @@ +--- +# The home-server project produces a multi-purpose setup using Ansible. +# Copyright © 2018 Y. Gablin, under the GPL-3.0-or-later license. +# Full licensing information in the LICENSE file, or gnu.org/licences/gpl-3.0.txt if the file is missing. + +- name: restart nginx.service + systemd: + daemon_reload: true + name: nginx.service + state: restarted diff --git a/roles/dmz_motion_front/meta.OK/main.yml b/roles/dmz_motion_front/meta.OK/main.yml new file mode 100644 index 0000000..7f3de13 --- /dev/null +++ b/roles/dmz_motion_front/meta.OK/main.yml @@ -0,0 +1,7 @@ +--- +# The home-server project produces a multi-purpose setup using Ansible. +# Copyright © 2018 Y. Gablin, under the GPL-3.0-or-later license. +# Full licensing information in the LICENSE file, or gnu.org/licences/gpl-3.0.txt if the file is missing. + +dependencies: + - role: dmz_nginx diff --git a/roles/dmz_motion_front/tasks/main.yml b/roles/dmz_motion_front/tasks/main.yml new file mode 100644 index 0000000..68ee7e8 --- /dev/null +++ b/roles/dmz_motion_front/tasks/main.yml @@ -0,0 +1,56 @@ +--- +# The home-server project produces a multi-purpose setup using Ansible. +# Copyright © 2018 Y. Gablin, under the GPL-3.0-or-later license. +# Full licensing information in the LICENSE file, or gnu.org/licences/gpl-3.0.txt if the file is missing. + +- name: create a directory for the Motion web page + file: + name: /srv/http/motion + state: directory + mode: 0750 + owner: root + group: http + +- name: send the feedback and control web page for Motion + template: + src: templates/index.html.j2 + dest: /srv/http/motion/index.html + mode: 0640 + owner: root + group: http + +- name: configure nginx for Motion + copy: + content: | + location {{http_pfx_motion}}/ { + alias /srv/http/motion/; + index index.html; + } + location {{http_pfx_motion}}/control/ { + proxy_pass http://unix:/run/shared_sockets/motion_control.socket:/; + proxy_buffering off; + proxy_cache off; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + } + location {{http_pfx_motion}}/camera/ { + proxy_pass http://unix:/run/shared_sockets/motion_stream.socket:/; + proxy_buffering off; + proxy_cache off; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + } + dest: /etc/nginx/inc.d/motion.https.inc + mode: 0440 + owner: http + group: http + notify: + - restart nginx.service + +### LOCAL COMMIT ⇒ ### +- name: commit local changes + include_role: name=etckeeper.inc allow_duplicates=true tasks_from=local.yml + vars: + msg: Gitea +### ⇐ LOCAL COMMIT ### +- meta: flush_handlers diff --git a/roles/dmz_motion_front/templates/index.html.j2 b/roles/dmz_motion_front/templates/index.html.j2 new file mode 100644 index 0000000..db4b813 --- /dev/null +++ b/roles/dmz_motion_front/templates/index.html.j2 @@ -0,0 +1,103 @@ + + + + + {{motion_web_title}} + + + + +

{{motion_web_title}}

+{% for camera in (motion_cameras | from_json) %} +
+{% endfor %} + +
+

+
+ + + +
+
+ + +
+
+ + diff --git a/roles/front_run/meta.OK/main.yml b/roles/front_run/meta.OK/main.yml index bbfb196..5bed625 100644 --- a/roles/front_run/meta.OK/main.yml +++ b/roles/front_run/meta.OK/main.yml @@ -8,6 +8,7 @@ dependencies: - role: dovecot - role: front - role: ihmgit_back + - role: motion_back - role: movim_back - role: nextcloud_back - role: postgresql diff --git a/roles/motion_back/files/example_mask_640_360.pgm b/roles/motion_back/files/example_mask_640_360.pgm new file mode 100644 index 0000000..9b90e23 Binary files /dev/null and b/roles/motion_back/files/example_mask_640_360.pgm differ diff --git a/roles/motion_back/handlers/main.yml b/roles/motion_back/handlers/main.yml new file mode 100644 index 0000000..a92a1dc --- /dev/null +++ b/roles/motion_back/handlers/main.yml @@ -0,0 +1,22 @@ +--- +# The home-server project produces a multi-purpose setup using Ansible. +# Copyright © 2018 Y. Gablin, under the GPL-3.0-or-later license. +# Full licensing information in the LICENSE file, or gnu.org/licences/gpl-3.0.txt if the file is missing. + +- name: restart motion.service + systemd: + daemon_reload: true + name: motion.service + state: restarted + +- name: restart socat-unix-to-tcp4@-run-shared_sockets-motion_control.socket\x3alocalhost\x3a1080.service + systemd: + daemon_reload: true + name: socat-unix-to-tcp4@-run-shared_sockets-motion_control.socket\x3alocalhost\x3a1080.service + state: restarted + +- name: restart socat-unix-to-tcp4@-run-shared_sockets-motion_stream.socket\x3alocalhost\x3a1081.service + systemd: + daemon_reload: true + name: socat-unix-to-tcp4@-run-shared_sockets-motion_stream.socket\x3alocalhost\x3a1081.service + state: restarted diff --git a/roles/motion_back/meta.OK/main.yml b/roles/motion_back/meta.OK/main.yml new file mode 100644 index 0000000..38034be --- /dev/null +++ b/roles/motion_back/meta.OK/main.yml @@ -0,0 +1,8 @@ +--- +# The home-server project produces a multi-purpose setup using Ansible. +# Copyright © 2018 Y. Gablin, under the GPL-3.0-or-later license. +# Full licensing information in the LICENSE file, or gnu.org/licences/gpl-3.0.txt if the file is missing. + +dependencies: + - role: cleanupdate + - role: sockets diff --git a/roles/motion_back/tasks/main.yml b/roles/motion_back/tasks/main.yml new file mode 100644 index 0000000..900caf8 --- /dev/null +++ b/roles/motion_back/tasks/main.yml @@ -0,0 +1,251 @@ +--- +# The home-server project produces a multi-purpose setup using Ansible. +# Copyright © 2018 Y. Gablin, under the GPL-3.0-or-later license. +# Full licensing information in the LICENSE file, or gnu.org/licences/gpl-3.0.txt if the file is missing. + +### UPSTREAM BEGIN ⇒ ### +- name: pull prerequisites from upstream + include_role: name=etckeeper.inc allow_duplicates=true tasks_from=upstream.yml + vars: + msg: motion +### ⇐ UPSTREAM BEGIN ### + +- name: install software + package: + name: "{{item}}" + state: present + with_items: + - curl + - motion + - 's-nail' + - socat + +### UPSTREAM END ⇒ ### +- name: merge upstream + include_role: name=etckeeper.inc allow_duplicates=true tasks_from=merge.yml + vars: + msg: motion +### ⇐ UPSTREAM END ### + +- name: send the script for Motion to send emails + template: + src: templates/email.sh.j2 + dest: /etc/motion/email.sh + owner: root + group: motion + mode: 0750 + notify: + - restart motion.service + +- name: send the script for Motion to upload files + template: + src: templates/upload.sh.j2 + dest: /etc/motion/upload.sh + owner: root + group: motion + mode: 0750 + notify: + - restart motion.service + +- name: send main Motion configuration + copy: + content: | + target_dir {{motion_data}} + on_event_end /etc/motion/email.sh %$ %v %Y-%m-%d %H:%M:%S + on_picture_save /etc/motion/upload.sh "%f" + minimum_motion_frames 5 + event_gap 10 + picture_output on + picture_quality 50 + picture_filename %$-%v-%Y%m%d%H%M%S-%q@%K_%L + movie_output off + webcontrol_port 1080 + webcontrol_localhost on + webcontrol_interface 1 + webcontrol_parms 1 + stream_port 1081 + stream_localhost on + stream_preview_method 4 + stream_quality 20 + camera_dir /etc/motion/camera.d + dest: /etc/motion/motion.conf + owner: root + group: motion + mode: 0640 + notify: + - restart motion.service + +- name: create the directory for Motion cameras + file: + name: /etc/motion/camera.d + state: directory + owner: root + group: motion + mode: 0750 + +- name: send mask-files for Motion cameras + copy: + src: files/{{item.mask_file}} + dest: /etc/motion/camera.d/{{item.mask_file}} + owner: root + group: motion + mode: 0640 + with_items: "{{motion_cameras}}" + when: + - (item.mask_file != None) + notify: + - restart motion.service + +- name: send Motion cameras configuration + copy: + content: | + camera_id {{item.id}} + camera_name {{item.name}} + netcam_url {{item.url}} + {{ ('mask_file /etc/motion/camera.d/' + item.mask_file) if item.mask_file != None else '' }} + width {{item.width}} + height {{item.height}} + framerate {{item.framerate}} + text_right %q (%ix%J+%K+%L) + auto_brightness 0 + noise_tune on + lightswitch_percent 40 + lightswitch_frames 15 + dest: /etc/motion/camera.d/camera_{{item.id}}.conf + owner: root + group: motion + mode: 0640 + with_items: "{{motion_cameras}}" + notify: + - restart motion.service + +- name: identify all Motion cameras configured on the server + find: + paths: [ '/etc/motion/camera.d' ] + patterns: [ 'camera_*.conf' ] + register: existing_cameras + changed_when: false + +- name: only keep basenames of configured Motion cameras + set_fact: + existing_cameras: "{{ existing_cameras.files | map(attribute='path') | map('basename') | list }}" + changed_when: false + +- name: filter out up-to-date Motion cameras + set_fact: + existing_cameras: "{{ existing_cameras | reject('contains', 'camera_' + (item.id | string) + '.conf') | list }}" + with_items: "{{ motion_cameras }}" + changed_when: false + +- name: delete old Motion cameras + file: + path: /etc/motion/camera.d/{{item}} + state: absent + with_items: "{{ existing_cameras }}" + notify: + - restart motion.service + +- name: ensure ownership of the Motion data directory + file: + path: "{{motion_data}}" + state: directory + owner: motion + recurse: true + +- name: prepare override of Motion launch parameters + file: + name: /etc/systemd/system/motion.service.d + state: directory + +- name: override Motion launch parameters + copy: + content: | + [Unit] + Description=Motion daemon, paused + [Service] + ExecStart= + ExecStart=/usr/bin/motion -n -m + dest: /etc/systemd/system/motion.service.d/paused-mode.conf + mode: 0644 + notify: + - restart motion.service + +- name: create a generic service for socat-based port-forwarding + copy: + content: | + [Unit] + Description=socat-based Unix domain socket to IPv4/TCP forwarding + After=network-online.target + Wants=network-online.target + [Service] + ExecStartPre=/usr/bin/sh -c 'rm -f "$${0%%%%:*}"' "%I" + ExecStart=/usr/bin/sh -c 'exec /usr/bin/socat -d UNIX-LISTEN:"$${0%%%%:*}",fork,mode=0666 TCP4:$${0#*:}' "%I" + PrivateDevices=yes + ProtectSystem=full + NoNewPrivileges=yes + ReadWritePaths=/run /tmp + dest: /etc/systemd/system/socat-unix-to-tcp4@.service + mode: 0644 + notify: + - restart socat-unix-to-tcp4@-run-shared_sockets-motion_control.socket\x3alocalhost\x3a1080.service + - restart socat-unix-to-tcp4@-run-shared_sockets-motion_stream.socket\x3alocalhost\x3a1081.service + +- name: prepare instanciation of socat-based port-forwarding for Motion control + file: + name: /etc/systemd/system/socat-unix-to-tcp4@-run-shared_sockets-motion_control.socket\x3alocalhost\x3a1080.service.d + state: directory + +- name: instanciate socat-based port-forwarding for Motion control + copy: + content: | + [Unit] + Description=socat-based Unix–TCP forwarding of Motion control + After=motion.service + Wants=motion.service + dest: /etc/systemd/system/socat-unix-to-tcp4@-run-shared_sockets-motion_control.socket\x3alocalhost\x3a1080.service.d/dependency.conf + mode: 0644 + notify: + - restart socat-unix-to-tcp4@-run-shared_sockets-motion_control.socket\x3alocalhost\x3a1080.service + +- name: prepare instanciation of socat-based port-forwarding for Motion stream + file: + name: /etc/systemd/system/socat-unix-to-tcp4@-run-shared_sockets-motion_stream.socket\x3alocalhost\x3a1081.service.d + state: directory + +- name: instanciate socat-based port-forwarding for Motion stream + copy: + content: | + [Unit] + Description=socat-based Unix–TCP forwarding of Motion stream + After=motion.service + Wants=motion.service + dest: /etc/systemd/system/socat-unix-to-tcp4@-run-shared_sockets-motion_stream.socket\x3alocalhost\x3a1081.service.d/dependency.conf + mode: 0644 + notify: + - restart socat-unix-to-tcp4@-run-shared_sockets-motion_stream.socket\x3alocalhost\x3a1081.service + +- name: enable Motion + systemd: + daemon_reload: true + name: motion.service + enabled: true + +- name: enable unix-to-tcp forwarding for Motion control + systemd: + daemon_reload: true + name: socat-unix-to-tcp4@-run-shared_sockets-motion_control.socket\x3alocalhost\x3a1080.service + enabled: true + +- name: enable unix-to-tcp forwarding for Motion stream + systemd: + daemon_reload: true + name: socat-unix-to-tcp4@-run-shared_sockets-motion_stream.socket\x3alocalhost\x3a1081.service + enabled: true + +### LOCAL COMMIT ⇒ ### +- name: commit local changes + include_role: name=etckeeper.inc allow_duplicates=true tasks_from=local.yml + vars: + msg: motion +### ⇐ LOCAL COMMIT ### +- meta: flush_handlers diff --git a/roles/motion_back/templates/email.sh.j2 b/roles/motion_back/templates/email.sh.j2 new file mode 100755 index 0000000..cf33abd --- /dev/null +++ b/roles/motion_back/templates/email.sh.j2 @@ -0,0 +1,9 @@ +#!/bin/bash +# $1: camera +# $2: event number +# $3: ISO date +# $4: ISO time + +printf 'Camera: %s\nEvent: %s\nDate: %s %s\n\nCloud: %s' \ + "$1" "$2" "$3" "$4" '{{motion_cloud_url}}' \ +| mail -s 'Motion event' {{motion_email_recipient}} diff --git a/roles/motion_back/templates/upload.sh.j2 b/roles/motion_back/templates/upload.sh.j2 new file mode 100755 index 0000000..fa46ea6 --- /dev/null +++ b/roles/motion_back/templates/upload.sh.j2 @@ -0,0 +1,40 @@ +#!/bin/bash +# $1: file to upload + +BASE_URL=https://www.mediafire.com/api +TOKEN_FILE=/var/lib/motion/token +LOGIN='{{motion_cloud_login}}' +PASSWORD='{{motion_cloud_password}}' + +# Plowshare App ID & API key +APP_ID={{motion_cloud_id}} +API_KEY='{{motion_cloud_key}}' + +token="$(find "$TOKEN_FILE" -mmin -9 -exec cat {} \; 2>/dev/null)" +set -e + +f="$1" +file_size="$(stat -L --printf=%s "$f")" +file_name="$(basename "$f")" + +if [ -z "$token" ]; then + token_json="$(curl \ + -d "email=$LOGIN" --data-urlencode "password=$PASSWORD" \ + -d "application_id=$APP_ID" \ + -d "signature=$(printf '%s' "$LOGIN$PASSWORD$APP_ID$API_KEY" | sha1sum | head -c40)" \ + -d 'version=1' -d 'response_format=json' \ + "$BASE_URL/user/get_session_token.php" \ + | tr -d '\r\n ')" + + grep -qF '"result":"Success"' <<<"$token_json" + + token="$(sed -rn 's/.*"session_token":"(([^"]|\\")*)".*/\1/p' <<<"$token_json" \ + | sed 's/\\\(.\)/\1/g')" + + printf '%s' "$token" >"$TOKEN_FILE" +fi + +curl \ + -F "Filedata=@$f;filename=$file_name" \ + -H "x-filename: $file_name" -H "x-filesize: $file_size" \ + "$BASE_URL/upload/simple.php?session_token=${token}&path=/Camera&action_on_duplicate=keep&response_format=json" diff --git a/roles/ssowat/templates/conf.json.j2 b/roles/ssowat/templates/conf.json.j2 index 934295d..142a58e 100644 --- a/roles/ssowat/templates/conf.json.j2 +++ b/roles/ssowat/templates/conf.json.j2 @@ -47,11 +47,13 @@ }, "you": { "allow": { + "{{net_soa}}{{http_pfx_motion}}/": "Surveillance", "{{net_soa}}{{http_pfx_transmission}}": "BitTorrent" } }, "me": { "allow": { + "{{net_soa}}{{http_pfx_motion}}/": "Surveillance", "{{net_soa}}{{http_pfx_movim}}/": "Social", "{{net_soa}}{{http_pfx_transmission}}": "BitTorrent", "{{net_soa}}{{http_pfx_wallabag}}/": "Read later" diff --git a/site.yml b/site.yml index de991eb..1136c84 100644 --- a/site.yml +++ b/site.yml @@ -33,6 +33,7 @@ - ssh - dovecot - mediaplayer + - motion_back - front_run - acme_back - nextcloud_davfs @@ -61,6 +62,7 @@ - dmz_dotclear_front - dmz_ihmldap - dmz_prosody_front + - dmz_motion_front # - dmz_wallabag_front - acme_front - privatebin