Skip to content

Commit f88d289

Browse files
committed
Feature: make mTLS optional via configuration toggle
1 parent de2945b commit f88d289

3 files changed

Lines changed: 35 additions & 17 deletions

File tree

README.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ This tool is suitable for zero-trust network access, secure internal infrastruct
1818
## Features
1919

2020
- **Standard Compliant**: Full support for RFC 7231 HTTP/1.1 `CONNECT` tunneling.
21-
- **Secure by Design**: Enforced mTLS authentication + optional username/password.
21+
- **Secure by Design**: Optional mTLS authentication + mandatory/optional HTTP Basic Auth.
2222
- **Minimalist**: Single binary, zero dependencies, built on Go standard library.
2323
- **Production Ready**: OOM protection, graceful shutdown, atomic counters.
2424

@@ -39,6 +39,7 @@ Edit `hproxy.json`:
3939
"server_pem": "/path/to/server.crt",
4040
"server_key": "/path/to/server.key",
4141
"client_pem": "/path/to/client.pem",
42+
"mtls": true,
4243
"auth_users": {
4344
"your-username": "your-secure-password"
4445
}
@@ -50,7 +51,8 @@ Edit `hproxy.json`:
5051
| `listen_addr` | Address and port to listen on |
5152
| `server_pem` | Server certificate (PEM format) |
5253
| `server_key` | Server private key |
53-
| `client_pem` | CA certificate for verifying client certs |
54+
| `client_pem` | CA certificate for verifying client certs (only if `mtls` is `true`) |
55+
| `mtls` | (Optional) Enable/disable Client Certificate verification (Default: `true`) |
5456
| `auth_users` | (Optional) Username/password pairs for HTTP Basic Auth |
5557

5658
### 3. Run
@@ -110,7 +112,7 @@ Any client that supports "HTTPS Proxy" with client certificate authentication ca
110112

111113
## Security Features
112114

113-
- **mTLS Enforcement**: Clients must present a valid certificate signed by the configured CA.
115+
- **Optional mTLS**: Toggleable client certificate verification via the `mtls` config.
114116
- **OOM Protection**: HTTP handshake limited to 16KB to prevent memory exhaustion.
115117
- **Log Sanitization**: Target addresses are filtered to prevent log injection attacks.
116118
- **Graceful Shutdown**: Handles SIGINT/SIGTERM for clean connection termination.

hproxy.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
"server_pem": "server.crt",
44
"server_key": "server.key",
55
"client_pem": "client.pem",
6+
"mtls": true,
67
"auth_users": {
78
"your-username": "your-secure-password"
89
}

main.go

Lines changed: 29 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ type Config struct {
2424
ServerPEM string `json:"server_pem"`
2525
ServerKey string `json:"server_key"`
2626
ClientPEM string `json:"client_pem"`
27+
MTLS *bool `json:"mtls,omitempty"` // Use pointer to distinguish between false and missing
2728
ListenAddr string `json:"listen_addr"`
2829
AuthUsers map[string]string `json:"auth_users,omitempty"`
2930
}
@@ -32,14 +33,16 @@ type Server struct {
3233
serverPEM string
3334
serverKEY string
3435
clientPEM string
36+
mtls bool
3537
authUsers map[string]string
3638
}
3739

38-
func NewServer(serverPEM string, serverKEY string, clientPEM string, authUsers map[string]string) *Server {
40+
func NewServer(serverPEM string, serverKEY string, clientPEM string, mtls bool, authUsers map[string]string) *Server {
3941
return &Server{
4042
serverPEM: serverPEM,
4143
serverKEY: serverKEY,
4244
clientPEM: clientPEM,
45+
mtls: mtls,
4346
authUsers: authUsers,
4447
}
4548
}
@@ -219,25 +222,32 @@ func (s *Server) ListenAndServe(addr string) error {
219222
return errors.New("failed to load server certificate and key: " + err.Error())
220223
}
221224

222-
certBytes, err := os.ReadFile(s.clientPEM)
223-
if err != nil {
224-
return errors.New("failed to read client CA file: " + err.Error())
225-
}
226-
227-
clientCertPool := x509.NewCertPool()
228-
if ok := clientCertPool.AppendCertsFromPEM(certBytes); !ok {
229-
return errors.New("failed to parse client CA certificates")
230-
}
231-
232225
tlsConfig := &tls.Config{
233226
MinVersion: tls.VersionTLS12,
234227
Certificates: []tls.Certificate{cert},
235-
ClientAuth: tls.RequireAndVerifyClientCert,
236-
ClientCAs: clientCertPool,
237228
SessionTicketsDisabled: false,
238229
ClientSessionCache: tls.NewLRUClientSessionCache(128),
239230
}
240231

232+
if s.mtls {
233+
certBytes, err := os.ReadFile(s.clientPEM)
234+
if err != nil {
235+
return errors.New("failed to read client CA file: " + err.Error())
236+
}
237+
238+
clientCertPool := x509.NewCertPool()
239+
if ok := clientCertPool.AppendCertsFromPEM(certBytes); !ok {
240+
return errors.New("failed to parse client CA certificates")
241+
}
242+
243+
tlsConfig.ClientAuth = tls.RequireAndVerifyClientCert
244+
tlsConfig.ClientCAs = clientCertPool
245+
log.Printf("[SYSTEM] Server listening on %s (mTLS Enabled)", addr)
246+
} else {
247+
tlsConfig.ClientAuth = tls.NoClientCert
248+
log.Printf("[SYSTEM] Server listening on %s (mTLS Disabled)", addr)
249+
}
250+
241251
listener, err := tls.Listen("tcp", addr, tlsConfig)
242252
if err != nil {
243253
return err
@@ -295,7 +305,12 @@ func main() {
295305
log.Fatalf("Failed to parse configuration file: %v", err)
296306
}
297307

298-
s := NewServer(config.ServerPEM, config.ServerKey, config.ClientPEM, config.AuthUsers)
308+
mtlsEnabled := true
309+
if config.MTLS != nil {
310+
mtlsEnabled = *config.MTLS
311+
}
312+
313+
s := NewServer(config.ServerPEM, config.ServerKey, config.ClientPEM, mtlsEnabled, config.AuthUsers)
299314

300315
if err := s.ListenAndServe(config.ListenAddr); err != nil {
301316
log.Fatalf("Server failed: %v", err)

0 commit comments

Comments
 (0)