Commit 2781c75ff99cd5da7b25f6b883d78ff3de987308
Added tranparent proyxing
Yves Rutschle committed on 7/21/2013, 11:46:45 AMParent: d02ffcd15482a7feaed11d9109ae578eda3734dd
Files changed
ChangeLog | changed |
README | changed |
common.c | changed |
common.h | changed |
sslh-fork.c | changed |
sslh-main.c | changed |
sslh-select.c | changed |
ChangeLog | ||
---|---|---|
@@ -1,5 +1,9 @@ | ||
1 | 1 | vNEXT: |
2 | + Added --transparent option for transparent proxying. | |
3 | + See README for iptables magic and capability | |
4 | + management. | |
5 | + | |
2 | 6 | Fixed bug in sslh-select: if socket dropped while |
3 | 7 | defered_data was present, sslh-select would crash. |
4 | 8 | |
5 | 9 | v1.14: 21DEC2012 |
README | ||
---|---|---|
@@ -156,34 +156,68 @@ | ||
156 | 156 | |
157 | 157 | sslh options: -i for inetd mode, --http to forward http |
158 | 158 | connexions to port 80, and SSH connexions to port 22. |
159 | 159 | |
160 | -==== IP_TPROXY support ==== | |
160 | +==== capapbilities support ==== | |
161 | 161 | |
162 | -There is a netfilter patch that adds an option to the Linux | |
163 | -TCP/IP stack to allow a program to set the source address | |
164 | -of an IP packet that it sends. This could let sslh set the | |
165 | -address of packets to that of the actual client, so that | |
166 | -sshd would see and log the IP address of the client, making | |
167 | -sslh transparent. | |
162 | +On Linux (only?), you can use POSIX capabilities to reduce a | |
163 | +server's capabilities to the minimum it needs (see | |
164 | +capabilities(8). For sslh, this is CAP_NET_ADMIN (to | |
165 | +perform transparent proxy-ing) and CAP_NET_BIND_SERVICE (to | |
166 | +bind to port 443 without being root). | |
168 | 167 | |
169 | -This is not, and won't be, implemented in sslh for the | |
170 | -following reasons (in increasing order of importance): | |
168 | +The simplest way to use capabilities is to give them to the | |
169 | +executable as root: | |
171 | 170 | |
172 | - * It's not vital: the real connecting IP address can be | |
173 | - found in logs. Little gain. | |
174 | - * It's Linux only: it means increased complexity for no | |
175 | - gain to some users. | |
176 | - * It's a patch: it means it'd only be useful to Linux | |
177 | - users who compile their own kernel. | |
178 | - * Only root can use the feature: that's a definite no-no. | |
179 | - Sslh should not, must not, will never run as root. | |
171 | +# setcap cap_net_bind_service,cap_net_admin+pe sslh-select | |
180 | 172 | |
181 | -This isn't to mean that it won't eventually get implemented, | |
182 | -when/if the feature finds its way into the main kernel and | |
183 | -it becomes usuable by non-root processes. | |
173 | +Then you can run sslh-select as an unpriviledged user, e.g.: | |
184 | 174 | |
175 | +$ sslh-select -p myname:443 --ssh localhost:22 --ssl localhost:443 | |
185 | 176 | |
177 | +This has 2 advantages over starting as root with -u: | |
178 | +- You no longer start as root (duh) | |
179 | +- This enables transparent proxying. | |
180 | + | |
181 | +Caveat: CAP_NET_ADMIN does give sslh too many rights, e.g. | |
182 | +configuring the interface. If you're not going to use | |
183 | +transparent proxying, just don't use it. | |
184 | + | |
185 | +==== Transparent proxy support ==== | |
186 | + | |
187 | +On Linux (only?) you can use the --transparent option to | |
188 | +request transparent proying. This means services behind sslh | |
189 | +(Apache, sshd and so on) will see the external IP and ports | |
190 | +as if the external world connected directly to them. This | |
191 | +simplifies IP-based access control (or makes it possible at | |
192 | +all). | |
193 | + | |
194 | +sslh needs extended rights to perform this: you'll need to | |
195 | +give it cap_net_admin capabilities (see appropriate chapter) | |
196 | +or run it as root (but don't do that). | |
197 | + | |
198 | +The firewalling tables also need to be adjusted as follow | |
199 | +(example to connect to https on 4443 -- adapt to your needs | |
200 | +(I don't think it is possible to have httpd listen to 443 in | |
201 | +this scheme -- let me know if you manage that))): | |
202 | + | |
203 | +# iptables -t mangle -N SSLH | |
204 | +# iptables -t mangle -A OUTPUT --protocol tcp --out-interface eth0 --sport 22 --jump SSLH | |
205 | +# iptables -t mangle -A OUTPUT --protocol tcp --out-interface eth0 --sport 4443 --jump SSLH | |
206 | +# iptables -t mangle -A SSLH --jump MARK --set-mark 0x1 | |
207 | +# iptables -t mangle -A SSLH --jump ACCEPT | |
208 | +# ip rule add fwmark 0x1 lookup 100 | |
209 | +# ip route add local 0.0.0.0/0 dev lo table 100 | |
210 | + | |
211 | +This will only work if sslh does not use any loopback | |
212 | +addresses (no 127.0.0.1 or localhost), you'll need to use | |
213 | +explicit IP addresses (or names): | |
214 | + | |
215 | +sslh --listen 192.168.0.1:443 --ssh 192.168.0.1:22 --ssl 192.168.0.1:4443 | |
216 | + | |
217 | +This will not work: | |
218 | +sslh --listen 192.168.0.1:443 --ssh 127.0.0.1:22 --ssl 127.0.0.1:4443 | |
219 | + | |
186 | 220 | ==== Comments? Questions? ==== |
187 | 221 | |
188 | 222 | You can subscribe to the sslh mailing list here: |
189 | 223 | http://rutschle.net/cgi-bin/mailman/listinfo/sslh |
common.c | ||
---|---|---|
@@ -24,8 +24,9 @@ | ||
24 | 24 | int probing_timeout = 2; |
25 | 25 | int inetd = 0; |
26 | 26 | int foreground = 0; |
27 | 27 | int background = 0; |
28 | +int transparent = 0; | |
28 | 29 | int numeric = 0; |
29 | 30 | const char *user_name, *pid_file; |
30 | 31 | |
31 | 32 | struct addrinfo *addr_listen = NULL; /* what addresses do we listen to? */ |
@@ -95,11 +96,37 @@ | ||
95 | 96 | |
96 | 97 | return num_addr; |
97 | 98 | } |
98 | 99 | |
100 | +/* Transparent proxying: bind the peer address of fd to the peer address of | |
101 | + * fd_from */ | |
102 | + | |
103 | +int bind_peer(int fd, int fd_from) | |
104 | +{ | |
105 | + struct addrinfo from; | |
106 | + struct sockaddr_storage ss; | |
107 | + int res, trans = 1; | |
108 | + | |
109 | + memset(&from, 0, sizeof(from)); | |
110 | + from.ai_addr = (struct sockaddr*)&ss; | |
111 | + from.ai_addrlen = sizeof(ss); | |
112 | + | |
113 | + res = getpeername(fd_from, from.ai_addr, &from.ai_addrlen); | |
114 | + CHECK_RES_DIE(res, "getpeername"); | |
115 | + res = setsockopt(fd, SOL_IP, IP_TRANSPARENT, &trans, sizeof(trans)); | |
116 | + CHECK_RES_DIE(res, "setsockopt"); | |
117 | + res = bind(fd, from.ai_addr, from.ai_addrlen); | |
118 | + CHECK_RES_RETURN(res, "bind"); | |
119 | + | |
120 | + return 0; | |
121 | +} | |
122 | + | |
99 | 123 | /* Connect to first address that works and returns a file descriptor, or -1 if |
100 | - * none work. cnx_name points to the name of the service (for logging) */ | |
101 | -int connect_addr(struct addrinfo *addr, const char* cnx_name) | |
124 | + * none work. | |
125 | + * If transparent proxying is on, use fd_from peer address on external address | |
126 | + * of new file descriptor. | |
127 | + * cnx_name points to the name of the service (for logging) */ | |
128 | +int connect_addr(struct addrinfo *addr, int fd_from, const char* cnx_name) | |
102 | 129 | { |
103 | 130 | struct addrinfo *a; |
104 | 131 | char buf[NI_MAXHOST]; |
105 | 132 | int fd, res; |
@@ -112,8 +139,10 @@ | ||
112 | 139 | fd = socket(a->ai_family, SOCK_STREAM, 0); |
113 | 140 | if (fd == -1) { |
114 | 141 | log_message(LOG_ERR, "forward to %s failed:socket: %s\n", cnx_name, strerror(errno)); |
115 | 142 | } else { |
143 | + if (transparent) | |
144 | + bind_peer(fd, fd_from); | |
116 | 145 | res = connect(fd, a->ai_addr, a->ai_addrlen); |
117 | 146 | if (res == -1) { |
118 | 147 | log_message(LOG_ERR, "forward to %s failed:connect: %s\n", |
119 | 148 | cnx_name, strerror(errno)); |
common.h | ||
---|---|---|
@@ -33,9 +33,9 @@ | ||
33 | 33 | } |
34 | 34 | |
35 | 35 | |
36 | 36 | if (res == -1) { \ |
37 | - log_message(LOG_CRIT, "%s: %d\n", str, errno); \ | |
37 | + log_message(LOG_CRIT, "%s:%d:%s\n", str, errno, strerror(errno)); \ | |
38 | 38 | return res; \ |
39 | 39 | } |
40 | 40 | |
41 | 41 | |
@@ -79,9 +79,9 @@ | ||
79 | 79 | |
80 | 80 | |
81 | 81 | /* common.c */ |
82 | 82 | void init_cnx(struct connection *cnx); |
83 | -int connect_addr(struct addrinfo *addr, const char* cnx_name); | |
83 | +int connect_addr(struct addrinfo *addr, int fd_from, const char* cnx_name); | |
84 | 84 | int fd2fd(struct queue *target, struct queue *from); |
85 | 85 | char* sprintaddr(char* buf, size_t size, struct addrinfo *a); |
86 | 86 | void resolve_name(struct addrinfo **out, char* fullname); |
87 | 87 | struct proto* probe_client_protocol(struct connection *cnx); |
@@ -99,9 +99,10 @@ | ||
99 | 99 | |
100 | 100 | int defer_write(struct queue *q, void* data, int data_size); |
101 | 101 | int flush_defered(struct queue *q); |
102 | 102 | |
103 | -extern int probing_timeout, verbose, inetd, foreground, background, numeric; | |
103 | +extern int probing_timeout, verbose, inetd, foreground, | |
104 | + background, transparent, numeric; | |
104 | 105 | extern struct sockaddr_storage addr_ssl, addr_ssh, addr_openvpn; |
105 | 106 | extern struct addrinfo *addr_listen; |
106 | 107 | extern const char* USAGE_STRING; |
107 | 108 | extern const char* user_name, *pid_file; |
sslh-fork.c | ||
---|---|---|
@@ -101,9 +101,9 @@ | ||
101 | 101 | exit(0); |
102 | 102 | } |
103 | 103 | |
104 | 104 | /* Connect the target socket */ |
105 | - out_socket = connect_addr(saddr, prot->description); | |
105 | + out_socket = connect_addr(saddr, in_socket, prot->description); | |
106 | 106 | CHECK_RES_DIE(out_socket, "connect"); |
107 | 107 | |
108 | 108 | cnx.q[1].fd = out_socket; |
109 | 109 |
sslh-main.c | ||
---|---|---|
@@ -57,8 +57,9 @@ | ||
57 | 57 | static struct option const_options[] = { |
58 | 58 | { "inetd", no_argument, &inetd, 1 }, |
59 | 59 | { "foreground", no_argument, &foreground, 1 }, |
60 | 60 | { "background", no_argument, &background, 1 }, |
61 | + { "transparent", no_argument, &transparent, 1 }, | |
61 | 62 | { "numeric", no_argument, &numeric, 1 }, |
62 | 63 | { "verbose", no_argument, &verbose, 1 }, |
63 | 64 | { "user", required_argument, 0, 'u' }, |
64 | 65 | { "config", required_argument, 0, 'F' }, |
sslh-select.c | ||
---|---|---|
@@ -121,9 +121,9 @@ | ||
121 | 121 | fd_set *fds_r, fd_set *fds_w) |
122 | 122 | { |
123 | 123 | struct queue *q = &cnx->q[1]; |
124 | 124 | |
125 | - q->fd = connect_addr(addr, cnx_name); | |
125 | + q->fd = connect_addr(addr, cnx->q[0].fd, cnx_name); | |
126 | 126 | if (q->fd != -1) { |
127 | 127 | log_connection(cnx); |
128 | 128 | set_nonblock(q->fd); |
129 | 129 | flush_defered(q); |
Built with git-ssb-web