Running Behind a Reverse Proxy

Signotaur can be deployed behind a reverse proxy — for example to centralise TLS certificate management, present a friendly hostname, or consolidate host management. This is fully supported, but the proxy must be configured correctly, because Signotaur serves two different kinds of traffic on the same hostname.

Signotaur is a code-signing service that holds private signing keys. We strongly recommend keeping it accessible only from within your organisation — from trusted internal networks or over a VPN — rather than exposing it to the public internet. A reverse proxy is well suited to simplifying that internal access; we recommend pairing it with firewall rules, allow / deny rules, or an internal-only network interface to keep access restricted.

Why a Reverse Proxy Needs Special Configuration

The Signotaur web dashboard and HTTP API use ordinary HTTP/1.1. The signing service used by SignotaurTool (the command-line client), however, uses gRPC, which requires HTTP/2 from end to end — from the tool, through the proxy, to the Signotaur server.

Most reverse proxies, by default, forward requests to the backend over HTTP/1.1. That works for the dashboard but breaks gRPC: the signing endpoint rejects the downgraded request. A standard proxy_pass-style rule is therefore not sufficient on its own.

Because both kinds of traffic share a single Signotaur hostname and port, the proxy must route them separately:

  • Requests beginning with /SignService/ are gRPC and must be forwarded as gRPC (HTTP/2).
  • All other requests are the web dashboard and HTTP API, and are forwarded normally.

Signotaur listens for both HTTP/1.1 and HTTP/2 on a single HTTPS port, so no change is required on the Signotaur server itself — only the reverse proxy needs configuring.

Configuring nginx

nginx forwards gRPC traffic with the grpc_pass directive, which is separate from the proxy_pass directive used for ordinary HTTP. The example below routes gRPC and HTTP traffic to the same Signotaur backend using two location blocks.

server {
    listen 443 ssl;
    http2 on;                       # HTTP/2 must be enabled for gRPC
    server_name signotaur.example.com;

    ssl_certificate     /etc/nginx/ssl/signotaur.crt;
    ssl_certificate_key /etc/nginx/ssl/signotaur.key;

    # gRPC signing traffic (SignotaurTool) — must use grpc_pass.
    location /SignService/ {
        grpc_pass            grpcs://signotaur-backend:443;
        grpc_ssl_server_name on;
        grpc_ssl_name        $host;
        grpc_ssl_verify      off;
        grpc_read_timeout    300s;
        grpc_send_timeout    300s;
    }

    # Web dashboard and HTTP API.
    location / {
        proxy_pass         https://signotaur-backend:443;
        proxy_http_version 1.1;
        proxy_set_header   Host              $host;
        proxy_set_header   X-Forwarded-For   $proxy_add_x_forwarded_for;
        proxy_set_header   X-Forwarded-Proto $scheme;
        proxy_ssl_verify   off;
    }
}

Replace signotaur.example.com with the public hostname, and signotaur-backend:443 with the host name (or IP address) and port that the Signotaur server itself listens on — the same address you would use to reach the dashboard directly.

A few points to note:

  • http2 on; must be present. Without HTTP/2 enabled on the proxy, SignotaurTool cannot negotiate a gRPC connection at all. On nginx versions earlier than 1.25.1, enable it on the listener instead: listen 443 ssl http2;.
  • The Signotaur server always serves HTTPS, so the backend address uses the grpcs:// (gRPC over TLS) and https:// schemes.
  • grpc_ssl_verify off and proxy_ssl_verify off are used because the Signotaur server's own certificate is usually issued for its internal host name and will not match the public hostname. If the backend certificate is trusted and its name matches, these lines can be removed.
  • /SignService/ is the fixed path of the Signotaur gRPC service. nginx automatically routes requests under that prefix to the grpc_pass block and everything else to the proxy_pass block, regardless of the order in which the blocks are defined.
  • The example above omits access restrictions for brevity. We recommend restricting the proxy to trusted internal networks with allow / deny directives or firewall rules, as noted above.

Nginx Proxy Manager

Nginx Proxy Manager generates a proxy_pass rule for each proxy host automatically, but does not provide a way to add a grpc_pass location through its interface. Add the gRPC location manually:

  1. Edit the proxy host and open the Advanced tab ("Custom Nginx Configuration").
  2. Add the location /SignService/ { ... } block shown above.
  3. Leave the proxy host's main forwarding (the location / rule) as it is — it continues to serve the dashboard.
  4. Ensure HTTP/2 Support is enabled for the proxy host.

A location block does not inherit access rules (allow / deny) from the proxy host's main configuration. If you restrict access by IP address, repeat the same rules inside the location /SignService/ block, otherwise the signing endpoint will not be protected.

Other Reverse Proxies

Any reverse proxy can be used, provided it can forward gRPC traffic to the backend. When choosing or configuring one:

  • It must support HTTP/2 on the front-end connection, so SignotaurTool can connect.
  • It must be able to forward gRPC to the backend for the /SignService/ path — not all proxies can do this.
  • It must still forward ordinary HTTP/1.1 for all other paths, so the web dashboard works.

nginx is the recommended option. Caddy and Traefik also support gRPC routing. Apache can proxy gRPC using the mod_proxy_http2 module and the h2:// scheme, though its gRPC support is less complete than nginx's. Microsoft IIS, including the Application Request Routing (ARR) module, does not forward gRPC and is not suitable in front of the Signotaur signing service.

Verifying the Configuration

After configuring the proxy, test signing through the proxy hostname:

signotaurtool sign --sign-server https://signotaur.example.com --api-key "<your-api-key>" yourfile.exe

If the certificate public key is fetched and the file is signed, the gRPC path is working correctly.

Common Errors

The following errors reported by SignotaurTool indicate a reverse proxy that is not forwarding gRPC correctly:

Error message Cause
Request protocol 'HTTP/1.1' is not supported. The proxy is forwarding the request to Signotaur over HTTP/1.1 (a proxy_pass-style rule) instead of gRPC. Use grpc_pass for the /SignService/ path.
The SSL connection could not be established — application protocol negotiation failed HTTP/2 is not enabled on the proxy's front-end listener, so SignotaurTool cannot negotiate a gRPC connection. Enable HTTP/2 (http2 on;).

The web dashboard can work correctly even when signing does not. Because the dashboard uses ordinary HTTP, a proxy that only handles HTTP/1.1 will serve the dashboard but fail every signing operation. Always test signing itself after changing the proxy configuration.