Skip to content

Commit 27b5ecc

Browse files
committed
First version, with hooks support
1 parent e131e06 commit 27b5ecc

File tree

15 files changed

+298
-99
lines changed

15 files changed

+298
-99
lines changed

README.md

Lines changed: 110 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -4,39 +4,122 @@ Secure Storage
44
Encrypts the data with a TrueCrypt AES 256 hidden volume, and exposes a HTTP endpoint for having a possibility
55
to enter the passphrase when the server will go down.
66

7+
Protect your server against hosting providers. Even if they would mount your storage it will be encrypted.
8+
Its much more difficult to get into your data when its encrypted, but REMEMBER, it's not impossible!
9+
710
```bash
811
ansible-galaxy install blackandred.server_secure_storage
912
```
1013

14+
Mounting and unmounting from shell
15+
----------------------------------
16+
17+
To mount/unmount a volume from shell there are prepared easy to use scripts.
18+
19+
```bash
20+
# please replace "storage" with the name you placed in "enc_mount_name" variable (see configuration reference)
21+
22+
# mounting
23+
/usr/local/bin/tcmount-storage.sh 'your-secret-here'
24+
25+
# unmounting
26+
/usr/local/bin/tcunmount-storage.sh
27+
```
28+
29+
Mounting by a HTTP call
30+
-----------------------
31+
32+
You can mount the storage using an HTTP call, so also you can easily automate the process using some healthchecks.
33+
34+
```bash
35+
curl -v http://your-host:8015/deploy/volume_mount?enc_token=YOUR-PASSWORD-THERE&token=YOUR-DEPLOYER-TOKEN-HERE
36+
```
37+
38+
Legend:
39+
- enc_token: Its a volume password or secret password (depends on which volume you want to mount)
40+
- token: Thin-Deployer token, configurable in `deployer_token` (see: configuration reference)
41+
42+
Notes:
43+
- IT IS HIGHLY RECOMMENDED TO HIDE DEPLOYER SERVICE BEHIND A SSL GATEWAY
44+
1145
Configuration reference
1246
-----------------------
1347

1448
```yamlex
15-
enc_file: /.do-not-delete # path, where all of the data will be stored
16-
enc_file_size: 400M # examples: 256M, 20G, 500G
17-
enc_mount_name: storage # mount name, should be a-z, lower case, without special letters
18-
enc_file_filesystem: ext4 # any filesystem supported by mkfs (and supported by the operating system)
19-
enc_filesystem_create_if_not_exists: true
20-
21-
# passwords, change them
22-
enc_passphrase: "test123"
23-
enc_hidden_volume_passphrase: "hidden123"
24-
enc_hidden_volume_size: "390M"
25-
26-
# tcplay settings
27-
hashing_algorithm: whirlpool
28-
encryption_algorithm: AES-256-XTS
29-
30-
# Mounting webhook
31-
# ================
32-
# Allows to expose a HTTP endpoint, so you could
33-
# invoke that endpoint to put the passphrase to mount the volume
34-
# eg. after server crash. So the password will not be stored on the server
35-
# and how you will secure it is your concern.
36-
#
37-
deployer_token: "" # set a token to enable
38-
slack_or_mattermost_webhook_url: "" # put a slack/mattermost webhook URL to enable notifications
39-
systemd_service_name: "volume-deployer"
40-
deployer_listen: "0.0.0.0"
41-
deployer_listen_port: "8015"
49+
roles:
50+
- role: blackandred.server_secure_storage
51+
tags: decrypt
52+
vars:
53+
enc_file: /.do-not-delete # path, where all of the data will be stored
54+
enc_file_size: 10000M # examples: 256M, 20G, 500G
55+
enc_mount_name: storage # mount name, should be a-z, lower case, without special letters
56+
enc_file_filesystem: ext4 # any filesystem supported by mkfs (and supported by the operating system)
57+
enc_filesystem_create_if_not_exists: true
58+
59+
# passwords, change them, NOTE: You can keep them secure in an Ansible Vault
60+
# by default the hidden volume is mounted during deployment time
61+
# but normally you can choose over the HTTP endpoint or via SHELL which volume you want to mount
62+
# by choosing one of defined passwords just
63+
enc_passphrase: "test123"
64+
enc_hidden_volume_passphrase: "hidden123"
65+
enc_hidden_volume_size: "9950M"
66+
67+
# tcplay settings
68+
hashing_algorithm: whirlpool
69+
encryption_algorithm: AES-256-XTS
70+
71+
# Mounting webhook
72+
# ================
73+
# Allows to expose a HTTP endpoint, so you could
74+
# invoke that endpoint to put the passphrase to mount the volume
75+
# eg. after server crash. So the password will not be stored on the server
76+
# and how you will secure it is your concern.
77+
#
78+
deployer_token: "" # set a token to enable
79+
slack_or_mattermost_webhook_url: "" # put a slack/mattermost webhook URL to enable notifications
80+
systemd_service_name: "volume-deployer"
81+
deployer_listen: "0.0.0.0"
82+
deployer_listen_port: "8015"
83+
```
84+
85+
Hooks PRE/POST
86+
--------------
87+
88+
Before encryption (detaching the volume) you can execute your code to eg. shutdown services,
89+
and after decryption you can bring them up back.
90+
91+
Example:
92+
93+
```yamlex
94+
hook_pre_mount: ""
95+
96+
hook_post_mount: >
97+
set -x;
98+
99+
mkdir -p /mnt/storage/project /mnt/storage/docker /project /var/lib/docker;
100+
mount -o bind /mnt/storage/project /project || exit 1;
101+
mount -o bind /mnt/storage/docker /var/lib/docker || exit 1;
102+
mount --bind /var/lib/docker/plugins /var/lib/docker/plugins || true;
103+
mount --make-private /var/lib/docker/plugins || true;
104+
105+
if [[ -f /etc/systemd/system/project.service ]]; then
106+
sudo systemctl restart docker;
107+
sleep 5;
108+
sudo systemctl restart project;
109+
fi;
110+
111+
hook_pre_unmount: >
112+
if [[ -f /etc/systemd/system/project.service ]]; then
113+
sudo systemctl disable docker;
114+
sudo systemctl disable project;
115+
116+
sudo systemctl stop project;
117+
sudo systemctl stop docker;
118+
fi;
119+
120+
umount /var/lib/docker/plugins || true;
121+
umount /project || true;
122+
umount /var/lib/docker || true;
123+
124+
hook_post_unmount: ""
42125
```

defaults/main.yml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
enc_file: /.do-not-delete # path, where all of the data will be stored
22
enc_file_size: 400M # examples: 256M, 20G, 500G
33
enc_mount_name: storage # mount name, should be a-z, lower case, without special letters
4+
enc_mount_path: /mnt/storage
45
enc_file_filesystem: ext4 # any filesystem supported by mkfs (and supported by the operating system)
56
enc_filesystem_create_if_not_exists: true
7+
hide_sensitive_output: true
68

79
# passwords, change them
810
enc_passphrase: "test123"
@@ -25,3 +27,19 @@ slack_or_mattermost_webhook_url: "" # put a slack/mattermost webhook URL to ena
2527
systemd_service_name: "volume-deployer"
2628
deployer_listen: "0.0.0.0"
2729
deployer_listen_port: "8015"
30+
31+
#
32+
# Hooks
33+
# =====
34+
# Allows to execute actions before/after mounting and unmounting
35+
#
36+
hook_pre_mount: >
37+
echo 'Mounting'
38+
39+
hook_post_mount: ""
40+
41+
hook_pre_unmount: >
42+
echo 'Unmounting'
43+
44+
hook_post_unmount: >
45+
echo 'Unmounted'

tasks/create-deploy-hooks.yml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
- name: Create a directory for hooks
2+
become: yes
3+
file:
4+
path: "/usr/local/server-secure-storage/{{ enc_mount_name }}/hooks.d/{{ item }}"
5+
state: directory
6+
with_items:
7+
- pre_mount
8+
- post_mount
9+
- pre_unmount
10+
- post_unmount
11+
12+
- name: Adding hooks
13+
become: yes
14+
template:
15+
src: "usr/local/server-secure-storage/hooks.d/{{ item }}/001_default.sh"
16+
dest: "/usr/local/server-secure-storage/{{ enc_mount_name }}/hooks.d/{{ item }}/001_default.sh"
17+
mode: +x
18+
with_items:
19+
- pre_mount
20+
- post_mount
21+
- pre_unmount
22+
- post_unmount

tasks/create.yml

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@
1111
become: yes
1212
shell: "losetup '{{ loopback.stdout }}' '{{ enc_file }}'"
1313

14-
- name: Create an encrypted volume
14+
- name: Create an encrypted volume, this may take a while, you can observe /tmp/create.log
1515
become: yes
16-
no_log: True
17-
shell: "time /usr/local/bin/tc-create-volume.sh '-d /dev/{{ loopback.stdout }} -g -a {{ hashing_algorithm }} -b {{ encryption_algorithm }}' '{{ enc_passphrase }}' '{{ enc_hidden_volume_passphrase }}' '{{ enc_hidden_volume_size }}'"
16+
no_log: "{{ hide_sensitive_output }}"
17+
shell: "time /usr/local/bin/tc-create-volume.sh '-d /dev/{{ loopback.stdout }} -g -a {{ hashing_algorithm }} -b {{ encryption_algorithm }}' '{{ enc_mount_name }}' '{{ enc_passphrase }}' '{{ enc_hidden_volume_passphrase }}' '{{ enc_hidden_volume_size }}'"
18+
vars:
19+
ansible_command_timeout: 21600

tasks/generate-scripts.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
- name: Copy shared bash code
2+
become: yes
3+
template:
4+
src: "./usr/local/lib/server-secure-storage.sh"
5+
dest: "/usr/local/lib/server-secure-storage.sh"
6+
mode: +x
7+
18
- name: Generate scripts
29
become: yes
310
template:
@@ -8,3 +15,4 @@
815
- { src: 'tc-create-volume.sh', dest: 'tc-create-volume.sh' }
916
- { src: 'tc-mount-volume.sh', dest: 'tc-mount-volume.sh' }
1017
- { src: 'tcmount-xxx.sh.j2', dest: "tcmount-{{ enc_mount_name }}.sh" }
18+
- { src: 'tcunmount-xxx.sh.j2', dest: "tcunmount-{{ enc_mount_name }}.sh" }

tasks/main.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88
- name: Generate scripts
99
include: generate-scripts.yml
1010

11+
- name: Create deploy hooks
12+
include: create-deploy-hooks.yml
13+
1114
- name: Create a new volume if it was not created yet
1215
include: create.yml
1316
when: enc_filesystem_create_if_not_exists == True and storage_file.stat.exists == False

tasks/mount.yml

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,4 @@
1-
- name: Mount a volume first time with the secret passphrase (with filesystem formatting)
2-
become: yes
3-
no_log: True
4-
shell: "/usr/local/bin/tcmount-{{ enc_mount_name }}.sh '{{ enc_hidden_volume_passphrase }}' --format"
5-
vars:
6-
ansible_ssh_pipelining: no
7-
when: storage_file.stat.exists == False
8-
91
- name: Mount a volume usually with the secret passphrase
102
become: yes
11-
no_log: True
3+
no_log: "{{ hide_sensitive_output }}"
124
shell: "/usr/local/bin/tcmount-{{ enc_mount_name }}.sh '{{ enc_hidden_volume_passphrase }}'"
13-
when: storage_file.stat.exists == True
14-

templates/usr/local/bin/tc-create-volume.sh

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@
33
set -x
44

55
TCPLAY_PARAMS=$1
6-
PASSPHRASE=$2
7-
HIDDEN_VOLUME_PASSPHRASE=$3
8-
HIDDEN_VOLUME_SIZE=$4
6+
MOUNT_NAME=$2
7+
PASSPHRASE=$3
8+
HIDDEN_VOLUME_PASSPHRASE=$4
9+
HIDDEN_VOLUME_SIZE=$5
910

1011
expect -c "
1112
spawn tcplay -c ${TCPLAY_PARAMS};
@@ -30,4 +31,14 @@ expect -c "
3031
send y\r;
3132
3233
interact;
33-
"
34+
" > /tmp/create.log
35+
36+
cat /tmp/create.log
37+
38+
if [[ $(cat /tmp/create.log) != *"Writing volume headers to disk..."* ]]; then
39+
echo " .. Failed to create a volume"
40+
exit 1
41+
fi
42+
43+
/usr/local/bin/tcmount-${MOUNT_NAME}.sh ${PASSPHRASE} --format
44+
exit $?
Lines changed: 4 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
#!/bin/bash
22

3+
source /usr/local/lib/server-secure-storage.sh
4+
35
#
46
# This file is automatically generated by Ansible
57
# All changes made manually to this file will be LOST!
@@ -9,55 +11,12 @@
911
MAPPED_DEVICE_PATH=/dev/mapper/{{ enc_mount_name }}
1012
LOOPBACK_CACHE_PATH=/tmp/{{ enc_mount_name }}
1113
ENC_FILE={{ enc_file }}
12-
MOUNT_PATH=/mnt/{{ enc_mount_name }}
14+
MOUNT_PATH={{ enc_mount_path }}
1315
MOUNT_NAME={{ enc_mount_name }}
1416
MOUNT_FS={{ enc_file_filesystem }}
17+
HOOKS_DIR=/usr/local/server-secure-storage/{{ enc_mount_name }}/hooks.d/
1518
### END OF GENERATED BY ANSIBLE
1619

17-
reformat () {
18-
mkfs.${MOUNT_FS} ${MAPPED_DEVICE_PATH}
19-
}
20-
21-
setup_loopback () {
22-
# at first deactivate all previously assigned loopback devices
23-
for prev_loopback in $(losetup -a |grep "${ENC_FILE}" | cut -d":" -f1); do
24-
losetup -d ${prev_loopback}
25-
done
26-
27-
loopback_device=$(losetup -f)
28-
losetup ${loopback_device} ${ENC_FILE} > /dev/null
29-
echo ${loopback_device}
30-
}
31-
32-
mount_mapped_volume () {
33-
mkdir -p ${MOUNT_PATH}
34-
mount ${MAPPED_DEVICE_PATH} ${MOUNT_PATH}/
35-
}
36-
37-
umount_previously_mounted_volume () {
38-
if [[ -d ${MOUNT_PATH} ]]; then
39-
umount ${MOUNT_PATH} 2> /dev/null
40-
fi
41-
42-
if [[ -e ${MAPPED_DEVICE_PATH} ]]; then
43-
echo " .. Closing the previously opened device"
44-
cryptsetup close ${MAPPED_DEVICE_PATH}
45-
fi
46-
}
47-
48-
decrypt () {
49-
passphrase=$1
50-
loopback_device=$(setup_loopback)
51-
52-
tc-mount-volume.sh "${MOUNT_NAME} -d ${loopback_device}" ${passphrase}
53-
sleep 1
54-
55-
if [[ ! -e ${MAPPED_DEVICE_PATH} ]]; then
56-
echo " .. Cannot decrypt volume"
57-
exit 1
58-
fi
59-
}
60-
6120
main () {
6221
echo " >> Mounting ${MOUNT_NAME}"
6322

@@ -77,13 +36,4 @@ main () {
7736
verify_and_exit
7837
}
7938

80-
verify_and_exit () {
81-
if mount | grep ${MOUNT_PATH} > /dev/null; then
82-
exit 0
83-
fi
84-
85-
echo " .. Cannot find ${MOUNT_PATH} in the list of active mount points"
86-
exit 1
87-
}
88-
8939
main "$@"
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
#!/bin/bash
2+
3+
source /usr/local/lib/server-secure-storage.sh
4+
5+
#
6+
# This file is automatically generated by Ansible
7+
# All changes made manually to this file will be LOST!
8+
#
9+
10+
### GENERATED BY ANSIBLE
11+
MAPPED_DEVICE_PATH=/dev/mapper/{{ enc_mount_name }}
12+
LOOPBACK_CACHE_PATH=/tmp/{{ enc_mount_name }}
13+
ENC_FILE={{ enc_file }}
14+
MOUNT_PATH={{ enc_mount_path }}
15+
MOUNT_NAME={{ enc_mount_name }}
16+
MOUNT_FS={{ enc_file_filesystem }}
17+
HOOKS_DIR=/usr/local/server-secure-storage/{{ enc_mount_name }}/hooks.d/
18+
### END OF GENERATED BY ANSIBLE
19+
20+
main () {
21+
exec_hooks "pre_unmount"
22+
umount_previously_mounted_volume
23+
exec_hooks "pre_unmount"
24+
}

0 commit comments

Comments
 (0)