├── .gitignore ├── README.md ├── ansible.cfg ├── group_vars ├── all.yaml ├── demobox.yaml └── prodbox-nyc.yaml ├── hosts.ini ├── justfile ├── requirements.yaml ├── runme.yaml └── vars └── vault.yaml /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.toptal.com/developers/gitignore/api/visualstudiocode,git,python,ansible 2 | # Edit at https://www.toptal.com/developers/gitignore?templates=visualstudiocode,git,python,ansible 3 | 4 | ### Ansible ### 5 | *.retry 6 | galaxy_roles/ 7 | .vault-password 8 | 9 | ### Git ### 10 | # Created by git for backups. To disable backups in Git: 11 | # $ git config --global mergetool.keepBackup false 12 | *.orig 13 | 14 | # Created by git when using merge tools for conflicts 15 | *.BACKUP.* 16 | *.BASE.* 17 | *.LOCAL.* 18 | *.REMOTE.* 19 | *_BACKUP_*.txt 20 | *_BASE_*.txt 21 | *_LOCAL_*.txt 22 | *_REMOTE_*.txt 23 | 24 | ### Python ### 25 | # Byte-compiled / optimized / DLL files 26 | __pycache__/ 27 | *.py[cod] 28 | *$py.class 29 | 30 | # C extensions 31 | *.so 32 | 33 | # Distribution / packaging 34 | .Python 35 | build/ 36 | develop-eggs/ 37 | dist/ 38 | downloads/ 39 | eggs/ 40 | .eggs/ 41 | lib/ 42 | lib64/ 43 | parts/ 44 | sdist/ 45 | var/ 46 | wheels/ 47 | share/python-wheels/ 48 | *.egg-info/ 49 | .installed.cfg 50 | *.egg 51 | MANIFEST 52 | 53 | # PyInstaller 54 | # Usually these files are written by a python script from a template 55 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 56 | *.manifest 57 | *.spec 58 | 59 | # Installer logs 60 | pip-log.txt 61 | pip-delete-this-directory.txt 62 | 63 | # Unit test / coverage reports 64 | htmlcov/ 65 | .tox/ 66 | .nox/ 67 | .coverage 68 | .coverage.* 69 | .cache 70 | nosetests.xml 71 | coverage.xml 72 | *.cover 73 | *.py,cover 74 | .hypothesis/ 75 | .pytest_cache/ 76 | cover/ 77 | 78 | # Translations 79 | *.mo 80 | *.pot 81 | 82 | # Django stuff: 83 | *.log 84 | local_settings.py 85 | db.sqlite3 86 | db.sqlite3-journal 87 | 88 | # Flask stuff: 89 | instance/ 90 | .webassets-cache 91 | 92 | # Scrapy stuff: 93 | .scrapy 94 | 95 | # Sphinx documentation 96 | docs/_build/ 97 | 98 | # PyBuilder 99 | .pybuilder/ 100 | target/ 101 | 102 | # Jupyter Notebook 103 | .ipynb_checkpoints 104 | 105 | # IPython 106 | profile_default/ 107 | ipython_config.py 108 | 109 | # pyenv 110 | # For a library or package, you might want to ignore these files since the code is 111 | # intended to run in multiple environments; otherwise, check them in: 112 | # .python-version 113 | 114 | # pipenv 115 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 116 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 117 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 118 | # install all needed dependencies. 119 | #Pipfile.lock 120 | 121 | # poetry 122 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 123 | # This is especially recommended for binary packages to ensure reproducibility, and is more 124 | # commonly ignored for libraries. 125 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 126 | #poetry.lock 127 | 128 | # pdm 129 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. 130 | #pdm.lock 131 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it 132 | # in version control. 133 | # https://pdm.fming.dev/#use-with-ide 134 | .pdm.toml 135 | 136 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm 137 | __pypackages__/ 138 | 139 | # Celery stuff 140 | celerybeat-schedule 141 | celerybeat.pid 142 | 143 | # SageMath parsed files 144 | *.sage.py 145 | 146 | # Environments 147 | .env 148 | .venv 149 | env/ 150 | venv/ 151 | ENV/ 152 | env.bak/ 153 | venv.bak/ 154 | 155 | # Spyder project settings 156 | .spyderproject 157 | .spyproject 158 | 159 | # Rope project settings 160 | .ropeproject 161 | 162 | # mkdocs documentation 163 | /site 164 | 165 | # mypy 166 | .mypy_cache/ 167 | .dmypy.json 168 | dmypy.json 169 | 170 | # Pyre type checker 171 | .pyre/ 172 | 173 | # pytype static type analyzer 174 | .pytype/ 175 | 176 | # Cython debug symbols 177 | cython_debug/ 178 | 179 | # PyCharm 180 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 181 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 182 | # and can be added to the global gitignore or merged into this file. For a more nuclear 183 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 184 | #.idea/ 185 | 186 | ### Python Patch ### 187 | # Poetry local configuration file - https://python-poetry.org/docs/configuration/#local-configuration 188 | poetry.toml 189 | 190 | # ruff 191 | .ruff_cache/ 192 | 193 | # LSP config files 194 | pyrightconfig.json 195 | 196 | ### VisualStudioCode ### 197 | .vscode/* 198 | !.vscode/settings.json 199 | !.vscode/tasks.json 200 | !.vscode/launch.json 201 | !.vscode/extensions.json 202 | !.vscode/*.code-snippets 203 | 204 | # Local History for Visual Studio Code 205 | .history/ 206 | 207 | # Built Visual Studio Code Extensions 208 | *.vsix 209 | 210 | ### VisualStudioCode Patch ### 211 | # Ignore all local history of files 212 | .history 213 | .ionide 214 | 215 | # End of https://www.toptal.com/developers/gitignore/api/visualstudiocode,git,python,ansible 216 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # tailscale-dev/devrel-demo-infra 2 | 3 | This repo contains Ansible code used to configure the backend of demos used by Dev Rel at events, etc. 4 | 5 | ## How to use 6 | 7 | Firstly, this repo probably isn't really designed to operated outside of the devrel team but here's some documentation around it anyways. It assumes you're deploying the [id-headers-demo](https://github.com/pango-lin-tailnet/id-headers-demo) but you can deploy anything else you'd like using docker using this method too. 8 | 9 | > You will need [casey/just](https://github.com/casey/just) as this runs wrappers around ansible commands for you. 10 | 11 | In `groups_vars/demobox.yaml` define the containers you want to run. For example: 12 | 13 | ``` 14 | containers: 15 | - service_name: id-demo-test 16 | active: true 17 | image: ghcr.io/tailscale-dev/devrel-demo-infra:test 18 | volumes: 19 | - "{{ appdata_path }}/id-demo-test:/var/lib/tailscale" 20 | environment: 21 | - "TAILSCALE_AUTHKEY={{ tsauthkey_id_demo_test }}" 22 | - TAILSCALE_HOSTNAME=id-demo-docker 23 | - TAILSCALE_FUNNEL=on 24 | - DEMO_INVITE_LINK=https://invite.link.from.tailscale.admin.console.after.node.added.to.tailnet.com 25 | include_global_env_vars: true 26 | restart: unless-stopped 27 | ``` 28 | 29 | + `image: ghcr.io/tailscale-dev/devrel-demo-infra:test` = This can be any docker image, but here we assume each demo has something unique so push your unique image to a unique tag. 30 | + `volumes:` it is important that each container instance has its own volume as this is used to store tailscale daemon state. 31 | + `TAILSCALE_AUTHKEY` = The authkey is encrypted in `vars/vault.yaml`. To add a new key run `just vault decrypt`, edit `vault.yaml` and then `just vault encrypt`. 32 | + `TAILSCALE_HOSTNAME` = This is the hostname that funnel/serve will use. E.g. your demo will be at `id-demo-docker.pango-lin.ts.net`. 33 | + `TAILSCALE_FUNNEL` = Turns funnel on or off. 34 | 35 | Once happy with your edits, run the command `just compose demobox`. Under the hood, Ansible will run and ingest your configuration spitting out a `docker-compose.yaml` file in the home dir of the ssh user. 36 | 37 | ``` 38 | # run on your local dev machine 39 | ## configure the whole node 40 | just run demobox 41 | 42 | ## update just the docker-compose file 43 | just compose demobox 44 | ``` 45 | 46 | Once the automation is run it is up to the user to manually start the new containers on the remote host `dcp pull` followed by `dcp up -d` (`dcp` is an alias for `docker-compose`). 47 | 48 | ``` 49 | # run on the remote node demobox 50 | ## pulls the latest images 51 | dcp pull 52 | 53 | ## starts the containers 54 | dcp up -d 55 | ``` 56 | 57 | # Resources 58 | - https://tailscale.dev/blog/docker-mod-tailscale 59 | -------------------------------------------------------------------------------- /ansible.cfg: -------------------------------------------------------------------------------- 1 | [defaults] 2 | nocows = 1 3 | host_key_checking = False 4 | retry_files_enabled = False 5 | roles_path = $PWD/galaxy_roles:$PWD/roles 6 | inventory = ./hosts.ini 7 | become_ask_pass = True 8 | vault_password_file = ./.vault-password 9 | stdout_callback = yaml 10 | bin_ansible_callbacks = True 11 | 12 | [ssh_connection] 13 | pipelining = True -------------------------------------------------------------------------------- /group_vars/all.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # grog.package 3 | package_list: 4 | - name: bash-completion 5 | - name: curl 6 | - name: htop 7 | - name: ncdu 8 | - name: net-tools 9 | - name: nmap 10 | - name: sudo 11 | - name: tmux 12 | - name: tree 13 | - name: wget 14 | - name: zfsutils-linux 15 | 16 | # geerlingguy.ntp 17 | ntp_timezone: "America/New_York" 18 | 19 | # ironicbadger.bash-aliases (formerly ferrarimarco.bash-aliases) 20 | bash_aliases: 21 | - { alias: "dtail", command: "docker logs -tf --tail='50' " } 22 | - { alias: "dstop", command: "docker stop `docker ps -aq`" } 23 | - { alias: "drm", command: "docker rm `docker ps -aq`" } 24 | - { alias: "dcp", command: "docker-compose -f ~/docker-compose.yml " } 25 | - { alias: "dprune", command: "docker image prune" } 26 | - { alias: "dprunesys", command: "docker system prune --all" } 27 | - { alias: "dtop", command: "docker run --name ctop -it --rm -v /var/run/docker.sock:/var/run/docker.sock quay.io/vektorlab/ctop" } 28 | - { alias: "appdata", command: "cd /mnt/tank/appdata" } 29 | - { alias: "zspace", command: "zfs list -o space" } 30 | - { alias: "zsnap", command: "zfs list -t snapshot" } 31 | 32 | # artis3n.tailscale 33 | tailscale_up_skip: true 34 | 35 | # ironicbadger.docker_compose_generator 36 | main_uid: "1000" 37 | main_gid: "{{ main_uid }}" 38 | global_env_vars: 39 | - "PUID={{ main_uid }}" 40 | - "PGID={{ main_gid }}" 41 | - "TZ={{ ntp_timezone }}" 42 | - DOCKER_MODS=ghcr.io/tailscale-dev/docker-mod:main 43 | - TAILSCALE_STATE_DIR=/var/lib/tailscale 44 | - TAILSCALE_SERVE_MODE=https 45 | - TAILSCALE_SERVE_PORT=5000 46 | - TAILSCALE_USE_SSH=0 -------------------------------------------------------------------------------- /group_vars/demobox.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | # ironicbadger.docker_compose_generator 4 | appdata_path: /mnt/zfs/appdata 5 | containers: 6 | ######################## 7 | ## Copy this block and modify for each additional demo required 8 | ######################## 9 | ## Lead Dev Berlin - Dec 2023 10 | - service_name: id-demo-leaddev-berlin 11 | active: true 12 | image: ghcr.io/tailscale-dev/demo-id-headers:leaddev-berlin 13 | volumes: 14 | - "{{ appdata_path }}/2023/leaddev-berlin:/var/lib/tailscale" 15 | environment: 16 | - "TAILSCALE_AUTHKEY={{ tsauthkey_devrel }}" 17 | - TAILSCALE_HOSTNAME=berlin2023 18 | - TAILSCALE_FUNNEL=on 19 | - "DEMO_INVITE_LINK={{ leaddev_berlin_invite_link }}" 20 | include_global_env_vars: true 21 | restart: unless-stopped -------------------------------------------------------------------------------- /group_vars/prodbox-nyc.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | # ironicbadger.docker_compose_generator 4 | appdata_path: /mnt/zfs/appdata 5 | containers: 6 | - service_name: traefik 7 | active: true 8 | image: traefik 9 | volumes: 10 | - "/opt/appdata/traefik/letsencrypt:/letsencrypt" 11 | - /var/run/docker.sock:/var/run/docker.sock:ro 12 | command: 13 | #- "--log.level=DEBUG" 14 | - "--api.insecure=false" 15 | - "--providers.docker=true" 16 | - "--providers.docker.exposedbydefault=false" 17 | - "--entrypoints.web.address=:80" 18 | - "--entrypoints.web.http.redirections.entryPoint.to=websecure" 19 | - "--entrypoints.web.http.redirections.entryPoint.scheme=https" 20 | - "--entrypoints.websecure.address=:443" 21 | - "--certificatesresolvers.tsresolver.acme.httpchallenge=true" 22 | - "--certificatesresolvers.tsresolver.acme.httpchallenge.entrypoint=web" 23 | #- "--certificatesresolvers.tsresolver.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory" 24 | - "--certificatesresolvers.tsresolver.acme.email=infra+tailandscales@tailscale.com" 25 | - "--certificatesresolvers.tsresolver.acme.storage=/letsencrypt/acme.json" 26 | ports: 27 | - "80:80" 28 | - "443:443" 29 | include_global_env_vars: false 30 | restart: unless-stopped 31 | - service_name: tailandscalesdotcom 32 | active: true 33 | image: ghcr.io/tailscale-dev/society-of-pangolin-enthusiasts:latest 34 | labels: 35 | - "traefik.enable=true" 36 | - "traefik.http.routers.tscom.rule=Host(`tailandscales.com`)" 37 | - "traefik.http.routers.tscom.entrypoints=websecure" 38 | - "traefik.http.routers.tscom.tls.certresolver=tsresolver" 39 | - "traefik.http.services.tscom.loadbalancer.server.port=8080" 40 | ## DO NOT MODIFY ABOVE THIS LINE 41 | ######################## 42 | ## Copy this block and modify for each additional demo required 43 | ######################## 44 | ## DoD Austin 2024 45 | - service_name: id-demo-dodaustin2024 46 | active: true 47 | image: ghcr.io/tailscale-dev/demo-id-headers:dodaustin 48 | volumes: 49 | - "{{ appdata_path }}/2024/dod2024:/var/lib/tailscale" 50 | environment: 51 | - "TAILSCALE_AUTHKEY={{ tsauthkey_devrel }}" 52 | - TAILSCALE_HOSTNAME=dod2024 53 | - TAILSCALE_FUNNEL=on 54 | - "DEMO_INVITE_LINK={{ dod2024_invite_link }}" 55 | include_global_env_vars: true 56 | restart: unless-stopped -------------------------------------------------------------------------------- /hosts.ini: -------------------------------------------------------------------------------- 1 | [demobox] 2 | demobox ansible_ssh_user=root 3 | 4 | [prodbox-nyc] 5 | 100.83.181.78 ansible_ssh_user=root -------------------------------------------------------------------------------- /justfile: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env -S just --justfile 2 | # ^ A shebang isn't required, but allows a justfile to be executed 3 | # like a script, with `./justfile test`, for example. 4 | 5 | #alias t := test 6 | 7 | #alias c := check 8 | 9 | bt := '0' 10 | 11 | export RUST_BACKTRACE := bt 12 | 13 | log := "warn" 14 | 15 | export JUST_LOG := log 16 | 17 | run HOST *TAGS: 18 | ansible-playbook -b runme.yaml --limit {{HOST}} {{TAGS}} 19 | 20 | compose HOST: 21 | ansible-playbook -b runme.yaml --limit {{HOST}} --tags compose 22 | 23 | ## repo stuff 24 | # optionally use --force to force reinstall all requirements 25 | reqs *FORCE: 26 | ansible-galaxy install -r requirements.yaml {{FORCE}} 27 | 28 | # just vault (encrypt/decrypt/edit) 29 | vault ACTION: 30 | EDITOR='code --wait' ansible-vault {{ACTION}} vars/vault.yaml -------------------------------------------------------------------------------- /requirements.yaml: -------------------------------------------------------------------------------- 1 | - src: geerlingguy.docker 2 | - src: geerlingguy.ntp 3 | - src: grog.package 4 | - src: artis3n.tailscale 5 | - src: ironicbadger.docker_compose_generator 6 | - src: ironicbadger.bash_aliases 7 | - src: ironicbadger.figurine -------------------------------------------------------------------------------- /runme.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - hosts: prodbox-nyc 4 | vars_files: 5 | - 'vars/vault.yaml' 6 | tasks: 7 | - hostname: 8 | name: devrel-demobox 9 | roles: 10 | - role: grog.package 11 | - role: geerlingguy.docker 12 | - role: geerlingguy.ntp 13 | - role: artis3n.tailscale 14 | - role: ironicbadger.figurine 15 | - role: ironicbadger.bash_aliases 16 | - role: ironicbadger.docker_compose_generator 17 | tags: compose 18 | -------------------------------------------------------------------------------- /vars/vault.yaml: -------------------------------------------------------------------------------- 1 | $ANSIBLE_VAULT;1.1;AES256 2 | 34303836376564646561353332303934303230613938383666326335653031326531323133393764 3 | 3165376130316562396238343264326462363561333363390a613766373963363135653234643234 4 | 35656333366261636465653939306237333762666136333830383665333363393766346432663966 5 | 3461343361626561620a656136313465653266613635646564646139316339373465303935356262 6 | 33336339613964313333363237313533353231623065346631303266653534653838366637343365 7 | 30333538363737396362373333373963393239626234383933346332623734323536366332366338 8 | 64633037373934303166663934616532353931383263663733633664346436643531353562396363 9 | 31653261386439633335623831386138323737373138306538323234636138303264376364613663 10 | 61343466333233633736333038663433343131383365316362303763623934316261643466626531 11 | 66326632386636646339636433376664633162316338373235366338373535333961623937636163 12 | 62313164623531346639623262646561663761386366346361353936343136356465313032616565 13 | 34623330363538636365 14 | --------------------------------------------------------------------------------