ServerClient #1

Closed
jgaunt wants to merge 30 commits from ServerClient into master
3 changed files with 72 additions and 39 deletions

View File

@@ -1,2 +1,19 @@
# FreeNAS-Network-Unlock
Configuration variables are changed within the [config.py](config.py) file.
#### Setup password-less SSH connection
You are responsible for setting up the password-less ssh connection from freenas to the other computer. You can use the following as a base.
```
# Run ssh-keygen to create the default ~/.ssh/id_rsa ssh key (no passphrase)
ssh-keygen
# Add the public key of this ssh key to the authorized keys of the PI
# We will be prompted to enter the password of the Pi use in order to access the Pi on this occasion, but once the keys are installed on the Pi we won't need to use the password again
cat ~/.ssh/id_rsa.pub | ssh <KEY_HOST_USER>@<KEY_HOST> 'mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys'
# Check that password-less access is working by running
ssh <KEY_HOST_USER>@<KEY_HOST>
# You should be dumped straight to the terminal of the Pi without being prompted for a password. You can now logout of the Pi using:
exit
```

View File

@@ -1,23 +1,15 @@
#!/usr/bin/env python
# Descriptive name for the host
HOST_SHORTNAME = 'freenas'
# FQDN for the host, can use IP too
HOSTNAME = 'freenas.local'
# FQDN for the host containing the luks volume with the recovery keys, can use IP too
KEY_HOST = 'pi.local'
# Username to use to login to the key host
KEY_HOST_USER = 'root'
# Location of the luks volume file
LUKS_VOLUME = '/root/secure.luks'
# Root password for the system, api is only able to use basic auth with the root account
ROOT_PASSWORD = 'my super secret password'
# path to the CA certificate to verify the ssl cert. If you don't wish to verify, make this False
CA_CERT_PATH = '/root/ca.crt'
# pool names and their encryption keys, the index is the pool name and the value is the encryption passphrase for the pool
# Ex: ENCRYPTION_PASSPHRASES = {'zroot': 'super secret password goes here'} this would be the zroot pool and password to unlock it
ENCRYPTION_PASSPHRASES = {
'POOL_NAME': 'ENCRYPTION_PASSPHRASE',
'POOL_NAME2': 'ENCRYPTION_PASSPHRASE2'
}
# STMP Settings
SMTP_SERVER = 'smtp.gmail.com'
SMTP_PORT = 587
SMTP_SSL = True
SMTP_FROM = 'admin@gmail.com'
SMTP_USERNAME = 'admin@gmail.com'
SMTP_PASSWORD = 'super secret password goes here'
FREENAS_ROOT_PASSWORD = 'my super secret password'
# path to the CA certificate to verify the ssl cert on FreeNAS. If you don't wish to verify, make this False
CA_CERT_PATH = 'False'
# pool names, this makes things easy when looking for the keys in the Luks volume
POOL_NAMES = {'POOL_NAME','POOL_NAME2'}
# Luks encryption password
LUKS_PASSWORD = "<PUT PASSWORD HERE>"

View File

@@ -1,27 +1,28 @@
#!/usr/bin/env python
import requests, platform, subprocess, config, logging, simplejson as json, argparse
import requests, platform, subprocess, config, logging, simplejson as json, argparse, base64
from subprocess import call
# You must initialize logging, otherwise you'll not see debug output.
logging.basicConfig(level=logging.INFO,format='%(asctime)s - [%(levelname)s] %(message)s', datefmt='%Y-%m-%d %H:%M:%S')
#logging.getLogger().setLevel(logging.DEBUG)
#requests_log = logging.getLogger("requests.packages.urllib3")
#requests_log.setLevel(logging.DEBUG)
#requests_log.propagate = True
def request(resource, method='GET', data=None):
if data is None:
data = ''
url = 'https://{}/api/v1.0/{}'.format(config.HOSTNAME, resource)
url = 'https://127.0.0.1/api/v1.0/{}'.format(resource)
logging.debug('Request URL: {}'.format(url))
logging.debug('Request Data: {}'.format(data))
logging.debug('CA Certificate Path: {}'.format(config.CA_CERT_PATH))
if config.CA_CERT_PATH == 'False':
config.CA_CERT_PATH = False
else:
config.CA_CERT_PATH = '{}'.format(config.CA_CERT_PATH)
r = requests.request(
method,
url,
data=json.dumps(data),
headers={'Content-Type': "application/json"},
auth=('root', '{}'.format(config.ROOT_PASSWORD)),
verify='{}'.format(config.CA_CERT_PATH)
auth=('root', '{}'.format(config.FREENAS_ROOT_PASSWORD)),
verify=config.CA_CERT_PATH
)
logging.debug('Request Status Code: {}'.format(r.status_code))
if r.ok:
@@ -33,15 +34,38 @@ def request(resource, method='GET', data=None):
return {'ok': r.ok, 'status_code': r.status_code, 'response': r.text}
raise ValueError(r)
POOLS = request('storage/volume/', 'GET')
# Create a small ramdrive to store our recovery keys temporarily
rc = call("mkdir /mnt/ramfs", shell=True)
rc = call("mdmfs -s 1m md /mnt/ramfs", shell=True)
# Send our unlock/mount script to the pi and execute it on the pi using ssh
rc = call("ssh {}@{} 'mkdir /mnt/FreeNASRecoveryKeys; echo -n {} | cryptsetup luksOpen {} FreeNASRecoveryKeys -d - && mount /dev/mapper/FreeNASRecoveryKeys /mnt/FreeNASRecoveryKeys'".format(config.KEY_HOST_USER, config.KEY_HOST, config.LUKS_PASSWORD, config.LUKS_VOLUME), shell=True)
# Now we can copy the keys to the ramfs
for poolName in config.POOL_NAMES:
rc= call("scp {}@{}:/mnt/FreeNASRecoveryKeys/{}.recoveryKey /mnt/ramfs".format(config.KEY_HOST_USER, config.KEY_HOST, poolName), shell=True)
# We can close the luks volume now
rc = call("ssh {}@{} 'umount /mnt/FreeNASRecoveryKeys; cryptsetup luksClose FreeNASRecoveryKeys; rm -rf /mnt/FreeNASRecoveryKeys'".format(config.KEY_HOST_USER, config.KEY_HOST), shell=True)
# Loop through the pools and only unlock the locked ones
POOLS = request('storage/volume/', 'GET')
for pool in POOLS['response']:
if pool['is_decrypted'] == False:
logging.info('Pool {} is locked'.format(pool['name']))
response = request('storage/volume/{}/unlock/'.format(pool['name']), 'POST', {'passphrase': '{}'.format(config.ENCRYPTION_PASSPHRASES[pool['name']])})
if response['ok']:
logging.info('Pool {} was unlocked successfully'.format(pool['name']))
else:
logging.error('Pool {} was NOT unlocked successfully'.format(pool['name']))
else:
logging.debug('Pool {} is already unlocked'.format(pool['name']))
if pool['is_decrypted'] == False:
recovery_key = open('/mnt/ramfs/{}.recoveryKey'.format(pool['name']),'rb')
recovery_key_binary = recovery_key.read()
recovery_key_string = (base64.b64encode(recovery_key_binary)).decode('ascii')
response = request('storage/volume/{}/unlock/'.format(pool['name']), 'POST', {'recovery_key': '{}'.format(recovery_key_string)})
if response['ok']:
logging.info('Pool {} was unlocked successfully'.format(pool['name']))
else:
logging.error('Pool {} was NOT unlocked successfully'.format(pool['name']))
else:
logging.debug('Pool {} is already unlocked'.format(pool['name']))
# wipe the files, unmount the ramfs, and remove the folder
rc = call("rm -fP /mnt/ramfs/*; umount /mnt/ramfs; rmdir /mnt/ramfs", shell=True)