tlsgate

TLS reverse proxy
git clone git://git.akobets.xyz/tlsgate
Log | Files | Refs | README | LICENSE

commit 29fc72c661e5dcde3dd91ba57b7cf7fff9880082
parent e5f6df13fc86b62d4d691c3acfc212ad92e59549
Author: Artem Kobets <artem@akobets.xyz>
Date:   Wed, 23 Sep 2020 20:46:54 +0300

add -s, -S, -t flags

Diffstat:
Mmain.c | 216++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------------
Mtlsgate.1 | 69+++++++++++++++++++++++++++++++++++++++++++++------------------------
2 files changed, 181 insertions(+), 104 deletions(-)

diff --git a/main.c b/main.c @@ -14,10 +14,28 @@ #include "sock.h" #include "util.h" -// milliseconds -#define REQUEST_TIMEOUT 30000 - -static void serve(struct tls *ctx, int cfd, struct sockaddr_storage *addr); +enum { + MODE_NONE, + MODE_TLS_CLIENT, + MODE_TLS_SERVER +}; + +struct settings { + int mode; + struct tls *tls_ctx; + int timeout; + char *tls_cert_file; + char *tls_key_file; + char *tls_ca_file; + char *proxy_host; + char *proxy_port; + char *proxy_udsfile; + char *server_host; + char *server_port; + char *server_udsfile; +}; + +static void serve(struct settings *s, int cfd, struct sockaddr_storage *addr); static void cleanup(void); static void sigchld(int unused); static void sigcleanup(int sig); @@ -25,31 +43,23 @@ static void handle_termsignals(void (*func)(int)); static void usage(void); char *argv0; - -static char *cert_file = NULL; -static char *key_file = NULL; -static char *ca_file = NULL; -static char *proxy_host = NULL; -static char *proxy_port = NULL; -static char *proxy_udsfile = NULL; -static char *server_host = NULL; -static char *server_port = NULL; -static char *server_udsfile = NULL; +static char *proxy_udsfile; void -serve(struct tls *ctx, int cfd, struct sockaddr_storage *addr) +serve(struct settings *s, int cfd, struct sockaddr_storage *addr) { - struct tls *cctx = NULL; - int sfd = -1; + time_t t; + char addr_str[INET6_ADDRSTRLEN]; // > INET_ADDRSTRLEN + char tstmp[21]; + + int sfd, fd_tls = -1, fd_norm = -1; + struct tls *tls_ctx = NULL; int nready; int ret; struct pollfd pfds[2], pfd[1]; char buf[BUFSIZ], *bufp; ssize_t nread, nwritten; - - time_t t; - char addr_str[INET6_ADDRSTRLEN]; // > INET_ADDRSTRLEN - char tstmp[21]; + int poll_timeout; // log t = time(NULL); @@ -57,41 +67,58 @@ serve(struct tls *ctx, int cfd, struct sockaddr_storage *addr) warnx("strftime: Exceeded buffer capacity"); goto cleanup; } - if (sock_get_addr_str(addr, addr_str, sizeof(addr_str))) + if (sock_get_addr_str(addr, addr_str, sizeof(addr_str)) == -1) goto cleanup; printf("%s %s\n", tstmp, addr_str); // connect to server - sfd = server_udsfile - ? sock_server_uds(server_udsfile) - : sock_server_ips(server_host, server_port); + sfd = s->server_udsfile + ? sock_server_uds(s->server_udsfile) + : sock_server_ips(s->server_host, s->server_port); if (sfd == -1) goto cleanup; - if (sock_set_nonblock(cfd) == -1) + if (s->mode == MODE_TLS_CLIENT) { + fd_tls = cfd; + fd_norm = sfd; + } else { + // MODE_TLS_SERVER + fd_tls = sfd; + fd_norm = cfd; + } + + if (sock_set_nonblock(fd_tls) == -1) goto cleanup; - if (sock_set_nonblock(sfd) == -1) + if (sock_set_nonblock(fd_norm) == -1) goto cleanup; - if (tls_accept_socket(ctx, &cctx, cfd) == -1) { - warn("tls_accept_socket"); - goto cleanup; + if (s->mode == MODE_TLS_CLIENT) { + if (tls_accept_socket(s->tls_ctx, &tls_ctx, fd_tls) == -1) { + warn("tls_accept_socket"); + goto cleanup; + } + } else { + // MODE_TLS_SERVER + if (tls_connect_socket(s->tls_ctx, fd_tls, s->server_host) == -1) { + warn("tls_connect_socket"); + goto cleanup; + } + tls_ctx = s->tls_ctx; } - // client - pfds[0].fd = cfd; + pfds[0].fd = fd_tls; pfds[0].events = POLLIN | POLLOUT; - // server - pfds[1].fd = sfd; + pfds[1].fd = fd_norm; pfds[1].events = POLLIN; + poll_timeout = s->timeout > 0 ? s->timeout * 1000 : -1; // when checking POLLIN, also check POLLHUP - while (poll(pfds, 2, REQUEST_TIMEOUT) > 0) { - // client->proxy->server + while (poll(pfds, 2, poll_timeout) > 0) { + // tls->proxy->normal if (pfds[0].revents & (pfds[0].events | POLLHUP)) { - // read client->proxy + // read tls->proxy while (1) { - nread = tls_read(cctx, buf, sizeof(buf)); + nread = tls_read(tls_ctx, buf, sizeof(buf)); if (nread == TLS_WANT_POLLIN) { pfds[0].events = POLLIN; break; @@ -106,19 +133,19 @@ serve(struct tls *ctx, int cfd, struct sockaddr_storage *addr) } else if (nread == 0) { goto cleanup; } else { - // write proxy->server - pfd[0].fd = sfd; + // write proxy->normal + pfd[0].fd = fd_norm; pfd[0].events = POLLOUT; bufp = buf; while (nread > 0) { - nready = poll(pfd, 1, REQUEST_TIMEOUT); + nready = poll(pfd, 1, poll_timeout); if (nready == -1) { goto cleanup; } else if (nready == 0) { goto cleanup; } else { if (pfd[0].revents & pfd[0].events) { - nwritten = write(sfd, bufp, nread); + nwritten = write(fd_norm, bufp, nread); if (nwritten == -1) { goto cleanup; } else { @@ -138,11 +165,11 @@ serve(struct tls *ctx, int cfd, struct sockaddr_storage *addr) goto cleanup; } - // server->proxy->client + // normal->proxy->tls if (pfds[1].revents & (pfds[1].events | POLLHUP)) { - // read server->proxy + // read normal->proxy while (1) { - nread = read(sfd, buf, sizeof(buf)); + nread = read(fd_norm, buf, sizeof(buf)); if (nread == -1) { if (errno == EAGAIN || errno == EWOULDBLOCK) { break; @@ -152,19 +179,19 @@ serve(struct tls *ctx, int cfd, struct sockaddr_storage *addr) } else if (nread == 0) { goto cleanup; } else { - // write proxy->client - pfd[0].fd = cfd; + // write proxy->tls + pfd[0].fd = fd_tls; pfd[0].events = POLLIN | POLLOUT; bufp = buf; while (nread > 0) { - nready = poll(pfd, 1, REQUEST_TIMEOUT); + nready = poll(pfd, 1, poll_timeout); if (nready == -1) { goto cleanup; } else if (nready == 0) { goto cleanup; } else { if (pfd[0].revents & (pfd[0].events | POLLHUP)) { - nwritten = tls_write(cctx, bufp, nread); + nwritten = tls_write(tls_ctx, bufp, nread); if (nwritten == TLS_WANT_POLLIN) { pfd[0].events = POLLIN; } else if (nwritten == TLS_WANT_POLLOUT) { @@ -190,9 +217,9 @@ serve(struct tls *ctx, int cfd, struct sockaddr_storage *addr) } cleanup: - if (cctx != NULL) { + if (tls_ctx != NULL) { while (1) { - ret = tls_close(cctx); + ret = tls_close(tls_ctx); if ( ret == TLS_WANT_POLLIN || ret == TLS_WANT_POLLOUT @@ -202,7 +229,7 @@ cleanup: break; } } - tls_free(cctx); + tls_free(tls_ctx); } close(cfd); if (sfd != -1) @@ -253,7 +280,7 @@ usage(void) { fprintf( stderr, - "usage: %s [-v] -c cert -k key [-C ca]\n" + "usage: %s -s/-S [-v] [-c cert] [-k key] [-C ca] [-t timeout]\n" " [-h host] [-p port] [-u file]\n" " [-H host] [-P port] [-U file]\n", argv0 @@ -264,8 +291,8 @@ usage(void) int main(int argc, char **argv) { + struct settings s; int opt; - struct tls *ctx; struct tls_config *config; int fd, cfd; struct sigaction act; @@ -274,34 +301,56 @@ main(int argc, char **argv) argv0 = argv[0]; - while ((opt = getopt(argc, argv, "c:k:C:h:p:u:H:P:U:v")) != -1) { + s.mode = MODE_NONE; + s.tls_ctx = NULL; + s.timeout = 30; + s.tls_cert_file = NULL; + s.tls_key_file = NULL; + s.tls_ca_file = NULL; + s.proxy_host = NULL; + s.proxy_port = NULL; + s.proxy_udsfile = NULL; + s.server_host = NULL; + s.server_port = NULL; + s.server_udsfile = NULL; + + while ((opt = getopt(argc, argv, "sSc:k:C:h:p:u:H:P:U:t:v")) != -1) { switch (opt) { + case 's': + s.mode = MODE_TLS_CLIENT; + break; + case 'S': + s.mode = MODE_TLS_SERVER; + break; case 'c': - cert_file = optarg; + s.tls_cert_file = optarg; break; case 'k': - key_file = optarg; + s.tls_key_file = optarg; break; case 'C': - ca_file = optarg; + s.tls_ca_file = optarg; break; case 'h': - proxy_host = optarg; + s.proxy_host = optarg; break; case 'p': - proxy_port = optarg; + s.proxy_port = optarg; break; case 'u': - proxy_udsfile = optarg; + s.proxy_udsfile = proxy_udsfile = optarg; break; case 'H': - server_host = optarg; + s.server_host = optarg; break; case 'P': - server_port = optarg; + s.server_port = optarg; break; case 'U': - server_udsfile = optarg; + s.server_udsfile = optarg; + break; + case 't': + s.timeout = atoi(optarg); break; case 'v': puts(VERSION); @@ -313,36 +362,43 @@ main(int argc, char **argv) } } + if (s.mode == MODE_NONE) + usage(); // cert and private key files are required - if (cert_file == NULL || key_file == NULL) + if (s.tls_cert_file == NULL || s.tls_key_file == NULL) usage(); // allow IPS or UDS proxy if ( - (proxy_host != NULL && proxy_udsfile != NULL) || - !(proxy_port != NULL || proxy_udsfile != NULL) + (s.proxy_host != NULL && s.proxy_udsfile != NULL) || + !(s.proxy_port != NULL || s.proxy_udsfile != NULL) ) usage(); // allow IPS or UDS server if ( - (server_host != NULL && server_udsfile != NULL) || - !(server_port != NULL || server_udsfile != NULL) + (s.server_host != NULL && s.server_udsfile != NULL) || + !(s.server_port != NULL || s.server_udsfile != NULL) ) usage(); // setup tls - if ((ctx = tls_server()) == NULL) - err("tls_server"); + if (s.mode == MODE_TLS_CLIENT) { + if ((s.tls_ctx = tls_server()) == NULL) + err("tls_server"); + } else { + if ((s.tls_ctx = tls_client()) == NULL) + err("tls_client"); + } if ((config = tls_config_new()) == NULL) err("tls_config_new"); - if (tls_config_set_cert_file(config, cert_file) == -1) + if (tls_config_set_cert_file(config, s.tls_cert_file) == -1) err("tls_config_set_cert_file"); - if (tls_config_set_key_file(config, key_file) == -1) + if (tls_config_set_key_file(config, s.tls_key_file) == -1) err("tls_config_set_key_file"); - if (ca_file != NULL) { - if (tls_config_set_ca_file(config, ca_file) == -1) + if (s.tls_ca_file != NULL) { + if (tls_config_set_ca_file(config, s.tls_ca_file) == -1) err("tls_config_set_ca_file"); } - if (tls_configure(ctx, config) == -1) + if (tls_configure(s.tls_ctx, config) == -1) err("tls_configure"); // set new process group @@ -352,9 +408,9 @@ main(int argc, char **argv) handle_termsignals(sigcleanup); // setup proxy socket - fd = proxy_udsfile - ? sock_proxy_uds(proxy_udsfile) - : sock_proxy_ips(proxy_host, proxy_port); + fd = s.proxy_udsfile + ? sock_proxy_uds(s.proxy_udsfile) + : sock_proxy_ips(s.proxy_host, s.proxy_port); switch (fork()) { case 0: @@ -380,7 +436,7 @@ main(int argc, char **argv) switch (fork()) { case 0: - serve(ctx, cfd, &addr); + serve(&s, cfd, &addr); exit(0); case -1: warn("fork"); diff --git a/tlsgate.1 b/tlsgate.1 @@ -1,54 +1,75 @@ -.TH TLSGATE 1 2020-09-19 +.TH TLSGATE 1 2020-09-23 .SH NAME -tlsgate \- TLS reverse proxy +tlsgate - TLS reverse proxy .SH SYNOPSIS .B tlsgate -[-v] -c cert -k key [-C ca] [-h host] -[-p port] [-H host] [-P port] [-U file] +-s/-S [-v] [-c cert] [-k key] [-C ca] [-t timeout] [-h host] [-p port] +[-u file] [-H host] [-P port] [-U file] .SH DESCRIPTION .B tlsgate is a TLS reverse proxy which can be used to expose an unencrypted connection. For example, to set up an HTTPS connection for an HTTP server. .SH OPTIONS .TP -.B \-v -Print version number and exit. +.B -s +Client side is TLS side. +.TP +.B -S +Server side is TLS side. .TP -.B \-c cert -Path to certificate. +.B -c cert +Path to public certificate. Required with -s. .TP -.B \-k key -Path to private key. +.B -k key +Path to private key. Required with -s. .TP -.B \-C ca +.B -C ca Path to CA root certificates. .TP -.B \-h host +.B -t timeout +Connection timeout (in seconds). Default is 30. 0 means no timeout. +Default makes sure hanging client connections are removed. +Setting this to 0 is useful if you do not want connections to timeout, +for example if you're setting up TLS support for an IRC client (see EXAMPLES). +.TP +.B -h host TLS proxy hostname. .TP -.B \-p port +.B -p port TLS proxy port number. .TP -.B \-u file +.B -u file TLS UNIX domain socket path. .TP -.B \-H host +.B -H host Server hostname. .TP -.B \-P port +.B -P port Server port number. .TP -.B \-U file +.B -U file Server UNIX domain socket path. +.TP +.B -v +Print version number and exit. .SH EXAMPLES Accept connections on port 443 and pass them to a local http server on port 80. .PP -tlsgate \\ - -c /etc/path/to/cert/cert.pem \\ - -k /etc/path/to/key/key.pem \\ - -h 0.0.0.0 \\ - -p 443 \\ - -H 0.0.0.0 \\ - -P 80 + tlsgate \\ + -s \\ + -c /etc/path/to/cert/cert.pem \\ + -k /etc/path/to/key/key.pem \\ + -h 0.0.0.0 \\ + -p 443 \\ + -H 0.0.0.0 \\ + -P 80 +.PP +Use TLS with an IRC client that does not support it (client is connecting to /tmp/irc.sock). +.PP + tlsgate \\ + -S \\ + -u /tmp/irc.sock \\ + -H irc.server.com \\ + -P 6697 .SH AUTHOR Artem Kobets <artem@akobets.xyz>