├── Makefile ├── ip_vs_tls ├── Makefile └── ip_vs_tls.c └── README.md /Makefile: -------------------------------------------------------------------------------- 1 | obj-y := ip_vs_tls/ tlsparser/ 2 | KDIR := /lib/modules/$(shell uname -r)/build 3 | PWD:=$(shell pwd) 4 | all: 5 | make -C $(KDIR) M=$(PWD) modules 6 | 7 | -------------------------------------------------------------------------------- /ip_vs_tls/Makefile: -------------------------------------------------------------------------------- 1 | ifneq ($(KERNELRELEASE),) 2 | obj-m:=ip_vs_tls.o 3 | else 4 | KDIR := /lib/modules/$(shell uname -r)/build 5 | PWD:=$(shell pwd) 6 | all: 7 | make -C $(KDIR) M=$(PWD) modules 8 | clean: 9 | rm -f *.ko *.o *.symvers *.cmd *.cmd.o 10 | endif 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ipvstls 2 | 3 | ## Introduction 4 | A Lvs module (Run in kernel 3.10/Centos 7). 5 | It's a simple TLS fw that can block specified TLS session. 6 | Supported by tlsparser. 7 | Ipvstls module now listens 443 by default, you can modify port in source code. 8 | Ipvstls module now blocks the TLS session whose server name in ClientHello contains "google", you can add your code into source code. 9 | 10 | 11 | ## Usage 12 | ### Ipvsadm 13 | Add virtual server and realserver by `ipvsadm` which is listening the same port as ipvstls. 14 | 15 | For example: 16 | `sudo ipvsadm -A -t $YOURVIP:443 -s rr` 17 | `sudo ipvsadm -a -t $YOURVIP:443 -r $YOURRSIP:443 -m` 18 | 19 | Now you need to guarantee your virtual server is valid. Do `curl https://$YOURVIP:443/ -ik` to check. 20 | 21 | ### install ipvstls 22 | `git clone git@github.com:mrpre/ipvstls.git` 23 | `cd ipvstls` 24 | `git clone git@github.com:mrpre/tlsparser.git` 25 | `make` 26 | `sudo insmod ./tlsparser/tlspaser.ko;sudo insmod ./ipvstls/ip_vs_tls.ko` 27 | -------------------------------------------------------------------------------- /ip_vs_tls/ip_vs_tls.c: -------------------------------------------------------------------------------- 1 | #define KMSG_COMPONENT "IPVS" 2 | #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | 14 | 15 | #define SERVER_STRING "227 " 16 | #define CLIENT_STRING "PORT" 17 | 18 | 19 | /* 20 | * List of ports (up to IP_VS_APP_MAX_PORTS) to be handled by helper 21 | * First port is set to the default port. 22 | */ 23 | static unsigned int ports_count = 1; 24 | static unsigned short ports[IP_VS_APP_MAX_PORTS] = {1111, 0}; 25 | module_param_array(ports, ushort, &ports_count, 0444); 26 | MODULE_PARM_DESC(ports, "Ports to monitor for TLS control commands"); 27 | 28 | extern void Tp_parse(void *ctx, unsigned char *buf, unsigned int len); 29 | extern void Tp_set_clnt_sni_cb(void *ctx, void* func); 30 | extern void *Tp_ctx_new(void); 31 | extern void Tp_ctx_free(void *ctx); 32 | extern void *Tp_ctx_get_pri(void *ctx); 33 | extern void Tp_ctx_set_pri(void *ctx, void *pri); 34 | extern int Tp_is_reject(void *ctx); 35 | extern void Tp_set_reject(void *ctx); 36 | int get_clnt_sni(void *ctx, char *str, unsigned int len) 37 | { 38 | struct ip_vs_conn *cp = Tp_ctx_get_pri(ctx); 39 | if (unlikely(!cp)) 40 | return 0; 41 | 42 | if (len >= (sizeof("google") -1 ) && strstr(str, "google")) { 43 | 44 | /*block*/ 45 | Tp_set_reject(ctx); 46 | return 0; 47 | } 48 | return 1; 49 | } 50 | 51 | 52 | static int 53 | ip_vs_tls_init_conn(struct ip_vs_app *app, struct ip_vs_conn *cp) 54 | { 55 | void *ctx = Tp_ctx_new(); 56 | if (unlikely(!ctx)) { 57 | return 0; 58 | } 59 | /*set callback*/ 60 | Tp_set_clnt_sni_cb(ctx, get_clnt_sni); 61 | /*save ipvs session to ctx*/ 62 | Tp_ctx_set_pri(ctx, cp); 63 | /*save ctx to ipvs session*/ 64 | cp->app_data = ctx; 65 | return 0; 66 | } 67 | 68 | 69 | static int 70 | ip_vs_tls_done_conn(struct ip_vs_app *app, struct ip_vs_conn *cp) 71 | { 72 | if (cp->app_data) { 73 | Tp_ctx_free(cp->app_data); 74 | cp->app_data = NULL; 75 | } 76 | return 0; 77 | } 78 | 79 | static int ip_vs_tls_out(struct ip_vs_app *app, struct ip_vs_conn *cp, 80 | struct sk_buff *skb, int *diff) 81 | { 82 | *diff = 0; 83 | return 1; 84 | } 85 | 86 | 87 | static int ip_vs_tls_in(struct ip_vs_app *app, struct ip_vs_conn *cp, 88 | struct sk_buff *skb, int *diff) 89 | { 90 | struct iphdr *iph; 91 | struct tcphdr *th; 92 | char *data, *data_start, *data_limit; 93 | 94 | *diff = 0; 95 | 96 | /* Only useful for established sessions */ 97 | if (cp->state != IP_VS_TCP_S_ESTABLISHED) 98 | return 1; 99 | 100 | /* Linear packets are much easier to deal with. */ 101 | if (!skb_make_writable(skb, skb->len)) 102 | return 0; 103 | 104 | iph = ip_hdr(skb); 105 | th = (struct tcphdr *)&(((char *)iph)[iph->ihl*4]); 106 | 107 | /* Since there may be OPTIONS in the TCP packet and the HLEN is 108 | * the length of the header in 32-bit multiples, it is accurate 109 | * to calculate data address by th+HLEN*4 */ 110 | data = data_start = (char *)th + (th->doff << 2); 111 | data_limit = skb_tail_pointer(skb); 112 | 113 | if (cp->app_data && data_limit > data) { 114 | Tp_parse(cp->app_data, data, (unsigned int)(data_limit - data)); 115 | if (Tp_is_reject(cp->app_data)) { 116 | return 0; 117 | } 118 | } 119 | 120 | return 1; 121 | } 122 | 123 | 124 | static struct ip_vs_app ip_vs_tls = { 125 | .name = "tls", 126 | .type = 2, 127 | .protocol = IPPROTO_TCP, 128 | .module = THIS_MODULE, 129 | .incs_list = LIST_HEAD_INIT(ip_vs_tls.incs_list), 130 | .init_conn = ip_vs_tls_init_conn, 131 | .done_conn = ip_vs_tls_done_conn, 132 | .bind_conn = NULL, 133 | .unbind_conn = NULL, 134 | .pkt_out = ip_vs_tls_out, 135 | .pkt_in = ip_vs_tls_in, 136 | }; 137 | 138 | /* 139 | *per netns ip_vs_tls initialization 140 | */ 141 | static int __net_init __ip_vs_tls_init(struct net *net) 142 | { 143 | int i, ret; 144 | struct ip_vs_app *app; 145 | struct netns_ipvs *ipvs = net_ipvs(net); 146 | 147 | if (!ipvs) 148 | return -ENOENT; 149 | 150 | app = register_ip_vs_app(net, &ip_vs_tls); 151 | if (IS_ERR(app)) 152 | return PTR_ERR(app); 153 | 154 | for (i = 0; i < ports_count; i++) { 155 | if (!ports[i]) 156 | continue; 157 | ret = register_ip_vs_app_inc(net, app, app->protocol, ports[i]); 158 | if (ret) 159 | goto err_unreg; 160 | pr_info("%s: loaded support on port[%d] = %d\n", 161 | app->name, i, ports[i]); 162 | } 163 | return 0; 164 | 165 | err_unreg: 166 | unregister_ip_vs_app(net, &ip_vs_tls); 167 | return ret; 168 | } 169 | /* 170 | *netns exit 171 | */ 172 | static void __ip_vs_tls_exit(struct net *net) 173 | { 174 | unregister_ip_vs_app(net, &ip_vs_tls); 175 | } 176 | 177 | static struct pernet_operations ip_vs_ftp_ops = { 178 | .init = __ip_vs_tls_init, 179 | .exit = __ip_vs_tls_exit, 180 | }; 181 | 182 | static int __init ip_vs_tls_init(void) 183 | { 184 | int rv; 185 | rv = register_pernet_subsys(&ip_vs_ftp_ops); 186 | /* rcu_barrier() is called by netns on error */ 187 | return rv; 188 | } 189 | 190 | /* 191 | *ip_vs_tls finish. 192 | */ 193 | static void __exit ip_vs_tls_exit(void) 194 | { 195 | unregister_pernet_subsys(&ip_vs_ftp_ops); 196 | /* rcu_barrier() is called by netns */ 197 | } 198 | 199 | 200 | module_init(ip_vs_tls_init); 201 | module_exit(ip_vs_tls_exit); 202 | MODULE_LICENSE("GPL"); 203 | 204 | --------------------------------------------------------------------------------