Skip to content

Commit

Permalink
Feature/167-docker-compose-setup-with-rootless-docker (#169)
Browse files Browse the repository at this point in the history
* Add network to docker compose and expose ports instead of using network mode host

* Unrestrict proxy to accept any connection from 443 for dev purposes

* Development now works with rootless Docker

* Fix typo

* Add comment for context

* AJP address is now set to localhost

* Rename 'app_network' to 'eft_network'

* Use images from registry for docker-compose-prod.yml

* Update readme with more detailed information about privileged ports and only allow sp container hostname in proxy conf

* Revert test proxy conf and dev docker compose to network mode host, use eclipse-temurin instead of openjdk image because it is deprecated

* Add spring boot profile env var to prod docker compose file

* Set default value of SB_PROFILE env var to test in app server config

---------

Co-authored-by: Giannin <[email protected]>
  • Loading branch information
RandomTannenbaum and MasterEvarior authored Nov 8, 2024
1 parent aee0a32 commit 7ca7b48
Show file tree
Hide file tree
Showing 6 changed files with 64 additions and 5 deletions.
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM openjdk:21
FROM eclipse-temurin:21
RUN adduser --no-create-home eft-user
USER eft-user
COPY target .
Expand Down
21 changes: 21 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,27 @@ To use the installation script, simply run the following command as a non-root u
`curl -fsSL https://get.docker.com/rootless | sh`
#### Prepare Environment
Rootless Docker cannot access privileged ports, usually those below 1024. This is a problem because we need our Shibboleth SP to listen on port `443`.
If possible give your rootless Docker the permission to bind privileged ports.
```shell
sudo setcap cap_net_bind_service=ep /usr/bin/rootlesskit
systemctl --user restart docker
```
Alternatively, if you cannot use `setcap`, you can set a firewall rule.
Add the following rule to your iptables:
```shell
sudo iptables -t nat -A OUTPUT -o lo -p tcp --dport 443 -j REDIRECT --to-port=4443
```
This will redirect requests from port `443` to `4443`. This is also the port you will need to adjust in the `docker-compose` file.
```yaml
shibboleth-service-provider:
ports:
- "4443:443"
```
**This rule will not persist after a reboot!** To persist/delete this rule, see [this article](https://www.baeldung.com/linux/iptables-delete-rules).
#### Usage
> Attention: Rootless docker can't run systemwide
Expand Down
19 changes: 17 additions & 2 deletions docker-compose-prod.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,20 @@ services:
image: ghcr.io/puzzle/unilu-pruefungsabfrage:${TAG}
container_name: unilu-pruefungsabfrage
hostname: unilu-pruefungsabfrage
network_mode: host
ports:
- "8448:8448"
- "8443:8443"
networks:
eft_network:
aliases:
- edview.unilu.ch

environment:
KS_PW: ${KS_PW}
KS_TYPE: ${KS_TYPE:-PKCS12}
KEY_ALIAS: ${KEY_ALIAS:-unilu-eft}
KS_NAME: ${KS_NAME:-httpd.keystore.p12}
SB_PROFILE: prod
volumes:
- type: bind
source: ./${SEC_LOCATION:-shibboleth/secrets/prod}/${KS_NAME:-httpd.keystore.p12}
Expand All @@ -23,7 +31,11 @@ services:
image: ghcr.io/puzzle/unilu-pruefungsabfrage-shibboleth-sp:${TAG}
container_name: unilu-pruefungsabfrage-shibboleth-sp
hostname: unilu-pruefungsabfrage-shibboleth-sp
network_mode: host
ports:
- "443:443"
networks:
- eft_network

environment:
LOG_LEVEL: warn
volumes:
Expand All @@ -47,3 +59,6 @@ services:
source: ./shibboleth/secrets/SWITCHaaiRootCA.crt.pem
target: /etc/shibboleth/SWITCHaaiRootCA.crt.pem
read_only: true
networks:
eft_network:
driver: bridge
1 change: 1 addition & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ services:
SPRING_THYMELEAF_CACHE: false
SPRING_WEB_RESOURCES_STATIC_LOCATIONS: file:src/main/resources/static/
SPRING_WEB_RESOURCES_CACHE_PERIOD: 0
SB_PROFILE: ${SB_PROFILE:-dev}
healthcheck:
test: [ "CMD", "sh", "-c", "echo > /dev/tcp/localhost/8448" ]
interval: 5s
Expand Down
2 changes: 1 addition & 1 deletion shibboleth/resources/prod/proxy.conf
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Route requests to Spring Boot application EFT via AJP
<VirtualHost edview.unilu.ch:443>
<VirtualHost shibboleth-service-provider:443>
ProxyPreserveHost On
ProxyRequests Off
ProxyIOBufferSize 65536
Expand Down
24 changes: 23 additions & 1 deletion src/main/java/ch/puzzle/eft/config/AppServerConfig.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
package ch.puzzle.eft.config;

import java.net.InetAddress;
import java.net.UnknownHostException;

import org.apache.catalina.connector.Connector;
import org.apache.coyote.ajp.AbstractAjpProtocol;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
Expand All @@ -10,7 +15,7 @@

@Configuration
public class AppServerConfig {

private static final Logger logger = LoggerFactory.getLogger(AppServerConfig.class);
private static final String PROTOCOL = "AJP/1.3";

@Value("${server.ajp.protocol:https}")
Expand Down Expand Up @@ -41,7 +46,24 @@ private Connector redirectConnector() {
protocol.setTomcatAuthentication(false);
protocol.setAllowedRequestAttributesPattern(".{1,}"); // should be "AJP_"
protocol.setPacketSize(ajpPacketSize);
protocol.setAddress(getLocalhost());

return connector;
}

// necessary because without it AJP won't work over Docker bridge networks
// https://stackoverflow.com/questions/61524263/ajp-in-containerised-spring-boot-app-doesnt-work
private InetAddress getLocalhost() {
try {
InetAddress localhost = ((System.getenv()
.getOrDefault("SB_PROFILE", "test")).equals("prod"))
? InetAddress.getLocalHost()
: InetAddress.getLoopbackAddress();
logger.debug("Setting address for AJP protocol to '{}'", localhost);
return localhost;
} catch (UnknownHostException e) {
logger.error("Cannot determine localhost address, which is needed for the AJP connector");
throw new RuntimeException(e);
}
}
}

0 comments on commit 7ca7b48

Please sign in to comment.