diff --git a/.github/workflows/ssh-key-signer-server-docker.yml b/.github/workflows/ssh-key-signer-server-docker.yml index 3a55f4a..2e8d2e8 100644 --- a/.github/workflows/ssh-key-signer-server-docker.yml +++ b/.github/workflows/ssh-key-signer-server-docker.yml @@ -79,7 +79,7 @@ jobs: uses: docker/build-push-action@v6 with: context: ${{ env.PROJECT_DIR }}/${{ env.PROJECT }} - push: true + push: ${{ startsWith(github.ref, 'refs/tags/v') }} platforms: linux/amd64,linux/arm64 tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} diff --git a/server/ssh-key-signer-server/src/main/java/io/binarycodes/homelab/sshkeysigner/keymanagement/KeyService.java b/server/ssh-key-signer-server/src/main/java/io/binarycodes/homelab/sshkeysigner/keymanagement/KeyService.java index 4c1b843..dc041b5 100644 --- a/server/ssh-key-signer-server/src/main/java/io/binarycodes/homelab/sshkeysigner/keymanagement/KeyService.java +++ b/server/ssh-key-signer-server/src/main/java/io/binarycodes/homelab/sshkeysigner/keymanagement/KeyService.java @@ -154,17 +154,17 @@ public Optional signHostKey(final String filename, fina return signKey(SshCertificateType.HOST, filename, bytes, keyId, principals, applicationProperties.caHostValidity()); } - private Optional signKey(final SshCertificateType signType, final String filename, final byte[] bytes, final String keyId, final List principals, final Duration validitySeconds) { + private Optional signKey(final SshCertificateType certType, final String filename, final byte[] bytes, final String keyId, final List principals, final Duration validitySeconds) { try { final var publicKeyFileToSign = SshPublicKeyFileFactory.parse(bytes); final var keyPairToSign = SshKeyPair.getKeyPair(null, publicKeyFileToSign.toPublicKey()); - final var signedBy = switch (signType) { + final var signedBy = switch (certType) { case HOST -> readHostCAKeys(); case USER -> readUserCAKeys(); }; - final var signed = SshCertManager.generateCertificate(signType, keyPairToSign, keyId, principals, validitySeconds, applicationProperties.sourceAddresses(), applicationProperties.knownExtensions(), signedBy); + final var signed = SshCertManager.generateCertificate(certType, keyPairToSign, keyId, principals, validitySeconds, applicationProperties.sourceAddresses(), applicationProperties.knownExtensions(), signedBy); final var signedKey = SshPublicKeyFileFactory.create(signed.getCertificate(), publicKeyFileToSign.getComment(), SshPublicKeyFileFactory.OPENSSH_FORMAT); final var signedKeyString = new String(signedKey.getFormattedKey(), StandardCharsets.UTF_8); diff --git a/server/ssh-key-signer-server/src/main/java/io/binarycodes/homelab/sshkeysigner/keymanagement/SshCertManager.java b/server/ssh-key-signer-server/src/main/java/io/binarycodes/homelab/sshkeysigner/keymanagement/SshCertManager.java index c7ba9a9..4f4d8b6 100644 --- a/server/ssh-key-signer-server/src/main/java/io/binarycodes/homelab/sshkeysigner/keymanagement/SshCertManager.java +++ b/server/ssh-key-signer-server/src/main/java/io/binarycodes/homelab/sshkeysigner/keymanagement/SshCertManager.java @@ -14,16 +14,18 @@ import com.sshtools.common.ssh.components.jce.OpenSshEd25519Certificate; import com.sshtools.common.ssh.components.jce.OpenSshRsaCertificate; import com.sshtools.common.util.UnsignedInteger64; +import org.apache.commons.lang3.ObjectUtils; import java.io.IOException; import java.time.Duration; import java.time.Instant; import java.time.ZoneOffset; +import java.util.Collections; import java.util.List; public class SshCertManager { - public static SshCertificate generateCertificate(final SshCertificateType signType, + public static SshCertificate generateCertificate(final SshCertificateType certType, final SshKeyPair key, final String keyId, final List validPrincipals, @@ -32,19 +34,13 @@ public static SshCertificate generateCertificate(final SshCertificateType signTy final List knownExtensions, final SshKeyPair signedBy) throws SshException, IOException, InvalidPassphraseException { - final var type = switch (signType) { + final var type = switch (certType) { case HOST -> SshCertificate.SSH_CERT_TYPE_HOST; case USER -> SshCertificate.SSH_CERT_TYPE_USER; }; - final var criticalOptions = new CriticalOption.Builder() - .sourceAddress(sourceAddresses.toArray(String[]::new)) - .build(); - - final var extensionsBuilder = new CertificateExtension.Builder(); - knownExtensions.forEach(extension -> extensionsBuilder.knownExtension(new NamedCertificateExtension(extension, true))); - - final var extensions = extensionsBuilder.build(); + final var criticalOptions = buildCriticalOptions(certType, sourceAddresses); + final var extensions = buildCertificateExtensions(certType, knownExtensions); final var now = Instant.now().atZone(ZoneOffset.UTC); final UnsignedInteger64 validAfter = new UnsignedInteger64(now.toEpochSecond()); @@ -53,6 +49,28 @@ public static SshCertificate generateCertificate(final SshCertificateType signTy return generateCertificate(key, 0L, type, keyId, validPrincipals, validAfter, validBefore, criticalOptions, extensions, signedBy); } + + private static List buildCriticalOptions(SshCertificateType certType, List sourceAddresses) { + if (certType == SshCertificateType.HOST || ObjectUtils.isEmpty(sourceAddresses)) { + return Collections.emptyList(); + } + + return new CriticalOption.Builder() + .sourceAddress(sourceAddresses.toArray(String[]::new)) + .build(); + } + + private static List buildCertificateExtensions(SshCertificateType certType, List knownExtensions) { + if (certType == SshCertificateType.HOST || ObjectUtils.isEmpty(knownExtensions)) { + return Collections.emptyList(); + } + + final var extensionsBuilder = new CertificateExtension.Builder(); + knownExtensions.forEach(extension -> extensionsBuilder.knownExtension(new NamedCertificateExtension(extension, true))); + return extensionsBuilder.build(); + } + + public static SshCertificate generateCertificate(final SshKeyPair key, final long serial, final int type,