Skip to content

Commit d07167a

Browse files
authored
Merge pull request #2 from AtomGraph/feat-RemoteIpValve
Support for adding `RemoteIpValve`
2 parents 84813b2 + 283579e commit d07167a

File tree

4 files changed

+228
-71
lines changed

4 files changed

+228
-71
lines changed

CLAUDE.md

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
# CLAUDE.md
2+
3+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4+
5+
## Overview
6+
7+
This repository provides a Docker image that combines Apache Tomcat with Let's Encrypt HTTPS certificates. It automates the conversion of Let's Encrypt PEM certificates to Java keystores (JKS) and dynamically configures Tomcat's `server.xml` using XSLT transformations.
8+
9+
## Architecture
10+
11+
### Certificate Conversion Flow
12+
13+
The entrypoint script (`entrypoint.sh`) performs certificate conversion on container startup:
14+
15+
1. Validates required environment variables (`LETSENCRYPT_CERT_DIR`, `PKCS12_PASSWORD`, `JKS_KEY_PASSWORD`, `JKS_STORE_PASSWORD`)
16+
2. Converts Let's Encrypt PEM certificates to PKCS12 format using OpenSSL
17+
3. Imports PKCS12 into Java Keystore (JKS) format using keytool
18+
4. Transforms Tomcat's `server.xml` configuration using XSLT
19+
5. Starts Tomcat with `catalina.sh run`
20+
21+
### Dynamic Configuration via XSLT
22+
23+
The `letsencrypt-tomcat.xsl` stylesheet dynamically modifies Tomcat's `server.xml` based on environment variables:
24+
25+
- **HTTP Connector**: Conditionally enabled/disabled, with proxy settings and compression
26+
- **HTTPS Connector**: Adds SSL/TLS configuration with keystore settings when enabled
27+
- **RemoteIpValve**: Optional configuration for X-Forwarded headers when behind a proxy
28+
29+
The XSLT transformation is executed by `xsltproc` with environment variables passed as string parameters.
30+
31+
## Building and Testing
32+
33+
### Build Docker Image
34+
35+
```bash
36+
docker build -t atomgraph/letsencrypt-tomcat .
37+
```
38+
39+
### Run Container
40+
41+
```bash
42+
docker run \
43+
-v /etc/letsencrypt:/etc/letsencrypt \
44+
-e LETSENCRYPT_CERT_DIR=/etc/letsencrypt \
45+
-e PKCS12_PASSWORD=<password> \
46+
-e JKS_KEY_PASSWORD=<password> \
47+
-e JKS_STORE_PASSWORD=<password> \
48+
atomgraph/letsencrypt-tomcat
49+
```
50+
51+
### Test XSLT Transformation
52+
53+
To test XSLT changes without building the full image:
54+
55+
```bash
56+
xsltproc \
57+
--stringparam http true \
58+
--stringparam http.port 8080 \
59+
--stringparam https true \
60+
--stringparam https.port 8443 \
61+
--stringparam https.keystoreFile letsencrypt.jks \
62+
--stringparam https.keystorePass <password> \
63+
--stringparam https.keyAlias letsencrypt \
64+
--stringparam https.keyPass <password> \
65+
letsencrypt-tomcat.xsl /path/to/server.xml
66+
```
67+
68+
## CI/CD
69+
70+
GitHub Actions workflow (`.github/workflows/image.yml`) triggers on git tags:
71+
72+
- Builds multi-platform images (linux/amd64, linux/arm64)
73+
- Pushes to Docker Hub with tags: `latest`, `MAJOR.MINOR.PATCH`, `MAJOR.MINOR`, `MAJOR`
74+
- Uses QEMU for cross-platform builds
75+
76+
## Key Files
77+
78+
- **Dockerfile**: Base image from `tomcat:10.1.34-jdk17`, installs xsltproc
79+
- **entrypoint.sh**: Certificate conversion and Tomcat configuration script
80+
- **letsencrypt-tomcat.xsl**: XSLT stylesheet for server.xml transformation
81+
82+
## Environment Variables
83+
84+
All configuration is done through environment variables. See README.md for the full list of supported variables for HTTP/HTTPS connectors.
85+
86+
Required variables:
87+
- `LETSENCRYPT_CERT_DIR`: Path to Let's Encrypt certificates
88+
- `PKCS12_PASSWORD`: Password for PKCS12 keystore
89+
- `JKS_KEY_PASSWORD`: Password for JKS key
90+
- `JKS_STORE_PASSWORD`: Password for JKS store
91+
92+
## XSLT Parameter Naming Convention
93+
94+
Parameters in the XSLT stylesheet use dot notation (e.g., `Connector.port.http`, `Connector.keystoreFile.https`), while the entrypoint script converts environment variables with underscores (e.g., `HTTP_PORT`, `HTTPS_PORT`) to the corresponding XSLT parameter names with dots and prefixes.

README.md

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,15 @@ Docker image combining Tomcat and LetsEncrypt HTTPS certificates
1515

1616
Supported environment variables:
1717
* `HTTP` - enable HTTP connector? (default: `true`)
18+
* `HTTP_SCHEME`
1819
* `HTTP_PORT`
1920
* `HTTP_PROXY_NAME`
2021
* `HTTP_PROXY_PORT`
2122
* `HTTP_REDIRECT_PORT`
2223
* `HTTP_CONNECTION_TIMEOUT`
2324
* `HTTP_COMPRESSION`
24-
* `HTTP` - enable HTTPS connector? (default: `false`)
25+
* `HTTPS` - enable HTTPS connector? (default: `false`)
26+
* `HTTPS_SCHEME`
2527
* `HTTPS_PORT`
2628
* `HTTPS_MAX_THREADS`
2729
* `HTTPS_CLIENT_AUTH`
@@ -33,6 +35,11 @@ Supported environment variables:
3335
* `KEY_ALIAS`
3436
* `JKS_STORE_PASSWORD`
3537
* `P12_FILE`
38+
* `REMOTE_IP_VALVE` - enable `RemoteIpValve`? (default: `false`)
39+
* `REMOTE_IP_VALVE_PROTOCOL_HEADER` - protocol header name (default: `X-Forwarded-Proto`)
40+
* `REMOTE_IP_VALVE_PORT_HEADER` - port header name (default: `X-Forwarded-Port`)
41+
* `REMOTE_IP_VALVE_REMOTE_IP_HEADER` - remote IP header name (default: `X-Forwarded-For`)
42+
* `REMOTE_IP_VALVE_HOST_HEADER` - host header name (default: `X-Forwarded-Host`)
3643

3744
## More info
3845

entrypoint.sh

Lines changed: 45 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -53,82 +53,102 @@ keytool -importkeystore \
5353
# change server configuration
5454

5555
if [ -n "$HTTP" ] ; then
56-
HTTP_PARAM="--stringparam http $HTTP "
56+
HTTP_PARAM="--stringparam Connector.http $HTTP "
5757
fi
5858

5959
if [ -n "$HTTP_SCHEME" ] ; then
60-
HTTP_SCHEME_PARAM="--stringparam http.scheme $HTTP_SCHEME "
60+
HTTP_SCHEME_PARAM="--stringparam Connector.scheme.http $HTTP_SCHEME "
6161
fi
6262

6363
if [ -n "$HTTP_PORT" ] ; then
64-
HTTP_PORT_PARAM="--stringparam http.port $HTTP_PORT "
64+
HTTP_PORT_PARAM="--stringparam Connector.port.http $HTTP_PORT "
6565
fi
6666

6767
if [ -n "$HTTP_PROXY_NAME" ] ; then
68-
HTTP_PROXY_NAME_PARAM="--stringparam http.proxyName $HTTP_PROXY_NAME "
68+
HTTP_PROXY_NAME_PARAM="--stringparam Connector.proxyName.http $HTTP_PROXY_NAME "
6969
fi
7070

7171
if [ -n "$HTTP_PROXY_PORT" ] ; then
72-
HTTP_PROXY_PORT_PARAM="--stringparam http.proxyPort $HTTP_PROXY_PORT "
72+
HTTP_PROXY_PORT_PARAM="--stringparam Connector.proxyPort.http $HTTP_PROXY_PORT "
7373
fi
7474

7575
if [ -n "$HTTP_REDIRECT_PORT" ] ; then
76-
HTTP_REDIRECT_PORT_PARAM="--stringparam http.redirectPort $HTTP_REDIRECT_PORT "
76+
HTTP_REDIRECT_PORT_PARAM="--stringparam Connector.redirectPort.http $HTTP_REDIRECT_PORT "
7777
fi
7878

7979
if [ -n "$HTTP_CONNECTION_TIMEOUT" ] ; then
80-
HTTP_CONNECTION_TIMEOUT_PARAM="--stringparam http.connectionTimeout $HTTP_CONNECTION_TIMEOUT "
80+
HTTP_CONNECTION_TIMEOUT_PARAM="--stringparam Connector.connectionTimeout.http $HTTP_CONNECTION_TIMEOUT "
8181
fi
8282

8383
if [ -n "$HTTP_COMPRESSION" ] ; then
84-
HTTP_COMPRESSION_PARAM="--stringparam http.compression $HTTP_COMPRESSION "
84+
HTTP_COMPRESSION_PARAM="--stringparam Connector.compression.http $HTTP_COMPRESSION "
8585
fi
8686

8787
if [ -n "$HTTPS" ] ; then
88-
HTTPS_PARAM="--stringparam https $HTTPS "
88+
HTTPS_PARAM="--stringparam Connector.https $HTTPS "
8989
fi
9090

9191
if [ -n "$HTTPS_SCHEME" ] ; then
92-
HTTPS_SCHEME_PARAM="--stringparam https.scheme $HTTPS_SCHEME "
92+
HTTPS_SCHEME_PARAM="--stringparam Connector.scheme.https $HTTPS_SCHEME "
9393
fi
9494

9595
if [ -n "$HTTPS_PORT" ] ; then
96-
HTTPS_PORT_PARAM="--stringparam https.port $HTTPS_PORT "
96+
HTTPS_PORT_PARAM="--stringparam Connector.port.https $HTTPS_PORT "
9797
fi
9898

9999
if [ -n "$HTTPS_MAX_THREADS" ] ; then
100-
HTTPS_MAX_THREADS_PARAM="--stringparam https.maxThreads $HTTPS_MAX_THREADS "
100+
HTTPS_MAX_THREADS_PARAM="--stringparam Connector.maxThreads.https $HTTPS_MAX_THREADS "
101101
fi
102102

103103
if [ -n "$HTTPS_CLIENT_AUTH" ] ; then
104-
HTTPS_CLIENT_AUTH_PARAM="--stringparam https.clientAuth $HTTPS_CLIENT_AUTH "
104+
HTTPS_CLIENT_AUTH_PARAM="--stringparam Connector.clientAuth.https $HTTPS_CLIENT_AUTH "
105105
fi
106106

107107
if [ -n "$HTTPS_PROXY_NAME" ] ; then
108-
HTTPS_PROXY_NAME_PARAM="--stringparam https.proxyName $HTTPS_PROXY_NAME "
108+
HTTPS_PROXY_NAME_PARAM="--stringparam Connector.proxyName.https $HTTPS_PROXY_NAME "
109109
fi
110110

111111
if [ -n "$HTTPS_PROXY_PORT" ] ; then
112-
HTTPS_PROXY_PORT_PARAM="--stringparam https.proxyPort $HTTPS_PROXY_PORT "
112+
HTTPS_PROXY_PORT_PARAM="--stringparam Connector.proxyPort.https $HTTPS_PROXY_PORT "
113113
fi
114114

115115
if [ -n "$HTTPS_COMPRESSION" ] ; then
116-
HTTPS_COMPRESSION_PARAM="--stringparam https.compression $HTTPS_COMPRESSION "
116+
HTTPS_COMPRESSION_PARAM="--stringparam Connector.compression.https $HTTPS_COMPRESSION "
117117
fi
118118

119119
if [ -n "$JKS_FILE" ] ; then
120-
JKS_FILE_PARAM="--stringparam https.keystoreFile $JKS_FILE "
120+
JKS_FILE_PARAM="--stringparam Connector.keystoreFile.https $JKS_FILE "
121121
fi
122122
if [ -n "$JKS_KEY_PASSWORD" ] ; then
123-
JKS_KEY_PASSWORD_PARAM="--stringparam https.keystorePass $JKS_KEY_PASSWORD "
123+
JKS_KEY_PASSWORD_PARAM="--stringparam Connector.keystorePass.https $JKS_KEY_PASSWORD "
124124
fi
125125

126126
if [ -n "$KEY_ALIAS" ] ; then
127-
KEY_ALIAS_PARAM="--stringparam https.keyAlias $KEY_ALIAS "
127+
KEY_ALIAS_PARAM="--stringparam Connector.keyAlias.https $KEY_ALIAS "
128128
fi
129129

130130
if [ -n "$JKS_STORE_PASSWORD" ] ; then
131-
JKS_STORE_PASSWORD_PARAM="--stringparam https.keyPass $JKS_STORE_PASSWORD "
131+
JKS_STORE_PASSWORD_PARAM="--stringparam Connector.keyPass.https $JKS_STORE_PASSWORD "
132+
fi
133+
134+
if [ -n "$REMOTE_IP_VALVE" ] ; then
135+
REMOTE_IP_VALVE_PARAM="--stringparam RemoteIpValve $REMOTE_IP_VALVE "
136+
fi
137+
138+
if [ -n "$REMOTE_IP_VALVE_PROTOCOL_HEADER" ] ; then
139+
REMOTE_IP_VALVE_PROTOCOL_HEADER_PARAM="--stringparam RemoteIpValve.protocolHeader $REMOTE_IP_VALVE_PROTOCOL_HEADER "
140+
fi
141+
142+
if [ -n "$REMOTE_IP_VALVE_PORT_HEADER" ] ; then
143+
REMOTE_IP_VALVE_PORT_HEADER_PARAM="--stringparam RemoteIpValve.portHeader $REMOTE_IP_VALVE_PORT_HEADER "
144+
fi
145+
146+
if [ -n "$REMOTE_IP_VALVE_REMOTE_IP_HEADER" ] ; then
147+
REMOTE_IP_VALVE_REMOTE_IP_HEADER_PARAM="--stringparam RemoteIpValve.remoteIpHeader $REMOTE_IP_VALVE_REMOTE_IP_HEADER "
148+
fi
149+
150+
if [ -n "$REMOTE_IP_VALVE_HOST_HEADER" ] ; then
151+
REMOTE_IP_VALVE_HOST_HEADER_PARAM="--stringparam RemoteIpValve.hostHeader $REMOTE_IP_VALVE_HOST_HEADER "
132152
fi
133153

134154
transform="xsltproc \
@@ -153,6 +173,11 @@ transform="xsltproc \
153173
$JKS_KEY_PASSWORD_PARAM \
154174
$KEY_ALIAS_PARAM \
155175
$JKS_STORE_PASSWORD_PARAM \
176+
$REMOTE_IP_VALVE_PARAM \
177+
$REMOTE_IP_VALVE_PROTOCOL_HEADER_PARAM \
178+
$REMOTE_IP_VALVE_PORT_HEADER_PARAM \
179+
$REMOTE_IP_VALVE_REMOTE_IP_HEADER_PARAM \
180+
$REMOTE_IP_VALVE_HOST_HEADER_PARAM \
156181
conf/letsencrypt-tomcat.xsl \
157182
conf/server.xml"
158183

0 commit comments

Comments
 (0)