├── .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 | --------------------------------------------------------------------------------