├── .editorconfig
├── .gitignore
├── .gitmodules
├── LICENSE
├── README.md
├── ansible.cfg
├── files
├── coredns
│ ├── Corefile.j2
│ └── zones
│ │ └── db.ulmlernt.org
├── grafana
│ └── dashboards
│ │ ├── bigbluebutton-all-servers.json
│ │ ├── bigbluebutton-server-instance.json
│ │ ├── cockpit.json
│ │ └── node-exporter-full.json
└── logo
│ ├── ulm_lernt_fahne_transparent.png
│ ├── ulm_lernt_fahne_transparent.svg
│ ├── ulm_lernt_fahne_weiss.png
│ ├── ulm_lernt_fahne_weiss.svg
│ ├── ulm_lernt_logo_transparent.png
│ ├── ulm_lernt_logo_transparent.svg
│ ├── ulm_lernt_logo_weiss.png
│ └── ulm_lernt_logo_weiss.svg
├── helper
├── disable_bbb_hosts.yml
├── enable_bbb_hosts.yml
├── fix_ufw.yml
├── reboot.yml
├── reset_apt_package_lists.yml
├── store_known_hosts.yml
└── upgradereboot.yml
├── inventory
├── main.yml
├── requirements.yml
├── roles
├── base-user
│ └── tasks
│ │ └── main.yml
├── base
│ ├── handlers
│ │ └── main.yml
│ └── tasks
│ │ ├── firewall.yml
│ │ └── main.yml
├── bbb-collect
│ └── tasks
│ │ └── main.yml
├── bbb-easy-join
│ ├── tasks
│ │ └── main.yml
│ └── templates
│ │ ├── bbb-easy-join.service.j2
│ │ ├── env.j2
│ │ └── nginx-bbb-easy-join.conf
├── bbb-exporter
│ ├── tasks
│ │ └── main.yml
│ └── templates
│ │ ├── bbb-exporter.service
│ │ └── env.j2
├── bbb-ulmlernt
│ ├── files
│ │ ├── conf-muted-ulmlernt.wav
│ │ ├── conf-unmuted-ulmlernt.wav
│ │ ├── default.odp
│ │ └── default.pdf
│ ├── handlers
│ │ └── main.yml
│ └── tasks
│ │ └── main.yml
├── coredns-ufw
│ └── tasks
│ │ └── main.yml
├── dirty-docker
│ └── tasks
│ │ └── main.yml
├── docker
│ └── tasks
│ │ └── main.yml
├── dokuwiki
│ ├── tasks
│ │ └── main.yml
│ └── templates
│ │ ├── dokuwiki-backup.sh
│ │ └── dokuwiki.nginx
├── greenlight
│ ├── handlers
│ │ └── main.yml
│ ├── tasks
│ │ └── main.yml
│ └── templates
│ │ ├── env.j2
│ │ ├── greenlight.service
│ │ └── nginx-greenlight.conf.j2
├── nginx-tls-add
│ ├── handlers
│ │ └── main.yml
│ ├── tasks
│ │ ├── config.yml
│ │ ├── http.yml
│ │ ├── letsencrypt.yml
│ │ └── main.yml
│ └── templates
│ │ ├── a13.conf.j2
│ │ └── http.conf.j2
├── nginx-tls-monitoring
│ ├── handlers
│ │ └── main.yml
│ ├── tasks
│ │ ├── config.yml
│ │ ├── http.yml
│ │ ├── letsencrypt.yml
│ │ └── main.yml
│ └── templates
│ │ ├── http-mon.conf.j2
│ │ └── mon.conf.j2
├── nginx-tls-redirect
│ ├── handlers
│ │ └── main.yml
│ ├── tasks
│ │ ├── config.yml
│ │ ├── http.yml
│ │ ├── letsencrypt.yml
│ │ └── main.yml
│ └── templates
│ │ ├── a13.conf.j2
│ │ └── http.conf.j2
├── nginx-tls
│ ├── files
│ │ ├── hackhack.gif
│ │ └── index.html
│ ├── handlers
│ │ └── main.yml
│ ├── tasks
│ │ ├── config.yml
│ │ ├── http.yml
│ │ ├── letsencrypt.yml
│ │ └── main.yml
│ └── templates
│ │ ├── a13.conf.j2
│ │ └── http.conf.j2
├── nginx
│ ├── handlers
│ │ └── main.yml
│ └── tasks
│ │ └── main.yml
├── no-docker
│ └── tasks
│ │ └── main.yml
├── no-rocketchat
│ ├── handlers
│ │ └── main.yml
│ ├── tasks
│ │ └── main.yml
│ └── templates
│ │ └── no-rocketchat.nginx
├── no-turn
│ └── tasks
│ │ └── main.yml
├── node-exporter-ufw
│ └── tasks
│ │ └── main.yml
├── prometheus-ufw
│ └── tasks
│ │ └── main.yml
├── restic-client
│ ├── tasks
│ │ └── main.yml
│ └── templates
│ │ ├── backup_directory.sh
│ │ ├── backup_postgres.sh
│ │ └── init_repo.sh
├── restic-server
│ ├── files
│ │ └── rest-server-0.9.7-linux-amd64
│ ├── handlers
│ │ └── main.yml
│ ├── tasks
│ │ ├── letsencrypt.yml
│ │ └── main.yml
│ └── templates
│ │ └── rest-server.service
├── scalelite-config
│ └── tasks
│ │ └── main.yml
├── scalelite
│ ├── handlers
│ │ └── main.yml
│ ├── tasks
│ │ └── main.yml
│ └── templates
│ │ ├── env.j2
│ │ ├── nginx-scalelite.conf
│ │ ├── scalelite-api.service
│ │ ├── scalelite-poller.service
│ │ └── scalelite.target
└── turn-standalone
│ ├── handlers
│ └── main.yml
│ ├── tasks
│ ├── certbot.yml
│ ├── coturn.yml
│ ├── firewall.yml
│ └── main.yml
│ └── templates
│ ├── certbot-zerossl.sh
│ ├── coturn.logrotate
│ ├── coturn.service
│ └── turnserver.conf.j2
├── tmp
└── .gitkeep
└── vars.yml
/.editorconfig:
--------------------------------------------------------------------------------
1 | # http://editorconfig.or
2 | root = true
3 |
4 | [*]
5 | charset = utf-8
6 | end_of_line = lf
7 | insert_final_newline = true
8 | trim_trailing_whitespace = true
9 |
10 | [*.{py,rst,ini}]
11 | indent_style = space
12 | indent_size = 4
13 |
14 | [*.yml]
15 | indent_style = space
16 | indent_size = 2
17 |
18 | [*.md]
19 | trim_trailing_whitespace = false
20 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.retry
2 | tmp/
3 | !tmp/.gitkeep
4 | vault_password
5 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stadtulm/a13-ansible/3b0c44d9712af5c5d3bd745c0a987cc9acf92102/.gitmodules
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Stadt Ulm
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # a13-ansible
2 |
3 | This repository is no longer maintained.
4 | Currently (12/2021) maintained ansible roles for BBB can be found here:
5 | - https://gitlab.com/infra.run
6 | - https://github.com/ebbba-org/ansible-role-bigbluebutton
7 | - https://codeberg.org/DigitalSouveraeneSchule/bbb.git
8 |
9 | ### Prepare
10 |
11 | #### Install modules
12 | ```
13 | ansible-galaxy install -r requirements.yml
14 | ```
15 |
16 | Note: `cloudalchemy.node-exporter` requires the gnu variant of `tar` on macOS. (`brew install gnu-tar`)
17 | Note: `cloudalchemy.prometheus` requires the `jmespath` python module on your (deployer) machine
18 |
19 | #### Passwords
20 |
21 | You need to create the file `vault_password` and put the ansible vault password in there.
22 |
23 | #### SSH Host Keys
24 |
25 | Get all SSH host keys and store in local .ssh/known\_hosts file by executing following skript
26 |
27 | ```
28 | ansible-playbook helper/store_known_hosts.yml
29 | ```
30 |
31 | ### Run
32 | ```
33 | ansible-playbook main.yml
34 | ```
35 |
36 | ## HowTo
37 | ### Add new machine
38 | * Update DNS zonefile in `files/coredns/zones/`
39 | * add A and AAAA records
40 | * update serial (`yyyymmddvv` with `vv` being the version increment. E.g., 2020040101)
41 | * Update DNS by `ansible-playbook main.yml --tags dns`
42 | * Enter Hostname twice in `inventory`, below `[all]` and below the other role the machine should have, eg. `[bbb]`
43 | * Confirm that you can ssh into the machine by its newly aquired dns name (this also adds the host key to your `~/.ssh/known_hosts`)
44 | * run `ansible-playbook main.yml -l your.fresh.hostname.example` (you may need `--user root` if you don't have an user yet, the base role creates one for you)
45 | * ...?
46 | * register your new bbb instance:
47 | * at the monitoring by running `ansible-playbook main.yml --tags monitoring`
48 | * at the loadbalancer by running `ansible-playbook main.yml --tags config`
49 | * enable it manually in the loadbalancer
50 |
51 | ## Things to tell your Network Admin
52 | * Proxy exeptions for IPv4 adress of turnserver
53 |
54 |
--------------------------------------------------------------------------------
/ansible.cfg:
--------------------------------------------------------------------------------
1 | [defaults]
2 | inventory = ./inventory
3 | interpreter_python = /usr/bin/python3
4 | fact_caching = yaml
5 | fact_caching_connection = tmp
6 | vault_password_file = ./vault_password
7 |
8 | [ssh_connection]
9 | pipelining = True
10 |
--------------------------------------------------------------------------------
/files/coredns/Corefile.j2:
--------------------------------------------------------------------------------
1 | ulmlernt.org {
2 | bind {{ ansible_default_ipv4.address }}
3 | bind {{ ansible_default_ipv6.address }}
4 | prometheus {{ ansible_default_ipv4.address }}:9153
5 |
6 | root /etc/coredns/zones
7 | file db.ulmlernt.org {
8 | # hetzner secondary ns
9 | transfer to 78.46.255.56
10 | transfer to 213.239.242.238
11 | transfer to 213.133.105.6
12 | transfer to 193.47.99.3
13 | }
14 | }
--------------------------------------------------------------------------------
/files/coredns/zones/db.ulmlernt.org:
--------------------------------------------------------------------------------
1 | $ORIGIN ulmlernt.org.
2 | @ 3600 IN SOA ns.ulmlernt.org. noc.ulmlernt.de. (
3 | 2021070801 ; serial ;;; PLEASE UPDATE THIS AFTER EACH EDIT!
4 | 1800 ; refresh (30 min)
5 | 1800 ; retry (30 min)
6 | 86400 ; expire (1 day)
7 | 3600 ; minimum (1 hour)
8 | )
9 |
10 | @ 3600 IN NS hydrogen.ns.hetzner.com.
11 | @ 3600 IN NS oxygen.ns.hetzner.com.
12 | @ 3600 IN NS helium.ns.hetzner.de.
13 | @ 3600 IN NS ns.ulmlernt.org.
14 |
15 | ns IN A 195.201.237.77
16 | ns IN AAAA 2a01:4f8:c17:af72::1
17 |
18 | fsn01 3600 IN A 195.201.237.77
19 | fsn01 3600 IN AAAA 2a01:4f8:c17:af72::1
20 |
21 | fsn02 3600 IN A 144.76.96.80
22 | fsn02 3600 IN AAAA 2a01:4f8:192:314f::2
23 |
24 | fsn03 3600 IN A 78.47.162.149
25 | fsn03 3600 IN AAAA 2a01:4f8:c17:9865::1
26 |
27 | fsn04 3600 IN A 136.243.8.92
28 | fsn04 3600 IN AAAA 2a01:4f8:211:21db::2
29 |
30 | fsn05 3600 IN A 136.243.18.16
31 | fsn05 3600 IN AAAA 2a01:4f8:211:2b8f::2
32 |
33 | fsn06 3600 IN A 148.251.6.47
34 | fsn06 3600 IN AAAA 2a01:4f8:201:602e::2
35 |
36 | fsn07 3600 IN A 148.251.47.101
37 | fsn07 3600 IN AAAA 2a01:4f8:202:264::2
38 |
39 | fsn29 3600 IN A 188.34.190.64
40 |
41 | fsn34 3600 IN A 138.201.118.3
42 | fsn34 3600 IN AAAA 2a01:4f8:c17:f692::2
43 |
44 | bck 3600 IN A 49.12.102.120
45 | bck 3600 IN AAAA 2a01:4f8:c17:badd::1
46 |
47 |
48 | lb 3600 IN CNAME fsn01.ulmlernt.org.
49 | mon 3600 IN CNAME fsn01.ulmlernt.org.
50 | gl 3600 IN CNAME fsn03.ulmlernt.org.
51 | chat 3600 IN CNAME fsn03.ulmlernt.org.
52 | doku 3600 IN CNAME fsn03.ulmlernt.org.
53 | turn01 3600 IN CNAME fsn34.ulmlernt.org.
54 | turnipv4only01 3600 IN CNAME fsn29.ulmlernt.org.
--------------------------------------------------------------------------------
/files/grafana/dashboards/bigbluebutton-all-servers.json:
--------------------------------------------------------------------------------
1 | {
2 | "__inputs": [
3 | {
4 | "name": "DS_PROMETHEUS",
5 | "label": "prometheus",
6 | "description": "",
7 | "type": "datasource",
8 | "pluginId": "prometheus",
9 | "pluginName": "Prometheus"
10 | }
11 | ],
12 | "__requires": [
13 | {
14 | "type": "grafana",
15 | "id": "grafana",
16 | "name": "Grafana",
17 | "version": "6.7.3"
18 | },
19 | {
20 | "type": "panel",
21 | "id": "graph",
22 | "name": "Graph",
23 | "version": ""
24 | },
25 | {
26 | "type": "datasource",
27 | "id": "prometheus",
28 | "name": "Prometheus",
29 | "version": "1.0.0"
30 | },
31 | {
32 | "type": "panel",
33 | "id": "stat",
34 | "name": "Stat",
35 | "version": ""
36 | }
37 | ],
38 | "annotations": {
39 | "list": [
40 | {
41 | "builtIn": 1,
42 | "datasource": "-- Grafana --",
43 | "enable": true,
44 | "hide": true,
45 | "iconColor": "rgba(0, 211, 255, 1)",
46 | "name": "Annotations & Alerts",
47 | "type": "dashboard"
48 | }
49 | ]
50 | },
51 | "editable": true,
52 | "gnetId": null,
53 | "graphTooltip": 0,
54 | "id": null,
55 | "iteration": 1587902354603,
56 | "links": [],
57 | "panels": [
58 | {
59 | "collapsed": false,
60 | "datasource": "${DS_PROMETHEUS}",
61 | "gridPos": {
62 | "h": 1,
63 | "w": 24,
64 | "x": 0,
65 | "y": 0
66 | },
67 | "id": 28,
68 | "panels": [],
69 | "title": "Main",
70 | "type": "row"
71 | },
72 | {
73 | "cacheTimeout": null,
74 | "datasource": "${DS_PROMETHEUS}",
75 | "gridPos": {
76 | "h": 26,
77 | "w": 4,
78 | "x": 0,
79 | "y": 1
80 | },
81 | "id": 12,
82 | "links": [],
83 | "maxPerRow": 2,
84 | "options": {
85 | "colorMode": "background",
86 | "fieldOptions": {
87 | "calcs": [
88 | "last"
89 | ],
90 | "defaults": {
91 | "mappings": [
92 | {
93 | "from": "",
94 | "id": 1,
95 | "operator": "",
96 | "text": "Online",
97 | "to": "",
98 | "type": 1,
99 | "value": "1"
100 | },
101 | {
102 | "from": "",
103 | "id": 2,
104 | "operator": "",
105 | "text": "Offline",
106 | "to": "",
107 | "type": 1,
108 | "value": "0"
109 | }
110 | ],
111 | "thresholds": {
112 | "mode": "absolute",
113 | "steps": [
114 | {
115 | "color": "red",
116 | "value": null
117 | },
118 | {
119 | "color": "green",
120 | "value": 1
121 | }
122 | ]
123 | },
124 | "title": ""
125 | },
126 | "overrides": [],
127 | "values": false
128 | },
129 | "graphMode": "none",
130 | "justifyMode": "auto",
131 | "orientation": "horizontal"
132 | },
133 | "pluginVersion": "6.7.3",
134 | "repeat": null,
135 | "repeatDirection": "h",
136 | "targets": [
137 | {
138 | "aggregation": "Last",
139 | "alias": "A",
140 | "decimals": 2,
141 | "displayAliasType": "Always",
142 | "displayType": "Regular",
143 | "displayValueWithAlias": "Never",
144 | "expr": "bbb_api_up",
145 | "instant": false,
146 | "interval": "",
147 | "legendFormat": "{{instance}}",
148 | "refId": "A",
149 | "units": "none",
150 | "valueHandler": "Number Threshold"
151 | }
152 | ],
153 | "timeFrom": null,
154 | "timeShift": null,
155 | "title": "BBB API",
156 | "type": "stat"
157 | },
158 | {
159 | "aliasColors": {},
160 | "bars": false,
161 | "dashLength": 10,
162 | "dashes": false,
163 | "datasource": "${DS_PROMETHEUS}",
164 | "decimals": 0,
165 | "fill": 1,
166 | "fillGradient": 0,
167 | "gridPos": {
168 | "h": 9,
169 | "w": 20,
170 | "x": 4,
171 | "y": 1
172 | },
173 | "hiddenSeries": false,
174 | "id": 2,
175 | "legend": {
176 | "alignAsTable": true,
177 | "avg": true,
178 | "current": true,
179 | "max": true,
180 | "min": true,
181 | "rightSide": true,
182 | "show": true,
183 | "total": false,
184 | "values": true
185 | },
186 | "lines": true,
187 | "linewidth": 1,
188 | "nullPointMode": "null",
189 | "options": {
190 | "dataLinks": []
191 | },
192 | "percentage": false,
193 | "pointradius": 2,
194 | "points": false,
195 | "renderer": "flot",
196 | "seriesOverrides": [],
197 | "spaceLength": 10,
198 | "stack": false,
199 | "steppedLine": false,
200 | "targets": [
201 | {
202 | "expr": "bbb_meetings_participants",
203 | "interval": "",
204 | "legendFormat": "{{instance}}",
205 | "refId": "A"
206 | }
207 | ],
208 | "thresholds": [],
209 | "timeFrom": null,
210 | "timeRegions": [],
211 | "timeShift": null,
212 | "title": "Participants",
213 | "tooltip": {
214 | "shared": true,
215 | "sort": 0,
216 | "value_type": "individual"
217 | },
218 | "type": "graph",
219 | "xaxis": {
220 | "buckets": null,
221 | "mode": "time",
222 | "name": null,
223 | "show": true,
224 | "values": []
225 | },
226 | "yaxes": [
227 | {
228 | "decimals": 0,
229 | "format": "short",
230 | "label": "Participants",
231 | "logBase": 1,
232 | "max": null,
233 | "min": "0",
234 | "show": true
235 | },
236 | {
237 | "format": "short",
238 | "label": null,
239 | "logBase": 1,
240 | "max": null,
241 | "min": null,
242 | "show": false
243 | }
244 | ],
245 | "yaxis": {
246 | "align": false,
247 | "alignLevel": null
248 | }
249 | },
250 | {
251 | "aliasColors": {},
252 | "bars": false,
253 | "dashLength": 10,
254 | "dashes": false,
255 | "datasource": "${DS_PROMETHEUS}",
256 | "decimals": 0,
257 | "fill": 1,
258 | "fillGradient": 0,
259 | "gridPos": {
260 | "h": 8,
261 | "w": 20,
262 | "x": 4,
263 | "y": 10
264 | },
265 | "hiddenSeries": false,
266 | "id": 6,
267 | "legend": {
268 | "alignAsTable": true,
269 | "avg": true,
270 | "current": true,
271 | "max": true,
272 | "min": true,
273 | "rightSide": true,
274 | "show": true,
275 | "total": false,
276 | "values": true
277 | },
278 | "lines": true,
279 | "linewidth": 1,
280 | "nullPointMode": "null",
281 | "options": {
282 | "dataLinks": []
283 | },
284 | "percentage": false,
285 | "pointradius": 2,
286 | "points": false,
287 | "renderer": "flot",
288 | "seriesOverrides": [],
289 | "spaceLength": 10,
290 | "stack": false,
291 | "steppedLine": false,
292 | "targets": [
293 | {
294 | "expr": "bbb_meetings_voice_participants",
295 | "interval": "",
296 | "legendFormat": "{{instance}}",
297 | "refId": "A"
298 | }
299 | ],
300 | "thresholds": [],
301 | "timeFrom": null,
302 | "timeRegions": [],
303 | "timeShift": null,
304 | "title": "Voice Participants",
305 | "tooltip": {
306 | "shared": true,
307 | "sort": 0,
308 | "value_type": "individual"
309 | },
310 | "type": "graph",
311 | "xaxis": {
312 | "buckets": null,
313 | "mode": "time",
314 | "name": null,
315 | "show": true,
316 | "values": []
317 | },
318 | "yaxes": [
319 | {
320 | "decimals": 0,
321 | "format": "short",
322 | "label": "Participants",
323 | "logBase": 1,
324 | "max": null,
325 | "min": "0",
326 | "show": true
327 | },
328 | {
329 | "format": "short",
330 | "label": null,
331 | "logBase": 1,
332 | "max": null,
333 | "min": null,
334 | "show": false
335 | }
336 | ],
337 | "yaxis": {
338 | "align": false,
339 | "alignLevel": null
340 | }
341 | },
342 | {
343 | "aliasColors": {},
344 | "bars": false,
345 | "dashLength": 10,
346 | "dashes": false,
347 | "datasource": "${DS_PROMETHEUS}",
348 | "decimals": 0,
349 | "fill": 1,
350 | "fillGradient": 0,
351 | "gridPos": {
352 | "h": 9,
353 | "w": 20,
354 | "x": 4,
355 | "y": 18
356 | },
357 | "hiddenSeries": false,
358 | "id": 8,
359 | "legend": {
360 | "alignAsTable": true,
361 | "avg": true,
362 | "current": true,
363 | "max": true,
364 | "min": true,
365 | "rightSide": true,
366 | "show": true,
367 | "total": false,
368 | "values": true
369 | },
370 | "lines": true,
371 | "linewidth": 1,
372 | "nullPointMode": "null",
373 | "options": {
374 | "dataLinks": []
375 | },
376 | "percentage": false,
377 | "pointradius": 2,
378 | "points": false,
379 | "renderer": "flot",
380 | "seriesOverrides": [],
381 | "spaceLength": 10,
382 | "stack": false,
383 | "steppedLine": false,
384 | "targets": [
385 | {
386 | "expr": "bbb_meetings_video_participants",
387 | "interval": "",
388 | "legendFormat": "{{instance}}",
389 | "refId": "A"
390 | }
391 | ],
392 | "thresholds": [],
393 | "timeFrom": null,
394 | "timeRegions": [],
395 | "timeShift": null,
396 | "title": "Video Participants",
397 | "tooltip": {
398 | "shared": true,
399 | "sort": 0,
400 | "value_type": "individual"
401 | },
402 | "type": "graph",
403 | "xaxis": {
404 | "buckets": null,
405 | "mode": "time",
406 | "name": null,
407 | "show": true,
408 | "values": []
409 | },
410 | "yaxes": [
411 | {
412 | "decimals": 0,
413 | "format": "short",
414 | "label": "Participants",
415 | "logBase": 1,
416 | "max": null,
417 | "min": "0",
418 | "show": true
419 | },
420 | {
421 | "decimals": 0,
422 | "format": "short",
423 | "label": null,
424 | "logBase": 1,
425 | "max": null,
426 | "min": null,
427 | "show": false
428 | }
429 | ],
430 | "yaxis": {
431 | "align": false,
432 | "alignLevel": null
433 | }
434 | },
435 | {
436 | "collapsed": false,
437 | "datasource": "${DS_PROMETHEUS}",
438 | "gridPos": {
439 | "h": 1,
440 | "w": 24,
441 | "x": 0,
442 | "y": 27
443 | },
444 | "id": 34,
445 | "panels": [],
446 | "title": "Max Participants",
447 | "type": "row"
448 | },
449 | {
450 | "datasource": "${DS_PROMETHEUS}",
451 | "gridPos": {
452 | "h": 2,
453 | "w": 24,
454 | "x": 0,
455 | "y": 28
456 | },
457 | "id": 24,
458 | "maxPerRow": 24,
459 | "options": {
460 | "colorMode": "value",
461 | "fieldOptions": {
462 | "calcs": [
463 | "max"
464 | ],
465 | "defaults": {
466 | "mappings": [],
467 | "thresholds": {
468 | "mode": "absolute",
469 | "steps": [
470 | {
471 | "color": "green",
472 | "value": null
473 | }
474 | ]
475 | },
476 | "title": ""
477 | },
478 | "overrides": [],
479 | "values": false
480 | },
481 | "graphMode": "none",
482 | "justifyMode": "auto",
483 | "orientation": "auto"
484 | },
485 | "pluginVersion": "6.7.3",
486 | "repeat": null,
487 | "repeatDirection": "h",
488 | "targets": [
489 | {
490 | "expr": "bbb_meetings_participants",
491 | "format": "time_series",
492 | "hide": false,
493 | "interval": "",
494 | "legendFormat": "{{instance}}",
495 | "refId": "A"
496 | }
497 | ],
498 | "timeFrom": null,
499 | "timeShift": null,
500 | "title": "",
501 | "transparent": true,
502 | "type": "stat"
503 | },
504 | {
505 | "collapsed": false,
506 | "datasource": "${DS_PROMETHEUS}",
507 | "gridPos": {
508 | "h": 1,
509 | "w": 24,
510 | "x": 0,
511 | "y": 30
512 | },
513 | "id": 75,
514 | "panels": [],
515 | "title": "Max Video Participants",
516 | "type": "row"
517 | },
518 | {
519 | "datasource": "${DS_PROMETHEUS}",
520 | "gridPos": {
521 | "h": 2,
522 | "w": 24,
523 | "x": 0,
524 | "y": 31
525 | },
526 | "id": 4,
527 | "maxPerRow": 24,
528 | "options": {
529 | "colorMode": "value",
530 | "fieldOptions": {
531 | "calcs": [
532 | "max"
533 | ],
534 | "defaults": {
535 | "mappings": [],
536 | "thresholds": {
537 | "mode": "absolute",
538 | "steps": [
539 | {
540 | "color": "green",
541 | "value": null
542 | }
543 | ]
544 | },
545 | "title": ""
546 | },
547 | "overrides": [],
548 | "values": false
549 | },
550 | "graphMode": "none",
551 | "justifyMode": "auto",
552 | "orientation": "vertical"
553 | },
554 | "pluginVersion": "6.7.3",
555 | "repeat": null,
556 | "repeatDirection": "h",
557 | "targets": [
558 | {
559 | "expr": "bbb_meetings_video_participants",
560 | "interval": "",
561 | "legendFormat": "{{instance}}",
562 | "refId": "A"
563 | }
564 | ],
565 | "timeFrom": null,
566 | "timeShift": null,
567 | "title": "",
568 | "transparent": true,
569 | "type": "stat"
570 | },
571 | {
572 | "collapsed": false,
573 | "datasource": "${DS_PROMETHEUS}",
574 | "gridPos": {
575 | "h": 1,
576 | "w": 24,
577 | "x": 0,
578 | "y": 33
579 | },
580 | "id": 115,
581 | "panels": [],
582 | "title": "Current Meetings",
583 | "type": "row"
584 | },
585 | {
586 | "datasource": "${DS_PROMETHEUS}",
587 | "gridPos": {
588 | "h": 2,
589 | "w": 24,
590 | "x": 0,
591 | "y": 34
592 | },
593 | "id": 135,
594 | "maxPerRow": 24,
595 | "options": {
596 | "colorMode": "value",
597 | "fieldOptions": {
598 | "calcs": [
599 | "last"
600 | ],
601 | "defaults": {
602 | "mappings": [],
603 | "thresholds": {
604 | "mode": "absolute",
605 | "steps": [
606 | {
607 | "color": "green",
608 | "value": null
609 | }
610 | ]
611 | },
612 | "title": ""
613 | },
614 | "overrides": [],
615 | "values": false
616 | },
617 | "graphMode": "none",
618 | "justifyMode": "auto",
619 | "orientation": "vertical"
620 | },
621 | "pluginVersion": "6.7.3",
622 | "repeat": null,
623 | "repeatDirection": "h",
624 | "targets": [
625 | {
626 | "expr": "bbb_meetings",
627 | "instant": true,
628 | "interval": "",
629 | "legendFormat": "{{instance}}",
630 | "refId": "A"
631 | }
632 | ],
633 | "timeFrom": null,
634 | "timeShift": null,
635 | "title": "",
636 | "transparent": true,
637 | "type": "stat"
638 | },
639 | {
640 | "collapsed": true,
641 | "datasource": "${DS_PROMETHEUS}",
642 | "gridPos": {
643 | "h": 1,
644 | "w": 24,
645 | "x": 0,
646 | "y": 36
647 | },
648 | "id": 32,
649 | "panels": [
650 | {
651 | "aliasColors": {},
652 | "bars": false,
653 | "cacheTimeout": null,
654 | "dashLength": 10,
655 | "dashes": false,
656 | "datasource": "${DS_PROMETHEUS}",
657 | "fill": 1,
658 | "fillGradient": 0,
659 | "gridPos": {
660 | "h": 11,
661 | "w": 24,
662 | "x": 0,
663 | "y": 37
664 | },
665 | "hiddenSeries": false,
666 | "id": 22,
667 | "legend": {
668 | "alignAsTable": true,
669 | "avg": true,
670 | "current": true,
671 | "max": true,
672 | "min": true,
673 | "rightSide": true,
674 | "show": true,
675 | "total": false,
676 | "values": true
677 | },
678 | "lines": true,
679 | "linewidth": 1,
680 | "links": [],
681 | "nullPointMode": "null",
682 | "options": {
683 | "dataLinks": []
684 | },
685 | "percentage": false,
686 | "pluginVersion": "6.7.0",
687 | "pointradius": 2,
688 | "points": false,
689 | "renderer": "flot",
690 | "seriesOverrides": [],
691 | "spaceLength": 10,
692 | "stack": false,
693 | "steppedLine": false,
694 | "targets": [
695 | {
696 | "expr": "histogram_quantile(0.95, sum(rate(bbb_api_latency_bucket{endpoint=\"getMeetings\"}[5m])) by (le, instance))",
697 | "instant": false,
698 | "interval": "",
699 | "intervalFactor": 1,
700 | "legendFormat": "{{instance}} - getMeetings",
701 | "refId": "A"
702 | }
703 | ],
704 | "thresholds": [],
705 | "timeFrom": null,
706 | "timeRegions": [],
707 | "timeShift": null,
708 | "title": "API 95-th Percentile Latency",
709 | "tooltip": {
710 | "shared": true,
711 | "sort": 0,
712 | "value_type": "individual"
713 | },
714 | "type": "graph",
715 | "xaxis": {
716 | "buckets": null,
717 | "mode": "time",
718 | "name": null,
719 | "show": true,
720 | "values": []
721 | },
722 | "yaxes": [
723 | {
724 | "format": "short",
725 | "label": "Latency [s]",
726 | "logBase": 1,
727 | "max": null,
728 | "min": null,
729 | "show": true
730 | },
731 | {
732 | "format": "short",
733 | "label": null,
734 | "logBase": 1,
735 | "max": null,
736 | "min": null,
737 | "show": false
738 | }
739 | ],
740 | "yaxis": {
741 | "align": false,
742 | "alignLevel": null
743 | }
744 | }
745 | ],
746 | "title": "API",
747 | "type": "row"
748 | }
749 | ],
750 | "refresh": "1m",
751 | "schemaVersion": 22,
752 | "style": "dark",
753 | "tags": [],
754 | "templating": {
755 | "list": [
756 | {
757 | "current": {
758 | "selected": false,
759 | "text": "prometheus",
760 | "value": "prometheus"
761 | },
762 | "hide": 2,
763 | "label": "datasource",
764 | "name": "DS_PROMETHEUS",
765 | "options": [],
766 | "query": "prometheus",
767 | "refresh": 1,
768 | "regex": "",
769 | "type": "datasource"
770 | },
771 | {
772 | "allValue": null,
773 | "current": {},
774 | "datasource": "${DS_PROMETHEUS}",
775 | "definition": "label_values(bbb_api_up, instance)",
776 | "hide": 0,
777 | "includeAll": true,
778 | "index": -1,
779 | "label": "Instance",
780 | "multi": true,
781 | "name": "instance",
782 | "options": [],
783 | "query": "label_values(bbb_api_up, instance)",
784 | "refresh": 1,
785 | "regex": "",
786 | "skipUrlSync": false,
787 | "sort": 3,
788 | "tagValuesQuery": "",
789 | "tags": [],
790 | "tagsQuery": "",
791 | "type": "query",
792 | "useTags": false
793 | }
794 | ]
795 | },
796 | "time": {
797 | "from": "now-12h",
798 | "to": "now"
799 | },
800 | "timepicker": {
801 | "refresh_intervals": [
802 | "15s",
803 | "30s",
804 | "1m",
805 | "5m",
806 | "15m",
807 | "30m",
808 | "1h",
809 | "2h",
810 | "1d"
811 | ]
812 | },
813 | "timezone": "",
814 | "title": "BigBlueButton All Servers",
815 | "uid": "HIbd_CXZz",
816 | "variables": {
817 | "list": []
818 | },
819 | "version": 9
820 | }
821 |
--------------------------------------------------------------------------------
/files/grafana/dashboards/bigbluebutton-server-instance.json:
--------------------------------------------------------------------------------
1 | {
2 | "__inputs": [
3 | {
4 | "name": "DS_PROMETHEUS",
5 | "label": "Prometheus",
6 | "description": "",
7 | "type": "datasource",
8 | "pluginId": "prometheus",
9 | "pluginName": "Prometheus"
10 | }
11 | ],
12 | "__requires": [
13 | {
14 | "type": "grafana",
15 | "id": "grafana",
16 | "name": "Grafana",
17 | "version": "6.7.1"
18 | },
19 | {
20 | "type": "panel",
21 | "id": "graph",
22 | "name": "Graph",
23 | "version": ""
24 | },
25 | {
26 | "type": "datasource",
27 | "id": "prometheus",
28 | "name": "Prometheus",
29 | "version": "1.0.0"
30 | },
31 | {
32 | "type": "panel",
33 | "id": "stat",
34 | "name": "Stat",
35 | "version": ""
36 | }
37 | ],
38 | "annotations": {
39 | "list": [
40 | {
41 | "builtIn": 1,
42 | "datasource": "-- Grafana --",
43 | "enable": true,
44 | "hide": true,
45 | "iconColor": "rgba(0, 211, 255, 1)",
46 | "name": "Annotations & Alerts",
47 | "type": "dashboard"
48 | }
49 | ]
50 | },
51 | "editable": true,
52 | "gnetId": null,
53 | "graphTooltip": 0,
54 | "id": null,
55 | "iteration": 1586958609639,
56 | "links": [],
57 | "panels": [
58 | {
59 | "collapsed": false,
60 | "datasource": "${DS_PROMETHEUS}",
61 | "gridPos": {
62 | "h": 1,
63 | "w": 24,
64 | "x": 0,
65 | "y": 0
66 | },
67 | "id": 36,
68 | "panels": [],
69 | "title": "Main",
70 | "type": "row"
71 | },
72 | {
73 | "cacheTimeout": null,
74 | "datasource": "${DS_PROMETHEUS}",
75 | "gridPos": {
76 | "h": 2,
77 | "w": 4,
78 | "x": 0,
79 | "y": 1
80 | },
81 | "id": 12,
82 | "links": [],
83 | "options": {
84 | "colorMode": "value",
85 | "fieldOptions": {
86 | "calcs": [
87 | "last"
88 | ],
89 | "defaults": {
90 | "mappings": [
91 | {
92 | "from": "",
93 | "id": 1,
94 | "operator": "",
95 | "text": "Online",
96 | "to": "",
97 | "type": 1,
98 | "value": "1"
99 | },
100 | {
101 | "from": "",
102 | "id": 2,
103 | "operator": "",
104 | "text": "Offline",
105 | "to": "",
106 | "type": 1,
107 | "value": "0"
108 | }
109 | ],
110 | "thresholds": {
111 | "mode": "absolute",
112 | "steps": [
113 | {
114 | "color": "red",
115 | "value": null
116 | },
117 | {
118 | "color": "green",
119 | "value": 1
120 | }
121 | ]
122 | }
123 | },
124 | "overrides": [],
125 | "values": false
126 | },
127 | "graphMode": "none",
128 | "justifyMode": "auto",
129 | "orientation": "horizontal"
130 | },
131 | "pluginVersion": "6.7.1",
132 | "targets": [
133 | {
134 | "expr": "bbb_api_up{instance=\"$instance\"}",
135 | "interval": "",
136 | "legendFormat": "",
137 | "refId": "A"
138 | }
139 | ],
140 | "timeFrom": null,
141 | "timeShift": null,
142 | "title": "BBB API",
143 | "type": "stat"
144 | },
145 | {
146 | "aliasColors": {
147 | "Participants": "blue"
148 | },
149 | "bars": false,
150 | "dashLength": 10,
151 | "dashes": false,
152 | "datasource": "${DS_PROMETHEUS}",
153 | "fill": 1,
154 | "fillGradient": 0,
155 | "gridPos": {
156 | "h": 8,
157 | "w": 20,
158 | "x": 4,
159 | "y": 1
160 | },
161 | "hiddenSeries": false,
162 | "id": 2,
163 | "legend": {
164 | "alignAsTable": true,
165 | "avg": true,
166 | "current": true,
167 | "max": true,
168 | "min": true,
169 | "show": true,
170 | "total": false,
171 | "values": true
172 | },
173 | "lines": true,
174 | "linewidth": 1,
175 | "nullPointMode": "null",
176 | "options": {
177 | "dataLinks": []
178 | },
179 | "percentage": false,
180 | "pointradius": 2,
181 | "points": false,
182 | "renderer": "flot",
183 | "seriesOverrides": [],
184 | "spaceLength": 10,
185 | "stack": false,
186 | "steppedLine": false,
187 | "targets": [
188 | {
189 | "expr": "bbb_meetings_participants{instance=\"$instance\"}",
190 | "interval": "",
191 | "legendFormat": "Participants",
192 | "refId": "A"
193 | }
194 | ],
195 | "thresholds": [],
196 | "timeFrom": null,
197 | "timeRegions": [],
198 | "timeShift": null,
199 | "title": "Participants",
200 | "tooltip": {
201 | "shared": true,
202 | "sort": 0,
203 | "value_type": "individual"
204 | },
205 | "type": "graph",
206 | "xaxis": {
207 | "buckets": null,
208 | "mode": "time",
209 | "name": null,
210 | "show": true,
211 | "values": []
212 | },
213 | "yaxes": [
214 | {
215 | "decimals": 0,
216 | "format": "short",
217 | "label": "Participants",
218 | "logBase": 1,
219 | "max": null,
220 | "min": "0",
221 | "show": true
222 | },
223 | {
224 | "format": "short",
225 | "label": null,
226 | "logBase": 1,
227 | "max": null,
228 | "min": null,
229 | "show": false
230 | }
231 | ],
232 | "yaxis": {
233 | "align": false,
234 | "alignLevel": null
235 | }
236 | },
237 | {
238 | "datasource": "${DS_PROMETHEUS}",
239 | "gridPos": {
240 | "h": 4,
241 | "w": 4,
242 | "x": 0,
243 | "y": 3
244 | },
245 | "id": 40,
246 | "options": {
247 | "colorMode": "value",
248 | "fieldOptions": {
249 | "calcs": [
250 | "last"
251 | ],
252 | "defaults": {
253 | "mappings": [],
254 | "thresholds": {
255 | "mode": "absolute",
256 | "steps": [
257 | {
258 | "color": "green",
259 | "value": null
260 | }
261 | ]
262 | }
263 | },
264 | "overrides": [],
265 | "values": false
266 | },
267 | "graphMode": "area",
268 | "justifyMode": "auto",
269 | "orientation": "horizontal"
270 | },
271 | "pluginVersion": "6.7.1",
272 | "targets": [
273 | {
274 | "expr": "bbb_meetings{instance=\"$instance\"}",
275 | "interval": "",
276 | "legendFormat": "",
277 | "refId": "A"
278 | }
279 | ],
280 | "timeFrom": null,
281 | "timeShift": null,
282 | "title": "Active Meetings",
283 | "type": "stat"
284 | },
285 | {
286 | "datasource": "${DS_PROMETHEUS}",
287 | "gridPos": {
288 | "h": 4,
289 | "w": 4,
290 | "x": 0,
291 | "y": 7
292 | },
293 | "id": 30,
294 | "options": {
295 | "colorMode": "value",
296 | "fieldOptions": {
297 | "calcs": [
298 | "max"
299 | ],
300 | "defaults": {
301 | "mappings": [],
302 | "thresholds": {
303 | "mode": "absolute",
304 | "steps": [
305 | {
306 | "color": "green",
307 | "value": null
308 | }
309 | ]
310 | }
311 | },
312 | "overrides": [],
313 | "values": false
314 | },
315 | "graphMode": "none",
316 | "justifyMode": "auto",
317 | "orientation": "horizontal"
318 | },
319 | "pluginVersion": "6.7.1",
320 | "targets": [
321 | {
322 | "expr": "bbb_meetings_participants{instance=\"$instance\"}",
323 | "interval": "",
324 | "legendFormat": "Selected timeframe",
325 | "refId": "A"
326 | },
327 | {
328 | "expr": "max_over_time(bbb_meetings_participants{instance=\"$instance\"}[100y])",
329 | "interval": "",
330 | "legendFormat": "All time",
331 | "refId": "B"
332 | }
333 | ],
334 | "timeFrom": null,
335 | "timeShift": null,
336 | "title": "Max Participants",
337 | "type": "stat"
338 | },
339 | {
340 | "aliasColors": {
341 | "Participants": "blue"
342 | },
343 | "bars": false,
344 | "dashLength": 10,
345 | "dashes": false,
346 | "datasource": "${DS_PROMETHEUS}",
347 | "fill": 1,
348 | "fillGradient": 0,
349 | "gridPos": {
350 | "h": 8,
351 | "w": 14,
352 | "x": 4,
353 | "y": 9
354 | },
355 | "hiddenSeries": false,
356 | "id": 34,
357 | "legend": {
358 | "alignAsTable": true,
359 | "avg": true,
360 | "current": true,
361 | "max": true,
362 | "min": true,
363 | "show": true,
364 | "total": false,
365 | "values": true
366 | },
367 | "lines": true,
368 | "linewidth": 1,
369 | "nullPointMode": "null",
370 | "options": {
371 | "dataLinks": []
372 | },
373 | "percentage": false,
374 | "pointradius": 2,
375 | "points": false,
376 | "renderer": "flot",
377 | "seriesOverrides": [
378 | {
379 | "alias": "Participants",
380 | "yaxis": 2
381 | }
382 | ],
383 | "spaceLength": 10,
384 | "stack": false,
385 | "steppedLine": false,
386 | "targets": [
387 | {
388 | "expr": "100 - (avg by (instance) (irate(node_cpu_seconds_total{job=\"node\",mode=\"idle\",instance=\"$instance\"}[5m])) * 100)",
389 | "interval": "",
390 | "legendFormat": "CPU Utilization",
391 | "refId": "A"
392 | },
393 | {
394 | "expr": "bbb_meetings_participants{instance=\"$instance\"}",
395 | "interval": "",
396 | "legendFormat": "Participants",
397 | "refId": "B"
398 | }
399 | ],
400 | "thresholds": [],
401 | "timeFrom": null,
402 | "timeRegions": [],
403 | "timeShift": null,
404 | "title": "CPU Utilization vs. Participants",
405 | "tooltip": {
406 | "shared": true,
407 | "sort": 0,
408 | "value_type": "individual"
409 | },
410 | "type": "graph",
411 | "xaxis": {
412 | "buckets": null,
413 | "mode": "time",
414 | "name": null,
415 | "show": true,
416 | "values": []
417 | },
418 | "yaxes": [
419 | {
420 | "format": "percent",
421 | "label": "CPU Utlization [%]",
422 | "logBase": 1,
423 | "max": "100",
424 | "min": null,
425 | "show": true
426 | },
427 | {
428 | "decimals": 0,
429 | "format": "short",
430 | "label": "Participants",
431 | "logBase": 1,
432 | "max": null,
433 | "min": "0",
434 | "show": true
435 | }
436 | ],
437 | "yaxis": {
438 | "align": false,
439 | "alignLevel": null
440 | }
441 | },
442 | {
443 | "aliasColors": {
444 | "Voice participants": "orange"
445 | },
446 | "bars": false,
447 | "dashLength": 10,
448 | "dashes": false,
449 | "datasource": "${DS_PROMETHEUS}",
450 | "fill": 1,
451 | "fillGradient": 0,
452 | "gridPos": {
453 | "h": 8,
454 | "w": 6,
455 | "x": 18,
456 | "y": 9
457 | },
458 | "hiddenSeries": false,
459 | "id": 6,
460 | "legend": {
461 | "alignAsTable": true,
462 | "avg": true,
463 | "current": true,
464 | "max": true,
465 | "min": true,
466 | "rightSide": false,
467 | "show": true,
468 | "total": false,
469 | "values": true
470 | },
471 | "lines": true,
472 | "linewidth": 1,
473 | "nullPointMode": "null",
474 | "options": {
475 | "dataLinks": []
476 | },
477 | "percentage": false,
478 | "pointradius": 2,
479 | "points": false,
480 | "renderer": "flot",
481 | "seriesOverrides": [],
482 | "spaceLength": 10,
483 | "stack": false,
484 | "steppedLine": false,
485 | "targets": [
486 | {
487 | "expr": "bbb_meetings_voice_participants{instance=\"$instance\"}",
488 | "interval": "",
489 | "legendFormat": "Voice participants",
490 | "refId": "A"
491 | }
492 | ],
493 | "thresholds": [],
494 | "timeFrom": null,
495 | "timeRegions": [],
496 | "timeShift": null,
497 | "title": "Voice Participants",
498 | "tooltip": {
499 | "shared": true,
500 | "sort": 0,
501 | "value_type": "individual"
502 | },
503 | "type": "graph",
504 | "xaxis": {
505 | "buckets": null,
506 | "mode": "time",
507 | "name": null,
508 | "show": true,
509 | "values": []
510 | },
511 | "yaxes": [
512 | {
513 | "decimals": 0,
514 | "format": "short",
515 | "label": "Participants",
516 | "logBase": 1,
517 | "max": null,
518 | "min": "0",
519 | "show": true
520 | },
521 | {
522 | "format": "short",
523 | "label": null,
524 | "logBase": 1,
525 | "max": null,
526 | "min": null,
527 | "show": false
528 | }
529 | ],
530 | "yaxis": {
531 | "align": false,
532 | "alignLevel": null
533 | }
534 | },
535 | {
536 | "datasource": "${DS_PROMETHEUS}",
537 | "gridPos": {
538 | "h": 7,
539 | "w": 4,
540 | "x": 0,
541 | "y": 11
542 | },
543 | "id": 26,
544 | "options": {
545 | "colorMode": "value",
546 | "fieldOptions": {
547 | "calcs": [
548 | "last"
549 | ],
550 | "defaults": {
551 | "mappings": [],
552 | "thresholds": {
553 | "mode": "absolute",
554 | "steps": [
555 | {
556 | "color": "green",
557 | "value": null
558 | }
559 | ]
560 | }
561 | },
562 | "overrides": [],
563 | "values": false
564 | },
565 | "graphMode": "none",
566 | "justifyMode": "auto",
567 | "orientation": "horizontal"
568 | },
569 | "pluginVersion": "6.7.1",
570 | "targets": [
571 | {
572 | "expr": "bbb_meetings_participants{instance=\"$instance\"}",
573 | "interval": "",
574 | "legendFormat": "Participants",
575 | "refId": "A"
576 | },
577 | {
578 | "expr": "bbb_meetings_listeners{instance=\"$instance\"}",
579 | "interval": "",
580 | "legendFormat": "Listeners",
581 | "refId": "B"
582 | },
583 | {
584 | "expr": "bbb_meetings_voice_participants{instance=\"$instance\"}",
585 | "interval": "",
586 | "legendFormat": "Voice",
587 | "refId": "C"
588 | },
589 | {
590 | "expr": "bbb_meetings_video_participants{instance=\"$instance\"}",
591 | "interval": "",
592 | "legendFormat": "Video",
593 | "refId": "D"
594 | }
595 | ],
596 | "timeFrom": null,
597 | "timeShift": null,
598 | "title": "Type of Users Count",
599 | "type": "stat"
600 | },
601 | {
602 | "aliasColors": {
603 | "Participants": "blue"
604 | },
605 | "bars": false,
606 | "dashLength": 10,
607 | "dashes": false,
608 | "datasource": "${DS_PROMETHEUS}",
609 | "fill": 1,
610 | "fillGradient": 0,
611 | "gridPos": {
612 | "h": 9,
613 | "w": 14,
614 | "x": 4,
615 | "y": 17
616 | },
617 | "hiddenSeries": false,
618 | "id": 32,
619 | "legend": {
620 | "alignAsTable": true,
621 | "avg": true,
622 | "current": true,
623 | "max": true,
624 | "min": true,
625 | "show": true,
626 | "total": false,
627 | "values": true
628 | },
629 | "lines": true,
630 | "linewidth": 1,
631 | "nullPointMode": "null",
632 | "options": {
633 | "dataLinks": []
634 | },
635 | "percentage": false,
636 | "pointradius": 2,
637 | "points": false,
638 | "renderer": "flot",
639 | "seriesOverrides": [
640 | {
641 | "alias": "Participants",
642 | "yaxis": 2
643 | },
644 | {
645 | "alias": "CPU total utilization",
646 | "yaxis": 2
647 | }
648 | ],
649 | "spaceLength": 10,
650 | "stack": false,
651 | "steppedLine": false,
652 | "targets": [
653 | {
654 | "expr": "irate(node_network_transmit_bytes_total{instance=\"$instance\",device!~'tap.*|veth.*|br.*|docker.*|virbr*|lo*'}[5m])*8",
655 | "interval": "",
656 | "intervalFactor": 1,
657 | "legendFormat": "Bandwidth out",
658 | "refId": "A"
659 | },
660 | {
661 | "expr": "irate(node_network_receive_bytes_total{instance=\"$instance\",device!~'tap.*|veth.*|br.*|docker.*|virbr*|lo*'}[5m])*8",
662 | "interval": "",
663 | "intervalFactor": 1,
664 | "legendFormat": "Bandwidth in",
665 | "refId": "C"
666 | },
667 | {
668 | "expr": "bbb_meetings_participants{instance=\"$instance\"}",
669 | "format": "time_series",
670 | "instant": false,
671 | "interval": "",
672 | "intervalFactor": 1,
673 | "legendFormat": "Participants",
674 | "refId": "B"
675 | }
676 | ],
677 | "thresholds": [],
678 | "timeFrom": null,
679 | "timeRegions": [],
680 | "timeShift": null,
681 | "title": "Bandwidth vs. Participants",
682 | "tooltip": {
683 | "shared": true,
684 | "sort": 0,
685 | "value_type": "individual"
686 | },
687 | "type": "graph",
688 | "xaxis": {
689 | "buckets": null,
690 | "mode": "time",
691 | "name": null,
692 | "show": true,
693 | "values": []
694 | },
695 | "yaxes": [
696 | {
697 | "format": "bps",
698 | "label": "Bandwidth",
699 | "logBase": 1,
700 | "max": null,
701 | "min": "0",
702 | "show": true
703 | },
704 | {
705 | "decimals": 0,
706 | "format": "short",
707 | "label": "Participants",
708 | "logBase": 1,
709 | "max": null,
710 | "min": "0",
711 | "show": true
712 | }
713 | ],
714 | "yaxis": {
715 | "align": false,
716 | "alignLevel": null
717 | }
718 | },
719 | {
720 | "aliasColors": {
721 | "Voice participants": "orange"
722 | },
723 | "bars": false,
724 | "dashLength": 10,
725 | "dashes": false,
726 | "datasource": "${DS_PROMETHEUS}",
727 | "fill": 1,
728 | "fillGradient": 0,
729 | "gridPos": {
730 | "h": 9,
731 | "w": 6,
732 | "x": 18,
733 | "y": 17
734 | },
735 | "hiddenSeries": false,
736 | "id": 8,
737 | "legend": {
738 | "alignAsTable": true,
739 | "avg": true,
740 | "current": true,
741 | "max": true,
742 | "min": true,
743 | "show": true,
744 | "total": false,
745 | "values": true
746 | },
747 | "lines": true,
748 | "linewidth": 1,
749 | "nullPointMode": "null",
750 | "options": {
751 | "dataLinks": []
752 | },
753 | "percentage": false,
754 | "pointradius": 2,
755 | "points": false,
756 | "renderer": "flot",
757 | "seriesOverrides": [],
758 | "spaceLength": 10,
759 | "stack": false,
760 | "steppedLine": false,
761 | "targets": [
762 | {
763 | "expr": "bbb_meetings_video_participants{instance=\"$instance\"}",
764 | "interval": "",
765 | "legendFormat": "Voice participants",
766 | "refId": "A"
767 | }
768 | ],
769 | "thresholds": [],
770 | "timeFrom": null,
771 | "timeRegions": [],
772 | "timeShift": null,
773 | "title": "Video Participants",
774 | "tooltip": {
775 | "shared": true,
776 | "sort": 0,
777 | "value_type": "individual"
778 | },
779 | "type": "graph",
780 | "xaxis": {
781 | "buckets": null,
782 | "mode": "time",
783 | "name": null,
784 | "show": true,
785 | "values": []
786 | },
787 | "yaxes": [
788 | {
789 | "decimals": 0,
790 | "format": "short",
791 | "label": "Participants",
792 | "logBase": 1,
793 | "max": null,
794 | "min": "0",
795 | "show": true
796 | },
797 | {
798 | "format": "short",
799 | "label": null,
800 | "logBase": 1,
801 | "max": null,
802 | "min": null,
803 | "show": false
804 | }
805 | ],
806 | "yaxis": {
807 | "align": false,
808 | "alignLevel": null
809 | }
810 | },
811 | {
812 | "datasource": "${DS_PROMETHEUS}",
813 | "gridPos": {
814 | "h": 2,
815 | "w": 4,
816 | "x": 0,
817 | "y": 18
818 | },
819 | "id": 24,
820 | "options": {
821 | "colorMode": "value",
822 | "fieldOptions": {
823 | "calcs": [
824 | "last"
825 | ],
826 | "defaults": {
827 | "mappings": [],
828 | "thresholds": {
829 | "mode": "absolute",
830 | "steps": [
831 | {
832 | "color": "green",
833 | "value": null
834 | }
835 | ]
836 | }
837 | },
838 | "overrides": [],
839 | "values": false
840 | },
841 | "graphMode": "area",
842 | "justifyMode": "auto",
843 | "orientation": "horizontal"
844 | },
845 | "pluginVersion": "6.7.1",
846 | "targets": [
847 | {
848 | "expr": "bbb_meetings_participants - bbb_meetings_listeners{instance=\"$instance\"}",
849 | "interval": "",
850 | "legendFormat": "{{instance}}",
851 | "refId": "A"
852 | }
853 | ],
854 | "timeFrom": null,
855 | "timeShift": null,
856 | "title": "(Participants - Listeners) Delta",
857 | "type": "stat"
858 | },
859 | {
860 | "datasource": "${DS_PROMETHEUS}",
861 | "gridPos": {
862 | "h": 6,
863 | "w": 4,
864 | "x": 0,
865 | "y": 20
866 | },
867 | "id": 16,
868 | "options": {
869 | "colorMode": "value",
870 | "fieldOptions": {
871 | "calcs": [
872 | "last"
873 | ],
874 | "defaults": {
875 | "mappings": [],
876 | "thresholds": {
877 | "mode": "absolute",
878 | "steps": [
879 | {
880 | "color": "green",
881 | "value": null
882 | }
883 | ]
884 | }
885 | },
886 | "overrides": [],
887 | "values": false
888 | },
889 | "graphMode": "none",
890 | "justifyMode": "auto",
891 | "orientation": "auto"
892 | },
893 | "pluginVersion": "6.7.1",
894 | "targets": [
895 | {
896 | "expr": "bbb_recordings_processing{instance=\"$instance\"}",
897 | "interval": "",
898 | "legendFormat": "Processing",
899 | "refId": "B"
900 | },
901 | {
902 | "expr": "bbb_recordings_processed{instance=\"$instance\"}",
903 | "interval": "",
904 | "legendFormat": "Processed",
905 | "refId": "A"
906 | },
907 | {
908 | "expr": "bbb_recordings_published{instance=\"$instance\"}",
909 | "interval": "",
910 | "legendFormat": "Published",
911 | "refId": "E"
912 | },
913 | {
914 | "expr": "bbb_recordings_unpublished{instance=\"$instance\"}",
915 | "interval": "",
916 | "legendFormat": "Unpublished",
917 | "refId": "C"
918 | },
919 | {
920 | "expr": "bbb_recordings_deleted{instance=\"$instance\"}",
921 | "interval": "",
922 | "legendFormat": "Deleted",
923 | "refId": "D"
924 | }
925 | ],
926 | "timeFrom": null,
927 | "timeShift": null,
928 | "title": "Recordings",
929 | "type": "stat"
930 | },
931 | {
932 | "collapsed": true,
933 | "datasource": "${DS_PROMETHEUS}",
934 | "gridPos": {
935 | "h": 1,
936 | "w": 24,
937 | "x": 0,
938 | "y": 26
939 | },
940 | "id": 38,
941 | "panels": [
942 | {
943 | "aliasColors": {
944 | "getRecordings deleted": "purple"
945 | },
946 | "bars": false,
947 | "cacheTimeout": null,
948 | "dashLength": 10,
949 | "dashes": false,
950 | "datasource": "${DS_PROMETHEUS}",
951 | "fill": 1,
952 | "fillGradient": 0,
953 | "gridPos": {
954 | "h": 7,
955 | "w": 14,
956 | "x": 0,
957 | "y": 27
958 | },
959 | "hiddenSeries": false,
960 | "id": 22,
961 | "legend": {
962 | "alignAsTable": true,
963 | "avg": true,
964 | "current": true,
965 | "max": true,
966 | "min": true,
967 | "rightSide": true,
968 | "show": true,
969 | "total": false,
970 | "values": true
971 | },
972 | "lines": true,
973 | "linewidth": 1,
974 | "links": [],
975 | "nullPointMode": "null",
976 | "options": {
977 | "dataLinks": []
978 | },
979 | "percentage": false,
980 | "pluginVersion": "6.7.0",
981 | "pointradius": 2,
982 | "points": false,
983 | "renderer": "flot",
984 | "seriesOverrides": [],
985 | "spaceLength": 10,
986 | "stack": false,
987 | "steppedLine": false,
988 | "targets": [
989 | {
990 | "expr": "histogram_quantile(0.95, sum(rate(bbb_api_latency_bucket{endpoint=\"getMeetings\", instance=\"$instance\"}[5m])) by (le, instance))",
991 | "instant": false,
992 | "interval": "",
993 | "intervalFactor": 1,
994 | "legendFormat": "getMeetings",
995 | "refId": "A"
996 | },
997 | {
998 | "expr": "histogram_quantile(0.95, sum(rate(bbb_api_latency_bucket{endpoint=\"getRecordings\", parameters=\"state=processing\", instance=\"$instance\"}[5m])) by (le, instance))",
999 | "hide": false,
1000 | "interval": "",
1001 | "legendFormat": "getRecordings processing",
1002 | "refId": "B"
1003 | },
1004 | {
1005 | "expr": "histogram_quantile(0.95, sum(rate(bbb_api_latency_bucket{endpoint=\"getRecordings\", parameters=\"state=processed\", instance=\"$instance\"}[5m])) by (le, instance))",
1006 | "interval": "",
1007 | "legendFormat": "getRecordings processed",
1008 | "refId": "C"
1009 | },
1010 | {
1011 | "expr": "histogram_quantile(0.95, sum(rate(bbb_api_latency_bucket{endpoint=\"getRecordings\", parameters=\"state=published\", instance=\"$instance\"}[5m])) by (le, instance))",
1012 | "interval": "",
1013 | "legendFormat": "getRecordings published",
1014 | "refId": "D"
1015 | },
1016 | {
1017 | "expr": "histogram_quantile(0.95, sum(rate(bbb_api_latency_bucket{endpoint=\"getRecordings\", parameters=\"state=unpublished\", instance=\"$instance\"}[5m])) by (le, instance))",
1018 | "interval": "",
1019 | "legendFormat": "getRecordings unpublished",
1020 | "refId": "E"
1021 | },
1022 | {
1023 | "expr": "histogram_quantile(0.95, sum(rate(bbb_api_latency_bucket{endpoint=\"getRecordings\", parameters=\"state=deleted\", instance=\"$instance\"}[5m])) by (le, instance))",
1024 | "interval": "",
1025 | "legendFormat": "getRecordings deleted",
1026 | "refId": "F"
1027 | }
1028 | ],
1029 | "thresholds": [],
1030 | "timeFrom": null,
1031 | "timeRegions": [],
1032 | "timeShift": null,
1033 | "title": "API 95-th Percentile Latency",
1034 | "tooltip": {
1035 | "shared": true,
1036 | "sort": 0,
1037 | "value_type": "individual"
1038 | },
1039 | "type": "graph",
1040 | "xaxis": {
1041 | "buckets": null,
1042 | "mode": "time",
1043 | "name": null,
1044 | "show": true,
1045 | "values": []
1046 | },
1047 | "yaxes": [
1048 | {
1049 | "decimals": null,
1050 | "format": "short",
1051 | "label": "Latency [s]",
1052 | "logBase": 1,
1053 | "max": null,
1054 | "min": null,
1055 | "show": true
1056 | },
1057 | {
1058 | "format": "short",
1059 | "label": "",
1060 | "logBase": 1,
1061 | "max": null,
1062 | "min": null,
1063 | "show": false
1064 | }
1065 | ],
1066 | "yaxis": {
1067 | "align": false,
1068 | "alignLevel": null
1069 | }
1070 | }
1071 | ],
1072 | "title": "API",
1073 | "type": "row"
1074 | }
1075 | ],
1076 | "refresh": "30s",
1077 | "schemaVersion": 22,
1078 | "style": "dark",
1079 | "tags": [],
1080 | "templating": {
1081 | "list": [
1082 | {
1083 | "current": {
1084 | "selected": false,
1085 | "text": "prometheus",
1086 | "value": "prometheus"
1087 | },
1088 | "hide": 2,
1089 | "label": "datasource",
1090 | "name": "DS_PROMETHEUS",
1091 | "options": [],
1092 | "query": "prometheus",
1093 | "refresh": 1,
1094 | "regex": "",
1095 | "type": "datasource"
1096 | },
1097 | {
1098 | "allValue": null,
1099 | "current": {},
1100 | "datasource": "${DS_PROMETHEUS}",
1101 | "definition": "label_values(bbb_api_up, instance)",
1102 | "hide": 0,
1103 | "includeAll": false,
1104 | "index": -1,
1105 | "label": "Instance",
1106 | "multi": false,
1107 | "name": "instance",
1108 | "options": [],
1109 | "query": "label_values(bbb_api_up, instance)",
1110 | "refresh": 1,
1111 | "regex": "",
1112 | "skipUrlSync": false,
1113 | "sort": 0,
1114 | "tagValuesQuery": "",
1115 | "tags": [],
1116 | "tagsQuery": "",
1117 | "type": "query",
1118 | "useTags": false
1119 | }
1120 | ]
1121 | },
1122 | "time": {
1123 | "from": "now-6h",
1124 | "to": "now"
1125 | },
1126 | "timepicker": {
1127 | "refresh_intervals": [
1128 | "15s",
1129 | "30s",
1130 | "1m",
1131 | "5m",
1132 | "15m",
1133 | "30m",
1134 | "1h",
1135 | "2h",
1136 | "1d"
1137 | ]
1138 | },
1139 | "timezone": "",
1140 | "title": "BigBlueButton Server Instance",
1141 | "uid": "HIbd_CXZz2",
1142 | "variables": {
1143 | "list": []
1144 | },
1145 | "version": 14
1146 | }
1147 |
--------------------------------------------------------------------------------
/files/grafana/dashboards/cockpit.json:
--------------------------------------------------------------------------------
1 | {
2 | "annotations": {
3 | "list": [
4 | {
5 | "builtIn": 1,
6 | "datasource": "-- Grafana --",
7 | "enable": true,
8 | "hide": true,
9 | "iconColor": "rgba(0, 211, 255, 1)",
10 | "name": "Annotations & Alerts",
11 | "type": "dashboard"
12 | }
13 | ]
14 | },
15 | "description": "Fuer Leute mit wenig Beinfreiheit",
16 | "editable": true,
17 | "gnetId": null,
18 | "graphTooltip": 0,
19 | "iteration": 1619357145525,
20 | "links": [],
21 | "panels": [
22 | {
23 | "aliasColors": {},
24 | "bars": false,
25 | "dashLength": 10,
26 | "dashes": false,
27 | "datasource": "${DS_PROMETHEUS}",
28 | "description": "",
29 | "fieldConfig": {
30 | "defaults": {
31 | "links": []
32 | },
33 | "overrides": []
34 | },
35 | "fill": 1,
36 | "fillGradient": 3,
37 | "gridPos": {
38 | "h": 4,
39 | "w": 11,
40 | "x": 0,
41 | "y": 0
42 | },
43 | "hiddenSeries": false,
44 | "id": 8,
45 | "legend": {
46 | "avg": false,
47 | "current": false,
48 | "max": false,
49 | "min": false,
50 | "show": true,
51 | "total": false,
52 | "values": false
53 | },
54 | "lines": true,
55 | "linewidth": 1,
56 | "nullPointMode": "null",
57 | "options": {
58 | "alertThreshold": true
59 | },
60 | "percentage": false,
61 | "pluginVersion": "7.5.4",
62 | "pointradius": 2,
63 | "points": false,
64 | "renderer": "flot",
65 | "seriesOverrides": [],
66 | "spaceLength": 10,
67 | "stack": false,
68 | "steppedLine": false,
69 | "targets": [
70 | {
71 | "expr": "sum(bbb_meetings)",
72 | "interval": "",
73 | "legendFormat": "Meetings",
74 | "refId": "A"
75 | },
76 | {
77 | "expr": "sum(bbb_meetings_participants)",
78 | "interval": "",
79 | "legendFormat": "Teilnehmende",
80 | "refId": "B"
81 | },
82 | {
83 | "expr": "sum(bbb_meetings_video_participants)",
84 | "interval": "",
85 | "legendFormat": "Videos",
86 | "refId": "C"
87 | }
88 | ],
89 | "thresholds": [],
90 | "timeFrom": null,
91 | "timeRegions": [],
92 | "timeShift": null,
93 | "title": "Zusammenfassung",
94 | "tooltip": {
95 | "shared": true,
96 | "sort": 0,
97 | "value_type": "individual"
98 | },
99 | "type": "graph",
100 | "xaxis": {
101 | "buckets": null,
102 | "mode": "time",
103 | "name": null,
104 | "show": true,
105 | "values": []
106 | },
107 | "yaxes": [
108 | {
109 | "$$hashKey": "object:278",
110 | "format": "short",
111 | "label": null,
112 | "logBase": 1,
113 | "max": null,
114 | "min": null,
115 | "show": true
116 | },
117 | {
118 | "$$hashKey": "object:279",
119 | "format": "short",
120 | "label": null,
121 | "logBase": 1,
122 | "max": null,
123 | "min": null,
124 | "show": true
125 | }
126 | ],
127 | "yaxis": {
128 | "align": false,
129 | "alignLevel": null
130 | }
131 | },
132 | {
133 | "datasource": "${DS_PROMETHEUS}",
134 | "fieldConfig": {
135 | "defaults": {
136 | "color": {
137 | "mode": "thresholds"
138 | },
139 | "mappings": [],
140 | "thresholds": {
141 | "mode": "absolute",
142 | "steps": [
143 | {
144 | "color": "dark-blue",
145 | "value": null
146 | }
147 | ]
148 | }
149 | },
150 | "overrides": []
151 | },
152 | "gridPos": {
153 | "h": 4,
154 | "w": 5,
155 | "x": 11,
156 | "y": 0
157 | },
158 | "id": 6,
159 | "options": {
160 | "colorMode": "value",
161 | "graphMode": "area",
162 | "justifyMode": "auto",
163 | "orientation": "auto",
164 | "reduceOptions": {
165 | "calcs": [
166 | "last"
167 | ],
168 | "fields": "",
169 | "values": false
170 | },
171 | "text": {},
172 | "textMode": "auto"
173 | },
174 | "pluginVersion": "7.5.4",
175 | "targets": [
176 | {
177 | "expr": "sum(bbb_meetings_participants)",
178 | "interval": "",
179 | "legendFormat": "Teilnehmende",
180 | "refId": "B"
181 | },
182 | {
183 | "expr": "sum(bbb_meetings)",
184 | "interval": "",
185 | "legendFormat": "Meetings",
186 | "refId": "A"
187 | },
188 | {
189 | "expr": "sum(bbb_meetings_video_participants)",
190 | "interval": "",
191 | "legendFormat": "Videoteilnehmende",
192 | "refId": "C"
193 | }
194 | ],
195 | "timeFrom": null,
196 | "timeShift": null,
197 | "title": "Aktueller Status",
198 | "type": "stat"
199 | },
200 | {
201 | "datasource": "${DS_PROMETHEUS}",
202 | "fieldConfig": {
203 | "defaults": {
204 | "color": {
205 | "mode": "thresholds"
206 | },
207 | "mappings": [],
208 | "thresholds": {
209 | "mode": "absolute",
210 | "steps": [
211 | {
212 | "color": "green",
213 | "value": null
214 | }
215 | ]
216 | }
217 | },
218 | "overrides": []
219 | },
220 | "gridPos": {
221 | "h": 4,
222 | "w": 4,
223 | "x": 16,
224 | "y": 0
225 | },
226 | "id": 10,
227 | "options": {
228 | "colorMode": "value",
229 | "graphMode": "none",
230 | "justifyMode": "auto",
231 | "orientation": "auto",
232 | "reduceOptions": {
233 | "calcs": [
234 | "max"
235 | ],
236 | "fields": "",
237 | "values": false
238 | },
239 | "text": {},
240 | "textMode": "auto"
241 | },
242 | "pluginVersion": "7.5.4",
243 | "targets": [
244 | {
245 | "expr": "sum(bbb_meetings_participants)",
246 | "interval": "",
247 | "legendFormat": "Teilnehmende",
248 | "refId": "B"
249 | },
250 | {
251 | "expr": "sum(bbb_meetings)",
252 | "interval": "",
253 | "legendFormat": "Meetings",
254 | "refId": "A"
255 | },
256 | {
257 | "expr": "sum(bbb_meetings_video_participants)",
258 | "interval": "",
259 | "legendFormat": "Videoteilnehmende",
260 | "refId": "C"
261 | }
262 | ],
263 | "timeFrom": null,
264 | "timeShift": null,
265 | "title": "Maximum bisher",
266 | "type": "stat"
267 | },
268 | {
269 | "datasource": null,
270 | "fieldConfig": {
271 | "defaults": {
272 | "color": {
273 | "mode": "thresholds"
274 | },
275 | "mappings": [],
276 | "max": 1,
277 | "min": 0,
278 | "thresholds": {
279 | "mode": "absolute",
280 | "steps": [
281 | {
282 | "color": "green",
283 | "value": null
284 | },
285 | {
286 | "color": "#EAB839",
287 | "value": 0.8
288 | },
289 | {
290 | "color": "red",
291 | "value": 1
292 | }
293 | ]
294 | }
295 | },
296 | "overrides": []
297 | },
298 | "gridPos": {
299 | "h": 4,
300 | "w": 3,
301 | "x": 20,
302 | "y": 0
303 | },
304 | "id": 12,
305 | "options": {
306 | "colorMode": "value",
307 | "graphMode": "area",
308 | "justifyMode": "auto",
309 | "orientation": "horizontal",
310 | "reduceOptions": {
311 | "calcs": [
312 | "last"
313 | ],
314 | "fields": "",
315 | "values": false
316 | },
317 | "text": {},
318 | "textMode": "auto"
319 | },
320 | "pluginVersion": "7.5.4",
321 | "targets": [
322 | {
323 | "exemplar": true,
324 | "expr": "node_load1",
325 | "instant": false,
326 | "interval": "",
327 | "legendFormat": "{{instance}}",
328 | "refId": "A"
329 | }
330 | ],
331 | "timeFrom": null,
332 | "timeShift": null,
333 | "title": "UlmLernt.de Load",
334 | "transformations": [
335 | {
336 | "id": "filterFieldsByName",
337 | "options": {
338 | "include": {
339 | "names": [
340 | "Time",
341 | "fsn03.ulmlernt.org"
342 | ]
343 | }
344 | }
345 | }
346 | ],
347 | "transparent": true,
348 | "type": "stat"
349 | },
350 | {
351 | "aliasColors": {},
352 | "bars": false,
353 | "dashLength": 10,
354 | "dashes": false,
355 | "datasource": null,
356 | "fieldConfig": {
357 | "defaults": {},
358 | "overrides": []
359 | },
360 | "fill": 1,
361 | "fillGradient": 0,
362 | "gridPos": {
363 | "h": 12,
364 | "w": 12,
365 | "x": 0,
366 | "y": 4
367 | },
368 | "hiddenSeries": false,
369 | "id": 16,
370 | "legend": {
371 | "avg": false,
372 | "current": false,
373 | "max": false,
374 | "min": false,
375 | "show": true,
376 | "total": false,
377 | "values": false
378 | },
379 | "lines": true,
380 | "linewidth": 1,
381 | "nullPointMode": "null",
382 | "options": {
383 | "alertThreshold": true
384 | },
385 | "percentage": false,
386 | "pluginVersion": "7.5.4",
387 | "pointradius": 2,
388 | "points": false,
389 | "renderer": "flot",
390 | "seriesOverrides": [],
391 | "spaceLength": 10,
392 | "stack": false,
393 | "steppedLine": false,
394 | "targets": [
395 | {
396 | "exemplar": true,
397 | "expr": "histogram_quantile(0.95, sum(rate(bbb_api_latency_bucket{endpoint=\"getMeetings\"}[5m])) by (le, instance)) ",
398 | "interval": "",
399 | "legendFormat": "{{instance}}",
400 | "refId": "A"
401 | }
402 | ],
403 | "thresholds": [],
404 | "timeRegions": [],
405 | "title": "95 Quantil Latency BBB Server",
406 | "tooltip": {
407 | "shared": true,
408 | "sort": 0,
409 | "value_type": "individual"
410 | },
411 | "type": "graph",
412 | "xaxis": {
413 | "buckets": null,
414 | "mode": "time",
415 | "name": null,
416 | "show": true,
417 | "values": []
418 | },
419 | "yaxes": [
420 | {
421 | "format": "short",
422 | "label": null,
423 | "logBase": 1,
424 | "max": null,
425 | "min": null,
426 | "show": true
427 | },
428 | {
429 | "format": "short",
430 | "label": null,
431 | "logBase": 1,
432 | "max": null,
433 | "min": null,
434 | "show": true
435 | }
436 | ],
437 | "yaxis": {
438 | "align": false,
439 | "alignLevel": null
440 | }
441 | },
442 | {
443 | "datasource": "${DS_PROMETHEUS}",
444 | "fieldConfig": {
445 | "defaults": {
446 | "color": {
447 | "mode": "thresholds"
448 | },
449 | "mappings": [],
450 | "max": 60,
451 | "min": 0,
452 | "thresholds": {
453 | "mode": "absolute",
454 | "steps": [
455 | {
456 | "color": "green",
457 | "value": null
458 | },
459 | {
460 | "color": "dark-yellow",
461 | "value": 20
462 | },
463 | {
464 | "color": "dark-red",
465 | "value": 40
466 | }
467 | ]
468 | }
469 | },
470 | "overrides": []
471 | },
472 | "gridPos": {
473 | "h": 12,
474 | "w": 4,
475 | "x": 12,
476 | "y": 4
477 | },
478 | "id": 4,
479 | "options": {
480 | "displayMode": "lcd",
481 | "orientation": "horizontal",
482 | "reduceOptions": {
483 | "calcs": [
484 | "last"
485 | ],
486 | "fields": "",
487 | "values": false
488 | },
489 | "showUnfilled": true,
490 | "text": {}
491 | },
492 | "pluginVersion": "7.5.4",
493 | "targets": [
494 | {
495 | "expr": "bbb_meetings_video_participants",
496 | "interval": "",
497 | "legendFormat": "{{instance}}",
498 | "refId": "A"
499 | }
500 | ],
501 | "timeFrom": null,
502 | "timeShift": null,
503 | "title": "Video Participants",
504 | "transparent": true,
505 | "type": "bargauge"
506 | },
507 | {
508 | "cacheTimeout": null,
509 | "datasource": "${DS_PROMETHEUS}",
510 | "description": "",
511 | "fieldConfig": {
512 | "defaults": {
513 | "color": {
514 | "mode": "thresholds"
515 | },
516 | "mappings": [],
517 | "max": 350,
518 | "min": 0,
519 | "thresholds": {
520 | "mode": "absolute",
521 | "steps": [
522 | {
523 | "color": "green",
524 | "value": null
525 | },
526 | {
527 | "color": "#EAB839",
528 | "value": 150
529 | },
530 | {
531 | "color": "red",
532 | "value": 250
533 | }
534 | ]
535 | },
536 | "unit": "none"
537 | },
538 | "overrides": []
539 | },
540 | "gridPos": {
541 | "h": 12,
542 | "w": 4,
543 | "x": 16,
544 | "y": 4
545 | },
546 | "id": 2,
547 | "links": [],
548 | "options": {
549 | "displayMode": "lcd",
550 | "orientation": "horizontal",
551 | "reduceOptions": {
552 | "calcs": [
553 | "lastNotNull"
554 | ],
555 | "fields": "",
556 | "values": false
557 | },
558 | "showUnfilled": true,
559 | "text": {}
560 | },
561 | "pluginVersion": "7.5.4",
562 | "targets": [
563 | {
564 | "expr": "bbb_meetings_participants",
565 | "format": "time_series",
566 | "interval": "",
567 | "legendFormat": "{{instance}}",
568 | "refId": "A"
569 | }
570 | ],
571 | "timeFrom": null,
572 | "timeShift": null,
573 | "title": "Teilnehmer",
574 | "transparent": true,
575 | "type": "bargauge"
576 | },
577 | {
578 | "datasource": null,
579 | "fieldConfig": {
580 | "defaults": {
581 | "color": {
582 | "mode": "thresholds"
583 | },
584 | "mappings": [],
585 | "thresholds": {
586 | "mode": "absolute",
587 | "steps": [
588 | {
589 | "color": "red",
590 | "value": null
591 | },
592 | {
593 | "color": "green",
594 | "value": 1
595 | }
596 | ]
597 | },
598 | "unit": "short"
599 | },
600 | "overrides": []
601 | },
602 | "gridPos": {
603 | "h": 12,
604 | "w": 3,
605 | "x": 20,
606 | "y": 4
607 | },
608 | "id": 14,
609 | "options": {
610 | "displayMode": "basic",
611 | "orientation": "horizontal",
612 | "reduceOptions": {
613 | "calcs": [
614 | "lastNotNull"
615 | ],
616 | "fields": "",
617 | "values": false
618 | },
619 | "showUnfilled": true,
620 | "text": {}
621 | },
622 | "pluginVersion": "7.5.4",
623 | "targets": [
624 | {
625 | "exemplar": true,
626 | "expr": "up",
627 | "interval": "",
628 | "legendFormat": "{{instance}}",
629 | "refId": "A"
630 | }
631 | ],
632 | "timeFrom": null,
633 | "timeShift": null,
634 | "title": "Turn Server",
635 | "transformations": [
636 | {
637 | "id": "filterFieldsByName",
638 | "options": {
639 | "include": {
640 | "names": [
641 | "Time",
642 | "fsn17.ulmlernt.org",
643 | "fsn18.ulmlernt.org",
644 | "fsn19.ulmlernt.org",
645 | "fsn21.ulmlernt.org",
646 | "fsn28.ulmlernt.org",
647 | "fsn29.ulmlernt.org",
648 | "fsn30.ulmlernt.org",
649 | "fsn31.ulmlernt.org",
650 | "fsn32.ulmlernt.org",
651 | "fsn33.ulmlernt.org",
652 | "fsn34.ulmlernt.org",
653 | "fsn35.ulmlernt.org"
654 | ]
655 | }
656 | }
657 | }
658 | ],
659 | "type": "bargauge"
660 | }
661 | ],
662 | "refresh": "10s",
663 | "schemaVersion": 27,
664 | "style": "dark",
665 | "tags": [],
666 | "templating": {
667 | "list": [
668 | {
669 | "current": {
670 | "selected": false,
671 | "text": "prometheus",
672 | "value": "prometheus"
673 | },
674 | "description": null,
675 | "error": null,
676 | "hide": 2,
677 | "includeAll": false,
678 | "label": "datasource",
679 | "multi": false,
680 | "name": "DS_PROMETHEUS",
681 | "options": [],
682 | "query": "prometheus",
683 | "refresh": 1,
684 | "regex": "",
685 | "skipUrlSync": false,
686 | "type": "datasource"
687 | }
688 | ]
689 | },
690 | "time": {
691 | "from": "now-6h",
692 | "to": "now"
693 | },
694 | "timepicker": {
695 | "refresh_intervals": [
696 | "5s",
697 | "10s",
698 | "30s",
699 | "1m",
700 | "5m",
701 | "15m",
702 | "30m",
703 | "1h",
704 | "2h",
705 | "1d"
706 | ]
707 | },
708 | "timezone": "",
709 | "title": "Cockpit",
710 | "uid": "ZxiNVbeWk",
711 | "version": 10
712 | }
--------------------------------------------------------------------------------
/files/logo/ulm_lernt_fahne_transparent.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stadtulm/a13-ansible/3b0c44d9712af5c5d3bd745c0a987cc9acf92102/files/logo/ulm_lernt_fahne_transparent.png
--------------------------------------------------------------------------------
/files/logo/ulm_lernt_fahne_transparent.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
18 |
--------------------------------------------------------------------------------
/files/logo/ulm_lernt_fahne_weiss.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stadtulm/a13-ansible/3b0c44d9712af5c5d3bd745c0a987cc9acf92102/files/logo/ulm_lernt_fahne_weiss.png
--------------------------------------------------------------------------------
/files/logo/ulm_lernt_fahne_weiss.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
21 |
--------------------------------------------------------------------------------
/files/logo/ulm_lernt_logo_transparent.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stadtulm/a13-ansible/3b0c44d9712af5c5d3bd745c0a987cc9acf92102/files/logo/ulm_lernt_logo_transparent.png
--------------------------------------------------------------------------------
/files/logo/ulm_lernt_logo_transparent.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
12 |
--------------------------------------------------------------------------------
/files/logo/ulm_lernt_logo_weiss.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stadtulm/a13-ansible/3b0c44d9712af5c5d3bd745c0a987cc9acf92102/files/logo/ulm_lernt_logo_weiss.png
--------------------------------------------------------------------------------
/files/logo/ulm_lernt_logo_weiss.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
15 |
--------------------------------------------------------------------------------
/helper/disable_bbb_hosts.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Deactivate hosts in scalelite
3 | hosts: loadbalancer
4 | become: yes
5 | become_user: scalelite
6 | vars:
7 | bbb_disabled: bbb_tok
8 | tasks:
9 | - name: remove hosts from scalelite
10 | shell: |
11 | cd /var/www/scalelite/
12 | set -o allexport; source .env; set +o allexport
13 | SERVER={{ item }}
14 | ID=$(bundle exec ./bin/rake servers | sed -E "s/$/;/" | sed -E "s/\s//g" | tr --delete '\n' | sed -E "s/online;/online;\n/g" | grep $SERVER | cut -d";" -f1 | cut -d":" -f2 )
15 | ./bin/rake servers:disable[$ID]
16 | args:
17 | executable: /bin/bash
18 | with_inventory_hostnames: "{{ bbb_disabled }}"
19 | - name: display scalelite server status
20 | shell: |
21 | cd /var/www/scalelite/
22 | set -o allexport; source .env; set +o allexport
23 | ./bin/rake servers status
24 | args:
25 | executable: /bin/bash
26 | register: shell_result
27 | - debug:
28 | var: shell_result.stdout_lines
--------------------------------------------------------------------------------
/helper/enable_bbb_hosts.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Deactivate hosts in scalelite
3 | hosts: loadbalancer
4 | become: yes
5 | become_user: scalelite
6 | vars:
7 | bbb_enabled: bbb_tik
8 | tasks:
9 | - name: add hosts to scalelite
10 | shell: |
11 | cd /var/www/scalelite/
12 | set -o allexport; source .env; set +o allexport
13 | SERVER={{ item }}
14 | ID=$(bundle exec ./bin/rake servers | sed -E "s/$/;/" | sed -E "s/\s//g" | tr --delete '\n' | sed -E "s/online;/online;\n/g" | grep $SERVER | cut -d";" -f1 | cut -d":" -f2 )
15 | ./bin/rake servers:enable[$ID]
16 | args:
17 | executable: /bin/bash
18 | with_inventory_hostnames: "{{ bbb_enabled }}"
19 | - name: display scalelite server status
20 | shell: |
21 | cd /var/www/scalelite/
22 | set -o allexport; source .env; set +o allexport
23 | ./bin/rake servers status
24 | args:
25 | executable: /bin/bash
26 | register: shell_result
27 | - debug:
28 | var: shell_result.stdout_lines
--------------------------------------------------------------------------------
/helper/fix_ufw.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: restore ufw with iptables / chains errors
3 | hosts: all
4 | become: yes
5 | tasks:
6 | - ufw:
7 | state: disabled
8 | - shell: "iptables -F; iptables -X; ip6tables -F; ip6tables -X"
9 | - ufw:
10 | state: enabled
11 |
--------------------------------------------------------------------------------
/helper/reboot.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: reboot server(s)
3 | hosts: all
4 | become: yes
5 | tags:
6 | - reboot
7 | tasks:
8 | - name: reboot Server
9 | reboot:
10 |
11 |
--------------------------------------------------------------------------------
/helper/reset_apt_package_lists.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: remove all package lists from /etc/apt/sources.list.d
3 | hosts: all
4 | become: yes
5 | tasks:
6 | - shell: /bin/rm /etc/apt/sources.list.d/*
7 |
8 |
9 |
--------------------------------------------------------------------------------
/helper/store_known_hosts.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Store known hosts of 'all' the hosts in the inventory file
3 | # Source: https://www.ipspace.net/kb/Ansible/Collect_SSH_Keys_Ansible.html
4 | # execute: ANSIBLE_HOST_KEY_CHECKING=false ansible-playbook helper/store_known_hosts.yml
5 | hosts: all
6 | gather_facts: no
7 | connection: local
8 | vars:
9 | - known_hosts: "~/.ssh/known_hosts"
10 | tasks:
11 | - name: scan and register
12 | command: "ssh-keyscan {{ansible_host|default(inventory_hostname)}}"
13 | register: "host_keys"
14 | changed_when: false
15 |
16 | - file: path={{known_hosts}} state=touch
17 | run_once: true
18 |
19 | - blockinfile:
20 | dest: "{{known_hosts}}"
21 | marker: "# {mark} This part managed by Ansible"
22 | block: |
23 | {% for h in groups['all'] if hostvars[h].host_keys is defined %}
24 | {{ hostvars[h].host_keys.stdout }}
25 | {% endfor %}
26 | run_once: true
27 |
--------------------------------------------------------------------------------
/helper/upgradereboot.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: upgrade and reboot server(s)
3 | hosts: all
4 | become: yes
5 | tags:
6 | - upgradeandreboot
7 | tasks:
8 | - name: upgrade server
9 | apt:
10 | update_cache: yes
11 | cache_valid_time: 3600
12 | upgrade: safe
13 | - name: reboot server
14 | reboot:
15 |
--------------------------------------------------------------------------------
/inventory:
--------------------------------------------------------------------------------
1 | [all]
2 | fsn01.ulmlernt.org ansible_host=195.201.237.77
3 | fsn02.ulmlernt.org ansible_host=144.76.96.80
4 | fsn03.ulmlernt.org ansible_host=78.47.162.149
5 | fsn04.ulmlernt.org ansible_host=136.243.8.92
6 | fsn05.ulmlernt.org ansible_host=136.243.18.16
7 | fsn06.ulmlernt.org ansible_host=148.251.6.47
8 | fsn29.ulmlernt.org ansible_host=188.34.190.64
9 | fsn34.ulmlernt.org ansible_host=138.201.118.3
10 | bck.ulmlernt.org ansible_host=49.12.102.120
11 |
12 | [dns]
13 | fsn01.ulmlernt.org
14 |
15 | [bbb]
16 | fsn02.ulmlernt.org bbb_additional_turn_server="turn01.ulmlernt.org" bbb_turn_ipv4_only="turnipv4only01.ulmlernt.org"
17 | fsn04.ulmlernt.org bbb_additional_turn_server="turn01.ulmlernt.org" bbb_turn_ipv4_only="turnipv4only01.ulmlernt.org"
18 | fsn05.ulmlernt.org bbb_additional_turn_server="turn01.ulmlernt.org" bbb_turn_ipv4_only="turnipv4only01.ulmlernt.org"
19 | fsn06.ulmlernt.org bbb_additional_turn_server="turn01.ulmlernt.org" bbb_turn_ipv4_only="turnipv4only01.ulmlernt.org"
20 |
21 | [loadbalancer]
22 | fsn01.ulmlernt.org nginx_domain_name="lb.ulmlernt.org"
23 |
24 | [monitoring]
25 | fsn01.ulmlernt.org
26 |
27 | [frontend]
28 | fsn03.ulmlernt.org
29 |
30 | [backupstorage]
31 | bck.ulmlernt.org
32 |
33 | [turn]
34 | fsn34.ulmlernt.org turn_hostname="turn01.ulmlernt.org"
35 |
36 | [turnipv4only]
37 | fsn29.ulmlernt.org turn_hostname="turnipv4only01.ulmlernt.org"
38 |
--------------------------------------------------------------------------------
/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Basic Setup
3 | hosts: all
4 | become: yes
5 | tags:
6 | - base
7 | vars_files:
8 | - vars.yml
9 | roles:
10 | - base
11 |
12 | - name: Base User setup/update
13 | hosts: all
14 | become: yes
15 | tags:
16 | - base
17 | - user
18 | vars_files:
19 | - vars.yml
20 | roles:
21 | - base-user
22 |
23 | - name: Monitored host
24 | hosts: all
25 | become: yes
26 | vars:
27 | _node_exporter_binary_install_dir: '/opt/node_exporter'
28 | _node_exporter_system_group: 'prometheus'
29 | _node_exporter_system_user: 'prometheus'
30 | roles:
31 | - cloudalchemy.node-exporter
32 | - node-exporter-ufw
33 |
34 | - name: DNS Server
35 | hosts: dns
36 | become: yes
37 | tags:
38 | - dns
39 | vars:
40 | coredns_config_file: files/coredns/Corefile.j2
41 | roles:
42 | - cloudalchemy.coredns
43 | - coredns-ufw
44 |
45 | - name: TURN Server
46 | hosts: turn, turn_test
47 | become: yes
48 | tags:
49 | - turn
50 | - test
51 | vars_files:
52 | - vars.yml
53 | vars:
54 | turn_ipv4_only: no
55 | certbot_source: letsencrypt
56 | roles:
57 | - turn-standalone
58 |
59 | - name: TURN IPv4 only Server
60 | hosts: turnipv4only, turnipv4only_test
61 | become: yes
62 | tags:
63 | - turn
64 | - test
65 | vars_files:
66 | - vars.yml
67 | vars:
68 | turn_ipv4_only: yes
69 | certbot_source: letsencrypt
70 | roles:
71 | - turn-standalone
72 |
73 | - name: BigBlueButton
74 | hosts: bbb, bbb_test
75 | become: yes
76 | tags:
77 | - bbb
78 | vars_files:
79 | - vars.yml
80 | vars:
81 | bbb_letsencrypt_enable: yes
82 | bbb_letsencrypt_email: "{{ letsencrypt_email }}"
83 | bbb_coturn_enable: no
84 | bbb_turn_enable: yes
85 | bbb_stun_servers:
86 | - server: "{{ bbb_additional_turn_server }}"
87 | bbb_turn_servers:
88 | - server: "{{ bbb_turn_ipv4_only }}"
89 | port: 443
90 | secret: "{{ turn_secret }}"
91 | tls: true
92 | - server: "{{ bbb_additional_turn_server }}"
93 | port: 443
94 | secret: "{{ turn_secret }}"
95 | tls: true
96 | - server: "{{ bbb_additional_turn_server }}"
97 | port: 3478
98 | secret: "{{ turn_secret }}"
99 | tls: false
100 | bbb_default_welcome_message: Wilkommen bei %%CONFNAME%%!
Hilfe und Anleitungen finden Sie in unserer ulmlernt Dokumentation.
Sollten Probleme auftauchen bitten wir Sie darum unsere allgemeinen Hinweise zur Nutzung von ulmlernt zu beachten. Dort finden Sie auch eine Anleitung zur Störungsmeldung.
101 | bbb_default_welcome_message_footer: ulmlernt wird von der Stadt Ulm durch die Abteilung Bildung und Sport in Kooperation mit der Digitalen Agenda bereitgestellt und verwendet BigBlueButton.
102 | bbb_greenlight_enable: no
103 | bbb_api_demos_enable: no
104 | bbb_ntp_cron: yes
105 | roles:
106 | - nginx
107 | - dirty-docker # dirty hack because docker role in n0emis BBB will fail if docker group not present
108 | - n0emis.bigbluebutton
109 | - bbb-ulmlernt
110 | # TODO remove Docker from install process
111 | # removal of docker group makes problems if docker is reinstalled by gerlingguyscript in n0emis
112 | - no-docker
113 | - no-turn
114 |
115 | - name: Collect BBB secrets
116 | hosts: bbb, bbb_test
117 | become: yes
118 | tags:
119 | - bbb
120 | - config
121 | - test
122 | roles:
123 | - bbb-collect
124 |
125 | - name: BigBlueButton Test
126 | hosts: bbb_test
127 | become: yes
128 | tags:
129 | - bbb
130 | - test
131 | vars_files:
132 | - vars.yml
133 | roles:
134 | - bbb-easy-join
135 |
136 |
137 | - name: BBB Exporter
138 | hosts: bbb
139 | become: yes
140 | tags:
141 | - bbb
142 | roles:
143 | - bbb-exporter
144 |
145 | - name: Loadbalancer
146 | hosts: loadbalancer
147 | become: yes
148 | tags:
149 | - loadbalancer
150 | vars_files:
151 | - vars.yml
152 | vars:
153 | redis_port: 6379
154 | redis_bind_interface: 127.0.0.1
155 | ruby_install_from_source: yes
156 | ruby_version: 2.6.6
157 | ruby_download_url: https://cache.ruby-lang.org/pub/ruby/2.6/ruby-2.6.6.tar.gz
158 | ruby_install_bundler: yes
159 | postgresql_hba_entries:
160 | - {type: local, database: all, user: postgres, auth_method: peer}
161 | - {type: local, database: all, user: all, auth_method: peer}
162 | - {type: host, database: all, user: all, address: '127.0.0.1/32', auth_method: trust}
163 | - {type: host, database: all, user: all, address: '::1/128', auth_method: trust}
164 | postgresql_users:
165 | - name: scalelite
166 | state: present
167 | - name: grafana
168 | state: present
169 | postgresql_databases:
170 | - name: scalelite
171 | owner: scalelite
172 | state: present
173 | - name: grafana
174 | owner: grafana
175 | state: present
176 | roles:
177 | - nginx
178 | - nginx-tls
179 | - geerlingguy.redis
180 | - role: geerlingguy.postgresql
181 | become: yes
182 | - geerlingguy.ruby
183 | - scalelite
184 |
185 | - name: Register BBBs at Loadbalancer
186 | hosts: loadbalancer
187 | become: yes
188 | tags:
189 | - loadbalancer
190 | - config
191 | roles:
192 | - scalelite-config
193 |
194 | # TODO Remove Easyjoin from LB
195 | # - name: Easy Join
196 | # hosts: loadbalancer
197 | # become: yes
198 | # vars_files:
199 | # - vars.yml
200 | # vars:
201 | # nodejs_version: "12.x"
202 | # bbb_secret: "{{ scalelite_loadbalancer_secret }}"
203 | # tags:
204 | # - loadbalancer
205 | # roles:
206 | # - nginx
207 | # - nginx-tls
208 | # - geerlingguy.nodejs
209 | # - bbb-easy-join
210 |
211 | - name: Frontend - Greenlight
212 | hosts: frontend
213 | become: yes
214 | vars_files:
215 | - vars.yml
216 | tags:
217 | - frontend
218 | - greenlight
219 | vars:
220 | nginx_domain_name: "ulmlernt.de"
221 | nginx_redirect_alias: "www.ulmlernt.de"
222 | lb_url: "{{ scalelite_loadbalancer_url }}"
223 | lb_secret: "{{ scalelite_loadbalancer_secret }}"
224 | redis_port: 6379
225 | redis_bind_interface: 127.0.0.1
226 | ruby_install_from_source: yes
227 | ruby_version: 2.6.6
228 | ruby_download_url: https://cache.ruby-lang.org/pub/ruby/2.6/ruby-2.6.6.tar.gz
229 | ruby_install_bundler: yes
230 | nodejs_version: "12.x"
231 | postgresql_hba_entries:
232 | - {type: local, database: all, user: postgres, auth_method: peer}
233 | - {type: local, database: all, user: all, auth_method: peer}
234 | - {type: host, database: all, user: all, address: '127.0.0.1/32', auth_method: trust}
235 | - {type: host, database: all, user: all, address: '::1/128', auth_method: trust}
236 | postgresql_users:
237 | - name: greenlight
238 | state: present
239 | role_attr_flags: CREATEDB # rake db:migrate needs priv
240 | postgresql_databases:
241 | - name: greenlight_production
242 | owner: greenlight
243 | state: present
244 | roles:
245 | - nginx
246 | - nginx-tls
247 | - nginx-tls-redirect
248 | - geerlingguy.redis
249 | - role: geerlingguy.postgresql
250 | become: yes
251 | - geerlingguy.ruby
252 | - geerlingguy.nodejs
253 | #- docker
254 | - greenlight
255 | #- greenlight-docker
256 |
257 | - name: Frontend - no rocketchat
258 | hosts: frontend
259 | become: yes
260 | vars_files:
261 | - vars.yml
262 | vars:
263 | nginx_domain_name: "ulmlernt.de"
264 | nginx_redirect_alias: "chat.ulmlernt.de"
265 | tags:
266 | - no-rocketchat
267 | roles:
268 | - nginx
269 | - nginx-tls-redirect
270 |
271 |
272 | - name: Frontend - Dokuwiki
273 | hosts: frontend
274 | become: yes
275 | vars_files:
276 | - vars.yml
277 | tags:
278 | - frontend
279 | - dokuwiki
280 | vars:
281 | nginx_domain_name: "doku.ulmlernt.de"
282 | nginx_root: "/var/www/dokuwiki"
283 | roles:
284 | - nginx
285 | - nginx-tls-add
286 | - dokuwiki
287 |
288 | - name: Monitoring
289 | hosts: loadbalancer
290 | become: yes
291 | tags:
292 | - monitoring
293 | vars_files:
294 | - vars.yml
295 | vars:
296 | nginx_domain_name: mon.ulmlernt.org
297 | prometheus_targets:
298 | node:
299 | - targets:
300 | "{{ groups['all'] | map('extract', hostvars, ['ansible_fqdn']) | map('regex_replace', '$', ':9100') | list }}"
301 | bbb:
302 | - targets:
303 | "{{ groups['bbb'] | map('extract', hostvars, ['ansible_fqdn']) | map('regex_replace', '$', ':9688') | list }}"
304 | prometheus_scrape_configs:
305 | - job_name: "prometheus"
306 | metrics_path: "/metrics"
307 | static_configs:
308 | - targets:
309 | - "127.0.0.1:9090"
310 | - job_name: "coredns"
311 | metrics_path: "/metrics"
312 | static_configs:
313 | - targets:
314 | - "{{ ansible_default_ipv4.address }}:9153"
315 | - job_name: "node"
316 | file_sd_configs:
317 | - files:
318 | - "/etc/prometheus/file_sd/node.yml"
319 | relabel_configs:
320 | - source_labels: ['__address__']
321 | separator: ':'
322 | regex: '(.*):.*'
323 | target_label: 'instance'
324 | replacement: '$1'
325 | - job_name: "bbb"
326 | file_sd_configs:
327 | - files:
328 | - "/etc/prometheus/file_sd/bbb.yml"
329 | relabel_configs:
330 | - source_labels: ['__address__']
331 | separator: ':'
332 | regex: '(.*):.*'
333 | target_label: 'instance'
334 | replacement: '$1'
335 | grafana_address: 127.0.0.1
336 | grafana_port: 3001
337 | grafana_url: "https://mon.ulmlernt.org"
338 | grafana_domain: "mon.ulmlernt.org"
339 | grafana_database:
340 | type: postgres
341 | host: 127.0.0.1:5432
342 | name: grafana
343 | user: grafana
344 | password: trust
345 | grafana_datasources:
346 | - name: prometheus
347 | type: prometheus
348 | access: proxy
349 | url: 'http://localhost:9090'
350 | basicAuth: false
351 | isDefault: true
352 | grafana_dashboards_dir: files/grafana/dashboards
353 | grafana_security:
354 | admin_user: admin
355 | admin_password: "{{ monitoring_grafana_admin_password }}"
356 | grafana_users:
357 | allow_sign_up: false
358 | default_theme: dark
359 | auto_assign_org: true
360 | editors_can_admin: true
361 | auto_assign_org_role: Editor
362 | grafana_auth:
363 | disable_login_form: true
364 | anonymous:
365 | enabled: true
366 | org_name: ulmlernt
367 | org_role: Viewer
368 | github:
369 | enabled: true
370 | allow_sign_up: true
371 | client_id: "{{ monitoring_github_client_id }}"
372 | client_secret: "{{ monitoring_github_client_secret }}"
373 | scopes: user:email,read:org
374 | auth_url: https://github.com/login/oauth/authorize
375 | token_url: https://github.com/login/oauth/access_token
376 | api_url: https://api.github.com/user
377 | team_ids: ""
378 | allowed_organizations: stadtulm
379 | roles:
380 | - cloudalchemy.prometheus
381 | - prometheus-ufw
382 | - cloudalchemy.grafana
383 | - nginx-tls-monitoring
384 |
385 | - name: Backup Server
386 | hosts: backupstorage
387 | tags:
388 | - backup
389 | become: yes
390 | vars:
391 | rest_backup_storage_dir: "/mnt/backup/restic"
392 | rest_backup_server: "bck.ulmlernt.org"
393 | rest_backup_tasks:
394 | - { name: "dokuwiki", conn_pw: "{{ restic_backup_dokuwiki_conn_pw }}", backup_pw: "{{ restic_backup_dokuwiki_backup_pw }}" }
395 | - { name: "greenlight" , conn_pw: "{{ restic_backup_greenlight_conn_pw }}", backup_pw: "{{ restic_backup_greenlight_backup_pw }}" }
396 | - { name: "zammad" , conn_pw: "{{ restic_backup_zammad_conn_pw }}", backup_pw: "{{ restic_backup_zammad_backup_pw }}" }
397 | vars_files:
398 | - vars.yml
399 | roles:
400 | - restic-server
401 |
402 | - name: Backup Client - Dokuwiki
403 | hosts: frontend
404 | become: yes
405 | tags:
406 | - backup
407 | vars_files:
408 | - vars.yml
409 | vars:
410 | backup_name: "dokuwiki"
411 | backup_server: "{{ restic_backup_server }}"
412 | backup_conn_pw: "{{ restic_backup_dokuwiki_conn_pw }}"
413 | backup_pw: "{{ restic_backup_dokuwiki_backup_pw }}"
414 | backup_script: "backup_directory.sh"
415 | backup_dir: "/var/www/dokuwiki"
416 | roles:
417 | - restic-client
418 |
419 | - name: Backup Client - Greenlight
420 | hosts: frontend
421 | become: yes
422 | tags:
423 | - backup
424 | vars_files:
425 | - vars.yml
426 | vars:
427 | backup_name: "greenlight"
428 | backup_server: "{{ restic_backup_server }}"
429 | backup_conn_pw: "{{ restic_backup_greenlight_conn_pw }}"
430 | backup_pw: "{{ restic_backup_greenlight_backup_pw }}"
431 | backup_script: "backup_postgres.sh"
432 | backup_pg_db: "greenlight_production"
433 | roles:
434 | - restic-client
435 |
436 |
--------------------------------------------------------------------------------
/requirements.yml:
--------------------------------------------------------------------------------
1 | ---
2 | roles:
3 | - name: cloudalchemy.node-exporter
4 | version: 0.19.0
5 | - name: cloudalchemy.coredns
6 | version: 0.3.2
7 | - name: n0emis.bigbluebutton
8 | - name: geerlingguy.nodejs
9 | version: 5.1.1
10 | - name: geerlingguy.docker
11 | version: 2.7.0
12 | - name: geerlingguy.redis
13 | version: 1.6.0
14 | - name: geerlingguy.postgresql
15 | version: 2.2.0
16 | - name: geerlingguy.ruby
17 | version: 2.6.0
18 | - name: cloudalchemy.prometheus
19 | version: 2.15.0
20 | - name: cloudalchemy.grafana
21 | version: 0.17.0
22 | - name: geerlingguy.elasticsearch
23 | version: 4.2.0
24 | - name: geerlingguy.java
25 | version: 1.10.0
26 | - name: geerlingguy.mailhog
27 | version: 2.2.0
--------------------------------------------------------------------------------
/roles/base-user/tasks/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Ensure 'sudo' group exists
3 | group:
4 | name: sudo
5 | state: present
6 |
7 | - name: Create users
8 | user:
9 | name: '{{ item.name }}'
10 | shell: /bin/bash
11 | password: '!'
12 | update_password: on_create
13 | groups:
14 | - sudo
15 | state: present
16 | with_items: '{{ users }}'
17 |
18 | - name: Add SSH keys
19 | authorized_key:
20 | user: '{{ item.name }}'
21 | state: present
22 | key: '{{ item.sshkeys }}'
23 | with_items: '{{ users }}'
24 |
25 | - name: Ensure sudo group is passwordless
26 | lineinfile:
27 | path: /etc/sudoers.d/sudo-nopasswd
28 | line: '%sudo ALL=(ALL) NOPASSWD: ALL'
29 | state: present
30 | mode: 0440
31 | create: yes
32 | validate: '/usr/sbin/visudo -cf %s'
33 |
34 | - name: Ensure sudoers uses sudoers.d
35 | lineinfile:
36 | path: /etc/sudoers
37 | line: '#includedir /etc/sudoers.d'
38 | state: present
39 | validate: '/usr/sbin/visudo -cf %s'
40 |
--------------------------------------------------------------------------------
/roles/base/handlers/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: restart sshd
3 | service:
4 | name: sshd
5 | state: restarted
6 |
7 | - name: restart ufw
8 | service:
9 | name: ufw
10 | state: restarted
11 |
12 | - name: restart fail2ban
13 | service:
14 | name: fail2ban
15 | state: restarted
16 |
--------------------------------------------------------------------------------
/roles/base/tasks/firewall.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Ensure ufw and fail2ban packages
3 | apt:
4 | name:
5 | - ufw
6 | - fail2ban
7 | state: present
8 |
9 | - name: Allow ssh in ufw
10 | ufw:
11 | rule: allow
12 | name: OpenSSH
13 |
14 | # https://tomschlick.com/2017/05/24/dealing-with-ufw-chain-already-exists/
15 | - name: Pause for ufw ... to mitigate hickups
16 | pause:
17 | seconds: 1
18 |
19 | - name: Allow everything out
20 | ufw:
21 | direction: outgoing
22 | policy: allow
23 |
24 | # https://tomschlick.com/2017/05/24/dealing-with-ufw-chain-already-exists/
25 | - name: Pause for ufw ... to mitigate hickups
26 | pause:
27 | seconds: 1
28 |
29 | - name: Allow default nothing in and enable UFW
30 | ufw:
31 | direction: incoming
32 | policy: deny
33 | state: enabled
34 |
35 | # https://tomschlick.com/2017/05/24/dealing-with-ufw-chain-already-exists/
36 | - name: Pause for ufw ... to mitigate hickups
37 | pause:
38 | seconds: 1
39 |
40 | - name: ensure fail2ban uses ufw
41 | copy:
42 | content: |
43 | [DEFAULT]
44 | banaction = ufw
45 | mode: 0644
46 | dest: /etc/fail2ban/jail.local
47 | notify: restart fail2ban
48 |
--------------------------------------------------------------------------------
/roles/base/tasks/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Ensure wrong hostname is not in hosts file
3 | lineinfile:
4 | dest: /etc/hosts
5 | regexp: '{{ ansible_hostname }}$'
6 | state: absent
7 | backup: yes
8 | when: ansible_hostname != inventory_hostname_short
9 |
10 | - name: Ensure Hostname
11 | hostname:
12 | name: '{{ inventory_hostname_short }}'
13 | when: ansible_hostname != inventory_hostname_short
14 |
15 | - name: Ensure Hostname in hosts file
16 | lineinfile:
17 | dest: /etc/hosts
18 | regexp: '{{ inventory_hostname_short }}$'
19 | line: '{{ ansible_default_ipv4.address }} {{ inventory_hostname }} {{ inventory_hostname_short }}'
20 | state: present
21 | when: ansible_default_ipv4.address is defined
22 |
23 | - name: Update apt
24 | apt:
25 | update_cache: yes
26 | cache_valid_time: 3600
27 | # upgrade disabled for stability reasons
28 | # upgrade: safe
29 |
30 | - name: Ensure basic packages
31 | apt:
32 | name:
33 | - byobu
34 | - zsh
35 | - curl
36 | - wget
37 | - dnsutils
38 | - git
39 | - sudo
40 | - htop
41 | - vim
42 | state: present
43 |
44 | - name: Ensure clean motd
45 | file:
46 | path: '/etc/update-motd.d/{{item}}'
47 | # only removing executable rights would be cleaner, but they are gone now
48 | state: absent
49 | with_items:
50 | - 10-help-text
51 | - 50-motd-news
52 | - 80-esm
53 | - 80-livepatch
54 |
55 | # TODO: why is this failing on fsn10 ?
56 | # - name: Ensure clean motd, news updater doesn't run
57 | # systemd:
58 | # name: motd-news
59 | # enabled: no
60 | # masked: yes
61 |
62 | - name: Ensure notice about configuration managment in motd
63 | copy:
64 | content: |
65 | #!/bin/sh
66 | printf "\n"
67 | printf "This system is managed with ansible, see following repository for details\n"
68 | printf "https://github.com/stadtulm/a13-ansible\n"
69 | printf "\n"
70 | mode: 0755
71 | dest: /etc/update-motd.d/11-help-text
72 |
73 | - name: Ensure that LANG from ssh clients is not used
74 | lineinfile:
75 | path: /etc/ssh/sshd_config
76 | line: 'AcceptEnv LANG LC_*'
77 | state: absent
78 | notify: restart sshd
79 |
80 | - import_tasks: firewall.yml
81 |
--------------------------------------------------------------------------------
/roles/bbb-collect/tasks/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Register bbb conf
3 | command: bbb-conf --secret
4 | register: bbb_conf_result
5 | check_mode: no
6 |
7 | - name: Extract bbb url
8 | set_fact:
9 | bbb_url: "{{ bbb_conf_result.stdout | regex_search('URL: (.+)', multiline=True) | regex_replace('URL: ') }}"
10 | cacheable: yes
11 |
12 | - name: Extract bbb secret
13 | set_fact:
14 | ## regex_replace with multiline only works when following PR is released:
15 | ## https://github.com/ansible/ansible/pull/65051
16 | # bbb_secret: "{{ bbb_conf_result.stdout | regex_replace('Secret: ([a-zA-Z0-9]+)', '\\1', multiline=True) }}"
17 | bbb_secret: "{{ bbb_conf_result.stdout | regex_search('Secret: ([a-zA-Z0-9]*)', multiline=True) | regex_replace('Secret: ') }}"
18 | cacheable: yes
19 |
--------------------------------------------------------------------------------
/roles/bbb-easy-join/tasks/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Ensure git is available
3 | apt:
4 | name:
5 | - git
6 | state: present
7 |
8 | - name: Create user
9 | user:
10 | name: bbb-easy-join
11 | shell: /bin/bash
12 | home: /var/lib/bbb-easy-join
13 | create_home: yes
14 | password: '!'
15 | update_password: on_create
16 | groups: nogroup
17 | system: yes
18 | state: present
19 |
20 | - name: Checkout bbb-easy-join
21 | git:
22 | repo: https://github.com/stadtulm/bbb-easy-join.git
23 | dest: /var/www/bbb-easy-join
24 | version: 4c0ac089753e331610ec7b1c0b43ef3cd55be430
25 | force: yes
26 |
27 | - name: Ensure bbb-easy-join is owner of /var/www/bbb-easy-join
28 | file:
29 | path: /var/www/bbb-easy-join
30 | recurse: yes
31 | owner: bbb-easy-join
32 |
33 | - name: Ensure required npm packages are installed
34 | npm:
35 | path: /var/www/bbb-easy-join
36 | state: present
37 | become: yes
38 | become_user: bbb-easy-join
39 |
40 | - name: Ensure environment for bbb-easy-join
41 | template:
42 | src: env.j2
43 | dest: /var/www/bbb-easy-join/.env
44 | owner: bbb-easy-join
45 |
46 | - name: Ensure systemd unit for bbb-easy-join
47 | template:
48 | src: bbb-easy-join.service.j2
49 | dest: /etc/systemd/system/bbb-easy-join.service
50 |
51 | - name: Enable bbb-easy-join
52 | systemd:
53 | daemon_reload: yes
54 | name: bbb-easy-join
55 | enabled: yes
56 | state: started
57 |
58 | - name: Ensure bbb-easy-join nginx config exists
59 | template:
60 | src: nginx-bbb-easy-join.conf
61 | dest: /etc/bigbluebutton/nginx/bbb-easy-join.nginx
62 | notify: reload nginx
63 |
--------------------------------------------------------------------------------
/roles/bbb-easy-join/templates/bbb-easy-join.service.j2:
--------------------------------------------------------------------------------
1 | [Unit]
2 | Description=BigBlueButton Easy Join
3 | Requires=network.target
4 |
5 | [Service]
6 | Type=simple
7 | ExecStart=/usr/bin/node app.js
8 | WorkingDirectory=/var/www/bbb-easy-join
9 | Restart=always
10 | RestartSec=60
11 | User=bbb-easy-join
12 |
13 | [Install]
14 | WantedBy=multi-user.target
15 |
--------------------------------------------------------------------------------
/roles/bbb-easy-join/templates/env.j2:
--------------------------------------------------------------------------------
1 | HOST=127.0.0.1
2 | PORT=5000
3 | # you can call `sudo bbb-conf --secret` for the following two values
4 | BBB_API_URL=https://{{ ansible_fqdn }}/bigbluebutton
5 | BBB_API_SECRET={{ bbb_secret }}
6 | WELCOME_MESSAGE="Welcome to %%CONFNAME%%!
Invite others with following URL: %%JOINURL%%
Use a headset to avoid causing background noise for others."
--------------------------------------------------------------------------------
/roles/bbb-easy-join/templates/nginx-bbb-easy-join.conf:
--------------------------------------------------------------------------------
1 | location /b {
2 | proxy_pass http://127.0.0.1:5000;
3 |
4 | proxy_set_header Host $host;
5 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
6 | proxy_set_header X-Forwarded-Proto $scheme;
7 | proxy_http_version 1.1;
8 | }
9 |
--------------------------------------------------------------------------------
/roles/bbb-exporter/tasks/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Ensure git and pip are available
3 | apt:
4 | name:
5 | - git
6 | - python3-pip
7 | state: present
8 |
9 | - name: Create system user for bbb-exporter
10 | user:
11 | name: bbb-exporter
12 | shell: /bin/bash
13 | home: /var/lib/bbb-exporter
14 | create_home: yes
15 | password: '!'
16 | update_password: on_create
17 | system: yes
18 | state: present
19 |
20 | - name: Checkout bbb-exporter
21 | git:
22 | repo: https://github.com/greenstatic/bigbluebutton-exporter.git
23 | dest: /var/www/bbb-exporter
24 | version: ca57db839188726e2c1b29387cd4747a23a0043e
25 |
26 | - name: Ensure bbb-exporter is owner of /var/www/bbb-exporter
27 | file:
28 | path: /var/www/bbb-exporter
29 | recurse: yes
30 | owner: bbb-exporter
31 | group: bbb-exporter
32 |
33 | - name: Ensure required pip packages are installed
34 | pip:
35 | requirements: /var/www/bbb-exporter/requirements.txt
36 | state: present
37 | extra_args: --user
38 | become: yes
39 | become_user: bbb-exporter
40 |
41 | - name: Ensure environment for bbb-exporter
42 | template:
43 | src: env.j2
44 | dest: /var/www/bbb-exporter/.env
45 | owner: bbb-exporter
46 |
47 | - name: Ensure systemd unit for bbb-exporter
48 | template:
49 | src: bbb-exporter.service
50 | dest: /etc/systemd/system/bbb-exporter.service
51 |
52 | - name: Enable bbb-exporter
53 | systemd:
54 | daemon_reload: yes
55 | name: bbb-exporter
56 | enabled: yes
57 | state: started
58 |
59 | - name: Ensure exporter config for ufw
60 | copy:
61 | content: |
62 | [bbb-exporter]
63 | title=bbb-exporter
64 | description=prometheus bbb exporter
65 | ports=9688/tcp
66 | mode: 0644
67 | dest: /etc/ufw/applications.d/bbb-exporter
68 |
69 | - name: allow bbb-exporter in ufw
70 | ufw:
71 | rule: allow
72 | name: bbb-exporter
73 | state: enabled
74 |
--------------------------------------------------------------------------------
/roles/bbb-exporter/templates/bbb-exporter.service:
--------------------------------------------------------------------------------
1 | [Unit]
2 | Description=BBB Exporter
3 | Requires=network.target
4 |
5 | [Service]
6 | Type=simple
7 | EnvironmentFile=/var/www/bbb-exporter/.env
8 | WorkingDirectory=/var/www/bbb-exporter/bbb-exporter
9 | Restart=always
10 | RestartSec=60
11 | ExecStart=/usr/bin/python3 /var/www/bbb-exporter/bbb-exporter/server.py
12 | User=bbb-exporter
13 |
14 | [Install]
15 | WantedBy=multi-user.target
16 |
--------------------------------------------------------------------------------
/roles/bbb-exporter/templates/env.j2:
--------------------------------------------------------------------------------
1 | API_BASE_URL={{ bbb_url }}api/
2 | API_SECRET={{ bbb_secret }}
3 |
--------------------------------------------------------------------------------
/roles/bbb-ulmlernt/files/conf-muted-ulmlernt.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stadtulm/a13-ansible/3b0c44d9712af5c5d3bd745c0a987cc9acf92102/roles/bbb-ulmlernt/files/conf-muted-ulmlernt.wav
--------------------------------------------------------------------------------
/roles/bbb-ulmlernt/files/conf-unmuted-ulmlernt.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stadtulm/a13-ansible/3b0c44d9712af5c5d3bd745c0a987cc9acf92102/roles/bbb-ulmlernt/files/conf-unmuted-ulmlernt.wav
--------------------------------------------------------------------------------
/roles/bbb-ulmlernt/files/default.odp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stadtulm/a13-ansible/3b0c44d9712af5c5d3bd745c0a987cc9acf92102/roles/bbb-ulmlernt/files/default.odp
--------------------------------------------------------------------------------
/roles/bbb-ulmlernt/files/default.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stadtulm/a13-ansible/3b0c44d9712af5c5d3bd745c0a987cc9acf92102/roles/bbb-ulmlernt/files/default.pdf
--------------------------------------------------------------------------------
/roles/bbb-ulmlernt/handlers/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: reload nginx
3 | service:
4 | name: nginx
5 | state: reloaded
6 |
7 | - name: restart bigbluebutton
8 | command: bbb-conf --restart
9 | become: yes
10 |
--------------------------------------------------------------------------------
/roles/bbb-ulmlernt/tasks/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: ensure default recording is disabled
3 | lineinfile:
4 | path: /usr/share/bbb-web/WEB-INF/classes/bigbluebutton.properties
5 | regexp: '^disableRecordingDefault'
6 | line: 'disableRecordingDefault=true'
7 | notify: restart bigbluebutton
8 |
9 | - name: ensure recording auto start is disabled
10 | lineinfile:
11 | path: /usr/share/bbb-web/WEB-INF/classes/bigbluebutton.properties
12 | regexp: '^autoStartRecording'
13 | line: 'autoStartRecording=false'
14 | notify: restart bigbluebutton
15 |
16 | - name: ensure users are not allowed to start recording
17 | lineinfile:
18 | path: /usr/share/bbb-web/WEB-INF/classes/bigbluebutton.properties
19 | regexp: '^allowStartStopRecording'
20 | line: 'allowStartStopRecording=false'
21 | notify: restart bigbluebutton
22 |
23 | - name: ensure http call to root is redirected
24 | copy:
25 | content: |
26 | location = / {
27 | return 301 https://ulmlernt.de;
28 | }
29 | mode: 0755
30 | dest: /etc/bigbluebutton/nginx/redirectroot.nginx
31 | notify: reload nginx
32 |
33 | - name: ensure our own default slides are used
34 | copy:
35 | src: default.pdf
36 | dest: /var/www/bigbluebutton-default/default.pdf
37 |
38 | - name: copy our own mute/unmute sounds
39 | copy:
40 | src: '{{ item }}'
41 | dest: '/opt/freeswitch/share/freeswitch/sounds/en/us/callie/conference/48000/{{item}}'
42 | with_items:
43 | - conf-muted-ulmlernt.wav
44 | - conf-unmuted-ulmlernt.wav
45 |
46 | - name: ensure our own mute sound is used
47 | xml:
48 | path: /opt/freeswitch/etc/freeswitch/autoload_configs/conference.conf.xml
49 | xpath: '/configuration/profiles/profile[@name="cdquality"]/param[@name="muted-sound"]'
50 | attribute: value
51 | value: conference/conf-muted-ulmlernt.wav
52 | notify: restart bigbluebutton
53 |
54 | - name: ensure our own unmute sound is used
55 | xml:
56 | path: /opt/freeswitch/etc/freeswitch/autoload_configs/conference.conf.xml
57 | xpath: '/configuration/profiles/profile[@name="cdquality"]/param[@name="unmuted-sound"]'
58 | attribute: value
59 | value: conference/conf-unmuted-ulmlernt.wav
60 | notify: restart bigbluebutton
61 |
62 | - name: adapt freeswitch ports in ufw
63 | copy:
64 | content: |
65 | [freeswitch]
66 | title=freeswitch
67 | description=freeswitch telecom stack
68 | ports=16384:32768/udp
69 | mode: 0644
70 | dest: /etc/ufw/applications.d/freeswitch
71 |
72 | - name: allow freeswitch in ufw
73 | ufw:
74 | rule: allow
75 | name: freeswitch
76 | state: enabled
77 |
78 | - name: Restart BBB every night @4am
79 | cron:
80 | state: present
81 | name: "restart BigBlueButton"
82 | minute: "0"
83 | hour: "4"
84 | user: root
85 | job: "bbb-conf --restart"
86 |
87 | - name: run bbb-conf --setip to ensure no echo test hanging after upgrade
88 | become: yes
89 | shell:
90 | cmd: "bbb-conf --setip {{ inventory_hostname }}"
91 |
92 |
--------------------------------------------------------------------------------
/roles/coredns-ufw/tasks/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Ensure coredns config for ufw
3 | copy:
4 | content: |
5 | [coredns]
6 | title=coredns
7 | description=coredns dns server
8 | ports=53,9153/tcp|53/udp
9 | mode: 0644
10 | dest: /etc/ufw/applications.d/coredns
11 |
12 | - name: allow coredns in ufw
13 | ufw:
14 | rule: allow
15 | name: coredns
16 | state: enabled
17 |
--------------------------------------------------------------------------------
/roles/dirty-docker/tasks/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Ensure Docker Group exists because otherwise n0emis BBB docker dependency will fail
3 | group:
4 | name: docker
5 | state: present
--------------------------------------------------------------------------------
/roles/docker/tasks/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | # source: https://docs.docker.com/engine/install/ubuntu/
3 | - name: Ensure Prequisitions for Docker are met
4 | apt:
5 | name:
6 | - apt-transport-https
7 | - ca-certificates
8 | - curl
9 | - gnupg-agent
10 | - software-properties-common
11 | state: present
12 |
13 | - name: Ensure Docker Key is present
14 | apt_key:
15 | url: https://download.docker.com/linux/ubuntu/gpg
16 | state: present
17 |
18 | - name: Ensure Docker Repository is present
19 | apt_repository:
20 | repo: deb [arch=amd64] https://download.docker.com/linux/ubuntu bionic stable
21 | state: present
22 |
23 | - name: Ensure Docker is installed
24 | apt:
25 | name:
26 | - docker-ce
27 | - docker-ce-cli
28 | - containerd.io
29 | - docker-compose
30 | state: present
31 |
32 | - name: Ensure Users are in Docker group
33 | user:
34 | name: '{{ item.name }}'
35 | groups: docker
36 | append: yes
37 | state: present
38 | with_items: '{{ users }}'
39 |
--------------------------------------------------------------------------------
/roles/dokuwiki/tasks/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Ensure PHP nginx installed
3 | apt:
4 | name:
5 | - nginx
6 | - php7.2-fpm
7 | - php7.2
8 |
9 | - name: Ensure current Dokuwiki
10 | unarchive:
11 | src: https://download.dokuwiki.org/src/dokuwiki/dokuwiki-stable.tgz
12 | dest: /tmp
13 | remote_src: yes
14 |
15 | - name: Ensure Dokuwiki directory exists
16 | file:
17 | path: /var/www/dokuwiki
18 | state: directory
19 | owner: www-data
20 | group: www-data
21 |
22 | - name: Ensure dokuwiki install.php not present
23 | file:
24 | path: /var/www/dokuwiki/install.php
25 | state: absent
26 |
27 | - name: Ensure Nginx Dokuwiki conf
28 | template:
29 | src: dokuwiki.nginx
30 | dest: "/etc/nginx/{{ nginx_domain_name }}.d/dokuwiki.conf"
31 |
32 |
33 |
--------------------------------------------------------------------------------
/roles/dokuwiki/templates/dokuwiki-backup.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | BACKUPFILE=dokuwiki_`date +%d-%m-%Y"_"%H_%M_%S`.tgz
4 | LOGFILE=/var/backups/dokuwiki/dokuwiki.log
5 |
6 | {
7 | tar -zcvf /var/backups/dokuwiki/$BACKUPFILE {{ nginx_root }}
8 | rsync -av --delete /var/backups/dokuwiki srf@lb.ulmlernt.org:/var/backups/dokuwiki
9 | } 2>&1 | tee -a $LOGFILE
10 |
11 |
--------------------------------------------------------------------------------
/roles/dokuwiki/templates/dokuwiki.nginx:
--------------------------------------------------------------------------------
1 | client_max_body_size 200M;
2 |
3 | location / {
4 | index doku.php;
5 | try_files $uri $uri/ @dokuwiki;
6 | }
7 |
8 | location ~ ^/lib.*\.(gif|png|ico|jpg)$ {
9 | expires 30d;
10 | }
11 |
12 | location ^~ /conf/ { return 403; }
13 | location ^~ /data/ { return 403; }
14 |
15 | location @dokuwiki {
16 | rewrite ^/_media/(.*) /lib/exe/fetch.php?media=$1 last;
17 | rewrite ^/_detail/(.*) /lib/exe/detail.php?media=$1 last;
18 | rewrite ^/_export/([^/]+)/(.*) /doku.php?do=export_$1&id=$2 last;
19 | rewrite ^/(.*) /doku.php?id=$1 last;
20 | }
21 |
22 | location ~ \.php$ {
23 | include fastcgi_params;
24 | fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
25 | fastcgi_pass unix:/run/php/php7.2-fpm.sock;
26 | }
27 |
--------------------------------------------------------------------------------
/roles/greenlight/handlers/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: restart greenlight
3 | service:
4 | name: greenlight
5 | state: restarted
6 |
--------------------------------------------------------------------------------
/roles/greenlight/tasks/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Ensure group "greenlight" exists
3 | group:
4 | name: greenlight
5 | state: present
6 |
7 | - name: Ensure user greenlight exists
8 | user:
9 | name: greenlight
10 | shell: /bin/bash
11 | password: '!'
12 | update_password: on_create
13 | groups: greenlight
14 | state: present
15 |
16 | - name: Checkout greenlight repository
17 | git:
18 | repo: https://github.com/stadtulm/greenlight.git
19 | dest: /var/www/greenlight
20 | version: ulmlernt
21 | force: yes
22 | notify:
23 | - restart greenlight
24 | - reload nginx
25 |
26 | - name: Ensure greenlight is owner of /var/www/greenlight
27 | file:
28 | path: /var/www/greenlight
29 | recurse: yes
30 | owner: greenlight
31 | group: greenlight
32 |
33 | - name: Ensure Bundler 2 is installed
34 | gem:
35 | name: bundler
36 | state: latest
37 | user_install: no
38 |
39 | - name: Ensure sqlite is installed
40 | apt:
41 | name:
42 | - sqlite3
43 | - libsqlite3-dev
44 | state: present
45 |
46 | # bundler: deployment mode by cli argument is deprecated, so we're going to run this:
47 | - name: Ensure bundle in is deployment mode
48 | command:
49 | cmd: bundle config set deployment 'true'
50 | chdir: /var/www/greenlight
51 | creates: /var/www/greenlight/.bundle/config
52 | become: yes
53 | become_user: greenlight
54 |
55 | - name: Ensure gems for greenlight are installed
56 | bundler:
57 | state: present
58 | exclude_groups:
59 | - development
60 | - test
61 | chdir: /var/www/greenlight
62 | become: yes
63 | become_user: greenlight
64 |
65 | - name: find out what the current greenlight env is
66 | slurp:
67 | src: /var/www/greenlight/.env
68 | register: current_greenlight_env
69 | ignore_errors: yes
70 | when: not greenlight_key_base is defined
71 |
72 | - name: register current turn secret
73 | set_fact:
74 | greenlight_key_base: "{{ current_greenlight_env.content | b64decode | regex_findall('SECRET_KEY_BASE=(.+)') | first }}"
75 | when: not greenlight_key_base is defined and not current_greenlight_env.failed
76 |
77 | - name: Generate greenlight secret key base
78 | command: openssl rand -hex 64
79 | register: openssl_rand64
80 | check_mode: no
81 | when: not greenlight_key_base is defined
82 |
83 | - name: Register secret key base
84 | set_fact:
85 | greenlight_key_base: "{{ openssl_rand64.stdout }}"
86 | when: not greenlight_key_base is defined
87 |
88 | - name: Ensure environment file for greenlight
89 | template:
90 | src: env.j2
91 | dest: /var/www/greenlight/.env
92 | become: yes
93 | become_user: greenlight
94 |
95 | - name: Ensure systemd unit for greenlight
96 | template:
97 | src: greenlight.service
98 | dest: /etc/systemd/system/greenlight.service
99 |
100 | - name: Enable greenlight units
101 | systemd:
102 | daemon_reload: yes
103 | name: greenlight
104 | enabled: yes
105 |
106 | - name: Ensure greenlight is started
107 | systemd:
108 | name: greenlight
109 | state: started
110 |
111 | - name: Ensure greenlight nginx config exists
112 | template:
113 | src: nginx-greenlight.conf.j2
114 | dest: /etc/nginx/a13.d/greenlight.conf
115 | notify: reload nginx
116 |
117 | - name: Restart Greenlight every night @4am
118 | cron:
119 | state: present
120 | name: "restart Greenlight"
121 | minute: "0"
122 | hour: "4"
123 | user: root
124 | job: "systemctl restart greenlight.service"
125 |
--------------------------------------------------------------------------------
/roles/greenlight/templates/env.j2:
--------------------------------------------------------------------------------
1 | PORT=5000
2 | RAILS_ENV=production
3 |
4 | REDIS_URL=redis://127.0.0.1:6379
5 |
6 | # Create a Secret Key for Rails
7 | #
8 | # You can generate a secure one through the Greenlight docker image
9 | # with the command.
10 | #
11 | # docker run --rm bigbluebutton/greenlight:v2 bundle exec rake secret
12 | #
13 | SECRET_KEY_BASE={{ greenlight_key_base }}
14 |
15 | # The endpoint and secret for your BigBlueButton server.
16 | # Set these if you are running GreenLight on a single BigBlueButton server.
17 | # You can retrive these by running the following command on your BigBlueButton server:
18 | #
19 | # bbb-conf --secret
20 | #
21 | BIGBLUEBUTTON_ENDPOINT={{ lb_url }}
22 | BIGBLUEBUTTON_SECRET={{ lb_secret }}
23 |
24 |
25 | # Set this to true if you want GreenLight to support user signup and login without
26 | # Omniauth. For more information, see:
27 | #
28 | # https://docs.bigbluebutton.org/greenlight/gl-overview.html#accounts-and-profile
29 | #
30 | ALLOW_GREENLIGHT_ACCOUNTS=true
31 |
32 | # Set this to true if you want GreenLight to send verification emails upon
33 | # the creation of a new account
34 | #
35 | ALLOW_MAIL_NOTIFICATIONS=true
36 |
37 | # The notifications are sent using sendmail, unless the SMTP_SERVER variable is set.
38 | # In that case, make sure the rest of the variables are properly set.
39 | #
40 | # SMTP_SERVER=smtp.gmail.com
41 | # SMTP_PORT=587
42 | # SMTP_DOMAIN=gmail.com
43 | # SMTP_USERNAME=
44 | # SMTP_PASSWORD=
45 | # SMTP_AUTH=plain
46 | # SMTP_STARTTLS_AUTO=true
47 | #
48 | SMTP_SERVER={{ mail_admin.server }}
49 | SMTP_PORT={{ mail_admin.port }}
50 | SMTP_DOMAIN={{ mail_admin.domain }}
51 | SMTP_USERNAME={{ mail_admin.user }}
52 | SMTP_PASSWORD={{ mail_admin_password }}
53 | SMTP_AUTH={{ mail_admin.auth }}
54 | SMTP_STARTTLS_AUTO={{ mail_admin.starttls }}
55 |
56 | # Specify the email address that all mail is sent from
57 | SMTP_SENDER={{ mail_admin.sender }}
58 |
59 | # Prefix for the applications root URL.
60 | # Useful for deploying the application to a subdirectory, which is highly recommended
61 | # if deploying on a BigBlueButton server. Keep in mind that if you change this, you'll
62 | # have to update your authentication callback URL's to reflect this change.
63 | #
64 | # The recommended prefix is "/b".
65 | #
66 | RELATIVE_URL_ROOT=/
67 |
68 | # Specify which settings you would like the users to configure on room creation
69 | # or edit after the room has been created
70 | # By default, all settings are turned OFF.
71 | #
72 | # Current settings available:
73 | # mute-on-join: Automatically mute users by default when they join a room
74 | # require-moderator-approval: Require moderators to approve new users before they can join the room
75 | # anyone-can-start: Allows anyone with the join url to start the room in BigBlueButton
76 | # all-join-moderator: All users join as moderators in BigBlueButton
77 | ROOM_FEATURES=mute-on-join,require-moderator-approval,anyone-can-start,all-join-moderator
78 |
79 | # Specify the maximum number of records to be sent to the BigBlueButton API in one call
80 | # Default is set to 25 records
81 | PAGINATION_NUMBER=25
82 |
83 | # Specify the maximum number of rows that should be displayed per page for a paginated table
84 | # Default is set to 25 rows
85 | NUMBER_OF_ROWS=25
86 |
87 | # Specify if you want to display the Google Calendar button
88 | # ENABLE_GOOGLE_CALENDAR_BUTTON=true|false
89 | ENABLE_GOOGLE_CALENDAR_BUTTON=false
90 |
91 | # Set the application into Maintenance Mode
92 | #
93 | # Current options supported:
94 | # true: Renders an error page that does not allow users to access any of the features in the application
95 | # false: Application runs normally
96 | MAINTENANCE_MODE=false
97 |
98 | # Displays a flash that appears to inform the user of a scheduled maintenance window
99 | # This variable should contain ONLY the date and time of the scheduled maintenance
100 | #
101 | # Ex: MAINTENANCE_WINDOW=Friday August 18 6pm-10pm EST
102 | MAINTENANCE_WINDOW=
103 |
104 | # The link to the Report an Issue button that appears on the 500 page and in the Account Dropdown
105 | #
106 | # Defaults to the Github Issues Page for Greenlight
107 | # Button can be disabled by setting the value to blank
108 | REPORT_ISSUE_URL=https://github.com/bigbluebutton/greenlight/issues/new
109 |
110 | # Comment this out to send logs to STDOUT in production instead of log/production.log .
111 | #
112 | RAILS_LOG_TO_STDOUT=true
113 |
114 | #
115 | # Force SSL
116 | #
117 | ENABLE_SSL=true
118 |
119 | # Database settings
120 | #
121 | # Greenlight may work out of the box with sqlite3, but for production it is recommended to use postgresql.
122 | # In such case, these variables must be included.
123 | #
124 | DB_ADAPTER=postgresql
125 | DB_HOST=127.0.0.1
126 | DB_NAME=greenlight_production
127 | DB_USERNAME=greenlight
128 | DB_PASSWORD=trust
129 |
130 | # Specify the default registration to be used by Greenlight until an administrator sets the
131 | # registration method
132 | # Allowed values are:
133 | # open - For open registration
134 | # invite - For invite only registration
135 | # approval - For approve/decline registration
136 | DEFAULT_REGISTRATION=approval
137 |
--------------------------------------------------------------------------------
/roles/greenlight/templates/greenlight.service:
--------------------------------------------------------------------------------
1 | [Unit]
2 | Description=Greenlight
3 | After=network-online.target
4 | Wants=network-online.target postgresql.service
5 |
6 | [Service]
7 | Type=simple
8 | EnvironmentFile=/var/www/greenlight/.env
9 | WorkingDirectory=/var/www/greenlight
10 | Restart=always
11 | RestartSec=60
12 | ExecStart=/var/www/greenlight/bin/start
13 | User=greenlight
14 |
15 | [Install]
16 | WantedBy=multi-user.target
17 |
--------------------------------------------------------------------------------
/roles/greenlight/templates/nginx-greenlight.conf.j2:
--------------------------------------------------------------------------------
1 | access_log /var/log/nginx/access-greenlight.log cri;
2 |
3 | location / {
4 | proxy_pass http://127.0.0.1:5000;
5 | proxy_set_header Host $host;
6 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
7 | proxy_set_header X-Forwarded-Proto $scheme;
8 | proxy_set_header X-Request-Id $request_id;
9 | proxy_http_version 1.1;
10 | }
11 |
12 | location /cable {
13 | proxy_pass http://127.0.0.1:5000;
14 | proxy_set_header Host $host;
15 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
16 | proxy_set_header X-Forwarded-Proto $scheme;
17 | proxy_set_header X-Request-Id $request_id;
18 | proxy_set_header Upgrade $http_upgrade;
19 | proxy_set_header Connection "Upgrade";
20 | proxy_http_version 1.1;
21 | proxy_read_timeout 6h;
22 | proxy_send_timeout 6h;
23 | client_body_timeout 6h;
24 | send_timeout 6h;
25 | }
26 |
27 | # Allow larger body size for uploading presentations
28 | location ~ /preupload_presentation$ {
29 | client_max_body_size 30m;
30 |
31 | proxy_pass http://127.0.0.1:5000;
32 | proxy_set_header Host $host;
33 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
34 | proxy_set_header X-Forwarded-Proto $scheme;
35 | proxy_set_header X-Request-Id $request_id;
36 | proxy_http_version 1.1;
37 | }
38 |
--------------------------------------------------------------------------------
/roles/nginx-tls-add/handlers/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: reload nginx
3 | service:
4 | name: nginx
5 | state: reloaded
6 |
--------------------------------------------------------------------------------
/roles/nginx-tls-add/tasks/config.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Ensure nginx subconfig folder exists
3 | file:
4 | path: "/etc/nginx/{{ nginx_domain_name }}.d"
5 | state: directory
6 |
7 | - name: Register certbot certificate file
8 | stat:
9 | path: "/etc/letsencrypt/live/{{ nginx_domain_name }}/fullchain.pem"
10 | register: certbot_certificate_file_path
11 |
12 | - name: Ensure availability of generic nginx config
13 | template:
14 | src: a13.conf.j2
15 | dest: "/etc/nginx/sites-available/{{ nginx_domain_name }}.https.conf"
16 |
17 | - name: Generate dhparams
18 | shell: openssl dhparam -out /etc/nginx/dhparams.pem 2048
19 | args:
20 | creates: /etc/nginx/dhparams.pem
21 |
22 | - name: Ensure generic nginx config is applied
23 | file:
24 | state: link
25 | src: "/etc/nginx/sites-available/{{ nginx_domain_name }}.https.conf"
26 | dest: "/etc/nginx/sites-enabled/{{ nginx_domain_name }}.https.conf"
27 | when: certbot_certificate_file_path.stat.exists
28 | notify: reload nginx
29 |
--------------------------------------------------------------------------------
/roles/nginx-tls-add/tasks/http.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Remove default nginx config
3 | file:
4 | name: /etc/nginx/sites-enabled/default
5 | state: absent
6 |
7 | - name: Ensure folder for letsencrypt exists
8 | file:
9 | name: /var/www/letsencrypt
10 | state: directory
11 |
12 | - name: Ensure nginx site for letsencrypt requests
13 | template:
14 | src: http.conf.j2
15 | dest: "/etc/nginx/sites-available/{{ nginx_domain_name}}.http.conf"
16 |
17 | - name: Ensure generic nginx config is applied
18 | file:
19 | state: link
20 | src: "/etc/nginx/sites-available/{{ nginx_domain_name}}.http.conf"
21 | dest: "/etc/nginx/sites-enabled/{{ nginx_domain_name}}.http.conf"
22 | notify: reload nginx
23 |
--------------------------------------------------------------------------------
/roles/nginx-tls-add/tasks/letsencrypt.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Add certbot apt repo
3 | apt_repository:
4 | repo: ppa:certbot/certbot
5 | state: present
6 |
7 | - name: Install certbot
8 | apt:
9 | pkg:
10 | - certbot
11 | - python3-certbot-nginx
12 | update_cache: true
13 | state: present
14 |
15 | - name: Ensure Let's Encrypt uses API v2
16 | lineinfile:
17 | path: /etc/letsencrypt/cli.ini
18 | regexp: '^server'
19 | line: 'server = https://acme-v02.api.letsencrypt.org/directory'
20 |
21 | - name: Register certbot certificate file
22 | stat:
23 | path: "/etc/letsencrypt/live/{{ nginx_domain_name }}/fullchain.pem"
24 | register: certbot_certificate_file_path
25 |
26 | - name: Generate Certificates
27 | command: certbot certonly -d {{ nginx_domain_name }} --agree-tos --email {{ letsencrypt_email }} -n --nginx
28 | notify: reload nginx
29 | when: not certbot_certificate_file_path.stat.exists
30 |
31 | # FIXME: webroot
32 |
--------------------------------------------------------------------------------
/roles/nginx-tls-add/tasks/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - import_tasks: http.yml
3 |
4 | - import_tasks: letsencrypt.yml
5 |
6 | - import_tasks: config.yml
7 |
--------------------------------------------------------------------------------
/roles/nginx-tls-add/templates/a13.conf.j2:
--------------------------------------------------------------------------------
1 | server {
2 | listen 443 ssl http2;
3 | listen [::]:443 ssl http2;
4 | server_name {{ nginx_domain_name }};
5 |
6 | ssl_certificate /etc/letsencrypt/live/{{ nginx_domain_name }}/fullchain.pem;
7 | ssl_certificate_key /etc/letsencrypt/live/{{ nginx_domain_name }}/privkey.pem;
8 | ssl_trusted_certificate /etc/letsencrypt/live/{{ nginx_domain_name }}/fullchain.pem;
9 |
10 | ssl_session_cache shared:SSL:50m;
11 | ssl_session_timeout 5m;
12 | ssl_stapling on;
13 | ssl_stapling_verify on;
14 |
15 | ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
16 | ssl_ciphers "ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:DES-CBC3-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4";
17 |
18 | ssl_dhparam /etc/nginx/dhparams.pem;
19 | ssl_prefer_server_ciphers on;
20 |
21 | {% if nginx_root is defined %}
22 | root {{ nginx_root }};
23 | {% else %}
24 | root /var/www/html;
25 | {% endif %}
26 | include /etc/nginx/{{ nginx_domain_name }}.d/*.conf;
27 | }
28 |
--------------------------------------------------------------------------------
/roles/nginx-tls-add/templates/http.conf.j2:
--------------------------------------------------------------------------------
1 | server {
2 | listen 80;
3 | listen [::]:80;
4 | server_name {{ nginx_domain_name }};
5 |
6 | location /.well-known/acme-challenge {
7 | root /var/www/letsencrypt;
8 | try_files $uri $uri/ =404;
9 | }
10 |
11 | location / {
12 | rewrite ^ https://{{ nginx_domain_name }}$request_uri? permanent;
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/roles/nginx-tls-monitoring/handlers/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: reload nginx
3 | service:
4 | name: nginx
5 | state: reloaded
6 |
--------------------------------------------------------------------------------
/roles/nginx-tls-monitoring/tasks/config.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Register certbot certificate file
3 | stat:
4 | path: "/etc/letsencrypt/live/{{ nginx_domain_name }}/fullchain.pem"
5 | register: certbot_certificate_file_path
6 |
7 | - name: Ensure availability of monitoring nginx config
8 | template:
9 | src: mon.conf.j2
10 | dest: /etc/nginx/sites-available/mon.conf
11 |
12 | - name: Generate dhparams
13 | shell: openssl dhparam -out /etc/nginx/dhparams.pem 2048
14 | args:
15 | creates: /etc/nginx/dhparams.pem
16 |
17 | - name: Ensure generic nginx config is applied
18 | file:
19 | state: link
20 | src: /etc/nginx/sites-available/mon.conf
21 | dest: /etc/nginx/sites-enabled/mon.conf
22 | when: certbot_certificate_file_path.stat.exists
23 | notify: reload nginx
24 |
--------------------------------------------------------------------------------
/roles/nginx-tls-monitoring/tasks/http.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Remove default nginx config
3 | file:
4 | name: /etc/nginx/sites-enabled/default
5 | state: absent
6 |
7 | - name: Ensure folder for letsencrypt exists
8 | file:
9 | name: /var/www/letsencrypt
10 | state: directory
11 |
12 | - name: Ensure nginx site for letsencrypt requests
13 | template:
14 | src: http-mon.conf.j2
15 | dest: /etc/nginx/sites-available/http-mon.conf
16 |
17 | - name: Ensure generic nginx config is applied
18 | file:
19 | state: link
20 | src: /etc/nginx/sites-available/http-mon.conf
21 | dest: /etc/nginx/sites-enabled/http-mon.conf
22 | notify: reload nginx
23 |
--------------------------------------------------------------------------------
/roles/nginx-tls-monitoring/tasks/letsencrypt.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Add certbot apt repo
3 | apt_repository:
4 | repo: ppa:certbot/certbot
5 | state: present
6 |
7 | - name: Install certbot
8 | apt:
9 | pkg:
10 | - certbot
11 | - python3-certbot-nginx
12 | update_cache: true
13 | state: present
14 |
15 | - name: Ensure Let's Encrypt uses API v2
16 | lineinfile:
17 | path: /etc/letsencrypt/cli.ini
18 | regexp: '^server'
19 | line: 'server = https://acme-v02.api.letsencrypt.org/directory'
20 |
21 | - name: Register certbot certificate file
22 | stat:
23 | path: "/etc/letsencrypt/live/{{ nginx_domain_name }}/fullchain.pem"
24 | register: certbot_certificate_file_path
25 |
26 | - name: Generate Certificates
27 | command: certbot certonly -d {{ nginx_domain_name }} --agree-tos --email {{ letsencrypt_email }} -n --nginx
28 | notify: reload nginx
29 | when: not certbot_certificate_file_path.stat.exists
30 |
--------------------------------------------------------------------------------
/roles/nginx-tls-monitoring/tasks/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - import_tasks: http.yml
3 |
4 | - import_tasks: letsencrypt.yml
5 |
6 | - import_tasks: config.yml
7 |
--------------------------------------------------------------------------------
/roles/nginx-tls-monitoring/templates/http-mon.conf.j2:
--------------------------------------------------------------------------------
1 | server {
2 | listen 80;
3 | listen [::]:80;
4 | server_name {{ nginx_domain_name }};
5 |
6 | location /.well-known/acme-challenge {
7 | root /var/www/letsencrypt;
8 | try_files $uri $uri/ =404;
9 | }
10 |
11 | location / {
12 | rewrite ^ https://{{ nginx_domain_name }}$request_uri? permanent;
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/roles/nginx-tls-monitoring/templates/mon.conf.j2:
--------------------------------------------------------------------------------
1 | server {
2 | listen 443 ssl http2;
3 | listen [::]:443 ssl http2;
4 | server_name {{ nginx_domain_name }};
5 |
6 | ssl_certificate /etc/letsencrypt/live/{{ nginx_domain_name }}/fullchain.pem;
7 | ssl_certificate_key /etc/letsencrypt/live/{{ nginx_domain_name }}/privkey.pem;
8 | ssl_trusted_certificate /etc/letsencrypt/live/{{ nginx_domain_name }}/fullchain.pem;
9 |
10 | ssl_session_cache shared:SSL:50m;
11 | ssl_session_timeout 5m;
12 | ssl_stapling on;
13 | ssl_stapling_verify on;
14 |
15 | ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
16 | ssl_ciphers "ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:DES-CBC3-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4";
17 |
18 | ssl_dhparam /etc/nginx/dhparams.pem;
19 | ssl_prefer_server_ciphers on;
20 |
21 | root /var/www/html;
22 |
23 | location / {
24 | proxy_pass http://127.0.0.1:3001;
25 |
26 | proxy_read_timeout 60s;
27 | proxy_redirect off;
28 |
29 | proxy_set_header Host $http_host;
30 |
31 | proxy_set_header X-Real-IP $remote_addr;
32 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
33 | proxy_set_header X-Forwarded-Proto $scheme;
34 |
35 | proxy_http_version 1.1;
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/roles/nginx-tls-redirect/handlers/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: reload nginx
3 | service:
4 | name: nginx
5 | state: reloaded
6 |
--------------------------------------------------------------------------------
/roles/nginx-tls-redirect/tasks/config.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Ensure availability of generic nginx config
3 | template:
4 | src: a13.conf.j2
5 | dest: "/etc/nginx/sites-available/{{ nginx_redirect_alias }}.https.conf"
6 |
7 | - name: Ensure generic nginx config is applied
8 | file:
9 | state: link
10 | src: "/etc/nginx/sites-available/{{ nginx_redirect_alias }}.https.conf"
11 | dest: "/etc/nginx/sites-enabled/{{ nginx_redirect_alias }}.https.conf"
12 | when: certbot_certificate_file_path.stat.exists
13 | notify: reload nginx
14 |
--------------------------------------------------------------------------------
/roles/nginx-tls-redirect/tasks/http.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Ensure nginx site for letsencrypt requests
3 | template:
4 | src: http.conf.j2
5 | dest: "/etc/nginx/sites-available/{{ nginx_redirect_alias }}.http.conf"
6 |
7 | - name: Ensure generic nginx config is applied
8 | file:
9 | state: link
10 | src: "/etc/nginx/sites-available/{{ nginx_redirect_alias }}.http.conf"
11 | dest: "/etc/nginx/sites-enabled/{{ nginx_redirect_alias }}.http.conf"
12 | notify: reload nginx
13 |
--------------------------------------------------------------------------------
/roles/nginx-tls-redirect/tasks/letsencrypt.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Add certbot apt repo
3 | apt_repository:
4 | repo: ppa:certbot/certbot
5 | state: present
6 |
7 | - name: Install certbot
8 | apt:
9 | pkg:
10 | - certbot
11 | - python3-certbot-nginx
12 | update_cache: true
13 | state: present
14 |
15 | - name: Ensure Let's Encrypt uses API v2
16 | lineinfile:
17 | path: /etc/letsencrypt/cli.ini
18 | regexp: '^server'
19 | line: 'server = https://acme-v02.api.letsencrypt.org/directory'
20 |
21 |
22 | - name: Register certbot certificate file
23 | stat:
24 | path: "/etc/letsencrypt/live/{{ nginx_redirect_alias }}/fullchain.pem"
25 | register: certbot_certificate_file_path
26 |
27 | - name: Generate Certificates
28 | command: certbot certonly -d {{ nginx_redirect_alias }} --agree-tos --email {{ letsencrypt_email }} -n --nginx
29 | notify: reload nginx
30 | when: not certbot_certificate_file_path.stat.exists
31 |
32 | # FIXME: webroot
33 |
--------------------------------------------------------------------------------
/roles/nginx-tls-redirect/tasks/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - import_tasks: http.yml
3 |
4 | - import_tasks: letsencrypt.yml
5 |
6 | - import_tasks: config.yml
7 |
--------------------------------------------------------------------------------
/roles/nginx-tls-redirect/templates/a13.conf.j2:
--------------------------------------------------------------------------------
1 | server {
2 | listen 443 ssl http2;
3 | listen [::]:443 ssl http2;
4 | server_name {{ nginx_redirect_alias }};
5 |
6 | ssl_certificate /etc/letsencrypt/live/{{ nginx_redirect_alias }}/fullchain.pem;
7 | ssl_certificate_key /etc/letsencrypt/live/{{ nginx_redirect_alias }}/privkey.pem;
8 | ssl_trusted_certificate /etc/letsencrypt/live/{{ nginx_redirect_alias }}/fullchain.pem;
9 |
10 | ssl_session_cache shared:SSL:50m;
11 | ssl_session_timeout 5m;
12 | ssl_stapling on;
13 | ssl_stapling_verify on;
14 |
15 | ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
16 | ssl_ciphers "ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:DES-CBC3-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4";
17 |
18 | ssl_dhparam /etc/nginx/dhparams.pem;
19 | ssl_prefer_server_ciphers on;
20 |
21 | root /var/www/html;
22 |
23 | location / {
24 | rewrite ^ https://{{ nginx_domain_name }}$request_uri? permanent;
25 | }
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/roles/nginx-tls-redirect/templates/http.conf.j2:
--------------------------------------------------------------------------------
1 | server {
2 | listen 80;
3 | listen [::]:80;
4 | server_name {{ nginx_redirect_alias }};
5 |
6 | location /.well-known/acme-challenge {
7 | root /var/www/letsencrypt;
8 | try_files $uri $uri/ =404;
9 | }
10 |
11 | location / {
12 | rewrite ^ https://{{ nginx_domain_name }}$request_uri? permanent;
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/roles/nginx-tls/files/hackhack.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stadtulm/a13-ansible/3b0c44d9712af5c5d3bd745c0a987cc9acf92102/roles/nginx-tls/files/hackhack.gif
--------------------------------------------------------------------------------
/roles/nginx-tls/files/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | ulmlernt
6 |
27 |
28 |
29 | Wartungsarbeiten

30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/roles/nginx-tls/handlers/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: reload nginx
3 | service:
4 | name: nginx
5 | state: reloaded
6 |
--------------------------------------------------------------------------------
/roles/nginx-tls/tasks/config.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Ensure nginx subconfig folder exists
3 | file:
4 | path: /etc/nginx/a13.d
5 | state: directory
6 |
7 | - name: Register certbot certificate file
8 | stat:
9 | path: "/etc/letsencrypt/live/{{ nginx_domain_name }}/fullchain.pem"
10 | register: certbot_certificate_file_path
11 |
12 | - name: Ensure availability of generic nginx config
13 | template:
14 | src: a13.conf.j2
15 | dest: /etc/nginx/sites-available/a13.conf
16 |
17 | - name: Ensure existence of maintenance folder
18 | file:
19 | path: /var/www/html/maintenance
20 | state: directory
21 |
22 | - name: Copy maintenance files
23 | copy:
24 | src: "{{item}}"
25 | dest: "/var/www/html/maintenance/{{item}}"
26 | with_items:
27 | - index.html
28 | - hackhack.gif
29 |
30 | - name: Generate dhparams
31 | shell: openssl dhparam -out /etc/nginx/dhparams.pem 2048
32 | args:
33 | creates: /etc/nginx/dhparams.pem
34 |
35 | - name: Ensure generic nginx config is applied
36 | file:
37 | state: link
38 | src: /etc/nginx/sites-available/a13.conf
39 | dest: /etc/nginx/sites-enabled/a13.conf
40 | when: certbot_certificate_file_path.stat.exists
41 | notify: reload nginx
42 |
--------------------------------------------------------------------------------
/roles/nginx-tls/tasks/http.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Remove default nginx config
3 | file:
4 | name: /etc/nginx/sites-enabled/default
5 | state: absent
6 |
7 | - name: Ensure folder for letsencrypt exists
8 | file:
9 | name: /var/www/letsencrypt
10 | state: directory
11 |
12 | - name: Ensure nginx site for letsencrypt requests
13 | template:
14 | src: http.conf.j2
15 | dest: /etc/nginx/sites-available/http.conf
16 |
17 | - name: Ensure generic nginx config is applied
18 | file:
19 | state: link
20 | src: /etc/nginx/sites-available/http.conf
21 | dest: /etc/nginx/sites-enabled/http.conf
22 | notify: reload nginx
23 |
--------------------------------------------------------------------------------
/roles/nginx-tls/tasks/letsencrypt.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Add certbot apt repo
3 | apt_repository:
4 | repo: ppa:certbot/certbot
5 | state: present
6 |
7 | - name: Install certbot
8 | apt:
9 | pkg:
10 | - certbot
11 | - python3-certbot-nginx
12 | update_cache: true
13 | state: present
14 |
15 | - name: Ensure Let's Encrypt uses API v2
16 | lineinfile:
17 | path: /etc/letsencrypt/cli.ini
18 | regexp: '^server'
19 | line: 'server = https://acme-v02.api.letsencrypt.org/directory'
20 |
21 | - name: Register certbot certificate file
22 | stat:
23 | path: "/etc/letsencrypt/live/{{ nginx_domain_name }}/fullchain.pem"
24 | register: certbot_certificate_file_path
25 |
26 | - name: Generate Certificates
27 | command: certbot certonly -d {{ nginx_domain_name }} --agree-tos --email {{ letsencrypt_email }} -n --nginx
28 | notify: reload nginx
29 | when: not certbot_certificate_file_path.stat.exists
30 |
31 | # FIXME: webroot
32 |
--------------------------------------------------------------------------------
/roles/nginx-tls/tasks/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - import_tasks: http.yml
3 |
4 | - import_tasks: letsencrypt.yml
5 |
6 | - import_tasks: config.yml
7 |
--------------------------------------------------------------------------------
/roles/nginx-tls/templates/a13.conf.j2:
--------------------------------------------------------------------------------
1 | server {
2 | listen 443 ssl http2;
3 | listen [::]:443 ssl http2;
4 | server_name {{ nginx_domain_name }};
5 |
6 | ssl_certificate /etc/letsencrypt/live/{{ nginx_domain_name }}/fullchain.pem;
7 | ssl_certificate_key /etc/letsencrypt/live/{{ nginx_domain_name }}/privkey.pem;
8 | ssl_trusted_certificate /etc/letsencrypt/live/{{ nginx_domain_name }}/fullchain.pem;
9 |
10 | ssl_session_cache shared:SSL:50m;
11 | ssl_session_timeout 5m;
12 | ssl_stapling on;
13 | ssl_stapling_verify on;
14 |
15 | ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
16 | ssl_ciphers "ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:DES-CBC3-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4";
17 |
18 | ssl_dhparam /etc/nginx/dhparams.pem;
19 | ssl_prefer_server_ciphers on;
20 |
21 | root /var/www/html;
22 |
23 | location /maintenance {
24 | index index.html;
25 | }
26 |
27 | # uncomment this for maintenance
28 | #if ($remote_addr != 127.0.0.1) {
29 | # return 503;
30 | #}
31 |
32 | error_page 503 @maintenance;
33 | location @maintenance {
34 | rewrite ^(.*)hackhack.gif$ /maintenance/hackhack.gif break;
35 | rewrite ^(.*)$ /maintenance/index.html break;
36 | }
37 |
38 | include /etc/nginx/a13.d/*.conf;
39 | }
40 |
--------------------------------------------------------------------------------
/roles/nginx-tls/templates/http.conf.j2:
--------------------------------------------------------------------------------
1 | server_tokens off;
2 |
3 | server {
4 | listen 80 default_server;
5 | listen [::]:80 default_server;
6 | server_name {{ nginx_domain_name }};
7 |
8 | location /.well-known/acme-challenge {
9 | root /var/www/letsencrypt;
10 | try_files $uri $uri/ =404;
11 | }
12 |
13 | location / {
14 | rewrite ^ https://{{ nginx_domain_name }}$request_uri? permanent;
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/roles/nginx/handlers/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: reload nginx
3 | service:
4 | name: nginx
5 | state: reloaded
6 |
--------------------------------------------------------------------------------
/roles/nginx/tasks/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Ensure nginx ppa is used for newer nginx versions
3 | apt_repository:
4 | repo: ppa:nginx/stable
5 | register: nginx_repo
6 |
7 | - name: Ensure nginx from ppa is pinned
8 | copy:
9 | content: |
10 | Package: *
11 | Pin: release n=xenial,o=LP-PPA-nginx-stable
12 | Pin-Priority: 1000
13 | dest: /etc/apt/preferences.d/ppa-nginx
14 | mode: 0755
15 | register: nginx_ppa_pin
16 |
17 | - name: Ensure apt cache gets updated
18 | apt:
19 | update_cache: yes
20 | when: nginx_repo.changed or nginx_ppa_pin.changed
21 |
22 | - name: Ensure nginx installed
23 | apt:
24 | name: nginx
25 | state: present
26 |
27 | - name: Ensure nginx from ppa is really installed
28 | apt:
29 | name: nginx
30 | state: latest
31 | when: nginx_repo.changed or nginx_ppa_pin.changed
32 |
33 | - name: Ensure nginx ufw configuration is available
34 | copy:
35 | # this is the standard config from the nginx package(?)
36 | # space character in the rule name is not that nice, but whatever
37 | content: |
38 | [Nginx HTTP]
39 | title=Web Server (Nginx, HTTP)
40 | description=Small, but very powerful and efficient web server
41 | ports=80/tcp
42 |
43 | [Nginx HTTPS]
44 | title=Web Server (Nginx, HTTPS)
45 | description=Small, but very powerful and efficient web server
46 | ports=443/tcp
47 |
48 | [Nginx Full]
49 | title=Web Server (Nginx, HTTP + HTTPS)
50 | description=Small, but very powerful and efficient web server
51 | ports=80,443/tcp
52 | mode: 0644
53 | dest: /etc/ufw/applications.d/nginx
54 |
55 | - name: Allow nginx in ufw
56 | ufw:
57 | rule: allow
58 | name: Nginx Full
59 | state: enabled
60 |
61 | - name: Ensure nginx configuration contains log format with request id
62 | lineinfile:
63 | line: "log_format cri '$remote_addr - $remote_user [$time_local] \"$request\" $status $body_bytes_sent \"$http_referer\" \"$http_user_agent\" request_id=$request_id';"
64 | insertbefore: '^\s*access_log'
65 | path: /etc/nginx/nginx.conf
66 | # validate: '/usr/sbin/nginx -t -c %s'
67 | notify: reload nginx
68 |
--------------------------------------------------------------------------------
/roles/no-docker/tasks/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Ensure Docker Key is removed
3 | apt_key:
4 | id: 9DC858229FC7DD38854AE2D88D81803C0EBFCD88
5 | state: absent
6 |
7 | - name: Ensure Docker Repository is absent
8 | apt_repository:
9 | repo: "{{ item }}"
10 | state: absent
11 | with_items:
12 | - "deb [arch=amd64] https://download.docker.com/linux/ubuntu bionic stable"
13 | - "deb [arch=amd64] https://download.docker.com/linux/ubuntu xenial stable"
14 |
15 | - name: Ensure Docker is not installed
16 | apt:
17 | name:
18 | - docker-ce
19 | - docker-ce-cli
20 | - containerd.io
21 | - docker-compose
22 | state: absent
23 |
24 | - name: Ensure Docker group is deleted
25 | group:
26 | name: docker
27 | state: absent
28 |
29 | - name: check if docker network interface exists
30 | shell: ip a | grep docker
31 | register: docker_network_interface
32 |
33 | - name: Ensure Docker network interface is deleted
34 | command: ip link delete docker0
35 | when: docker_network_interface.rc == 0
36 |
--------------------------------------------------------------------------------
/roles/no-rocketchat/handlers/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: reload nginx
3 | service:
4 | name: nginx
5 | state: reloaded
6 |
--------------------------------------------------------------------------------
/roles/no-rocketchat/tasks/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: ensure rocketchat user does not exist
3 | user:
4 | name: rocketchat
5 | state: absent
6 |
7 | - name: Ensure rocketchat files / dir do not exist
8 | file:
9 | path: "{{ item }}"
10 | state: absent
11 | loop:
12 | - "/srv/rocketchat"
13 | - "/etc/nginx/chat.ulmlernt.org.d"
14 | - "/etc/nginx/sites-available/chat.ulmlernt.org.http.conf"
15 | - "/etc/nginx/sites-available/chat.ulmlernt.org.https.conf"
16 | - "/etc/nginx/sites-enabled/chat.ulmlernt.org.http.conf"
17 | - "/etc/nginx/sites-enabled/chat.ulmlernt.org.https.conf"
18 | notify: reload nginx
19 |
20 | - name: Ensure Docker is removed
21 | apt:
22 | name: docker-ce
23 | state: absent
24 |
--------------------------------------------------------------------------------
/roles/no-rocketchat/templates/no-rocketchat.nginx:
--------------------------------------------------------------------------------
1 | client_max_body_size 200M;
2 |
3 | location / {
4 | index doku.php;
5 | try_files $uri $uri/ @dokuwiki;
6 | }
7 |
8 | location ~ ^/lib.*\.(gif|png|ico|jpg)$ {
9 | expires 30d;
10 | }
11 |
12 | location ^~ /conf/ { return 403; }
13 | location ^~ /data/ { return 403; }
14 |
15 | location @dokuwiki {
16 | rewrite ^/_media/(.*) /lib/exe/fetch.php?media=$1 last;
17 | rewrite ^/_detail/(.*) /lib/exe/detail.php?media=$1 last;
18 | rewrite ^/_export/([^/]+)/(.*) /doku.php?do=export_$1&id=$2 last;
19 | rewrite ^/(.*) /doku.php?id=$1 last;
20 | }
21 |
22 | location ~ \.php$ {
23 | include fastcgi_params;
24 | fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
25 | fastcgi_pass unix:/run/php/php7.2-fpm.sock;
26 | }
27 |
--------------------------------------------------------------------------------
/roles/no-turn/tasks/main.yml:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stadtulm/a13-ansible/3b0c44d9712af5c5d3bd745c0a987cc9acf92102/roles/no-turn/tasks/main.yml
--------------------------------------------------------------------------------
/roles/node-exporter-ufw/tasks/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Ensure node-exporter config for ufw
3 | copy:
4 | content: |
5 | [node-exporter]
6 | title=node-exporter
7 | description=prometheus node exporter
8 | ports=9100/tcp
9 | mode: 0644
10 | dest: /etc/ufw/applications.d/node-exporter
11 |
12 | - name: allow node-exporter in ufw
13 | ufw:
14 | rule: allow
15 | name: node-exporter
16 | state: enabled
17 |
--------------------------------------------------------------------------------
/roles/prometheus-ufw/tasks/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Ensure prometheus config for ufw
3 | copy:
4 | content: |
5 | [prometheus]
6 | title=prometheus
7 | description=prometheus monitoring
8 | ports=9090/tcp
9 | mode: 0644
10 | dest: /etc/ufw/applications.d/prometheus
11 |
12 | - name: allow prometheus in ufw
13 | ufw:
14 | rule: allow
15 | name: prometheus
16 | state: enabled
17 |
--------------------------------------------------------------------------------
/roles/restic-client/tasks/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: "Ensure restic binary"
3 | apt:
4 | name: restic
5 | state: present
6 |
7 | - name: "Ensure restic task base directory"
8 | file:
9 | path: "/srv/restic/"
10 | owner: root
11 | mode: '0700'
12 | state: directory
13 |
14 | - name: "Ensure restic task base for {{ backup_name }}"
15 | file:
16 | path: "/srv/restic/{{ backup_name }}/"
17 | owner: root
18 | mode: '0700'
19 | state: directory
20 |
21 | - name: "Ensure init repo script for {{ backup_name }}"
22 | template:
23 | src: "init_repo.sh"
24 | dest: "/srv/restic/{{ backup_name }}/init_repo.sh"
25 | owner: root
26 | mode: '0700'
27 |
28 | - name: "Ensure backup repository for {{ backup_name }} exists"
29 | command: "/srv/restic/{{ backup_name }}/init_repo.sh"
30 | become: yes
31 | become_user: root
32 | run_once: true
33 |
34 | - name: "Ensure Backup Script for {{ backup_name }}"
35 | template:
36 | src: "{{ backup_script }}"
37 | dest: "/srv/restic/{{ backup_name }}/{{ backup_script }}"
38 | owner: root
39 | mode: '0700'
40 |
41 | - name: "Ensure Cron Job for backup {{ backup_name }} exists"
42 | cron:
43 | name: "Backup {{ backup_name }}"
44 | minute: "0"
45 | hour: "8-22"
46 | user: root
47 | job: "/srv/restic/{{ backup_name }}/{{ backup_script }}"
48 |
49 |
50 |
51 |
52 | #- name: "check if rest server connection password file exists"
53 | # stat:
54 | # path: "/srv/restic/rest_conn_password.txt"
55 | # register: rest_conn_pw_file
56 | #
57 | #- name: "if password file exists read pw file"
58 | # command: "cat /srv/restic/rest_conn_password.txt"
59 | # register: rest_conn_pw
60 | # when: rest_conn_pw_file.stat.exists
61 | #
62 | #- name: "generate pw if not exist"
63 | # command: "openssl rand -base64 64"
64 | # register: rest_conn_pw
65 | # when: rest_conn_pw_file.stat.exists == false
66 | #
67 | #- name: "Create PW file if not exist"
68 | # copy:
69 | # content: "{{ rest_conn_pw.stdout }}"
70 | # dest: "/srv/restic/rest_conn_password.txt"
71 | # when: rest_conn_pw_file.stat.exists == false
72 | #
73 | #- name: "debug"
74 | # debug:
75 | # var: rest_conn_pw
76 | #
77 | #- name: "Ensure Password for rest-server"
78 | # set_fact:
79 | # rest_conn_password: "{{ rest_conn_pw.stdout }}"
80 | # cacheable: yes
81 | #
82 |
--------------------------------------------------------------------------------
/roles/restic-client/templates/backup_directory.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | export RESTIC_REPOSITORY="rest:https://{{ backup_name }}:{{ backup_conn_pw }}@{{ backup_server }}:8000/{{ backup_name }}"
4 | export RESTIC_PASSWORD="{{ backup_pw }}"
5 |
6 | LOGFILE="/srv/restic/{{ backup_name }}/restic.log"
7 | BACKUPDIR="{{ backup_dir }}"
8 |
9 | {
10 | restic backup $BACKUPDIR
11 | } 2>&1 | tee -a $LOGFILE
12 |
13 |
--------------------------------------------------------------------------------
/roles/restic-client/templates/backup_postgres.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | export RESTIC_REPOSITORY="rest:https://{{ backup_name }}:{{ backup_conn_pw }}@{{ backup_server }}:8000/{{ backup_name }}"
4 | export RESTIC_PASSWORD="{{ backup_pw }}"
5 |
6 | LOGFILE="/srv/restic/{{ backup_name }}/restic.log"
7 |
8 | {
9 | cd /tmp/ # "errorhandling" for sudo postgres
10 | sudo -u postgres pg_dump -F p {{ backup_pg_db }} | restic backup --stdin #
11 | } 2>&1 | tee -a $LOGFILE
12 |
13 |
--------------------------------------------------------------------------------
/roles/restic-client/templates/init_repo.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | export RESTIC_REPOSITORY="rest:https://{{ backup_name }}:{{ backup_conn_pw }}@{{ backup_server }}:8000/{{ backup_name }}"
4 | export RESTIC_PASSWORD="{{ backup_pw }}"
5 |
6 | LOGFILE="/srv/restic/{{ backup_name }}/restic.log"
7 |
8 | {
9 | restic snapshots > /dev/null || restic init # initialize repository if not already exits
10 | } 2>&1 | tee -a $LOGFILE
11 |
12 |
--------------------------------------------------------------------------------
/roles/restic-server/files/rest-server-0.9.7-linux-amd64:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stadtulm/a13-ansible/3b0c44d9712af5c5d3bd745c0a987cc9acf92102/roles/restic-server/files/rest-server-0.9.7-linux-amd64
--------------------------------------------------------------------------------
/roles/restic-server/handlers/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: restart rest-server
3 | service:
4 | name: rest-server
5 | state: restarted
6 |
7 | - name: reload ufw
8 | service:
9 | name: ufw
10 | state: reloaded
11 |
--------------------------------------------------------------------------------
/roles/restic-server/tasks/letsencrypt.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Add certbot apt repo
3 | apt_repository:
4 | repo: ppa:certbot/certbot
5 | state: present
6 |
7 | - name: Install certbot
8 | apt:
9 | pkg:
10 | - certbot
11 | update_cache: true
12 | state: present
13 |
14 | - name: Ensure Let's Encrypt uses API v2
15 | lineinfile:
16 | path: /etc/letsencrypt/cli.ini
17 | regexp: '^server'
18 | line: 'server = https://acme-v02.api.letsencrypt.org/directory'
19 |
20 | - name: Register certbot certificate file
21 | stat:
22 | path: "/etc/letsencrypt/live/{{ rest_backup_server }}/fullchain.pem"
23 | register: certbot_certificate_file_path
24 |
25 | - name: Generate Certificates
26 | command: certbot certonly -d {{ rest_backup_server }} --agree-tos --email {{ letsencrypt_email }} --standalone
27 | when: not certbot_certificate_file_path.stat.exists
28 |
29 | - name: enable (limited) http in ufw for certbot renewal
30 | ufw:
31 | rule: limit
32 | port: http
33 |
34 | - name: ensure certbot renew in cron
35 | cron:
36 | name: "certbot renew weekly"
37 | special_time: weekly
38 | job: "certbot renew"
39 | user: root
--------------------------------------------------------------------------------
/roles/restic-server/tasks/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: "Ensure certs group exists"
3 | group:
4 | name: certs
5 | state: present
6 |
7 | - name: "Ensure restic-server user"
8 | user:
9 | name: rest-server
10 | shell: /bin/bash
11 | password: '!'
12 | update_password: on_create
13 | groups: certs
14 | state: present
15 |
16 | - name: "Ensure restic-server binary"
17 | copy:
18 | src: ../files/rest-server-0.9.7-linux-amd64
19 | dest: /usr/local/bin/rest-server
20 | owner: rest-server
21 | mode: '0700'
22 |
23 | - name: "Ensure restic-server backup root directory"
24 | file:
25 | path: "{{ rest_backup_storage_dir }}"
26 | state: directory
27 | owner: rest-server
28 | mode: '0700'
29 |
30 |
31 | - name: "ensure python lib for htpasswd actions"
32 | apt:
33 | name:
34 | - python3-passlib
35 | - python3-bcrypt
36 | state: present
37 |
38 | # for certbot standalone mode
39 | - name: "ensure no Webserver installed"
40 | apt:
41 | name:
42 | - nginx
43 | - apache2
44 | state: absent
45 |
46 | - import_tasks: letsencrypt.yml
47 |
48 | - name: "Ensure /etc/rest-server"
49 | file:
50 | path: "/etc/rest-server"
51 | state: directory
52 | owner: rest-server
53 | mode: '0700'
54 |
55 | - name: "Ensure read on certpaths for certificates"
56 | file:
57 | path: /etc/letsencrypt
58 | group: certs
59 | mode: g+rx
60 | recurse: yes
61 |
62 | - name: "Ensure restic-server systemd unit"
63 | template:
64 | src: rest-server.service
65 | dest: /etc/systemd/system/rest-server.service
66 |
67 | - name: "enable rest-server systemd"
68 | systemd:
69 | name: rest-server.service
70 | state: started
71 | enabled: yes
72 |
73 | - name: "Ensure Backup User can connect"
74 | htpasswd:
75 | path: "{{ rest_backup_storage_dir }}/.htpasswd"
76 | name: "{{ item.name }}"
77 | password: "{{ item.conn_pw }}"
78 | owner: rest-server
79 | mode: "0600"
80 | crypt_scheme: bcrypt
81 | with_items: "{{ rest_backup_tasks }}"
82 |
83 |
84 | - name: adapt rest-server ports in ufw
85 | copy:
86 | content: |
87 | [rest-server]
88 | title=Restic REST Server
89 | description=REST Server for restic backup
90 | ports=8000/tcp
91 | mode: 0644
92 | dest: /etc/ufw/applications.d/rest-server
93 |
94 | - name: allow rest-server in ufw
95 | ufw:
96 | rule: allow
97 | name: rest-server
98 | state: enabled
--------------------------------------------------------------------------------
/roles/restic-server/templates/rest-server.service:
--------------------------------------------------------------------------------
1 | [Unit]
2 | Description=Rest Server
3 | After=syslog.target
4 | After=network.target
5 |
6 | [Service]
7 | Type=simple
8 | User=rest-server
9 | Group=rest-server
10 | ExecStart=/usr/local/bin/rest-server --path {{ rest_backup_storage_dir }} --private-repos --tls --tls-cert /etc/letsencrypt/live/{{ rest_backup_server }}/fullchain.pem --tls-key /etc/letsencrypt/live/{{ rest_backup_server }}/privkey.pem
11 | Restart=always
12 | RestartSec=5
13 |
14 | [Install]
15 | WantedBy=multi-user.target
16 |
--------------------------------------------------------------------------------
/roles/scalelite-config/tasks/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | #- name: Build a list of bbb instances with their secrets
3 | # debug:
4 | # msg: "{{ hostvars[item]['bbb_url'] }} : {{ hostvars[item]['bbb_secret'] }}"
5 | # with_items: "{{ groups['bbb'] }}"
6 |
7 | - name: Panic and remove all BBB Servers from loadbalancer before adding
8 | shell:
9 | chdir: /var/www/scalelite
10 | executable: /bin/bash
11 | cmd: "set -o allexport; source .env; set +o allexport; bundle exec ./bin/rake servers | grep id | cut -d ' ' -f2 | xargs -I % bundle exec ./bin/rake servers:{{ item }}[%]"
12 | with_items:
13 | - panic
14 | - remove
15 | become: yes
16 | become_user: scalelite
17 |
18 | - name: Ensure all servers are registered at the loadbalancer
19 | shell:
20 | chdir: /var/www/scalelite
21 | executable: /bin/bash
22 | cmd: "set -o allexport; source .env; set +o allexport; bundle exec ./bin/rake servers:add[{{ (hostvars[item]['bbb_url'] + 'api') | quote }},{{ hostvars[item]['bbb_secret'] | quote }}]"
23 | with_items: "{{ groups['bbb'] }}"
24 | when: hostvars[item]['bbb_url'] is defined
25 | become: yes
26 | become_user: scalelite
27 |
28 | - name: Enable all servers at loadbalancer
29 | shell:
30 | chdir: /var/www/scalelite
31 | executable: /bin/bash
32 | cmd: "set -o allexport; source .env; set +o allexport; bundle exec ./bin/rake servers | grep id | cut -d ' ' -f2 | xargs -I % bundle exec ./bin/rake servers:enable[%]"
33 | become: yes
34 | become_user: scalelite
35 |
36 | - name: Ensure new servers are instantly polled
37 | shell:
38 | chdir: /var/www/scalelite
39 | executable: /bin/bash
40 | cmd: "set -o allexport; source .env; set +o allexport; bundle exec ./bin/rake poll:all"
41 | become: yes
42 | become_user: scalelite
43 |
44 | # FIXME: aquire scalelite secret
45 |
--------------------------------------------------------------------------------
/roles/scalelite/handlers/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: restart scalelite
3 | service:
4 | name: scalelite.target
5 | state: restarted
6 |
--------------------------------------------------------------------------------
/roles/scalelite/tasks/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Ensure group "scalelite" exists
3 | group:
4 | name: scalelite
5 | state: present
6 |
7 | - name: Ensure user scalelite exists
8 | user:
9 | name: scalelite
10 | shell: /bin/bash
11 | password: '!'
12 | update_password: on_create
13 | groups: scalelite
14 | state: present
15 |
16 | - name: Checkout scalelite repository
17 | git:
18 | repo: https://github.com/blindsidenetworks/scalelite.git
19 | dest: /var/www/scalelite
20 | version: b49c803e6db62cabed3fa49796f6e8917d222bfb
21 | notify: restart scalelite
22 |
23 |
24 | - name: Ensure scalelite is owner of /var/www/scalelite
25 | file:
26 | path: /var/www/scalelite
27 | recurse: yes
28 | owner: scalelite
29 | group: scalelite
30 |
31 | - name: Ensure Bundler 2 is installed
32 | gem:
33 | name: bundler
34 | state: latest
35 | user_install: no
36 |
37 | - name: Ensure sqlite is installed
38 | apt:
39 | name:
40 | - sqlite3
41 | - libsqlite3-dev
42 | state: present
43 |
44 | # bundler: deployment mode by cli argument is deprecated, so we're going to run this:
45 | - name: Ensure bundle in is deployment mode
46 | command:
47 | cmd: bundle config set deployment 'true'
48 | chdir: /var/www/scalelite
49 | creates: /var/www/scalelite/.bundle/config
50 | become: yes
51 | become_user: scalelite
52 |
53 | - name: Ensure gems for scalelite are installed
54 | bundler:
55 | state: present
56 | exclude_groups:
57 | - development
58 | - test
59 | chdir: /var/www/scalelite
60 | become: yes
61 | become_user: scalelite
62 |
63 | - name: Generate scalelite secret key base
64 | command: openssl rand -hex 64
65 | register: openssl_rand64
66 | check_mode: no
67 | when: not scalelite_secret_key_base is defined
68 |
69 | - name: Register secret key base
70 | set_fact:
71 | scalelite_secret_key_base: "{{ openssl_rand64.stdout }}"
72 | when: not scalelite_secret_key_base is defined
73 |
74 | - name: Generate scalelite loadbalancer secret
75 | command: openssl rand -hex 32
76 | register: openssl_rand32
77 | check_mode: no
78 | when: not scalelite_loadbalancer_secret is defined
79 |
80 | - name: Register loadbalancer secret
81 | set_fact:
82 | scalelite_loadbalancer_secret: "{{ openssl_rand32.stdout }}"
83 | when: not scalelite_loadbalancer_secret is defined
84 |
85 | - name: Ensure environment file for scalelite
86 | template:
87 | src: env.j2
88 | dest: /var/www/scalelite/.env
89 | become: yes
90 | become_user: scalelite
91 |
92 | - name: Ensure systemd unit for scalelite api
93 | template:
94 | src: scalelite-api.service
95 | dest: /etc/systemd/system/scalelite-api.service
96 |
97 | - name: Ensure systemd unit for scalelite poller
98 | template:
99 | src: scalelite-poller.service
100 | dest: /etc/systemd/system/scalelite-poller.service
101 |
102 | - name: Ensure systemd target for scalelite
103 | template:
104 | src: scalelite.target
105 | dest: /etc/systemd/system/scalelite.target
106 |
107 | - name: Enable scalelite units
108 | systemd:
109 | daemon_reload: yes
110 | name: "{{ item }}"
111 | enabled: yes
112 | with_items:
113 | - scalelite-api
114 | - scalelite-poller
115 |
116 | - name: Count scalelite database tables
117 | shell:
118 | cmd: "psql -h 127.0.0.1 -U scalelite scalelite -qAt -c 'select count(*) from pg_stat_user_tables;'"
119 | check_mode: no
120 | register: scalelite_database_tablecount
121 |
122 | - name: Ensure database schema is loaded
123 | shell:
124 | chdir: /var/www/scalelite
125 | executable: /bin/bash
126 | cmd: "set -o allexport; source .env; set +o allexport; bundle exec rails db:schema:load"
127 | become: yes
128 | become_user: scalelite
129 | when: scalelite_database_tablecount.stdout|int <= 1
130 |
131 | - name: Ensure scalelite-api is started
132 | systemd:
133 | name: scalelite-api
134 | state: started
135 |
136 | - name: Ensure scalelite-poller is started
137 | systemd:
138 | name: scalelite-poller
139 | state: started
140 |
141 | - name: Ensure scalelite nginx config exists
142 | template:
143 | src: nginx-scalelite.conf
144 | dest: /etc/nginx/a13.d/scalelite.conf
145 | notify: reload nginx
146 |
147 | - name: Restart Scalelite every night @4am
148 | cron:
149 | state: present
150 | name: "restart Scalelite"
151 | minute: "0"
152 | hour: "4"
153 | user: root
154 | job: "systemctl restart scalelite.target"
--------------------------------------------------------------------------------
/roles/scalelite/templates/env.j2:
--------------------------------------------------------------------------------
1 | RAILS_ENV=production
2 | RAILS_LOG_TO_STDOUT=1
3 | DISABLE_SPRING=1
4 | BIND=tcp://127.0.0.1:3000
5 | DATABASE_URL=postgresql://scalelite:trust@127.0.0.1:5432/scalelite
6 | REDIS_URL=redis://127.0.0.1:6379
7 | SECRET_KEY_BASE={{ scalelite_secret_key_base }}
8 | LOADBALANCER_SECRET={{ scalelite_loadbalancer_secret }}
9 | # BUILD_NUMBER=${CI_COMMIT_TAG:-${CI_COMMIT_REF_NAME}-${CI_COMMIT_SHORT_SHA}}
10 |
--------------------------------------------------------------------------------
/roles/scalelite/templates/nginx-scalelite.conf:
--------------------------------------------------------------------------------
1 | location / {
2 | proxy_pass http://127.0.0.1:3000;
3 |
4 | proxy_read_timeout 60s;
5 | proxy_redirect off;
6 |
7 | proxy_set_header Host $http_host;
8 |
9 | proxy_set_header X-Real-IP $remote_addr;
10 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
11 | proxy_set_header X-Forwarded-Proto $scheme;
12 | proxy_set_header X-Request-Id $request_id;
13 |
14 | proxy_http_version 1.1;
15 | }
16 |
--------------------------------------------------------------------------------
/roles/scalelite/templates/scalelite-api.service:
--------------------------------------------------------------------------------
1 | [Unit]
2 | Description=Scalelite API
3 | After=network-online.target
4 | Wants=network-online.target
5 | Before=scalelite.target
6 | PartOf=scalelite.target
7 |
8 | [Service]
9 | Type=simple
10 | EnvironmentFile=/var/www/scalelite/.env
11 | WorkingDirectory=/var/www/scalelite
12 | Restart=always
13 | RestartSec=60
14 | ExecStart=/usr/local/bin/bundle exec puma -C config/puma.rb
15 | User=scalelite
16 |
17 | [Install]
18 | WantedBy=scalelite.target
19 |
--------------------------------------------------------------------------------
/roles/scalelite/templates/scalelite-poller.service:
--------------------------------------------------------------------------------
1 | [Unit]
2 | Description=Scalelite Poller
3 | After=network-online.target
4 | Wants=network-online.target
5 | Before=scalelite.target
6 | PartOf=scalelite.target
7 |
8 | [Service]
9 | Type=simple
10 | EnvironmentFile=/var/www/scalelite/.env
11 | WorkingDirectory=/var/www/scalelite
12 | Restart=always
13 | RestartSec=60
14 | ExecStart=/usr/local/bin/bundle exec rake poll
15 | User=scalelite
16 |
17 | [Install]
18 | WantedBy=scalelite.target
19 |
--------------------------------------------------------------------------------
/roles/scalelite/templates/scalelite.target:
--------------------------------------------------------------------------------
1 | [Unit]
2 | Description=Scalelite
3 |
4 | [Install]
5 | WantedBy=multi-user.target
6 |
--------------------------------------------------------------------------------
/roles/turn-standalone/handlers/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: restart coturn
3 | service:
4 | name: coturn
5 | state: restarted
6 |
--------------------------------------------------------------------------------
/roles/turn-standalone/tasks/certbot.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Ensure 'ssl-cert' group exists
3 | group:
4 | name: ssl-cert
5 | state: present
6 |
7 | - name: Copy certbot-zerossl Wrapper for certbot
8 | template:
9 | src: certbot-zerossl.sh
10 | dest: /usr/local/bin/certbot-zerossl
11 | owner: root
12 | group: root
13 | mode: u=rwx,g=rx,o=rx
14 | when: certbot_source == "zerossl"
15 |
16 | - name: Install certbot
17 | apt:
18 | name: certbot
19 | update_cache: true
20 | state: present
21 |
22 | - name: Install required packages for zerossl
23 | apt:
24 | name: certbot
25 | update_cache: true
26 | state: present
27 | when: certbot_source == "zerossl"
28 |
29 | - name: Ensure ZeroSSL/LE Directories are owned by ssl-cert group
30 | file:
31 | path: "{{ item }}"
32 | owner: root
33 | group: ssl-cert
34 | mode: g+rx
35 | state: directory
36 | with_items:
37 | - /etc/letsencrypt/live
38 | - /etc/letsencrypt/archive
39 | - /etc/letsencrypt/keys
40 |
41 | - name: Register certbot certificate file
42 | stat:
43 | path: "/etc/letsencrypt/live/{{ turn_hostname }}/fullchain.pem"
44 | register: certbot_certificate_file_path
45 |
46 | - name: Generate ZeroSSL Certificates
47 | command: certbot-zerossl certonly -d {{ turn_hostname }} -d {{ ansible_fqdn }} --agree-tos --email {{ letsencrypt_email }} --standalone
48 | when: not certbot_certificate_file_path.stat.exists and certbot_source == "zerossl"
49 |
50 | - name: Generate Lets Encrypt Certificates
51 | command: certbot certonly -d {{ turn_hostname }} -d {{ ansible_fqdn }} --agree-tos --email {{ letsencrypt_email }} --standalone
52 | when: not certbot_certificate_file_path.stat.exists and certbot_source == "letsencrypt"
53 |
54 | - name: Ensure ZeroSSL/LE Certificate and Keys are owned by ssl-cert group and readable
55 | file:
56 | path: "{{ item }}"
57 | mode: 'u=rw,g=r'
58 | group: ssl-cert
59 | with_items:
60 | - /etc/letsencrypt/live/{{ turn_hostname }}/fullchain.pem
61 | - /etc/letsencrypt/live/{{ turn_hostname }}/privkey.pem
62 |
--------------------------------------------------------------------------------
/roles/turn-standalone/tasks/coturn.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Install coturn
3 | apt:
4 | name: coturn
5 | update_cache: true
6 | state: present
7 |
8 | - name: Copy systemd coturn unit
9 | template:
10 | src: "coturn.service"
11 | dest: /etc/systemd/system/coturn.service
12 |
13 | - name: Ensure coturn is in ssl-cert group
14 | user:
15 | name: turnserver
16 | groups: ssl-cert
17 | append: yes
18 | state: present
19 |
20 | - name: Enable coturn
21 | systemd:
22 | daemon_reload: yes
23 | name: coturn
24 | enabled: yes
25 |
26 | - name: Copy coturn config-file
27 | template:
28 | src: "turnserver.conf.j2"
29 | dest: /etc/turnserver.conf
30 | notify: restart coturn
31 |
32 | - name: Enable coturn server in defaults
33 | lineinfile:
34 | path: /etc/default/coturn
35 | regexp: 'TURNSERVER_ENABLED'
36 | line: 'TURNSERVER_ENABLED=1'
37 | notify: restart coturn
38 |
39 | - name: Disable IPv6 if coturn is set to IPv4 only
40 | sysctl:
41 | name: net.ipv6.conf.all.disable_ipv6
42 | value: '1'
43 | sysctl_set: yes
44 | state: present
45 | reload: yes
46 | when: turn_ipv4_only
47 |
48 | - name: Ensure Coturn Logfile
49 | file:
50 | path: /var/log/coturn.log
51 | owner: turnserver
52 | mode: '0600'
53 | state: touch
54 |
55 | - name: Ensure Log rotation
56 | template:
57 | src: coturn.logrotate
58 | dest: /etc/logrotate.d/coturn
59 |
60 | - name: Ensure coturn is started
61 | systemd:
62 | name: coturn
63 | state: started
64 |
65 | - name: Ensure coturn is restarted everyday @ 4 am
66 | cron:
67 | name: "restart coturn"
68 | minute: "0"
69 | hour: "4"
70 | user: root
71 | job: "systemctl restart coturn.service"
--------------------------------------------------------------------------------
/roles/turn-standalone/tasks/firewall.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: adapt letsencrypt/zerossl standalone ports in ufw
3 | copy:
4 | content: |
5 | [letsencrypt]
6 | title=letsencrypt standalone on 80,443
7 | description=Standalone certbot certificate aquiring
8 | ports=80,443/tcp
9 | mode: 0644
10 | dest: /etc/ufw/applications.d/letsencrypt
11 |
12 | - name: allow letsencrypt/zerossl in ufw
13 | ufw:
14 | rule: allow
15 | name: letsencrypt
16 | state: enabled
17 |
18 | - name: adapt coturn ports in ufw
19 | copy:
20 | content: |
21 | [turnserver]
22 | title=Coturn turnserver
23 | description=Free open source implementation of TURN and STUN Server
24 | ports=443,444,3478,3479,32768:65535/tcp|443,444,3478,3479,32768:65535/udp
25 | mode: 0644
26 | dest: /etc/ufw/applications.d/turnserver
27 |
28 | - name: allow turnserver in ufw
29 | ufw:
30 | rule: allow
31 | name: turnserver
32 | state: enabled
33 |
--------------------------------------------------------------------------------
/roles/turn-standalone/tasks/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - import_tasks: firewall.yml
3 |
4 | - import_tasks: certbot.yml
5 |
6 | - import_tasks: coturn.yml
7 |
--------------------------------------------------------------------------------
/roles/turn-standalone/templates/certbot-zerossl.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | CERTBOT_ARGS=()
4 |
5 | function parse_eab_credentials()
6 | {
7 | PYTHONIOENCODING=utf8
8 | ZEROSSL_EAB_KID=$(echo $1 | python3 -c "import sys, json; print(json.load(sys.stdin)['eab_kid'])")
9 | ZEROSSL_EAB_HMAC_KEY=$(echo $1 | python3 -c "import sys, json; print(json.load(sys.stdin)['eab_hmac_key'])")
10 | CERTBOT_ARGS+=(--eab-kid "$ZEROSSL_EAB_KID" --eab-hmac-key "$ZEROSSL_EAB_HMAC_KEY" --server "https://acme.zerossl.com/v2/DV90")
11 | }
12 |
13 | while [[ "$#" -gt 0 ]]; do
14 | case $1 in
15 | --zerossl-api-key=*)
16 | ZEROSSL_API_KEY="${1:18}"
17 | ;;
18 | --zerossl-api-key|-z)
19 | ZEROSSL_API_KEY="${2}"
20 | shift
21 | ;;
22 | --zerossl-email=*)
23 | ZEROSSL_EMAIL="${1:16}"
24 | ;;
25 | --email|--zerossl-email|-m)
26 | ZEROSSL_EMAIL="${2}"
27 | CERTBOT_ARGS+=(-m "${2}")
28 | shift
29 | ;;
30 | *) CERTBOT_ARGS+=($1) ;;
31 | esac
32 | shift
33 | done
34 |
35 | if [[ -n $ZEROSSL_API_KEY ]]; then
36 | parse_eab_credentials $(curl -s -X POST "https://api.zerossl.com/acme/eab-credentials?access_key=$ZEROSSL_API_KEY")
37 | elif [[ -n $ZEROSSL_EMAIL ]]; then
38 | parse_eab_credentials $(curl -s https://api.zerossl.com/acme/eab-credentials-email --data "email=$ZEROSSL_EMAIL")
39 | fi
40 |
41 | certbot ${CERTBOT_ARGS[@]}
42 |
--------------------------------------------------------------------------------
/roles/turn-standalone/templates/coturn.logrotate:
--------------------------------------------------------------------------------
1 | /var/log/coturn.log
2 | {
3 | rotate 30
4 | daily
5 | missingok
6 | notifempty
7 | delaycompress
8 | compress
9 | postrotate
10 | systemctl kill -sHUP coturn.service
11 | endscript
12 | }
--------------------------------------------------------------------------------
/roles/turn-standalone/templates/coturn.service:
--------------------------------------------------------------------------------
1 | [Unit]
2 | Description=Coturn STUN/TURN Server
3 | Documentation=man:coturn(1) man:turnadmin(1) man:turnserver(1)
4 | After=network.target
5 | After=network-online.target
6 | After=remote-fs.target
7 | Wants=network-online.target
8 |
9 | [Service]
10 | User=turnserver
11 | Group=turnserver
12 | Type=forking
13 | RuntimeDirectory=turnserver
14 | PIDFile=/run/turnserver/turnserver.pid
15 | ExecStart=/usr/bin/turnserver --daemon -c /etc/turnserver.conf --pidfile /run/turnserver/turnserver.pid
16 | #FixMe: turnserver exit faster than it is finshing the setup and ready for handling the connection.
17 | ExecStartPost=/bin/sleep 2
18 | Restart=on-failure
19 | InaccessibleDirectories=/home
20 | PrivateTmp=yes
21 | AmbientCapabilities=CAP_NET_BIND_SERVICE
22 | LimitNOFILE=1048576
23 |
24 | [Install]
25 | WantedBy=multi-user.target
26 | Alias=turnserver.service
27 |
--------------------------------------------------------------------------------
/roles/turn-standalone/templates/turnserver.conf.j2:
--------------------------------------------------------------------------------
1 | listening-port=3478
2 | tls-listening-port=443
3 |
4 | listening-ip={{ ansible_default_ipv4.address }}
5 | relay-ip={{ ansible_default_ipv4.address }}
6 |
7 | {% if turn_ipv4_only is sameas false %}
8 | relay-ip={{ ansible_default_ipv6.address }}
9 | listening-ip={{ ansible_default_ipv6.address }}
10 | {% endif %}
11 |
12 | min-port=32768
13 | max-port=65535
14 |
15 | fingerprint
16 | lt-cred-mech
17 | use-auth-secret
18 | static-auth-secret={{ turn_secret }}
19 | realm={{ turn_hostname }}
20 |
21 | cert=/etc/letsencrypt/live/{{ turn_hostname }}/fullchain.pem
22 | pkey=/etc/letsencrypt/live/{{ turn_hostname }}/privkey.pem
23 |
24 | # Limit the allowed ciphers to improve security
25 | # Based on https://hynek.me/articles/hardening-your-web-servers-ssl-ciphers/
26 | cipher-list="ECDH+AESGCM:ECDH+CHACHA20:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:RSA+AESGCM:RSA+AES:!aNULL:!MD5:!DSS"
27 |
28 | # Enable longer DH TLS key to improve security
29 | dh2066
30 |
31 | keep-address-family
32 | no-cli
33 | no-tlsv1
34 | no-tlsv1_1
35 |
36 | # Log to a single filename (rather than new log files each startup). You'll
37 | # want to install a logrotate configuration (see below)
38 | log-file=/var/log/coturn.log
39 |
40 | # To enable single filename logs you need to enable the simple-log flag
41 | simple-log
42 |
43 | # Block connections to IP ranges which shouldn't be reachable
44 | no-loopback-peers
45 | no-multicast-peers
46 | # CVE-2020-26262
47 | # If running coturn version older than 4.5.2, uncomment these rules and ensure
48 | # that you have listening-ip set to ipv4 addresses only.
49 | #denied-peer-ip=0.0.0.0-0.255.255.255
50 | #denied-peer-ip=127.0.0.0-127.255.255.255
51 | #denied-peer-ip=::1
52 | # Private (LAN) addresses
53 | # If you are running BigBlueButton within a LAN, you might need to add an "allow" rule for your address range.
54 | # IPv4 Private-Use
55 | denied-peer-ip=10.0.0.0-10.255.255.255
56 | denied-peer-ip=172.16.0.0-172.31.255.255
57 | denied-peer-ip=192.168.0.0-192.168.255.255
58 | # Other IPv4 Special-Purpose addresses
59 | denied-peer-ip=100.64.0.0-100.127.255.255
60 | denied-peer-ip=169.254.0.0-169.254.255.255
61 | denied-peer-ip=192.0.0.0-192.0.0.255
62 | denied-peer-ip=192.0.2.0-192.0.2.255
63 | denied-peer-ip=198.18.0.0-198.19.255.255
64 | denied-peer-ip=198.51.100.0-198.51.100.255
65 | denied-peer-ip=203.0.113.0-203.0.113.255
66 | # IPv6 Unique-Local
67 | denied-peer-ip=fc00::-fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff
68 | # IPv6 Link-Local Unicast
69 | denied-peer-ip=fe80::-febf:ffff:ffff:ffff:ffff:ffff:ffff:ffff
70 | # Other IPv6 Special-Purpose assignments
71 | denied-peer-ip=::ffff:0:0-::ffff:ffff:ffff
72 | denied-peer-ip=64:ff9b::-64:ff9b::ffff:ffff
73 | denied-peer-ip=64:ff9b:1::-64:ff9b:1:ffff:ffff:ffff:ffff:ffff
74 | denied-peer-ip=2001::-2001:1ff:ffff:ffff:ffff:ffff:ffff:ffff
75 | denied-peer-ip=2001:db8::-2001:db8:ffff:ffff:ffff:ffff:ffff:ffff
76 | denied-peer-ip=2002::-2002:ffff:ffff:ffff:ffff:ffff:ffff:ffff
77 |
--------------------------------------------------------------------------------
/tmp/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stadtulm/a13-ansible/3b0c44d9712af5c5d3bd745c0a987cc9acf92102/tmp/.gitkeep
--------------------------------------------------------------------------------
/vars.yml:
--------------------------------------------------------------------------------
1 | ---
2 | users:
3 | - { name: 'robbi5', sshkeys: 'https://github.com/robbi5.keys' }
4 | - { name: 'stk', sshkeys: 'https://github.com/stkdiretto.keys' }
5 | - { name: 'ubahnverleih', sshkeys: 'https://github.com/ubahnverleih.keys' }
6 | - { name: 'srf', sshkeys: 'https://github.com/sebastianfischer.keys' }
7 |
8 | letsencrypt_email: admin@ulmlernt.de
9 |
10 | mail_admin:
11 | { server: 'smtp.ionos.de', port: '587', domain: 'ulmlernt.de',
12 | user: 'admin@ulmlernt.de', auth: 'plain', starttls: 'true', sender: 'admin@ulmlernt.de' }
13 |
14 | mail_admin_password: !vault |
15 | $ANSIBLE_VAULT;1.1;AES256
16 | 33613237653334643333306539356430623662306537656235386539343261323435396637346233
17 | 6662366662653264653339333135383934616637316661640a363934663864663066633134363935
18 | 64376439373363363834643532313062633134393933636239346430353561623931396365306465
19 | 3365616636656461610a373262633630393030366630626239656266313066343034376565363738
20 | 34313163633866343739653832373534663436653237373130333364626263303730343965356635
21 | 6630323530356661323535613365326662366466333764633164
22 |
23 | greenlight_key_base: !vault |
24 | $ANSIBLE_VAULT;1.1;AES256
25 | 39303265396332343262343933373433393763363836363331366139646264336634363562386661
26 | 3735333036336539316338373365643961393961356631330a373065343434373933353538393236
27 | 38623064343866376134373132313262373039393464653239623532653233663139323331643065
28 | 3630663839333830650a393166386333666238633265313462653636303638383761643736663232
29 | 66386237386339646466666238333836386237396136373432646632646330303337336562633630
30 | 38653234356639666564316432303537643162303534623230653065333934663533393038653162
31 | 39336461383630383633343535343334353334383938663134393466663732356332653939316431
32 | 36666461323636366633306239623933626635323836613536333532366364323532333130323762
33 | 62363161613937356230633666383138663037326435663139353261373263613738336131333765
34 | 64326531653833386538316631363534353439386664366663633436393263366162613964366533
35 | 353665356234396461663137333162663134
36 |
37 | greenlight_postgres_password: !vault |
38 | $ANSIBLE_VAULT;1.1;AES256
39 | 39353834613161356466346161613062303332616632343535333435303831373465376338326138
40 | 3766353639306133376138663363306233373131663162320a316532643565323664396264666335
41 | 39623161336138303938313436336331333535613539656236326335353538383065373630316536
42 | 3463343736643265610a396164626464626365333366663531343761386538356232323461633133
43 | 37666639306436626436386136646436343533363436373536623865326264306237
44 |
45 | scalelite_loadbalancer_url: "https://lb.ulmlernt.org/bigbluebutton/"
46 | scalelite_loadbalancer_secret: !vault |
47 | $ANSIBLE_VAULT;1.1;AES256
48 | 33316130636133336266613962643637613662616530646564356235333533306331323635393830
49 | 3538653839326331373066653337353339636164333531380a653563306430373637646532656166
50 | 62326638363562346565626138326437626339636464613834396137386266326663623261366665
51 | 3636353638623166300a616431383664343538636537666534323564373763633336386562383534
52 | 36373534373530313861373134393634333437346530366232623035386162383463343064623862
53 | 33303635653534643737656166363038616564656530366437393436343537343933636261343936
54 | 61373039663131663132366133643263666436346362323530386234663037373235366333643139
55 | 64396661373561376164
56 |
57 | monitoring_grafana_admin_password: !vault |
58 | $ANSIBLE_VAULT;1.1;AES256
59 | 30366237316237353732313038643866373533313837336136333464636233663738663433643661
60 | 3039386337643663316330393464656261316332343263300a343861383534356236373166656235
61 | 35353337303435383261323238663665373131663433326530633933346130656465376137376234
62 | 6535613334373838380a346165653830373066316232343139323231613537303939653631393735
63 | 62333430383961333063316663613436353137616539383135643537333761356635
64 |
65 | monitoring_github_client_id: !vault |
66 | $ANSIBLE_VAULT;1.1;AES256
67 | 61613032633166643564343530616562306635366537333761393638626237613137363532383361
68 | 3362333364313562613636656261666437633065653338640a393237333437376362643431366562
69 | 39393362636130313161653462383230633230386636323963306135383232616261653061356236
70 | 6537616263636664340a316666363363613066386337333731363338653936323435323332313665
71 | 64613235303533303061386135343831323333663561316466633166616538636131
72 |
73 | monitoring_github_client_secret: !vault |
74 | $ANSIBLE_VAULT;1.1;AES256
75 | 64313333653634353637373539353362343331626330353833316262343234336663343064653537
76 | 3933343131303436653336653936623863613538643634640a623539313239323535323534366335
77 | 36633539323734353131616637346664363162383131633764653338663534663262303636653062
78 | 3939623836666463370a636362623464356137353231616436303433383535623135636331303465
79 | 39653739326537353536353433646638376139333738333432646561373836373230313833643665
80 | 6633323363353132363532656330373130383435323137663934
81 |
82 | turn_secret: !vault |
83 | $ANSIBLE_VAULT;1.1;AES256
84 | 65636566386434313831343137383835636636363933376239373264643734383233343262323036
85 | 3131366433346139626630386233623037323235646233630a633131356536313739626335343837
86 | 32306238393432613366633132646365326333383864343563633137343234663336636637636663
87 | 3732663330356365640a656635393466303363303362353033353239326438623232656432333933
88 | 34643265656665643465636665656337616264636465613933653335353664343139393363326532
89 | 3061333734626431326334663962356230376163636334336535
90 |
91 | restic_backup_server: "bck.ulmlernt.org"
92 |
93 | restic_backup_dokuwiki_conn_pw: !vault |
94 | $ANSIBLE_VAULT;1.1;AES256
95 | 64346536323264313162633037623163306330393533623563373437303132666232303765376363
96 | 3130363734313166633738663332396332353236616533620a393937366466366235353364353963
97 | 61363431383134383661653961333336373365663365373037376266383537323461643034666334
98 | 6330386430656235370a373239343964616638386261623039643665643731653665643938306566
99 | 30366266643135366462396334623631626665336330313536373331643062613532353339316437
100 | 64323035643639396139353361346632306365636339623365313936316231303663643666376436
101 | 36623638393538663263616235643863383737643438356237623339343535363464353864386265
102 | 62356637636430363761386661313162393136353564643737343738356332343264346231323730
103 | 3635
104 | restic_backup_dokuwiki_backup_pw: !vault |
105 | $ANSIBLE_VAULT;1.1;AES256
106 | 65316563343230616634636237313534306339663336663030343965393666356639643166366136
107 | 3630633261343565336162613534653034666232316562320a653863623430616434646638373639
108 | 30663533646638316131333965643137636532303337386631343062303737623537303664386635
109 | 6437366138386331350a656666633437303363643163656330653766636439366338353365656136
110 | 62653730646431313336333463376466313434633738353339306334323233303561653130363838
111 | 38333435363763393631376535316266373839346331653931333934663961326664303138323932
112 | 30323263666565666537393264396137623334303464336166356162363936323535346566353431
113 | 35393962366234616534346136613738303735326533386637663763313135636330343935323866
114 | 3239
115 |
116 | restic_backup_greenlight_conn_pw: !vault |
117 | $ANSIBLE_VAULT;1.1;AES256
118 | 62383866393731663735303166363936303635393761373534643137336462383636646235303461
119 | 3865353238373832653665623763656565633161636535340a363138636333393037353532626538
120 | 37373538323936316664383433323864383366623139373665316432656133323032623939643866
121 | 6364656437613539320a313638653062626432386239616136633435376239643331306563383464
122 | 36313233663137363735393735383931646634333436366535353531633133333137393135383232
123 | 32396461333331623161393665653438316233303261616336333533313265623861303636663863
124 | 38303535313164316537626664623838326537613934623435373731303362363736636638316263
125 | 35646561666534643134
126 |
127 | restic_backup_greenlight_backup_pw: !vault |
128 | $ANSIBLE_VAULT;1.1;AES256
129 | 39633430306338633963343665343036366438386266336533306265373335396538333134343962
130 | 3463323232656136616339346334646566663037393439360a306433623563663534326537333931
131 | 61383866373531613765623232653133653664666663346262666337626365316263363638393836
132 | 3666316537366565610a363034383738323337386336346436336337663539653463353038636561
133 | 38633565363865656538323365363665366233613338636631666663623033306566623863616636
134 | 34373965303432326432303539353931333062313330613534643837666339613963623537663433
135 | 37613264383066393437356138313837366531666638306366633863343561333736393536386262
136 | 65633439316537373734
137 |
138 | restic_backup_zammad_conn_pw: !vault |
139 | $ANSIBLE_VAULT;1.1;AES256
140 | 62373462366366333136363735643339356434666236343136313132396339623939343564353738
141 | 3662646462313839333836313833636438353465323662300a623638383730633535626430336537
142 | 65383035313835303034376435333562633061663361303033373765356362313164616538336537
143 | 6161323032646330340a383031643664613564323366393735326665303232326262623237376636
144 | 39376639366430616662353336396131663237633330323965353135383834333938616230333137
145 | 36326262363366313838323234393765613965383361353038316534343430396462346234623734
146 | 33623164646434396634646362306138303866343164366632383434613833336433613830323863
147 | 35366633326637626436633732393334666334326663366336323431666234666336343766373836
148 | 64393864313934373835616634353935353630386361663932633331373638303563393666643832
149 | 32316237646265393131653732613831373237353064393162306239633264663064396231653064
150 | 663763613932393230386262326465616538
151 |
152 | restic_backup_zammad_backup_pw: !vault |
153 | $ANSIBLE_VAULT;1.1;AES256
154 | 33353266343131366663656466663030303634356666333239646364656130646331613432316539
155 | 3334663335366535393431326435373262393534346364630a303437363838333637303434616234
156 | 65383331353238653439633234393333643062366334663562303533323965313766623936383733
157 | 3131306230386566380a373363353766376232356336623864346166373035643335363563653661
158 | 62343039633337346161626466336665363737386230353965656361613731313437396566613066
159 | 35653964306235653162323366366330323661363761663238386636363162373865343563633030
160 | 33313862316463363233386162613236353063346138386534666535623932363030366661343331
161 | 65366532346530643338666539366435363064313932353865353064366336613733313964653634
162 | 63613638373137626363393166633935363637303562363131656563333564373161356235666433
163 | 37656130623861353564363561376562613362386366313430616132336166613831393739663834
164 | 346163353230343661646337313763333636
--------------------------------------------------------------------------------