├── README.md ├── aws-ec2-setup.sh └── traefik ├── .env.example ├── .gitignore ├── docker-compose.yml └── traefik.toml /README.md: -------------------------------------------------------------------------------- 1 | # AWS EC2 TRAEFIK SETUP 2 | 3 | [Traefik](https://containo.us/traefik/) setup to run all docker containers on AWS EC2 instances behind the same port 80 and 443 with automated LetsEncrypt certificates creation and renewal. 4 | 5 | 6 | ## CREATE A NEW AWS EC2 INSTANCE 7 | 8 | First of all create new AWS EC2 instance, otherwise you need to guarantee that the existing one doesn't have anything listening on port `80` or port `443`. 9 | 10 | Now grab the IP address for it in order to point a domain to it. 11 | 12 | 13 | ## DOMAIN DNS SETUP 14 | 15 | Before starting the setup a domain needs to be set-ed to point at the EC2 instance. 16 | 17 | For example if the `demo.example.com` is used, then each backend added will use it as their base domain. So when adding a backend for the python shapes api you give it the domain in the likes of `python-shapes.demo.example.com`, and for nodejs `nodejs-shapes.demo.example.com`. 18 | 19 | Go ahead and configure a domain at Route53 or at any other provider, and point it to the IP address from the previous step. 20 | 21 | > **NOTE:** It's important that you add also a wild-card entry in the DNS record to point any sub-domain to the same IP. 22 | 23 | 24 | ## FIREWALL SETUP 25 | 26 | Ensure that port `80` and `443` are open. 27 | 28 | 29 | ## AWS EC2 INSTANCE SETUP 30 | 31 | ### Install Git 32 | 33 | ``` 34 | yum install -y git 35 | ``` 36 | 37 | ### SSH Key 38 | 39 | If the instance already has one, then just `cat ~/.ssh/id_rsa.pub` and add it to your Gitlab/Github account, otherwise create it first. 40 | 41 | 42 | ### Instance Setup 43 | 44 | #### Cloning this repository 45 | 46 | Let's start by cloning this repository: 47 | 48 | ``` 49 | git clone https://github.com/approov/aws-ec2-traefik-setup.git && cd aws-ec2-traefik-setup 50 | ``` 51 | 52 | #### The Traefik environment file 53 | 54 | Creating the `.env` file for Traefik: 55 | 56 | ``` 57 | sudo mkdir /opt/traefik && sudo cp ./traefik/.env.example /opt/traefik/.env 58 | ``` 59 | 60 | Customize the `env.` file with your values: 61 | 62 | ``` 63 | sudo nano /opt/traefik/.env 64 | ``` 65 | 66 | #### Run the setup 67 | 68 | Traefik, Docker and Docker Compose will be installed and configured by running the bash script in the root of this repo: 69 | 70 | ``` 71 | ./aws-ec2-setup.sh 72 | ``` 73 | 74 | The end of the output will look like this: 75 | 76 | ``` 77 | ---> DOCKER COMPOSE VERSION <--- 78 | docker-compose version 1.25.5, build 8a1c60f6 79 | 80 | 81 | ---> GIT VERSION<--- 82 | git version 2.23.3 83 | 84 | 85 | ---> TRAEFIK installed at: /opt/traefik <--- 86 | 87 | From /opt/traefik folder you can ran any docker-compose command. 88 | 89 | Some useful examples: 90 | 91 | ## Restart Traefik: 92 | sudo docker-compose restart traefik 93 | 94 | ## Start Traefik: 95 | sudo docker-compose up -d traefik 96 | 97 | ## Destroy Traefik: 98 | sudo docker-compose down 99 | 100 | ## Tailing the Traefik logs in realtime: 101 | sudo docker-compose logs --follow traefik 102 | 103 | ---> TRAEFIK is now listening for new docker containers <--- 104 | ``` 105 | 106 | This setup script will let Traefik running and listening for incoming requests on port `80` and `443`, where requests for port `80` will be redirected to port `443`. 107 | 108 | 109 | ## TLS CERTIFICATES 110 | 111 | Traefik uses LetsEncrypt to automatically generated and renew TLS certificates for all domains is listening on, and the will keep the public key unchanged, thus a mobile app can implement certificate pinning against the public key without the concern of having the pin changed at each renewal of the certificate. 112 | 113 | 114 | ## DEPLOY SERVER EXAMPLE 115 | 116 | Let's see an example of deploying Python Shapes API backend into an EC2 instance listening at `*.demo.example.com`. 117 | 118 | #### Create the folder 119 | 120 | ``` 121 | mkdir -p ~/backend && cd ~/backend 122 | ``` 123 | 124 | #### Clone the repo 125 | 126 | ``` 127 | git clone https://github.com/approov/python-flask_approov-shapes-api-server && cd python-flask_approov-shapes-api-server 128 | ``` 129 | 130 | #### Create the .env file 131 | 132 | ``` 133 | cp .env.example .env 134 | ``` 135 | 136 | #### Edit the .env file 137 | 138 | Replace the default domain with your own server domain: 139 | 140 | ```bash 141 | PYTHON_FLASK_SHAPES_DOMAIN=python-shapes.demo.example.com 142 | ``` 143 | 144 | Replace the dummy Approov secret on it with the one for your Approov account: 145 | 146 | ```bash 147 | # approov secret -get base64 148 | APPROOV_BASE64_SECRET=your-secret-here 149 | ``` 150 | 151 | #### Start the Docker Stack 152 | 153 | ``` 154 | sudo docker-compose up -d 155 | ``` 156 | 157 | Now in your browser visit `python-shapes.demo.example.com` to check the server is accepting requests. 158 | 159 | #### Tail the logs 160 | 161 | ``` 162 | sudo docker-compose logs -f 163 | ``` 164 | 165 | ## ADD A CONTAINER TO TRAEFIK 166 | 167 | > **NOTE:** No need to follow this for the above Deploy Server Example. You only need to follow this part when your project doesn't have yet Traekik labels in the `docker-compose.yml` file. 168 | 169 | Traefik inspects the labels in all running docker containers to know for what ones needs to proxy requests. 170 | 171 | So if your backend does not have yet support for Traefik in the `docker-compose.yml` file you can configure your service like this: 172 | 173 | ```yml 174 | services: 175 | 176 | api: 177 | ... 178 | 179 | labels: 180 | - "traefik.enable=true" 181 | 182 | # The public domain name for your docker container 183 | - "traefik.frontend.rule=Host:api.demo.example.com" 184 | 185 | # Doesn't need to be exactly the same as the domain name. 186 | - "traefik.backend=api.demo.example.com" 187 | 188 | # The external docker network that Traefik uses to proxy request to containers. 189 | - "traefik.docker.network=traefik" 190 | 191 | # This is the internal container port, not the public one. 192 | - "traefik.port=5000" 193 | ... 194 | 195 | networks: 196 | ... 197 | 198 | traefik: 199 | external: true 200 | 201 | ``` 202 | 203 | With this configuration all requests for `https://api.demo.example.com` will be proxy by Traefik to the docker container with the backend label `traefik.backend=api.demo.example.com` on the internal container network port `traefik.port=5000`. 204 | -------------------------------------------------------------------------------- /aws-ec2-setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -eu 4 | 5 | Setup_Depedencies() { 6 | printf "\n---> INSTALL DEPENDENCIES <---\n" 7 | sudo yum update -y 8 | sudo yum install -y git curl 9 | } 10 | 11 | Setup_Docker() { 12 | printf "\n---> INSTALL DOCKER <---\n" 13 | sudo amazon-linux-extras install -y docker 14 | sudo service docker start 15 | } 16 | 17 | Setup_Docker_Compose() { 18 | printf "\n---> INSTALL DOCKER COMPOSE <---\n" 19 | local _download_url="https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" 20 | sudo curl -L "${_download_url}" -o /usr/bin/docker-compose 21 | sudo chmod ug+x /usr/bin/docker-compose 22 | } 23 | 24 | Setup_Traefik() { 25 | printf "\n---> INSTALL TRAEFIK <---\n" 26 | 27 | sudo cp -r ./traefik /opt 28 | 29 | cd /opt/traefik 30 | 31 | sudo touch acme.json 32 | 33 | # Traefik will not create the certificates if we don't fix the permissions 34 | # for the file where it stores the LetsEncrypt certificates. 35 | sudo chmod 600 acme.json 36 | 37 | # Creates a docker network that will be used by Traefik to proxy the requests to the docker containers: 38 | sudo docker network create traefik || true 39 | 40 | if [ ! -f ./.env ]; then 41 | printf "\n 42 | ===> ERROR: 43 | 44 | Please copy the .env.example file to .env: 45 | $ sudo cp ./traefik/.env.example /opt/traefik/.env 46 | 47 | Now customize it to your values with: 48 | $ sudo nano /opt/traefik/.env 49 | 50 | Afterwards just re-run the setup again: 51 | $ ./aws-ec2-setup.sh 52 | 53 | \n" 54 | 55 | exit 1 56 | fi 57 | 58 | # Traefik will be listening on port 80 and 443, and proxy the requests to 59 | # the associated container for the domain. Check the README for more details. 60 | sudo docker-compose up -d traefik 61 | 62 | # Just give sometime for it to start in order to check the logs afterwards. 63 | sleep 5 64 | 65 | printf "\n---> CHECK TRAEFIK LOGS <---\n" 66 | sudo docker-compose logs traefik 67 | 68 | cd - 69 | } 70 | 71 | Main() { 72 | Setup_Depedencies 73 | Setup_Docker 74 | Setup_Docker_Compose 75 | Setup_Traefik 76 | 77 | printf "\n\n---> DOCKER VERSION <---\n" 78 | sudo docker version 79 | 80 | printf "\n---> DOCKER COMPOSE VERSION <---\n" 81 | sudo docker-compose --version 82 | echo 83 | 84 | printf "\n---> GIT VERSION<---\n" 85 | git version 86 | echo 87 | 88 | printf "\n---> TRAEFIK installed at: /opt/traefik <---\n" 89 | 90 | printf "\nFrom /opt/traefik folder you can ran any docker-compose command.\n" 91 | printf "\nSome useful examples:\n" 92 | 93 | printf "\n## Start Traefik:\n" 94 | printf "sudo docker-compose up -d traefik\n" 95 | 96 | printf "\n## Restart Traefik:\n" 97 | printf "sudo docker-compose restart traefik\n" 98 | 99 | printf "\n## Destroy Traefik:\n" 100 | printf "sudo docker-compose down\n" 101 | 102 | printf "\n## Tailing the Traefik logs in realtime:\n" 103 | printf "sudo docker-compose logs --follow traefik\n" 104 | 105 | printf "\n---> TRAEFIK is now listening for new docker containers <---\n\n" 106 | } 107 | 108 | Main ${@} 109 | -------------------------------------------------------------------------------- /traefik/.env.example: -------------------------------------------------------------------------------- 1 | TRAEFIK_DOCKER_DOMAIN=demo.approov.io 2 | TRAEFIK_ACME_EMAIL=webmaster@approov.io 3 | -------------------------------------------------------------------------------- /traefik/.gitignore: -------------------------------------------------------------------------------- 1 | acme.json 2 | -------------------------------------------------------------------------------- /traefik/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '2.3' 2 | 3 | services: 4 | traefik: 5 | image: traefik:1.7 6 | restart: always 7 | ports: 8 | - 80:80 9 | - 443:443 10 | networks: 11 | - traefik 12 | volumes: 13 | - /var/run/docker.sock:/var/run/docker.sock 14 | - ./traefik.toml:/traefik.toml 15 | - ./acme.json:/acme.json 16 | container_name: traefik 17 | labels: 18 | - "traefik.acme.email=${TRAEFIK_ACME_EMAIL:? Missing TRAEFIK_ACME_EMAIL env var.}" 19 | - "traefik.docker.domain=${TRAEFIK_DOCKER_DOMAIN:? Missing TRAEFIK_DOCKER_DOMAIN env var.}" 20 | networks: 21 | traefik: 22 | external: true 23 | -------------------------------------------------------------------------------- /traefik/traefik.toml: -------------------------------------------------------------------------------- 1 | debug = false 2 | 3 | logLevel = "ERROR" 4 | defaultEntryPoints = ["https","http"] 5 | 6 | [entryPoints] 7 | [entryPoints.http] 8 | address = ":80" 9 | [entryPoints.http.redirect] 10 | entryPoint = "https" 11 | 12 | [entryPoints.https] 13 | address = ":443" 14 | [entryPoints.https.tls] 15 | 16 | [retry] 17 | 18 | [docker] 19 | endpoint = "unix:///var/run/docker.sock" 20 | watch = true 21 | exposedByDefault = false 22 | 23 | [acme] 24 | storage = "acme.json" 25 | entryPoint = "https" 26 | onHostRule = true 27 | [acme.httpChallenge] 28 | entryPoint = "http" 29 | 30 | --------------------------------------------------------------------------------