diff --git a/DEPLOYING.md b/DEPLOYING.md index 2ee71ff..645fccd 100644 --- a/DEPLOYING.md +++ b/DEPLOYING.md @@ -29,10 +29,7 @@ 1. Doppler is installed and authenticated for use with the `ttl-sh` project a. Have a valid `DOPPLER_TOKEN` from `ttl-sh` set b. `echo $DOPPLER_TOKEN | doppler configure set token --scope /` -2. `./install.sh` -3. Successful run states: - a. `Container ttlsh-redis Started` - b. `Container ttlsh-hooks Started` - b. `Container ttlsh-reaper Started` - c. `Container ttlsh-registry Started` - d. `Container ttlsh-nginx Started` +2. `cd ansible` +3. `./ansible.sh` +4. Successful run indicates approximately the following: + a. `ok=42 changed=6 unreachable=0 failed=0 skipped=3` diff --git a/ansible/ansible.cfg b/ansible/ansible.cfg new file mode 100644 index 0000000..1094ac1 --- /dev/null +++ b/ansible/ansible.cfg @@ -0,0 +1,12 @@ +[defaults] +inventory = ./inventory/hosts +interpreter_python = auto_silent +retry_files_enabled = False +stdout_callback = default +bin_ansible_callbacks = True + +[callback_default] +result_format = yaml + +[ssh_connection] +pipelining = True diff --git a/ansible/ansible.sh b/ansible/ansible.sh new file mode 100755 index 0000000..42a77a1 --- /dev/null +++ b/ansible/ansible.sh @@ -0,0 +1,66 @@ +#!/usr/bin/env bash + +set -euo pipefail + +# Minimal wrapper to run Ansible with Cloudflare vars resolved via Doppler. +# Usage: +# bash ansible.sh + +HERE="$(cd "$(dirname "$0")" && pwd)" + +require_bin() { + command -v "$1" >/dev/null 2>&1 || { + echo "error: required command not found: $1" >&2 + exit 1 + } +} + +require_env() { + local name=$1 + if [[ -z "${!name:-}" ]]; then + echo "error: required env var not set: $name" >&2 + exit 1 + fi +} + +get_secret() { + local key=$1 + # Uses DOPPLER_TOKEN from environment; Doppler CLI will read it. + # --plain prints only the value with a trailing newline, strip it. + local val + if ! val=$(doppler secrets get "$key" --plain 2>/dev/null); then + echo "error: failed to fetch secret '$key' from Doppler" >&2 + exit 1 + fi + printf %s "$val" +} + +main() { + require_bin ansible-playbook + require_bin doppler + require_env DOPPLER_TOKEN + + # Always run from the ansible dir so relative paths match + cd "$HERE" + + ansible-playbook \ + -e "cloudflare_api_token=$(get_secret CF_API_TOKEN)" \ + -e "cloudflare_zone_id=$(get_secret CF_ZONE_ID)" \ + -e "cloudflare_email=$(get_secret LE_EMAIL)" \ + -e "le_email=$(get_secret LE_EMAIL)" \ + -e "gcloud_sa_email=$(get_secret GOOGLE_APPLICATION_CREDENTIALS_EMAIL)" \ + -e "gcloud_sa_key_json=$(get_secret GCS_KEY_ENCODED)" \ + -e "gcloud_registry_host=$(get_secret GOOGLE_CLOUD_ARTIFACT_REGISTRY_URI)" \ + -e "gcs_key_encoded=$(get_secret GCS_KEY_ENCODED)" \ + -e "hook_token=$(get_secret HOOK_TOKEN)" \ + -e "hook_uri=$(get_secret HOOK_URI)" \ + -e "redis_password=$(get_secret REDIS_PASSWORD)" \ + -e "rediscloud_url=$(get_secret REDISCLOUD_URL)" \ + -e "replreg_host=$(get_secret REPLREG_HOST)" \ + -e "replreg_secret=$(get_secret REPLREG_SECRET)" \ + -e "registry_url=$(get_secret REGISTRY_URL)" \ + playbooks/site.yml +} + +main "$@" + diff --git a/ansible/inventory/group_vars/all.yml b/ansible/inventory/group_vars/all.yml new file mode 100644 index 0000000..4881504 --- /dev/null +++ b/ansible/inventory/group_vars/all.yml @@ -0,0 +1,45 @@ +--- +# Domain to provision +domain_name: ttl.sh + +# Compute zone name from domain (ttl.sh) +zone_name: "ttl.sh" + +# Cloudflare details +cloudflare_zone_id: "_in_doppler" +cloudflare_api_token: "_in_doppler" +cloudflare_email: "_in_doppler" +cloudflare_proxied: false + +# A record target +target_ip: 178.156.198.215 + +# Let's Encrypt contact email (using provided email) +le_email: "_in_doppler" + +# Repo path (relative to repo root) for static assets; easy to change later +static_site_repo_path: static + +# Google Cloud (gcloud) configuration +# Provide the service account email and base64-encoded JSON key to configure auth. +gcloud_sa_email: "_in_doppler" +gcloud_sa_key_json: "_in_doppler" +# Artifact Registry hostname to configure Docker for +gcloud_registry_host: "_in_doppler" + +# Docker install settings +docker_users: + - root + +# Application deployment +app_root: /opt/ttlsh + +# Docker Compose .env +gcs_key_encoded: "_in_doppler" +hook_token: "_in_doppler" +hook_uri: "_in_doppler" +redis_password: "_in_doppler" +rediscloud_url: "_in_doppler" +replreg_host: "_in_doppler" +replreg_secret: "_in_doppler" +registry_url: "_in_doppler" \ No newline at end of file diff --git a/ansible/inventory/hosts b/ansible/inventory/hosts new file mode 100644 index 0000000..b433764 --- /dev/null +++ b/ansible/inventory/hosts @@ -0,0 +1,2 @@ +[ttlsh] +178.156.198.215 ansible_user=root diff --git a/ansible/playbooks/site.yml b/ansible/playbooks/site.yml new file mode 100644 index 0000000..e5207e4 --- /dev/null +++ b/ansible/playbooks/site.yml @@ -0,0 +1,285 @@ +--- +# Single play: run everything against the remote host +- name: Install and configure Nginx + Certbot (Cloudflare DNS-01) + hosts: ttlsh + become: true + vars: + site_dir: "/var/www/{{ domain_name }}/html" + cloudflare_credentials_path: /etc/letsencrypt/cloudflare.ini + # derive DNS record prefix, e.g. 'dev' for dev.ttl.sh + dns_record: "{{ '@' if domain_name == zone_name else domain_name[:-(zone_name|length + 1)] }}" + # repo-relative source for static assets + static_src_path: "{{ playbook_dir }}/../../{{ static_site_repo_path }}/" + pre_tasks: [] + tasks: + - name: Ensure Cloudflare API token present + ansible.builtin.assert: + that: cloudflare_api_token is defined and (cloudflare_api_token | length) > 0 + fail_msg: "cloudflare_api_token is missing." + + - name: Ensure A record for {{ domain_name }} -> {{ target_ip }} (Cloudflare) + community.general.cloudflare_dns: + zone: "{{ zone_name }}" + record: "{{ dns_record }}" + type: A + value: "{{ target_ip }}" + proxied: "{{ cloudflare_proxied }}" + api_token: "{{ cloudflare_api_token }}" + + - name: Install packages + ansible.builtin.apt: + name: + - nginx + - certbot + - python3-certbot-dns-cloudflare + update_cache: true + state: present + + - name: Ensure nginx is enabled and started + ansible.builtin.service: + name: nginx + state: started + enabled: true + + - name: Write Cloudflare credentials for Certbot + ansible.builtin.template: + src: ../templates/cloudflare.ini.j2 + dest: "{{ cloudflare_credentials_path }}" + owner: root + group: root + mode: "0600" + + - name: Check if certificate already exists + ansible.builtin.stat: + path: "/etc/letsencrypt/live/{{ domain_name }}/fullchain.pem" + register: cert_stat + + - name: Obtain certificate using DNS-01 (Cloudflare) + ansible.builtin.command: >- + certbot certonly + --agree-tos + --email {{ le_email }} + --key-type ecdsa + --elliptic-curve secp384r1 + --dns-cloudflare + --dns-cloudflare-credentials {{ cloudflare_credentials_path }} + --dns-cloudflare-propagation-seconds 60 + -d {{ domain_name }} + --non-interactive + args: + creates: "/etc/letsencrypt/live/{{ domain_name }}/fullchain.pem" + when: not cert_stat.stat.exists + + - name: Create web root + ansible.builtin.file: + path: "{{ site_dir }}" + state: directory + owner: www-data + group: www-data + mode: "0755" + recurse: true + + - name: Deploy static assets from repo + ansible.builtin.copy: + src: "{{ static_src_path }}" + dest: "{{ site_dir }}/" + owner: www-data + group: www-data + mode: "0644" + directory_mode: "0755" + + - name: Remove legacy test index.html if present + ansible.builtin.file: + path: "{{ site_dir }}/index.html" + state: absent + + - name: Drop Nginx site config (TLS enabled) + ansible.builtin.template: + src: ../templates/nginx_site.conf.j2 + dest: "/etc/nginx/sites-available/{{ domain_name }}" + owner: root + group: root + mode: "0644" + + - name: Disable default site if present (available) + ansible.builtin.file: + path: /etc/nginx/sites-available/default + state: absent + + - name: Disable default site if present (enabled) + ansible.builtin.file: + path: /etc/nginx/sites-enabled/default + state: absent + + - name: Enable {{ domain_name }} site + ansible.builtin.file: + src: "/etc/nginx/sites-available/{{ domain_name }}" + dest: "/etc/nginx/sites-enabled/{{ domain_name }}" + state: link + force: true + + - name: Test nginx configuration + ansible.builtin.command: nginx -t + register: nginx_test + changed_when: false + + - name: Reload nginx + ansible.builtin.service: + name: nginx + state: reloaded + + - name: Ensure nginx reloads on renewal + ansible.builtin.copy: + dest: /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh + content: | + #!/bin/sh + /bin/systemctl reload nginx + owner: root + group: root + mode: "0755" + + - name: Add cron to renew twice daily + ansible.builtin.cron: + name: "Certbot renew" + user: root + minute: "0" + hour: "*/12" + job: "/usr/bin/certbot renew -q" + + # --- Google Cloud CLI install and configuration --- + - name: Install Google Cloud CLI (role) + ansible.builtin.include_role: + name: google.cloud.gcloud + + - name: Ensure gcloud key directory exists + ansible.builtin.file: + path: /root/.gcloud + state: directory + owner: root + group: root + mode: "0700" + when: (gcloud_sa_email | length) > 0 and (gcloud_sa_key_json | length) > 0 + + - name: Write GCP service account key (decoded from base64) + ansible.builtin.copy: + dest: /root/.gcloud/sa.json + content: "{{ gcloud_sa_key_json | b64decode }}" + owner: root + group: root + mode: "0600" + when: (gcloud_sa_email | length) > 0 and (gcloud_sa_key_json | length) > 0 + + - name: Check existing gcloud authenticated accounts + ansible.builtin.command: gcloud auth list --format=value(account) + register: gcloud_auth_list + changed_when: false + failed_when: false + environment: + CLOUDSDK_CORE_DISABLE_PROMPTS: "1" + when: (gcloud_sa_email | length) > 0 and (gcloud_sa_key_json | length) > 0 + + - name: Activate service account for gcloud + ansible.builtin.command: >- + gcloud auth activate-service-account {{ gcloud_sa_email }} + --key-file=/root/.gcloud/sa.json --quiet + environment: + CLOUDSDK_CORE_DISABLE_PROMPTS: "1" + when: (gcloud_sa_email | length) > 0 and (gcloud_sa_key_json | length) > 0 and (gcloud_sa_email not in (gcloud_auth_list.stdout_lines | default([]))) + + - name: Configure Docker auth for Artifact Registry + ansible.builtin.command: >- + gcloud auth configure-docker {{ gcloud_registry_host }} --quiet + environment: + CLOUDSDK_CORE_DISABLE_PROMPTS: "1" + when: (gcloud_sa_email | length) > 0 and (gcloud_sa_key_json | length) > 0 + + # --- Docker Engine + Compose install --- + - name: Install Docker prereqs + ansible.builtin.apt: + name: + - ca-certificates + - curl + - gnupg + - lsb-release + state: present + update_cache: true + + - name: Ensure keyrings directory exists + ansible.builtin.file: + path: /etc/apt/keyrings + state: directory + mode: "0755" + + - name: Add Docker GPG key + ansible.builtin.get_url: + url: https://download.docker.com/linux/ubuntu/gpg + dest: /etc/apt/keyrings/docker.asc + mode: "0644" + force: true + + - name: Determine deb arch for Docker repo + ansible.builtin.set_fact: + docker_deb_arch: >- + {{ 'arm64' if ansible_architecture in ['aarch64', 'arm64'] else 'amd64' }} + + - name: Add Docker apt repository + ansible.builtin.apt_repository: + repo: >- + deb [arch={{ docker_deb_arch }} signed-by=/etc/apt/keyrings/docker.asc] + https://download.docker.com/linux/ubuntu {{ ansible_distribution_release }} stable + filename: docker + state: present + update_cache: true + + - name: Install Docker Engine and Compose plugin + ansible.builtin.apt: + name: + - docker-ce + - docker-ce-cli + - containerd.io + - docker-buildx-plugin + - docker-compose-plugin + state: present + + - name: Enable and start Docker + ansible.builtin.service: + name: docker + state: started + enabled: true + + - name: Add users to docker group + ansible.builtin.user: + name: "{{ item }}" + groups: docker + append: true + loop: "{{ docker_users | default([]) }}" + ignore_errors: true + + # --- Deploy docker-compose app and start services --- + - name: Ensure app root exists + ansible.builtin.file: + path: "{{ app_root }}" + state: directory + mode: "0755" + + - name: Copy docker-compose.yaml to host + ansible.builtin.copy: + src: "{{ playbook_dir }}/../../docker-compose.yaml" + dest: "{{ app_root }}/docker-compose.yaml" + mode: "0644" + + - name: Render .env file + ansible.builtin.template: + src: ../templates/env.j2 + dest: "{{ app_root }}/.env" + mode: "0600" + + - name: Pull images + ansible.builtin.command: docker compose -f {{ app_root }}/docker-compose.yaml pull + args: + chdir: "{{ app_root }}" + + - name: Start services + ansible.builtin.command: docker compose -f {{ app_root }}/docker-compose.yaml up -d + args: + chdir: "{{ app_root }}" diff --git a/ansible/requirements.yml b/ansible/requirements.yml new file mode 100644 index 0000000..6350bdf --- /dev/null +++ b/ansible/requirements.yml @@ -0,0 +1,5 @@ +--- +collections: + - name: community.general + - name: google.cloud + - name: community.docker diff --git a/ansible/templates/cloudflare.ini.j2 b/ansible/templates/cloudflare.ini.j2 new file mode 100644 index 0000000..4e7d9ac --- /dev/null +++ b/ansible/templates/cloudflare.ini.j2 @@ -0,0 +1 @@ +dns_cloudflare_api_token = {{ cloudflare_api_token }} diff --git a/ansible/templates/env.j2 b/ansible/templates/env.j2 new file mode 100644 index 0000000..f6fc769 --- /dev/null +++ b/ansible/templates/env.j2 @@ -0,0 +1,8 @@ +GCS_KEY_ENCODED={{ gcs_key_encoded }} +HOOK_TOKEN={{ hook_token }} +HOOK_URI={{ hook_uri }} +REDIS_PASSWORD={{ redis_password }} +REDISCLOUD_URL={{ rediscloud_url }} +REPLREG_HOST={{ replreg_host }} +REPLREG_SECRET={{ replreg_secret }} +REGISTRY_URL={{ registry_url }} diff --git a/ansible/templates/nginx_site.conf.j2 b/ansible/templates/nginx_site.conf.j2 new file mode 100644 index 0000000..9a841f1 --- /dev/null +++ b/ansible/templates/nginx_site.conf.j2 @@ -0,0 +1,47 @@ +server { + listen 80; + server_name {{ domain_name }}; + return 301 https://$host$request_uri; +} + +server { + listen 443 ssl http2; + server_name {{ domain_name }}; + + ssl_certificate /etc/letsencrypt/live/{{ domain_name }}/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/{{ domain_name }}/privkey.pem; + ssl_session_timeout 1d; + ssl_session_cache shared:SSL:10m; + ssl_protocols TLSv1.2 TLSv1.3; + ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384; + + root /var/www/{{ domain_name }}/html; + index index.htm index.html; + + # Static site + location / { + try_files $uri $uri/ =404; + } + + # Block listing catalog + location ~ v2/_catalog$ { + return 401; + } + + # Docker Registry proxy + location /v2 { + client_max_body_size 10000m; + client_body_timeout 3600s; + proxy_connect_timeout 300s; + proxy_send_timeout 3600s; + proxy_read_timeout 3600s; + send_timeout 3600s; + proxy_pass http://127.0.0.1:5000; + proxy_set_header X-Forwarded-Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header Host $host; + proxy_request_buffering off; + } +} diff --git a/docker-compose.yaml b/docker-compose.yaml index 3960dcc..be53a3e 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -69,26 +69,6 @@ services: - ./redis/data:/data command: redis-server --requirepass ${REDIS_PASSWORD} - nginx: - image: us-east4-docker.pkg.dev/rgstry/ttl-sh-nginx/nginx:latest - build: - context: nginx - dockerfile: Dockerfile - platforms: - - "linux/amd64" - container_name: ttlsh-nginx - restart: always - pull_policy: always - environment: - - PORT=80 - ports: - - "80:80" - - "443:443" - depends_on: - - registry - networks: - - ttlsh-network - networks: ttlsh-network: driver: bridge diff --git a/install.sh b/install.sh deleted file mode 100755 index a0b7eae..0000000 --- a/install.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env bash - -REMOTE_HOST=178.156.198.215 -REMOTE_USER=root -INSTALL_DIR=/opt/ttlsh - -function setup_docker_compose() { - ssh $REMOTE_USER@$REMOTE_HOST "mkdir -p $INSTALL_DIR" - scp docker-compose.yaml $REMOTE_USER@$REMOTE_HOST:$INSTALL_DIR/docker-compose.yaml -} - -function setup_provisioner_script() { - scp provision.sh $REMOTE_USER@$REMOTE_HOST:$INSTALL_DIR/provision.sh - ssh $REMOTE_USER@$REMOTE_HOST "chmod a+x $INSTALL_DIR/provision.sh" -} - -function run_provisioner_script() { - ssh $REMOTE_USER@$REMOTE_HOST "bash $INSTALL_DIR/provision.sh $1" -} - -function install() { - setup_docker_compose - setup_provisioner_script - run_provisioner_script $DOPPLER_TOKEN -} - -install \ No newline at end of file diff --git a/nginx/Dockerfile b/nginx/Dockerfile deleted file mode 100644 index 37bd5fd..0000000 --- a/nginx/Dockerfile +++ /dev/null @@ -1,12 +0,0 @@ -FROM nginx:latest - -COPY nginx.conf /etc/nginx/nginx.conf - -COPY src/favicon.ico /usr/share/nginx/html/ -COPY src/index.htm /usr/share/nginx/html/ -COPY src/ttlsh_files /usr/share/nginx/html/ttlsh_files/ - -ADD entrypoint.sh /entrypoint.sh - -ENTRYPOINT ["/entrypoint.sh"] -CMD ["nginx", "-g", "daemon off;"] \ No newline at end of file diff --git a/nginx/entrypoint.sh b/nginx/entrypoint.sh deleted file mode 100755 index 4616af1..0000000 --- a/nginx/entrypoint.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/sh - -set -e - -sed -i "s/__PORT__/$PORT/g" /etc/nginx/nginx.conf - -exec "$@" diff --git a/nginx/nginx.conf b/nginx/nginx.conf deleted file mode 100644 index 4d4b18b..0000000 --- a/nginx/nginx.conf +++ /dev/null @@ -1,39 +0,0 @@ -events {} -http { - include mime.types; - default_type application/octet-stream; - - upstream registry { - server ttlsh-registry:5000; - } - - server { - listen __PORT__; - server_name _; - - location / { - root /usr/share/nginx/html; - index index.htm; - } - - location ~ v2/_catalog$ { - return 401; - } - - location /v2 { - client_max_body_size 10000m; - client_body_timeout 3600s; - proxy_connect_timeout 300s; - proxy_send_timeout 3600s; - proxy_read_timeout 3600s; - send_timeout 3600s; - proxy_pass http://registry; - proxy_set_header X-Forwarded-Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_set_header Host $host; - proxy_request_buffering off; - } - } -} diff --git a/provision.sh b/provision.sh deleted file mode 100644 index 9a98b35..0000000 --- a/provision.sh +++ /dev/null @@ -1,94 +0,0 @@ -#!/usr/bin/env bash - -DOPPLER_TOKEN=$1 - -function setup_doppler() { - # install for ubuntu: https://docs.doppler.com/docs/cli - sudo apt-get update && sudo apt-get install -y apt-transport-https ca-certificates curl gnupg - curl -sLf --retry 3 --tlsv1.2 --proto "=https" 'https://packages.doppler.com/public/cli/gpg.DE2A7741A397C129.key' | sudo gpg --dearmor -o /usr/share/keyrings/doppler-archive-keyring.gpg - echo "deb [signed-by=/usr/share/keyrings/doppler-archive-keyring.gpg] https://packages.doppler.com/public/cli/deb/debian any-version main" | sudo tee /etc/apt/sources.list.d/doppler-cli.list - sudo apt-get update && sudo apt-get install doppler - - # authenticate with service token - echo $DOPPLER_TOKEN | doppler configure set token --scope / -} - -setup_docker() { - # Adapted from: - # * https://docs.docker.com/engine/install/ubuntu/#install-using-the-repository - - # Add Docker's official GPG key: - sudo apt-get update - sudo apt-get install ca-certificates curl - sudo install -m 0755 -d /etc/apt/keyrings - sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc - sudo chmod a+r /etc/apt/keyrings/docker.asc - - # Add the repository to Apt sources: - echo \ - "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \ - $(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}") stable" | \ - sudo tee /etc/apt/sources.list.d/docker.list > /dev/null - sudo apt-get update - - # install necessary packages - sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin -y -} - -setup_gcloud() { - # Adapted from: - # * https://cloud.google.com/sdk/docs/downloads-interactive - # * Artifact Registry setup commands - - INSTALL_PATH="$(doppler secrets get GOOGLE_APPLICATION_CREDENTIALS --plain)" - INSTALL_EMAIL="$(doppler secrets get GOOGLE_APPLICATION_CREDENTIALS_EMAIL --plain)" - REGISTRY_URI="$(doppler secrets get GOOGLE_CLOUD_ARTIFACT_REGISTRY_URI --plain)" - - mkdir -p "$(dirname "$INSTALL_PATH")" - doppler secrets get GOOGLE_APPLICATION_CREDENTIALS_JSON --plain >"$INSTALL_PATH" - chmod 600 "$INSTALL_PATH" - - echo "export GOOGLE_APPLICATION_CREDENTIALS=\"$INSTALL_PATH\"" >> "$HOME/.bashrc" - - # download the installer - curl https://sdk.cloud.google.com | bash - - # Explicitly add gcloud to PATH for this session - export PATH="$HOME/google-cloud-sdk/bin:$PATH" - - gcloud auth activate-service-account "$INSTALL_EMAIL" --key-file="$INSTALL_PATH" --quiet - gcloud auth configure-docker "$REGISTRY_URI" --quiet -} - -setup_ttlsh_env() { - for VAR in \ - GCS_KEY_ENCODED \ - HOOK_TOKEN \ - HOOK_URI \ - REDIS_PASSWORD \ - REDISCLOUD_URL \ - REPLREG_HOST \ - REPLREG_SECRET \ - REGISTRY_URL - do - echo "$VAR=$(doppler secrets get "$VAR" --plain)" >> /opt/ttlsh/.env - done - - chmod 600 "$ENV_FILE" -} - -function run_ttlsh() -{ - docker compose -f /opt/ttlsh/docker-compose.yaml pull - docker compose -f /opt/ttlsh/docker-compose.yaml up -d -} - -function provision() { - setup_doppler - setup_docker - setup_gcloud - setup_ttlsh_env - run_ttlsh -} - -provision \ No newline at end of file diff --git a/nginx/src/favicon.ico b/static/favicon.ico similarity index 100% rename from nginx/src/favicon.ico rename to static/favicon.ico diff --git a/nginx/src/index.htm b/static/index.htm similarity index 100% rename from nginx/src/index.htm rename to static/index.htm diff --git a/nginx/src/ttlsh_files/AllTemplates.min.css b/static/ttlsh_files/AllTemplates.min.css similarity index 100% rename from nginx/src/ttlsh_files/AllTemplates.min.css rename to static/ttlsh_files/AllTemplates.min.css diff --git a/nginx/src/ttlsh_files/css b/static/ttlsh_files/css similarity index 100% rename from nginx/src/ttlsh_files/css rename to static/ttlsh_files/css