diff --git a/.drone.yml b/.drone.yml index f366012..c8ff368 100644 --- a/.drone.yml +++ b/.drone.yml @@ -1,7 +1,7 @@ --- kind: pipeline type: kubernetes -#type: docker +# type: docker name: default steps: diff --git a/defaults/main.yml b/defaults/main.yml index 4636822..173d4a1 100644 --- a/defaults/main.yml +++ b/defaults/main.yml @@ -1,19 +1,29 @@ my_context: kubernetes -traefik_version: "2.3" -traefik_domain: "local" +traefik_version: "3.3.6" +traefik_helm_chart_version: "35.0.1" +traefikcrds_helm_chart_version: "1.6.0" +cluster_domain: "local" traefik_namespace: "traefik" -#ingress_whitelist: -# - 10.96.0.0/12 -# - 10.244.0.0/16 -# - 192.168.0.0/24 -#traefik_node_selector: -# - localhost +traefik_service_type: LoadBalancer +# ingress_whitelist: +# - 10.96.0.0/12 +# - 10.244.0.0/16 +# - 192.168.0.0/24 +# traefik_node_selector: +# - localhost traefik_cpu_limit: 500m traefik_memory_limit: 300Mi -traefik_entrypoints: - - { name: "http", port: 8000, proto: "TCP", hostport: 80 } - - { name: "https", port: 4443, proto: "TCP", hostport: 443, tls: true } - - { name: "traefik", port: 8080, proto: "TCP" } +traefik_entrypoints: [] +# - { name: "http", port: 8000, proto: "TCP", hostport: 80 middlewares: ["{{ traefik_namespace }}-crowdsec-traefik-bouncer@kubernetescrd"] } +# - { name: "https", port: 4443, proto: "TCP", hostport: 443, tls: true middlewares: ["{{ traefik_namespace }}-crowdsec-traefik-bouncer@kubernetescrd"] } +# - { name: "traefik", port: 8080, proto: "TCP" } +# traefik_external_ips: [] +# - 1.2.3.4 basic_auth: false -#traefik_dashboard_certificate: wildcard-cluster \ No newline at end of file +# traefik_dashboard_certificate: wildcard-cluster + +crowdsec_namespace: "crowdsec" +crowdsec_traefik_bouncer_chart_version: "0.1.3" + +traefik_sabblier_version: "1.9.0" diff --git a/files/grafana-dashboard.yml b/files/grafana-dashboard.yml new file mode 100644 index 0000000..22ecb26 --- /dev/null +++ b/files/grafana-dashboard.yml @@ -0,0 +1,1523 @@ +# file from https://github.com/traefik/traefik/blob/master/contrib/grafana/traefik-kubernetes.json +apiVersion: v1 +kind: ConfigMap +metadata: + labels: + grafana_dashboard: "1" + name: traefik-grafana-dashboard +data: + traefik-kubernetes.json: |- + { + "__inputs": [ + { + "name": "DS_PROMETHEUS", + "label": "Prometheus", + "description": "", + "type": "datasource", + "pluginId": "prometheus", + "pluginName": "Prometheus" + } + ], + "__elements": {}, + "__requires": [ + { + "type": "grafana", + "id": "grafana", + "name": "Grafana", + "version": "9.3.1" + }, + { + "type": "panel", + "id": "piechart", + "name": "Pie chart", + "version": "" + }, + { + "type": "datasource", + "id": "prometheus", + "name": "Prometheus", + "version": "1.0.0" + }, + { + "type": "panel", + "id": "stat", + "name": "Stat", + "version": "" + }, + { + "type": "panel", + "id": "timeseries", + "name": "Time series", + "version": "" + } + ], + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "target": { + "limit": 100, + "matchAny": false, + "tags": [], + "type": "dashboard" + }, + "type": "dashboard" + } + ] + }, + "description": "Official dashboard for Traefik on Kubernetes", + "editable": false, + "fiscalYearStartMonth": 0, + "gnetId": 17347, + "graphTooltip": 0, + "id": null, + "links": [], + "liveNow": false, + "panels": [ + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 9, + "panels": [], + "title": "General", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 2, + "w": 5, + "x": 0, + "y": 1 + }, + "id": 13, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "9.3.1", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "count(traefik_config_reloads_total)", + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Traefik Instances", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "reqps" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 7, + "x": 5, + "y": 1 + }, + "id": 7, + "options": { + "legend": { + "calcs": [ + "mean", + "max" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true, + "sortBy": "Max", + "sortDesc": true + }, + "tooltip": { + "mode": "multi", + "sort": "desc" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "sum(rate(traefik_entrypoint_requests_total{entrypoint=~\"$entrypoint\"}[1m])) by (entrypoint)", + "legendFormat": "{{entrypoint}}", + "range": true, + "refId": "A" + } + ], + "title": "Requests per Entrypoint", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "https://medium.com/@tristan_96324/prometheus-apdex-alerting-d17a065e39d0", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 1 + }, + "id": 6, + "options": { + "legend": { + "calcs": [ + "mean", + "max" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true, + "sortBy": "Max", + "sortDesc": true + }, + "tooltip": { + "mode": "multi", + "sort": "desc" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "(sum(rate(traefik_entrypoint_request_duration_seconds_bucket{le=\"0.3\",code=\"200\",entrypoint=~\"$entrypoint\"}[5m])) by (method) + \n sum(rate(traefik_entrypoint_request_duration_seconds_bucket{le=\"1.2\",code=\"200\",entrypoint=~\"$entrypoint\"}[5m])) by (method)) / 2 / \n sum(rate(traefik_entrypoint_request_duration_seconds_count{code=\"200\",entrypoint=~\"$entrypoint\"}[5m])) by (method)\n", + "legendFormat": "{{method}}", + "range": true, + "refId": "A" + } + ], + "title": "Apdex score", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "Mean Distribution", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + } + }, + "mappings": [], + "unit": "reqps" + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 5, + "x": 0, + "y": 3 + }, + "id": 14, + "options": { + "legend": { + "displayMode": "list", + "placement": "right", + "showLegend": true, + "values": [ + "percent" + ] + }, + "pieType": "pie", + "reduceOptions": { + "calcs": [ + "mean" + ], + "fields": "", + "values": false + }, + "tooltip": { + "mode": "multi", + "sort": "asc" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "sum(rate(traefik_service_requests_total{service=~\"$service.*\",protocol=\"http\"}[1m])) by (method, code)", + "legendFormat": "{{method}}[{{code}}]", + "range": true, + "refId": "A" + } + ], + "title": "Http Code ", + "type": "piechart" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "s" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 9 + }, + "id": 23, + "options": { + "legend": { + "calcs": [ + "mean", + "max" + ], + "displayMode": "table", + "placement": "right", + "showLegend": true, + "sortBy": "Mean", + "sortDesc": true + }, + "tooltip": { + "mode": "multi", + "sort": "desc" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "topk(15,\n label_replace(\n traefik_service_request_duration_seconds_sum{service=~\"$service.*\",protocol=\"http\"} / \n traefik_service_request_duration_seconds_count{service=~\"$service.*\",protocol=\"http\"},\n \"service\", \"$1\", \"service\", \"([^@]+)@.*\")\n)\n\n", + "legendFormat": "{{method}}[{{code}}] on {{service}}", + "range": true, + "refId": "A" + } + ], + "title": "Top slow services", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "reqps" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 9 + }, + "id": 5, + "options": { + "legend": { + "calcs": [ + "mean", + "max" + ], + "displayMode": "table", + "placement": "right", + "showLegend": true, + "sortBy": "Mean", + "sortDesc": true + }, + "tooltip": { + "mode": "multi", + "sort": "desc" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "topk(15,\n label_replace(\n sum by (service,code) \n (rate(traefik_service_requests_total{service=~\"$service.*\",protocol=\"http\"}[5m])) > 0,\n \"service\", \"$1\", \"service\", \"([^@]+)@.*\")\n)", + "legendFormat": "[{{code}}] on {{service}}", + "range": true, + "refId": "A" + } + ], + "title": "Most requested services", + "type": "timeseries" + }, + { + "collapsed": true, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 17 + }, + "id": 11, + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "percentunit" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 18 + }, + "id": 3, + "options": { + "legend": { + "calcs": [ + "mean", + "max" + ], + "displayMode": "table", + "placement": "right", + "showLegend": true, + "sortBy": "Max", + "sortDesc": true + }, + "tooltip": { + "mode": "multi", + "sort": "desc" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "label_replace(\n 1 - (sum by (service)\n (rate(traefik_service_request_duration_seconds_bucket{le=\"1.2\",service=~\"$service.*\"}[5m])) / sum by (service) \n (rate(traefik_service_request_duration_seconds_count{service=~\"$service.*\"}[5m]))\n ) > 0,\n \"service\", \"$1\", \"service\", \"([^@]+)@.*\"\n)", + "legendFormat": "{{service}}", + "range": true, + "refId": "A" + } + ], + "title": "Services failing SLO of 1200ms", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "percentunit" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 18 + }, + "id": 4, + "options": { + "legend": { + "calcs": [ + "mean", + "max" + ], + "displayMode": "table", + "placement": "right", + "showLegend": true, + "sortBy": "Max", + "sortDesc": true + }, + "tooltip": { + "mode": "multi", + "sort": "desc" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "label_replace(\n 1 - (sum by (service)\n (rate(traefik_service_request_duration_seconds_bucket{le=\"0.3\",service=~\"$service.*\"}[5m])) / sum by (service) \n (rate(traefik_service_request_duration_seconds_count{service=~\"$service.*\"}[5m]))\n ) > 0,\n \"service\", \"$1\", \"service\", \"([^@]+)@.*\"\n)", + "legendFormat": "{{service}}", + "range": true, + "refId": "A" + } + ], + "title": "Services failing SLO of 300ms", + "type": "timeseries" + } + ], + "title": "SLO", + "type": "row" + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 18 + }, + "id": 16, + "panels": [], + "title": "HTTP Details", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "reqps" + }, + "overrides": [] + }, + "gridPos": { + "h": 12, + "w": 8, + "x": 0, + "y": 19 + }, + "id": 17, + "options": { + "legend": { + "calcs": [ + "mean", + "max" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true, + "sortBy": "Mean", + "sortDesc": true + }, + "tooltip": { + "mode": "multi", + "sort": "desc" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "topk(15,\n label_replace(\n sum by (service,method,code) \n (rate(traefik_service_requests_total{service=~\"$service.*\",code=~\"2..\",protocol=\"http\"}[5m])) > 0,\n \"service\", \"$1\", \"service\", \"([^@]+)@.*\")\n)", + "legendFormat": "{{method}}[{{code}}] on {{service}}", + "range": true, + "refId": "A" + } + ], + "title": "2xx over 5 min", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisGridShow": true, + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "reqps" + }, + "overrides": [] + }, + "gridPos": { + "h": 12, + "w": 8, + "x": 8, + "y": 19 + }, + "id": 18, + "options": { + "legend": { + "calcs": [ + "mean", + "max" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true, + "sortBy": "Mean", + "sortDesc": true + }, + "tooltip": { + "mode": "multi", + "sort": "desc" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "topk(15,\n label_replace(\n sum by (service,method,code) \n (rate(traefik_service_requests_total{service=~\"$service.*\",code=~\"5..\",protocol=\"http\"}[5m])) > 0,\n \"service\", \"$1\", \"service\", \"([^@]+)@.*\")\n)", + "legendFormat": "{{method}}[{{code}}] on {{service}}", + "range": true, + "refId": "A" + } + ], + "title": "5xx over 5 min", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisGridShow": true, + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "reqps" + }, + "overrides": [] + }, + "gridPos": { + "h": 12, + "w": 8, + "x": 16, + "y": 19 + }, + "id": 19, + "options": { + "legend": { + "calcs": [ + "mean", + "max" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true, + "sortBy": "Mean", + "sortDesc": true + }, + "tooltip": { + "mode": "multi", + "sort": "desc" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "topk(15,\n label_replace(\n sum by (service,method,code) \n (rate(traefik_service_requests_total{service=~\"$service.*\",code!~\"2..|5..\",protocol=\"http\"}[5m])) > 0,\n \"service\", \"$1\", \"service\", \"([^@]+)@.*\")\n)", + "legendFormat": "{{method}}[{{code}}] on {{service}}", + "range": true, + "refId": "A" + } + ], + "title": "Other codes over 5 min", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisGridShow": true, + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "binBps" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 31 + }, + "id": 20, + "options": { + "legend": { + "calcs": [ + "mean", + "max" + ], + "displayMode": "table", + "placement": "right", + "showLegend": true, + "sortBy": "Mean", + "sortDesc": true + }, + "tooltip": { + "mode": "multi", + "sort": "desc" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "topk(15,\n label_replace(\n sum by (service,method) \n (rate(traefik_service_requests_bytes_total{service=~\"$service.*\",protocol=\"http\"}[1m])) > 0,\n \"service\", \"$1\", \"service\", \"([^@]+)@.*\")\n)", + "legendFormat": "{{method}} on {{service}}", + "range": true, + "refId": "A" + } + ], + "title": "Requests Size", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisGridShow": true, + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "binBps" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 31 + }, + "id": 24, + "options": { + "legend": { + "calcs": [ + "mean", + "max" + ], + "displayMode": "table", + "placement": "right", + "showLegend": true, + "sortBy": "Mean", + "sortDesc": true + }, + "tooltip": { + "mode": "multi", + "sort": "desc" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "topk(15,\n label_replace(\n sum by (service,method) \n (rate(traefik_service_responses_bytes_total{service=~\"$service.*\",protocol=\"http\"}[1m])) > 0,\n \"service\", \"$1\", \"service\", \"([^@]+)@.*\")\n)", + "legendFormat": "{{method}} on {{service}}", + "range": true, + "refId": "A" + } + ], + "title": "Responses Size", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 39 + }, + "id": 21, + "options": { + "legend": { + "calcs": [ + "mean", + "max" + ], + "displayMode": "table", + "placement": "right", + "showLegend": true, + "sortBy": "Max", + "sortDesc": true + }, + "tooltip": { + "mode": "multi", + "sort": "desc" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "sum(traefik_open_connections{entrypoint=~\"$entrypoint\"}) by (entrypoint)\n", + "legendFormat": "{{entrypoint}}", + "range": true, + "refId": "A" + } + ], + "title": "Connections per Entrypoint", + "type": "timeseries" + } + ], + "refresh": false, + "schemaVersion": 37, + "style": "dark", + "tags": [], + "templating": { + "list": [ + { + "current": { + "selected": false, + "text": "Prometheus", + "value": "Prometheus" + }, + "hide": 0, + "includeAll": false, + "multi": false, + "name": "DS_PROMETHEUS", + "label": "datasource", + "options": [], + "query": "prometheus", + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "type": "datasource" + }, + { + "current": {}, + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "definition": "label_values(traefik_open_connections, entrypoint)", + "hide": 0, + "includeAll": true, + "multi": false, + "name": "entrypoint", + "options": [], + "query": { + "query": "label_values(traefik_open_connections, entrypoint)", + "refId": "StandardVariableQuery" + }, + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "type": "query" + }, + { + "current": {}, + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "definition": "label_values(traefik_service_requests_total, service)", + "hide": 0, + "includeAll": true, + "multi": false, + "name": "service", + "options": [], + "query": { + "query": "label_values(traefik_service_requests_total, service)", + "refId": "StandardVariableQuery" + }, + "refresh": 2, + "regex": "/([^@]+)@.*/", + "skipUrlSync": false, + "sort": 1, + "type": "query" + } + ] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "Traefik Official Kubernetes Dashboard", + "uid": "n5bu_kv4k", + "version": 7, + "weekStart": "" + } diff --git a/meta/main.yml b/meta/main.yml index c8bee80..22bf70c 100644 --- a/meta/main.yml +++ b/meta/main.yml @@ -6,8 +6,8 @@ galaxy_info: galaxy_tags: [] license: GPL2 collections: - - community.kubernetes + - kubernetes.core platforms: - - name: kubernetes - version: - - all + - name: kubernetes + version: + - all diff --git a/tasks/main.yml b/tasks/main.yml index 9844fba..28b44c9 100644 --- a/tasks/main.yml +++ b/tasks/main.yml @@ -1,134 +1,257 @@ - name: traefik setup block: - - name: namespace - k8s: - state: present - context: "{{ my_context }}" - merge_type: merge - definition: - api_version: v1 - kind: Namespace - metadata: - name: traefik - labels: - namespace: '{{ traefik_namespace }}' +# - name: Deploy Traefik CRDs +# kubernetes.core.k8s: +# state: present +# context: "{{ my_context }}" +# apply: true +# definition: "{{ lookup('url', item , split_lines=False) | from_yaml_all }}" +# with_items: +## - "https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.2.0/standard-install.yaml" +# - "https://raw.githubusercontent.com/traefik/traefik/v3.2/docs/content/reference/dynamic-configuration/kubernetes-crd-definition-v1.yml" +## - "https://raw.githubusercontent.com/traefik/traefik/v3.2/docs/content/reference/dynamic-configuration/kubernetes-crd-rbac.yml" +## ansible.builtin.command: > +## kubectl --context "{{ my_context }}" apply --server-side --force-conflicts -k +## https://github.com/traefik/traefik-helm-chart/tree/v{{ traefik_helm_chart_version }}/traefik/crds/ + - name: namespace + kubernetes.core.k8s: + state: present + context: "{{ my_context }}" + merge_type: merge + definition: + api_version: v1 + kind: Namespace + metadata: + name: '{{ traefik_namespace }}' + labels: + namespace: '{{ traefik_namespace }}' - - name: Create a Secret object for basic authentification - k8s: - state: present - context: "{{ my_context }}" - definition: - apiVersion: v1 - kind: Secret - metadata: - name: basic-auth - namespace: '{{ traefik_namespace }}' - type: Opaque - data: - basic_auth: "{{ basic_auth_data | b64encode }}" - when: - - basic_auth|bool - - - name: Add host label for traefik deployment - k8s: - state: present - context: "{{ my_context }}" - definition: - apiVersion: v1 - kind: Node - metadata: - name: "{{ item }}" - labels: - entrypoint: traefik - with_items: - - '{{ traefik_node_selector }}' - when: - - traefik_node_selector is defined - -# - name: Get Deployment information object -# k8s_info: -# context: "{{ my_context }}" -# api_version: v1 -# kind: DaemonSet -# name: traefik -# namespace: '{{ traefik_namespace }}' -# field_selectors: -# - spec.template.spec.containers.image -# register: traefik_actual_resources -# -# - name: Retreive actual traefik version -# shell: echo "{{ traefik_actual_resources.resources }}" | sed "s/.*traefik:\([0-9]\.[0-9]*\).*/\1/" | uniq -# register: traefik_actual_version -# -# - name: Remove old traefik version {{ traefik_actual_version.stdout }} -# k8s: -# state: "absent" -# context: "{{ my_context }}" -# resource_definition: "{{ lookup('template', item) | from_yaml }}" -# with_items: -# - "{{ lookup('vars', 'traefik_' + traefik_actual_version.stdout | regex_replace('[.]','_') + '_list') | reverse | list }}" -## - hostvars[inventory_hostname]['traefik_' + traefik_actual_version.stdout + '_list'] | reverse -# when: -# - not traefik_actual_version.stdout == "[]" -# - not traefik_version == traefik_actual_version.stdout -# - traefik_actual_version.stdout is version(traefik_version, '>') - - - name: Defined traefik repository - community.kubernetes.helm_repository: - name: traefik - repo_url: "https://helm.traefik.io/traefik" - tags: traefik - - name: Deploy latest version of Traefik - community.kubernetes.helm: - name: traefik - chart_ref: traefik/traefik - release_namespace: traefik - values: - image: - tag: "{{ traefik_version_2_3 }}" - additionalArguments: - - --configFile=/etc/traefik/traefik.yaml - podSecurityPolicy: - enabled: true - service: - enabled: false - ingressRoute: - dashboard: - enabled: false - ingressClass: - enabled: true - isDefaultClass: true - ports: - web: - redirectTo: websecure - hostPort: 80 - websecure: - hostPort: 443 - volumes: - - mountPath: /etc/traefik - name: traefik-conf - type: configMap - - mountPath: /etc/traefik/file - name: traefik-files - type: configMap - - mountPath: /etc/traefik/basic-auth + - name: Create a Secret object for basic authentification + kubernetes.core.k8s: + state: present + context: "{{ my_context }}" + namespace: '{{ traefik_namespace }}' + definition: + apiVersion: v1 + kind: Secret + metadata: name: basic-auth - type: secret + type: Opaque + data: + basic_auth: "{{ basic_auth_data | b64encode }}" + when: + - basic_auth|bool - - name: Install traefik configuration - k8s: - state: "present" - context: "{{ my_context }}" - namespace: '{{ traefik_namespace }}' -# merge_type: merge - apply: yes - resource_definition: "{{ lookup('template', item) | from_yaml }}" - with_items: -# - "{{ lookup('vars', 'traefik_' + traefik_version | regex_replace('[.]','_') + '_list') }}" - - traefik-cm.yml.j2 - - traefik-files.yml.j2 -# - traefik-sa.yml.j2 - - traefik-ingressroute.yml.j2 - - traefik-svc.yml.j2 + - name: Add host label for traefik deployment + kubernetes.core.k8s: + state: present + context: "{{ my_context }}" + definition: + apiVersion: v1 + kind: Node + metadata: + name: "{{ item }}" + labels: + entrypoint: traefik + with_items: + - '{{ traefik_node_selector }}' + when: + - traefik_node_selector is defined + + - name: Deploy latest version of CrowdSec Traefik bouncer + kubernetes.core.helm: + context: "{{ my_context }}" + name: crowdsec-traefik-bouncer + release_namespace: "{{ traefik_namespace }}" + create_namespace: true + chart_ref: crowdsec/crowdsec-traefik-bouncer + chart_version: "{{ crowdsec_traefik_bouncer_chart_version }}" + values: + image: + tag: "0.5.0" + bouncer: + crowdsec_bouncer_api_key: "{{ traefik_crowdsec_bouncer_apikey }}" + crowdsec_agent_host: "crowdsec-service.{{ crowdsec_namespace }}.svc.cluster.local:8080" + crowdsec_bouncer_gin_mode: "release" + replicaCount: 1 + podSecurityContext: + fsGroup: 2000 + securityContext: + capabilities: + drop: + - ALL + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 1000 + when: + - traefik_crowdsec_bouncer_apikey is defined + +# - name: Get Deployment information object +# kubernetes.core.k8s_info: +# context: "{{ my_context }}" +# api_version: v1 +# kind: DaemonSet +# name: traefik +# namespace: '{{ traefik_namespace }}' +# field_selectors: +# - spec.template.spec.containers.image +# register: traefik_actual_resources +# +# - name: Retreive actual traefik version +# ansible.builtin.shell: echo "{{ traefik_actual_resources.resources }}" | sed "s/.*traefik:\([0-9]\.[0-9]*\).*/\1/" | uniq +# register: traefik_actual_version +# +# - name: Remove old traefik version {{ traefik_actual_version.stdout }} +# kubernetes.core.k8s: +# state: "absent" +# context: "{{ my_context }}" +# resource_definition: "{{ lookup('template', item) | from_yaml }}" +# with_items: +# - "{{ lookup('vars', 'traefik_' + traefik_actual_version.stdout | regex_replace('[.]','_') + '_list') | reverse | list }}" +## - hostvars[inventory_hostname]['traefik_' + traefik_actual_version.stdout + '_list'] | reverse +# when: +# - not traefik_actual_version.stdout == "[]" +# - not traefik_version == traefik_actual_version.stdout +# - traefik_actual_version.stdout is version(traefik_version, '>') + +# https://github.com/traefik/traefik-helm-chart + - name: Defined traefik repository + kubernetes.core.helm_repository: + name: traefik + repo_url: "https://helm.traefik.io/traefik" + - name: Deploy Traefik CRDs + kubernetes.core.helm: + context: "{{ my_context }}" + name: traefik-crds + chart_ref: traefik/traefik-crds + chart_version: "{{ traefikcrds_helm_chart_version }}" + release_namespace: "{{ traefik_namespace }}" + create_namespace: true +# - name: show templating results +# ansible.builtin.debug: +# msg: "{{ lookup('ansible.builtin.template', 'traefik-helm-value.yaml.j2') }}" + - name: Deploy latest version of Traefik + kubernetes.core.helm: + context: "{{ my_context }}" + name: traefik + chart_ref: traefik/traefik + chart_version: "{{ traefik_helm_chart_version }}" + release_namespace: "{{ traefik_namespace }}" + create_namespace: true + skip_crds: true + values: "{{ lookup('template', 'traefik-helm-value.yaml.j2') | from_yaml }}" + + - name: Install traefik configuration + kubernetes.core.k8s: + state: "present" + context: "{{ my_context }}" + namespace: '{{ traefik_namespace }}' +# merge_type: merge + apply: true + resource_definition: "{{ lookup('template', item) | from_yaml_all }}" + with_items: + - default-network-dns-policy.yaml.j2 +# - "{{ lookup('vars', 'traefik_' + traefik_version | regex_replace('[.]','_') + '_list') }}" + - traefik-certificate.yml.j2 + - traefik-cm.yml.j2 + - traefik-files.yml.j2 +# - traefik-sa.yml.j2 + - traefik-ingressroute.yml.j2 +# - traefik-svc.yml.j2 +# - traefik-defaultbackend.yml.j2 + + - name: Install traefik plugin's + kubernetes.core.k8s: + state: "present" + context: "{{ my_context }}" + namespace: '{{ traefik_namespace }}' +# merge_type: merge + apply: true + resource_definition: "{{ lookup('template', item) | from_yaml_all }}" + with_items: + - traefik-ondemand-plugin.yml.j2 + when: + - traefik_ondemand is defined + + - name: ReInstall traefik-hub certificate if already know + kubernetes.core.k8s: + state: "present" + context: "{{ my_context }}" + namespace: '{{ traefik_namespace }}' +# merge_type: merge +# apply: true + resource_definition: "{{ lookup('template', item) | from_yaml_all }}" + with_items: + - traefik-hub-certificate.yml.j2 + when: + - traefik_hub_tlscrt is defined + - traefik_hub_tlskey is defined + - name: Defined traefik-hub repository + kubernetes.core.helm_repository: + name: traefik-hub + repo_url: "https://helm.traefik.io/hub" + when: + - traefik_hub_token is defined + - name: Deploy latest version of Traefik-hub + kubernetes.core.helm: + context: "{{ my_context }}" + name: hub-agent + chart_ref: traefik-hub/hub-agent + release_namespace: "{{ traefik_namespace }}" + values: + token: "{{ traefik_hub_token }}" + when: + - traefik_hub_token is defined + +# echo 'apiVersion: v1 +# kind: Service +# metadata: +# annotations: +# # external-dns.alpha.kubernetes.io/endpoints-type: HostIP +# external-dns.alpha.kubernetes.io/hostname: traefik.ibm.reslinger.net +# external-dns.alpha.kubernetes.io/endpoints-type: NodeExternalIP +# # external-dns.alpha.kubernetes.io/target: "1.2.3.4" +# name: traefik-dns +# namespace: traefik +# spec: +# clusterIP: None +# ports: +# - name: web +# port: 80 +# protocol: TCP +# targetPort: web +# - name: websecure +# port: 443 +# protocol: TCP +# targetPort: websecure +# selector: +# app.kubernetes.io/instance: traefik-traefik +# app.kubernetes.io/name: traefik' | kubectl --context kubeibm -n traefik apply -f - + +# - name: Deploy latest version of Switchboard +# kubernetes.core.helm: +# context: "{{ my_context }}" +# name: switchboard +# chart_ref: oci://ghcr.io/borchero/charts/switchboard +# release_namespace: "{{ traefik_namespace }}" +# # values: "{{ lookup('template', 'traefik-helm-value.yaml.j2') | from_yaml }}" +# values: +# integrations: +# # certManager: +# # enabled: true +# # issuer: "letsencrypt-prod" +# externalDNS: +# enabled: true +# targetIPs: [10.144.217.172] + + - name: Install traefik grafana dashboard + kubernetes.core.k8s: + state: "present" + context: "{{ my_context }}" + namespace: 'traefik' + apply: yes + resource_definition: "{{ lookup('file', item) | from_yaml_all }}" + with_items: + - grafana-dashboard.yml tags: traefik diff --git a/templates/default-network-dns-policy.yaml.j2 b/templates/default-network-dns-policy.yaml.j2 new file mode 100644 index 0000000..185500e --- /dev/null +++ b/templates/default-network-dns-policy.yaml.j2 @@ -0,0 +1,46 @@ +kind: NetworkPolicy +apiVersion: networking.k8s.io/v1 +metadata: + name: intra-namespace +spec: + podSelector: {} + ingress: + - from: + - namespaceSelector: + matchLabels: + name: {{ traefik_namespace }} + +#--- +#apiVersion: networking.k8s.io/v1 +#kind: NetworkPolicy +#metadata: +# name: allow-dns-access +#spec: +# podSelector: +# matchLabels: {} +# policyTypes: +# - Egress +# egress: +# - to: +# - namespaceSelector: +# matchLabels: +# kubernetes.io/metadata.name: kube-system +# podSelector: +# matchLabels: +# k8s-app: kube-dns +# ports: +# - protocol: UDP +# port: 53 +--- +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: allow-all-traefik-v121-ingress +spec: + podSelector: + matchLabels: + app.kubernetes.io/name: traefik + ingress: + - {} + policyTypes: + - Ingress diff --git a/templates/traefik-certificate.yml.j2 b/templates/traefik-certificate.yml.j2 new file mode 100644 index 0000000..799ef52 --- /dev/null +++ b/templates/traefik-certificate.yml.j2 @@ -0,0 +1,12 @@ +--- +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + name: traefik.{{ cluster_domain }} +spec: + dnsNames: + - traefik.{{ cluster_domain }} + issuerRef: + name: letsencrypt-prod + kind: ClusterIssuer + secretName: traefik.{{ cluster_domain }} diff --git a/templates/traefik-cm.yml.j2 b/templates/traefik-cm.yml.j2 index a29ddaa..c4b25ad 100644 --- a/templates/traefik-cm.yml.j2 +++ b/templates/traefik-cm.yml.j2 @@ -15,49 +15,111 @@ data: web: address: ":8000/tcp" http: + # middlewares: + # - auth@file + # - secure_headers@file + # - crowdsec-bouncer@file + # - {{ traefik_namespace }}-crowdsec-traefik-bouncer@kubernetescrd redirections: entryPoint: to: websecure scheme: https + permanent: true websecure: - address: ":8443/tcp" + address: ":8443" http: tls: options: default + # middlewares: + # - auth@file + # - secure_headers@file + # - crowdsec-bouncer@file + http3: + advertisedPort: 443 traefik: - address: ":9000/tcp" + address: ":8080/tcp" + metrics: + address: ":9100/tcp" +{% if traefik_hub_token is defined %} + traefikhub-api: + address: ":9900" + traefikhub-tunl: + address: ":9901/tcp" +{% endif%} {% for traefik_entrypoint in traefik_entrypoints %} {{ traefik_entrypoint.name }}: - address: :{{ traefik_entrypoint.port }} -{% if traefik_entrypoint.tls is defined and traefik_entrypoint.tls|bool %} +{% if traefik_entrypoint.proto is defined %} + address: ":{{ traefik_entrypoint.port }}/{{ traefik_entrypoint.proto | lower }}" +{% else %} + address: ":{{ traefik_entrypoint.port }}" +{% endif %} +{% if traefik_entrypoint.tls is defined or traefik_entrypoint.middlewares is defined %} http: +{% if traefik_entrypoint.middlewares is defined %} + middlewares: +{% for middleware in traefik_entrypoint.middlewares %} + - {{ middleware }} +{% endfor %} +{% endif %} +{% if traefik_entrypoint.tls is defined and traefik_entrypoint.tls|bool %} tls: {} {% endif %} +{% endif %} {% endfor %} providers: kubernetesCRD: # ingressClass: "traefik" throttleDuration: 2s +{% if traefik_ondemand is defined %} + allowEmptyServices: true +{% endif%} kubernetesIngress: ingressClass: "traefik" +{% if traefik_hub_token is defined %} + allowExternalNameServices: true +{% endif%} +{% if traefik_ondemand is defined %} + allowEmptyServices: true +{% endif%} + kubernetesGateway: {} file: directory: /etc/traefik/file/ watch: true metrics: prometheus: - entryPoint: traefik + entryPoint: metrics + addRoutersLabels: true ping: entryPoint: traefik api: dashboard: true +{% if traefik_hub_token is defined %} + hub: {} +{% endif %} log: - level: WARN - format: json - accessLog: - format: json - fields: - names: - BackendAddr: keep - BackendName: keep - BackendURL: keep - FrontendName: keep + level: ERROR + # format: json + accessLog: {} + #accessLog: + # filePath: "/var/log/traefik/access.log" + # bufferingSize: 50 + ## format: json + ## fields: + ## names: + ## BackendAddr: keep + ## BackendName: keep + ## BackendURL: keep + ## FrontendName: keep +{% if traefik_hub_token is defined or traefik_ondemand is defined %} + experimental: + # kubernetesGateway: true +{% if traefik_hub_token is defined %} + hub: true +{% endif %} +{% if traefik_ondemand is defined %} + plugins: + sablier: + moduleName: github.com/sablierapp/sablier + version: v{{ traefik_sabblier_version }} +{% endif %} +{% endif %} diff --git a/templates/traefik-defaultbackend.yml.j2 b/templates/traefik-defaultbackend.yml.j2 new file mode 100644 index 0000000..40ad31d --- /dev/null +++ b/templates/traefik-defaultbackend.yml.j2 @@ -0,0 +1,16 @@ +--- +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: cheese + annotations: + traefik.ingress.kubernetes.io/router.entrypoints: web,websecure + traefik.ingress.kubernetes.io/router.priority: "1" + traefik.ingress.kubernetes.io/router.middlewares: security_headers@file,compress@file + +spec: + defaultBackend: + service: + name: stilton + port: + number: 80 diff --git a/templates/traefik-files.yml.j2 b/templates/traefik-files.yml.j2 index 4a0c27e..c046713 100644 --- a/templates/traefik-files.yml.j2 +++ b/templates/traefik-files.yml.j2 @@ -7,6 +7,21 @@ data: traefik-middlewares.yaml: | http: middlewares: + min_security: + chain: + middlewares: + - security_headers +{% if ingress_whitelist is defined %} + - traefik-ipwhitelist +{% endif %} + - rate-limit + - compress +{% if false %} +{% if basic_auth|bool %} + - basic-auth +{% endif %} + - authelia +{% endif %} compress: compress: excludedContentTypes: ["text/event-stream"] @@ -17,7 +32,7 @@ data: security_headers: headers: accessControlAllowMethods: ["GET", "OPTIONS", "PUT"] - accessControlAllowOrigin: "origin-list-or-null" + # accessControlAllowOrigin: "origin-list-or-null" accessControlMaxAge: 100 addVaryHeader: true browserXssFilter: true @@ -28,13 +43,15 @@ data: stsPreload: true customFrameOptionsValue: "SAMEORIGIN" referrerPolicy: "same-origin" - featurePolicy: "vibrate 'self'" + # permissionsPolicy: "vibrate 'self'" + permissionsPolicy: "camera 'none'; microphone 'none'; geolocation 'none'; payment 'none';" stsSeconds: 315360000 - sslRedirect: true - contentSecurityPolicy: "default-src 'self' 'unsafe-inline'" + # contentSecurityPolicy: "default-src 'self' 'unsafe-inline'" # customResponseHeaders: # X-Robots-Tag: "none,noarchive,nosnippet,notranslate,noimageindex," # server: "" + hostsProxyHeaders: + - "X-Forwarded-Host" {% if ingress_whitelist is defined %} traefik-ipwhitelist: ipWhiteList: @@ -43,6 +60,19 @@ data: - {{ acl_whitelist }} {% endfor %} {% endif %} +{% if traefik_ondemand is defined %} + ondemand: + plugin: + sablier: + #group: default + dynamic: + displayName: Application is starting + refreshFrequency: 5s + showDetails: "true" + theme: hacker-terminal + sablierUrl: http://sablier:10000 + sessionDuration: 1m +{% endif %} {% if basic_auth|bool %} basic-auth: basicAuth: @@ -55,7 +85,30 @@ data: forwardAuth: address: "http://authelia:9091/api/verify?rd=https://login.example.com/" trustForwardHeader: true - authReponseHeaders: ["Remote-User", "Remote-Groups", "Remote-Name", "Remote-Email"] + authResponseHeaders: + - "Remote-User" + - "Remote-Groups" + - "Remote-Name" + - "Remote-Email" + authelia-basic: + forwardAuth: + address: "http://authelia:9091/api/verify?auth=basic" + trustForwardHeader: true + authResponseHeaders: + - "Remote-User" + - "Remote-Groups" + - "Remote-Name" + - "Remote-Email" + crowdsec-bouncer: + forwardAuth: + address: "http://crowdsec-traefik-bouncer-service/api/v1/forwardAuth" + trustForwardHeader: true + + traefik-servers-transport.yaml: | + http: + serversTransports: + skip-verify-https-backend: + insecureSkipVerify: true traefik-tls-defaults-options.yaml: | tls: @@ -70,3 +123,33 @@ data: - TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 - TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 - TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305 + alpnProtocols: + - h2 + - http/1.1 +{% if false %} + stores: + default: + defaultCertificate: + certFile: path/to/wildcardcert.crt + keyFile: path/to/wildcardcert.key + + certificates: + - certFile: /path/to/domain.cert + keyFile: /path/to/domain.key + - certFile: /path/to/other-domain.cert + keyFile: /path/to/other-domain.key +{% endif %} + +# dashboard.yaml: | +# http: +# routers: +# traefik: +# rule: "Host(`traefik.{{ domain | lower }}`)" +# entryPoints: +# - "websecure" +# middlewares: +# - "min_security@file" +#{% if basic_auth|bool %} +# - "basic-auth@file" +#{% endif %} +# service: "api@internal" diff --git a/templates/traefik-helm-value.yaml.j2 b/templates/traefik-helm-value.yaml.j2 new file mode 100644 index 0000000..718ad26 --- /dev/null +++ b/templates/traefik-helm-value.yaml.j2 @@ -0,0 +1,184 @@ +# https://github.com/traefik/traefik-helm-chart/blob/master/traefik/values.yaml +{% if traefik_version is defined %} +image: + tag: "{{ traefik_version }}" +{% endif %} +#fullnameOverride: "{{ my_context }}" +additionalArguments: + - --configFile=/etc/traefik/traefik.yaml +#podSecurityPolicy: +# enabled: true +service: + type: {{ traefik_service_type }} +{% if traefik_external_ips is defined %} + externalIPs: +{% for external_ip in traefik_external_ips %} + - {{ external_ip }} +{% endfor %} +{% endif %} +{% if traefik_service_type == "LoadBalancer" %} + annotations: + external-dns.alpha.kubernetes.io/hostname: traefik.{{ cluster_domain }} +{% endif %} +ingressRoute: + dashboard: + enabled: false +podDisruptionBudget: + enabled: true + minAvailable: 1 +ingressClass: + enabled: true + isDefaultClass: true +{% if false %} +autoscaling: + enabled: true + minReplicas: 1 + maxReplicas: 10 + metrics: + - type: Resource + resource: + name: cpu + targetAverageUtilization: 60 + - type: Resource + resource: + name: memory + targetAverageUtilization: 60 +{% endif %} +{% if traefik_service_type == "NodePort" or (traefik_hostport is defined and traefik_hostport == true) or traefik_hub_token is defined %} +ports: +{% if traefik_service_type == "NodePort" or (traefik_hostport is defined and traefik_hostport == true) %} + web: +# redirectTo: websecure +{% if traefik_hostport is defined and traefik_hostport == true %} + hostPort: 80 +{% endif %} +{% if traefik_service_type == "NodePort" %} + nodePort: 80 +{% endif %} + websecure: +{% if traefik_hostport is defined and traefik_hostport == true %} + hostPort: 443 +{% endif %} +{% if traefik_service_type == "NodePort" %} + nodePort: 443 +{% endif %} + http3: + enabled: true + advertisedPort: 443 +# tls: +# enabled: true +# options: default +{% endif %} +{% if traefik_hub_token is defined %} + traefikhub-tunl: + port: 9901 + expose: true + exposedPort: 9901 + protocol: "TCP" +{% endif %} +{% endif %} +volumes: + - mountPath: /etc/traefik + name: traefik-conf + type: configMap + - mountPath: /etc/traefik/file + name: traefik-files + type: configMap + - mountPath: /etc/traefik/basic-auth + name: basic-auth + type: secret +deployment: +{% if traefik_hostport is defined and traefik_hostport == true %} + kind: DaemonSet +{% else %} + replicas: 1 +{% endif %} + revisionHistoryLimit: 3 +# podAnnotations: +# prometheus.io/port: '9100' +# prometheus.io/scrape: 'true' +# prometheus.io/path: "/metrics" +{% if traefik_hostport is defined and traefik_hostport == true %} +updateStrategy: + type: OnDelete +{% endif %} +metrics: + prometheus: + service: + enabled: true + serviceMonitor: + metricRelabelings: [] +# # - sourceLabels: [__name__] +# # separator: ; +# # regex: ^fluentd_output_status_buffer_(oldest|newest)_.+ +# # replacement: $1 +# # action: drop + relabelings: [] +# # - sourceLabels: [__meta_kubernetes_pod_node_name] +# # separator: ; +# # regex: ^(.*)$ +# # targetLabel: nodename +# # replacement: $1 +# # action: replace +# jobLabel: traefik +# interval: 30s +# honorLabels: true +# # (Optional) +# # scrapeTimeout: 5s +# # honorTimestamps: true +# # enableHttp2: true +# # followRedirects: true +# # additionalLabels: +# # foo: bar +# # namespace: "another-namespace" +# # namespaceSelector: {} +# prometheusRule: +# enabled: true +# additionalLabels: {} +# namespace: "{{ traefik_namespace }}" +# rules: +# - alert: TraefikDown +# expr: up{job="traefik"} == 0 +# for: 5m +# labels: +# context: traefik +# severity: warning +# annotations: +# summary: "Traefik Down" +# description: "{% raw %}{{ $labels.pod }} on {{ $labels.nodename }} is down{% endraw %}" +experimental: +{% if traefik_ondemand is defined %} + plugins: + sablier: + moduleName: "github.com/sablierapp/sablier" + version: "v1.8.1" +{% endif %} +{% if traefik_hub_token is defined %} + hub: + enabled: true +{% endif %} +securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: [ALL] + readOnlyRootFilesystem: true + seccompProfile: + type: RuntimeDefault +{% if false %} +{% raw %} +extraObjects: + - apiVersion: v1 + kind: ConfigMap + metadata: + name: "extra" + data: + something: "extra" + - | + apiVersion: v1 + kind: ConfigMap + metadata: + name: "templated" + data: + something: {{ printf "templated" }} +{% endraw %} +{% endif %} diff --git a/templates/traefik-hub-certificate.yml.j2 b/templates/traefik-hub-certificate.yml.j2 new file mode 100644 index 0000000..72997c3 --- /dev/null +++ b/templates/traefik-hub-certificate.yml.j2 @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: List +items: +- apiVersion: v1 + kind: Secret + metadata: + annotations: + app.kubernetes.io/managed-by: traefik-hub + name: hub-certificate + namespace: {{ traefik_namespace }} + type: kubernetes.io/tls + data: + tls.crt: {{ traefik_hub_tlscrt | b64encode }} + tls.key: {{ traefik_hub_tlskey | b64encode }} diff --git a/templates/traefik-ingressroute.yml.j2 b/templates/traefik-ingressroute.yml.j2 index 587857f..7e27fde 100644 --- a/templates/traefik-ingressroute.yml.j2 +++ b/templates/traefik-ingressroute.yml.j2 @@ -1,18 +1,29 @@ -apiVersion: traefik.containo.us/v1alpha1 +apiVersion: traefik.io/v1alpha1 kind: IngressRoute metadata: name: traefik labels: app: traefik + annotations: + kubernetes.io/ingress.class: traefik +{% if false %} + external-dns.alpha.kubernetes.io/hostname: traefik.{{ cluster_domain }} + external-dns.alpha.kubernetes.io/endpoints-type: NodeExternalIP + # external-dns.alpha.kubernetes.io/endpoints-type: HostIP + # external-dns.alpha.kubernetes.io/target: "1.2.3.4" + + # external-dns.alpha.kubernetes.io/ttl: "120" + # external-dns.alpha.kubernetes.io/cloudflare-proxied: "true" +{% endif %} spec: entryPoints: - - https + - websecure routes: # Match is the rule corresponding to an underlying router. # Later on, match could be the simple form of a path prefix, e.g. just "/bar", # but for now we only support a traefik style matching rule. - - match: Host(`traefik.{{ traefik_domain }}`) && (PathPrefix(`/dashboard`) || PathPrefix(`/api`)) + - match: Host(`traefik.{{ cluster_domain }}`) && (PathPrefix(`/dashboard`) || PathPrefix(`/api`)) # kind could eventually be one of "Rule", "Path", "Host", "Method", "Header", # "Parameter", etc, to support simpler forms of rule matching, but for now we # only support "Rule". @@ -20,29 +31,31 @@ spec: {% if basic_auth is defined or ingress_whitelist is defined %} middlewares: {% if ingress_whitelist is defined %} - - name: traefik-ipwhitelist + - name: traefik-ipwhitelist@file {% endif %} {% if basic_auth is defined %} - - name: basic-auth + - name: basic-auth@file {% endif %} {% endif %} services: - name: api@internal kind: TraefikService - - match: Host(`traefik.{{ traefik_domain }}`) && PathPrefix(`/ping`) + - match: Host(`traefik.{{ cluster_domain }}`) && PathPrefix(`/ping`) kind: Rule services: - name: ping@internal kind: TraefikService - - match: Host(`traefik.{{ traefik_domain }}`) && PathPrefix(`/metrics`) - kind: Rule - services: - - name: prometheus@internal - kind: TraefikService +# - match: Host(`traefik.{{ cluster_domain }}`) && PathPrefix(`/metrics`) +# kind: Rule +# services: +# - name: prometheus@internal +# kind: TraefikService tls: {% if traefik_dashboard_certificate is defined %} secretName: {{ traefik_dashboard_certificate }} +{% else %} + secretName: traefik.{{ cluster_domain }} {% endif %} diff --git a/templates/traefik-middleware-ipwhitelist.yml.j2 b/templates/traefik-middleware-ipwhitelist.yml.j2 deleted file mode 100644 index 2e987a9..0000000 --- a/templates/traefik-middleware-ipwhitelist.yml.j2 +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: traefik.containo.us/v1alpha1 -kind: Middleware -metadata: - name: traefik-ipwhitelist -spec: - ipWhiteList: - sourceRange: -{% if ingress_whitelist is defined %} -{% for acl_whitelist in ingress_whitelist %} - - {{ acl_whitelist }} -{% endfor %} -{% endif %} \ No newline at end of file diff --git a/templates/traefik-ondemand-plugin.yml.j2 b/templates/traefik-ondemand-plugin.yml.j2 new file mode 100644 index 0000000..76fdb93 --- /dev/null +++ b/templates/traefik-ondemand-plugin.yml.j2 @@ -0,0 +1,134 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: sablier + labels: + app.kubernetes.io/name: sablier +spec: + replicas: 1 + selector: + matchLabels: + app.kubernetes.io/name: sablier + template: + metadata: + labels: + app.kubernetes.io/name: sablier + spec: + serviceAccountName: sablier + serviceAccount: sablier + containers: + - name: sablier + image: sablierapp/sablier:{{ traefik_sabblier_version }} + args: + - "start" + - "--provider.name=kubernetes" + - "--server.port=10000" + - "--storage.file=/dev/shm/state.json" + ports: + - containerPort: 10000 + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: [ALL] + readOnlyRootFilesystem: true + seccompProfile: + type: RuntimeDefault + securityContext: + runAsGroup: 65532 + runAsNonRoot: true + runAsUser: 65532 +# --configFile=path/to/myconfigfile.yml +--- +apiVersion: v1 +kind: Service +metadata: + name: sablier +spec: + selector: + app.kubernetes.io/name: sablier + ports: + - protocol: TCP + port: 10000 + targetPort: 10000 +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: sablier +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: sablier +# namespace: {{ traefik_namespace }} +rules: + - apiGroups: + - apps + - "" + resources: + - deployments + - statefulsets + verbs: + - get # Retrieve info about specific dep + - list # Events + - watch # Events + - apiGroups: + - apps + - "" + resources: + - deployments/scale + - statefulsets/scale + verbs: + - patch # Scale up and down + - get # Retrieve info about specific dep + - update # Scale up and down + - list # Events + - watch # Events +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: sablier +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: sablier +subjects: + - kind: ServiceAccount + name: sablier + namespace: {{ traefik_namespace }} +--- +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: allow-traefik-to-sablier +spec: + podSelector: + matchLabels: + app.kubernetes.io/name: sablier + ingress: + - ports: + - port: 10000 + from: + - podSelector: + matchLabels: + app.kubernetes.io/name: traefik + policyTypes: + - Ingress +#--- +#apiVersion: traefik.io/v1alpha1 +#kind: Middleware +#metadata: +# name: ondemand +#spec: +# plugin: +# names: toto # Comma separated names of containers/services/deployments etc. +# group: default # Group name to use to filter by label, ignored if names is set +# dynamic: +# displayName: My Title # (Optional) Defaults to the middleware name +# refreshFrequency: 5s # (Optional) The loading page refresh frequency +# showDetails: "true" # (Optional) Set to true or false to show details specifcally for this middleware, unset to use Sablier server defaults +# theme: hacker-terminal # (Optional) The theme to use +# sablierUrl: http://sablier.{{ traefik_namespace }}:10000 # The sablier URL service, must be reachable from the Traefik instance +# sessionDuration: 1m # The session duration after which containers/services/deployments instances are shutdown + diff --git a/templates/traefik-svc.yml.j2 b/templates/traefik-svc.yml.j2 index dc82b82..b206f0e 100644 --- a/templates/traefik-svc.yml.j2 +++ b/templates/traefik-svc.yml.j2 @@ -9,15 +9,23 @@ metadata: spec: ports: - name: web - hostPort: 80 port: 80 protocol: TCP targetPort: web - name: websecure - hostPort: 443 port: 443 protocol: TCP targetPort: websecure + - name: websecure-http3 + port: 443 + protocol: UDP + targetPort: websecure +{% if traefik_external_ips is defined %} + externalIPs: +{% for traefik_external_ip in traefik_external_ips %} + - {{ traefik_external_ip }} +{% endfor %} +{% endif %} selector: app.kubernetes.io/instance: traefik app.kubernetes.io/name: traefik diff --git a/todo.sh b/todo.sh new file mode 100644 index 0000000..3cab209 --- /dev/null +++ b/todo.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash + +cat << 'EOF' | kubectl --context my_context apply -f - +--- +apiVersion: networking.k8s.io/v1 +kind: IngressClass +metadata: + name: traefik +spec: + controller: traefik.io/ingress-controller +EOF + diff --git a/vars/main.yml b/vars/main.yml index 3720128..ed97d53 100644 --- a/vars/main.yml +++ b/vars/main.yml @@ -1,12 +1 @@ - -traefik_version_2_3: 2.3.7 -traefik_2_3_list: - - 2.3/traefik-crd-ingressroutes.yml.j2 - - 2.3/traefik-crd-ingressroutetcps.yml.j2 - - 2.3/traefik-crd-ingressrouteudps.yml.j2 - - 2.3/traefik-crd-middlewares.yml.j2 - - 2.3/traefik-crd-tlsoptions.yml.j2 - - 2.3/traefik-crd-tlsstores.yml.j2 - - 2.3/traefik-crd-traefikservices.yml.j2 - - 2.3/traefik-clusterrole.yml.j2 - - 2.3/traefik-clusterrolebinding.yml.j2 +---