As I understand it, when a client wants to authenticate to an application server using Kerberos it must first request a service ticket from the KDC (and possibly a ticket-granting-ticket if it does not already have one). For scenarios where the client cannot directly connect to the KDC but can connect to the application server (for example when an employee brings a laptop home and wants to authenticate with a company website) is there any way the application server can offer to proxy messages to the KDC? It seems like this would not pose any added risk, since Kerberos already doesn't trust the network. Also, is there any reason this is not how things work by default? Is it just that Kerberos was designed in a time when NAT was less common?
There exists this protocol called
IAKerb. It is very much intended to allow clients to proxy messages through application servers when the clients don't have line of sight to a KDC.
It's about as simple as any Kerberos extension protocol goes. When the client can't find a KDC, the GSS-API stack will attempt to bundle up the original Kerberos messages and stuff them into an SPNego message and fire them off the to the application. The Kerberos dance occurs inside the encapsulated message flow and is opaque to the application itself. Once the user gets everything they needed out the protocol flow the client returns to normal and issues tickets for the application.
The trick here is finding compatible clients and applications. MIT supports it. I don't think Heimdal supports it. Windows definitely doesn't support it. Apple supports it.
You can find the spec in the IETF working group: https://tools.ietf.org/html/draft-ietf-kitten-iakerb-03
I'm not aware of any way to do this. For most installations, Kerberos KDCs are looked up via DNS. While it's possible to specify a list of KDCs in the configuration file on the system, this is less robust against changes than DNS.
Normally one wants to be able to obtain a ticket before an application server is even seen. For example, I have a PAM module on my system to automatically fetch a ticket-granting ticket when I log in, and I suspect most Windows systems work similarly. In order to fetch a TGT from the application server, the password would have to be persisted in memory until a user opted to connect to a server that needed a Kerberos request, which might be much later or never.
Additionally, most organizations would either (a) expose the Kerberos server to the Internet, which is how many universities work, or (b) force all users to connect to a VPN to access organizational resources (including the KDC), which is more common among corporations. NAT is not typically a concern in the latter case, since there's ample private network space for all but the largest organizations.
Kerberos is also primarily a UDP-based protocol, which is generally hard to proxy. (For example, SSH proxies TCP and Unix sockets, but not UDP.) Because no connection is established, and it's hard to distinguish between a dropped packet, a firewalled port, and a protocol with no response, proxying wouldn't be very robust, since in the vast majority of cases the packet would be firewalled and therefore the user would have to wait for a timeout before trying with the KDC. While this might not be a problem for some interactive connections, it could be burdensome for automated services or scripts. It's also a lot of extra code for what is a relative edge case.
Even if the packets were sent over the main transport connection, not all protocols are capable of proxying that kind of data. For example, HTTP is poorly suited to such a case due to its single-request, single-response paradigm, where each request is authenticated individually. Even the way that SPNEGO is bolted onto HTTP right now is kind of a hack, and I'm pretty sure the HTTP folks at the IETF would scream pretty loudly at this kind of approach. I sure would if I were them.