rewrite
This commit is contained in:
parent
c5ee561bbd
commit
40128c57b5
4
setup.py
4
setup.py
|
|
@ -7,8 +7,8 @@ setup(
|
||||||
packages=find_packages(),
|
packages=find_packages(),
|
||||||
install_requires=[],
|
install_requires=[],
|
||||||
entry_points={
|
entry_points={
|
||||||
'console_scripts': [
|
"console_scripts": [
|
||||||
'tenant=tenant.main:main',
|
"tenant=tenant.main:main",
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,40 +1,32 @@
|
||||||
# tenant/commands/init.py
|
# tenant/commands/init.py
|
||||||
import os
|
import os
|
||||||
from tenant.utils.common import get_secure_password
|
from tenant.utils.common import get_secure_password, generate_key, generate_csr
|
||||||
|
from tenant.utils.terraform import create_tfvars_file
|
||||||
|
import tenant.utils.generatecsr
|
||||||
|
import tenant.utils.generate_secrets_file
|
||||||
|
|
||||||
|
|
||||||
def add_subparser(subparsers):
|
def add_subparser(subparsers):
|
||||||
init_parser = subparsers.add_parser("init", help="Initialize a new tenant")
|
init_parser = subparsers.add_parser("init", help="Initialize a new tenant")
|
||||||
default_tenant_name = os.getenv("TENANT_NAME", None)
|
|
||||||
init_parser.add_argument(
|
|
||||||
"tenant_name", help="Name of the tenant", default=default_tenant_name, nargs="?"
|
|
||||||
)
|
|
||||||
init_parser.add_argument(
|
init_parser.add_argument(
|
||||||
"--target", default=".", help="Target directory (default: current directory)"
|
"--target", default=".", help="Target directory (default: current directory)"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def execute(args):
|
def execute(args):
|
||||||
if args.tenant_name is not None:
|
tenant_name = os.environ.get("TENANT_NAME")
|
||||||
tenant_name = args.tenant_name
|
if not tenant_name:
|
||||||
# If tenant_name is not provided and TENANT_NAME is not set, prompt the user
|
tenant_name = input("Please enter the desired tenant name: ")
|
||||||
if args.tenant_name is None and os.getenv("TENANT_NAME") is None:
|
else:
|
||||||
tenant_name = input("Please enter the tenant name: ")
|
|
||||||
|
|
||||||
# Ask the user to confirm the tenant name if the environment variable is set
|
|
||||||
if os.getenv("TENANT_NAME") == args.tenant_name:
|
|
||||||
user_confirmation = input(
|
user_confirmation = input(
|
||||||
f"Use '{os.getenv('TENANT_NAME')}' as the tenant name? (yes/no): "
|
f"Current tenant name is {tenant_name}. Is this correct? (y/n): "
|
||||||
).lower()
|
)
|
||||||
if user_confirmation not in ("yes", "y"):
|
if user_confirmation != "y":
|
||||||
tenant_name = input("Please enter the desired tenant name: ")
|
tenant_name = input("Please enter the tenant name: ")
|
||||||
|
|
||||||
if os.getenv("TENANT_NAME") and args.tenant_name != os.getenv("TENANT_NAME"):
|
ingress = input(
|
||||||
user_confirmation = input(
|
"Please enter the FQDN of the Kibana ingress, without the 'kibana' prefix: "
|
||||||
f"Both env var TENANT_NAME and tenant_name argument are set. Use '{os.getenv('TENANT_NAME')}' as the tenant name? (If no, '{tenant_name}' will be used) (yes/no): "
|
)
|
||||||
).lower()
|
|
||||||
if user_confirmation in ("yes", "y"):
|
|
||||||
tenant_name = os.getenv("TENANT_NAME")
|
|
||||||
|
|
||||||
target_directory = args.target
|
target_directory = args.target
|
||||||
|
|
||||||
|
|
@ -42,25 +34,42 @@ def execute(args):
|
||||||
|
|
||||||
# Check if the tenant directory already exists
|
# Check if the tenant directory already exists
|
||||||
if os.path.exists(tenant_directory):
|
if os.path.exists(tenant_directory):
|
||||||
print(f"Error: Tenant directory '{tenant_directory}' already exists.")
|
print(
|
||||||
|
f"Error: Tenant directory '{tenant_directory}' already exists. Init aborted."
|
||||||
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
# Prompt the user for the GitSync password securely
|
# Prompt the user for the GitSync password securely
|
||||||
git_sync_password = get_secure_password(
|
git_sync_password = get_secure_password(
|
||||||
prompt="Please insert known password for GitSync: "
|
prompt="Please insert predefined password for GitSync: "
|
||||||
)
|
)
|
||||||
|
|
||||||
terraform_directory = os.path.join(tenant_directory, "terraform")
|
# define and create necessary folder structure
|
||||||
kubernetes_directory = os.path.join(tenant_directory, "kubernetes")
|
terraform_directory = os.path.join(tenant_directory, "00-terraform")
|
||||||
certificates_directory = os.path.join(tenant_directory, "certificates")
|
certificates_directory = os.path.join(tenant_directory, "01-certificates")
|
||||||
|
kubernetes_directory = os.path.join(tenant_directory, "02-kubernetes")
|
||||||
|
helm_directory = os.path.join(tenant_directory, "03-helm")
|
||||||
|
|
||||||
|
os.makedirs(certificates_directory)
|
||||||
os.makedirs(terraform_directory)
|
os.makedirs(terraform_directory)
|
||||||
os.makedirs(kubernetes_directory)
|
os.makedirs(kubernetes_directory)
|
||||||
os.makedirs(certificates_directory)
|
os.makedirs(helm_directory)
|
||||||
|
|
||||||
# Create symbolic links for *.tf files
|
# generate key and csr if not exist
|
||||||
|
keyfile = os.path.join(certificates_directory, ingress + ".key")
|
||||||
|
csrfile = os.path.join(certificates_directory, ingress + ".csr")
|
||||||
|
|
||||||
|
if os.path.exists(keyfile):
|
||||||
|
print("Keyfile file already exists")
|
||||||
|
print(keyfile)
|
||||||
|
exit(1)
|
||||||
|
else:
|
||||||
|
generate_key(keyfile)
|
||||||
|
generate_csr(csrfile, ingress)
|
||||||
|
|
||||||
|
# Create symbolic links for *.tf files in tenant directory
|
||||||
source_tf_dir = os.path.join(target_directory, "terraform")
|
source_tf_dir = os.path.join(target_directory, "terraform")
|
||||||
target_tf_dir = os.path.join(tenant_directory, "terraform")
|
target_tf_dir = terraform_directory
|
||||||
|
|
||||||
for filename in os.listdir(source_tf_dir):
|
for filename in os.listdir(source_tf_dir):
|
||||||
if filename.endswith(".tf"):
|
if filename.endswith(".tf"):
|
||||||
|
|
@ -74,6 +83,22 @@ def execute(args):
|
||||||
print(
|
print(
|
||||||
f"Warning: Source file '{filename}' not found in '{source_tf_dir}'."
|
f"Warning: Source file '{filename}' not found in '{source_tf_dir}'."
|
||||||
)
|
)
|
||||||
print(
|
|
||||||
f"Tenant '{tenant_name}' initialized in '{tenant_directory}' with GitSync password provided."
|
variables = {
|
||||||
)
|
"tenant_name": tenant_name,
|
||||||
|
}
|
||||||
|
|
||||||
|
tfvars_filepath = os.path.join(terraform_directory, tenant_name + ".tfvars")
|
||||||
|
create_tfvars_file(variables, tfvars_filepath)
|
||||||
|
|
||||||
|
# generate secrets file if not already exist, not yet encrypted on the fly
|
||||||
|
secrets_file = os.path.join(helm_directory, tenant_name + ".secrets.yaml")
|
||||||
|
if os.path.exists(secrets_file):
|
||||||
|
print("Secrets file already exists")
|
||||||
|
print(secrets_file)
|
||||||
|
else:
|
||||||
|
tenant.utils.generate_secrets_file.generate_secrets_file(
|
||||||
|
secrets_file, git_sync_password
|
||||||
|
)
|
||||||
|
|
||||||
|
print(f"Tenant '{tenant_name}' initialized in '{tenant_directory}'.")
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,62 @@
|
||||||
# tenant/utils/common.py
|
# tenant/utils/common.py
|
||||||
import getpass
|
import getpass
|
||||||
|
from OpenSSL import crypto
|
||||||
|
|
||||||
|
TYPE_RSA = crypto.TYPE_RSA
|
||||||
|
TYPE_DSA = crypto.TYPE_DSA
|
||||||
|
key = crypto.PKey()
|
||||||
|
|
||||||
|
|
||||||
def get_secure_password(prompt="Enter password: "):
|
def get_secure_password(prompt="Enter password: "):
|
||||||
# Use getpass to securely input a password without displaying it
|
# Use getpass to securely input a password without displaying it
|
||||||
return getpass.getpass(prompt=prompt)
|
return getpass.getpass(prompt=prompt)
|
||||||
|
|
||||||
|
|
||||||
|
def generate_key(keyfile):
|
||||||
|
print("Generating Key: ")
|
||||||
|
key.generate_key(TYPE_RSA, 4096)
|
||||||
|
f = open(keyfile, "wb")
|
||||||
|
f.write(crypto.dump_privatekey(crypto.FILETYPE_PEM, key))
|
||||||
|
|
||||||
|
|
||||||
|
def generate_csr(csrfile, ingress):
|
||||||
|
req = crypto.X509Req()
|
||||||
|
req.get_subject().CN = ingress
|
||||||
|
req.get_subject().C = "DE"
|
||||||
|
req.get_subject().ST = "Nordrhein-Westfalen"
|
||||||
|
req.get_subject().L = "Bonn"
|
||||||
|
req.get_subject().O = "Informationstechnikzentrum Bund (ITZBund)"
|
||||||
|
req.get_subject().OU = "Laas/DaaS"
|
||||||
|
req.get_subject().emailAddress = "logging@itzbund.de"
|
||||||
|
req.set_pubkey(key)
|
||||||
|
req.sign(key, "sha256")
|
||||||
|
f = open(csrfile, "wb")
|
||||||
|
f.write(crypto.dump_certificate_request(crypto.FILETYPE_PEM, req))
|
||||||
|
f.close()
|
||||||
|
print("CSR file generated")
|
||||||
|
|
||||||
|
|
||||||
|
def calculate_java_settings(memory_limit):
|
||||||
|
# Use regex to extract the value and unit
|
||||||
|
match = re.match(r"(\d*\.?\d+)([GgMm])i?", memory_limit)
|
||||||
|
|
||||||
|
if match:
|
||||||
|
value, unit = match.groups()
|
||||||
|
|
||||||
|
# Convert the value to an integer
|
||||||
|
value = float(value)
|
||||||
|
|
||||||
|
if unit.lower() == "g":
|
||||||
|
# Convert GiB to MiB
|
||||||
|
value *= 1024
|
||||||
|
|
||||||
|
# Calculate Xmx value
|
||||||
|
xmx_value = value * 0.75
|
||||||
|
|
||||||
|
# Build the Java Xmx settings string without decimal part if it's ".0"
|
||||||
|
value = f"{int(xmx_value) if xmx_value.is_integer() else xmx_value}m"
|
||||||
|
java_settings = "-Xmx" + value + " " + "-Xms" + value
|
||||||
|
return java_settings
|
||||||
|
else:
|
||||||
|
# If the unit is neither "Gi" nor "m", return an error message or handle accordingly
|
||||||
|
return "Unsupported memory unit"
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,67 @@
|
||||||
|
import random
|
||||||
|
import os
|
||||||
|
import ruamel.yaml
|
||||||
|
import string
|
||||||
|
|
||||||
|
yaml = ruamel.yaml.YAML()
|
||||||
|
|
||||||
|
|
||||||
|
def generate_random_string(length):
|
||||||
|
characters = string.ascii_letters + string.digits
|
||||||
|
return "".join(random.choice(characters) for i in range(length))
|
||||||
|
|
||||||
|
|
||||||
|
def generate_secrets_file(secrets_file, git_sync_password):
|
||||||
|
with open(secrets_file, "w", encoding="utf-8") as file:
|
||||||
|
yaml.dump(
|
||||||
|
{
|
||||||
|
"elasticsearch": {
|
||||||
|
"config": {
|
||||||
|
"rbac": {
|
||||||
|
"builtinUsers": {
|
||||||
|
"apm_system": generate_random_string(8),
|
||||||
|
"beats_system": generate_random_string(8),
|
||||||
|
"elastic": generate_random_string(8),
|
||||||
|
"kibana_system": generate_random_string(8),
|
||||||
|
"logstash_system": generate_random_string(8),
|
||||||
|
"remote_monitoring_user": generate_random_string(8),
|
||||||
|
},
|
||||||
|
"customUsers": {
|
||||||
|
"logstash_internal": {
|
||||||
|
"password": generate_random_string(8)
|
||||||
|
},
|
||||||
|
"logstash_writer": {
|
||||||
|
"password": generate_random_string(8)
|
||||||
|
},
|
||||||
|
"prometheus": {"password": "monitor"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"kibana": {
|
||||||
|
"config": {
|
||||||
|
"encryption": {
|
||||||
|
"common": generate_random_string(32),
|
||||||
|
"reporting": generate_random_string(32),
|
||||||
|
"savedObjects": generate_random_string(32),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"logstash": {
|
||||||
|
"gitSync": {"password": git_sync_password},
|
||||||
|
"password": generate_random_string(32),
|
||||||
|
},
|
||||||
|
"oauthProxy": {
|
||||||
|
"clientSecret": generate_random_string(20),
|
||||||
|
"cookie_secret": generate_random_string(32),
|
||||||
|
},
|
||||||
|
"tls": {
|
||||||
|
"externalCertificates": {
|
||||||
|
"kibana": {"tls_key": "ImportMeFromSopsFile"}
|
||||||
|
},
|
||||||
|
"keystorePassword": generate_random_string(8),
|
||||||
|
"truststorePassword": generate_random_string(8),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
file,
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
def create_tfvars_file(variables, filepath):
|
||||||
|
with open(filepath, "w") as tfvars_file:
|
||||||
|
for key, value in variables.items():
|
||||||
|
# Writing variable assignments to the file
|
||||||
|
tfvars_file.write(f'{key} = "{value}"\n')
|
||||||
|
|
@ -5,6 +5,7 @@ from unittest.mock import patch
|
||||||
from argparse import Namespace # Import Namespace for creating args object
|
from argparse import Namespace # Import Namespace for creating args object
|
||||||
from tenant.commands import init
|
from tenant.commands import init
|
||||||
|
|
||||||
|
|
||||||
class TestInitCommand(unittest.TestCase):
|
class TestInitCommand(unittest.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
# Set up any necessary configurations or resources
|
# Set up any necessary configurations or resources
|
||||||
|
|
@ -25,7 +26,9 @@ class TestInitCommand(unittest.TestCase):
|
||||||
os.rmdir(tenant_directory)
|
os.rmdir(tenant_directory)
|
||||||
|
|
||||||
# Create an args object
|
# Create an args object
|
||||||
args = Namespace(command="init", tenant_name=tenant_name, target=target_directory)
|
args = Namespace(
|
||||||
|
command="init", tenant_name=tenant_name, target=target_directory
|
||||||
|
)
|
||||||
|
|
||||||
# Call the init command with the args object
|
# Call the init command with the args object
|
||||||
init.execute(args)
|
init.execute(args)
|
||||||
|
|
@ -42,5 +45,6 @@ class TestInitCommand(unittest.TestCase):
|
||||||
os.rmdir(os.path.join(tenant_directory, "certificates"))
|
os.rmdir(os.path.join(tenant_directory, "certificates"))
|
||||||
os.rmdir(tenant_directory)
|
os.rmdir(tenant_directory)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue