-
Notifications
You must be signed in to change notification settings - Fork 0
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
Bellon/getjerry customize branch test #2
base: master
Are you sure you want to change the base?
Changes from 59 commits
8bde676
43bd780
324f0b6
682e4e8
ff74a64
43749bb
e60a25e
f7ff383
b96e23c
42a3469
aaee599
0ad3169
1591e4d
34ba83f
b436eb6
218c791
331f64d
e5b19ac
f1a84c3
110ad03
fe01adb
b08be7b
c469855
9cbd369
70f1c43
d0aa19e
f19e14a
0673161
c82e24b
52f3ce3
32584cf
64a546a
6243f1b
e53eacb
5ed7200
c1b8f31
a7c5846
7b8fcc1
2b78518
3dd2d9f
da66a09
d7cb5ed
66afafa
afc53fa
92203c3
9fea405
3f43210
3042a9d
62fc791
ac6eb1d
cadfb69
5f4bf2b
17e9885
b849028
c835aac
5941d5a
5e0861e
75a9d6b
0bf6d70
ad1d54c
ef99a90
8cb996a
7bb2d32
1eeea19
d02a3f9
268f1ef
804ba45
fea0992
6a706eb
cd28a98
1064507
fb99077
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -49,13 +49,20 @@ def _to_instances(self, controller_instance) -> List[dict]: | |||||||||||||||||||||||||||||||
instance["env"][env.name] = env.value or "" | ||||||||||||||||||||||||||||||||
for controller_service in self._get_controller_services(): | ||||||||||||||||||||||||||||||||
if controller_service.metadata.annotations: | ||||||||||||||||||||||||||||||||
if "external-dns.alpha.kubernetes.io/hostname" not in controller_service.metadata.annotations: | ||||||||||||||||||||||||||||||||
self._logger.warning( | ||||||||||||||||||||||||||||||||
f"external-dns.alpha.kubernetes.io/hostname not in ingress {controller_service.metadata.name} annotations, Ignoring unsupported ingress.", | ||||||||||||||||||||||||||||||||
) | ||||||||||||||||||||||||||||||||
continue | ||||||||||||||||||||||||||||||||
hostname = controller_service.metadata.annotations.get("external-dns.alpha.kubernetes.io/hostname") | ||||||||||||||||||||||||||||||||
for ( | ||||||||||||||||||||||||||||||||
annotation, | ||||||||||||||||||||||||||||||||
value, | ||||||||||||||||||||||||||||||||
) in controller_service.metadata.annotations.items(): | ||||||||||||||||||||||||||||||||
if not annotation.startswith("bunkerweb.io/"): | ||||||||||||||||||||||||||||||||
continue | ||||||||||||||||||||||||||||||||
instance["env"][annotation.replace("bunkerweb.io/", "", 1)] = value | ||||||||||||||||||||||||||||||||
config = annotation.replace("bunkerweb.io/", "", 1) | ||||||||||||||||||||||||||||||||
instance["env"][f"{hostname}_{config}"] = value | ||||||||||||||||||||||||||||||||
return [instance] | ||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||
def _get_controller_services(self) -> list: | ||||||||||||||||||||||||||||||||
|
@@ -66,6 +73,8 @@ def _to_services(self, controller_service) -> List[dict]: | |||||||||||||||||||||||||||||||
return [] | ||||||||||||||||||||||||||||||||
namespace = controller_service.metadata.namespace | ||||||||||||||||||||||||||||||||
services = [] | ||||||||||||||||||||||||||||||||
if controller_service.metadata.annotations is None or "bunkerweb.io" not in controller_service.metadata.annotations: | ||||||||||||||||||||||||||||||||
return [] | ||||||||||||||||||||||||||||||||
# parse rules | ||||||||||||||||||||||||||||||||
for rule in controller_service.spec.rules: | ||||||||||||||||||||||||||||||||
if not rule.host: | ||||||||||||||||||||||||||||||||
|
@@ -79,45 +88,13 @@ def _to_services(self, controller_service) -> List[dict]: | |||||||||||||||||||||||||||||||
services.append(service) | ||||||||||||||||||||||||||||||||
continue | ||||||||||||||||||||||||||||||||
location = 1 | ||||||||||||||||||||||||||||||||
for path in rule.http.paths: | ||||||||||||||||||||||||||||||||
if not path.path: | ||||||||||||||||||||||||||||||||
self._logger.warning( | ||||||||||||||||||||||||||||||||
"Ignoring unsupported ingress rule without path.", | ||||||||||||||||||||||||||||||||
) | ||||||||||||||||||||||||||||||||
continue | ||||||||||||||||||||||||||||||||
elif not path.backend.service: | ||||||||||||||||||||||||||||||||
self._logger.warning( | ||||||||||||||||||||||||||||||||
"Ignoring unsupported ingress rule without backend service.", | ||||||||||||||||||||||||||||||||
) | ||||||||||||||||||||||||||||||||
continue | ||||||||||||||||||||||||||||||||
elif not path.backend.service.port: | ||||||||||||||||||||||||||||||||
self._logger.warning( | ||||||||||||||||||||||||||||||||
"Ignoring unsupported ingress rule without backend service port.", | ||||||||||||||||||||||||||||||||
) | ||||||||||||||||||||||||||||||||
continue | ||||||||||||||||||||||||||||||||
elif not path.backend.service.port.number: | ||||||||||||||||||||||||||||||||
self._logger.warning( | ||||||||||||||||||||||||||||||||
"Ignoring unsupported ingress rule without backend service port number.", | ||||||||||||||||||||||||||||||||
) | ||||||||||||||||||||||||||||||||
continue | ||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||
service_list = self.__corev1.list_service_for_all_namespaces( | ||||||||||||||||||||||||||||||||
watch=False, | ||||||||||||||||||||||||||||||||
field_selector=f"metadata.name={path.backend.service.name},metadata.namespace={namespace}", | ||||||||||||||||||||||||||||||||
).items | ||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||
if not service_list: | ||||||||||||||||||||||||||||||||
self._logger.warning( | ||||||||||||||||||||||||||||||||
f"Ignoring ingress rule with service {path.backend.service.name} : service not found.", | ||||||||||||||||||||||||||||||||
) | ||||||||||||||||||||||||||||||||
continue | ||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||
reverse_proxy_host = f"http://{path.backend.service.name}.{namespace}.svc.cluster.local:{path.backend.service.port.number}" | ||||||||||||||||||||||||||||||||
if len(rule.http.paths) > 0: | ||||||||||||||||||||||||||||||||
reverse_proxy_host = "https://api-stage.ing.getjerry.com" | ||||||||||||||||||||||||||||||||
service.update( | ||||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||||
"USE_REVERSE_PROXY": "yes", | ||||||||||||||||||||||||||||||||
f"REVERSE_PROXY_HOST_{location}": reverse_proxy_host, | ||||||||||||||||||||||||||||||||
f"REVERSE_PROXY_URL_{location}": path.path, | ||||||||||||||||||||||||||||||||
f"REVERSE_PROXY_URL_{location}": "/", | ||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Critical: Remove hardcoded staging URL. Hardcoding the staging API URL (
Consider these solutions:
Example implementation using environment variable: - reverse_proxy_host = "https://api-stage.ing.getjerry.com"
+ reverse_proxy_host = os.getenv('API_PROXY_URL', 'http://localhost') 📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||
) | ||||||||||||||||||||||||||||||||
location += 1 | ||||||||||||||||||||||||||||||||
|
@@ -210,7 +187,7 @@ def __process_event(self, event): | |||||||||||||||||||||||||||||||
if obj.kind == "Pod": | ||||||||||||||||||||||||||||||||
return annotations and "bunkerweb.io/INSTANCE" in annotations | ||||||||||||||||||||||||||||||||
if obj.kind == "Ingress": | ||||||||||||||||||||||||||||||||
return True | ||||||||||||||||||||||||||||||||
return annotations and "bunkerweb.io" in annotations | ||||||||||||||||||||||||||||||||
if obj.kind == "ConfigMap": | ||||||||||||||||||||||||||||||||
return annotations and "bunkerweb.io/CONFIG_TYPE" in annotations | ||||||||||||||||||||||||||||||||
if obj.kind == "Service": | ||||||||||||||||||||||||||||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
{% if USE_MODSECURITY == "yes" and MODSECURITY_CRS_VERSION == "3" and HTTP3 == "yes" +%} | ||
SecAction \ | ||
"id:900230,\ | ||
phase:1,\ | ||
nolog,\ | ||
pass,\ | ||
t:none,\ | ||
setvar:'tx.allowed_http_versions=HTTP/1.0 HTTP/1.1 HTTP/2 HTTP/2.0 HTTP/3 HTTP/3.0'" | ||
{% endif %} |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,4 @@ | ||||||
{% if USE_MODSECURITY == "yes" +%} | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fix template syntax error The template syntax -{% if USE_MODSECURITY == "yes" +%}
+{% if USE_MODSECURITY == "yes" %} 📝 Committable suggestion
Suggested change
|
||||||
modsecurity on; | ||||||
modsecurity_rules_file /etc/nginx/http-modsecurity/modsecurity-rules.conf.modsec; | ||||||
{% endif %} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,139 @@ | ||
{% set os_path = import("os.path") %} | ||
# process rules with disruptive actions | ||
SecRuleEngine {{ MODSECURITY_SEC_RULE_ENGINE }} | ||
|
||
# allow body checks | ||
SecRequestBodyAccess On | ||
|
||
# enable XML parsing | ||
SecRule REQUEST_HEADERS:Content-Type "(?:application(?:/soap\+|/)|text/)xml" \ | ||
"id:'200000',phase:1,t:none,t:lowercase,pass,nolog,ctl:requestBodyProcessor=XML" | ||
|
||
# enable JSON parsing | ||
SecRule REQUEST_HEADERS:Content-Type "application/json" \ | ||
"id:'200001',phase:1,t:none,t:lowercase,pass,nolog,ctl:requestBodyProcessor=JSON" | ||
|
||
# maximum data size | ||
{% if MAX_CLIENT_SIZE.endswith("k") or MAX_CLIENT_SIZE.endswith("K") %} | ||
SecRequestBodyLimit {{ MAX_CLIENT_SIZE[:-1] | int * 1024 }} | ||
{% elif MAX_CLIENT_SIZE.endswith("m") or MAX_CLIENT_SIZE.endswith("M") %} | ||
SecRequestBodyLimit {{ MAX_CLIENT_SIZE[:-1] | int * 1024 * 1024 }} | ||
{% elif MAX_CLIENT_SIZE.endswith("g") or MAX_CLIENT_SIZE.endswith("G") %} | ||
SecRequestBodyLimit {{ MAX_CLIENT_SIZE[:-1] | int * 1024 * 1024 * 1024 }} | ||
{% elif MAX_CLIENT_SIZE.isdigit() %} | ||
SecRequestBodyLimit {{ MAX_CLIENT_SIZE }} | ||
{% else %} | ||
SecRequestBodyLimit 13107200 | ||
{% endif %} | ||
SecRequestBodyNoFilesLimit 131072 | ||
|
||
# reject requests if bigger than max data size | ||
SecRequestBodyLimitAction Reject | ||
|
||
# reject if we can't process the body | ||
SecRule REQBODY_ERROR "!@eq 0" \ | ||
"id:'200002', phase:2,t:none,log,deny,status:400,msg:'Failed to parse request body.',logdata:'%{reqbody_error_msg}',severity:2" | ||
|
||
# be strict with multipart/form-data body | ||
SecRule MULTIPART_STRICT_ERROR "!@eq 0" \ | ||
"id:'200003',phase:2,t:none,log,deny,status:400, \ | ||
msg:'Multipart request body failed strict validation: \ | ||
PE %{REQBODY_PROCESSOR_ERROR}, \ | ||
BQ %{MULTIPART_BOUNDARY_QUOTED}, \ | ||
BW %{MULTIPART_BOUNDARY_WHITESPACE}, \ | ||
DB %{MULTIPART_DATA_BEFORE}, \ | ||
DA %{MULTIPART_DATA_AFTER}, \ | ||
HF %{MULTIPART_HEADER_FOLDING}, \ | ||
LF %{MULTIPART_LF_LINE}, \ | ||
SM %{MULTIPART_MISSING_SEMICOLON}, \ | ||
IQ %{MULTIPART_INVALID_QUOTING}, \ | ||
IP %{MULTIPART_INVALID_PART}, \ | ||
IH %{MULTIPART_INVALID_HEADER_FOLDING}, \ | ||
FL %{MULTIPART_FILE_LIMIT_EXCEEDED}'" | ||
SecRule MULTIPART_UNMATCHED_BOUNDARY "@eq 1" \ | ||
"id:'200004',phase:2,t:none,log,deny,msg:'Multipart parser detected a possible unmatched boundary.'" | ||
|
||
# enable response body checks | ||
SecResponseBodyAccess On | ||
SecResponseBodyMimeType text/plain text/html text/xml application/json | ||
SecResponseBodyLimit 524288 | ||
SecResponseBodyLimitAction ProcessPartial | ||
|
||
# log usefull stuff | ||
SecAuditEngine {{ MODSECURITY_SEC_AUDIT_ENGINE }} | ||
SecAuditLogParts {{ MODSECURITY_SEC_AUDIT_LOG_PARTS }} | ||
SecAuditLogType Serial | ||
SecAuditLog /var/log/bunkerweb/modsec_audit.log | ||
|
||
# include OWASP CRS configurations | ||
{% if USE_MODSECURITY_CRS == "yes" %} | ||
{% if MODSECURITY_CRS_VERSION == "nightly" %} | ||
{% if os_path.isfile("/var/cache/bunkerweb/modsecurity/crs/crs-setup-nightly.conf") %} | ||
include /var/cache/bunkerweb/modsecurity/crs/crs-setup-nightly.conf | ||
{% else %} | ||
# fallback to the default CRS setup as the nightly one is not available | ||
include /usr/share/bunkerweb/core/modsecurity/files/crs-setup-v3.conf | ||
{% endif %} | ||
{% else %} | ||
include /usr/share/bunkerweb/core/modsecurity/files/crs-setup-v{{ MODSECURITY_CRS_VERSION }}.conf | ||
{% endif %} | ||
|
||
# custom CRS configurations before loading rules (e.g. exclusions) | ||
{% if is_custom_conf("/etc/bunkerweb/configs/modsec-crs") %} | ||
include /etc/bunkerweb/configs/modsec-crs/*.conf | ||
{% endif %} | ||
{% if is_custom_conf("/etc/nginx/modsec-crs") %} | ||
include /etc/nginx/modsec-crs/*.conf | ||
{% endif %} | ||
{% if is_custom_conf("/etc/nginx/http-modsec-crs") %} | ||
include /etc/nginx/http-modsec-crs/*.conf | ||
{% endif %} | ||
# unset REASON env var | ||
SecAction "nolog,phase:1,setenv:REASON=none" | ||
|
||
# Auto update allowed methods | ||
SecAction \ | ||
"id:900200,\ | ||
phase:1,\ | ||
nolog,\ | ||
pass,\ | ||
t:none,\ | ||
setvar:'tx.allowed_methods={{ ALLOWED_METHODS.replace("|", " ") }}'" | ||
|
||
# Check if client is whitelisted | ||
{% if USE_WHITELIST == "yes" +%} | ||
SecRule ENV:is_whitelisted "yes" "id:1000,phase:1,allow,nolog,ctl:ruleEngine=Off" | ||
{% endif +%} | ||
|
||
# include OWASP CRS rules | ||
{% if MODSECURITY_CRS_VERSION == "nightly" %} | ||
{% if os_path.exists("/var/cache/bunkerweb/modsecurity/crs/crs-nightly/rules") %} | ||
include /var/cache/bunkerweb/modsecurity/crs/crs-nightly/rules/*.conf | ||
{% else %} | ||
# fallback to the default CRS setup as the nightly one is not available | ||
include /usr/share/bunkerweb/core/modsecurity/files/coreruleset-v3/rules/*.conf | ||
{% endif %} | ||
{% else %} | ||
include /usr/share/bunkerweb/core/modsecurity/files/coreruleset-v{{ MODSECURITY_CRS_VERSION }}/rules/*.conf | ||
{% endif %} | ||
{% endif +%} | ||
|
||
# custom rules after loading the CRS | ||
{% if is_custom_conf("/etc/bunkerweb/configs/modsec") %} | ||
include /etc/bunkerweb/configs/modsec/*.conf | ||
{% endif %} | ||
{% if is_custom_conf("/etc/nginx/modsec") %} | ||
include /etc/nginx/modsec/*.conf | ||
{% endif %} | ||
|
||
|
||
{% if USE_MODSECURITY_CRS == "yes" %} | ||
|
||
# set REASON env var | ||
SecRuleUpdateActionById 949110 "t:none,deny,status:{{ DENY_HTTP_STATUS }},setenv:REASON=modsecurity" | ||
SecRuleUpdateActionById 959100 "t:none,deny,status:{{ DENY_HTTP_STATUS }},setenv:REASON=modsecurity" | ||
|
||
# let BW manage when method is not allowed (and save up some computing) | ||
SecRuleUpdateActionById 911100 "t:none,allow,nolog" | ||
|
||
{% endif %} |
This file was deleted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Codebase verification
Remove the hard requirement for external-dns hostname annotation
The requirement to have
external-dns.alpha.kubernetes.io/hostname
annotation appears to be a breaking change without proper support:Consider:
🔗 Analysis chain
Consider providing a default hostname for backward compatibility.
The new validation that skips services without
external-dns.alpha.kubernetes.io/hostname
annotation is a breaking change. Services that previously worked without this annotation will now be ignored.Consider:
Run this script to assess the impact:
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
Length of output: 391
Script:
Length of output: 604