1.. include:: ../global.rst.inc
2.. highlight:: none
3
4Central repository server with Ansible or Salt
5==============================================
6
7This section will give an example how to setup a borg repository server for multiple
8clients.
9
10Machines
11--------
12
13There are multiple machines used in this section and will further be named by their
14respective fully qualified domain name (fqdn).
15
16* The backup server: `backup01.srv.local`
17* The clients:
18
19  - John Doe's desktop: `johndoe.clnt.local`
20  - Webserver 01: `web01.srv.local`
21  - Application server 01: `app01.srv.local`
22
23User and group
24--------------
25
26The repository server needs to have only one UNIX user for all the clients.
27Recommended user and group with additional settings:
28
29* User: `backup`
30* Group: `backup`
31* Shell: `/bin/bash` (or other capable to run the `borg serve` command)
32* Home: `/home/backup`
33
34Most clients shall initiate a backup from the root user to catch all
35users, groups and permissions (e.g. when backing up `/home`).
36
37Folders
38-------
39
40The following folder tree layout is suggested on the repository server:
41
42* User home directory, /home/backup
43* Repositories path (storage pool): /home/backup/repos
44* Clients restricted paths (`/home/backup/repos/<client fqdn>`):
45
46  - johndoe.clnt.local: `/home/backup/repos/johndoe.clnt.local`
47  - web01.srv.local: `/home/backup/repos/web01.srv.local`
48  - app01.srv.local: `/home/backup/repos/app01.srv.local`
49
50Restrictions
51------------
52
53Borg is instructed to restrict clients into their own paths:
54``borg serve --restrict-to-path /home/backup/repos/<client fqdn>``
55
56The client will be able to access any file or subdirectory inside of ``/home/backup/repos/<client fqdn>``
57but no other directories. You can allow a client to access several separate directories by passing multiple
58``--restrict-to-path`` flags, for instance: ``borg serve --restrict-to-path /home/backup/repos/<client fqdn> --restrict-to-path /home/backup/repos/<other client fqdn>``,
59which could make sense if multiple machines belong to one person which should then have access to all the
60backups of their machines.
61
62There is only one ssh key per client allowed. Keys are added for ``johndoe.clnt.local``, ``web01.srv.local`` and
63``app01.srv.local``. But they will access the backup under only one UNIX user account as:
64``backup@backup01.srv.local``. Every key in ``$HOME/.ssh/authorized_keys`` has a
65forced command and restrictions applied as shown below:
66
67::
68
69  command="cd /home/backup/repos/<client fqdn>;
70           borg serve --restrict-to-path /home/backup/repos/<client fqdn>",
71           restrict <keytype> <key> <host>
72
73.. note:: The text shown above needs to be written on a single line!
74
75The options which are added to the key will perform the following:
76
771. Change working directory
782. Run ``borg serve`` restricted to the client base path
793. Restrict ssh and do not allow stuff which imposes a security risk
80
81Due to the ``cd`` command we use, the server automatically changes the current
82working directory. Then client doesn't need to have knowledge of the absolute
83or relative remote repository path and can directly access the repositories at
84``<user>@<host>:<repo>``.
85
86.. note:: The setup above ignores all client given commandline parameters
87          which are normally appended to the `borg serve` command.
88
89Client
90------
91
92The client needs to initialize the `pictures` repository like this:
93
94::
95
96 borg init backup@backup01.srv.local:pictures
97
98Or with the full path (should actually never be used, as only for demonstrational purposes).
99The server should automatically change the current working directory to the `<client fqdn>` folder.
100
101::
102
103  borg init backup@backup01.srv.local:/home/backup/repos/johndoe.clnt.local/pictures
104
105When `johndoe.clnt.local` tries to access a not restricted path the following error is raised.
106John Doe tries to backup into the Web 01 path:
107
108::
109
110  borg init backup@backup01.srv.local:/home/backup/repos/web01.srv.local/pictures
111
112::
113
114  ~~~ SNIP ~~~
115  Remote: borg.remote.PathNotAllowed: /home/backup/repos/web01.srv.local/pictures
116  ~~~ SNIP ~~~
117  Repository path not allowed
118
119Ansible
120-------
121
122Ansible takes care of all the system-specific commands to add the user, create the
123folder, install and configure software.
124
125::
126
127  - hosts: backup01.srv.local
128    vars:
129      user: backup
130      group: backup
131      home: /home/backup
132      pool: "{{ home }}/repos"
133      auth_users:
134        - host: johndoe.clnt.local
135          key: "{{ lookup('file', '/path/to/keys/johndoe.clnt.local.pub') }}"
136        - host: web01.clnt.local
137          key: "{{ lookup('file', '/path/to/keys/web01.clnt.local.pub') }}"
138        - host: app01.clnt.local
139          key: "{{ lookup('file', '/path/to/keys/app01.clnt.local.pub') }}"
140    tasks:
141    - package: name=borg state=present
142    - group: name="{{ group }}" state=present
143    - user: name="{{ user }}" shell=/bin/bash home="{{ home }}" createhome=yes group="{{ group }}" groups= state=present
144    - file: path="{{ home }}" owner="{{ user }}" group="{{ group }}" mode=0700 state=directory
145    - file: path="{{ home }}/.ssh" owner="{{ user }}" group="{{ group }}" mode=0700 state=directory
146    - file: path="{{ pool }}" owner="{{ user }}" group="{{ group }}" mode=0700 state=directory
147    - authorized_key: user="{{ user }}"
148                      key="{{ item.key }}"
149                      key_options='command="cd {{ pool }}/{{ item.host }};borg serve --restrict-to-path {{ pool }}/{{ item.host }}",restrict'
150      with_items: "{{ auth_users }}"
151    - file: path="{{ home }}/.ssh/authorized_keys" owner="{{ user }}" group="{{ group }}" mode=0600 state=file
152    - file: path="{{ pool }}/{{ item.host }}" owner="{{ user }}" group="{{ group }}" mode=0700 state=directory
153      with_items: "{{ auth_users }}"
154
155Salt
156----
157
158This is a configuration similar to the one above, configured to be deployed with
159Salt running on a Debian system.
160
161::
162
163  Install borg backup from pip:
164    pkg.installed:
165      - pkgs:
166        - python3
167        - python3-dev
168        - python3-pip
169        - python-virtualenv
170        - libssl-dev
171        - openssl
172        - libacl1-dev
173        - libacl1
174        - build-essential
175        - libfuse-dev
176        - fuse
177        - pkg-config
178    pip.installed:
179      - pkgs: ["borgbackup"]
180      - bin_env: /usr/bin/pip3
181
182  Setup backup user:
183    user.present:
184      - name: backup
185      - fullname: Backup User
186      - home: /home/backup
187      - shell: /bin/bash
188  # CAUTION!
189  # If you change the ssh command= option below, it won't necessarily get pushed to the backup
190  # server correctly unless you delete the ~/.ssh/authorized_keys file and re-create it!
191  {% for host in backupclients %}
192  Give backup access to {{host}}:
193    ssh_auth.present:
194      - user: backup
195      - source: salt://conf/ssh-pubkeys/{{host}}-backup.id_ecdsa.pub
196      - options:
197        - command="cd /home/backup/repos/{{host}}; borg serve --restrict-to-path /home/backup/repos/{{host}}"
198        - restrict
199  {% endfor %}
200
201
202Enhancements
203------------
204
205As this section only describes a simple and effective setup it could be further
206enhanced when supporting (a limited set) of client supplied commands. A wrapper
207for starting `borg serve` could be written. Or borg itself could be enhanced to
208autodetect it runs under SSH by checking the `SSH_ORIGINAL_COMMAND` environment
209variable. This is left open for future improvements.
210
211When extending ssh autodetection in borg no external wrapper script is necessary
212and no other interpreter or application has to be deployed.
213
214See also
215--------
216
217* `SSH Daemon manpage <https://www.openbsd.org/cgi-bin/man.cgi/OpenBSD-current/man8/sshd.8>`_
218* `Ansible <https://docs.ansible.com>`_
219* `Salt <https://docs.saltstack.com/>`_
220