From 399ecccba0a361db79d6ff70ff64a0edd4a73d4f Mon Sep 17 00:00:00 2001
From: Kai Reinhard <K.Reinhard@micromata.de>
Date: Sun, 18 Apr 2021 22:59:09 +0000
Subject: [PATCH] Version 0.5 prepared.
---
borgbutler-docker/buildDocker.sh | 2
borgbutler-docker/app/entrypoint.sh | 4
/dev/null | 87 -----------------
borgbutler-server/src/test/kotlin/de/micromata/borgbutler/server/user/UserFilterTest.kt | 28 +++++
README.adoc | 24 ++++
borgbutler-server/src/main/kotlin/de/micromata/borgbutler/server/rest/RestUtils.kt | 2
build.gradle | 6
borgbutler-server/src/main/kotlin/de/micromata/borgbutler/server/user/UserFilter.kt | 98 +++++++++++++++++++
borgbutler-docker/README.adoc | 6
borgbutler-docker/app/Dockerfile | 2
borgbutler-server/src/main/kotlin/de/micromata/borgbutler/server/RunningMode.kt | 2
11 files changed, 163 insertions(+), 98 deletions(-)
diff --git a/README.adoc b/README.adoc
index 92f511a..88b61f8 100644
--- a/README.adoc
+++ b/README.adoc
@@ -69,6 +69,8 @@
You may refer the log file through the web browser or in `$HOME/BorgButler/borgbutler.log`.
+For new versions of BorgButler or for changing the running options of your BorgButler container, simply delete the BorgButler docker container by `docker rm borgbutler` and call `docker run` again.
+
=== Starting from Java zip
You'll need OpenJDK 9+.
@@ -139,3 +141,25 @@
[link=doc/images/screen-logviewer.png]
image::doc/images/screen-logviewer.png[Log viewer of BorgButler,800]
+
+== Trouble shooting
+=== Docker
+==== Increase memory (OutOfMemory)
+1. `docker rm borgbutler`
+2. `docker run -e JAVA_OPTS="-Xmx2G" -v ...`
+If your docker container crashes on heavy usage of large borg archives, check the memory settings of your docker installation.
+
+=== How to download/restore?
+You may restore files or while directories by simply clicking the download icon. If you run BorgButler on localhost as Java process (not docker), after restoring single
+files or directories your system's file browser is opened.
+
+You will find all the restored files in the `restore` subdirectory of your BorgButler home directory.
+
+=== Access to Borg repo fails (lock)
+BorgButler tries to run only one job per repo at the same time. If your log file shows error on `Failed to create/acquire the lock ... lock.exclusive (timeout)` simply restart BorgButler.
+
+
+=== Browsing produces security warnings
+Due to security reasons, BorgButler is only available by the localhost's web browser. For docker
+installations the clients of the private net `172.17.0.*` are allowed.
+For enabling other client ip's, you may use option `-DallowedClientIps=192.168.78.` (docker: `docker run -e JAVA_OPTS="-DallowedeClientIps=192.168.78." -v ...` if your really now what you're doing!!! There is now https available at default!!!
diff --git a/borgbutler-docker/README.adoc b/borgbutler-docker/README.adoc
index 4b79323..4933184 100644
--- a/borgbutler-docker/README.adoc
+++ b/borgbutler-docker/README.adoc
@@ -8,12 +8,12 @@
ifdef::env-github,env-browser[:outfilesuffix: .adoc]
== Releasing new version
-1. Edit `build.gradle: version=0.4-SNAPSHOT`
-2. Edit `borgbutler-docker/Dockerfile: ARG DEPENDENCY=target/dependency/borgbutler-server-0.4-SNAPSHOT`
+1. Edit `build.gradle: version=0.5`
+2. Edit `borgbutler-docker/Dockerfile: ARG DEPENDENCY=target/dependency/borgbutler-server-0.5`
== Building docker container
-1. `gradle clean distZip` in top directory (compiles and builds the project)
+1. `gradle clean dist` in top directory (compiles and builds the project)
2. `cd borgbutler-docker`
3. `./buildDocker.sh` (builds the docker container)
diff --git a/borgbutler-docker/app/Dockerfile b/borgbutler-docker/app/Dockerfile
index b791fa9..d252cd9 100644
--- a/borgbutler-docker/app/Dockerfile
+++ b/borgbutler-docker/app/Dockerfile
@@ -16,7 +16,7 @@
USER borgbutler:borgbutler
# Don't put fat jar files in docker images: https://phauer.com/2019/no-fat-jar-in-docker-image/
-ARG DEPENDENCY=target/dependency/borgbutler-server-0.5-SNAPSHOT
+ARG DEPENDENCY=target/dependency/borgbutler-server-0.5
COPY ${DEPENDENCY}/lib /app/lib
#COPY ${DEPENDENCY}/META-INF /app/META-INF
#COPY ${DEPENDENCY}/BOOT-INF/classes /app
diff --git a/borgbutler-docker/app/entrypoint.sh b/borgbutler-docker/app/entrypoint.sh
index 1e92dd7..9afb503 100644
--- a/borgbutler-docker/app/entrypoint.sh
+++ b/borgbutler-docker/app/entrypoint.sh
@@ -47,9 +47,9 @@
#Trap SIGTERM
trap cleanup INT SIGTERM
-echo "Starting java ${JAVA_OPTS} -cp app/web/*:app/lib/* -DBorgButlerHome=/BorgButler/ -DapplicationHome=/app -DbindAddress=0.0.0.0 -DallowedClientIps=172.17. ${JAVA_MAIN} -q ${JAVA_ARGS}"
+echo "Starting java ${JAVA_OPTS} -cp app/web/*:app/lib/* -DBorgButlerHome=/BorgButler/ -Dserver.address=0.0.0.0 ${JAVA_MAIN} ${JAVA_ARGS}"
-java $JAVA_OPTS -cp app/web/*:app/lib/* -DBorgButlerHome=/BorgButler/ -DapplicationHome=/app -Dserver.address=0.0.0.0 -DallowedClientIps=172.17. $JAVA_MAIN -q $JAVA_ARGS &
+java $JAVA_OPTS -cp app/web/*:app/lib/* -DBorgButlerHome=/BorgButler/ -Dserver.address=0.0.0.0 -Ddocker=true $JAVA_MAIN $JAVA_ARGS &
CHILD=$!
wait $CHILD
diff --git a/borgbutler-docker/buildDocker.sh b/borgbutler-docker/buildDocker.sh
index 7a4f5dd..d451746 100755
--- a/borgbutler-docker/buildDocker.sh
+++ b/borgbutler-docker/buildDocker.sh
@@ -20,4 +20,4 @@
echo "Run without ssh: 'docker run -v $HOME/BorgButler:/BorgButler -p 127.0.0.1:9042:9042 --name borgbutler kreinhard/borgbutler'"
echo "Run with ssh: 'docker run -v $HOME/BorgButler:/BorgButler -v $HOME/.ssh:/home/borgbutler/.ssh:ro -p 127.0.0.1:9042:9042 --name borgbutler kreinhard/borgbutler'"
echo
-echo 'Increase Java memory: docker run -e JAVA_OPTS="-Xms4g -Xmx4g" -v ...'
+echo 'Increase Java memory: docker run -e JAVA_OPTS="-Xmx2g" -v ...'
diff --git a/borgbutler-server/src/main/java/de/micromata/borgbutler/server/user/UserFilter.java b/borgbutler-server/src/main/java/de/micromata/borgbutler/server/user/UserFilter.java
deleted file mode 100644
index 638bf5b..0000000
--- a/borgbutler-server/src/main/java/de/micromata/borgbutler/server/user/UserFilter.java
+++ /dev/null
@@ -1,87 +0,0 @@
-package de.micromata.borgbutler.server.user;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.stereotype.Component;
-
-import javax.servlet.*;
-import java.io.IOException;
-
-/**
- * Ensuring the user data inside request threads. For now, it's only a simple implementation (no login required).
- * Only the user's (client's) locale is used.
- * <br>
- * For requests from remote (not localhost) an exception is thrown due to security reasons.
- */
-@Component
-public class UserFilter implements Filter {
- private Logger log = LoggerFactory.getLogger(UserFilter.class);
-
- @Override
- public void init(FilterConfig filterConfig) {
- }
-
- @Override
- public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
- checkClientIp(request);
- try {
- UserData userData = UserUtils.getUser();
- if (userData != null) {
- log.warn("****************************************");
- log.warn("*********** **********");
- log.warn("*********** SECURITY WARNING! **********");
- log.warn("*********** **********");
- log.warn("*********** Internal error: **********");
- log.warn("*********** User already set! **********");
- log.warn("*********** **********");
- log.warn("****************************************");
- log.warn("Don't deliver this app in dev mode due to security reasons!");
- String message = "User already given for this request. Rejecting request due to security reasons. Given user: " + userData;
- log.error(message);
- throw new IllegalArgumentException(message);
- }
- userData = UserManager.instance().getUser("dummy");
- UserUtils.setUser(userData, request.getLocale());
- if (log.isDebugEnabled()) log.debug("Request for user: " + userData);
- //log.info("Request for user: " + userData + ": " + RequestLog.asString((HttpServletRequest) request));
- chain.doFilter(request, response);
- } finally {
- UserUtils.removeUser();
- }
- }
-
- @Override
- public void destroy() {
- }
-
- private void checkClientIp(ServletRequest request) {
- String remoteAddr = request.getRemoteAddr();
- boolean allowed = false;
- String allowedClientIps = System.getProperty("allowedClientIps");
- if (remoteAddr != null) {
- if (remoteAddr.equals("127.0.0.1")) {
- allowed = true;
- } else {
- if (allowedClientIps != null && remoteAddr.startsWith(allowedClientIps)) {
- allowed = true;
- }
- }
- }
- if (!allowed) {
- log.warn("****************************************");
- log.warn("*********** **********");
- log.warn("*********** SECURITY WARNING! **********");
- log.warn("*********** **********");
- log.warn("*********** Externa access: **********");
- log.warn("*********** " + remoteAddr + " **********");
- log.warn("*********** **********");
- log.warn("****************************************");
- if (allowedClientIps == null) {
- log.warn("Only access from local host yet supported due to security reasons. You may configure client address ranges by -DallowedClientIps=172.17.0.1 or -DallowedClientIps=172.17.");
- } else {
- log.warn("Only access from local host and " + allowedClientIps + " (option -DallowedClientIps) yet supported due to security reasons.");
- }
- throw new RuntimeException("Server is only available for localhost due to security reasons. A remote access is not yet available.");
- }
- }
-}
diff --git a/borgbutler-server/src/main/kotlin/de/micromata/borgbutler/server/RunningMode.kt b/borgbutler-server/src/main/kotlin/de/micromata/borgbutler/server/RunningMode.kt
index 95939fe..a009a0c 100644
--- a/borgbutler-server/src/main/kotlin/de/micromata/borgbutler/server/RunningMode.kt
+++ b/borgbutler-server/src/main/kotlin/de/micromata/borgbutler/server/RunningMode.kt
@@ -12,6 +12,8 @@
val headlessMode: Boolean = System.getProperty("java.awt.headless") == "true"
val desktopSupported = !headlessMode && Desktop.isDesktopSupported()
val desktopSupportsBrowse = desktopSupported && Desktop.getDesktop().isSupported(Desktop.Action.BROWSE)
+ @JvmStatic
+ val dockerMode = System.getProperty("docker") == "true"
@JvmStatic
val userManagement = UserManagement.SINGLE
diff --git a/borgbutler-server/src/main/kotlin/de/micromata/borgbutler/server/rest/RestUtils.kt b/borgbutler-server/src/main/kotlin/de/micromata/borgbutler/server/rest/RestUtils.kt
index 26ff078..f1c4e56 100644
--- a/borgbutler-server/src/main/kotlin/de/micromata/borgbutler/server/rest/RestUtils.kt
+++ b/borgbutler-server/src/main/kotlin/de/micromata/borgbutler/server/rest/RestUtils.kt
@@ -61,7 +61,7 @@
if (remoteAddr.contains(",")) {
// sometimes the header is of form client ip,proxy 1 ip,proxy 2 ip,...,proxy n ip,
// we just want the client
- remoteAddr = remoteAddr.split(',')[0].trim({ it <= ' ' })
+ remoteAddr = remoteAddr.split(',')[0].trim { it <= ' ' }
}
try {
// If ip4/6 address string handed over, simply does pattern validation.
diff --git a/borgbutler-server/src/main/kotlin/de/micromata/borgbutler/server/user/UserFilter.kt b/borgbutler-server/src/main/kotlin/de/micromata/borgbutler/server/user/UserFilter.kt
new file mode 100644
index 0000000..b7ea64c
--- /dev/null
+++ b/borgbutler-server/src/main/kotlin/de/micromata/borgbutler/server/user/UserFilter.kt
@@ -0,0 +1,98 @@
+package de.micromata.borgbutler.server.user
+
+import de.micromata.borgbutler.server.RunningMode.dockerMode
+import org.slf4j.LoggerFactory
+import org.springframework.stereotype.Component
+import java.io.IOException
+import javax.servlet.*
+
+/**
+ * Ensuring the user data inside request threads. For now, it's only a simple implementation (no login required).
+ * Only the user's (client's) locale is used.
+ * <br></br>
+ * For requests from remote (not localhost) an exception is thrown due to security reasons.
+ */
+@Component
+class UserFilter : Filter {
+ private val log = LoggerFactory.getLogger(UserFilter::class.java)
+ override fun init(filterConfig: FilterConfig) {}
+
+ @Throws(IOException::class, ServletException::class)
+ override fun doFilter(request: ServletRequest, response: ServletResponse, chain: FilterChain) {
+ checkClientIp(request)
+ try {
+ var userData = UserUtils.getUser()
+ if (userData != null) {
+ log.warn("****************************************")
+ log.warn("*********** **********")
+ log.warn("*********** SECURITY WARNING! **********")
+ log.warn("*********** **********")
+ log.warn("*********** Internal error: **********")
+ log.warn("*********** User already set! **********")
+ log.warn("*********** **********")
+ log.warn("****************************************")
+ log.warn("Don't deliver this app in dev mode due to security reasons!")
+ val message =
+ "User already given for this request. Rejecting request due to security reasons. Given user: $userData"
+ log.error(message)
+ throw IllegalArgumentException(message)
+ }
+ userData = UserManager.instance().getUser("dummy")
+ UserUtils.setUser(userData, request.locale)
+ if (log.isDebugEnabled) log.debug("Request for user: $userData")
+ //log.info("Request for user: " + userData + ": " + RequestLog.asString((HttpServletRequest) request));
+ chain.doFilter(request, response)
+ } finally {
+ UserUtils.removeUser()
+ }
+ }
+
+ override fun destroy() {}
+ private fun checkClientIp(request: ServletRequest) {
+ val remoteAddr = request.remoteAddr
+ if (check(remoteAddr)) {
+ return
+ }
+ log.warn("****************************************")
+ log.warn("*********** **********")
+ log.warn("*********** SECURITY WARNING! **********")
+ log.warn("*********** **********")
+ log.warn("*********** External access: **********")
+ log.warn("*********** $remoteAddr **********")
+ log.warn("*********** **********")
+ log.warn("****************************************")
+ if (allowedClientIps == null) {
+ log.warn("Only access from local host yet supported due to security reasons. You may configure client address ranges by -DallowedClientIps=172.17.0.1 or -DallowedClientIps=172.17.")
+ } else {
+ log.warn("Only access from local host and '${allowedClientIps.joinToString { it }}' (option -DallowedClientIps) yet supported due to security reasons.")
+ }
+ log.info("Access denied for client with remote address: $remoteAddr")
+ throw RuntimeException("Server is only available for localhost due to security reasons. A remote access is not yet available.")
+ }
+
+ internal fun check(remoteAddr: String?): Boolean {
+ remoteAddr ?: return false
+ if (remoteAddr == "127.0.0.1") {
+ return true
+ }
+ if (dockerMode && remoteAddr.startsWith("172.17.0.")) {
+ // Docker host uses ip address 171.17.0
+ return true
+ }
+ return allowedClientIps?.any { remoteAddr.startsWith(it) } == true
+ }
+
+ private val allowedClientIps =
+ System.getProperty(SYSTEM_PROPERTY_ALLOWED_CLIENT_IPS)?.split(",", ";", ":", " ")
+ ?.filter { it.isNotBlank() && it.indexOf('.') > 0 }?.map { it.trim { it <= ' ' } }
+
+ init {
+ if (!allowedClientIps.isNullOrEmpty()) {
+ log.warn("Configured and allowed client ips are: ${allowedClientIps.joinToString { it }}")
+ }
+ }
+
+ companion object {
+ internal const val SYSTEM_PROPERTY_ALLOWED_CLIENT_IPS = "allowedClientIps"
+ }
+}
diff --git a/borgbutler-server/src/test/kotlin/de/micromata/borgbutler/server/user/UserFilterTest.kt b/borgbutler-server/src/test/kotlin/de/micromata/borgbutler/server/user/UserFilterTest.kt
new file mode 100644
index 0000000..bebfccf
--- /dev/null
+++ b/borgbutler-server/src/test/kotlin/de/micromata/borgbutler/server/user/UserFilterTest.kt
@@ -0,0 +1,28 @@
+package de.micromata.borgbutler.server.user
+
+import de.micromata.borgbutler.server.BorgVersion
+import org.junit.jupiter.api.Assertions
+import org.junit.jupiter.api.Test
+
+class UserFilterTest {
+ @Test
+ fun checkRemoteAddressTest() {
+ check(null, null, false)
+ check("127.0.0.1", null, true)
+ check("127.0.0.1", "172.0.", true)
+ check("127.0.0.1", "192.168.", true)
+ check("192.168.1.1", "192.168.", true)
+ check("192.168.1.1", "192.168. 192.178.5", true)
+ check("192.178.5.1", "192.168. 192.178.5", true)
+ check("192.178.6.1", "192.168. 192.178.5", false)
+ }
+
+ fun check(remoteAddress: String?, allowedClientIps: String?, expected: Boolean) {
+ if (allowedClientIps != null) {
+ System.setProperty(UserFilter.SYSTEM_PROPERTY_ALLOWED_CLIENT_IPS, allowedClientIps)
+ } else {
+ System.clearProperty(UserFilter.SYSTEM_PROPERTY_ALLOWED_CLIENT_IPS)
+ }
+ Assertions.assertEquals(expected, UserFilter().check(remoteAddress))
+ }
+}
diff --git a/build.gradle b/build.gradle
index 443a1a7..8d87b85 100644
--- a/build.gradle
+++ b/build.gradle
@@ -12,14 +12,14 @@
allprojects {
group = 'de.micromata.borgbutler'
- version = '0.5-SNAPSHOT'
+ version = '0.5'
}
subprojects {
apply plugin: 'java'
apply plugin: 'org.jetbrains.kotlin.jvm'
- sourceCompatibility = 1.9 // Needed: since 1.9 i18n properties in UTF-8 format.
- targetCompatibility = 1.9
+ sourceCompatibility = 9 // Needed: since 1.9 i18n properties in UTF-8 format.
+ targetCompatibility = 9
tasks.withType(JavaCompile) {
options.compilerArgs << '-Xlint:unchecked'
--
Gitblit v1.10.0