After my previous posts, I wanted to setup a new server, using my new docker-compose setup and do it right this time.
SSH Keys
One of the security features I want to use, is to only allow login using SSH keys. Therefore I’m going to start generating some keys and then upload them to the server.
This will ask you where to save the keys. I use the default location of `~/.ssh/id_rsa`.
Next we enter a passphrase, this could be omittet if we want the key to be all we need to login. I don’t mind the extra security, so enter some password to pair the key with. This password is needed everytime you login with the key.
The whole output looks like:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
Generating public/private rsa key pair.
Enter file in which to save the key (/home/<username>/.ssh/id_rsa):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/<username>/id_rsa.
Your public key has been saved in /home/<username>/id_rsa.pub.
The key fingerprint is:
SHA256:kDH6Mp09kSd/T9ljc56CXSUEAhjOIYM8pcoktuOxQ <username>@<computer>
The key's randomart image is:
+---[RSA 2048]----+
| . =.o**+.o |
| = o + B.+.oo . |
|= O o + * . . . |
|oE . o * o ....|
|o.. o + S +o.|
|.+ o . . ..|
|o . |
| o |
| . |
+----[SHA256]-----+
|
Now we copy they key to the server, this can be done using:
1
|
cat ~/.ssh/id_rsa.pub | ssh root@<server-ip> "mkdir -p ~/.ssh && chmod 700 ~/.ssh && cat >> ~/.ssh/authorized_keys"
|
When you now login using `ssh root@<server-ip>` it will ask you for your passphrase which is the passphrase you used when generating the key.
Logging in to `root` I can disable login uisng root+password.
1
|
sudo nano /etc/ssh/sshd_config
|
Find the line with `PermitRootLogin` and replace with
1
|
PermitRootLogin without-password
|
Finally restart the sshd service
1
|
sudo systemctl reload sshd.service
|
Trying to login using `root` + `<root password>` will result in nothing, whereas using your ssh keys will allow you to login.
Setup Ubuntu
Updating
My server runs Docker on Ubuntu so before I start installing a bunch of things, let’s update it.
1
2
3
4
|
apt-get update
apt-get upgrade
do-release-upgrade
sudo apt install apache2-utils
|
Now everything is up to date and we can create a new user to login with, so I don’t do everything using `root`.
New User
In order to create a new user we run
1
2
|
adduser <username>
usermod -aG sudo <username>
|
Now my regular user can use `sudo` so I don’t have to switch to `root` all the time.
Next, I want to avoid using `sudo docker <cmd>` and just be able to run docker commands without `sudo`. This can be done by writing
1
|
sudo usermod -aG docker <username>
|
SSH keys
Right now we can use ssh keys to login to `root` but logging in to `<username>` does not require keys.
First copy the keys generated previously to the new user.
1
|
cat ~/.ssh/id_rsa.pub | ssh <username>@<server-ip> "mkdir -p ~/.ssh && chmod 700 ~/.ssh && cat >> ~/.ssh/authorized_keys"
|
Login using `root` and ssh keys and edit `sshd_config`
1
|
vi /etc/ssh/sshd_config
|
Find these two lines and set them to no
1
2
|
PasswordAuthentication no
ChallengeResponseAuthentication no
|
Then save the file and restart the service
We are not forced to login to the server using the ssh keys and cannot login using passwords.
Docker-Compose
Installing docker-compose is pretty easy. First I made the mistake of using `apt-get install docker-compose`, which giSo starting over I went to the docker website and followed
1
2
3
|
sudo curl -L "https://github.com/docker/compose/releases/download/1.22.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
docker-compose --version
|
Now the version is correct and I could continue without problems.
Folder structure
I like to keep a simple folder structure for my Docker containers. This is easy to keep track of local data and backups.
Docker
Let’s go through them one at a time and then see it all together.
For Docker containers I don’t like using volumes to store data. I prefer to keep it in folders in my docker-folder.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
/home/<username>
└───Docker
├───traefik
│ │ docker-compose.yml
│ └───config
│
├───portainer
│ │ docker-compose.yml
│ └───data
│
└───wallabag
│ docker-compose.yml
├───data
└───images
|
Above is an example of three containers (traefik, portainer and wallabag) and their folder structure. It is pretty simple, since with one main folder for each container, inside the folder are the subfolders that the container uses and the docker-compose file.
From previous blog posts, I know that traefik has one folder called `config`, portainer have a `data` folder and wallabag has two folders called `data` and `images`.
Backup
For backups it is about the same structure, except there is another nested folder for each backup. The backups are named for the date and time.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
/home/<username>
└───Backup
├───traefik
│ │ traefik.sh
│ ├───<date>
│ │ │ docker-compose.yml
│ │ └───config
│ └───<date>
│ │ docker-compose.yml
│ └───config
│
├───portainer
│ │ portainer.sh
│ ├───<date>
│ │ │ docker-compose.yml
│ │ └───data
│ └───<date>
│ │ docker-compose.yml
│ └───data
│
└───wallabag
│ wallabag.sh
├───<date>
│ │ docker-compose.yml
│ ├───data
│ └───images
└───<date>
│ docker-compose.yml
├───data
└───images
|
Each blog posts about containers should have backup scripts for their containers.
Restore
Restore is the easiest. This is just temporary folders where I dump backups before restoring from them.
1
2
3
4
5
6
7
8
|
/home/<username>
└───Restore
├───traefik
│ └───temp
├───portainer
│ └───temp
└───wallabag
└───temp
|
Combined
Combined the structure is as follows:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
|
/home/<username>
├───Docker
│ ├───traefik
│ │ │ docker-compose.yml
│ │ └───config
│ │
│ ├───portainer
│ │ │ docker-compose.yml
│ │ └───data
│ │
│ └───wallabag
│ │ docker-compose.yml
│ ├───data
│ └───images
│
├───Backup
│ ├───traefik
│ │ │ traefik.sh
│ │ ├───<date>
│ │ │ │ docker-compose.yml
│ │ │ └───config
│ │ └───<date>
│ │ │ docker-compose.yml
│ │ └───config
│ │
│ ├───portainer
│ │ │ portainer.sh
│ │ ├───<date>
│ │ │ │ docker-compose.yml
│ │ │ └───data
│ │ └───<date>
│ │ │ docker-compose.yml
│ │ └───data
│ │
│ └───wallabag
│ │ wallabag.sh
│ ├───<date>
│ │ │ docker-compose.yml
│ │ ├───data
│ │ └───images
│ └───<date>
│ │ docker-compose.yml
│ ├───data
│ └───images
└───Restore
├───traefik
│ └───temp
├───portainer
│ └───temp
└───wallabag
└───temp
|
This might seem like a lot, but it makes everything a lot easier to work with compared to using volumes.
Containers
Let’s run through some containers docker-compose file, although more info can be found in their respective blog posts.
Traefik
>Detailed blog post
First go to the traefik folder
1
2
|
cd ~/Docker/traefik
vi docker-compose.yml
|
In the docker-compose file, we write:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
version: "3"
services:
traefik:
container_name: traefik
image: traefik:alpine
ports:
- 80:80
- 443:443
volumes:
- ${PWD}/config:/etc/traefik
- /var/run/docker.sock:/var/run/docker.sock
labels:
- "traefik.enable=true"
- "traefik.frontend.rule=Host:traefik.${DOMAIN}"
- "traefik.port=8080"
networks:
default:
external:
name: webproxy
|
Here we setup all the needed information to use Traefik and Let’s Encrypt. Notice how in the `frontend.rule` i added `${DOMAIN}` instead of a my domain name. This is because I am using an environment file.
Write your domain name in the `.env` file.
We are not using the default network, so we have to create it before we can use it.
1
|
docker network create webproxy
|
Lastly we need the `config.toml` file. This
#begin_src toml
defaultEntryPoints = [“http”, “https”]
[entryPoints]
[entryPoints.http]
address = “:80”
[entryPoints.https]
address = “:443”
[entryPoints.https.tls]
[entryPoints.http.redirect]
entryPoint = “https”
[entryPoints.web]
address = “:8080”
[entryPoints.web.auth]
[entryPoints.web.auth.basic]
users = ["<username>:$apr1$9WtvIi9R$7UmmK6YEs0dDtLlM.1sbh."]
[api]
entryPoint = “web”
[acme]
email = “xxx@xxx.com”
entryPoint = “https”
storage = “acme.json”
onHostRule = true
[acme.httpChallenge]
entryPoint = “http”
[docker]
endpoint = “unix:///var/run/docker.sock”
domain = “domain.com”
exposedByDefault = false
#+end_src
Notice how we have a username and hashed password for our authentication. This is done using `htpasswd`
1
|
htpasswd -nb <username> <password>
|
Finally start the container
Portainer
> Detailed blog post
For Portainer, it’s a simple small container. Create the docker-compose file
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
version: "3"
services:
portainer:
container_name: portainer
image: portainer/portainer
command: -H unix:///var/run/docker.sock
volumes:
- ${PWD}/data:/data
- /var/run/docker.sock:/var/run/docker.sock
labels:
- "traefik.enable=true"
- "traefik.frontend.rule=Host:portainer.${DOMAIN}"
- "traefik.port=9000"
networks:
default:
external:
name: webproxy
|
Then copy the `.env` file
1
|
cp ../traefik/.env .env
|
And we are ready to start the container.
Go to `portainer.<domain>.<ext>` and create an admin user. Then login and you are good to go.
Wallabag
> Detailed blog post
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
version: "3"
services:
wallabag:
container_name: wallabag
image: wallabag/wallabag
volumes:
- ${PWD}/data:/var/www/wallabag/data
- ${PWD}/images:/var/www/wallabag/web/assets/images
environment:
- SYMFONY__ENV__FOSUSER_REGISTRATION=false
- SYMFONY__ENV__DOMAIN_NAME=https://wallabag.${DOMAIN}
- SYMFONY__ENV__FOSUSER_CONFIRMATION=false
labels:
- "traefik.enable=true"
- "traefik.frontend.rule=Host:wallabag.${DOMAIN}"
networks:
default:
external:
name: webproxy
|
Open the wallabag site and create a new user. Afterwards you can install the firefox extension or the iOS app if needed.
Cypht
> Detailed blog post
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
|
version: "3"
services:
cypht_app:
container_name: cypht_app
image: sailfrog/cypht-docker:latest
environment:
- CYPHT_AUTH_USERNAME=${USER}
- CYPHT_AUTH_PASSWORD=${PASS}
- CYPHT_DB_CONNECTION_TYPE=host
- CYPHT_DB_HOST=cypht_db:3306
- CYPHT_DB_NAME=${DB}
- CYPHT_DB_USER=${DB_USER}
- CYPHT_DB_PASS=${DB_PASS}
- CYPHT_ALLOW_EXTERNAL_IMAGE_SOURCES=true
- CYPHT_MODULE_NASA=enable
- CYPHT_DISABLE_IP_CHECK=true
volumes:
- ${PWD}/users:/var/lib/hm3/users
- ${PWD}/app_data:/var/lib/hm3/app_data
labels:
- "traefik.enable=true"
- "traefik.frontend.rule=Host:mail.${DOMAIN}"
depends_on:
- cypht_db
cypht_db:
container_name: cypht_db
image: mariadb:10
ports:
- 3307:3306
environment:
- MYSQL_ROOT_PASSWORD=${ROOT_PASS}
- MYSQL_DATABASE=${DB}
- MYSQL_USER=${DB_USER}
- MYSQL_PASSWORD=${DB_PASS}
volumes:
- ${PWD}/mysql:/var/lib/mysql
networks:
default:
external:
name: webproxy
|