summaryrefslogtreecommitdiffstats
path: root/src/main/java/org/openslx
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/org/openslx')
-rw-r--r--src/main/java/org/openslx/taskmanager/tasks/LighttpdHttps.java245
1 files changed, 204 insertions, 41 deletions
diff --git a/src/main/java/org/openslx/taskmanager/tasks/LighttpdHttps.java b/src/main/java/org/openslx/taskmanager/tasks/LighttpdHttps.java
index 08fac2a..20ef463 100644
--- a/src/main/java/org/openslx/taskmanager/tasks/LighttpdHttps.java
+++ b/src/main/java/org/openslx/taskmanager/tasks/LighttpdHttps.java
@@ -3,9 +3,11 @@ package org.openslx.taskmanager.tasks;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.List;
import org.openslx.satserver.util.Exec;
+import org.openslx.satserver.util.Exec.ExecCallback;
import org.openslx.satserver.util.Util;
import org.openslx.taskmanager.api.AbstractTask;
@@ -19,6 +21,8 @@ public class LighttpdHttps extends AbstractTask
{
private Output status = new Output();
+
+ // --- User-supplied cert ---
@Expose
private String importcert = null;
@@ -26,16 +30,43 @@ public class LighttpdHttps extends AbstractTask
private String importkey = null;
@Expose
private String importchain = null;
-
+
+ // --- Let's encrypt or similar (ACME) ---
+
+ @Expose
+ private String acmeMode = null;
+ @Expose
+ private String acmeMail = null;
+ @Expose
+ private String[] acmeDomains = null;
+ @Expose
+ private String acmeProvider = null;
+ @Expose
+ private boolean acmeWipeAll = false;
+ @Expose
+ private String acmeKeyId = null;
+ @Expose
+ private String acmeHmacKey = null;
+
+ // ---- Self-signed ----
+
+ /** IP address to put in self-signed cert */
@Expose
private String proxyip = null;
-
+
+ // ------ Force HTTPS? ------
+
+ /** Enable redirect from HTTP to HTTPS? */
@Expose
private boolean redirect;
+ /** Only set the requested redirect mode, nothing else */
@Expose
private boolean redirectOnly;
+
+ // -------
- private List<String> baseCmd = Arrays.asList( new String[] { "sudo", "-n", "-u", "root", "/opt/taskmanager/scripts/install-https" } );
+ private static List<String> BASE_CMD = Collections.unmodifiableList( Arrays.asList( new String[] {
+ "sudo", "-n", "-u", "root", "/opt/taskmanager/scripts/install-https" } ) );
@Override
protected boolean initTask()
@@ -51,14 +82,19 @@ public class LighttpdHttps extends AbstractTask
return setRedirect();
if ( this.importcert != null && this.importkey != null && !this.importcert.isEmpty() && !this.importkey.isEmpty() )
return createFromInput();
+ if ( this.acmeMode != null )
+ return handleAcme();
if ( this.proxyip != null && !this.proxyip.isEmpty() )
return createRandom();
return disableHttps();
}
+ /**
+ * Create a new self-signed certificate and use that.
+ */
private boolean createRandom()
{
- List<String> cmd = new ArrayList<>( baseCmd );
+ List<String> cmd = new ArrayList<>( BASE_CMD );
if ( this.redirect ) {
cmd.add( "--redirect" );
}
@@ -66,19 +102,21 @@ public class LighttpdHttps extends AbstractTask
cmd.add( this.proxyip );
int ret = Exec.sync( 45, cmd.toArray( new String[ cmd.size() ] ) );
if ( ret != 0 ) {
- status.error = "generator exited with code " + ret;
+ status.processStdOut( "generator exited with code " + ret );
return false;
}
return true;
}
+ /**
+ * Create HTTPS config from cert/key/chain provided in task data.
+ */
private boolean createFromInput()
{
// Import supplied certificate and key. Test if they are valid first
File tmpKey = null;
File tmpCert = null;
File tmpChain = null;
- List<String> cmd;
try {
try {
tmpCert = File.createTempFile( "bwlp-", ".pem" );
@@ -90,53 +128,163 @@ public class LighttpdHttps extends AbstractTask
Util.writeStringToFile( tmpChain, this.importchain );
}
} catch ( Exception e ) {
- status.error = "Could not create temporary files: " + e.getMessage();
+ status.processStdOut( "Could not create temporary files: " + e.getMessage() );
return false;
}
- int ret;
- cmd = new ArrayList<>( baseCmd );
- cmd.add( "--test" );
- cmd.add( tmpKey.getAbsolutePath() );
- cmd.add( tmpCert.getAbsolutePath() );
- ret = Exec.sync( 45, cmd.toArray( new String[ cmd.size() ] ) );
- if ( ret != 0 ) {
- status.error = "Given key and certificate do not match, or have invalid format (exit code: " + ret + ")";
- return false;
+ return createFromFiles( tmpKey, tmpCert, tmpChain );
+ } finally {
+ if ( tmpKey != null ) {
+ tmpKey.delete();
}
- cmd = new ArrayList<>( baseCmd );
- if ( this.redirect ) {
- cmd.add( "--redirect" );
+ if ( tmpCert != null ) {
+ tmpCert.delete();
}
- cmd.add( "--import" );
- cmd.add( tmpKey.getAbsolutePath() );
- cmd.add( tmpCert.getAbsolutePath() );
if ( tmpChain != null ) {
- cmd.add( tmpChain.getAbsolutePath() );
+ tmpChain.delete();
}
- ret = Exec.sync( 45, cmd.toArray( new String[ cmd.size() ] ) );
- if ( ret != 0 ) {
- status.error = "import exited with code " + ret;
- return false;
- }
- return true;
- } finally {
- if ( tmpKey != null )
- tmpKey.delete();
- if ( tmpCert != null )
- tmpCert.delete();
}
}
+
+ /**
+ * Call deployment script, passing along predefined key/cert/chain files.
+ */
+ private boolean createFromFiles( File tmpKey, File tmpCert, File tmpChain )
+ {
+ List<String> cmd;
+ int ret;
+
+ cmd = new ArrayList<>( BASE_CMD );
+ cmd.add( "--test" );
+ cmd.add( tmpKey.getAbsolutePath() );
+ cmd.add( tmpCert.getAbsolutePath() );
+ ret = Exec.sync( 45, status, cmd.toArray( new String[ cmd.size() ] ) );
+ if ( ret != 0 ) {
+ status.processStdOut( "Given key and certificate do not match, or have invalid format (exit code: " + ret + ")" );
+ return false;
+ }
+ cmd = new ArrayList<>( BASE_CMD );
+ if ( this.redirect ) {
+ cmd.add( "--redirect" );
+ }
+ cmd.add( "--import" );
+ cmd.add( tmpKey.getAbsolutePath() );
+ cmd.add( tmpCert.getAbsolutePath() );
+ if ( tmpChain != null ) {
+ cmd.add( tmpChain.getAbsolutePath() );
+ }
+ ret = Exec.sync( 45, status, cmd.toArray( new String[ cmd.size() ] ) );
+ if ( ret != 0 ) {
+ status.processStdOut( "import exited with code " + ret );
+ return false;
+ }
+ return true;
+ }
+
+ private boolean handleAcme()
+ {
+ if ( this.acmeMode.equals( "issue" ) ) {
+ return createWithAcme();
+ }
+ if ( this.acmeMode.equals( "renew" ) ) {
+ return checkRenewAcme();
+ }
+ if ( this.acmeMode.equals( "try-enable" ) ) {
+ return tryEnableAcme();
+ }
+ status.processStdOut( "Invalid ACME mode: " + this.acmeMode );
+ return false;
+ }
+
+ /**
+ * Create a brand new certificate via ACME
+ */
+ private boolean createWithAcme()
+ {
+ if ( Util.isEmpty( this.acmeMail ) ) {
+ status.processStdOut( "Invalid E-Mail provided" );
+ return false;
+ }
+ if ( this.acmeDomains == null || this.acmeDomains.length == 0 ) {
+ status.processStdOut( "No domains provided" );
+ return false;
+ }
+ List<String> cmd = new ArrayList<>( BASE_CMD );
+ if ( this.redirect ) {
+ cmd.add( "--redirect" );
+ }
+ if ( this.acmeWipeAll ) {
+ cmd.add( "--acme-wipe" );
+ }
+ if ( this.acmeKeyId != null ) {
+ cmd.add( "--acme-key-id" );
+ cmd.add( this.acmeKeyId );
+ }
+ if ( this.acmeHmacKey != null ) {
+ cmd.add( "--acme-hmac-key" );
+ cmd.add( this.acmeHmacKey );
+ }
+ cmd.add( "--acme-issue" );
+ cmd.add( this.acmeProvider );
+ cmd.add( this.acmeMail );
+ for ( String d : this.acmeDomains ) {
+ cmd.add( d );
+ }
+ int ret = Exec.sync( 120, status, cmd.toArray( new String[ cmd.size() ] ) );
+ if ( ret != 0 ) {
+ status.processStdOut( "acme issue exited with code " + ret );
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Trigger a renew check and according renew if required.
+ */
+ private boolean checkRenewAcme()
+ {
+ if ( this.acmeDomains == null || this.acmeDomains.length == 0 ) {
+ status.processStdOut( "No domain provided" );
+ return false;
+ }
+ List<String> cmd = new ArrayList<>( BASE_CMD );
+ if ( this.redirect ) {
+ cmd.add( "--redirect" );
+ }
+ cmd.add( "--acme-renew" );
+ cmd.add( this.acmeDomains[0] ); // Only needs primary domain to find config
+ int ret = Exec.sync( 120, status, cmd.toArray( new String[ cmd.size() ] ) );
+ if ( ret != 0 ) {
+ status.processStdOut( "acme renew exited with code " + ret );
+ return false;
+ }
+ return true;
+ }
+
+ private boolean tryEnableAcme()
+ {
+ List<String> cmd = new ArrayList<>( BASE_CMD );
+ if ( this.redirect ) {
+ cmd.add( "--redirect" );
+ }
+ cmd.add( "--acme-try-enable" );
+ int ret = Exec.sync( 10, status, cmd.toArray( new String[ cmd.size() ] ) );
+ if ( ret != 0 ) {
+ status.processStdOut( "acme try-enable exited with code " + ret );
+ return false;
+ }
+ return true;
+ }
private boolean setRedirect()
{
- List<String> cmd = new ArrayList<>( baseCmd );
+ List<String> cmd = new ArrayList<>( BASE_CMD );
cmd.add( "--redirect-only" );
if ( this.redirect ) {
cmd.add( "--redirect" );
}
int ret = Exec.sync( 10, cmd.toArray( new String[ cmd.size() ] ) );
if ( ret != 0 ) {
- status.error = "set redirect exited with code " + ret;
+ status.processStdOut( "set redirect exited with code " + ret );
return false;
}
return true;
@@ -144,11 +292,11 @@ public class LighttpdHttps extends AbstractTask
private boolean disableHttps()
{
- List<String> cmd = new ArrayList<>( baseCmd );
+ List<String> cmd = new ArrayList<>( BASE_CMD );
cmd.add( "--disable" );
int ret = Exec.sync( 10, cmd.toArray( new String[ cmd.size() ] ) );
if ( ret != 0 ) {
- status.error = "disable exited with code " + ret;
+ status.processStdOut( "disable exited with code " + ret );
return false;
}
return true;
@@ -157,10 +305,25 @@ public class LighttpdHttps extends AbstractTask
/**
* Output - contains additional status data of this task
*/
- @SuppressWarnings( "unused" )
- private static class Output
+ private static class Output implements ExecCallback
{
- protected String error = null;
+ protected StringBuilder error;
+
+ @Override
+ public void processStdOut( String line )
+ {
+ if ( error == null ) {
+ error = new StringBuilder();
+ }
+ error.append( line );
+ error.append( '\n' );
+ }
+
+ @Override
+ public void processStdErr( String line )
+ {
+ processStdOut( line );
+ }
}
}