Configure a Reverse Proxy in SignServer
It is best practice to place SignServer behind a reverse proxy server that handles TLS termination or load balancing for any production-like deployment. Configuring SignServer as a proxy back-end disables its internal TLS setup, which means the Admin UI will be open to anyone with network access unless the proxy enforces access controls.
When binding a proxy back-end protocol port, there are two approaches:
Binding to
0.0.0.0exposes the port on all interfaces. Care must be taken to ensure no traffic can reach the bound port directly, bypassing the proxy.Binding to
127.0.0.1restricts access to the local loopback interface, which is the expected setup when using a sidecar container deployed in the same Kubernetes Pod to forward requests internally.
Option | Description | Use Case |
|---|---|---|
RECOMMENDED | Uses a Keyfactor-provided NGINX sidecar that is configured alongside SignServer in the same Helm deployment. | For integrated, low-overhead setup without managing a separate proxy. |
Exposes SignServer through the AJP protocol on port 8009, to be used with an external reverse proxy (such as Apache HTTP Server) that speaks AJP. | If your existing infrastructure uses an AJP-compatible proxy, or if your organization's standards require AJP-based communication between the proxy and the application server. | |
Exposes SignServer over HTTP (port 8081) and HTTPS (port 8082) for use with any standard reverse proxy, such as NGINX or HAProxy, that you manage externally. Port 8082 supports the | If you already have an external reverse proxy in place and prefer HTTP-based communication over AJP. | |
Skips the reverse proxy entirely. SignServer handles TLS itself using JKS keystores you provide as Kubernetes secrets. | If you are not using a reverse proxy at all, for example, in a development or test environment. Not recommended for production. |
Option A: Built-in NGINX Reverse Proxy
To expose SignServer through an NGINX reverse proxy that handles TLS termination, enable the built-in NGINX reverse proxy and use the HTTP proxy back-end ports. Port 8082 accepts the SSL_CLIENT_CERT header for mTLS pass-through to SignServer.
Create the Kubernetes secret for secretInternalNginxCert, containing the NGINX server certificate, private key, and CA certificate used to verify client certificates. The file names must match the configured nginx.host, for example signserverhost.pem, signserverhost-Key.pem, and signserverhost-CA.pem.
Example secret creation:
kubectl create secret generic managementca-secret \
--namespace default \
--from-file=signserverhost.pem=/path/to/certificate.pem \
--from-file=signserverhost-Key.pem=/path/to/key.pem \
--from-file=signserverhost-CA.pem=/path/to/ca-certificate.pem
Configure the NGINX host. For more information on the parameters, see SignServer Helm Deployment Parameters | NGINX-Reverse-Proxy-Parameters:
nginx:
image: nginx:1.27.1
enabled: true
initializeWithSelfSignedTls: false
host: "signserverhost"
externalConfiguration:
# Server TLS credential for cluster internal communication
mountInternalNginxCert: true
secretInternalNginxCert: "managementca-secret"
service:
type: LoadBalancer
bindIP: 0.0.0.0
httpPort: 80
httpsPort: 443
# only relevant if multiple replicas
loadBalancerAccess:
enableStickySessionClientIp: false
# create a load balancer service for each Pod with separate IP address
enableReplicaSpecificAccess: false
additionalHosts:
Create the NGINX reverse proxy:
services:
directHttp:
enabled: false
proxyAJP:
enabled: false
proxyHttp:
enabled: true
type: ClusterIP
bindIP: 0.0.0.0
httpPort: 8081
httpsPort: 8082
Option B: External Reverse Proxy with AJP
This option exposes SignServer through the AJP (Apache JServ Protocol) on port 8009 as a ClusterIP service. Your external reverse proxy connects to this port and handles TLS termination before forwarding requests.
In the Helm values, directHttp and proxyHttp are both disabled, and proxyAJP is enabled with bindIP: 0.0.0.0 and port: 8009. The AJP service is internal to the cluster ClusterIP, so the external proxy must be able to reach it within the cluster network:
services:
directHttp:
enabled: false
proxyAJP:
enabled: true
type: ClusterIP
bindIP: 0.0.0.0
port: 8009
proxyHttp:
enabled: false
Option C: External Reverse Proxy with HTTP Back-end Ports
This option is similar to the built-in NGINX in terms of the ports used, but instead of using the built-in NGINX sidecar, you connect your own externally managed reverse proxy to SignServer's HTTP back-end ports. Port 8081 handles plain HTTP traffic, and port 8082 handles HTTPS and accepts the SSL_CLIENT_CERT header, enabling mTLS pass-through so that client certificate information is forwarded to SignServer.
In the Helm values, directHttp and proxyAJP are disabled, and proxyHttp is enabled with ClusterIP type, binding to 0.0.0.0 on ports 8081 and 8082.
services:
directHttp:
enabled: false
proxyAJP:
enabled: false
proxyHttp:
enabled: true
type: ClusterIP
bindIP: 0.0.0.0
httpPort: 8081
httpsPort: 8082
Option D: TLS Termination Directly in the Container
If you are not using a reverse proxy, SignServer handles TLS itself using JKS (Java KeyStore) keystores that you supply as Kubernetes secrets. Two secrets are required:
A keystore secret containing
server.jks(the server TLS keystore) andserver.storepasswd(a file with the keystore password)A truststore secret containing
truststore.jks(the mTLS truststore with the CA certificate(s) that issue administrator client certificates) andtruststore.storepasswd
kubectl create secret generic keystore-secret \
--from-file=server.jks=server.jks \
--from-file=server.storepasswd=server.storepasswd
kubectl create secret generic truststore-secret \
--from-file=truststore.jks=ManagementCA-chain.jks \
--from-file=truststore.storepasswd=truststore.storepasswd
Once the secrets are created, configure the Helm chart to import the secrets using importAppserverKeystore: true and importAppserverTruststore: true, pointing to the respective secret names:
signserver:
importAppserverKeystore: true
appserverKeystoreSecret: keystore-secret
importAppserverTruststore: true
appserverTruststoreSecret: truststore-secret