Browse Source

initial

remote
giles bradshaw 1 month ago
commit
2434b33665
36 changed files with 2959 additions and 0 deletions
  1. +142
    -0
      .drone copy.yml
  2. +238
    -0
      .drone.m.yml
  3. +243
    -0
      .drone.yml
  4. +3
    -0
      .gitignore
  5. +241
    -0
      README.md
  6. BIN
      add-secret.png
  7. +8
    -0
      certificates/ca.sh
  8. +4
    -0
      certificates/make-cert.sh
  9. +159
    -0
      docker-compose-do.yml
  10. +238
    -0
      docker-compose-drone.yml
  11. +370
    -0
      docker-compose.yml
  12. +33
    -0
      ghost/config.production.json
  13. +3
    -0
      guacamole-postgresql/Dockerfile
  14. +3
    -0
      guacamole-postgresql/init-user.sql
  15. +791
    -0
      guacamole-postgresql/initdb.sql
  16. +2
    -0
      letsencrypt-nginx-build.sh
  17. +3
    -0
      letsencrypt-nginx/Dockerfile
  18. +3
    -0
      letsencrypt-nginx/Dockerfile.blog
  19. +3
    -0
      letsencrypt-nginx/Dockerfile.chat
  20. +3
    -0
      letsencrypt-nginx/Dockerfile.drone
  21. +3
    -0
      letsencrypt-nginx/Dockerfile.git
  22. +3
    -0
      letsencrypt-nginx/Dockerfile.remote
  23. +3
    -0
      letsencrypt-nginx/build.sh
  24. +46
    -0
      letsencrypt-nginx/conf/blog.conf
  25. +56
    -0
      letsencrypt-nginx/conf/chat.conf
  26. +40
    -0
      letsencrypt-nginx/conf/drone.conf
  27. +109
    -0
      letsencrypt-nginx/conf/git.conf
  28. +49
    -0
      letsencrypt-nginx/conf/remote.conf
  29. +78
    -0
      letsencrypt-nginx/result
  30. +4
    -0
      letsencrypt-nginx/run.sh
  31. +2
    -0
      ngrok-build.sh
  32. +5
    -0
      ngrok2/Dockerfile
  33. +41
    -0
      ngrok2/ngrok._yml
  34. +24
    -0
      ngrok2/ngrok.m._yml
  35. +4
    -0
      ngrok2/run.sh
  36. +2
    -0
      postgres-build.sh

+ 142
- 0
.drone copy.yml View File

@@ -0,0 +1,142 @@
---

kind: pipeline
type: docker
name: default
when:
branch:
- master

clone:
# skip_verify: true

steps:
steps:
- name: wait
when:
branch:
- master
image: docker:dind
volumes:
- name: dockersock
path: /var/run

commands:
- sleep 60
- name: build-letsencrypt-chat
when:
branch:
- master
image: docker:dind
volumes:
- name: dockersock
path: /var/run
environment:
LOCAL_DOCKER_REGISTRY:
from_secret: local-docker-registry
commands:
- cd letsencrypt-nginx
- sh build.sh chat $${LOCAL_DOCKER_REGISTRY}
- name: scp files
when:
branch:
- master
image: appleboy/drone-scp
settings:
host:
from_secret: ssh-host
username:
from_secret: ssh-user
password:
from_secret: ssh-password
port:
from_secret: ssh-port
command_timeout: 2m
target: ~/gitea-drone-stack
source:
- .
- name: deploy
when:
branch:
- master
image: appleboy/drone-ssh
environment:
DRONE_RPC_SECRET:
from_secret: drone-rpc-secret
DRONE_GITEA_CLIENT_ID:
from_secret: drone-gitea-client-id
DRONE_GITEA_CLIENT_SECRET:
from_secret: drone-gitea-client-secret
LOCAL_DOCKER_REGISTRY:
from_secret: local-docker-registry
SSH_USER:
from_secret: ssh-user
CERTBOT_EMAIL:
from_secret: certbot-email
GIT_DOMAIN:
from_secret: git-domain
DRONE_DOMAIN:
from_secret: drone-domain
CHAT_DOMAIN:
from_secret: chat-domain
REMOTE_DOMAIN:
from_secret: remote-domain
BLOG_DOMAIN:
from_secret: blog-domain
settings:
envs:
- drone_rpc_secret
- drone_gitea_client_id
- drone_gitea_client_secret
- ssh_user
- local_docker_registry
- certbot_email
- git_domain
- drone_domain
- chat_domain
- remote_domain
- blog_domain
host:
from_secret: ssh-host
username:
from_secret: ssh-root-user
password:
from_secret: ssh-root-password
port:
from_secret: ssh-port
script:
- set -e
- export LOCAL_DOCKER_REGISTRY=$LOCAL_DOCKER_REGISTRY
- export DRONE_RPC_SECRET=$DRONE_RPC_SECRET
- export DRONE_GITEA_CLIENT_ID=$DRONE_GITEA_CLIENT_ID
- export DRONE_GITEA_CLIENT_SECRET=$DRONE_GITEA_CLIENT_SECRET
- export SSH_USER=$SSH_USER
- export CERTBOT_EMAIL=$CERTBOT_EMAIL
- export GIT_DOMAIN=$GIT_DOMAIN
- export DRONE_DOMAIN=$DRONE_DOMAIN
- export CHAT_DOMAIN=$CHAT_DOMAIN
- export REMOTE_DOMAIN=$REMOTE_DOMAIN
- export BLOG_DOMAIN=$BLOG_DOMAIN
- docker network prune -f
- cd /home/$SSH_USER/gitea-drone-stack
- docker stack rm gitea
- sleep 60
- docker stack deploy -c docker-compose.yml gitea
#- sleep 300

services:
- name: docker
image: docker:dind
privileged: true
volumes:
- name: dockersock
path: /var/run
- name: ca
path: /etc/docker/certs.d
volumes:
- name: dockersock
temp: {}
- name: ca
host:
path: /home/giles/gitea-drone-stack/.ca

+ 238
- 0
.drone.m.yml View File

@@ -0,0 +1,238 @@
---

kind: pipeline
type: docker
name: default
when:
branch:
- remote

clone:
# skip_verify: true

steps:
steps:
- name: test-ssh
when:
branch:
- remote
image: appleboy/drone-ssh
environment:
DRONE_RPC_SECRET:
from_secret: drone-rpc-secret
DRONE_GITEA_CLIENT_ID:
from_secret: drone-gitea-client-id
DRONE_GITEA_CLIENT_SECRET:
from_secret: drone-gitea-client-secret
LOCAL_DOCKER_REGISTRY:
from_secret: local-docker-registry
SSH_USER:
from_secret: ssh-user
CERTBOT_EMAIL:
from_secret: certbot-email
GIT_DOMAIN:
from_secret: git-domain
DRONE_DOMAIN:
from_secret: drone-domain
REMOTE_DOMAIN:
from_secret: remote-domain
settings:
envs:
- drone_rpc_secret
- drone_gitea_client_id
- drone_gitea_client_secret
- ssh_user
- local_docker_registry
- certbot_email
- git_domain
- drone_domain
- remote_domain
host:
from_secret: ssh-host
username:
from_secret: ssh-root-user
password:
from_secret: ssh-root-password
port:
from_secret: ssh-port
script:
- echo 'ssh ok'
- name: wait
when:
branch:
- remote
image: docker:dind
volumes:
- name: dockersock
path: /var/run

commands:
- sleep 60
- name: build-postgres
when:
branch:
- remote
image: docker:dind
volumes:
- name: dockersock
path: /var/run
environment:
LOCAL_DOCKER_REGISTRY:
from_secret: local-docker-registry
commands:
- cd guacamole-postgresql
- docker build . -t $${LOCAL_DOCKER_REGISTRY}/guacamole-postgresql
- docker push $${LOCAL_DOCKER_REGISTRY}/guacamole-postgresql
- name: build-ngrok
when:
branch:
- remote
image: docker:dind
volumes:
- name: dockersock
path: /var/run
environment:
LOCAL_DOCKER_REGISTRY:
from_secret: local-docker-registry
commands:
- cd ngrok2
- docker build . -t $${LOCAL_DOCKER_REGISTRY}/ngrok-gitea
- docker push $${LOCAL_DOCKER_REGISTRY}/ngrok-gitea
- name: build-letsencrypt-nginx
when:
branch:
- remote
image: docker:dind
volumes:
- name: dockersock
path: /var/run
environment:
LOCAL_DOCKER_REGISTRY:
from_secret: local-docker-registry
commands:
- cd letsencrypt-nginx
- docker build . -t $${LOCAL_DOCKER_REGISTRY}/letsencrypt-nginx
- docker push $${LOCAL_DOCKER_REGISTRY}/letsencrypt-nginx
- name: build-letsencrypt-drone
when:
branch:
- master
image: docker:dind
volumes:
- name: dockersock
path: /var/run
environment:
LOCAL_DOCKER_REGISTRY:
from_secret: local-docker-registry
commands:
- cd letsencrypt-nginx
- sh build.sh drone $${LOCAL_DOCKER_REGISTRY}
- name: build-letsencrypt-remote
when:
branch:
- master
image: docker:dind
volumes:
- name: dockersock
path: /var/run
environment:
LOCAL_DOCKER_REGISTRY:
from_secret: local-docker-registry
commands:
- cd letsencrypt-nginx
- sh build.sh remote $${LOCAL_DOCKER_REGISTRY}
- name: scp files
when:
branch:
- remote
image: appleboy/drone-scp
settings:
host:
from_secret: ssh-host
username:
from_secret: ssh-user
password:
from_secret: ssh-password
port:
from_secret: ssh-port
command_timeout: 2m
target: ~/gitea-drone-stack
source:
- .
- name: deploy
when:
branch:
- remote
image: appleboy/drone-ssh
environment:
DRONE_RPC_SECRET:
from_secret: drone-rpc-secret
DRONE_GITEA_CLIENT_ID:
from_secret: drone-gitea-client-id
DRONE_GITEA_CLIENT_SECRET:
from_secret: drone-gitea-client-secret
LOCAL_DOCKER_REGISTRY:
from_secret: local-docker-registry
SSH_USER:
from_secret: ssh-user
CERTBOT_EMAIL:
from_secret: certbot-email
GIT_DOMAIN:
from_secret: git-domain
DRONE_DOMAIN:
from_secret: drone-domain
REMOTE_DOMAIN:
from_secret: remote-domain
settings:
envs:
- drone_rpc_secret
- drone_gitea_client_id
- drone_gitea_client_secret
- ssh_user
- local_docker_registry
- certbot_email
- git_domain
- drone_domain
- remote_domain
host:
from_secret: ssh-host
username:
from_secret: ssh-root-user
password:
from_secret: ssh-root-password
port:
from_secret: ssh-port
script:
- set -e
- export LOCAL_DOCKER_REGISTRY=$LOCAL_DOCKER_REGISTRY
- export DRONE_RPC_SECRET=$DRONE_RPC_SECRET
- export DRONE_GITEA_CLIENT_ID=$DRONE_GITEA_CLIENT_ID
- export DRONE_GITEA_CLIENT_SECRET=$DRONE_GITEA_CLIENT_SECRET
- export SSH_USER=$SSH_USER
- export CERTBOT_EMAIL=$CERTBOT_EMAIL
- export GIT_DOMAIN=$GIT_DOMAIN
- export DRONE_DOMAIN=$DRONE_DOMAIN
- export REMOTE_DOMAIN=$REMOTE_DOMAIN
- docker network prune -f
- cd /home/$SSH_USER/gitea-drone-stack
- docker stack rm remote-drone
- sleep 60
- docker stack deploy -c docker-compose-drone.yml remote-drone
#- sleep 300

services:
- name: docker
image: docker:dind
privileged: true
volumes:
- name: dockersock
path: /var/run
- name: ca
path: /etc/docker/certs.d
volumes:
- name: dockersock
temp: {}
- name: ca
host:
path: /home/giles/gitea-drone-stack/.ca

+ 243
- 0
.drone.yml View File

@@ -0,0 +1,243 @@
---

kind: pipeline
type: docker
name: default
when:
branch:
- master

clone:
# skip_verify: true

steps:
steps:
- name: wait
when:
branch:
- master
image: docker:dind
volumes:
- name: dockersock
path: /var/run

commands:
- sleep 60
- name: build-postgres
when:
branch:
- master
image: docker:dind
volumes:
- name: dockersock
path: /var/run
environment:
LOCAL_DOCKER_REGISTRY:
from_secret: local-docker-registry
commands:
- cd guacamole-postgresql
- docker build . -t $${LOCAL_DOCKER_REGISTRY}/guacamole-postgresql
- docker push $${LOCAL_DOCKER_REGISTRY}/guacamole-postgresql
- name: build-ngrok
when:
branch:
- master
image: docker:dind
volumes:
- name: dockersock
path: /var/run
environment:
LOCAL_DOCKER_REGISTRY:
from_secret: local-docker-registry
commands:
- cd ngrok2
- docker build . -t $${LOCAL_DOCKER_REGISTRY}/ngrok-gitea
- docker push $${LOCAL_DOCKER_REGISTRY}/ngrok-gitea
- name: build-letsencrypt-nginx
when:
branch:
- master
image: docker:dind
volumes:
- name: dockersock
path: /var/run
environment:
LOCAL_DOCKER_REGISTRY:
from_secret: local-docker-registry
commands:
- cd letsencrypt-nginx
- docker build . -t $${LOCAL_DOCKER_REGISTRY}/letsencrypt-nginx
- docker push $${LOCAL_DOCKER_REGISTRY}/letsencrypt-nginx
- name: build-letsencrypt-blog
when:
branch:
- master
image: docker:dind
volumes:
- name: dockersock
path: /var/run
environment:
LOCAL_DOCKER_REGISTRY:
from_secret: local-docker-registry
commands:
- cd letsencrypt-nginx
- sh build.sh blog $${LOCAL_DOCKER_REGISTRY}
- name: build-letsencrypt-drone
when:
branch:
- master
image: docker:dind
volumes:
- name: dockersock
path: /var/run
environment:
LOCAL_DOCKER_REGISTRY:
from_secret: local-docker-registry
commands:
- cd letsencrypt-nginx
- sh build.sh drone $${LOCAL_DOCKER_REGISTRY}
- name: build-letsencrypt-git
when:
branch:
- master
image: docker:dind
volumes:
- name: dockersock
path: /var/run
environment:
LOCAL_DOCKER_REGISTRY:
from_secret: local-docker-registry
commands:
- cd letsencrypt-nginx
- sh build.sh git $${LOCAL_DOCKER_REGISTRY}
- name: build-letsencrypt-remote
when:
branch:
- master
image: docker:dind
volumes:
- name: dockersock
path: /var/run
environment:
LOCAL_DOCKER_REGISTRY:
from_secret: local-docker-registry
commands:
- cd letsencrypt-nginx
- sh build.sh remote $${LOCAL_DOCKER_REGISTRY}
- name: build-letsencrypt-chat
when:
branch:
- master
image: docker:dind
volumes:
- name: dockersock
path: /var/run
environment:
LOCAL_DOCKER_REGISTRY:
from_secret: local-docker-registry
commands:
- cd letsencrypt-nginx
- sh build.sh chat $${LOCAL_DOCKER_REGISTRY}
- name: scp files
when:
branch:
- master
image: appleboy/drone-scp
settings:
host:
from_secret: ssh-host
username:
from_secret: ssh-user
password:
from_secret: ssh-password
port:
from_secret: ssh-port
command_timeout: 2m
target: ~/gitea-drone-stack
source:
- .
- name: deploy
when:
branch:
- master
image: appleboy/drone-ssh
environment:
DRONE_RPC_SECRET:
from_secret: drone-rpc-secret
DRONE_GITEA_CLIENT_ID:
from_secret: drone-gitea-client-id
DRONE_GITEA_CLIENT_SECRET:
from_secret: drone-gitea-client-secret
LOCAL_DOCKER_REGISTRY:
from_secret: local-docker-registry
SSH_USER:
from_secret: ssh-user
CERTBOT_EMAIL:
from_secret: certbot-email
GIT_DOMAIN:
from_secret: git-domain
DRONE_DOMAIN:
from_secret: drone-domain
CHAT_DOMAIN:
from_secret: chat-domain
REMOTE_DOMAIN:
from_secret: remote-domain
BLOG_DOMAIN:
from_secret: blog-domain
settings:
envs:
- drone_rpc_secret
- drone_gitea_client_id
- drone_gitea_client_secret
- ssh_user
- local_docker_registry
- certbot_email
- git_domain
- drone_domain
- chat_domain
- remote_domain
- blog_domain
host:
from_secret: ssh-host
username:
from_secret: ssh-root-user
password:
from_secret: ssh-root-password
port:
from_secret: ssh-port
script:
- set -e
- export LOCAL_DOCKER_REGISTRY=$LOCAL_DOCKER_REGISTRY
- export DRONE_RPC_SECRET=$DRONE_RPC_SECRET
- export DRONE_GITEA_CLIENT_ID=$DRONE_GITEA_CLIENT_ID
- export DRONE_GITEA_CLIENT_SECRET=$DRONE_GITEA_CLIENT_SECRET
- export SSH_USER=$SSH_USER
- export CERTBOT_EMAIL=$CERTBOT_EMAIL
- export GIT_DOMAIN=$GIT_DOMAIN
- export DRONE_DOMAIN=$DRONE_DOMAIN
- export CHAT_DOMAIN=$CHAT_DOMAIN
- export REMOTE_DOMAIN=$REMOTE_DOMAIN
- export BLOG_DOMAIN=$BLOG_DOMAIN
- docker network prune -f
- cd /home/$SSH_USER/gitea-drone-stack
- docker stack rm gitea
- sleep 60
- docker stack deploy -c docker-compose.yml gitea
#- sleep 300

services:
- name: docker
image: docker:dind
privileged: true
volumes:
- name: dockersock
path: /var/run
- name: ca
path: /etc/docker/certs.d
volumes:
- name: dockersock
temp: {}
- name: ca
host:
path: /home/giles/gitea-drone-stack/.ca

+ 3
- 0
.gitignore View File

@@ -0,0 +1,3 @@
.certificates
.ca
.secrets

+ 241
- 0
README.md View File

@@ -0,0 +1,241 @@
# gitea in a stack with drone and guacamole

Remote system support.

Consisting of

* gitea repository - github like self hoisted git and web application
* drone ci system
* guacamole - rdp, vnc and ssh over the internet in the browser

[![Build Status](https://drone.sigyl.com/api/badges/giles/gitea-drone-stack/status.svg)](https://drone.sigyl.com/giles/gitea-drone-stack)

(nb when self deploying with drone it will will stick on started and have to be cancelled. This is because the deployment tears down the previous running drone).

NB each time you deploy this using drone it will leave an orphaned network for example:

drone-yoZbiXiqssFcSsG0dP5d

eventually this will start to cause an error with message: Docker “ERROR: could not find an available, non-overlapping IPv4 address pool among the defaults to assign to the network”


## installing docker

Start with a fresh install of Ubuntu server 19.04 connected to the internet

### update packages

```
sudo apt-get update
sudo apt-get upgrade
```

### remove old versions of docker

(if it's a fresh install of linux there shouldn't be any)

```
sudo apt-get remove docker docker-engine docker.io
```

### install docker

```
sudo apt install docker.io
```

### add current user to docker group

logout and back in afterwards

```
sudo usermod -aG docker $USER
```

### start and enable docker

```
sudo systemctl start docker
sudo systemctl enable docker
```

### change ssh port to 2022

```
sudo vi /etc/ssh/sshd_config

```

change Port 2022

### allow root to ssh

```
sudo vi /etc/ssh/sshd_config

```

## set the root password

```
sudo passwd root
```

change PermitRootLogin yes

reboot

start a stack running gitea to host repository.

## stack

### labels

get nodes with

```
docker node ls
```

add label with

```
docker node update --label-add com.sigyl.git-stack=yes [node id]
```

### generate certificates

where [registry-domain] is the domain on which the registry will be served

sh ca.sh [registry-domain]:5000


sh make-cert.sh [registry-domain] registry

### ngrok auth

obtain ngrok auth token and place in .secrets in form

authtoken: [token]

### initial deploy

```shell
export LOCAL_DOCKER_REGISTRY=registry.local-domain:5000
export CERTBOT_EMAIL=giles.bradshaw@sigyl.com
export GIT_DOMAIN=git.sigyl.com
export REMOTE_DOMAIN=remote.sigyl.com
export DRONE_DOMAIN=drone.sigyl.com
export BLOG_DOMAIN=blog.sigyl.com

export DRONE_RPC_SECRET=$(openssl rand -base64 48)
docker stack deploy -c docker-compose.yml gitea
```

### create gitea drone app with client id and secret

### add secrets to repository in drone


![add secret](./add-secret.png)

* blog-domain
* certbot-email
* drone-domain
* drone-gitea-client-id
* drone-gitea-client-secret
* drone-rpc-secret
* git-domain
* local-docker-registry
* remote-domain
* ssh-password
* ssh-port
* ssh-root-password
* ssh-root-user
* ssh-user
* ssh-host

### kill orphan docker:dind containers

Wen the system is deployed by pushing to repository the docker:dind container will be orphaned and will run forever unless killed..


## guacamole

np no spaces in postgres password

docker stack for guacamole

adapted from https://digitalmccullough.com/posts/setting-up-apache-guacamole-with-docker-stack.html

### initialising

find id

```
docker ps

```

execute initdb.sql

```
docker exec -it [id] psql -U postgres -d guacamole_db -f /initdb.sql
docker exec -it [id] psql -U postgres -d guacamole_db -f /init-user.sql -v password='somepassword'
```

initial admin is guacadmin:guacadmin

create a new admin and delete guacadmin

## docker-exec-runner on windows

These instructions are not very good...

https://exec-runner.docs.drone.io/installation/windows/

download and unpack on linux with

```
curl -L https://github.com/drone-runners/drone-runner-exec/releases/latest/download/drone_runner_exec_windows_amd64.tar.gz | tar zx
```


rename drone-runner-exec to drone-runner-exec.exe

make directory c:\Drone\drone-runner-exec on windows

copy drone-runner-exec.exe to directory

make config file with

```

DRONE_RPC_PROTO=https
DRONE_RPC_HOST=drone.sigyl.com:443
DRONE_RPC_SECRET=[rpc secret]
DRONE_LOG_FILE=C:\Drone\drone-runner-exec\log.txt
DRONE_RUNNER_LABELS=web:true
```


install and start service with

```
drone-runner-exec service install
drone-runner-exec service start
```

## chat

Once the chat-mongo container is up you need to get its id and do

```
docker exec -it [id] mongo --eval "printjson(rs.initiate({_id: 'rs0', members: [ { _id: 0, host: 'localhost:27017' } ]}))"
```

then scale up chat

```
docker service scale gitea_chat=1
```

BIN
add-secret.png View File

Before After
Width: 1208  |  Height: 560  |  Size: 62KB

+ 8
- 0
certificates/ca.sh View File

@@ -0,0 +1,8 @@
mkdir -p ../.certificates
openssl genrsa -des3 -out ../.certificates/ca.key 4096

openssl req -x509 -new -nodes -key ../.certificates/ca.key -sha256 -days 10000 -subj "/C=GB/ST=Devon/O=SiGyl/OU=SiGyl/CN=CA" -out ../.certificates/ca.crt
mkdir -p ../.ca/$1
cp ../.certificates/ca.crt ../.ca/$1
sudo mkdir -p /etc/docker/certs.d/$1
sudo cp ../.ca/$1/*.* /etc/docker/certs.d/$1

+ 4
- 0
certificates/make-cert.sh View File

@@ -0,0 +1,4 @@
openssl genrsa -out ../.certificates/$2.key 2048
openssl req -new -sha256 -key ../.certificates/$2.key -subj "/C=GB/ST=Devon/O=SiGyl/OU=Gitea/CN=$1" -out ../.certificates/$2.csr

openssl x509 -req -in ../.certificates/$2.csr -CA ../.certificates/ca.crt -CAkey ../.certificates/ca.key -CAcreateserial -out ../.certificates/$2.crt -days 10000 -sha256

+ 159
- 0
docker-compose-do.yml View File

@@ -0,0 +1,159 @@
version: "3.7"
services:
letsencrypt-git:
deploy:
placement:
constraints: [node.labels.com.sigyl.git-stack == yes]
replicas: 1
restart_policy:
condition: any
image: letsencrypt-git
environment:
- CERTBOT_EMAIL=${CERTBOT_EMAIL}
- SERVER_NAME=${GIT_DOMAIN}
- PROXY_PASS=http://gitea:3000/
- BLOG_PROXY_PASS=http://ghost:2368
- CHAT_PROXY_PASS=http://chat:3000
- LOCATION=/git/
- BLOG_LOCATION=/blog/
- CHAT_LOCATION=/chat/
volumes:
- letsencrypt-git:/etc/letsencrypt
networks:
- appnet
ports:
- 80:80
- 443:443
- 5000:5000
gitea:
deploy:
placement:
constraints: [node.labels.com.sigyl.git-stack == yes]
replicas: 1
restart_policy:
condition: any
image: gitea/gitea:latest
environment:
- USER_UID=1000
- USER_GID=1000
- ROOT_URL=https://${GIT_DOMAIN}/git
- SSH_DOMAIN=${GIT_DOMAIN}
volumes:
- gitea-app:/data
ports:
- 3000
- 22:22
networks:
- appnet
ghost:
deploy:
placement:
constraints: [node.labels.com.sigyl.git-stack == yes]
replicas: 1
restart_policy:
condition: any
image: ghost
volumes:
- ghost-content:/var/lib/ghost/content
- ./ghost/config.production.json:/var/lib/ghost/config.production.json
environment:
- url=https://${GIT_DOMAIN}/blog
networks:
- appnet
chat:
deploy:
placement:
constraints: [node.labels.com.sigyl.git-stack-chat == yes]
replicas: 0 # will scale after mongo initated
restart_policy:
condition: any
image: rocketchat/rocket.chat:latest
networks:
- appnet
environment:
- MONGO_OPLOG_URL=mongodb://chat-mongo:27017/local
- ROOT_URL=https://${GIT_DOMAIN}/chat/
- ROOT_URL_PATH_PREFIX=chat
- PORT=3000
- MONGO_URL=mongodb://chat-mongo:27017/rocketchat
volumes:
- chat-uploads:/app/uploads
chat-mongo:
deploy:
placement:
constraints: [node.labels.com.sigyl.git-stack-chat == yes]
replicas: 1
restart_policy:
condition: any
image: mongo:4.0
networks:
- appnet
environment:
- MONGO_DATA_DIR=/data/db
- MONGO_LOG_DIR=/dev/null
volumes:
- mongo-chat:/data/db
command: mongod --smallfiles --replSet rs0 --oplogSize 128
drone-server:
deploy:
placement:
constraints: [node.labels.com.sigyl.git-stack == yes]
replicas: 1
restart_policy:
condition: any
image: drone/drone:latest
volumes:
- drone:/var/lib/drone
- drone-data:/data
depends_on:
- gitea
environment:
- DRONE_LOGS_DEBUG=true
- DRONE_LOGS_PRETTY=true
- DRONE_GITEA_SERVER=https://${GIT_DOMAIN}/git
- DRONE_GITEA_CLIENT_ID=${DRONE_GITEA_CLIENT_ID}
- DRONE_GITEA_CLIENT_SECRET=${DRONE_GITEA_CLIENT_SECRET}
- DRONE_SERVER_HOST=${GIT_DOMAIN}:5000 # tunnel hostname
- DRONE_ADMIN=giles
- DRONE_SERVER_PROTO=https # tunnel adds https on top
- DRONE_SERVER_PORT=:8080
- DRONE_RPC_SECRET=${DRONE_RPC_SECRET}
- DRONE_USER_CREATE=username:giles,admin:true
- DRONE_AGENTS_ENABLED=true
#- DRONE_ENV_PLUGIN_ENDPOINT=http://git.local-domain:8888
#- DRONE_ENV_PLUGIN_TOKEN=anything
networks:
- appnet
drone-docker-runner:
deploy:
placement:
constraints: [node.labels.com.sigyl.git-stack == yes]
replicas: 1
restart_policy:
condition: any
image: drone/drone-runner-docker:1
depends_on:
- drone-server
volumes:
- /var/run/docker.sock:/var/run/docker.sock
environment:
- DRONE_RPC_PROTO=https
- DRONE_RPC_HOST=${DRONE_DOMAIN}:5000
- DRONE_RPC_SECRET=${DRONE_RPC_SECRET}
- DRONE_RUNNER_CAPACITY=8
- DRONE_RUNNER_NAME="docker-runner"
#- DRONE_ENV_PLUGIN_ENDPOINT=http://git.local-domain:8888
#- DRONE_ENV_PLUGIN_TOKEN=anything

volumes:
gitea-db:
gitea-app:
letsencrypt-git:
ghost-content:
mongo-chat:
chat-uploads:
drone:
drone-data:
networks:
appnet:
driver: overlay

+ 238
- 0
docker-compose-drone.yml View File

@@ -0,0 +1,238 @@
version: "3.7"
services:
letsencrypt-remote:
deploy:
placement:
constraints: [node.labels.com.sigyl.git-stack == yes]
replicas: 1
restart_policy:
condition: any
image: ${LOCAL_DOCKER_REGISTRY}/letsencrypt-remote
environment:
- SERVER_NAME=${REMOTE_DOMAIN}
- CERTBOT_EMAIL=${CERTBOT_EMAIL}
- PROXY_PASS=http://guacamole:8080/guacamole/
volumes:
- letsencrypt-remote:/etc/letsencrypt
networks:
- appnet
depends_on:
- guacamole
letsencrypt-drone:
deploy:
placement:
constraints: [node.labels.com.sigyl.git-stack == yes]
replicas: 1
restart_policy:
condition: any
image: ${LOCAL_DOCKER_REGISTRY}/letsencrypt-drone
environment:
- CERTBOT_EMAIL=${CERTBOT_EMAIL}
- SERVER_NAME=${DRONE_DOMAIN}
- PROXY_PASS=http://drone-server:8080/
volumes:
- letsencrypt-drone:/etc/letsencrypt
networks:
- appnet
depends_on:
- drone-server
ngrok:
deploy:
placement:
constraints: [node.labels.com.sigyl.git-stack == yes]
replicas: 1
restart_policy:
condition: any
image: ${LOCAL_DOCKER_REGISTRY}/ngrok-gitea
ports:
- "4040:4040"
volumes:
- ./ngrok2/ngrok.m._yml:/home/ngrok/.ngrok2/ngrok._yml:ro
environment:
- GIT_DOMAIN=${GIT_DOMAIN}
- DRONE_DOMAIN=${DRONE_DOMAIN}
- REMOTE_DOMAIN=${REMOTE_DOMAIN}
- BLOG_DOMAIN=${BLOG_DOMAIN}
depends_on:
- gitea
networks:
- appnet
secrets:
- ngrok-auth-token
drone-server:
deploy:
placement:
constraints: [node.labels.com.sigyl.git-stack == yes]
replicas: 1
restart_policy:
condition: any
image: drone/drone:latest
volumes:
- drone:/var/lib/drone
- drone-data:/data
depends_on:
- gitea
environment:
- DRONE_LOGS_DEBUG=true
- DRONE_LOGS_PRETTY=true
- DRONE_GITEA_SERVER=https://${GIT_DOMAIN}
- DRONE_GITEA_CLIENT_ID=${DRONE_GITEA_CLIENT_ID}
- DRONE_GITEA_CLIENT_SECRET=${DRONE_GITEA_CLIENT_SECRET}
- DRONE_SERVER_HOST=${DRONE_DOMAIN} # tunnel hostname
- DRONE_ADMIN=giles
- DRONE_SERVER_PROTO=https # tunnel adds https on top
- DRONE_SERVER_PORT=:8080
- DRONE_RPC_SECRET=${DRONE_RPC_SECRET}
- DRONE_USER_CREATE=username:giles,admin:true
- DRONE_AGENTS_ENABLED=true
#- DRONE_ENV_PLUGIN_ENDPOINT=http://git.local-domain:8888
#- DRONE_ENV_PLUGIN_TOKEN=anything
networks:
- appnet
drone-docker-runner:
deploy:
placement:
constraints: [node.labels.com.sigyl.git-stack == yes]
replicas: 1
restart_policy:
condition: any
image: drone/drone-runner-docker:1
depends_on:
- drone-server
volumes:
- /var/run/docker.sock:/var/run/docker.sock
environment:
- DRONE_RPC_PROTO=https
- DRONE_RPC_HOST=${DRONE_DOMAIN}
- DRONE_RPC_SECRET=${DRONE_RPC_SECRET}
- DRONE_RUNNER_CAPACITY=8
- DRONE_RUNNER_NAME="docker-runner"
#- DRONE_ENV_PLUGIN_ENDPOINT=http://git.local-domain:8888
#- DRONE_ENV_PLUGIN_TOKEN=anything

registry:
deploy:
placement:
constraints: [node.labels.com.sigyl.git-stack == yes]
replicas: 1
restart_policy:
condition: any
image: registry:2
ports:
- 5000:5000
volumes:
- registry-data:/var/lib/registry
environment:
- REGISTRY_HTTP_ADDR=0.0.0.0:5000
- REGISTRY_HTTP_TLS_CERTIFICATE="/run/secrets/registry-cert"
- REGISTRY_HTTP_TLS_KEY="/run/secrets/registry-key"
networks:
- appnet
secrets:
- registry-cert
- registry-key
registry-cache:
deploy:
placement:
constraints: [node.labels.com.sigyl.git-stack == yes]
replicas: 1
restart_policy:
condition: any
image: registry:2
ports:
- 5001:5001
volumes:
- registry-cache-data:/var/lib/registry
environment:
- REGISTRY_HTTP_ADDR=0.0.0.0:5001
- REGISTRY_HTTP_TLS_CERTIFICATE="/run/secrets/registry-cert"
- REGISTRY_HTTP_TLS_KEY="/run/secrets/registry-key"
- REGISTRY_PROXY_REMOTEURL=http://registry-1.docker.io
networks:
- appnet
secrets:
- registry-cert
- registry-key
guacamole-postgresql:
deploy:
placement:
constraints: [node.labels.com.sigyl.git-stack == yes]
replicas: 1
restart_policy:
condition: any
image: ${LOCAL_DOCKER_REGISTRY}/guacamole-postgresql:latest
environment:
POSTGRES_PASSWORD: guacroot
POSTGRES_DB: guacamole_db
volumes:
- guacamole-postgresql-data:/var/lib/postgresql/data
#secrets:
# - source: guacamole-postgresql-password
# target: password

#- /home/giles/guacamole-stack/initdb.sql:/initdb.sql
networks:
- appnet

# The backend guacamole server.
guacd:
deploy:
placement:
constraints: [node.labels.com.sigyl.git-stack == yes]
replicas: 1
restart_policy:
condition: any
image: guacamole/guacd:latest
networks:
- appnet

guacamole:
deploy:
placement:
constraints: [node.labels.com.sigyl.git-stack == yes]
replicas: 1
restart_policy:
condition: any
image: guacamole/guacamole:latest
secrets:
- source: guacamole-postgresql-database
target: database
- source: guacamole-postgresql-user
target: user
- source: guacamole-postgresql-password
target: password
environment:
- POSTGRES_HOSTNAME=guacamole-postgresql
- POSTGRES_PORT=5432
- POSTGRES_USER_FILE=/run/secrets/user
- POSTGRES_PASSWORD_FILE=/run/secrets/password
- POSTGRES_DATABASE_FILE=/run/secrets/database
- GUACD_HOSTNAME=guacd
networks:
- appnet
volumes:
drone:
drone-data:
registry-data:
registry-cache-data:
guacamole-postgresql-data:
letsencrypt-remote:
letsencrypt-drone:

networks:
appnet:
driver: overlay
#external: true
secrets:
'registry-cert':
file: .certificates/registry.crt
'registry-key':
file: .certificates/registry.key
'guacamole-postgresql-database':
file: .secrets/guacamole-postgresql-database
'guacamole-postgresql-user':
file: .secrets/guacamole-postgresql-user
'guacamole-postgresql-password':
file: .secrets/guacamole-postgresql-password
'ngrok-auth-token':
file: .secrets/ngrok-auth-token

+ 370
- 0
docker-compose.yml View File

@@ -0,0 +1,370 @@
version: "3.7"
services:
letsencrypt-chat:
deploy:
placement:
constraints: [node.labels.com.sigyl.git-stack == yes]
replicas: 1
restart_policy:
condition: any
image: ${LOCAL_DOCKER_REGISTRY}/letsencrypt-chat
environment:
- SERVER_NAME=${CHAT_DOMAIN}
- CERTBOT_EMAIL=${CERTBOT_EMAIL}
- PROXY_PASS=http://chat:3000/
volumes:
- letsencrypt-chat:/etc/letsencrypt
networks:
- appnet
letsencrypt-remote:
deploy:
placement:
constraints: [node.labels.com.sigyl.git-stack == yes]
replicas: 1
restart_policy:
condition: any
image: ${LOCAL_DOCKER_REGISTRY}/letsencrypt-remote
environment:
- SERVER_NAME=${REMOTE_DOMAIN}
- CERTBOT_EMAIL=${CERTBOT_EMAIL}
- PROXY_PASS=http://guacamole:8080/guacamole/
volumes:
- letsencrypt-remote:/etc/letsencrypt
networks:
- appnet
depends_on:
- guacamole
letsencrypt-git:
deploy:
placement:
constraints: [node.labels.com.sigyl.git-stack == yes]
replicas: 1
restart_policy:
condition: any
image: ${LOCAL_DOCKER_REGISTRY}/letsencrypt-git
environment:
- CERTBOT_EMAIL=${CERTBOT_EMAIL}
- SERVER_NAME=${GIT_DOMAIN}
- PROXY_PASS=http://gitea:3000/
volumes:
- letsencrypt-git:/etc/letsencrypt
networks:
- appnet
depends_on:
- gitea
letsencrypt-drone:
deploy:
placement:
constraints: [node.labels.com.sigyl.git-stack == yes]
replicas: 1
restart_policy:
condition: any
image: ${LOCAL_DOCKER_REGISTRY}/letsencrypt-drone
environment:
- CERTBOT_EMAIL=${CERTBOT_EMAIL}
- SERVER_NAME=${DRONE_DOMAIN}
- PROXY_PASS=http://drone-server:8080/
volumes:
- letsencrypt-drone:/etc/letsencrypt
networks:
- appnet
depends_on:
- drone-server
gitea:
deploy:
placement:
constraints: [node.labels.com.sigyl.git-stack == yes]
replicas: 1
restart_policy:
condition: any
image: gitea/gitea:latest
environment:
- USER_UID=1000
- USER_GID=1000
- ROOT_URL=https://${GIT_DOMAIN}/
- SSH_DOMAIN=${GIT_DOMAIN}
volumes:
- gitea-app:/data
ports:
- 3000:3000
- 22:22
networks:
- appnet
ngrok:
deploy:
placement:
constraints: [node.labels.com.sigyl.git-stack == yes]
replicas: 1
restart_policy:
condition: any
image: ${LOCAL_DOCKER_REGISTRY}/ngrok-gitea
ports:
- "4040:4040"
volumes:
- ./ngrok2/ngrok._yml:/home/ngrok/.ngrok2/ngrok._yml:ro
environment:
- GIT_DOMAIN=${GIT_DOMAIN}
- DRONE_DOMAIN=${DRONE_DOMAIN}
- REMOTE_DOMAIN=${REMOTE_DOMAIN}
- BLOG_DOMAIN=${BLOG_DOMAIN}
- CHAT_DOMAIN=${CHAT_DOMAIN}
#command: /bin/sh -c " cat /run/secrets/ngrok-auth-token /home/ngrok/.ngrok2/ngrok._yml > /home/ngrok/.ngrok2/ngrok.yml && ngrok start --all"
depends_on:
- gitea
networks:
- appnet
secrets:
- ngrok-auth-token
drone-server:
deploy:
placement:
constraints: [node.labels.com.sigyl.git-stack == yes]
replicas: 1
restart_policy:
condition: any
image: drone/drone:latest
volumes:
- drone:/var/lib/drone
- drone-data:/data
depends_on:
- gitea
environment:
- DRONE_LOGS_DEBUG=true
- DRONE_LOGS_PRETTY=true
- DRONE_GITEA_SERVER=https://${GIT_DOMAIN}
- DRONE_GITEA_CLIENT_ID=${DRONE_GITEA_CLIENT_ID}
- DRONE_GITEA_CLIENT_SECRET=${DRONE_GITEA_CLIENT_SECRET}
- DRONE_SERVER_HOST=${DRONE_DOMAIN} # tunnel hostname
- DRONE_ADMIN=giles
- DRONE_SERVER_PROTO=https # tunnel adds https on top
- DRONE_SERVER_PORT=:8080
- DRONE_RPC_SECRET=${DRONE_RPC_SECRET}
- DRONE_USER_CREATE=username:giles,admin:true
- DRONE_AGENTS_ENABLED=true
#- DRONE_ENV_PLUGIN_ENDPOINT=http://git.local-domain:8888
#- DRONE_ENV_PLUGIN_TOKEN=anything
networks:
- appnet
drone-docker-runner:
deploy:
placement:
constraints: [node.labels.com.sigyl.git-stack == yes]
replicas: 1
restart_policy:
condition: any
image: drone/drone-runner-docker:1
depends_on:
- drone-server
volumes:
- /var/run/docker.sock:/var/run/docker.sock
environment:
- DRONE_RPC_PROTO=https
- DRONE_RPC_HOST=${DRONE_DOMAIN}
- DRONE_RPC_SECRET=${DRONE_RPC_SECRET}
- DRONE_RUNNER_CAPACITY=8
- DRONE_RUNNER_NAME="docker-runner"
#- DRONE_ENV_PLUGIN_ENDPOINT=http://git.local-domain:8888
#- DRONE_ENV_PLUGIN_TOKEN=anything

registry:
deploy:
placement:
constraints: [node.labels.com.sigyl.git-stack == yes]
replicas: 1
restart_policy:
condition: any
image: registry:2
ports:
- 5000:5000
volumes:
- registry-data:/var/lib/registry
environment:
- REGISTRY_HTTP_ADDR=0.0.0.0:5000
- REGISTRY_HTTP_TLS_CERTIFICATE="/run/secrets/registry-cert"
- REGISTRY_HTTP_TLS_KEY="/run/secrets/registry-key"
networks:
- appnet
secrets:
- registry-cert
- registry-key
registry-cache:
deploy:
placement:
constraints: [node.labels.com.sigyl.git-stack == yes]
replicas: 1
restart_policy:
condition: any
image: registry:2
ports:
- 5001:5001
volumes:
- registry-cache-data:/var/lib/registry
environment:
- REGISTRY_HTTP_ADDR=0.0.0.0:5001
- REGISTRY_HTTP_TLS_CERTIFICATE="/run/secrets/registry-cert"
- REGISTRY_HTTP_TLS_KEY="/run/secrets/registry-key"
- REGISTRY_PROXY_REMOTEURL=http://registry-1.docker.io
networks:
- appnet
secrets:
- registry-cert
- registry-key
letsencrypt-blog:
deploy:
placement:
constraints: [node.labels.com.sigyl.git-stack == yes]
replicas: 1
restart_policy:
condition: any
image: ${LOCAL_DOCKER_REGISTRY}/letsencrypt-blog
environment:
- CERTBOT_EMAIL=${CERTBOT_EMAIL}
- SERVER_NAME=${BLOG_DOMAIN}
- PROXY_PASS=http://ghost:2368
volumes:
- letsencrypt-blog:/etc/letsencrypt
networks:
- appnet
depends_on:
- blog

ghost:
deploy:
placement:
constraints: [node.labels.com.sigyl.git-stack == yes]
replicas: 1
restart_policy:
condition: any
image: ghost
volumes:
- ghost-content:/var/lib/ghost/content
- ./ghost/config.production.json:/var/lib/ghost/config.production.json
environment:
- url=https://${BLOG_DOMAIN}
networks:
- appnet
guacamole-postgresql:
deploy:
placement:
constraints: [node.labels.com.sigyl.git-stack == yes]
replicas: 1
restart_policy:
condition: any
image: ${LOCAL_DOCKER_REGISTRY}/guacamole-postgresql:latest
environment:
POSTGRES_PASSWORD: guacroot
POSTGRES_DB: guacamole_db
volumes:
- guacamole-postgresql-data:/var/lib/postgresql/data
#secrets:
# - source: guacamole-postgresql-password
# target: password

#- /home/giles/guacamole-stack/initdb.sql:/initdb.sql
networks:
- appnet

# The backend guacamole server.
guacd:
deploy:
placement:
constraints: [node.labels.com.sigyl.git-stack == yes]
replicas: 1
restart_policy:
condition: any
image: guacamole/guacd:latest
networks:
- appnet

guacamole:
deploy:
placement:
constraints: [node.labels.com.sigyl.git-stack == yes]
replicas: 1
restart_policy:
condition: any
image: guacamole/guacamole:latest
secrets:
- source: guacamole-postgresql-database
target: database
- source: guacamole-postgresql-user
target: user
- source: guacamole-postgresql-password
target: password
environment:
- POSTGRES_HOSTNAME=guacamole-postgresql
- POSTGRES_PORT=5432
- POSTGRES_USER_FILE=/run/secrets/user
- POSTGRES_PASSWORD_FILE=/run/secrets/password
- POSTGRES_DATABASE_FILE=/run/secrets/database
- GUACD_HOSTNAME=guacd
networks:
- appnet
chat:
deploy:
placement:
constraints: [node.labels.com.sigyl.git-stack == yes]
replicas: 0 # will scale after mongo initated
restart_policy:
condition: any
image: rocketchat/rocket.chat:latest
networks:
- appnet
environment:
- MONGO_OPLOG_URL=mongodb://chat-mongo:27017/local
- ROOT_URL=https://${CHAT_DOMAIN}
- PORT=3000
- MONGO_URL=mongodb://chat-mongo:27017/rocketchat
volumes:
- chat-uploads:/app/uploads
chat-mongo:
deploy:
placement:
constraints: [node.labels.com.sigyl.git-stack == yes]
replicas: 1
restart_policy:
condition: any
image: mongo:4.0
networks:
- appnet
environment:
- MONGO_DATA_DIR=/data/db
- MONGO_LOG_DIR=/dev/null
volumes:
- mongo-chat:/data/db
command: mongod --smallfiles --replSet rs0 --oplogSize 128
volumes:
gitea-app:
gitea-db:
drone:
drone-data:
registry-data:
registry-cache-data:
guacamole-postgresql-data:
letsencrypt-remote:
letsencrypt-chat:
letsencrypt-git:
letsencrypt-drone:
letsencrypt-blog:
ghost-content:
mongo-chat:
chat-uploads:

networks:
appnet:
driver: overlay
#external: true
secrets:
'registry-cert':
file: .certificates/registry.crt
'registry-key':
file: .certificates/registry.key
'guacamole-postgresql-database':
file: .secrets/guacamole-postgresql-database
'guacamole-postgresql-user':
file: .secrets/guacamole-postgresql-user
'guacamole-postgresql-password':
file: .secrets/guacamole-postgresql-password
'ngrok-auth-token':
file: .secrets/ngrok-auth-token

+ 33
- 0
ghost/config.production.json View File

@@ -0,0 +1,33 @@
{
"url": "https://sigyl.com/ghost",
"server": {
"port": 2368,
"host": "0.0.0.0"
},
"database": {
"client": "sqlite3",
"connection": {
"filename": "/var/lib/ghost/content/data/ghost.db"
}
},
"mail": {
"transport": "SMTP",
"options": {
"service": "Mailgun",
"auth": {
"user": "postmaster@sandboxb393ecccde2a4447923b56a149879324.mailgun.org",
"pass": "85828e2f386a89851ddd4b8e32bfebdb"
}
}
},
"logging": {
"transports": [
"file",
"stdout"
]
},
"process": "systemd",
"paths": {
"contentPath": "/var/lib/ghost/content"
}
}

+ 3
- 0
guacamole-postgresql/Dockerfile View File

@@ -0,0 +1,3 @@
FROM postgres:12

COPY *.sql /

+ 3
- 0
guacamole-postgresql/init-user.sql View File

@@ -0,0 +1,3 @@
CREATE USER guacamole_user WITH PASSWORD :'password';
GRANT SELECT,INSERT,UPDATE,DELETE ON ALL TABLES IN SCHEMA public TO guacamole_user;
GRANT SELECT,USAGE ON ALL SEQUENCES IN SCHEMA public TO guacamole_user;

+ 791
- 0
guacamole-postgresql/initdb.sql View File

@@ -0,0 +1,791 @@
--
-- Licensed to the Apache Software Foundation (ASF) under one
-- or more contributor license agreements. See the NOTICE file
-- distributed with this work for additional information
-- regarding copyright ownership. The ASF licenses this file
-- to you under the Apache License, Version 2.0 (the
-- "License"); you may not use this file except in compliance
-- with the License. You may obtain a copy of the License at
--
-- http://www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing,
-- software distributed under the License is distributed on an
-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-- KIND, either express or implied. See the License for the
-- specific language governing permissions and limitations
-- under the License.
--

--
-- Connection group types
--

CREATE TYPE guacamole_connection_group_type AS ENUM(
'ORGANIZATIONAL',
'BALANCING'
);

--
-- Entity types
--

CREATE TYPE guacamole_entity_type AS ENUM(
'USER',
'USER_GROUP'
);

--
-- Object permission types
--

CREATE TYPE guacamole_object_permission_type AS ENUM(
'READ',
'UPDATE',
'DELETE',
'ADMINISTER'
);

--
-- System permission types
--

CREATE TYPE guacamole_system_permission_type AS ENUM(
'CREATE_CONNECTION',
'CREATE_CONNECTION_GROUP',
'CREATE_SHARING_PROFILE',
'CREATE_USER',
'CREATE_USER_GROUP',
'ADMINISTER'
);

--
-- Guacamole proxy (guacd) encryption methods
--

CREATE TYPE guacamole_proxy_encryption_method AS ENUM(
'NONE',
'SSL'
);

--
-- Table of connection groups. Each connection group has a name.
--

CREATE TABLE guacamole_connection_group (

connection_group_id serial NOT NULL,
parent_id integer,
connection_group_name varchar(128) NOT NULL,
type guacamole_connection_group_type
NOT NULL DEFAULT 'ORGANIZATIONAL',

-- Concurrency limits
max_connections integer,
max_connections_per_user integer,
enable_session_affinity boolean NOT NULL DEFAULT FALSE,

PRIMARY KEY (connection_group_id),

CONSTRAINT connection_group_name_parent
UNIQUE (connection_group_name, parent_id),

CONSTRAINT guacamole_connection_group_ibfk_1
FOREIGN KEY (parent_id)
REFERENCES guacamole_connection_group (connection_group_id)
ON DELETE CASCADE

);

CREATE INDEX guacamole_connection_group_parent_id
ON guacamole_connection_group(parent_id);

--
-- Table of connections. Each connection has a name, protocol, and
-- associated set of parameters.
-- A connection may belong to a connection group.
--

CREATE TABLE guacamole_connection (

connection_id serial NOT NULL,
connection_name varchar(128) NOT NULL,
parent_id integer,
protocol varchar(32) NOT NULL,
-- Concurrency limits
max_connections integer,
max_connections_per_user integer,

-- Connection Weight
connection_weight integer,
failover_only boolean NOT NULL DEFAULT FALSE,

-- Guacamole proxy (guacd) overrides
proxy_port integer,
proxy_hostname varchar(512),
proxy_encryption_method guacamole_proxy_encryption_method,

PRIMARY KEY (connection_id),

CONSTRAINT connection_name_parent
UNIQUE (connection_name, parent_id),

CONSTRAINT guacamole_connection_ibfk_1
FOREIGN KEY (parent_id)
REFERENCES guacamole_connection_group (connection_group_id)
ON DELETE CASCADE

);

CREATE INDEX guacamole_connection_parent_id
ON guacamole_connection(parent_id);

--
-- Table of base entities which may each be either a user or user group. Other
-- tables which represent qualities shared by both users and groups will point
-- to guacamole_entity, while tables which represent qualities specific to
-- users or groups will point to guacamole_user or guacamole_user_group.
--

CREATE TABLE guacamole_entity (

entity_id serial NOT NULL,
name varchar(128) NOT NULL,
type guacamole_entity_type NOT NULL,

PRIMARY KEY (entity_id),

CONSTRAINT guacamole_entity_name_scope
UNIQUE (type, name)

);

--
-- Table of users. Each user has a unique username and a hashed password
-- with corresponding salt. Although the authentication system will always set
-- salted passwords, other systems may set unsalted passwords by simply not
-- providing the salt.
--

CREATE TABLE guacamole_user (

user_id serial NOT NULL,
entity_id integer NOT NULL,

-- Optionally-salted password
password_hash bytea NOT NULL,
password_salt bytea,
password_date timestamptz NOT NULL,

-- Account disabled/expired status
disabled boolean NOT NULL DEFAULT FALSE,
expired boolean NOT NULL DEFAULT FALSE,

-- Time-based access restriction
access_window_start time,
access_window_end time,

-- Date-based access restriction
valid_from date,
valid_until date,

-- Timezone used for all date/time comparisons and interpretation
timezone varchar(64),

-- Profile information
full_name varchar(256),
email_address varchar(256),
organization varchar(256),
organizational_role varchar(256),

PRIMARY KEY (user_id),

CONSTRAINT guacamole_user_single_entity
UNIQUE (entity_id),

CONSTRAINT guacamole_user_entity
FOREIGN KEY (entity_id)
REFERENCES guacamole_entity (entity_id)
ON DELETE CASCADE

);

--
-- Table of user groups. Each user group may have an arbitrary set of member
-- users and member groups, with those members inheriting the permissions
-- granted to that group.
--

CREATE TABLE guacamole_user_group (

user_group_id serial NOT NULL,
entity_id integer NOT NULL,

-- Group disabled status
disabled boolean NOT NULL DEFAULT FALSE,

PRIMARY KEY (user_group_id),

CONSTRAINT guacamole_user_group_single_entity
UNIQUE (entity_id),

CONSTRAINT guacamole_user_group_entity
FOREIGN KEY (entity_id)
REFERENCES guacamole_entity (entity_id)
ON DELETE CASCADE

);

--
-- Table of users which are members of given user groups.
--

CREATE TABLE guacamole_user_group_member (

user_group_id integer NOT NULL,
member_entity_id integer NOT NULL,

PRIMARY KEY (user_group_id, member_entity_id),

-- Parent must be a user group
CONSTRAINT guacamole_user_group_member_parent
FOREIGN KEY (user_group_id)
REFERENCES guacamole_user_group (user_group_id) ON DELETE CASCADE,

-- Member may be either a user or a user group (any entity)
CONSTRAINT guacamole_user_group_member_entity
FOREIGN KEY (member_entity_id)
REFERENCES guacamole_entity (entity_id) ON DELETE CASCADE

);

--
-- Table of sharing profiles. Each sharing profile has a name, associated set
-- of parameters, and a primary connection. The primary connection is the
-- connection that the sharing profile shares, and the parameters dictate the
-- restrictions/features which apply to the user joining the connection via the
-- sharing profile.
--

CREATE TABLE guacamole_sharing_profile (

sharing_profile_id serial NOT NULL,
sharing_profile_name varchar(128) NOT NULL,
primary_connection_id integer NOT NULL,

PRIMARY KEY (sharing_profile_id),

CONSTRAINT sharing_profile_name_primary
UNIQUE (sharing_profile_name, primary_connection_id),

CONSTRAINT guacamole_sharing_profile_ibfk_1
FOREIGN KEY (primary_connection_id)
REFERENCES guacamole_connection (connection_id)
ON DELETE CASCADE

);

CREATE INDEX guacamole_sharing_profile_primary_connection_id
ON guacamole_sharing_profile(primary_connection_id);

--
-- Table of connection parameters. Each parameter is simply a name/value pair
-- associated with a connection.
--

CREATE TABLE guacamole_connection_parameter (

connection_id integer NOT NULL,
parameter_name varchar(128) NOT NULL,
parameter_value varchar(4096) NOT NULL,

PRIMARY KEY (connection_id,parameter_name),

CONSTRAINT guacamole_connection_parameter_ibfk_1
FOREIGN KEY (connection_id)
REFERENCES guacamole_connection (connection_id) ON DELETE CASCADE

);

CREATE INDEX guacamole_connection_parameter_connection_id
ON guacamole_connection_parameter(connection_id);

--
-- Table of sharing profile parameters. Each parameter is simply
-- name/value pair associated with a sharing profile. These parameters dictate
-- the restrictions/features which apply to the user joining the associated
-- connection via the sharing profile.
--

CREATE TABLE guacamole_sharing_profile_parameter (

sharing_profile_id integer NOT NULL,
parameter_name varchar(128) NOT NULL,
parameter_value varchar(4096) NOT NULL,

PRIMARY KEY (sharing_profile_id, parameter_name),

CONSTRAINT guacamole_sharing_profile_parameter_ibfk_1
FOREIGN KEY (sharing_profile_id)
REFERENCES guacamole_sharing_profile (sharing_profile_id) ON DELETE CASCADE

);

CREATE INDEX guacamole_sharing_profile_parameter_sharing_profile_id
ON guacamole_sharing_profile_parameter(sharing_profile_id);

--
-- Table of arbitrary user attributes. Each attribute is simply a name/value
-- pair associated with a user. Arbitrary attributes are defined by other
-- extensions. Attributes defined by this extension will be mapped to
-- properly-typed columns of a specific table.
--

CREATE TABLE guacamole_user_attribute (

user_id integer NOT NULL,
attribute_name varchar(128) NOT NULL,
attribute_value varchar(4096) NOT NULL,

PRIMARY KEY (user_id, attribute_name),

CONSTRAINT guacamole_user_attribute_ibfk_1
FOREIGN KEY (user_id)
REFERENCES guacamole_user (user_id) ON DELETE CASCADE

);

CREATE INDEX guacamole_user_attribute_user_id
ON guacamole_user_attribute(user_id);

--
-- Table of arbitrary user group attributes. Each attribute is simply a
-- name/value pair associated with a user group. Arbitrary attributes are
-- defined by other extensions. Attributes defined by this extension will be
-- mapped to properly-typed columns of a specific table.
--

CREATE TABLE guacamole_user_group_attribute (

user_group_id integer NOT NULL,
attribute_name varchar(128) NOT NULL,
attribute_value varchar(4096) NOT NULL,

PRIMARY KEY (user_group_id, attribute_name),

CONSTRAINT guacamole_user_group_attribute_ibfk_1
FOREIGN KEY (user_group_id)
REFERENCES guacamole_user_group (user_group_id) ON DELETE CASCADE

);

CREATE INDEX guacamole_user_group_attribute_user_group_id
ON guacamole_user_group_attribute(user_group_id);

--
-- Table of arbitrary connection attributes. Each attribute is simply a
-- name/value pair associated with a connection. Arbitrary attributes are
-- defined by other extensions. Attributes defined by this extension will be
-- mapped to properly-typed columns of a specific table.
--

CREATE TABLE guacamole_connection_attribute (

connection_id integer NOT NULL,
attribute_name varchar(128) NOT NULL,
attribute_value varchar(4096) NOT NULL,

PRIMARY KEY (connection_id, attribute_name),

CONSTRAINT guacamole_connection_attribute_ibfk_1
FOREIGN KEY (connection_id)
REFERENCES guacamole_connection (connection_id) ON DELETE CASCADE

);

CREATE INDEX guacamole_connection_attribute_connection_id
ON guacamole_connection_attribute(connection_id);

--
-- Table of arbitrary connection group attributes. Each attribute is simply a
-- name/value pair associated with a connection group. Arbitrary attributes are
-- defined by other extensions. Attributes defined by this extension will be
-- mapped to properly-typed columns of a specific table.
--

CREATE TABLE guacamole_connection_group_attribute (

connection_group_id integer NOT NULL,
attribute_name varchar(128) NOT NULL,
attribute_value varchar(4096) NOT NULL,

PRIMARY KEY (connection_group_id, attribute_name),

CONSTRAINT guacamole_connection_group_attribute_ibfk_1
FOREIGN KEY (connection_group_id)
REFERENCES guacamole_connection_group (connection_group_id) ON DELETE CASCADE

);

CREATE INDEX guacamole_connection_group_attribute_connection_group_id
ON guacamole_connection_group_attribute(connection_group_id);

--
-- Table of arbitrary sharing profile attributes. Each attribute is simply a
-- name/value pair associated with a sharing profile. Arbitrary attributes are
-- defined by other extensions. Attributes defined by this extension will be
-- mapped to properly-typed columns of a specific table.
--

CREATE TABLE guacamole_sharing_profile_attribute (

sharing_profile_id integer NOT NULL,
attribute_name varchar(128) NOT NULL,
attribute_value varchar(4096) NOT NULL,

PRIMARY KEY (sharing_profile_id, attribute_name),

CONSTRAINT guacamole_sharing_profile_attribute_ibfk_1
FOREIGN KEY (sharing_profile_id)
REFERENCES guacamole_sharing_profile (sharing_profile_id) ON DELETE CASCADE

);

CREATE INDEX guacamole_sharing_profile_attribute_sharing_profile_id
ON guacamole_sharing_profile_attribute(sharing_profile_id);