-
Notifications
You must be signed in to change notification settings - Fork 1.8k
[Kubernetes]: The kube server could be used as http-proxy for docker #7469
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 7 commits
Commits
Show all changes
17 commits
Select commit
Hold shift + click to select a range
bf9fc28
The kube server could be used as http-proxy for docker, if desired ex…
renukamanavalan 08d6e71
Fix lgtm warnings.
renukamanavalan c34b9f2
Fix syntax error
renukamanavalan 0380849
convert bytecode to string, before parsing.
renukamanavalan 899c172
Merge remote-tracking branch 'upstream/master' into proxy
renukamanavalan 859e30d
Updates per review comments.
renukamanavalan 21dccf8
Unit test updated to send proc o/p as byte code, in sync with subproc…
renukamanavalan e4f4c02
Merge remote-tracking branch 'upstream/master' into proxy
renukamanavalan 42f4b33
Added an explicit flag to configure k8s master as http-proxy.
renukamanavalan 6753a0f
Use k8s http-proxy or not is associated with config, instead of build…
renukamanavalan 380e9e6
Merge remote-tracking branch 'upstream/master' into proxy
renukamanavalan ce0f672
Fix syntax error
renukamanavalan b0d736d
kubelet demands the path to resolv.conf.
renukamanavalan d4dfa28
Reverted back to 1.18.6, as we are still working with 1.21.1
renukamanavalan 3581513
Merge remote-tracking branch 'upstream/master' into proxy
renukamanavalan 44a5127
Back to 1.21.1
renukamanavalan 840c997
Merge remote-tracking branch 'upstream/master' into proxy
renukamanavalan File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,135 @@ | ||
| #!/usr/bin/env python3 | ||
|
|
||
| import ipaddress | ||
| import os | ||
| import re | ||
| import socket | ||
| import subprocess | ||
| import syslog | ||
|
|
||
| UNIT_TESTING = 0 | ||
|
|
||
| # NOTE: | ||
| # Unable to use python-iptables as that does not create rules per ip-tables default | ||
| # which is nf_tables. So rules added via iptc package will not be listed under | ||
| # "sudo iptables -t nat -L -n". But available in kernel. To list, we need to | ||
| # use legacy mode as "sudo iptables-legacy -t nat -L -n". | ||
| # As we can't use two modes and using non-default could make any debugging effort | ||
| # very tough. | ||
|
|
||
|
|
||
| from urllib.parse import urlparse | ||
|
|
||
| DST_FILE = "/etc/systemd/system/docker.service.d/http_proxy.conf" | ||
| DST_IP = None | ||
| DST_PORT = None | ||
| SQUID_PORT = "3128" | ||
|
|
||
| def _get_ip(ip_str): | ||
| ret = "" | ||
| if ip_str: | ||
| try: | ||
| ipaddress.ip_address(ip_str) | ||
| ret = ip_str | ||
| except ValueError: | ||
| pass | ||
|
|
||
| if not ret: | ||
| try: | ||
| ret = socket.gethostbyname(ip_str) | ||
| except (OSError, socket.error): | ||
| pass | ||
| if not ret: | ||
| syslog.syslog(syslog.LOG_ERR, "{} is neither IP nor resolves to IP". | ||
| format(ip_str)) | ||
| return ret | ||
|
|
||
|
|
||
| def _get_dst_info(): | ||
| global DST_IP, DST_PORT | ||
| DST_IP = None | ||
| DST_PORT = None | ||
| print("DST_FILE={}".format(DST_FILE)) | ||
| if os.path.exists(DST_FILE): | ||
| with open(DST_FILE, "r") as s: | ||
| for line in s.readlines(): | ||
| url_match = re.search('^Environment=.HTTP_PROXY=(.+?)"', line) | ||
| if url_match: | ||
| url = urlparse(url_match.group(1)) | ||
| DST_IP = _get_ip(url.hostname) | ||
| DST_PORT = url.port | ||
| break | ||
| else: | ||
| print("{} not available".format(DST_FILE)) | ||
| print("DST_IP={}".format(DST_IP)) | ||
|
|
||
|
|
||
| def _is_rule_match(rule): | ||
| expect = "DNAT tcp -- 0.0.0.0/0 {} tcp dpt:{} to:".format( | ||
| DST_IP, DST_PORT) | ||
|
|
||
| # Remove duplicate spaces | ||
| rule = " ".join(rule.split()).strip() | ||
|
|
||
| if rule.startswith(expect): | ||
| return rule[len(expect):] | ||
| else: | ||
| return "" | ||
|
|
||
|
|
||
| def check_proc(proc): | ||
| if proc.returncode: | ||
| syslog.syslog(syslog.LOG_ERR, "Failed to run: cmd: {}".format(proc.args)) | ||
| syslog.syslog(syslog.LOG_ERR, "Failed to run: stdout: {}".format(proc.stdout)) | ||
| syslog.syslog(syslog.LOG_ERR, "Failed to run: stderr: {}".format(proc.stderr)) | ||
| if not UNIT_TESTING: | ||
isabelmsft marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| assert False | ||
|
|
||
|
|
||
| def iptable_proxy_rule_upd(ip_str, port = SQUID_PORT): | ||
| _get_dst_info() | ||
| if not DST_IP: | ||
| # There is no proxy in use. Bail out. | ||
| return "" | ||
|
|
||
| destination = "" | ||
| if ip_str: | ||
| upd_ip = _get_ip(ip_str) | ||
| if not upd_ip: | ||
| return "" | ||
| destination = "{}:{}".format(upd_ip, port) | ||
|
|
||
| found = False | ||
| num = 0 | ||
|
|
||
| while True: | ||
| num += 1 | ||
|
|
||
| cmd = "sudo iptables -t nat -n -L OUTPUT {}".format(num) | ||
| proc = subprocess.run(cmd, shell=True, capture_output=True) | ||
| check_proc(proc) | ||
|
|
||
| if not proc.stdout: | ||
| # No more rule | ||
| break | ||
|
|
||
| rule_dest = _is_rule_match(proc.stdout.decode("utf-8").strip()) | ||
renukamanavalan marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| if rule_dest: | ||
| if not found and destination and (rule_dest == destination): | ||
| found = True | ||
| else: | ||
| # Duplicate or different IP - delete it | ||
| cmd = "sudo iptables -t nat -D OUTPUT {}".format(num) | ||
| proc = subprocess.run(cmd, shell=True, capture_output=True) | ||
| check_proc(proc) | ||
| # Decrement number to accommodate deleted rule | ||
| num -= 1 | ||
|
|
||
| if destination and not found: | ||
| cmd = "sudo iptables -t nat -A OUTPUT -p tcp -d {} --dport {} -j DNAT --to-destination {}".format( | ||
| DST_IP, DST_PORT, destination) | ||
| proc = subprocess.run(cmd, shell=True, capture_output=True) | ||
|
|
||
| check_proc(proc) | ||
|
|
||
| return destination | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,178 @@ | ||
| import os | ||
| import re | ||
| import sys | ||
| from unittest.mock import MagicMock, patch | ||
|
|
||
| import pytest | ||
|
|
||
| from . import common_test | ||
|
|
||
| sys.path.append("ctrmgr") | ||
| import ctrmgr_iptables | ||
|
|
||
|
|
||
| PROXY_FILE="http_proxy.conf" | ||
|
|
||
| test_data = { | ||
| "1": { | ||
| "ip": "10.10.20.20", | ||
| "port": "3128", | ||
| "pre_rules": [ | ||
| "DNAT tcp -- 20.20.0.0/0 172.16.1.1 tcp dpt:8080 to:100.127.20.21:8080", | ||
| "DNAT tcp -- 0.0.0.0/0 172.16.1.1 tcp dpt:3128 to:11.11.11.11:8080", | ||
| "DNAT tcp -- 0.0.0.0/0 172.16.1.1 tcp dpt:3128 to:11.11.11.11:8080", | ||
| "DNAT tcp -- 0.0.0.0/0 172.16.1.1 tcp dpt:3128 to:11.11.11.11:8088", | ||
| "DNAT tcp -- 0.0.0.0/0 172.16.1.1 tcp dpt:3129 to:11.11.11.11:8088" | ||
| ], | ||
| "post_rules": [ | ||
| "DNAT tcp -- 20.20.0.0/0 172.16.1.1 tcp dpt:8080 to:100.127.20.21:8080", | ||
| "DNAT tcp -- 0.0.0.0/0 172.16.1.1 tcp dpt:3129 to:11.11.11.11:8088", | ||
| "DNAT tcp -- 0.0.0.0/0 172.16.1.1 tcp dpt:3128 to:10.10.20.20:3128" | ||
| ], | ||
| "ret": "10.10.20.20:3128" | ||
| }, | ||
| "2": { | ||
| "ip": "", | ||
| "port": "", | ||
| "pre_rules": [ | ||
| "DNAT tcp -- 20.20.0.0/0 172.16.1.1 tcp dpt:8080 to:100.127.20.21:8080", | ||
| "DNAT tcp -- 0.0.0.0/0 172.16.1.1 tcp dpt:3128 to:11.11.11.11:8080", | ||
| "DNAT tcp -- 0.0.0.0/0 172.16.1.1 tcp dpt:3128 to:11.11.11.11:8080", | ||
| "DNAT tcp -- 0.0.0.0/0 172.16.1.1 tcp dpt:3128 to:11.11.11.11:8088" | ||
| ], | ||
| "post_rules": [ | ||
| "DNAT tcp -- 20.20.0.0/0 172.16.1.1 tcp dpt:8080 to:100.127.20.21:8080" | ||
| ], | ||
| "ret": "" | ||
| }, | ||
| "3": { | ||
| "ip": "www.google.com", | ||
| "port": "3128", | ||
| "pre_rules": [ | ||
| "DNAT tcp -- 20.20.0.0/0 172.16.1.1 tcp dpt:8080 to:100.127.20.21:8080", | ||
| "DNAT tcp -- 0.0.0.0/0 172.16.1.1 tcp dpt:3128 to:11.11.11.11:8080" | ||
| ], | ||
| "post_rules": [ | ||
| "DNAT tcp -- 20.20.0.0/0 172.16.1.1 tcp dpt:8080 to:100.127.20.21:8080", | ||
| "DNAT tcp -- 0.0.0.0/0 172.16.1.1 tcp dpt:3128 to:.*3128" | ||
| ] | ||
| }, | ||
| "4": { | ||
| "ip": "www.google.comx", | ||
| "port": "3128", | ||
| "pre_rules": [ | ||
| "DNAT tcp -- 20.20.0.0/0 172.16.1.1 tcp dpt:8080 to:100.127.20.21:8080", | ||
| "DNAT tcp -- 0.0.0.0/0 172.16.1.1 tcp dpt:3128 to:11.11.11.11:8080" | ||
| ], | ||
| "post_rules": [ | ||
| "DNAT tcp -- 20.20.0.0/0 172.16.1.1 tcp dpt:8080 to:100.127.20.21:8080", | ||
| "DNAT tcp -- 0.0.0.0/0 172.16.1.1 tcp dpt:3128 to:11.11.11.11:8080" | ||
| ], | ||
| "ret": "" | ||
| }, | ||
| "5": { | ||
| "ip": "www.google.comx", | ||
| "port": "3128", | ||
| "conf_file": "no_proxy.conf", | ||
| "pre_rules": [ | ||
| "DNAT tcp -- 20.20.0.0/0 172.16.1.1 tcp dpt:8080 to:100.127.20.21:8080", | ||
| "DNAT tcp -- 0.0.0.0/0 172.16.1.1 tcp dpt:3128 to:11.11.11.11:8080" | ||
| ], | ||
| "post_rules": [ | ||
| "DNAT tcp -- 20.20.0.0/0 172.16.1.1 tcp dpt:8080 to:100.127.20.21:8080", | ||
| "DNAT tcp -- 0.0.0.0/0 172.16.1.1 tcp dpt:3128 to:11.11.11.11:8080" | ||
| ], | ||
| "ret": "" | ||
| } | ||
| } | ||
|
|
||
|
|
||
| current_tc = None | ||
| current_rules = None | ||
|
|
||
| class proc: | ||
| returncode = 0 | ||
| stdout = None | ||
| stderr = None | ||
|
|
||
| def __init__(self, ret, stdout, stderr): | ||
| self.returncode = ret | ||
| self.stdout = bytearray(stdout, 'utf-8') | ||
| self.stderr = bytearray(stderr, 'utf-8') | ||
| print("out={} err={}".format(stdout, stderr)) | ||
|
|
||
|
|
||
| def mock_subproc_run(cmd, shell, capture_output): | ||
| cmd_prefix = "sudo iptables -t nat " | ||
| list_cmd = "{}-n -L OUTPUT ".format(cmd_prefix) | ||
| del_cmd = "{}-D OUTPUT ".format(cmd_prefix) | ||
| ins_cmd = "{}-A OUTPUT -p tcp -d ".format(cmd_prefix) | ||
|
|
||
| assert shell | ||
|
|
||
| print("cmd={}".format(cmd)) | ||
| if cmd.startswith(list_cmd): | ||
| num = int(cmd[len(list_cmd):]) | ||
| out = current_rules[num] if len(current_rules) > num else "" | ||
| return proc(0, out, "") | ||
|
|
||
| if cmd.startswith(del_cmd): | ||
| num = int(cmd[len(del_cmd):]) | ||
| if num >= len(current_rules): | ||
| print("delete num={} is greater than len={}".format(num, len(current_rules))) | ||
| print("current_rules = {}".format(current_rules)) | ||
| assert False | ||
| del current_rules[num] | ||
| return proc(0, "", "") | ||
|
|
||
| if cmd.startswith(ins_cmd): | ||
| l = cmd.split() | ||
| assert len(l) == 16 | ||
| rule = "DNAT tcp -- 0.0.0.0/0 {} tcp dpt:{} to:{}".format(l[9], l[11], l[-1]) | ||
| current_rules.append(rule) | ||
| return proc(0, "", "") | ||
|
|
||
| print("unknown cmd: {}".format(cmd)) | ||
| return None | ||
|
|
||
|
|
||
| def match_rules(pattern_list, str_list): | ||
| if len(pattern_list) != len(str_list): | ||
| print("pattern len {} != given {}".format( | ||
| len(pattern_list), len(str_list))) | ||
| return False | ||
|
|
||
| for i in range(len(pattern_list)): | ||
| if not re.match(pattern_list[i], str_list[i]): | ||
| print("{}: {} != {}".format(i, pattern_list[i], str_list[i])) | ||
| return False | ||
| return True | ||
|
|
||
|
|
||
| class TestIPTableUpdate(object): | ||
|
|
||
| @patch("ctrmgr_iptables.subprocess.run") | ||
| def test_table(self, mock_proc): | ||
| global current_rules, current_tc | ||
|
|
||
| mock_proc.side_effect = mock_subproc_run | ||
| for i, tc in test_data.items(): | ||
| print("----- Test: {} Start ------------------".format(i)) | ||
| current_tc = tc | ||
| current_rules = tc["pre_rules"].copy() | ||
|
|
||
| ctrmgr_iptables.DST_IP = "" | ||
| ctrmgr_iptables.DST_PORT = "" | ||
| ctrmgr_iptables.DST_FILE = os.path.join( | ||
| os.path.dirname(os.path.realpath(__file__)), | ||
| tc.get("conf_file", PROXY_FILE)) | ||
| ret = ctrmgr_iptables.iptable_proxy_rule_upd(tc["ip"], tc["port"]) | ||
| if "ret" in tc: | ||
| assert ret == tc["ret"] | ||
| if not match_rules(tc["post_rules"], current_rules): | ||
| print("current_rules={}".format(current_rules)) | ||
| print("post_rules={}".format(tc["post_rules"])) | ||
| assert False | ||
| print("----- Test: {} End ------------------".format(i)) | ||
|
|
||
|
|
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.