Skip to content

Commit

Permalink
Chained proxy configuration create refactoring
Browse files Browse the repository at this point in the history
  • Loading branch information
rjpmestre committed Apr 24, 2024
1 parent b8a335b commit d945e30
Show file tree
Hide file tree
Showing 9 changed files with 814 additions and 229 deletions.
236 changes: 7 additions & 229 deletions java/code/src/com/redhat/rhn/manager/system/SystemManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,11 @@

package com.redhat.rhn.manager.system;

import static com.suse.utils.Predicates.allProvided;
import static com.suse.utils.Predicates.isAbsent;
import static java.util.Collections.emptyList;
import static java.util.Collections.emptyMap;
import static java.util.Collections.emptySet;
import static java.util.Optional.ofNullable;

import com.redhat.rhn.common.RhnRuntimeException;
import com.redhat.rhn.common.client.ClientCertificate;
import com.redhat.rhn.common.client.InvalidCertificateException;
import com.redhat.rhn.common.conf.Config;
Expand Down Expand Up @@ -118,6 +115,7 @@
import com.redhat.rhn.manager.system.entitling.SystemEntitlementManager;
import com.redhat.rhn.manager.system.entitling.SystemEntitler;
import com.redhat.rhn.manager.system.entitling.SystemUnentitler;
import com.redhat.rhn.manager.system.proxycontainerconfig.ProxyContainerConfigCreate;
import com.redhat.rhn.manager.user.UserManager;
import com.redhat.rhn.taskomatic.task.systems.SystemsOverviewUpdateDriver;
import com.redhat.rhn.taskomatic.task.systems.SystemsOverviewUpdateWorker;
Expand All @@ -138,23 +136,15 @@
import com.suse.manager.webui.services.iface.MonitoringManager;
import com.suse.manager.webui.services.iface.SaltApi;
import com.suse.manager.webui.services.iface.VirtManager;
import com.suse.manager.webui.services.impl.SaltSSHService;
import com.suse.manager.webui.services.impl.runner.MgrUtilRunner;
import com.suse.manager.webui.utils.YamlHelper;
import com.suse.manager.xmlrpc.dto.SystemEventDetailsDto;
import com.suse.utils.Opt;

import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream;
import org.apache.commons.compress.compressors.gzip.GzipCompressorOutputStream;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.hibernate.Session;

import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.IDN;
import java.security.SecureRandom;
Expand All @@ -177,7 +167,6 @@
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;


Expand Down Expand Up @@ -488,15 +477,6 @@ public static DataResult<Map<String, Object>> packagesFromChannel(Long sid, Long
return m.execute(params);
}

private static String createUniqueId(List<String> fields) {
// craft unique id based on given data
String delimiter = "_";
return delimiter + fields
.stream()
.reduce((i1, i2) -> i1 + delimiter + i2)
.orElseThrow();
}

/**
* Create an empty system profile with required values based on given data.
*
Expand Down Expand Up @@ -527,7 +507,7 @@ public MinionServer createSystemProfile(User creator, String systemName, Map<Str
throw new SystemsExistException(matchingProfiles.stream().map(Server::getId).collect(Collectors.toList()));
}

String uniqueId = createUniqueId(
String uniqueId = SystemManagerUtils.createUniqueId(
Arrays.asList(hwAddress, hostname).stream().flatMap(Opt::stream).collect(Collectors.toList()));

MinionServer server = new MinionServer();
Expand Down Expand Up @@ -767,7 +747,7 @@ public void deleteServer(User user, Long sid, boolean deleteSaltKey) {

// clean known_hosts
if (server.asMinionServer().isPresent()) {
removeSaltSSHKnownHosts(server);
SystemManagerUtils.removeSaltSSHKnownHosts(saltApi, server);
}

server.asMinionServer().ifPresent(minion -> {
Expand All @@ -790,22 +770,6 @@ private void deleteSystemOverview(Long sid) {
mode.executeUpdate(Map.of("sid", sid));
}

private void removeSaltSSHKnownHosts(Server server) {
Integer sshPort = server.getProxyInfo() != null ? server.getProxyInfo().getSshPort() : null;
int port = sshPort != null ? sshPort : SaltSSHService.SSH_DEFAULT_PORT;
Optional.ofNullable(server.getHostname()).ifPresentOrElse(
hostname -> {
Optional<MgrUtilRunner.RemoveKnowHostResult> result =
saltApi.removeSaltSSHKnownHost(hostname, port);
boolean removed = result.map(r -> "removed".equals(r.getStatus())).orElse(false);
if (!removed) {
log.warn("Hostname {}:{} could not be removed from /var/lib/salt/.ssh/known_hosts: {}",
hostname, port, result.map(r -> r.getComment()).orElse(""));
}
},
() -> log.warn("Unable to remove SSH key for {} from /var/lib/salt/.ssh/known_hosts: unknown hostname",
server.getName()));
}

/**
* Adds servers to a server group
Expand Down Expand Up @@ -2163,75 +2127,6 @@ public static void activateProxy(Server server, String version)
}
}

private Supplier<RhnRuntimeException> raiseAndLog(String message) {
log.error(message);
return () -> new RhnRuntimeException(message);
}

private Optional<Server> findByAnyFqdn(Set<String> fqdns) {
for (String fqdn : fqdns) {
Optional<Server> server = ServerFactory.findByFqdn(fqdn);
if (server.isPresent()) {
return server;
}
}
return Optional.empty();
}

private Server getOrCreateProxySystem(
User creator, String proxyName, Set<String> fqdns, Integer port, String sshPublicKey
) {
Optional<Server> existing = findByAnyFqdn(fqdns);
if (existing.isPresent()) {
Server server = existing.get();
if (!(server.hasEntitlement(EntitlementManager.FOREIGN) ||
server.hasEntitlement(EntitlementManager.SALT))) {
throw new SystemsExistException(List.of(server.getId()));
}
// The SSH key is going to change remove it from the known hosts
removeSaltSSHKnownHosts(server);
ProxyInfo info = server.getProxyInfo();
if (info == null) {
info = new ProxyInfo();
info.setServer(server);
server.setProxyInfo(info);
}
info.setSshPort(port);
info.setSshPublicKey(sshPublicKey.getBytes());
return server;
}
Server server = ServerFactory.createServer();
server.setName(proxyName);
server.setHostname(proxyName);
server.getFqdns().addAll(fqdns.stream()
.filter(fqdn -> !fqdn.contains("*"))
.map(fqdn -> new ServerFQDN(server, fqdn)).collect(Collectors.toList()));
server.setOrg(creator.getOrg());
server.setCreator(creator);

String uniqueId = createUniqueId(List.of(proxyName));
server.setDigitalServerId(uniqueId);
server.setMachineId(uniqueId);
server.setOs("(unknown)");
server.setRelease("(unknown)");
server.setSecret(RandomStringUtils.random(64, 0, 0, true, true,
null, new SecureRandom()));
server.setAutoUpdate("N");
server.setContactMethod(ServerFactory.findContactMethodByLabel("default"));
server.setLastBoot(System.currentTimeMillis() / 1000);
server.setServerArch(ServerFactory.lookupServerArchByLabel("x86_64-redhat-linux"));
server.updateServerInfo();
ServerFactory.save(server);

ProxyInfo info = new ProxyInfo();
info.setServer(server);
info.setSshPort(port);
info.setSshPublicKey(sshPublicKey.getBytes());
server.setProxyInfo(info);

systemEntitlementManager.setBaseEntitlement(server, EntitlementManager.FOREIGN);
return server;
}

/**
* Create and provide proxy container configuration.
Expand Down Expand Up @@ -2261,127 +2156,10 @@ public byte[] createProxyContainerConfig(User user, String proxyName, Integer pr
SSLCertPair caPair, String caPassword, SSLCertData certData,
SSLCertManager certManager)
throws IOException, InstantiationException, SSLCertGenerationException {
/* Prepare the archive streams where the config files will be stored into */
ByteArrayOutputStream bytesOut = new ByteArrayOutputStream();
BufferedOutputStream bufOut = new BufferedOutputStream(bytesOut);
GzipCompressorOutputStream gzOut = new GzipCompressorOutputStream(bufOut);
TarArchiveOutputStream tarOut = new TarArchiveOutputStream(gzOut);

// Create the proxy SSH keys ahead
MgrUtilRunner.SshKeygenResult proxySshKey = saltApi.generateSSHKey(null, null)
.orElseThrow(raiseAndLog("Could not generate proxy salt-ssh SSH keys."));

if (!(proxySshKey.getReturnCode() == 0 || proxySshKey.getReturnCode() == -1)) {
throw raiseAndLog("Generating proxy salt-ssh SSH keys failed: " + proxySshKey.getStderr()).get();
}

// config.yaml
Map<String, Object> config = new HashMap<>();

config.put("server", server);
config.put("max_cache_size_mb", maxCache);
config.put("email", email);
config.put("server_version", ConfigDefaults.get().getProductVersion());
config.put("proxy_fqdn", proxyName);

SSLCertPair proxyPair = proxyCertKey;
String rootCaCert = rootCA;
if (proxyCertKey == null || !proxyCertKey.isComplete()) {
proxyPair = certManager.generateCertificate(caPair, caPassword, certData);
rootCaCert = caPair.getCertificate();
}
config.put("ca_crt", rootCaCert);

addTarEntry(tarOut, "config.yaml", YamlHelper.INSTANCE.dumpPlain(config).getBytes(), 0644);
// End of config.yaml

// httpd.yaml
Map<String, Object> httpdRootConfig = new HashMap<>();
Map<String, Object> httpdConfig = new HashMap<>();

Set<String> fqdns = new HashSet<>();
fqdns.add(proxyName);
if (proxyCertKey != null && proxyCertKey.getCertificate() != null) {
// Get the cnames from the certificate using openssl
fqdns.addAll(certManager.getNamesFromSslCert(proxyCertKey.getCertificate()));
}
else {
fqdns.addAll(certData.getAllCnames());
}
Server proxySystem = getOrCreateProxySystem(user, proxyName, fqdns, proxyPort, proxySshKey.getPublicKey());
ClientCertificate cert = SystemManager.createClientCertificate(proxySystem);
httpdConfig.put("system_id", cert.asXml());

// Check the SSL files using mgr-ssl-cert-setup
try {
String certificate = saltApi.checkSSLCert(rootCaCert, proxyPair,
intermediateCAs != null ? intermediateCAs : List.of());
httpdConfig.put("server_crt", certificate);
httpdConfig.put("server_key", proxyPair.getKey());
}
catch (IllegalArgumentException err) {
throw new RhnRuntimeException("Certificate check failure: " + err.getMessage());
}

httpdRootConfig.put("httpd", httpdConfig);
addTarEntry(tarOut, "httpd.yaml", YamlHelper.INSTANCE.dumpPlain(httpdRootConfig).getBytes(), 0600);
// End of httpd.yaml

// ssh.yaml
Map<String, Object> sshRootConfig = new HashMap<>();
Map<String, Object> sshConfig = new HashMap<>();

sshConfig.put("server_ssh_key_pub", getServerSshPublicKey(user, server));
sshConfig.put("server_ssh_push", proxySshKey.getKey());
sshConfig.put("server_ssh_push_pub", proxySshKey.getPublicKey());

sshRootConfig.put("ssh", sshConfig);
addTarEntry(tarOut, "ssh.yaml", YamlHelper.INSTANCE.dumpPlain(sshRootConfig).getBytes(), 0600);
// End of ssh.yaml

tarOut.finish();
tarOut.close();

return bytesOut.toByteArray();
}

private String getServerSshPublicKey(User user, String serverFqdn) {
String localManagerFqdn = Config.get().getString(ConfigDefaults.SERVER_HOSTNAME);
if (isAbsent(localManagerFqdn)) {
throw raiseAndLog("Could not determine the local SUSE Manager FQDN.").get();
}

if (localManagerFqdn.equals(serverFqdn)) {
MgrUtilRunner.SshKeygenResult serverSshKey =
saltApi.generateSSHKey(SaltSSHService.SSH_KEY_PATH, SaltSSHService.SUMA_SSH_PUB_KEY)
.orElseThrow(raiseAndLog("Could not generate salt-ssh public key."));

if (!(serverSshKey.getReturnCode() == 0 || serverSshKey.getReturnCode() == -1)) {
throw raiseAndLog("Generating salt-ssh public key failed: " + serverSshKey.getStderr()).get();
}

return serverSshKey.getPublicKey();
}

Server serverServer = ServerFactory.lookupProxiesByOrg(user).stream()
.filter(proxy -> serverFqdn.equals(proxy.getName())).findFirst()
.orElseThrow(raiseAndLog("Could not find specified server named " + serverFqdn +
" in the organization."));

if (!allProvided(serverServer.getProxyInfo(), serverServer.getProxyInfo().getSshPublicKey())) {
throw raiseAndLog("Could not find the public SSH key for the server.").get();
}

return new String(serverServer.getProxyInfo().getSshPublicKey());
}

private void addTarEntry(TarArchiveOutputStream tarOut, String name, byte[] data, int mode) throws IOException {
TarArchiveEntry entry = new TarArchiveEntry(name);
entry.setSize(data.length);
entry.setMode(mode);
tarOut.putArchiveEntry(entry);
tarOut.write(data);
tarOut.closeArchiveEntry();
return new ProxyContainerConfigCreate().create(
saltApi, systemEntitlementManager, user, server, proxyName, proxyPort, maxCache, email,
rootCA, intermediateCAs, proxyCertKey, caPair, caPassword, certData, certManager);
}

/**
Expand Down Expand Up @@ -3936,7 +3714,7 @@ else if (pkgState == PackageStates.PURGED) { // using PURGED flag for unmanaged
* @return the created system
*/
public Server registerPeripheralServer(User creator, String fqdn) {
String uniqueId = createUniqueId(List.of(fqdn));
String uniqueId = SystemManagerUtils.createUniqueId(List.of(fqdn));

Server existing = ServerFactory.lookupForeignSystemByDigitalServerId(uniqueId);
if (existing != null) {
Expand Down
Loading

0 comments on commit d945e30

Please sign in to comment.