ServerClient #1
17
README.md
17
README.md
@@ -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
|
||||
```
|
||||
34
config.py
34
config.py
@@ -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>"
|
||||
@@ -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)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user