Skip to content
Snippets Groups Projects

Resolve "feature/ETQ Admin, je veux que ssham selle le vault au signal de fin"

13 files
+ 239
70
Compare changes
  • Side-by-side
  • Inline
Files
13
  • 2e656a1b
    Resolve "release-v1.1.1"
    
    - feature/ETQ Admin, je veux savoir comment déployer l'AC de l'IGC SSHAM
    - bugfix/zip corrompu
    - feature/ETQ Admin, je veux qu'en mode debug des informations soient affichées sur l'écran du service ssham
    - feature/ETQ admin, je veux un script à installer pour autoriser les utilitaires de copie dans le force-command
    - feature/ETQ admin, je veux des noms parlants pour les types de certificat
+ 59
19
import logging as lg
import logging
import hvac
import requests
import json
@@ -6,6 +6,7 @@ import re
import getpass
import base64
import codecs
import sys
from .singleton import MetaSingleton
@@ -25,21 +26,33 @@ class Vault(metaclass=MetaSingleton):
self._vault_shares = shares
self._vault_threshold = threshold
self._client = hvac.Client(url=self._vault_address)
lg.basicConfig(filename=log_filename, format='%(levelname)s:%(message)s', level=log_level)
self._lg = logging.getLogger(__name__)
self._lg.setLevel(log_level)
_filehandler = logging.FileHandler(log_filename)
_filehandler.setFormatter(logging.Formatter(' %(asctime)s [%(levelname)s] %(name)s -> %(funcName)s : line %(lineno)d : %(message)s'))
self._lg.addHandler(_filehandler)
_consolehandler = logging.StreamHandler(sys.stdout)
_consolehandler.setFormatter(logging.Formatter(' %(asctime)s [%(levelname)s] %(message)s'))
self._lg.addHandler(_consolehandler)
self._lg.debug("Vault.init - End")
def start(self):
"""Start Vault.
If Vault is not initialized, it is its first start.
This step configure vault with thershold, shares, setup root_token and keys
This step configure vault with threshold, shares, setup root_token and keys
If Vault is sealed, it was already configured. The main goal is to unseal it to interract with it.
This step unseal vault and configure _root_token and _vault_keys. If keys are not exists, Vault need them to recovery.
"""
self._lg.debug("vault.start - Start")
if not self._client.sys.is_initialized():
print(" Initializing Vault...")
self.initialize() # Vault is initialized and sealed
print(" Unsealing Vault...")
self.unseal() # Unseal Vault
self._lg.debug("vault.start - End")
def initialize(self):
"""Initialize Vault.
@@ -48,14 +61,14 @@ class Vault(metaclass=MetaSingleton):
Must be called only one time or when reinstalling Vault
Recovery keys are printed to stdout.
"""
self._lg.debug("vault.initialize - Start")
shares = self._vault_shares
threshold = self._vault_threshold
try:
result = self._client.sys.initialize(shares, threshold)
except(hvac.exceptions.InvalidRequest):
lg.error("Error in shares / threshold values.")
self._lg.error("Error in shares / threshold values.")
self._root_token = result['root_token']
self._client.token = result['root_token']
@@ -85,15 +98,17 @@ class Vault(metaclass=MetaSingleton):
self._client.sys.submit_unseal_keys(self._vault_keys)
self._vault_status['sealed'] = False
lg.info('Vault is unsealed')
lg.info('Performing CA setup')
self._lg.info('Vault is unsealed')
self._lg.info('Performing CA setup')
# Setup ssh-client-signer endpoint
self._vault_headers = {'X-Vault-Token': self._root_token}
vault_data = {'type': 'ssh'}
vault_url = self._vault_address + "v1/sys/mounts/" + self._vault_ep_name
vault_requests = requests.post(vault_url, headers=self._vault_headers, data=vault_data)
lg.info("initialize() : %d" % vault_requests.status_code)
self._lg.info("initialize() : %d" % vault_requests.status_code)
self._client.sys.seal() # Seal vault at the end of the init
self._lg.info('Vault is sealed')
self._lg.debug("vault.initialize - End")
def unseal(self):
"""Unseal Vault if it is not.
@@ -102,7 +117,7 @@ class Vault(metaclass=MetaSingleton):
User must provide recovery keys to unseal it.
This process generate the _root_token.
"""
self._lg.debug("vault.unseal - Start")
if self._client.sys.is_sealed():
self._vault_status['init'] = True
self._vault_status['sealed'] = True
@@ -113,6 +128,7 @@ class Vault(metaclass=MetaSingleton):
if self._root_token == '':
# generate root token
self._lg.debug("vault.unseal - Generate root token")
self._client.sys.read_root_generation_progress()
start_generate_root_response = self._client.sys.start_root_token_generation()
nonce = start_generate_root_response['nonce']
@@ -127,8 +143,9 @@ class Vault(metaclass=MetaSingleton):
self._vault_headers = {'X-Vault-Token': self._root_token}
if not self._client.sys.is_sealed() and self._client.sys.is_initialized() and self._root_token == "":
lg.error("Vault is already unsealed but without root_token, please restart vault and restart SSHAM")
print("Failed - Vault is already unsealed but without root_token, please restart vault and restart SSHAM")
exit()
self._lg.debug("vault.unseal - End")
def set_secrets(self):
"""Ask user to provide recovery keys
@@ -136,17 +153,21 @@ class Vault(metaclass=MetaSingleton):
Keys are needed, defined by threshold in initialized() method call.
When the threshold is reached with valid keys, the vault is unsealed
"""
self._lg.debug("vault.set_secrets - Start")
while self._client.sys.is_sealed():
key = ' '
while not re.match("[a-f0-9]+", key):
print(" ")
key = getpass.getpass("Provide an unseal key (hidden) : ")
self._vault_keys.append(key)
try:
self._client.sys.submit_unseal_keys(self._vault_keys)
print("Success - Keys validated")
except (hvac.exceptions.InvalidRequest, hvac.exceptions.InternalServerError):
lg.error("Bad key, please fill with factory generated keys.")
print("Failed - Bad key, please fill with factory generated keys.")
self._vault_keys.clear()
self._lg.debug("vault.set_secrets - End")
def setup_ca(self):
"""Setup the SSH CA
@@ -155,19 +176,23 @@ class Vault(metaclass=MetaSingleton):
- Private key is stored in vault and it is non recouvrable.
- Public key is accessible without authentication.
"""
self._lg.debug("vault.setup_ca - Start")
vault_data = {"generate_signing_key": True, "algorithm_signer": "rsa-sha2-256"}
vault_url = self._vault_address + "v1/" + self._vault_ep_name + "/config/ca"
vault_requests = requests.post(vault_url, headers=self._vault_headers, data=json.dumps(vault_data))
lg.info("setup_ca(): %d" % vault_requests.status_code)
self._lg.info("setup_ca(): %d" % vault_requests.status_code)
self._lg.debug("vault.setup_ca - End")
def setup_roles(self, vault_roles):
"""Setup the SSHAM ROLES
Setup roles as defined in config.py. A role's description file must be specified in ssham/vault/roles directory
"""
self._lg.debug("vault.setup_roles - Start")
self._vault_roles = vault_roles
# Setup roles
for name, role in vault_roles.items():
self._lg.debug("vault.setup_roles - Mounting role \'" + name + "\'")
vault_url = self._vault_address + "v1/" + self._vault_ep_name + "/roles/" + name
try:
with open(role['path'], 'r') as file:
@@ -176,10 +201,10 @@ class Vault(metaclass=MetaSingleton):
json_vault = json.dumps(vault_data)
vault_requests = requests.post(vault_url, headers=self._vault_headers, data=json_vault)
if vault_requests.status_code != 204 and vault_requests.status_code != 200:
lg.error("setup_roles(): unable to setup role %s" % name)
self._lg.error("setup_roles(): unable to setup role %s" % name)
except IOError:
lg.error("setup_roles(): %s not found" % role['path'])
self._lg.error("setup_roles(): %s not found" % role['path'])
# Setup policies for each role
vault_url = self._vault_address + "v1/sys/policies/acl/" + name
@@ -188,13 +213,15 @@ class Vault(metaclass=MetaSingleton):
vault_json = json.dumps(vault_data, ensure_ascii=False)
vault_requests = requests.post(vault_url, headers=self._vault_headers, data=vault_json)
if vault_requests.status_code != 204 and vault_requests.status_code != 200:
lg.error("setup_roles(): unable to setup acl for role %s" % name)
self._lg.error("setup_roles(): unable to setup acl for role %s" % name)
self._lg.debug("vault.setup_roles - End")
def setup_auth_backend(self, vault_auth_backend, vault_auth_backend_params):
"""Setup the authentication backend
Supported backends: ldap, userpass
"""
self._lg.debug("vault.setup_auth_backend(" + vault_auth_backend + ") - Start")
self._vault_auth_backend = vault_auth_backend
vault_supported_methods = {'userpass', 'ldap'}
@@ -210,7 +237,10 @@ class Vault(metaclass=MetaSingleton):
vault_requests = requests.post(vault_url, headers=self._vault_headers, data=json.dumps(vault_data))
if vault_requests.status_code != 204 and vault_requests.status_code != 200:
lg.error('setup_auth_backend(): unable to setup backend %s' % self._vault_auth_backend)
self._lg.error('setup_auth_backend(): unable to setup backend %s' % self._vault_auth_backend)
else:
self._lg.error('setup_auth_backend: unknown backend %s' % self._vault_auth_backend)
self._lg.debug("vault.setup_auth_backend - End")
def setup_user(self, username, password=""):
"""Setup user in vault
@@ -218,6 +248,7 @@ class Vault(metaclass=MetaSingleton):
In case of ldap backend, only setup roles
In case of userpass, setup user's roles and password
"""
self._lg.debug("vault.setup_user(" + username + ") - Start")
vault_url = self._vault_address + 'v1/auth/'+self._vault_auth_backend+'/users/'+username
user_policies = "default"
@@ -234,10 +265,19 @@ class Vault(metaclass=MetaSingleton):
vault_requests = requests.put(vault_url, headers=self._vault_headers, data=vault_data)
if vault_requests.status_code != 204:
lg.error('setup_user(): unable to setup user %s' % username)
self._lg.error('setup_user(): unable to setup user %s' % username)
self._lg.debug("vault.setup_user - End")
def seal(self):
"""Seal the vault if it is not.
"""
self._lg.debug("vault.seal - Start")
print(" Sealing Vault...")
if not self._client.sys.is_sealed():
self._client.sys.seal()
if self._client.sys.is_sealed():
print(" Success - Vault is sealed")
else:
self._lg.error('seal(): unable to seal Vault')
print(" Failed - Vault is unsealed")
self._lg.debug("vault.seal - End")
Loading