diff --git a/alpine/packages/iptables/.gitignore b/alpine/packages/iptables/.gitignore new file mode 100644 index 000000000..3457ccc49 --- /dev/null +++ b/alpine/packages/iptables/.gitignore @@ -0,0 +1 @@ +iptables diff --git a/alpine/packages/iptables/Dockerfile b/alpine/packages/iptables/Dockerfile index 92a67e69c..c39b33bfe 100644 --- a/alpine/packages/iptables/Dockerfile +++ b/alpine/packages/iptables/Dockerfile @@ -1,5 +1,7 @@ FROM ocaml/opam:alpine +RUN sudo apk add m4 +RUN opam install ocamlfind astring -y WORKDIR /app ADD . /app RUN sudo chown -R opam /app -RUN ocamlopt -o iptables main.ml +RUN opam config exec -- ocamlfind ocamlopt -package unix,astring -linkpkg -o iptables main.ml diff --git a/alpine/packages/iptables/main.ml b/alpine/packages/iptables/main.ml index a496c79cf..37517cce2 100644 --- a/alpine/packages/iptables/main.ml +++ b/alpine/packages/iptables/main.ml @@ -1,2 +1,82 @@ -let () = - print_string "Hello world!\n";; +(* ocamlfind ocamlopt -package unix,astring -linkpkg -o iptables iptables.ml *) + +(* +--wait -t nat -I DOCKER-INGRESS -p tcp --dport 80 -j DNAT --to-destination 172.18.0.2:80 +--wait -t nat -D DOCKER-INGRESS -p tcp --dport 80 -j DNAT --to-destination 172.18.0.2:80 +*) + +let _iptables = "/sbin/iptables" +let _proxy = "/usr/bin/docker-proxy" +let _pid_dir = "/var/run/service-port-opener" + +type port = { + proto: string; + dport: string; (* host port *) + ip: string; (* container ip *) + port: string; (* container port *) +} + +let log_fd = Unix.openfile "/var/run/log/service-port-opener.log" [ Unix.O_WRONLY; Unix.O_APPEND; Unix.O_CREAT ] 0o0644 + +let logf fmt = + Printf.ksprintf (fun s -> + let s = s ^ "\n" in + let rec loop ofs remaining = + if remaining > 0 then begin + let n = Unix.write log_fd s ofs remaining in + loop (ofs + n) (remaining - n) + end in + loop 0 (String.length s) + ) fmt + +let pid_filename { proto; dport; ip; port } = + Printf.sprintf "%s/%s.%s.%s.%s.pid" _pid_dir proto dport ip port + +let insert ({ proto; dport; ip; port } as p) = + let filename = pid_filename p in + logf "insert: creating a proxy for %s" filename; + let args = [ _proxy; "-proto"; proto; "-container-ip"; ip; "-container-port"; port; "-host-ip"; "0.0.0.0"; "-host-port"; dport; "-i" ] in + let pid = Unix.fork () in + if pid != 0 then begin + (* write pid to a file (not atomically) *) + let oc = open_out filename in + output_string oc (string_of_int pid); + close_out oc; + logf "binary = %s args = %s" _proxy (String.concat "; " args); + (try Unix.execv _proxy (Array.of_list args) with e -> logf "Failed with %s" (Printexc.to_string e)); + exit 1 + end + +let delete ({ proto; dport; ip; port } as p) = + let filename = pid_filename p in + logf "delete: removing a proxy for %s" filename; + (* read the pid from a file *) + try + let ic = open_in filename in + let pid = int_of_string (input_line ic) in + logf "Sending SIGTERM to %d" pid; + Unix.kill Sys.sigterm pid + with e -> + logf "delete: failed to remove proxy for %s: %s" filename (Printexc.to_string e); + raise e + +let parse_ip_port ip_port = match Astring.String.cut ~sep:":" ip_port with + | None -> + failwith ("Failed to parse :" ^ ip_port) + | Some (ip, port) -> + ip, port + +let _ = + ( try Unix.mkdir _pid_dir 0o0755 with Unix.Unix_error(Unix.EEXIST, _, _) -> () ); + logf "intercepted arguments [%s]" (String.concat "; " (Array.to_list Sys.argv)); + ( match Array.to_list Sys.argv with + | [ _; "--wait"; "-t"; "nat"; "-I"; "DOCKER-INGRESS"; "-p"; proto; "--dport"; dport; "-j"; "DNAT"; "--to-destination"; ip_port ] -> + let ip, port = parse_ip_port ip_port in + insert { proto; dport; ip; port } + | [ _; "--wait"; "-t"; "nat"; "-D"; "DOCKER-INGRESS"; "-p"; proto; "--dport"; dport; "-j"; "DNAT"; "--to-destination"; ip_port ] -> + let ip, port = parse_ip_port ip_port in + delete { proto; dport; ip; port } + | _ -> + () + ); + Unix.execv _iptables Sys.argv