Skip to content

Commit

Permalink
add docker-socket-proxy as option
Browse files Browse the repository at this point in the history
Signed-off-by: Simon L <szaimen@e.mail.de>
  • Loading branch information
szaimen committed Aug 24, 2023
1 parent 7e5fe5a commit 6685791
Show file tree
Hide file tree
Showing 14 changed files with 236 additions and 7 deletions.
9 changes: 9 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -165,3 +165,12 @@ updates:
labels:
- 3. to review
- dependencies
- package-ecosystem: "docker"
directory: "/Containers/docker-socket-proxy"
schedule:
interval: "daily"
time: "12:00"
open-pull-requests-limit: 10
labels:
- 3. to review
- dependencies
39 changes: 39 additions & 0 deletions Containers/docker-socket-proxy/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Inspiration: https://github.com/Tecnativa/docker-socket-proxy/blob/master/Dockerfile
FROM haproxy:2.4.24-alpine3.18

RUN set -ex; \
apk add --no-cache date; \
chmod 777 -R /run/; \
chmod 777 -R /var/lib/haproxy

EXPOSE 2375
ENV ALLOW_RESTARTS=1 \
AUTH=1 \
BUILD=0 \
COMMIT=0 \
CONFIGS=0 \
CONTAINERS=1 \
DISTRIBUTION=0 \
EVENTS=1 \
EXEC=0 \
GRPC=0 \
IMAGES=1 \
INFO=1 \
LOG_LEVEL=info \
NETWORKS=1 \
NODES=0 \
PING=1 \
PLUGINS=0 \
POST=0 \
SECRETS=0 \
SERVICES=1 \
SESSION=0 \
SOCKET_PATH=/var/run/docker.sock \
SWARM=0 \
SYSTEM=0 \
TASKS=0 \
VERSION=1 \
VOLUMES=1
COPY haproxy.cfg /usr/local/etc/haproxy/haproxy.cfg

USER haproxy:root
72 changes: 72 additions & 0 deletions Containers/docker-socket-proxy/haproxy.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# Inspiration: https://github.com/Tecnativa/docker-socket-proxy/blob/master/haproxy.cfg

global
log stdout format raw daemon "${LOG_LEVEL}"

pidfile /run/haproxy.pid
maxconn 4000

# Turn on stats unix socket
server-state-file /var/lib/haproxy/server-state

defaults
mode http
log global
option httplog
option dontlognull
option http-server-close
option redispatch
retries 3
timeout http-request 10s
timeout queue 1m
timeout connect 10s
timeout client 10m
timeout server 10m
timeout http-keep-alive 10s
timeout check 10s
maxconn 3000

# Allow seamless reloads
load-server-state-from-file global

# Use provided example error pages
errorfile 400 /usr/local/etc/haproxy/errors/400.http
errorfile 403 /usr/local/etc/haproxy/errors/403.http
errorfile 408 /usr/local/etc/haproxy/errors/408.http
errorfile 500 /usr/local/etc/haproxy/errors/500.http
errorfile 502 /usr/local/etc/haproxy/errors/502.http
errorfile 503 /usr/local/etc/haproxy/errors/503.http
errorfile 504 /usr/local/etc/haproxy/errors/504.http

backend dockerbackend
server dockersocket $SOCKET_PATH

frontend dockerfrontend
bind :2375
http-request deny unless METH_GET || { env(POST) -m bool }
http-request allow if { path,url_dec -m reg -i ^(/v[\d\.]+)?/containers/[a-zA-Z0-9_.-]+/((stop)|(restart)|(kill)) } { env(ALLOW_RESTARTS) -m bool }
http-request allow if { path,url_dec -m reg -i ^(/v[\d\.]+)?/auth } { env(AUTH) -m bool }
http-request allow if { path,url_dec -m reg -i ^(/v[\d\.]+)?/build } { env(BUILD) -m bool }
http-request allow if { path,url_dec -m reg -i ^(/v[\d\.]+)?/commit } { env(COMMIT) -m bool }
http-request allow if { path,url_dec -m reg -i ^(/v[\d\.]+)?/configs } { env(CONFIGS) -m bool }
http-request allow if { path,url_dec -m reg -i ^(/v[\d\.]+)?/containers } { env(CONTAINERS) -m bool }
http-request allow if { path,url_dec -m reg -i ^(/v[\d\.]+)?/distribution } { env(DISTRIBUTION) -m bool }
http-request allow if { path,url_dec -m reg -i ^(/v[\d\.]+)?/events } { env(EVENTS) -m bool }
http-request allow if { path,url_dec -m reg -i ^(/v[\d\.]+)?/exec } { env(EXEC) -m bool }
http-request allow if { path,url_dec -m reg -i ^(/v[\d\.]+)?/grpc } { env(GRPC) -m bool }
http-request allow if { path,url_dec -m reg -i ^(/v[\d\.]+)?/images } { env(IMAGES) -m bool }
http-request allow if { path,url_dec -m reg -i ^(/v[\d\.]+)?/info } { env(INFO) -m bool }
http-request allow if { path,url_dec -m reg -i ^(/v[\d\.]+)?/networks } { env(NETWORKS) -m bool }
http-request allow if { path,url_dec -m reg -i ^(/v[\d\.]+)?/nodes } { env(NODES) -m bool }
http-request allow if { path,url_dec -m reg -i ^(/v[\d\.]+)?/_ping } { env(PING) -m bool }
http-request allow if { path,url_dec -m reg -i ^(/v[\d\.]+)?/plugins } { env(PLUGINS) -m bool }
http-request allow if { path,url_dec -m reg -i ^(/v[\d\.]+)?/secrets } { env(SECRETS) -m bool }
http-request allow if { path,url_dec -m reg -i ^(/v[\d\.]+)?/services } { env(SERVICES) -m bool }
http-request allow if { path,url_dec -m reg -i ^(/v[\d\.]+)?/session } { env(SESSION) -m bool }
http-request allow if { path,url_dec -m reg -i ^(/v[\d\.]+)?/swarm } { env(SWARM) -m bool }
http-request allow if { path,url_dec -m reg -i ^(/v[\d\.]+)?/system } { env(SYSTEM) -m bool }
http-request allow if { path,url_dec -m reg -i ^(/v[\d\.]+)?/tasks } { env(TASKS) -m bool }
http-request allow if { path,url_dec -m reg -i ^(/v[\d\.]+)?/version } { env(VERSION) -m bool }
http-request allow if { path,url_dec -m reg -i ^(/v[\d\.]+)?/volumes } { env(VOLUMES) -m bool }
http-request deny
default_backend dockerbackend
19 changes: 19 additions & 0 deletions Containers/nextcloud/entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,8 @@ DATADIR_PERMISSION_CONF
touch "$NEXTCLOUD_DATA_DIR/install.failed"
exit 1
fi
# shellcheck disable=SC2016
installed_version="$(php -r 'require "/var/www/html/version.php"; echo implode(".", $OC_Version);')"
fi
php /var/www/html/occ app:disable updatenotification
rm -rf /var/www/html/apps/updatenotification
Expand Down Expand Up @@ -732,5 +734,22 @@ else
fi
fi

# Docker socket proxy
if version_greater "$installed_version" "28.0.0.0"; then
if [ "$DOCKER_SOCKET_PROXY_ENABLED" = 'yes' ]; then
if ! [ -d "/var/www/html/custom_apps/app_ecosystem_v2" ]; then
php /var/www/html/occ app:install app_ecosystem_v2
elif [ "$(php /var/www/html/occ config:app:get app_ecosystem_v2 enabled)" != "yes" ]; then
php /var/www/html/occ app:enable app_ecosystem_v2
elif [ "$SKIP_UPDATE" != 1 ]; then
php /var/www/html/occ app:update app_ecosystem_v2
fi
else
if [ -d "/var/www/html/custom_apps/app_ecosystem_v2" ]; then
php /var/www/html/occ app:remove app_ecosystem_v2
fi
fi
fi

# Remove the update skip file always
rm -f "$NEXTCLOUD_DATA_DIR"/skip.update
2 changes: 2 additions & 0 deletions manual-install/update-yaml.sh
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ OUTPUT="$(echo "$OUTPUT" | jq 'del(.services[].nextcloud_exec_commands)')"
OUTPUT="$(echo "$OUTPUT" | jq 'del(.services[] | select(.container_name == "nextcloud-aio-watchtower"))')"
OUTPUT="$(echo "$OUTPUT" | jq 'del(.services[] | select(.container_name == "nextcloud-aio-domaincheck"))')"
OUTPUT="$(echo "$OUTPUT" | jq 'del(.services[] | select(.container_name == "nextcloud-aio-borgbackup"))')"
OUTPUT="$(echo "$OUTPUT" | jq 'del(.services[] | select(.container_name == "nextcloud-aio-docker-socket-proxy"))')"
OUTPUT="$(echo "$OUTPUT" | jq '.services[] |= if has("depends_on") then .depends_on |= map({ (.): { "condition": "service_started", "required": false } }) else . end' | jq '.services[] |= if has("depends_on") then .depends_on |= reduce .[] as $item ({}; . + $item) else . end')"

snap install yq
Expand Down Expand Up @@ -66,6 +67,7 @@ do
sed -i "s|$variable|\${$sole_variable}|g" containers.yml
done

sed -i '/DOCKER_SOCKET_PROXY_ENABLED/d' sample.conf
sed -i 's|_ENABLED=|_ENABLED="no" # Setting this to "yes" (with quotes) enables the option in Nextcloud automatically.|' sample.conf
sed -i 's|CLAMAV_ENABLED=no.*|CLAMAV_ENABLED="no" # Setting this to "yes" (with quotes) enables the option in Nextcloud automatically. Note: arm64 has no clamav support|' sample.conf
sed -i 's|TALK_ENABLED=no|TALK_ENABLED="yes"|' sample.conf
Expand Down
29 changes: 27 additions & 2 deletions php/containers.json
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,8 @@
"nextcloud-aio-clamav",
"nextcloud-aio-fulltextsearch",
"nextcloud-aio-talk-recording",
"nextcloud-aio-imaginary"
"nextcloud-aio-imaginary",
"nextcloud-aio-docker-socket-proxy"
],
"display_name": "Nextcloud",
"image": "nextcloud/aio-nextcloud",
Expand Down Expand Up @@ -203,7 +204,8 @@
"TALK_RECORDING_ENABLED=%TALK_RECORDING_ENABLED%",
"RECORDING_SECRET=%RECORDING_SECRET%",
"TALK_RECORDING_HOST=nextcloud-aio-talk-recording",
"FULLTEXTSEARCH_PASSWORD=%FULLTEXTSEARCH_PASSWORD%"
"FULLTEXTSEARCH_PASSWORD=%FULLTEXTSEARCH_PASSWORD%",
"DOCKER_SOCKET_PROXY_ENABLED=%DOCKER_SOCKET_PROXY_ENABLED%"
],
"restart": "unless-stopped",
"devices": [
Expand Down Expand Up @@ -639,6 +641,29 @@
"secrets": [
"FULLTEXTSEARCH_PASSWORD"
]
},
{
"container_name": "nextcloud-aio-docker-socket-proxy",
"display_name": "Docker Socket Proxy",
"image": "nextcloud/aio-docker-socket-proxy",
"init": true,
"internal_port": "2375",
"environment": [
"TZ=%TIMEZONE%"
],
"volumes": [
{
"source": "%WATCHTOWER_DOCKER_SOCKET_PATH%",
"destination": "/var/run/docker.sock",
"writeable": false
}
],
"restart": "unless-stopped",
"read_only": true,
"tmpfs": [
"/run/",
"/var/lib/haproxy"
]
}
]
}
4 changes: 4 additions & 0 deletions php/public/disable-docker-socket-proxy.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
document.addEventListener("DOMContentLoaded", function(event) {
// Docker socket proxy
document.getElementById("docker-socket-proxy").disabled = true;
});
1 change: 1 addition & 0 deletions php/public/index.php
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@
'nextcloud_memory_limit' => $configurationManager->GetNextcloudMemoryLimit(),
'is_dri_device_enabled' => $configurationManager->isDriDeviceEnabled(),
'is_talk_recording_enabled' => $configurationManager->isTalkRecordingEnabled(),
'is_docker_socket_proxy_enabled' => $configurationManager->isDockerSocketProxyEnabled(),
]);
})->setName('profile');
$app->get('/login', function (Request $request, Response $response, array $args) use ($container) {
Expand Down
12 changes: 12 additions & 0 deletions php/public/options-form-submit.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,13 @@ function handleTalkVisibility() {
}
}

function handleDockerSocketProxyWarning() {
let dockerSocketProxy = document.getElementById("docker-socket-proxy");
if (dockerSocketProxy.checked) {
alert('⚠️ Warning! Enabling this container comes with possible Security problems since you are exposing the docker socket and all its privileges to the Nextcloud container. Enable this only if you are sure what you are doing!')
}
}

document.addEventListener("DOMContentLoaded", function(event) {
// handle submit button for options form
let optionsFormSubmit = document.getElementById("options-form-submit");
Expand Down Expand Up @@ -52,4 +59,9 @@ document.addEventListener("DOMContentLoaded", function(event) {
// Fulltextsearch
let fulltextsearch = document.getElementById("fulltextsearch");
fulltextsearch.addEventListener('change', makeOptionsFormSubmitVisible);

// Docker socket proxy
let dockerSocketProxy = document.getElementById("docker-socket-proxy");
dockerSocketProxy.addEventListener('change', makeOptionsFormSubmitVisible);
dockerSocketProxy.addEventListener('change', handleDockerSocketProxyWarning);
});
8 changes: 8 additions & 0 deletions php/src/ContainerDefinitionFetcher.php
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,10 @@ private function GetDefinition(bool $latest): array
if (!$this->configurationManager->isFulltextsearchEnabled()) {
continue;
}
} elseif ($entry['container_name'] === 'nextcloud-aio-docker-socket-proxy') {
if (!$this->configurationManager->isDockerSocketProxyEnabled()) {
continue;
}
}

$ports = new ContainerPorts();
Expand Down Expand Up @@ -195,6 +199,10 @@ private function GetDefinition(bool $latest): array
if (!$this->configurationManager->isFulltextsearchEnabled()) {
continue;
}
} elseif ($value === 'nextcloud-aio-docker-socket-proxy') {
if (!$this->configurationManager->isDockerSocketProxyEnabled()) {
continue;
}
}
$dependsOn[] = $value;
}
Expand Down
5 changes: 5 additions & 0 deletions php/src/Controller/ConfigurationController.php
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,11 @@ public function SetConfig(Request $request, Response $response, array $args) : R
} else {
$this->configurationManager->SetFulltextsearchEnabledState(0);
}
if (isset($request->getParsedBody()['docker-socket-proxy'])) {
$this->configurationManager->SetDockerSocketProxyEnabledState(1);
} else {
$this->configurationManager->SetDockerSocketProxyEnabledState(0);
}
}

if (isset($request->getParsedBody()['delete_collabora_dictionaries'])) {
Expand Down
15 changes: 15 additions & 0 deletions php/src/Data/ConfigurationManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,21 @@ public function isClamavEnabled() : bool {
}
}

public function isDockerSocketProxyEnabled() : bool {
$config = $this->GetConfig();
if (isset($config['isDockerSocketProxyEnabled']) && $config['isDockerSocketProxyEnabled'] === 1) {
return true;
} else {
return false;
}
}

public function SetDockerSocketProxyEnabledState(int $value) : void {
$config = $this->GetConfig();
$config['isDockerSocketProxyEnabled'] = $value;
$this->WriteConfig($config);
}

public function SetClamavEnabledState(int $value) : void {
$config = $this->GetConfig();
$config['isClamavEnabled'] = $value;
Expand Down
23 changes: 18 additions & 5 deletions php/src/Docker/DockerActionManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,12 @@ public function CreateContainer(Container $container) : void {
} else {
$replacements[1] = '';
}
} elseif ($out[1] === 'DOCKER_SOCKET_PROXY_ENABLED') {
if ($this->configurationManager->isDockerSocketProxyEnabled()) {
$replacements[1] = 'yes';
} else {
$replacements[1] = '';
}
} elseif ($out[1] === 'NEXTCLOUD_UPLOAD_LIMIT') {
$replacements[1] = $this->configurationManager->GetNextcloudUploadLimit();
} elseif ($out[1] === 'NEXTCLOUD_MEMORY_LIMIT') {
Expand Down Expand Up @@ -406,7 +412,11 @@ public function CreateContainer(Container $container) : void {
$portWithProtocol = $value->port . '/' . $value->protocol;
$exposedPorts[$portWithProtocol] = null;
}
$requestBody['HostConfig']['NetworkMode'] = 'nextcloud-aio';
if ($container->GetIdentifier() !== 'nextcloud-aio-docker-socket-proxy') {
$requestBody['HostConfig']['NetworkMode'] = 'nextcloud-aio';
} else {
$requestBody['HostConfig']['NetworkMode'] = 'nextcloud-aio-docker-socket-proxy-network';
}
} else {
$requestBody['HostConfig']['NetworkMode'] = 'host';
}
Expand Down Expand Up @@ -763,21 +773,20 @@ private function DisconnectContainerFromBridgeNetwork(string $id) : void
}
}

private function ConnectContainerIdToNetwork(string $id, string $internalPort) : void
private function ConnectContainerIdToNetwork(string $id, string $internalPort, string $network = 'nextcloud-aio') : void
{
if ($internalPort === 'host') {
return;
}

$network = 'nextcloud-aio';
$url = $this->BuildApiUrl('networks/create');
try {
$this->guzzleClient->request(
'POST',
$url,
[
'json' => [
'Name' => 'nextcloud-aio',
'Name' => $network,
'CheckDuplicate' => true,
'Driver' => 'bridge',
'Internal' => false,
Expand Down Expand Up @@ -815,13 +824,17 @@ private function ConnectContainerIdToNetwork(string $id, string $internalPort) :
public function ConnectMasterContainerToNetwork() : void
{
$this->ConnectContainerIdToNetwork('nextcloud-aio-mastercontainer', '');
$this->ConnectContainerIdToNetwork('nextcloud-aio-mastercontainer', '', 'nextcloud-aio-docker-socket-proxy-network');
// Don't disconnect here since it slows down the initial login by a lot. Is getting done during cron.sh instead.
// $this->DisconnectContainerFromBridgeNetwork('nextcloud-aio-mastercontainer');
}

public function ConnectContainerToNetwork(Container $container) : void
{
$this->ConnectContainerIdToNetwork($container->GetIdentifier(), $container->GetInternalPort());
$this->ConnectContainerIdToNetwork($container->GetIdentifier(), $container->GetInternalPort());
if ($container->GetIdentifier() === 'nextcloud-aio-nextcloud' || $container->GetIdentifier() === 'nextcloud-aio-docker-socket-proxy') {
$this->ConnectContainerIdToNetwork($container->GetIdentifier(), $container->GetInternalPort(), 'nextcloud-aio-docker-socket-proxy-network');
}
}

public function StopContainer(Container $container) : void {
Expand Down
5 changes: 5 additions & 0 deletions php/templates/containers.twig
Original file line number Diff line number Diff line change
Expand Up @@ -574,6 +574,11 @@
{% else %}
{#<input type="checkbox" id="onlyoffice" name="onlyoffice"><label for="onlyoffice">OnlyOffice</label><br>#}
{% endif %}
{% if is_docker_socket_proxy_enabled == true %}
<input type="checkbox" id="docker-socket-proxy" name="docker-socket-proxy" checked="checked"><label for="docker-socket-proxy">Docker Socket Proxy (needed for app-ecosystem v2)</label><br><br>
{% else %}
<input type="checkbox" id="docker-socket-proxy" name="docker-socket-proxy"><label for="docker-socket-proxy">Docker Socket Proxy (needed for app-ecosystem v2)</label><br><br>
{% endif %}
<input id="options-form-submit" class="button" type="submit" value="Save changes" />
<script type="text/javascript" src="options-form-submit.js"></script>
</form>
Expand Down

0 comments on commit 6685791

Please sign in to comment.