Skip to content

Commit eaf72b5

Browse files
committed
Add --web-auth basic command-line option
This option enables HTTP Basic authentication for the web interface instead of the default Digest authentication. This is useful when running Heritrix behind a reverse proxy that adds external authentication as typically they don't support Digest auth for the upstream server. #641
1 parent 0f0db37 commit eaf72b5

File tree

4 files changed

+40
-4
lines changed

4 files changed

+40
-4
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,12 @@
44

55
[Full Changelog](https://github.com/internetarchive/heritrix3/compare/3.9.0...HEAD)
66

7+
#### New features
8+
9+
- **Basic web auth:** You can now switch the web interface from Digest authentication to Basic authentication
10+
with the `--web-auth basic` command-line option. This is useful when running Heritrix behind a reverse proxy that
11+
adds external authentication.
12+
713
#### Fixes
814

915
- **Code editor:** The configuration editor and script console were upgraded to CodeMirror 6. This resolves some browser

docs/operating.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ Command-line Options
4242
Specifies a keystore path, keystore password, and key password for HTTPS use. Separate the values with
4343
commas and do not include whitespace. By default Heritrix will generate a self-signed certificate the
4444
first time it is run.
45+
--web-auth digest|basic
46+
Authentication mode for the web interface. **Default:** ``digest``
4547

4648
Environment Variables
4749
~~~~~~~~~~~~~~~~~~~~~

engine/src/main/java/org/archive/crawler/Heritrix.java

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,9 @@
5656
import org.restlet.Component;
5757
import org.restlet.Context;
5858
import org.restlet.Server;
59+
import org.restlet.data.ChallengeScheme;
5960
import org.restlet.data.Protocol;
61+
import org.restlet.security.ChallengeAuthenticator;
6062
import org.restlet.security.MapVerifier;
6163

6264

@@ -111,6 +113,13 @@ public class Heritrix {
111113
*/
112114
private static final String STARTLOG = "heritrix_dmesg.log";
113115

116+
private enum AuthMode {
117+
DIGEST, BASIC;
118+
119+
public String toString() {
120+
return name().toLowerCase();
121+
}
122+
}
114123

115124
private static void usage(PrintStream out, String[] args) {
116125
HelpFormatter hf = new HelpFormatter();
@@ -122,6 +131,8 @@ private static void usage(PrintStream out, String[] args) {
122131
private static Options options() {
123132
Options options = new Options();
124133
options.addOption("h", "help", true, "Usage information." );
134+
options.addOption(null, "web-auth", true, "Authentication mode for the" +
135+
" web interface: " + Arrays.toString(AuthMode.values()));
125136
options.addOption("a", "web-admin", true, "REQUIRED. Specifies the " +
126137
"authorization username and password which must be supplied to " +
127138
"access the web interface. This may be of the form " +
@@ -237,6 +248,7 @@ public void instanceMain(String[] args)
237248
// DEFAULTS until changed by cmd-line options
238249
int port = 8443;
239250
Set<String> bindHosts = new HashSet<String>();
251+
AuthMode authMode = AuthMode.DIGEST;
240252
String authLogin = "admin";
241253
String authPassword = null;
242254
String keystorePath;
@@ -330,6 +342,16 @@ public void instanceMain(String[] args)
330342
System.setProperty("https.proxyPort", proxyPort);
331343
}
332344

345+
if (cl.hasOption("web-auth")) {
346+
try {
347+
authMode = AuthMode.valueOf(cl.getOptionValue("web-auth").toUpperCase());
348+
} catch (IllegalArgumentException e) {
349+
System.err.println("Unsupported --web-auth value '" + cl.getOptionValue("web-auth") +
350+
"' (must be one of " + Arrays.toString(AuthMode.values()) + ")");
351+
System.exit(1);
352+
}
353+
}
354+
333355
// Restlet will reconfigure logging according to the system property
334356
// so we must set it for -l to work properly
335357
System.setProperty("java.util.logging.config.file", properties.getPath());
@@ -373,9 +395,13 @@ public void instanceMain(String[] args)
373395
MapVerifier verifier = new MapVerifier();
374396
verifier.getLocalSecrets().put(authLogin, authPassword.toCharArray());
375397

376-
RateLimitGuard guard = new RateLimitGuard(component.getContext().createChildContext(),
377-
"Authentication Required", UUID.randomUUID().toString());
378-
guard.setWrappedVerifier(verifier);
398+
Context guardContext = component.getContext().createChildContext();
399+
ChallengeAuthenticator guard = switch (authMode) {
400+
case BASIC -> new ChallengeAuthenticator(guardContext, false,
401+
ChallengeScheme.HTTP_BASIC, "Authentication Required", verifier);
402+
case DIGEST -> new RateLimitGuard(guardContext, "Authentication Required",
403+
UUID.randomUUID().toString(), verifier);
404+
};
379405
guard.setNext(new EngineApplication(engine));
380406

381407
component.getDefaultHost().attach(guard);

engine/src/main/java/org/archive/crawler/restlet/RateLimitGuard.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import org.restlet.Request;
2323
import org.restlet.Response;
2424
import org.restlet.ext.crypto.DigestAuthenticator;
25+
import org.restlet.security.LocalVerifier;
2526

2627
import java.util.logging.Logger;
2728

@@ -38,8 +39,9 @@ public class RateLimitGuard extends DigestAuthenticator {
3839

3940
protected long lastFailureTime = 0;
4041

41-
public RateLimitGuard(Context context, String realm, String serverKey) throws IllegalArgumentException {
42+
public RateLimitGuard(Context context, String realm, String serverKey, LocalVerifier verifier) throws IllegalArgumentException {
4243
super(context, realm, serverKey);
44+
setWrappedVerifier(verifier);
4345
}
4446

4547
@Override

0 commit comments

Comments
 (0)