def add_new_cert(): export_ads_certificate() cmd = "openssl x509 -fingerprint -md5 -noout -in /opt/opendj/config/ads-cert.crt" out, err, code = exec_cmd(cmd) if code != 0: err = err or out logger.error( f"Unable to get ads-cert fingerprint; reason={err.decode()}") sys.exit(1) alias = out.decode().split("=")[-1].replace(":", "").lower() cmd = " ".join([ "keytool -import -trustcacerts", f"-alias {alias}", "-keystore /opt/opendj/config/ads-truststore", "-storepass:file /opt/opendj/config/ads-truststore.pin", "-keypass:file /opt/opendj/config/ads-truststore.pin", "-file /opt/opendj/config/ads-cert.crt", "-noprompt", ]) out, err, code = exec_cmd(cmd) if code != 0: err = err or out logger.error(f"Unable to add new cert; reason={err.decode()}") sys.exit(1)
def run_upgrade(): buildinfo = "4.0.0" # check if we need to upgrade if os.path.isfile("/opt/opendj/config/buildinfo"): # example of buildinfo `3.0.1.c5ad2e4846d8aeb501ffdfe5ae2dfd35136dfa68` with open("/opt/opendj/config/buildinfo") as f: old_buildinfo = ".".join( [num for num in f.read().split(".") if num.isdigit()]) if old_buildinfo < buildinfo: logger.info("Trying to upgrade OpenDJ server") # backup old buildinfo exec_cmd( f"cp /opt/opendj/config/buildinfo /opt/opendj/config/buildinfo-{old_buildinfo}" ) _, err, retcode = exec_cmd( "/opt/opendj/upgrade --acceptLicense") if retcode != 0: raise SubprocessError( f"Failed to upgrade OpenDJ; reason={err.decode()}") # backup current buildinfo exec_cmd( f"cp /opt/opendj/config/buildinfo /opt/opendj/config/buildinfo-{buildinfo}" )
def replicate_from(peer, server, base_dn): """Configure replication between 2 LDAP servers. """ ldap_binddn = manager.config.get("ldap_binddn") with admin_password_bound(manager) as password_file: # enable replication for specific backend logger.info(f"Enabling OpenDJ replication of {base_dn} between {peer['name']} and {server['name']}.") enable_cmd = " ".join([ "/opt/opendj/bin/dsreplication", "enable", f"--host1 {peer['name']}", f"--port1 {peer['tags']['admin_port']}", f"--bindDN1 '{ldap_binddn}'", f"--bindPasswordFile1 {password_file}", f"--replicationPort1 {peer['tags']['replication_port']}", "--secureReplication1", f"--host2 {server['name']}", f"--port2 {server['tags']['admin_port']}", f"--bindDN2 '{ldap_binddn}'", f"--bindPasswordFile2 {password_file}", f"--replicationPort2 {server['tags']['replication_port']}", "--secureReplication2", "--adminUID admin", f"--adminPasswordFile {password_file}", f"--baseDN '{base_dn}'", "-X", "-n", "-Q", ]) # logger.info(enable_cmd) out, err, code = exec_cmd(enable_cmd) if code: err = err or out logger.warning(err.decode().strip()) # initialize replication for specific backend logger.info(f"Initializing OpenDJ replication of {base_dn} between {peer['name']} and {server['name']}.") init_cmd = " ".join([ "/opt/opendj/bin/dsreplication", "initialize", f"--baseDN '{base_dn}'", "--adminUID admin", f"--adminPasswordFile {password_file}", f"--hostSource {peer['name']}", f"--portSource {peer['tags']['admin_port']}", f"--hostDestination {server['name']}", f"--portDestination {server['tags']['admin_port']}", "-X", "-n", "-Q", ]) # logger.info(init_cmd) out, err, code = exec_cmd(init_cmd) if code: err = err or out logger.warning(err.decode().strip())
def main(): manager = get_manager() addr = guess_serf_addr() host = addr.split(":")[0] admin_port = os.environ.get("CN_LDAP_ADVERTISE_ADMIN_PORT", "4444") deregister_serf_peer(manager, addr) with admin_password_bound(manager) as password_file: cmd = " ".join([ "/opt/opendj/bin/dsreplication", "disable", "--disableAll", f"--port {admin_port}", f"--hostname {host}", "--adminUID admin", f"--adminPasswordFile {password_file}", "-X", "-n", "-Q", ]) out, err, code = exec_cmd(cmd) if code: err = err or out logger.warning( f"Unable to disable replication for current server; reason={err.decode()}" )
def get_server_info(): server = {} attempt = 1 logger.info("Getting current server info") while attempt <= 3: out, err, code = exec_cmd("serf info -format json") if code != 0: err = err or out logger.warning(f"Unable to get current server info from Serf; reason={err.decode()} ... retrying in 10 seconds") else: try: info = json.loads(out.decode()) server = { "name": info["agent"]["name"], "addr": guess_serf_addr(), "tags": info["tags"], } return server except json.decoder.JSONDecodeError as exc: logger.warning(f"Unable to decode JSON output from Serf command; reason={exc} ... retrying in 10 seconds") # bump the counter time.sleep(10) attempt += 1 if not server: logger.error("Unable to get info for current server after 3 attempts ... exiting") sys.exit(1) # return the server info return server
def create_backends(): logger.info("Creating backends.") mods = [ "create-backend --backend-name metric --set base-dn:o=metric --type je --set enabled:true --set db-cache-percent:10", ] if require_site(): mods.append( "create-backend --backend-name site --set base-dn:o=site --type je --set enabled:true --set db-cache-percent:20", ) hostname = guess_host_addr() binddn = manager.config.get("ldap_binddn") admin_port = os.environ.get("CN_LDAP_ADVERTISE_ADMIN_PORT", "4444") # admin_port = 4444 for mod in mods: cmd = " ".join([ "/opt/opendj/bin/dsconfig", "--trustAll", "--no-prompt", f"--hostname {hostname}", f"--port {admin_port}", f"--bindDN '{binddn}'", f"--bindPasswordFile {DEFAULT_ADMIN_PW_PATH}", mod, ]) _, err, code = exec_cmd(cmd) if code: logger.warning(err.decode()) sys.exit(1)
def generate_keystore(self): suffix = "opendj" passwd = self.manager.secret.get("ldap_truststore_pass") hostname = self.manager.config.get("hostname") logger.info(f"Generating /etc/certs/{suffix}.pkcs12 file") # Convert key to pkcs12 cmd = " ".join([ "openssl", "pkcs12", "-export", "-inkey /etc/certs/{}.key".format(suffix), "-in /etc/certs/{}.crt".format(suffix), "-out /etc/certs/{}.pkcs12".format(suffix), "-name {}".format(hostname), "-passout pass:{}".format(passwd), ]) _, err, retcode = exec_cmd(cmd) assert retcode == 0, "Failed to generate PKCS12 file; reason={}".format( err.decode()) if not self.dry_run: self.manager.secret.from_file( "ldap_pkcs12_base64", f"/etc/certs/{suffix}.pkcs12", encode=True, binary_mode=True, )
def delete_instance_key(): export_ads_certificate() cmd = "openssl x509 -fingerprint -md5 -noout -in /opt/opendj/config/ads-cert.crt" out, err, code = exec_cmd(cmd) if code != 0: err = err or out logger.error( f"Unable to get ads-cert fingerprint; reason={err.decode()}") sys.exit(1) cfg_key = out.decode().split("=")[-1].replace(":", "") host = "localhost:1636" user = manager.config.get("ldap_binddn") password = decode_text(manager.secret.get("encoded_ox_ldap_pw"), manager.secret.get("encoded_salt")) ldap_server = ldap3.Server(host, 1636, use_ssl=True) with ldap3.Connection(ldap_server, user, password) as conn: conn.delete( f"ds-cfg-key-id={cfg_key},cn=instance keys,cn=admin data") if conn.result["description"] != "success": logger.warning(conn.result["message"])
def test_exec_cmd(cmd): from jans.pycloudlib.utils import exec_cmd out, err, code = exec_cmd(cmd) assert out == b"foobar" assert err == b"" assert code == 0
def check_required_entry(host, port, user, base_dn): """Checks if entry is exist. """ if base_dn == "o=metric": dn = "ou=statistic,o=metric" elif base_dn == "o=site": dn = "ou=cache-refresh,o=site" else: client_id = manager.config.get('oxauth_client_id') dn = f"inum={client_id},ou=clients,{base_dn}" with admin_password_bound(manager) as password_file: cmd = " ".join([ "/opt/opendj/bin/ldapsearch", f"--hostname {host}", f"--port {port}", f"--baseDN '{dn}'", f"--bindDN '{user}'", f"--bindPasswordFile {password_file}", "-Z", "-X", "--searchScope base", "'(objectClass=*)' 1.1", ]) out, err, code = exec_cmd(cmd) return out.strip(), err.strip(), code
def generate_openid_keys_hourly(passwd, jks_path, jwks_path, dn, exp=48, sig_keys=DEFAULT_SIG_KEYS, enc_keys=DEFAULT_ENC_KEYS): cmd = " ".join([ "java", "-Dlog4j.defaultInitOverride=true", "-cp /app/javalibs/*", "io.jans.as.client.util.KeyGenerator", "-enc_keys", enc_keys, "-sig_keys", sig_keys, "-dnname", "{!r}".format(dn), "-expiration_hours", "{}".format(exp), "-keystore", jks_path, "-keypasswd", passwd, ]) out, err, retcode = exec_cmd(cmd) if retcode == 0: with open(jwks_path, "w") as f: f.write(out.decode()) return out, err, retcode
def recreate_admin_keystore(): os.unlink("/opt/opendj/config/admin-keystore") addr = guess_serf_addr().split(":")[0] hostname = socket.getfqdn() cmd = " ".join([ "keytool -genkeypair", "-alias admin-cert", "-keyalg RSA", "-validity 365", "-keysize 2048", "-storetype JKS", "-keystore /opt/opendj/config/admin-keystore", "-storepass:file /opt/opendj/config/admin-keystore.pin", "-keypass:file /opt/opendj/config/admin-keystore.pin", f"-dname 'CN={addr}, O=Administration Connector RSA Self-Signed Certificate'", f"-ext san=dns:{hostname},dns:{addr}", ]) out, err, code = exec_cmd(cmd) if code != 0: err = err or out logger.error( f"Unable to create admin-keystore; reason={err.decode()}") sys.exit(1)
def generate_keystore(cert_file, key_file, keystore_file, keystore_password): out, err, code = exec_cmd("openssl pkcs12 -export -name client-api " f"-out {keystore_file} " f"-inkey {key_file} " f"-in {cert_file} " f"-passout pass:{keystore_password}") assert code == 0, "Failed to generate application keystore; reason={}".format( err.decode())
def gen_idp3_key(cls, storepass): cmd = ( "java -classpath '/app/javalibs/*' " "net.shibboleth.utilities.java.support.security.BasicKeystoreKeyStrategyTool " "--storefile /etc/certs/sealer.jks " "--versionfile /etc/certs/sealer.kver " "--alias secret " f"--storepass {storepass}") return exec_cmd(cmd)
def ds_context(): """Ensures Directory Server are up and teardown at the end of the context. """ binddn = manager.config.get("ldap_binddn") cmd = f"/opt/opendj/bin/status -D '{binddn}' --bindPasswordFile {DEFAULT_ADMIN_PW_PATH} --connectTimeout 10000" out, _, code = exec_cmd(cmd) running = out.decode().startswith("Unable to connect to the server") if not running: exec_cmd("/opt/opendj/bin/start-ds") try: yield except Exception: raise finally: exec_cmd("/opt/opendj/bin/stop-ds --quiet")
def install_opendj(): logger.info("Installing OpenDJ.") # 1) render opendj-setup.properties # admin_port = 4444 admin_port = os.environ.get("CN_LDAP_ADVERTISE_ADMIN_PORT", "4444") ctx = { "ldap_hostname": guess_host_addr(), "ldap_port": manager.config.get("ldap_port"), "ldaps_port": manager.config.get("ldaps_port"), "ldap_jmx_port": 1689, "ldap_admin_port": admin_port, "opendj_ldap_binddn": manager.config.get("ldap_binddn"), "ldapPassFn": DEFAULT_ADMIN_PW_PATH, "ldap_backend_type": "je", } with open("/app/templates/opendj-setup.properties") as fr: content = fr.read() % ctx with open("/opt/opendj/opendj-setup.properties", "w") as fw: fw.write(content) # 2) run installer keypasswd = decode_text( manager.secret.get("encoded_ldapTrustStorePass"), manager.secret.get("encoded_salt"), ).decode() cmd = " ".join([ "/opt/opendj/setup", "--no-prompt", "--cli", "--acceptLicense", "--propertiesFilePath /opt/opendj/opendj-setup.properties", "--usePkcs12keyStore /etc/certs/opendj.pkcs12", f"--keyStorePassword {keypasswd}", "--doNotStart", ]) out, err, code = exec_cmd(cmd) if code and err: logger.warning(err.decode()) if all([ os.environ.get("JAVA_VERSION", "") >= "1.8.0", os.path.isfile("/opt/opendj/config/config.ldif") ]): with open("/opt/opendj/config/java.properties", "a") as f: status_arg = "\nstatus.java-args=-Xms8m -client -Dcom.sun.jndi.ldap.object.disableEndpointIdentification=true" max_ram_percentage = os.environ.get("CN_MAX_RAM_PERCENTAGE", "75.0") repl_arg = f"\ndsreplication.java-args=-client -Dcom.sun.jndi.ldap.object.disableEndpointIdentification=true -XX:+UseContainerSupport -XX:MaxRAMPercentage={max_ram_percentage}" args = "".join([status_arg, repl_arg]) f.write(args)
def generate_keystore(suffix, hostname, keypasswd): # converts key to pkcs12 _, err, retcode = exec_cmd( f"openssl pkcs12 -export -inkey /etc/certs/{suffix}.key " f"-in /etc/certs/{suffix}.crt -out /etc/certs/{suffix}.pkcs12 " f"-name {hostname} -passout pass:'******'" # noqa: C812 ) assert retcode == 0, \ f"Failed to generate PKCS12 keystore; reason={err.decode()}" # imports p12 to keystore _, err, retcode = exec_cmd( f"keytool -importkeystore -srckeystore /etc/certs/{suffix}.pkcs12 " f"-srcstorepass {keypasswd} -srcstoretype PKCS12 " f"-destkeystore /etc/certs/{suffix}.jks -deststorepass {keypasswd} " "-deststoretype JKS -keyalg RSA -noprompt" # noqa: C812 ) assert retcode == 0, \ f"Failed to generate JKS keystore; reason={err.decode()}"
def gen_idp3_key(storepass): cmd = " ".join([ "java", "-classpath '/app/javalibs/*'", "net.shibboleth.utilities.java.support.security.BasicKeystoreKeyStrategyTool", "--storefile /etc/certs/sealer.jks", "--versionfile /etc/certs/sealer.kver", "--alias secret", "--storepass {}".format(storepass), ]) return exec_cmd(cmd)
def generate_keystore(cert_file, key_file, keystore_file, keystore_password): out, err, code = exec_cmd("openssl pkcs12 -export -name client-api " f"-out {keystore_file} " f"-inkey {key_file} " f"-in {cert_file} " f"-passout pass:{keystore_password}") if code != 0: logger.warning( f"Failed to generate keystore; reason={err.decode()}") return False return True
def export_openid_keys(keystore, keypasswd, alias, export_file): cmd = " ".join([ "java", "-Dlog4j.defaultInitOverride=true", "-cp /app/javalibs/*", "io.jans.as.client.util.KeyExporter", "-keystore {}".format(keystore), "-keypasswd {}".format(keypasswd), "-alias {}".format(alias), "-exportfile {}".format(export_file), ]) return exec_cmd(cmd)
def generate_openid_keys(passwd, jks_path, dn, exp=48, sig_keys=SIG_KEYS, enc_keys=ENC_KEYS): if os.path.isfile(jks_path): os.unlink(jks_path) cmd = ( "java -Dlog4j.defaultInitOverride=true " "-cp /app/javalibs/* " "io.jans.as.client.util.KeyGenerator " f"-enc_keys {enc_keys} -sig_keys {sig_keys} " f"-dnname '{dn}' -expiration_hours {exp} " f"-keystore {jks_path} -keypasswd {passwd}" ) return exec_cmd(cmd)
def export_admin_certificate(): cmd = " ".join([ "keytool -export", "-alias admin-cert", "-keystore /opt/opendj/config/admin-keystore", "-storepass:file /opt/opendj/config/admin-keystore.pin", "-keypass:file /opt/opendj/config/admin-keystore.pin", "-file /opt/opendj/config/admin-cert.crt", "-rfc", ]) out, err, code = exec_cmd(cmd) if code != 0: err = err or out logger.error(f"Unable to export admin-cert; reason={err.decode()}") sys.exit(1)
def key_from_cmd(): keygen = "" logger.info("Loading Serf key from serf keygen command") out, err, code = exec_cmd("serf keygen") if code != 0: logger.warning( f"Unable to self-generate Serf key; reason={err.decode()}") return keygen keygen = out.decode().strip() # save it for subsequent access manager.secret.set("serf_jans_ldap_key", keygen) return keygen
def generate_pkcs12(suffix, passwd, hostname): # Convert key to pkcs12 cmd = " ".join([ "openssl", "pkcs12", "-export", "-inkey /etc/certs/{}.key".format(suffix), "-in /etc/certs/{}.crt".format(suffix), "-out /etc/certs/{}.pkcs12".format(suffix), "-name {}".format(hostname), "-passout pass:{}".format(passwd), ]) _, err, retcode = exec_cmd(cmd) assert retcode == 0, "Failed to generate PKCS12 file; reason={}".format( err)
def peers_from_serf_membership(): out, err, code = exec_cmd("serf members -tag role=ldap -status=alive -format json") if code != 0: err = err or out logger.warning(f"Unable to get peers; reason={err.decode()}") return [] members = json.loads(out.decode())["members"] return [ { "name": member["name"], "addr": member["addr"], "tags": member["tags"], } for member in members ]
def recreate_admin_truststore(): export_admin_certificate() os.unlink("/opt/opendj/config/admin-truststore") cmd = " ".join([ "keytool -import -trustcacerts", "-alias admin-cert", "-keystore /opt/opendj/config/admin-truststore", "-storepass:file /opt/opendj/config/admin-keystore.pin", "-keypass:file /opt/opendj/config/admin-keystore.pin", "-file /opt/opendj/config/admin-cert.crt", "-noprompt", ]) out, err, code = exec_cmd(cmd) if code != 0: err = err or out logger.error(f"Unable to add new cert; reason={err.decode()}") sys.exit(1)
def regenerate_ldap_pkcs12(): suffix = "opendj" passwd = manager.secret.get("ldap_truststore_pass") hostname = manager.config.get("hostname") # Convert key to pkcs12 cmd = " ".join([ "openssl", "pkcs12", "-export", f"-inkey /etc/certs/{suffix}.key", f"-in /etc/certs/{suffix}.crt", f"-out /etc/certs/{suffix}.pkcs12", f"-name {hostname}", f"-passout pass:{passwd}", ]) _, err, retcode = exec_cmd(cmd) if retcode != 0: raise SubprocessError( f"Failed to generate PKCS12 file; reason={err.decode()}")
def generate_openid_keys(passwd, jks_path, jwks_path, dn, exp=365, sig_keys=DEFAULT_SIG_KEYS, enc_keys=DEFAULT_ENC_KEYS): if os.path.isfile(jks_path): os.unlink(jks_path) cmd = ("java -Dlog4j.defaultInitOverride=true " "-cp /app/javalibs/* " "io.jans.as.client.util.KeyGenerator " f"-enc_keys {enc_keys} -sig_keys {sig_keys} " f"-dnname '{dn}' -expiration_hours {exp} " f"-keystore {jks_path} -keypasswd {passwd}") out, err, retcode = exec_cmd(cmd) if retcode == 0: with open(jwks_path, "w") as f: f.write(out.decode()) return out, err, retcode
def main(): manager = get_manager() for addr in peers_from_file(): register_serf_peer(manager, addr) addr = guess_serf_addr() register_serf_peer(manager, addr) mcast = as_boolean(os.environ.get("CN_SERF_MULTICAST_DISCOVER", False)) if mcast: # join Serf cluster using multicast (no extra code needed) return # join Serf cluster manually peers = " ".join(get_serf_peers(manager)) out, err, code = exec_cmd(f"serf join {peers}") err = err or out if code != 0: logger.warning(f"Unable to join Serf cluster; reason={err}")
def keytool_delete_key(jks_fn, alias, password): cmd = f"keytool -delete -alias {alias} -keystore {jks_fn} -storepass {password}" return exec_cmd(cmd)