tempesta
tempesta copied to clipboard
Access to any vhost is possible with the valid SNI
Access to vhosts does not controlled properly. Its is possible to access any vhost, even not-TLS enabled, after the connection SNI was validated.
Testing
Test to reproduce: tls.test_tls_cert.TlsSNIwithHttpTable Related issue: #tempesta-test/212
tempesta.conf
cache 0;
listen 443 proto=https;
srv_group sg1 { server 192.168.0.44:8000; }
srv_group sg2 { server 192.168.0.44:8001; }
srv_group sg3 { server 192.168.0.44:8002; }
vhost example.com {
proxy_pass sg1;
tls_certificate /tmp/host/tempesta.crt;
tls_certificate_key /tmp/host/tempesta.key;
}
vhost localhost-vhost {
proxy_pass sg2;
}
vhost default-vhost {
proxy_pass sg3;
}
http_chain {
host == "localhost" -> localhost-vhost;
-> default-vhost;
}
tempesta.crt
CN="example.com", SAN=["example.com"]
-----BEGIN CERTIFICATE-----
MIICbTCCAhSgAwIBAgIUXRWfm7k8wE4nnhrhZaYEIgP2kK4wCgYIKoZIzj0EAwIw
gagxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApXYXNoaW5ndG9uMRAwDgYDVQQHDAdT
ZWF0dGxlMSMwIQYDVQQKDBpUZW1wZXN0YSBUZWNobm9sb2dpZXMgSW5jLjEQMA4G
A1UECwwHVGVzdGluZzEUMBIGA1UEAwwLZXhhbXBsZS5jb20xJTAjBgkqhkiG9w0B
CQEWFmluZm9AdGVtcGVzdGEtdGVjaC5jb20wHhcNMjIwOTAxMDk1OTIxWhcNMjMw
OTAyMDk1OTIxWjCBqDELMAkGA1UEBhMCVVMxEzARBgNVBAgMCldhc2hpbmd0b24x
EDAOBgNVBAcMB1NlYXR0bGUxIzAhBgNVBAoMGlRlbXBlc3RhIFRlY2hub2xvZ2ll
cyBJbmMuMRAwDgYDVQQLDAdUZXN0aW5nMRQwEgYDVQQDDAtleGFtcGxlLmNvbTEl
MCMGCSqGSIb3DQEJARYWaW5mb0B0ZW1wZXN0YS10ZWNoLmNvbTBZMBMGByqGSM49
AgEGCCqGSM49AwEHA0IABP/jITvLdTS9pWoUw6DUETrKbh7GnKFVIPddYIUxvND6
3LXbKZ45gHtbwNcTAOHvbzyU84lb+VcfqdJkMzdhoVSjGjAYMBYGA1UdEQQPMA2C
C2V4YW1wbGUuY29tMAoGCCqGSM49BAMCA0cAMEQCIGql666A9VZIgmTxmYA8iwsA
mKgk00jUzpQjvOZ3IXfRAiAQuzZ3/bBuyT+HE1xuJFSXkX6E2dQoCrbOY4OapmUb
5A==
-----END CERTIFICATE-----
tempesta.key
-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgenyf7erszK0yZQq1
Zdk1m6YWZdMsfGJf+c7H2YxvfD6hRANCAAT/4yE7y3U0vaVqFMOg1BE6ym4expyh
VSD3XWCFMbzQ+ty12ymeOYB7W8DXEwDh7288lPOJW/lXH6nSZDM3YaFU
-----END PRIVATE KEY-----
It seems the bug was introduced by https://github.com/tempesta-tech/tempesta/pull/1609 , the fix for https://github.com/tempesta-tech/tempesta/issues/1608 . Also the statement
Tempesta FW does not validate SNI against virtual host name in run time. Instead, it validates Subject Alternative Names (SAN) on certificate loading for the vhost and later, in run time, validates SNI against the available SANs.
from the wiki page https://github.com/tempesta-tech/tempesta/wiki/HTTP-security apparently should be fixed: we must validate SNI against Host
header or :authority
if http_host_required
is enabled.
More related tests, interleaving multiple hosts in multiplexed connections: TlsSNIwithHttpTableMulti
List of related issues after tests: https://github.com/tempesta-tech/tempesta-test/issues/212#issuecomment-1239118068
The task is very confusing by many possible combinations of SNI, SAN, HTTP table rules, Host header value, authority and so on. There were many verbal and Slack communications with some examples. Please update https://github.com/tempesta-tech/tempesta/wiki/Tempesta-TLS#avoiding-virtual-host-confusion and https://github.com/tempesta-tech/tempesta/wiki/HTTP-security for http_strict_host_checking
by description of how the matching (in tfw_tls_sni()
) and verification work along with configuration examples.
This is required to finally finish with #1877 review
UPDATE Apparently https://github.com/tempesta-tech/tempesta/wiki/Virtual-hosts-and-locations also should be fixed since the config
vhost blog { ... }
vhost admin { ... }
http_chain {
host "blog.tempesta.dev" -> blog;
host "admin.tempesta.dev" -> admin;
}
leads to population the host names configuration used for SNI matching by the pseudo-names blog
and admin
. Instead a user must use real host names like in
vhost blog.tempesta.dev { ... }
vhost admin.tempesta.dev { ... }
http_chain {
host "blog.tempesta.dev" -> blog.tempesta.dev;
host "admin.tempesta.dev" -> admin.tempesta.dev;
}
The problem: vhost selection performed by HTTP tables mechanism doesn't know anything about SNI extension sent by client. This leads to a situation known as Domain fronting.
What could happen:
- client may send
SNI=harmless.stuff
and then sendHost: internal.guts
in HTTP headers. This could establish connection tointernal.guts
-related vhosts
Proposed solutions:
- store SNI and validate it later against authority information by plain string comparison. Pros: dumb, will always work. Cons: dynamic memory allocation during TLS handshake (almost doubles time spent in
tfw_tls_sni()
), grows TlsCtx when using static buffers. - compare vhosts selected by SNI in
tfw_tls_sni()
with vhost that had been assigned to the request after HTTP tables has finished its work. Pros: looks fast (only pointers comparison, no dynamic allocation). Cons: looks fragile (see below) - validate authority for EACH request against certificate used by TLS connection. Pros: less fragile that previous solution (but still could be broken by wildcard certificates like SNI=harmless.tempesta.dev, Host: malicious.tempesta.dev, SAN=DNS:.tempesta.dev)
Why vhost pointer comparison is fragile: Given
- certificate SAN=DNS:.tempesta.dev
- tempesta config
host foo { ... }
host bar { ... }
http_tables {
host "foo.tempesta.dev" -> foo;
host "bar.tempesta.dev" -> bar;
}
In tfw_tls_sni()
one of foo/bar vhosts would be assigned to the TLS connection (depending on order of appearance of that vhosts in config). If we compare vhosts after HTTP tables had selected proper virtual host, one of foo/bar
resource will always fail that check.
The only workaround that I've found is to always specify full hostnames in vhost identifiers — this way tfw_tls_sni()
will always select proper vhost. But that could not always be possible if, for example, we have multiple backends for a single virtual host.
@const-t proposed a sensible workaround for the SAN certificate issue: just write HTTP tables rules for strict SNI matching. I think we should try the 2nd way with vhost pointers matching and properly document the TLS and authority matching functionality
@krizhanovsky I've posted an example where HTTP tables do not work. As long as you can set exact server name in vhost directive, that solution could work more or less acceptable. Anyway, I offer you to look at #1878 where that solution had been implemented to see if it have any flaws.
Closed in https://github.com/tempesta-tech/tempesta/pull/1878