├── .gitignore
├── .restyled.yaml
├── .semaphore
└── semaphore.yml
├── LICENSE
├── README.md
├── data
├── cleanup.sh
├── compose.yaml
├── do_img_checks.sh
├── do_img_checks.sh-LICENSE.md
├── docker-compose-up.service
└── generate_key.sh
├── packer.json
├── redash_make_default.sh
└── setup.sh
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea
2 |
--------------------------------------------------------------------------------
/.restyled.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | enabled: false
3 |
--------------------------------------------------------------------------------
/.semaphore/semaphore.yml:
--------------------------------------------------------------------------------
1 | version: v1.0
2 | name: Build Cloud Images
3 | agent:
4 | machine:
5 | type: e1-standard-2
6 | os_image: ubuntu1804
7 |
8 | blocks:
9 | - name: "Build Cloud Images"
10 | task:
11 | env_vars:
12 | - name: AWS_DEFAULT_REGION
13 | value: "us-east-1"
14 | prologue:
15 | commands:
16 | - checkout
17 | - echo $APP_ENV
18 | - wget -O packer.zip https://releases.hashicorp.com/packer/1.3.1/packer_1.3.1_linux_amd64.zip
19 | - unzip packer.zip
20 | jobs:
21 | - name: Build AWS Image
22 | commands:
23 | - ./packer build -var "redash_version=5.0.2-$SEMAPHORE_JOB_ID" -var "image_version=5_0_2-$SEMAPHORE_JOB_ID" --only=redash-us-east-1 packer.json
24 | - name: Build GCE Image
25 | commands:
26 | - ./packer build -var "redash_version=5.0.2-$SEMAPHORE_JOB_ID" -var "image_version=5_0_2-$SEMAPHORE_JOB_ID" --only=googlecompute packer.json
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | BSD 2-Clause License
2 |
3 | Copyright (c) 2018, Redash
4 | All rights reserved.
5 |
6 | Redistribution and use in source and binary forms, with or without
7 | modification, are permitted provided that the following conditions are met:
8 |
9 | * Redistributions of source code must retain the above copyright notice, this
10 | list of conditions and the following disclaimer.
11 |
12 | * Redistributions in binary form must reproduce the above copyright notice,
13 | this list of conditions and the following disclaimer in the documentation
14 | and/or other materials provided with the distribution.
15 |
16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
20 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Setup script for Redash with Docker on Linux
2 |
3 | This is a reference setup for Redash on a single Linux server.
4 |
5 | It uses Docker and Docker Compose for deployment and management.
6 |
7 | This is the same setup we use for our official images (for AWS & Google Cloud) and can be used as reference if you want
8 | to manually setup Redash in a different environment (different OS or different deployment location).
9 |
10 | - `setup.sh` is the script that installs everything and creates the directories.
11 | - `compose.yaml` is the Docker Compose setup we use.
12 | - `packer.json` is Packer configuration we use to create the Cloud images.
13 |
14 | ## Tested
15 |
16 | - Alma Linux 8.x & 9.x
17 | - CentOS Stream 9.x
18 | - Debian 12.x
19 | - Fedora 38, 39 & 40
20 | - Oracle Linux 9.x
21 | - Red Hat Enterprise Linux 8.x & 9.x
22 | - Rocky Linux 8.x & 9.x
23 | - Ubuntu LTS 20.04 & 22.04
24 |
25 | ## How to use this
26 |
27 | This script should be run as the `root` user on a supported Linux system (as per above list):
28 |
29 | ```
30 | # ./setup.sh
31 | ```
32 |
33 | When run, the script will install the needed packages (mostly Docker) then install Redash, ready for you to configure
34 | and begin using.
35 |
36 | > [!TIP]
37 | > If you are not on a supported Linux system, you can manually install 'docker' and 'docker compose',
38 | > then run the script to start the Redash installation process.
39 |
40 | > [!IMPORTANT]
41 | > The very first time you load your Redash web interface it can take a while to appear, as the background Python code
42 | > is being compiled. On subsequent visits, the pages should load much quicker (near instantly).
43 |
44 | ## Optional parameters
45 |
46 | The setup script has the following optional parameters: `--dont-start`, `--preview`, `--version`, and `--overwrite`.
47 |
48 | These can be used independently of each other, or in combinations (with the exception that `--preview` and `--version` cannot be used together).
49 |
50 | ### --preview
51 |
52 | When the `--preview` parameter is given, the setup script will install the latest `preview`
53 | [image from Docker Hub](https://hub.docker.com/r/redash/redash/tags) instead of using the latest preview release.
54 |
55 | ```
56 | # ./setup.sh --preview
57 | ```
58 |
59 | ### --version
60 |
61 | When the `--version` parameter is given, the setup script will install the specified version of Redash instead of the latest stable release.
62 |
63 | ```
64 | # ./setup.sh --version 25.1.0
65 | ```
66 |
67 | This option allows you to install a specific version of Redash, which can be useful for testing, compatibility checks, or ensuring reproducible environments.
68 |
69 | > [!NOTE]
70 | > The `--version` and `--preview` options cannot be used together.
71 |
72 | ### Default Behavior
73 |
74 | When neither `--preview` nor `--version` is specified, the script will automatically detect and install the latest stable release of Redash using the GitHub API.
75 |
76 | ### --overwrite
77 |
78 | > [!CAUTION]
79 | > ***DO NOT*** use this parameter if you want to keep your existing Redash installation! It ***WILL*** be overwritten.
80 |
81 | When the `--overwrite` option is given, the setup script will delete the existing Redash environment file
82 | (`/opt/redash/env`) and Redash database, then set up a brand new (empty) Redash installation.
83 |
84 | ```
85 | # ./setup.sh --overwrite
86 | ```
87 |
88 | ### --dont-start
89 |
90 | When this option is given, the setup script will install Redash without starting it afterwards.
91 |
92 | This is useful for people wanting to customise or modify their Redash installation before it starts for the first time.
93 |
94 | ```
95 | # ./setup.sh --dont-start
96 | ```
97 |
98 | ## FAQ
99 |
100 | ### Can I use this in production?
101 |
102 | For small scale deployments -- yes. But for larger deployments we recommend at least splitting the database (and
103 | probably Redis) into its own server (preferably a managed service like RDS) and setting up at least 2 servers for
104 | Redash for redundancy. You will also need to tweak the number of workers based on your usage patterns.
105 |
106 | ### How do I upgrade to newer versions of Redash?
107 |
108 | See [Upgrade Guide](https://redash.io/help/open-source/admin-guide/how-to-upgrade).
109 |
110 | ### How do I use `setup.sh` on a different operating system?
111 |
112 | You will need to create a docker installation function that suits your operating system, and maybe other functions as
113 | well.
114 |
115 | The `install_docker_*()` functions in setup.sh shouldn't be too hard to adapt to other Linux distributions.
116 |
117 | ### How do I remove Redash if I no longer need it?
118 |
119 | 1. Stop the Redash containers and remove the images using `docker compose -f /opt/redash/compose.yaml down --volumes --rmi all`.
120 | 2. Remove the following lines from `~/.profile` and `~/.bashrc` if they're present.
121 |
122 | ```
123 | export COMPOSE_PROJECT_NAME=redash
124 | export COMPOSE_FILE=/opt/redash/compose.yaml
125 | ```
126 |
127 | 3. Delete the Redash folder using `sudo rm -fr /opt/redash`
128 |
--------------------------------------------------------------------------------
/data/cleanup.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | rm -rf /tmp/* /var/tmp/*
4 | history -c
5 | cat /dev/null >/root/.bash_history
6 | unset HISTFILE
7 | apt-get -y autoremove
8 | apt-get -y autoclean
9 | find /var/log -mtime -1 -type f -exec truncate -s 0 {} \;
10 | rm -rf /var/log/*.gz /var/log/*.[0-9] /var/log/*-????????
11 | rm -rf /var/lib/cloud/instances/*
12 | rm -rf /var/lib/cloud/instance
13 |
14 | echo "Removing keys..."
15 | rm -f /root/.ssh/authorized_keys /etc/ssh/*key*
16 | # dd if=/dev/zero of=/zerofile; sync; rm /zerofile; sync
17 | cat /dev/null >/var/log/lastlog
18 | cat /dev/null >/var/log/wtmp
19 |
--------------------------------------------------------------------------------
/data/compose.yaml:
--------------------------------------------------------------------------------
1 | x-redash-service: &redash-service
2 | image: redash/redash:__TAG__
3 | depends_on:
4 | - postgres
5 | - redis
6 | env_file: /opt/redash/env
7 | restart: always
8 | services:
9 | server:
10 | <<: *redash-service
11 | command: server
12 | ports:
13 | - "5000:5000"
14 | environment:
15 | REDASH_WEB_WORKERS: 4
16 | scheduler:
17 | <<: *redash-service
18 | command: scheduler
19 | depends_on:
20 | - server
21 | scheduled_worker:
22 | <<: *redash-service
23 | command: worker
24 | depends_on:
25 | - server
26 | environment:
27 | QUEUES: "scheduled_queries,schemas"
28 | WORKERS_COUNT: 1
29 | adhoc_worker:
30 | <<: *redash-service
31 | command: worker
32 | depends_on:
33 | - server
34 | environment:
35 | QUEUES: "queries"
36 | WORKERS_COUNT: 2
37 | redis:
38 | image: redis:7-alpine
39 | restart: unless-stopped
40 | postgres:
41 | image: pgautoupgrade/pgautoupgrade:latest
42 | env_file: /opt/redash/env
43 | volumes:
44 | - /opt/redash/postgres-data:/var/lib/postgresql/data
45 | restart: unless-stopped
46 | nginx:
47 | image: redash/nginx:latest
48 | ports:
49 | - "80:80"
50 | depends_on:
51 | - server
52 | links:
53 | - server:redash
54 | restart: always
55 | worker:
56 | <<: *redash-service
57 | command: worker
58 | environment:
59 | QUEUES: "periodic,emails,default"
60 | WORKERS_COUNT: 1
61 |
--------------------------------------------------------------------------------
/data/do_img_checks.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # DigitalOcean Marketplace Image Validation Tool
4 | # © 2021-2022 DigitalOcean LLC.
5 | # This code is licensed under Apache 2.0 license (see LICENSE.md for details)
6 |
7 | VERSION="v. 1.8.1"
8 | RUNDATE=$(date)
9 |
10 | # Script should be run with SUDO
11 | if [ "$EUID" -ne 0 ]; then
12 | echo "[Error] - This script must be run with sudo or as the root user."
13 | exit 1
14 | fi
15 |
16 | STATUS=0
17 | PASS=0
18 | WARN=0
19 | FAIL=0
20 |
21 | # $1 == command to check for
22 | # returns: 0 == true, 1 == false
23 | cmdExists() {
24 | if command -v "$1" >/dev/null 2>&1; then
25 | return 0
26 | else
27 | return 1
28 | fi
29 | }
30 |
31 | function getDistro() {
32 | if [ -f /etc/os-release ]; then
33 | # freedesktop.org and systemd
34 | # shellcheck disable=SC1091
35 | . /etc/os-release
36 | OS=$NAME
37 | VER=$VERSION_ID
38 | elif type lsb_release >/dev/null 2>&1; then
39 | # linuxbase.org
40 | OS=$(lsb_release -si)
41 | VER=$(lsb_release -sr)
42 | elif [ -f /etc/lsb-release ]; then
43 | # For some versions of Debian/Ubuntu without lsb_release command
44 | # shellcheck disable=SC1091
45 | . /etc/lsb-release
46 | OS=$DISTRIB_ID
47 | VER=$DISTRIB_RELEASE
48 | elif [ -f /etc/debian_version ]; then
49 | # Older Debian/Ubuntu/etc.
50 | OS=Debian
51 | VER=$(cat /etc/debian_version)
52 | elif [ -f /etc/SuSe-release ]; then
53 | # Older SuSE/etc.
54 | :
55 | elif [ -f /etc/redhat-release ]; then
56 | # Older Red Hat, CentOS, etc.
57 | VER=$(cut -d" " -f3 ", also works for BSD, etc.
64 | OS=$(uname -s)
65 | VER=$(uname -r)
66 | fi
67 | }
68 | function loadPasswords() {
69 | SHADOW=$(cat /etc/shadow)
70 | }
71 |
72 | function checkAgent() {
73 | # Check for the presence of the DO directory in the filesystem
74 | if [ -d /opt/digitalocean ]; then
75 | echo -en "\e[41m[FAIL]\e[0m DigitalOcean directory detected.\n"
76 | ((FAIL++))
77 | STATUS=2
78 | if [[ $OS == "CentOS Linux" ]] || [[ $OS == "CentOS Stream" ]] || [[ $OS == "Rocky Linux" ]] || [[ $OS == "AlmaLinux" ]]; then
79 | echo "To uninstall the agent: 'sudo yum remove droplet-agent'"
80 | echo "To remove the DO directory: 'find /opt/digitalocean/ -type d -empty -delete'"
81 | elif [[ $OS == "Ubuntu" ]] || [[ $OS == "Debian" ]]; then
82 | echo "To uninstall the agent and remove the DO directory: 'sudo apt-get purge droplet-agent'"
83 | fi
84 | else
85 | echo -en "\e[32m[PASS]\e[0m DigitalOcean Monitoring agent was not found\n"
86 | ((PASS++))
87 | fi
88 | }
89 |
90 | function checkLogs() {
91 | cp_ignore="/var/log/cpanel-install.log"
92 | echo -en "\nChecking for log files in /var/log\n\n"
93 | # Check if there are log archives or log files that have not been recently cleared.
94 | for f in /var/log/*-????????; do
95 | [[ -e $f ]] || break
96 | if [ "$f" != "$cp_ignore" ]; then
97 | echo -en "\e[93m[WARN]\e[0m Log archive ${f} found; Contents:\n"
98 | cat "$f"
99 | ((WARN++))
100 | if [[ $STATUS != 2 ]]; then
101 | STATUS=1
102 | fi
103 | fi
104 | done
105 | for f in /var/log/*.[0-9]; do
106 | [[ -e $f ]] || break
107 | echo -en "\e[93m[WARN]\e[0m Log archive ${f} found; Contents:\n"
108 | cat "$f"
109 | ((WARN++))
110 | if [[ $STATUS != 2 ]]; then
111 | STATUS=1
112 | fi
113 | done
114 | for f in /var/log/*.log; do
115 | [[ -e $f ]] || break
116 | if [[ "$f" == '/var/log/lfd.log' && "$(grep -E -v '/var/log/messages has been reset| Watching /var/log/messages' "$f" | wc -c)" -gt 50 ]]; then
117 | if [ "$f" != "$cp_ignore" ]; then
118 | echo -en "\e[93m[WARN]\e[0m un-cleared log file, ${f} found; Contents:\n"
119 | cat "$f"
120 | ((WARN++))
121 | if [[ $STATUS != 2 ]]; then
122 | STATUS=1
123 | fi
124 | fi
125 | elif [[ "$f" != '/var/log/lfd.log' && "$(wc -c <"$f")" -gt 50 ]]; then
126 | if [ "$f" != "$cp_ignore" ]; then
127 | echo -en "\e[93m[WARN]\e[0m un-cleared log file, ${f} found; Contents:\n"
128 | cat "$f"
129 | ((WARN++))
130 | if [[ $STATUS != 2 ]]; then
131 | STATUS=1
132 | fi
133 | fi
134 | fi
135 | done
136 | }
137 |
138 | function checkRoot() {
139 | user="root"
140 | uhome="/root"
141 | for usr in "${SHADOW[@]}"; do
142 | IFS=':' read -r -a u <<<"$usr"
143 | if [[ "${u[0]}" == "$user" ]]; then
144 | if [[ ${u[1]} == "!" ]] || [[ ${u[1]} == "!!" ]] || [[ ${u[1]} == "*" ]]; then
145 | echo -en "\e[32m[PASS]\e[0m User ${user} has no password set.\n"
146 | ((PASS++))
147 | else
148 | echo -en "\e[41m[FAIL]\e[0m User ${user} has a password set on their account.\n"
149 | ((FAIL++))
150 | STATUS=2
151 | fi
152 | fi
153 | done
154 | if [ -d "$uhome"/ ]; then
155 | if [ -d "$uhome"/.ssh/ ]; then
156 | if ls "$uhome"/.ssh/* >/dev/null 2>&1; then
157 | for key in "$uhome"/.ssh/*; do
158 | if [ "$key" == "${uhome}/.ssh/authorized_keys" ]; then
159 |
160 | if [ "$(wc -c <"$key")" -gt 50 ]; then
161 | echo -en "\e[41m[FAIL]\e[0m User \e[1m${user}\e[0m has a populated authorized_keys file in \e[93m${key}\e[0m\n"
162 | akey=$(cat "$key")
163 | echo "File Contents:"
164 | echo "$akey"
165 | echo "--------------"
166 | ((FAIL++))
167 | STATUS=2
168 | fi
169 | elif [ "$key" == "${uhome}/.ssh/id_rsa" ]; then
170 | if [ "$(wc -c <"$key")" -gt 0 ]; then
171 | echo -en "\e[41m[FAIL]\e[0m User \e[1m${user}\e[0m has a private key file in \e[93m${key}\e[0m\n"
172 | akey=$(cat "$key")
173 | echo "File Contents:"
174 | echo "$akey"
175 | echo "--------------"
176 | ((FAIL++))
177 | STATUS=2
178 | else
179 | echo -en "\e[93m[WARN]\e[0m User \e[1m${user}\e[0m has empty private key file in \e[93m${key}\e[0m\n"
180 | ((WARN++))
181 | if [[ $STATUS != 2 ]]; then
182 | STATUS=1
183 | fi
184 | fi
185 | elif [ "$key" != "${uhome}/.ssh/known_hosts" ]; then
186 | echo -en "\e[93m[WARN]\e[0m User \e[1m${user}\e[0m has a file in their .ssh directory at \e[93m${key}\e[0m\n"
187 | ((WARN++))
188 | if [[ $STATUS != 2 ]]; then
189 | STATUS=1
190 | fi
191 | else
192 | if [ "$(wc -c <"$key")" -gt 50 ]; then
193 | echo -en "\e[93m[WARN]\e[0m User \e[1m${user}\e[0m has a populated known_hosts file in \e[93m${key}\e[0m\n"
194 | ((WARN++))
195 | if [[ $STATUS != 2 ]]; then
196 | STATUS=1
197 | fi
198 | fi
199 | fi
200 | done
201 | else
202 | echo -en "\e[32m[ OK ]\e[0m User \e[1m${user}\e[0m has no SSH keys present\n"
203 | fi
204 | else
205 | echo -en "\e[32m[ OK ]\e[0m User \e[1m${user}\e[0m does not have an .ssh directory\n"
206 | fi
207 | if [ -f /root/.bash_history ]; then
208 |
209 | BH_S=$(wc -c = 1000 && $1 != "nobody" {print $1}' /dev/null 2>&1; then
262 | for key in "$uhome"/.ssh/*; do
263 | if [ "$key" == "${uhome}/.ssh/authorized_keys" ]; then
264 | if [ "$(wc -c <"$key")" -gt 50 ]; then
265 | echo -en "\e[41m[FAIL]\e[0m User \e[1m${user}\e[0m has a populated authorized_keys file in \e[93m${key}\e[0m\n"
266 | akey=$(cat "$key")
267 | echo "File Contents:"
268 | echo "$akey"
269 | echo "--------------"
270 | ((FAIL++))
271 | STATUS=2
272 | fi
273 | elif [ "$key" == "${uhome}/.ssh/id_rsa" ]; then
274 | if [ "$(wc -c <"$key")" -gt 0 ]; then
275 | echo -en "\e[41m[FAIL]\e[0m User \e[1m${user}\e[0m has a private key file in \e[93m${key}\e[0m\n"
276 | akey=$(cat "$key")
277 | echo "File Contents:"
278 | echo "$akey"
279 | echo "--------------"
280 | ((FAIL++))
281 | STATUS=2
282 | else
283 | echo -en "\e[93m[WARN]\e[0m User \e[1m${user}\e[0m has empty private key file in \e[93m${key}\e[0m\n"
284 | # shellcheck disable=SC2030
285 | ((WARN++))
286 | if [[ $STATUS != 2 ]]; then
287 | STATUS=1
288 | fi
289 | fi
290 | elif [ "$key" != "${uhome}/.ssh/known_hosts" ]; then
291 |
292 | echo -en "\e[93m[WARN]\e[0m User \e[1m${user}\e[0m has a file in their .ssh directory named \e[93m${key}\e[0m\n"
293 | ((WARN++))
294 | if [[ $STATUS != 2 ]]; then
295 | STATUS=1
296 | fi
297 |
298 | else
299 | if [ "$(wc -c <"$key")" -gt 50 ]; then
300 | echo -en "\e[93m[WARN]\e[0m User \e[1m${user}\e[0m has a known_hosts file in \e[93m${key}\e[0m\n"
301 | ((WARN++))
302 | if [[ $STATUS != 2 ]]; then
303 | STATUS=1
304 | fi
305 | fi
306 | fi
307 |
308 | done
309 |
310 | else
311 | echo -en "\e[32m[ OK ]\e[0m User \e[1m${user}\e[0m has no SSH keys present\n"
312 | fi
313 | else
314 | echo -en "\e[32m[ OK ]\e[0m User \e[1m${user}\e[0m does not have an .ssh directory\n"
315 | fi
316 | else
317 | echo -en "\e[32m[ OK ]\e[0m User \e[1m${user}\e[0m does not have a directory in /home\n"
318 | fi
319 |
320 | # Check for an uncleared .bash_history for this user
321 | if [ -f "${uhome}/.bash_history" ]; then
322 | BH_S=$(wc -c <"${uhome}/.bash_history")
323 |
324 | if [[ $BH_S -lt 200 ]]; then
325 | echo -en "\e[32m[PASS]\e[0m ${user}'s Bash History appears to have been cleared\n"
326 | ((PASS++))
327 | else
328 | echo -en "\e[41m[FAIL]\e[0m ${user}'s Bash History should be cleared to prevent sensitive information from leaking\n"
329 | ((FAIL++))
330 | STATUS=2
331 |
332 | fi
333 | echo -en "\n\n"
334 | fi
335 | fi
336 | done
337 | }
338 | function checkFirewall() {
339 |
340 | if [[ $OS == "Ubuntu" ]]; then
341 | fw="ufw"
342 | ufwa=$(ufw status | head -1 | sed -e "s/^Status:\ //")
343 | if [[ $ufwa == "active" ]]; then
344 | FW_VER="\e[32m[PASS]\e[0m Firewall service (${fw}) is active\n"
345 | # shellcheck disable=SC2031
346 | ((PASS++))
347 | else
348 | FW_VER="\e[93m[WARN]\e[0m No firewall is configured. Ensure ${fw} is installed and configured\n"
349 | # shellcheck disable=SC2031
350 | ((WARN++))
351 | fi
352 | elif [[ $OS == "CentOS Linux" ]] || [[ $OS == "CentOS Stream" ]] || [[ $OS == "Rocky Linux" ]] || [[ $OS == "AlmaLinux" ]]; then
353 | if [ -f /usr/lib/systemd/system/csf.service ]; then
354 | fw="csf"
355 | if [[ $(systemctl status "$fw" >/dev/null 2>&1) ]]; then
356 |
357 | FW_VER="\e[32m[PASS]\e[0m Firewall service (${fw}) is active\n"
358 | ((PASS++))
359 | elif cmdExists "firewall-cmd"; then
360 | if [[ $(systemctl is-active firewalld >/dev/null 2>&1 && echo 1 || echo 0) ]]; then
361 | FW_VER="\e[32m[PASS]\e[0m Firewall service (${fw}) is active\n"
362 | ((PASS++))
363 | else
364 | FW_VER="\e[93m[WARN]\e[0m No firewall is configured. Ensure ${fw} is installed and configured\n"
365 | ((WARN++))
366 | fi
367 | else
368 | FW_VER="\e[93m[WARN]\e[0m No firewall is configured. Ensure ${fw} is installed and configured\n"
369 | ((WARN++))
370 | fi
371 | else
372 | fw="firewalld"
373 | if [[ $(systemctl is-active firewalld >/dev/null 2>&1 && echo 1 || echo 0) ]]; then
374 | FW_VER="\e[32m[PASS]\e[0m Firewall service (${fw}) is active\n"
375 | ((PASS++))
376 | else
377 | FW_VER="\e[93m[WARN]\e[0m No firewall is configured. Ensure ${fw} is installed and configured\n"
378 | ((WARN++))
379 | fi
380 | fi
381 | elif [[ "$OS" =~ Debian.* ]]; then
382 | # user could be using a number of different services for managing their firewall
383 | # we will check some of the most common
384 | if cmdExists 'ufw'; then
385 | fw="ufw"
386 | ufwa=$(ufw status | head -1 | sed -e "s/^Status:\ //")
387 | if [[ $ufwa == "active" ]]; then
388 | FW_VER="\e[32m[PASS]\e[0m Firewall service (${fw}) is active\n"
389 | ((PASS++))
390 | else
391 | FW_VER="\e[93m[WARN]\e[0m No firewall is configured. Ensure ${fw} is installed and configured\n"
392 | ((WARN++))
393 | fi
394 | elif cmdExists "firewall-cmd"; then
395 | fw="firewalld"
396 | if [[ $(systemctl is-active --quiet "$fw") ]]; then
397 | FW_VER="\e[32m[PASS]\e[0m Firewall service (${fw}) is active\n"
398 | ((PASS++))
399 | else
400 | FW_VER="\e[93m[WARN]\e[0m No firewall is configured. Ensure ${fw} is installed and configured\n"
401 | ((WARN++))
402 | fi
403 | else
404 | # user could be using vanilla iptables, check if kernel module is loaded
405 | fw="iptables"
406 | if lsmod | grep -q '^ip_tables' 2>/dev/null; then
407 | FW_VER="\e[32m[PASS]\e[0m Firewall service (${fw}) is active\n"
408 | ((PASS++))
409 | else
410 | FW_VER="\e[93m[WARN]\e[0m No firewall is configured. Ensure ${fw} is installed and configured\n"
411 | ((WARN++))
412 | fi
413 | fi
414 | fi
415 |
416 | }
417 | function checkUpdates() {
418 | if [[ $OS == "Ubuntu" ]] || [[ "$OS" =~ Debian.* ]]; then
419 | # Ensure /tmp exists and has the proper permissions before
420 | # checking for security updates
421 | # https://github.com/digitalocean/marketplace-partners/issues/94
422 | if [[ ! -d /tmp ]]; then
423 | mkdir /tmp
424 | fi
425 | chmod 1777 /tmp
426 |
427 | echo -en "\nUpdating apt package database to check for security updates, this may take a minute...\n\n"
428 | apt-get -y update >/dev/null
429 |
430 | uc=$(apt-get --just-print upgrade | grep -i "security" -c)
431 | if [[ $uc -gt 0 ]]; then
432 | update_count=$((uc / 2))
433 | else
434 | update_count=0
435 | fi
436 |
437 | if [[ $update_count -gt 0 ]]; then
438 | echo -en "\e[41m[FAIL]\e[0m There are ${update_count} security updates available for this image that have not been installed.\n"
439 | echo -en
440 | echo -en "Here is a list of the security updates that are not installed:\n"
441 | sleep 2
442 | apt-get --just-print upgrade | grep -i security | awk '{print $2}' | awk '!seen[$0]++'
443 | echo -en
444 | # shellcheck disable=SC2031
445 | ((FAIL++))
446 | STATUS=2
447 | else
448 | echo -en "\e[32m[PASS]\e[0m There are no pending security updates for this image.\n\n"
449 | ((PASS++))
450 | fi
451 | elif [[ $OS == "CentOS Linux" ]] || [[ $OS == "CentOS Stream" ]] || [[ $OS == "Rocky Linux" ]] || [[ $OS == "AlmaLinux" ]]; then
452 | echo -en "\nChecking for available security updates, this may take a minute...\n\n"
453 |
454 | update_count=$(yum check-update --security --quiet | wc -l)
455 | if [[ $update_count -gt 0 ]]; then
456 | echo -en "\e[41m[FAIL]\e[0m There are ${update_count} security updates available for this image that have not been installed.\n"
457 | ((FAIL++))
458 | STATUS=2
459 | else
460 | echo -en "\e[32m[PASS]\e[0m There are no pending security updates for this image.\n"
461 | ((PASS++))
462 | fi
463 | else
464 | echo "Error encountered"
465 | exit 1
466 | fi
467 |
468 | return 1
469 | }
470 | function checkCloudInit() {
471 |
472 | if hash cloud-init 2>/dev/null; then
473 | CI="\e[32m[PASS]\e[0m Cloud-init is installed.\n"
474 | ((PASS++))
475 | else
476 | CI="\e[41m[FAIL]\e[0m No valid verison of cloud-init was found.\n"
477 | ((FAIL++))
478 | STATUS=2
479 | fi
480 | return 1
481 | }
482 |
483 | clear
484 | echo "DigitalOcean Marketplace Image Validation Tool ${VERSION}"
485 | echo "Executed on: ${RUNDATE}"
486 | echo "Checking local system for Marketplace compatibility..."
487 |
488 | getDistro
489 |
490 | echo -en "\n\e[1mDistribution:\e[0m ${OS}\n"
491 | echo -en "\e[1mVersion:\e[0m ${VER}\n\n"
492 |
493 | ost=0
494 | osv=0
495 |
496 | if [[ $OS == "Ubuntu" ]]; then
497 | ost=1
498 | if [[ $VER == "22.10" ]] || [[ $VER == "22.04" ]] || [[ $VER == "20.04" ]] || [[ $VER == "18.04" ]] || [[ $VER == "16.04" ]]; then
499 | osv=1
500 | fi
501 |
502 | elif [[ "$OS" =~ Debian.* ]]; then
503 | ost=1
504 | case "$VER" in
505 | 9)
506 | osv=1
507 | ;;
508 | 10)
509 | osv=1
510 | ;;
511 | 11)
512 | osv=1
513 | ;;
514 | 12)
515 | osv=1
516 | ;;
517 | *)
518 | osv=2
519 | ;;
520 | esac
521 |
522 | elif [[ $OS == "CentOS Linux" ]]; then
523 | ost=1
524 | if [[ $VER == "8" ]]; then
525 | osv=1
526 | elif [[ $VER == "7" ]]; then
527 | osv=1
528 | elif [[ $VER == "6" ]]; then
529 | osv=1
530 | else
531 | osv=2
532 | fi
533 | elif [[ $OS == "CentOS Stream" ]]; then
534 | ost=1
535 | if [[ $VER == "8" ]]; then
536 | osv=1
537 | elif [[ $VER == "9" ]]; then
538 | osv=1
539 | else
540 | osv=2
541 | fi
542 | elif [[ $OS == "Rocky Linux" ]]; then
543 | ost=1
544 | if [[ $VER =~ 8\. ]] || [[ $VER =~ 9\. ]]; then
545 | osv=1
546 | else
547 | osv=2
548 | fi
549 | elif [[ $OS == "AlmaLinux" ]]; then
550 | ost=1
551 | if [[ "$VERSION" =~ 8.* ]] || [[ "$VERSION" =~ 9.* ]]; then
552 | osv=1
553 | else
554 | osv=2
555 | fi
556 | else
557 | ost=0
558 | fi
559 |
560 | if [[ $ost == 1 ]]; then
561 | echo -en "\e[32m[PASS]\e[0m Supported Operating System Detected: ${OS}\n"
562 | ((PASS++))
563 | else
564 | echo -en "\e[41m[FAIL]\e[0m ${OS} is not a supported Operating System\n"
565 | ((FAIL++))
566 | STATUS=2
567 | fi
568 |
569 | if [[ $osv == 1 ]]; then
570 | echo -en "\e[32m[PASS]\e[0m Supported Release Detected: ${VER}\n"
571 | ((PASS++))
572 | elif [[ $ost == 1 ]]; then
573 | echo -en "\e[41m[FAIL]\e[0m ${OS} ${VER} is not a supported Operating System Version\n"
574 | ((FAIL++))
575 | STATUS=2
576 | else
577 | echo "Exiting..."
578 | exit 1
579 | fi
580 |
581 | checkCloudInit
582 |
583 | echo -en "$CI"
584 |
585 | checkFirewall
586 |
587 | echo -en "$FW_VER"
588 |
589 | checkUpdates
590 |
591 | loadPasswords
592 |
593 | checkLogs
594 |
595 | echo -en "\n\nChecking all user-created accounts...\n"
596 | checkUsers
597 |
598 | echo -en "\n\nChecking the root account...\n"
599 | checkRoot
600 |
601 | checkAgent
602 |
603 | # Summary
604 | echo -en "\n\n---------------------------------------------------------------------------------------------------\n"
605 |
606 | if [[ $STATUS == 0 ]]; then
607 | echo -en "Scan Complete.\n\e[32mAll Tests Passed!\e[0m\n"
608 | elif [[ $STATUS == 1 ]]; then
609 | echo -en "Scan Complete. \n\e[93mSome non-critical tests failed. Please review these items.\e[0m\e[0m\n"
610 | else
611 | echo -en "Scan Complete. \n\e[41mOne or more tests failed. Please review these items and re-test.\e[0m\n"
612 | fi
613 | echo "---------------------------------------------------------------------------------------------------"
614 | echo -en "\e[1m${PASS} Tests PASSED\e[0m\n"
615 | echo -en "\e[1m${WARN} WARNINGS\e[0m\n"
616 | echo -en "\e[1m${FAIL} Tests FAILED\e[0m\n"
617 | echo -en "---------------------------------------------------------------------------------------------------\n"
618 |
619 | if [[ $STATUS == 0 ]]; then
620 | echo -en "We did not detect any issues with this image. Please be sure to manually ensure that all software installed on the base system is functional, secure and properly configured (or facilities for configuration on first-boot have been created).\n\n"
621 | exit 0
622 | elif [[ $STATUS == 1 ]]; then
623 | echo -en "Please review all [WARN] items above and ensure they are intended or resolved. If you do not have a specific requirement, we recommend resolving these items before image submission\n\n"
624 | exit 0
625 | else
626 | echo -en "Some critical tests failed. These items must be resolved and this scan re-run before you submit your image to the DigitalOcean Marketplace.\n\n"
627 | exit 1
628 | fi
629 |
--------------------------------------------------------------------------------
/data/do_img_checks.sh-LICENSE.md:
--------------------------------------------------------------------------------
1 | Apache License
2 | ==============
3 |
4 | _Version 2.0, January 2004_
5 | _<>_
6 |
7 | ### Terms and Conditions for use, reproduction, and distribution
8 |
9 | #### 1. Definitions
10 |
11 | “License” shall mean the terms and conditions for use, reproduction, and
12 | distribution as defined by Sections 1 through 9 of this document.
13 |
14 | “Licensor” shall mean the copyright owner or entity authorized by the copyright
15 | owner that is granting the License.
16 |
17 | “Legal Entity” shall mean the union of the acting entity and all other entities
18 | that control, are controlled by, or are under common control with that entity.
19 | For the purposes of this definition, “control” means **(i)** the power, direct or
20 | indirect, to cause the direction or management of such entity, whether by
21 | contract or otherwise, or **(ii)** ownership of fifty percent (50%) or more of the
22 | outstanding shares, or **(iii)** beneficial ownership of such entity.
23 |
24 | “You” (or “Your”) shall mean an individual or Legal Entity exercising
25 | permissions granted by this License.
26 |
27 | “Source” form shall mean the preferred form for making modifications, including
28 | but not limited to software source code, documentation source, and configuration
29 | files.
30 |
31 | “Object” form shall mean any form resulting from mechanical transformation or
32 | translation of a Source form, including but not limited to compiled object code,
33 | generated documentation, and conversions to other media types.
34 |
35 | “Work” shall mean the work of authorship, whether in Source or Object form, made
36 | available under the License, as indicated by a copyright notice that is included
37 | in or attached to the work (an example is provided in the Appendix below).
38 |
39 | “Derivative Works” shall mean any work, whether in Source or Object form, that
40 | is based on (or derived from) the Work and for which the editorial revisions,
41 | annotations, elaborations, or other modifications represent, as a whole, an
42 | original work of authorship. For the purposes of this License, Derivative Works
43 | shall not include works that remain separable from, or merely link (or bind by
44 | name) to the interfaces of, the Work and Derivative Works thereof.
45 |
46 | “Contribution” shall mean any work of authorship, including the original version
47 | of the Work and any modifications or additions to that Work or Derivative Works
48 | thereof, that is intentionally submitted to Licensor for inclusion in the Work
49 | by the copyright owner or by an individual or Legal Entity authorized to submit
50 | on behalf of the copyright owner. For the purposes of this definition,
51 | “submitted” means any form of electronic, verbal, or written communication sent
52 | to the Licensor or its representatives, including but not limited to
53 | communication on electronic mailing lists, source code control systems, and
54 | issue tracking systems that are managed by, or on behalf of, the Licensor for
55 | the purpose of discussing and improving the Work, but excluding communication
56 | that is conspicuously marked or otherwise designated in writing by the copyright
57 | owner as “Not a Contribution.”
58 |
59 | “Contributor” shall mean Licensor and any individual or Legal Entity on behalf
60 | of whom a Contribution has been received by Licensor and subsequently
61 | incorporated within the Work.
62 |
63 | #### 2. Grant of Copyright License
64 |
65 | Subject to the terms and conditions of this License, each Contributor hereby
66 | grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
67 | irrevocable copyright license to reproduce, prepare Derivative Works of,
68 | publicly display, publicly perform, sublicense, and distribute the Work and such
69 | Derivative Works in Source or Object form.
70 |
71 | #### 3. Grant of Patent License
72 |
73 | Subject to the terms and conditions of this License, each Contributor hereby
74 | grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
75 | irrevocable (except as stated in this section) patent license to make, have
76 | made, use, offer to sell, sell, import, and otherwise transfer the Work, where
77 | such license applies only to those patent claims licensable by such Contributor
78 | that are necessarily infringed by their Contribution(s) alone or by combination
79 | of their Contribution(s) with the Work to which such Contribution(s) was
80 | submitted. If You institute patent litigation against any entity (including a
81 | cross-claim or counterclaim in a lawsuit) alleging that the Work or a
82 | Contribution incorporated within the Work constitutes direct or contributory
83 | patent infringement, then any patent licenses granted to You under this License
84 | for that Work shall terminate as of the date such litigation is filed.
85 |
86 | #### 4. Redistribution
87 |
88 | You may reproduce and distribute copies of the Work or Derivative Works thereof
89 | in any medium, with or without modifications, and in Source or Object form,
90 | provided that You meet the following conditions:
91 |
92 | * **(a)** You must give any other recipients of the Work or Derivative Works a copy of
93 | this License; and
94 | * **(b)** You must cause any modified files to carry prominent notices stating that You
95 | changed the files; and
96 | * **(c)** You must retain, in the Source form of any Derivative Works that You distribute,
97 | all copyright, patent, trademark, and attribution notices from the Source form
98 | of the Work, excluding those notices that do not pertain to any part of the
99 | Derivative Works; and
100 | * **(d)** If the Work includes a “NOTICE” text file as part of its distribution, then any
101 | Derivative Works that You distribute must include a readable copy of the
102 | attribution notices contained within such NOTICE file, excluding those notices
103 | that do not pertain to any part of the Derivative Works, in at least one of the
104 | following places: within a NOTICE text file distributed as part of the
105 | Derivative Works; within the Source form or documentation, if provided along
106 | with the Derivative Works; or, within a display generated by the Derivative
107 | Works, if and wherever such third-party notices normally appear. The contents of
108 | the NOTICE file are for informational purposes only and do not modify the
109 | License. You may add Your own attribution notices within Derivative Works that
110 | You distribute, alongside or as an addendum to the NOTICE text from the Work,
111 | provided that such additional attribution notices cannot be construed as
112 | modifying the License.
113 |
114 | You may add Your own copyright statement to Your modifications and may provide
115 | additional or different license terms and conditions for use, reproduction, or
116 | distribution of Your modifications, or for any such Derivative Works as a whole,
117 | provided Your use, reproduction, and distribution of the Work otherwise complies
118 | with the conditions stated in this License.
119 |
120 | #### 5. Submission of Contributions
121 |
122 | Unless You explicitly state otherwise, any Contribution intentionally submitted
123 | for inclusion in the Work by You to the Licensor shall be under the terms and
124 | conditions of this License, without any additional terms or conditions.
125 | Notwithstanding the above, nothing herein shall supersede or modify the terms of
126 | any separate license agreement you may have executed with Licensor regarding
127 | such Contributions.
128 |
129 | #### 6. Trademarks
130 |
131 | This License does not grant permission to use the trade names, trademarks,
132 | service marks, or product names of the Licensor, except as required for
133 | reasonable and customary use in describing the origin of the Work and
134 | reproducing the content of the NOTICE file.
135 |
136 | #### 7. Disclaimer of Warranty
137 |
138 | Unless required by applicable law or agreed to in writing, Licensor provides the
139 | Work (and each Contributor provides its Contributions) on an “AS IS” BASIS,
140 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied,
141 | including, without limitation, any warranties or conditions of TITLE,
142 | NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are
143 | solely responsible for determining the appropriateness of using or
144 | redistributing the Work and assume any risks associated with Your exercise of
145 | permissions under this License.
146 |
147 | #### 8. Limitation of Liability
148 |
149 | In no event and under no legal theory, whether in tort (including negligence),
150 | contract, or otherwise, unless required by applicable law (such as deliberate
151 | and grossly negligent acts) or agreed to in writing, shall any Contributor be
152 | liable to You for damages, including any direct, indirect, special, incidental,
153 | or consequential damages of any character arising as a result of this License or
154 | out of the use or inability to use the Work (including but not limited to
155 | damages for loss of goodwill, work stoppage, computer failure or malfunction, or
156 | any and all other commercial damages or losses), even if such Contributor has
157 | been advised of the possibility of such damages.
158 |
159 | #### 9. Accepting Warranty or Additional Liability
160 |
161 | While redistributing the Work or Derivative Works thereof, You may choose to
162 | offer, and charge a fee for, acceptance of support, warranty, indemnity, or
163 | other liability obligations and/or rights consistent with this License. However,
164 | in accepting such obligations, You may act only on Your own behalf and on Your
165 | sole responsibility, not on behalf of any other Contributor, and only if You
166 | agree to indemnify, defend, and hold each Contributor harmless for any liability
167 | incurred by, or claims asserted against, such Contributor by reason of your
168 | accepting any such warranty or additional liability.
169 |
170 | _END OF TERMS AND CONDITIONS_
171 |
172 | ### APPENDIX: How to apply the Apache License to your work
173 |
174 | To apply the Apache License to your work, attach the following boilerplate
175 | notice, with the fields enclosed by brackets `[]` replaced with your own
176 | identifying information. (Don't include the brackets!) The text should be
177 | enclosed in the appropriate comment syntax for the file format. We also
178 | recommend that a file or class name and description of purpose be included on
179 | the same “printed page” as the copyright notice for easier identification within
180 | third-party archives.
181 |
182 | Copyright [yyyy] [name of copyright owner]
183 |
184 | Licensed under the Apache License, Version 2.0 (the "License");
185 | you may not use this file except in compliance with the License.
186 | You may obtain a copy of the License at
187 |
188 | http://www.apache.org/licenses/LICENSE-2.0
189 |
190 | Unless required by applicable law or agreed to in writing, software
191 | distributed under the License is distributed on an "AS IS" BASIS,
192 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
193 | See the License for the specific language governing permissions and
194 | limitations under the License.
195 |
--------------------------------------------------------------------------------
/data/docker-compose-up.service:
--------------------------------------------------------------------------------
1 | [Unit]
2 | Description=Restart Docker Service
3 | After=docker.service
4 | Wants=network-online.target
5 | Requires=docker.service
6 |
7 | [Service]
8 | WorkingDirectory=/opt/redash
9 | ExecStart=/usr/local/bin/docker compose up -d
10 |
11 | [Install]
12 | WantedBy=default.target
--------------------------------------------------------------------------------
/data/generate_key.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | FLAG="/var/log/generate_secrets.log"
4 | if [ ! -f "$FLAG" ]; then
5 | COOKIE_SECRET=$(pwgen -1s 32)
6 | SECRET_KEY=$(pwgen -1s 32)
7 |
8 | sed -i "s/REDASH_COOKIE_SECRET=.*/REDASH_COOKIE_SECRET=$COOKIE_SECRET/g" /opt/redash/env
9 | sed -i "s/REDASH_SECRET_KEY=.*/REDASH_SECRET_KEY=$SECRET_KEY/g" /opt/redash/env
10 |
11 | #the next line creates an empty file so it won't run the next boot
12 | echo "$(date) Updated secrets." >>"$FLAG"
13 | else
14 | echo "Secrets already set, skipping."
15 | fi
16 |
17 | exit 0
18 |
--------------------------------------------------------------------------------
/packer.json:
--------------------------------------------------------------------------------
1 | {
2 | "variables": {
3 | "image_version": ""
4 | },
5 | "builders": [
6 | {
7 | "name": "redash-us-east-1",
8 | "type": "amazon-ebs",
9 | "region": "us-east-1",
10 | "source_ami": "ami-0ac019f4fcb7cb7e6",
11 | "instance_type": "t2.micro",
12 | "ssh_username": "ubuntu",
13 | "ami_name": "redash-{{user `image_version`}}-us-east-1"
14 | },
15 | {
16 | "type": "googlecompute",
17 | "project_id": "redash-bird-123",
18 | "source_image_family": "ubuntu-1804-lts",
19 | "zone": "us-central1-a",
20 | "ssh_username": "arik"
21 | },
22 | {
23 | "type": "digitalocean",
24 | "image": "ubuntu-18-04-x64",
25 | "region": "nyc3",
26 | "size": "1gb",
27 | "ssh_username": "root",
28 | "snapshot_name": "redash-{{user `image_version`}}-{{timestamp}}"
29 | }
30 | ],
31 | "provisioners": [
32 | {
33 | "type": "shell",
34 | "inline": ["sleep 30"]
35 | },
36 | {
37 | "type": "shell",
38 | "script": "setup.sh",
39 | "execute_command": "{{ .Vars }} sudo -E -S bash '{{ .Path }}'"
40 | },
41 | {
42 | "type": "shell",
43 | "inline": "sudo rm /root/.ssh/authorized_keys || true"
44 | },
45 | {
46 | "type": "shell",
47 | "inline": "sudo rm /home/ubuntu/.ssh/authorized_keys || true"
48 | },
49 | {
50 | "type": "file",
51 | "source": "data/generate_key.sh",
52 | "destination": "/tmp/rc.local"
53 | },
54 | {
55 | "type": "file",
56 | "source": "data/docker-compose-up.service",
57 | "destination": "/tmp/docker-compose-up.service"
58 | },
59 | {
60 | "type": "shell",
61 | "inline": [
62 | "sudo mv /tmp/rc.local /etc/rc.local",
63 | "sudo chown root /etc/rc.local",
64 | "sudo chmod 755 /etc/rc.local",
65 | "sudo mv /tmp/docker-compose-up.service /etc/systemd/system/docker-compose-up.service",
66 | "sudo chown root /etc/systemd/system/docker-compose-up.service",
67 | "sudo systemctl enable docker-compose-up"
68 | ]
69 | },
70 | {
71 | "type": "shell",
72 | "script": "data/cleanup.sh",
73 | "execute_command": "{{ .Vars }} sudo -E -S bash '{{ .Path }}'"
74 | },
75 | {
76 | "type": "shell",
77 | "script": "data/do_img_checks.sh",
78 | "execute_command": "{{ .Vars }} sudo -E -S bash '{{ .Path }}'"
79 | }
80 | ],
81 | "post-processors": [
82 | {
83 | "type": "googlecompute-export",
84 | "only": ["googlecompute"],
85 | "paths": ["gs://redash-images/redash.{{user `image_version`}}.tar.gz"],
86 | "keep_input_artifact": true
87 | }
88 | ]
89 | }
90 |
--------------------------------------------------------------------------------
/redash_make_default.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | # Trivial script to add Redash to the user login profile
4 |
5 | cat <>~/__TARGET_FILE__
6 |
7 | # Added by Redash 'redash_make_default.sh' script
8 | export COMPOSE_PROJECT_NAME=redash
9 | export COMPOSE_FILE=__COMPOSE_FILE__
10 | EOF
11 | echo "Redash has now been set as the default Docker Compose project"
12 |
--------------------------------------------------------------------------------
/setup.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | # This script sets up dockerized Redash on Debian 12.x, Fedora 38 or later, Ubuntu LTS 20.04 & 22.04, and RHEL (and compatible) 8.x & 9.x
4 | set -eu
5 |
6 | REDASH_BASE_PATH=/opt/redash
7 | DONT_START=no
8 | OVERWRITE=no
9 | PREVIEW=no
10 | REDASH_VERSION=""
11 |
12 | # Ensure the script is being run as root
13 | ID=$(id -u)
14 | if [ "0$ID" -ne 0 ]
15 | then echo "Please run this script as root"
16 | exit
17 | fi
18 |
19 | # Ensure the 'docker' and 'docker-compose' commands are available
20 | # and if not, ensure the script can install them
21 | SKIP_DOCKER_INSTALL=no
22 | if [ -x "$(command -v docker)" ]; then
23 | # The first condition is 'docker-compose (v1)' and the second is 'docker compose (v2)'.
24 | if [ -x "$(command -v docker-compose)" ] || (docker compose 1> /dev/null 2>& 1 && [ $? -eq 0 ]); then
25 | SKIP_DOCKER_INSTALL=yes
26 | fi
27 | elif [ ! -f /etc/os-release ]; then
28 | echo "Unknown Linux distribution. This script presently works only on Debian, Fedora, Ubuntu, and RHEL (and compatible)"
29 | exit
30 | fi
31 |
32 | # Parse any user provided parameters
33 | opts="$(getopt -o doph -l dont-start,overwrite,preview,help,version: --name "$0" -- "$@")"
34 | eval set -- "$opts"
35 |
36 | while true
37 | do
38 | case "$1" in
39 | -d|--dont-start)
40 | DONT_START=yes
41 | shift
42 | ;;
43 | -o|--overwrite)
44 | OVERWRITE=yes
45 | shift
46 | ;;
47 | -p|--preview)
48 | PREVIEW=yes
49 | shift
50 | ;;
51 | --version)
52 | REDASH_VERSION="$2"
53 | shift 2
54 | ;;
55 | -h|--help)
56 | echo "Redash setup script usage: $0 [-d|--dont-start] [-p|--preview] [-o|--overwrite] [--version ]"
57 | echo " The --preview (also -p) option uses the Redash 'preview' Docker image instead of the last stable release"
58 | echo " The --version option installs the specified version tag of Redash (e.g., 10.1.0)"
59 | echo " The --overwrite (also -o) option replaces any existing configuration with a fresh new install"
60 | echo " The --dont-start (also -d) option installs Redash, but doesn't automatically start it afterwards"
61 | exit 1
62 | ;;
63 | --)
64 | shift
65 | break
66 | ;;
67 | *)
68 | echo "Unknown option: $1" >&2
69 | exit 1
70 | ;;
71 | esac
72 | done
73 |
74 | install_docker_debian() {
75 | echo "** Installing Docker (Debian) **"
76 |
77 | export DEBIAN_FRONTEND=noninteractive
78 | apt-get -qqy update
79 | DEBIAN_FRONTEND=noninteractive apt-get -qqy -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" upgrade
80 | apt-get -yy install apt-transport-https ca-certificates curl software-properties-common pwgen gnupg
81 |
82 | # Add Docker GPG signing key
83 | if [ ! -f "/etc/apt/keyrings/docker.gpg" ]; then
84 | install -m 0755 -d /etc/apt/keyrings
85 | curl -fsSL https://download.docker.com/linux/debian/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg
86 | chmod a+r /etc/apt/keyrings/docker.gpg
87 | fi
88 |
89 | # Add Docker download repository to apt
90 | cat </etc/apt/sources.list.d/docker.list
91 | deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/debian $(. /etc/os-release && echo "$VERSION_CODENAME") stable
92 | EOF
93 | apt-get update && apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
94 | }
95 |
96 | install_docker_fedora() {
97 | echo "** Installing Docker (Fedora) **"
98 |
99 | # Add Docker package repository
100 | dnf -qy install dnf-plugins-core
101 | dnf config-manager --quiet --add-repo https://download.docker.com/linux/fedora/docker-ce.repo
102 |
103 | # Install Docker
104 | dnf install -qy docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin pwgen
105 |
106 | # Start Docker and enable it for automatic start at boot
107 | systemctl start docker && systemctl enable docker
108 | }
109 |
110 | install_docker_rhel() {
111 | echo "** Installing Docker (RHEL and compatible) **"
112 |
113 | # Add EPEL package repository
114 | if [ "x$DISTRO" = "xrhel" ]; then
115 | # Genuine RHEL doesn't have the epel-release package in its repos
116 | RHEL_VER=$(. /etc/os-release && echo "$VERSION_ID" | cut -d "." -f1)
117 | if [ "0$RHEL_VER" -eq "9" ]; then
118 | yum install -qy https://dl.fedoraproject.org/pub/epel/epel-release-latest-9.noarch.rpm
119 | else
120 | yum install -qy https://dl.fedoraproject.org/pub/epel/epel-release-latest-8.noarch.rpm
121 | fi
122 | yum install -qy yum-utils
123 | else
124 | # RHEL compatible distros do have epel-release available
125 | yum install -qy epel-release yum-utils
126 | fi
127 | yum update -qy
128 |
129 | # Add Docker package repository
130 | yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
131 | yum update -qy
132 |
133 | # Install Docker
134 | yum install -qy docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin pwgen
135 |
136 | # Start Docker and enable it for automatic start at boot
137 | systemctl start docker && systemctl enable docker
138 | }
139 |
140 | install_docker_ubuntu() {
141 | echo "** Installing Docker (Ubuntu) **"
142 |
143 | export DEBIAN_FRONTEND=noninteractive
144 | apt-get -qqy update
145 | DEBIAN_FRONTEND=noninteractive sudo -E apt-get -qqy -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" upgrade
146 | apt-get -yy install apt-transport-https ca-certificates curl software-properties-common pwgen gnupg
147 |
148 | # Add Docker GPG signing key
149 | if [ ! -f "/etc/apt/keyrings/docker.gpg" ]; then
150 | install -m 0755 -d /etc/apt/keyrings
151 | curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg
152 | chmod a+r /etc/apt/keyrings/docker.gpg
153 | fi
154 |
155 | # Add Docker download repository to apt
156 | cat </etc/apt/sources.list.d/docker.list
157 | deb [arch=""$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable
158 | EOF
159 | apt-get update && apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
160 | }
161 |
162 | create_directories() {
163 | echo "** Creating $REDASH_BASE_PATH directory structure for Redash **"
164 |
165 | if [ ! -e "$REDASH_BASE_PATH" ]; then
166 | mkdir -p "$REDASH_BASE_PATH"
167 | chown "$USER:" "$REDASH_BASE_PATH"
168 | fi
169 |
170 | if [ -e "$REDASH_BASE_PATH"/postgres-data ]; then
171 | # PostgreSQL database directory seems to exist already
172 |
173 | if [ "x$OVERWRITE" = "xyes" ]; then
174 | # We've been asked to overwrite the existing database
175 | echo "Shutting down any running Redash instance"
176 | if [ -e "$REDASH_BASE_PATH"/compose.yaml ]; then
177 | docker compose -f "$REDASH_BASE_PATH"/compose.yaml down
178 | fi
179 |
180 | echo "Moving old Redash PG database directory out of the way"
181 | mv "${REDASH_BASE_PATH}/postgres-data" "${REDASH_BASE_PATH}/postgres-data-${TIMESTAMP_NOW}"
182 | mkdir "$REDASH_BASE_PATH"/postgres-data
183 | fi
184 | else
185 | mkdir "$REDASH_BASE_PATH"/postgres-data
186 | fi
187 | }
188 |
189 | create_env() {
190 | echo "** Creating Redash environment file **"
191 |
192 | # Minimum mandatory values (when not just developing)
193 | COOKIE_SECRET=$(pwgen -1s 32)
194 | SECRET_KEY=$(pwgen -1s 32)
195 | PG_PASSWORD=$(pwgen -1s 32)
196 | DATABASE_URL="postgresql://postgres:${PG_PASSWORD}@postgres/postgres"
197 |
198 | if [ -e "$REDASH_BASE_PATH"/env ]; then
199 | # There's already an environment file
200 |
201 | if [ "x$OVERWRITE" = "xno" ]; then
202 | echo
203 | echo "Environment file already exists, reusing that one + and adding any missing (mandatory) values"
204 |
205 | # Add any missing mandatory values
206 | REDASH_COOKIE_SECRET=
207 | REDASH_COOKIE_SECRET=$(. "$REDASH_BASE_PATH"/env && echo "$REDASH_COOKIE_SECRET")
208 | if [ -z "$REDASH_COOKIE_SECRET" ]; then
209 | echo "REDASH_COOKIE_SECRET=$COOKIE_SECRET" >> "$REDASH_BASE_PATH"/env
210 | echo "REDASH_COOKIE_SECRET added to env file"
211 | fi
212 |
213 | REDASH_SECRET_KEY=
214 | REDASH_SECRET_KEY=$(. "$REDASH_BASE_PATH"/env && echo "$REDASH_SECRET_KEY")
215 | if [ -z "$REDASH_SECRET_KEY" ]; then
216 | echo "REDASH_SECRET_KEY=$SECRET_KEY" >> "$REDASH_BASE_PATH"/env
217 | echo "REDASH_SECRET_KEY added to env file"
218 | fi
219 |
220 | POSTGRES_PASSWORD=
221 | POSTGRES_PASSWORD=$(. "$REDASH_BASE_PATH"/env && echo "$POSTGRES_PASSWORD")
222 | if [ -z "$POSTGRES_PASSWORD" ]; then
223 | POSTGRES_PASSWORD=$PG_PASSWORD
224 | echo "POSTGRES_PASSWORD=$POSTGRES_PASSWORD" >> "$REDASH_BASE_PATH"/env
225 | echo "POSTGRES_PASSWORD added to env file"
226 | fi
227 |
228 | REDASH_DATABASE_URL=
229 | REDASH_DATABASE_URL=$(. "$REDASH_BASE_PATH"/env && echo "$REDASH_DATABASE_URL")
230 | if [ -z "$REDASH_DATABASE_URL" ]; then
231 | echo "REDASH_DATABASE_URL=postgresql://postgres:${POSTGRES_PASSWORD}@postgres/postgres" >> "$REDASH_BASE_PATH"/env
232 | echo "REDASH_DATABASE_URL added to env file"
233 | fi
234 |
235 | echo
236 | return
237 | fi
238 |
239 | # Move any existing environment file out of the way
240 | mv -f "${REDASH_BASE_PATH}/env" "${REDASH_BASE_PATH}/env.old-${TIMESTAMP_NOW}"
241 | fi
242 |
243 | echo "Generating brand new environment file"
244 |
245 | cat <"$REDASH_BASE_PATH"/env
246 | PYTHONUNBUFFERED=0
247 | REDASH_LOG_LEVEL=INFO
248 | REDASH_REDIS_URL=redis://redis:6379/0
249 | REDASH_COOKIE_SECRET=$COOKIE_SECRET
250 | REDASH_SECRET_KEY=$SECRET_KEY
251 | POSTGRES_PASSWORD=$PG_PASSWORD
252 | REDASH_DATABASE_URL=$DATABASE_URL
253 | REDASH_ENFORCE_CSRF=true
254 | REDASH_GUNICORN_TIMEOUT=60
255 | EOF
256 | }
257 |
258 | setup_compose() {
259 | echo "** Creating Redash Docker compose file **"
260 |
261 | cd "$REDASH_BASE_PATH"
262 | GIT_BRANCH="${REDASH_BRANCH:-master}" # Default branch/version to master if not specified in REDASH_BRANCH env var
263 | if [ "x$OVERWRITE" = "xyes" -a -e compose.yaml ]; then
264 | mv -f compose.yaml compose.yaml.old-${TIMESTAMP_NOW}
265 | fi
266 | curl -fsSOL https://raw.githubusercontent.com/getredash/setup/"$GIT_BRANCH"/data/compose.yaml
267 |
268 | # Check for conflicts between --version and --preview options
269 | if [ "x$PREVIEW" = "xyes" ] && [ -n "$REDASH_VERSION" ]; then
270 | echo "Error: Cannot specify both --preview and --version options"
271 | exit 1
272 | fi
273 |
274 | # Set TAG based on provided options
275 | if [ "x$PREVIEW" = "xyes" ]; then
276 | TAG="preview"
277 | echo "** Using preview version of Redash **"
278 | elif [ -n "$REDASH_VERSION" ]; then
279 | TAG="$REDASH_VERSION"
280 | echo "** Using specified Redash version: $TAG **"
281 | else
282 | # Get the latest stable version from GitHub API
283 | echo "** Fetching latest stable Redash version **"
284 | LATEST_TAG=$(curl -s https://api.github.com/repos/getredash/redash/releases/latest | grep -Po '"tag_name": "\K.*?(?=")')
285 | if [ -n "$LATEST_TAG" ]; then
286 | # Remove 'v' prefix if present (GitHub tags use 'v', Docker tags don't)
287 | TAG=$(echo "$LATEST_TAG" | sed 's/^v//')
288 | echo "** Using latest stable Redash version: $TAG **"
289 | else
290 | # Fallback to hardcoded version if API call fails
291 | TAG="latest"
292 | echo "** Warning: Failed to fetch latest version, using fallback version: $TAG **"
293 | fi
294 | fi
295 |
296 | sed -i "s|__TAG__|$TAG|" compose.yaml
297 | export COMPOSE_FILE="$REDASH_BASE_PATH"/compose.yaml
298 | export COMPOSE_PROJECT_NAME=redash
299 | }
300 |
301 | create_make_default() {
302 | echo "** Creating redash_make_default.sh script **"
303 |
304 | curl -fsSOL https://raw.githubusercontent.com/getredash/setup/"$GIT_BRANCH"/redash_make_default.sh
305 | sed -i "s|__COMPOSE_FILE__|$COMPOSE_FILE|" redash_make_default.sh
306 | sed -i "s|__TARGET_FILE__|$PROFILE|" redash_make_default.sh
307 | chmod +x redash_make_default.sh
308 | }
309 |
310 | startup() {
311 | if [ "x$DONT_START" != "xyes" ]; then
312 | echo
313 | echo "*********************"
314 | echo "** Starting Redash **"
315 | echo "*********************"
316 | echo "** Initialising Redash database **"
317 | docker compose run --rm server create_db
318 |
319 | echo "** Starting the rest of Redash **"
320 | docker compose up -d
321 |
322 | echo
323 | echo "Redash has been installed and is ready for configuring at http://$(hostname -f):5000"
324 | echo
325 | else
326 | echo
327 | echo "*************************************************************"
328 | echo "** As requested, Redash has been installed but NOT started **"
329 | echo "*************************************************************"
330 | echo
331 | fi
332 | }
333 |
334 | echo
335 | echo "Redash installation script. :)"
336 | echo
337 |
338 | TIMESTAMP_NOW=$(date +'%Y.%m.%d-%H.%M')
339 |
340 | # Run the distro specific Docker installation
341 | PROFILE=.profile
342 | if [ "$SKIP_DOCKER_INSTALL" = "yes" ]; then
343 | echo "Docker and Docker Compose are already installed, so skipping that step."
344 | else
345 | DISTRO=$(. /etc/os-release && echo "$ID")
346 | case "$DISTRO" in
347 | debian)
348 | install_docker_debian
349 | ;;
350 | fedora)
351 | install_docker_fedora
352 | ;;
353 | ubuntu)
354 | install_docker_ubuntu
355 | ;;
356 | almalinux|centos|ol|rhel|rocky)
357 | PROFILE=.bashrc
358 | install_docker_rhel
359 | ;;
360 | *)
361 | echo "This doesn't seem to be a Debian, Fedora, Ubuntu, nor RHEL (compatible) system, so this script doesn't know how to add Docker to it."
362 | echo
363 | echo "Please contact the Redash project via GitHub and ask about getting support added, or add it yourself and let us know. :)"
364 | echo
365 | exit
366 | ;;
367 | esac
368 | fi
369 |
370 | # Do the things that aren't distro specific
371 | create_directories
372 | create_env
373 | setup_compose
374 | create_make_default
375 | startup
376 |
377 | echo "If you want Redash to be your default Docker Compose project when you login to this server"
378 | echo "in future, then please run $REDASH_BASE_PATH/redash_make_default.sh"
379 | echo
380 | echo "That will set some Docker specific environment variables just for Redash. If you"
381 | echo "already use Docker Compose on this computer for other things, you should probably skip it."
382 |
--------------------------------------------------------------------------------