async def list_users() -> None: from kolombo.models import User active_users = [user.email for user in await User.all_active()] info(f"Active users: {len(active_users)}") if len(active_users) > 0: print_list(active_users)
async def list_domains() -> None: from kolombo.models import Domain all_domains = await Domain.all_active() active_pairs = [f"(MX) {domain.mx} -> {domain.actual}" for domain in all_domains] info(f"Active domains: {len(active_pairs)}") if len(active_pairs) > 0: print_list(active_pairs)
def get_txt_record( domain: str = Argument( # noqa: B008 ..., help="Domain to get DKIM TXT record for (if it was generated)"), ) -> None: if not path.exists(f"/etc/kolombo/dkim_keys/{domain}.txt"): error(f"There is no DKIM TXT record generated for {domain}!") exit(1) dkim_txt = read_dkim_txt_record(domain) info( f"[b]TXT[/] record for [b u]mail._domainkey.{domain}[/] is: {dkim_txt}" )
async def auth( x_secret_key: str = Header(...), # noqa: B008 domain: str = Header(..., alias="X-Domain"), # noqa: B008 protocol: str = Header(..., alias="Auth-Protocol"), # noqa: B008 auth_method: str = Header(...), # noqa: B008 email: str = Header(..., alias="Auth-User"), # noqa: B008 password: str = Header(..., alias="Auth-Pass"), # noqa: B008 login_attempt: int = Header(..., alias="Auth-Login-Attempt"), # noqa: B008 client_ip: str = Header(...), # noqa: B008 ) -> Response: """Endpoint used for auth of SMTP/IMAP users.""" now = datetime.now().isoformat() if x_secret_key != conf.NGINX_SECRET_KEY: # This MUST NOT happen if everything is set up properly error(f"({now}) Not nginx trying to use API") return AuthError("Go Away", retry=False) # Check for possible usage errors to close connection early if protocol not in _protocol_to_port_mapping: error(f"({now}) Unsupported protocol: {protocol}") return AuthError("Unsupported protocol", retry=False) elif auth_method != "plain": error(f"({now}) Unsupported auth method: {auth_method}") return AuthError("Unsupported auth method", retry=False) # Remove newline from credentials email = email.rstrip("%0A") password = password.rstrip("%0A") if not await check_credentials(email, password, domain): warning(f"({now}) Failed {protocol} auth as {email} from {client_ip}") retry = login_attempt < conf.MAX_ATTEMPTS return AuthError("Invalid login or password", retry=retry) info(f"({now}) Successful {protocol} auth as {email} from {client_ip}") server_host = "kolombo-receiver" if protocol == "smtp": server_host = f"kolombo-{domain}-sender" server_ip = get_ip_for_host(server_host) server_port = _protocol_to_port_mapping[protocol] debug( f"({now}) Forwarding nginx to {server_ip}:{server_port} ({server_host})" ) return AuthSuccess(server_ip, server_port)
def generate_keys( domain: str = Argument( ..., help="Domain to generate DKIM keys for"), # noqa: B008 ) -> None: client = from_env() build_kolombo_image(client, "dkim-gen") step(f"Generating DKIM keys for domain: {domain}") client.containers.run( "kolombo-dkim-gen", domain, stderr=True, auto_remove=True, volumes=["/etc/kolombo/dkim_keys:/etc/opendkim/keys"], ) dkim_txt = read_dkim_txt_record(domain) info( f"[b]TXT[/] record for [b u]mail._domainkey.{domain}[/] is: {dkim_txt}" )
def init() -> None: from kolombo import __version__ as version username = getpwuid(getuid()).pw_name started(f"Setting up Kolombo for current user [b]{username}[/]") step("Creating /etc/kolombo folder ([u]need root privileges[/])") info("Creating /etc/kolombo folder (as root)") run(["mkdir", "-p", "-m", "750", "/etc/kolombo"], as_root=True) info(f"Changing /etc/kolombo owner to {username} (as root)") run(["chown", f"{getuid()}:{getgid()}", "/etc/kolombo"], as_root=True) step("Writing configuration to /etc/kolombo/kolombo.conf") debug_mode = confirm("Enable debug mode?", default=False, show_default=True) if debug_mode: enable_debug() nginx_secret_key: str = prompt( "Enter secret key for communication between NginX and auth API", default="changeme", show_default=True, hide_input=True, confirmation_prompt=True, ) max_auth_attempts: int = prompt( "Enter maximum auth attempts per one session", default="3", show_default=True, type=INT, ) passwords_salt: str = prompt( "Enter secret key to be used as salt for passwords hashing", default="changeme", show_default=True, hide_input=True, confirmation_prompt=True, ) configuration = ( f"### Generated by kolombo v{version}\n\n" "# Whether debug mode is enabled (0 - disabled, 1 - enabled)\n" f"DEBUG={int(debug_mode)}\n" "# Secret key that is used to determine that nginx is using API\n" f"NGINX_SECRET_KEY={nginx_secret_key}\n" "# Maximum auth attempts per one session\n" f"MAX_ATTEMPTS={max_auth_attempts}\n" "# Salt used for passwords hashing\n" f"SALT={passwords_salt}\n") with open("/etc/kolombo/kolombo.conf", "w") as config_file: config_file.write(configuration) step("Populating /etc/kolombo with default folders and files") debug("Creating /etc/kolombo folders for volumes") folders = ("maildirs", "mail-enabled", "virtual", "dkim_keys") for folder in folders: Path(f"/etc/kolombo/{folder}").mkdir(mode=0o770, exist_ok=True) for file in ("addresses", "domains", "mailbox", "ssl_map"): debug(f"Writing default file to /etc/kolombo/virtual/{file}") with open(f"/etc/kolombo/virtual/{file}", "w") as virtual_file: virtual_file.write(f"# {_virtual_configs[file]}\n") step( "Installing auto-completion ([u]restart current shell session to use[/])" ) run([sys.argv[0], "--install-completion"]) finished("Kolombo is set up!")