mirror of
https://github.com/rubenhensen/k8scd.git
synced 2026-11-15 14:29:09 +01:00
Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d187f55e57 | ||
|
|
3fd585de5f |
No files matched your search
@@ -1,19 +0,0 @@
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: cloudnative-pg-folder
|
||||
namespace: argocd
|
||||
finalizers:
|
||||
- resources-finalizer.argocd.argoproj.io
|
||||
spec:
|
||||
project: default
|
||||
source:
|
||||
repoURL: https://github.com/rubenhensen/k8scd.git
|
||||
targetRevision: HEAD
|
||||
path: cloudnative-pg
|
||||
destination:
|
||||
server: https://kubernetes.default.svc
|
||||
namespace: cnpg-system
|
||||
syncPolicy:
|
||||
automated:
|
||||
selfHeal: true
|
||||
@@ -1,19 +0,0 @@
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: default-folder
|
||||
namespace: argocd
|
||||
finalizers:
|
||||
- resources-finalizer.argocd.argoproj.io
|
||||
spec:
|
||||
project: default
|
||||
source:
|
||||
repoURL: https://github.com/rubenhensen/k8scd.git
|
||||
targetRevision: HEAD
|
||||
path: default
|
||||
destination:
|
||||
server: https://kubernetes.default.svc
|
||||
namespace: default
|
||||
syncPolicy:
|
||||
automated:
|
||||
selfHeal: true
|
||||
@@ -1,19 +0,0 @@
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: external-secrets-folder
|
||||
namespace: argocd
|
||||
finalizers:
|
||||
- resources-finalizer.argocd.argoproj.io
|
||||
spec:
|
||||
project: default
|
||||
source:
|
||||
repoURL: https://github.com/rubenhensen/k8scd.git
|
||||
targetRevision: HEAD
|
||||
path: external-secrets
|
||||
destination:
|
||||
server: https://kubernetes.default.svc
|
||||
namespace: external-secrets
|
||||
syncPolicy:
|
||||
automated:
|
||||
selfHeal: true
|
||||
@@ -1,73 +0,0 @@
|
||||
# cert-manager's three pods reach each other, DNS and the API server (the
|
||||
# node, port 16443), and take connections only from each other and the
|
||||
# node: the API server calls the webhook from there and the kubelet probes
|
||||
# come from there too (10.1.7.64 is the node's Calico VXLAN address,
|
||||
# projectcalico.org/IPv4VXLANTunnelAddr). Nothing scrapes the metrics on
|
||||
# 9402. The HTTP-01 solver pods run in each Certificate's own namespace
|
||||
# (acme-http01-solver in <namespace>/networkpolicy.yaml), not here.
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: NetworkPolicy
|
||||
metadata:
|
||||
name: default
|
||||
spec:
|
||||
podSelector: {}
|
||||
policyTypes:
|
||||
- Ingress
|
||||
- Egress
|
||||
ingress:
|
||||
- from:
|
||||
- podSelector: {}
|
||||
- from:
|
||||
- ipBlock:
|
||||
cidr: 192.168.1.204/32
|
||||
- ipBlock:
|
||||
cidr: 10.1.7.64/32
|
||||
egress:
|
||||
- to:
|
||||
- podSelector: {}
|
||||
- to:
|
||||
- namespaceSelector:
|
||||
matchLabels:
|
||||
kubernetes.io/metadata.name: kube-system
|
||||
podSelector:
|
||||
matchLabels:
|
||||
k8s-app: kube-dns
|
||||
ports:
|
||||
- protocol: UDP
|
||||
port: 53
|
||||
- protocol: TCP
|
||||
port: 53
|
||||
- to:
|
||||
- ipBlock:
|
||||
cidr: 192.168.1.204/32
|
||||
ports:
|
||||
- port: 16443
|
||||
---
|
||||
# The controller talks to Let's Encrypt (443), and before it lets Let's
|
||||
# Encrypt check a challenge it fetches http://<host>/.well-known/... itself
|
||||
# (80). Every certificate's host has a public address, node001's or the
|
||||
# home router's (checked 2026-10-26), so the LAN stays out.
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: NetworkPolicy
|
||||
metadata:
|
||||
name: controller-to-internet
|
||||
spec:
|
||||
podSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: cert-manager
|
||||
app.kubernetes.io/component: controller
|
||||
policyTypes:
|
||||
- Egress
|
||||
egress:
|
||||
- to:
|
||||
- ipBlock:
|
||||
cidr: 0.0.0.0/0
|
||||
except:
|
||||
- 10.0.0.0/8
|
||||
- 100.64.0.0/10
|
||||
- 169.254.0.0/16
|
||||
- 172.16.0.0/12
|
||||
- 192.168.0.0/16
|
||||
ports:
|
||||
- port: 80
|
||||
- port: 443
|
||||
@@ -1,56 +0,0 @@
|
||||
# The CloudNativePG operator reaches DNS, the API server (the node, port
|
||||
# 16443) and the PostgreSQL instances it runs in other namespaces: the
|
||||
# instance manager's status port (8000) and PostgreSQL (5432), which the
|
||||
# instances' own policies let in (postgres-cnpg in authentik and immich).
|
||||
# Besides its own namespace it takes connections only from the node: the
|
||||
# API server calls its webhook (9443) from there and the kubelet probes
|
||||
# come from there too (10.1.7.64 is the node's Calico VXLAN address,
|
||||
# projectcalico.org/IPv4VXLANTunnelAddr). The chart's Application
|
||||
# (apps/templates/cloudnativepg-helm.yaml) only installs the chart, so
|
||||
# this folder gets an Application of its own, cloudnative-pg-folder.
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: NetworkPolicy
|
||||
metadata:
|
||||
name: default
|
||||
spec:
|
||||
podSelector: {}
|
||||
policyTypes:
|
||||
- Ingress
|
||||
- Egress
|
||||
ingress:
|
||||
- from:
|
||||
- podSelector: {}
|
||||
- from:
|
||||
- ipBlock:
|
||||
cidr: 192.168.1.204/32
|
||||
- ipBlock:
|
||||
cidr: 10.1.7.64/32
|
||||
egress:
|
||||
- to:
|
||||
- podSelector: {}
|
||||
- to:
|
||||
- namespaceSelector:
|
||||
matchLabels:
|
||||
kubernetes.io/metadata.name: kube-system
|
||||
podSelector:
|
||||
matchLabels:
|
||||
k8s-app: kube-dns
|
||||
ports:
|
||||
- protocol: UDP
|
||||
port: 53
|
||||
- protocol: TCP
|
||||
port: 53
|
||||
- to:
|
||||
- ipBlock:
|
||||
cidr: 192.168.1.204/32
|
||||
ports:
|
||||
- port: 16443
|
||||
- to:
|
||||
- namespaceSelector: {}
|
||||
podSelector:
|
||||
matchExpressions:
|
||||
- key: cnpg.io/cluster
|
||||
operator: Exists
|
||||
ports:
|
||||
- port: 8000
|
||||
- port: 5432
|
||||
@@ -1,14 +0,0 @@
|
||||
# Nothing runs in default (2026-10-26: only the kubernetes Service, whose
|
||||
# endpoint is the API server on the node, which this doesn't touch). A pod
|
||||
# started here, by `kubectl run` or a manifest without a namespace, gets
|
||||
# no network at all, instead of reaching the LAN, Vault and every app.
|
||||
# Anything that needs a network gets a namespace and a policy of its own.
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: NetworkPolicy
|
||||
metadata:
|
||||
name: default-deny
|
||||
spec:
|
||||
podSelector: {}
|
||||
policyTypes:
|
||||
- Ingress
|
||||
- Egress
|
||||
@@ -7,7 +7,6 @@ resources:
|
||||
- external-secret.yaml
|
||||
- rbac.yaml
|
||||
- cronjob.yaml
|
||||
- networkpolicy.yaml
|
||||
|
||||
configMapGenerator:
|
||||
- name: dns-sync-script
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
# The only pod here is the dns-sync Job (a PostSync hook): it installs its
|
||||
# Python packages from PyPI and pushes domains/ to TransIP's API, so it
|
||||
# reaches DNS and the internet (not the LAN, the API server or other
|
||||
# namespaces). Nothing connects to it.
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: NetworkPolicy
|
||||
metadata:
|
||||
name: default
|
||||
spec:
|
||||
podSelector: {}
|
||||
policyTypes:
|
||||
- Ingress
|
||||
- Egress
|
||||
egress:
|
||||
- to:
|
||||
- namespaceSelector:
|
||||
matchLabels:
|
||||
kubernetes.io/metadata.name: kube-system
|
||||
podSelector:
|
||||
matchLabels:
|
||||
k8s-app: kube-dns
|
||||
ports:
|
||||
- protocol: UDP
|
||||
port: 53
|
||||
- protocol: TCP
|
||||
port: 53
|
||||
- to:
|
||||
- ipBlock:
|
||||
cidr: 0.0.0.0/0
|
||||
except:
|
||||
- 10.0.0.0/8
|
||||
- 100.64.0.0/10
|
||||
- 169.254.0.0/16
|
||||
- 172.16.0.0/12
|
||||
- 192.168.0.0/16
|
||||
ports:
|
||||
- port: 443
|
||||
+36
-15
@@ -38,7 +38,8 @@ docs/longhorn-second-ssd.md:
|
||||
2026-10-25). The snap is held, so microk8s gets no updates at all until
|
||||
someone refreshes it
|
||||
- [ ] Second SSD, docs/longhorn-second-ssd.md
|
||||
- [ ] Optional: `MACHINE_LEARNING_MODEL_REVISION=v2` for immich ML
|
||||
- [x] `MACHINE_LEARNING_MODEL_REVISION=v2` for immich ML (PR #39,
|
||||
2026-10-25; the models are cached on a volume)
|
||||
|
||||
## Next, in order
|
||||
|
||||
@@ -82,7 +83,9 @@ friend.
|
||||
certificate; the admin API stays on the LAN. The cluster side (DNS,
|
||||
node001 tunnel, Certificate, HTTPRoute) goes in git. Needed: how much
|
||||
space he wants, free space on the NAS
|
||||
- [ ] Decide which copy of the immich library goes off-site (see 5)
|
||||
- [x] Which copy of the immich library goes off-site: Velero's, with the
|
||||
rest of the bucket (decided 2026-10-26). It is most of the ~190 GB, so
|
||||
ask him for room for it
|
||||
- [ ] A restore test that runs on a schedule. `backup-testing/` predates
|
||||
Envoy Gateway and still rewrites `*.hensen.io` and bypasses sol-auth.
|
||||
Planned on 2026-10-25, not run: a second Velero on the real bucket is
|
||||
@@ -202,8 +205,8 @@ Findings from 2026-10-25:
|
||||
maintenance job pods) was fixed that afternoon by
|
||||
`velero/skip-emptydir-policy.yaml`.
|
||||
|
||||
- [ ] Whether the immich library stays in Velero. It stays for now
|
||||
(2026-10-25); decide again with the off-site copy (2)
|
||||
- [x] Whether the immich library stays in Velero: it stays, since
|
||||
Velero's bucket is what goes off-site (2; decided 2026-10-26)
|
||||
- [ ] CNPG backups through the barman cloud plugin
|
||||
- [x] Velero's target, MinIO on the NAS, is no longer maintained: no
|
||||
community images since October 2025, maintenance mode since December
|
||||
@@ -298,10 +301,22 @@ Counted 2026-10-25 over the 73 running containers:
|
||||
OIDC logins of Vault and FreshRSS reach authentik, all 24
|
||||
ExternalSecrets refresh, raft intact; Gitea no longer reaches the NAS
|
||||
or Vault, and a pod in `default` no longer reaches Vault or Gitea
|
||||
- [ ] Certificates couldn't renew in those 11 namespaces: Envoy may only
|
||||
reach each app's own port, and cert-manager's HTTP-01 solver listens on
|
||||
8089. ynab's renewal hung from 2026-10-26 17:35 UTC; a policy for the
|
||||
solver pods, applied by hand in actual-budget that evening, let it
|
||||
through within 20 seconds (now valid until 2027-01-24). PR #51 adds it
|
||||
to all 11. Without it the next renewals hang too: gitea on 11-15,
|
||||
bolletjestrui on 11-18, the rest from 12-03
|
||||
- [ ] The infrastructure namespaces (cert-manager, cnpg-system,
|
||||
external-secrets, envoy-gateway-system, velero, mariadb-system, dns,
|
||||
default) still have none; `default` can reach the LAN and everything
|
||||
without a policy
|
||||
without a policy. Prepared on 2026-10-26, not yet merged: PR #53 for
|
||||
all but Envoy (one commit per namespace; `default` denies everything),
|
||||
PR #54 for envoy-gateway-system on its own, since a mistake there takes
|
||||
every public host down. Merge them when someone is home to check: the
|
||||
cert-manager, CNPG, ESO and MariaDB webhooks fail closed. Velero's
|
||||
policy only lets it reach Garage, not MinIO's port 9000
|
||||
- [x] Requests: PR #33, merged 2026-10-25 11:23 UTC, for everything that
|
||||
had none except the Vault server, from `kubectl top` every 5 minutes
|
||||
from 03:13 to 08:40 UTC (highest memory plus 10%, CPU at p95, 10m
|
||||
@@ -334,10 +349,14 @@ Counted 2026-10-25 over the 73 running containers:
|
||||
`litespeed-cache` only caches pages on a LiteSpeed server, so it does
|
||||
little here, `wordpress-importer` is usually for a one-off import, and
|
||||
`code-snippets` runs PHP from the dashboard
|
||||
- [ ] Rate limiting per visitor in Envoy needs the client's address, which
|
||||
node001 loses: it passes TLS through, so every request arrives from
|
||||
46.224.26.65. That needs the PROXY protocol from node001's nginx to Envoy
|
||||
(ClientTrafficPolicy `enableProxyProtocol`)
|
||||
- [ ] Rate limiting per visitor in Envoy, on hold. Since PR #50
|
||||
(2026-10-26) node001 passes the visitor's address on with the PROXY
|
||||
protocol, and the apps see it: authentik logs it, Apache logs it for
|
||||
WordPress, so Wordfence's lockout after 20 failed logins now hits one
|
||||
visitor instead of everyone. Envoy Gateway's local rate limit can't
|
||||
count per address (no `Distinct`); that takes its global rate limit,
|
||||
with a Redis and a ratelimit service next to it. Not worth two more
|
||||
components on the one node while the apps' own limits work
|
||||
- [x] Probes for gitea (`/api/healthz`), freshrss (TCP, since
|
||||
mod_auth_openidc answers 401 to non-browsers) and serpbear (`/login`)
|
||||
(2026-10-25). Liveness only after 5 failures 30s apart, to ride out the
|
||||
@@ -375,7 +394,10 @@ every PR.
|
||||
fail the run; tested with a planted Secret and an untagged image), plus
|
||||
PR annotations for containers without requests or probes. freshrss's
|
||||
`alpine/git` init container had no tag; now `v2.52.0`
|
||||
- [ ] Automerge patch versions of low-risk apps now that CI runs
|
||||
- [ ] Automerge patch versions of low-risk apps now that CI runs: PR #52
|
||||
(ntfy, gatus, freshrss, serpbear, actual-budget and CI's own tools;
|
||||
three days after the release, only on weekday evenings and at the
|
||||
weekend, when the cluster can be reached)
|
||||
- [x] Renovate listed external-snapshotter twice (#15 and #24): its
|
||||
`argocd` manager and a custom `github-tags` regex both matched. The
|
||||
regex is gone; Renovate should close #15 by itself
|
||||
@@ -432,11 +454,10 @@ step after that, if downtime starts to matter.
|
||||
there and the volumes hold nothing (0 bytes in every backup). Drop both
|
||||
mounts and the two PVCs, or make them ConfigMap files if custom config
|
||||
is wanted
|
||||
- [ ] `kubectl delete namespace secrets`, empty since its app went
|
||||
- [ ] 41 Failed Velero backups: the dailies from 2026-09-26 to 10-21 and
|
||||
the weeklies since 08-02. Velero doesn't restore from a Failed backup;
|
||||
left alone they expire by 2027-01-16.
|
||||
`kubectl -n velero exec deploy/velero -c velero -- /velero backup delete $(kubectl -n velero get backups.velero.io -o json | jq -r '.items[] | select(.status.phase=="Failed") | .metadata.name' | xargs) --confirm`
|
||||
- [x] The namespace `secrets` was already gone on 2026-10-26
|
||||
- [x] The Failed Velero backups (the dailies from 2026-09-26 to 10-21 and
|
||||
the weeklies since 08-02; 40 by then, one had expired), deleted through
|
||||
Velero on 2026-10-26. Left: 13 Completed, 3 PartiallyFailed
|
||||
- [ ] The mariadb-operator Backup `mbgwp-pre-migration-backup`, a dump
|
||||
from 2026-02-22 on its own 10 GiB volume (which has a Longhorn backup
|
||||
from 2026-10-22). The PVC has no owner, so it goes separately:
|
||||
|
||||
@@ -1,68 +0,0 @@
|
||||
# External Secrets' three pods reach each other, DNS and the API server
|
||||
# (the node, port 16443); the controller also reads Vault
|
||||
# (vault-active.vault.svc:8200, which vault/networkpolicy.yaml lets it
|
||||
# into). Besides their own namespace they take connections only from the
|
||||
# node: the API server calls the webhook from there and the kubelet probes
|
||||
# come from there too (10.1.7.64 is the node's Calico VXLAN address,
|
||||
# projectcalico.org/IPv4VXLANTunnelAddr). The Password generator behind
|
||||
# gatus/backup-check-token needs no network. The chart's Application
|
||||
# (apps/templates/external-secrets-helm.yaml) only installs the chart, so
|
||||
# this folder gets an Application of its own, external-secrets-folder.
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: NetworkPolicy
|
||||
metadata:
|
||||
name: default
|
||||
spec:
|
||||
podSelector: {}
|
||||
policyTypes:
|
||||
- Ingress
|
||||
- Egress
|
||||
ingress:
|
||||
- from:
|
||||
- podSelector: {}
|
||||
- from:
|
||||
- ipBlock:
|
||||
cidr: 192.168.1.204/32
|
||||
- ipBlock:
|
||||
cidr: 10.1.7.64/32
|
||||
egress:
|
||||
- to:
|
||||
- podSelector: {}
|
||||
- to:
|
||||
- namespaceSelector:
|
||||
matchLabels:
|
||||
kubernetes.io/metadata.name: kube-system
|
||||
podSelector:
|
||||
matchLabels:
|
||||
k8s-app: kube-dns
|
||||
ports:
|
||||
- protocol: UDP
|
||||
port: 53
|
||||
- protocol: TCP
|
||||
port: 53
|
||||
- to:
|
||||
- ipBlock:
|
||||
cidr: 192.168.1.204/32
|
||||
ports:
|
||||
- port: 16443
|
||||
---
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: NetworkPolicy
|
||||
metadata:
|
||||
name: controller-to-vault
|
||||
spec:
|
||||
podSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: external-secrets
|
||||
policyTypes:
|
||||
- Egress
|
||||
egress:
|
||||
- to:
|
||||
- namespaceSelector:
|
||||
matchLabels:
|
||||
kubernetes.io/metadata.name: vault
|
||||
podSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: vault
|
||||
ports:
|
||||
- port: 8200
|
||||
@@ -3,4 +3,3 @@ kind: Kustomization
|
||||
resources:
|
||||
- operator-crds-helm.yaml
|
||||
- operator-helm.yaml
|
||||
- networkpolicy.yaml
|
||||
@@ -1,54 +0,0 @@
|
||||
# The mariadb-operator's three pods reach each other, DNS and the API
|
||||
# server (the node, port 16443); the operator also connects to MariaDB on
|
||||
# 3306 for the databases, users and grants it manages (mbgwp's
|
||||
# mariadb-from-operator lets it in). Besides their own namespace they take
|
||||
# connections only from the node: the API server calls the webhook from
|
||||
# there and the kubelet probes come from there too (10.1.7.64 is the node's
|
||||
# Calico VXLAN address, projectcalico.org/IPv4VXLANTunnelAddr). This
|
||||
# folder's Application deploys into argocd (the operator's own
|
||||
# Applications), hence the namespace here.
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: NetworkPolicy
|
||||
metadata:
|
||||
name: default
|
||||
namespace: mariadb-system
|
||||
spec:
|
||||
podSelector: {}
|
||||
policyTypes:
|
||||
- Ingress
|
||||
- Egress
|
||||
ingress:
|
||||
- from:
|
||||
- podSelector: {}
|
||||
- from:
|
||||
- ipBlock:
|
||||
cidr: 192.168.1.204/32
|
||||
- ipBlock:
|
||||
cidr: 10.1.7.64/32
|
||||
egress:
|
||||
- to:
|
||||
- podSelector: {}
|
||||
- to:
|
||||
- namespaceSelector:
|
||||
matchLabels:
|
||||
kubernetes.io/metadata.name: kube-system
|
||||
podSelector:
|
||||
matchLabels:
|
||||
k8s-app: kube-dns
|
||||
ports:
|
||||
- protocol: UDP
|
||||
port: 53
|
||||
- protocol: TCP
|
||||
port: 53
|
||||
- to:
|
||||
- ipBlock:
|
||||
cidr: 192.168.1.204/32
|
||||
ports:
|
||||
- port: 16443
|
||||
- to:
|
||||
- namespaceSelector: {}
|
||||
podSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: mariadb
|
||||
ports:
|
||||
- port: 3306
|
||||
@@ -1,52 +0,0 @@
|
||||
# Velero, its node agent, and the pods it starts here (the data movers of
|
||||
# each pod volume backup or restore, kopia's maintenance jobs, the chart's
|
||||
# CRD upgrade job) reach each other, DNS, the API server (the node, port
|
||||
# 16443) and Garage on the NAS (192.168.1.99:3900, nas/garage). The node
|
||||
# agent reads volumes through its hostPath mount, not the network. Besides
|
||||
# their own namespace they take connections only from the node (kubelet
|
||||
# probes; 10.1.7.64 is the node's Calico VXLAN address,
|
||||
# projectcalico.org/IPv4VXLANTunnelAddr); nothing scrapes the metrics on
|
||||
# 8085. MinIO (the NAS, port 9000) is not allowed: going back to it means
|
||||
# adding its port here as well.
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: NetworkPolicy
|
||||
metadata:
|
||||
name: default
|
||||
spec:
|
||||
podSelector: {}
|
||||
policyTypes:
|
||||
- Ingress
|
||||
- Egress
|
||||
ingress:
|
||||
- from:
|
||||
- podSelector: {}
|
||||
- from:
|
||||
- ipBlock:
|
||||
cidr: 192.168.1.204/32
|
||||
- ipBlock:
|
||||
cidr: 10.1.7.64/32
|
||||
egress:
|
||||
- to:
|
||||
- podSelector: {}
|
||||
- to:
|
||||
- namespaceSelector:
|
||||
matchLabels:
|
||||
kubernetes.io/metadata.name: kube-system
|
||||
podSelector:
|
||||
matchLabels:
|
||||
k8s-app: kube-dns
|
||||
ports:
|
||||
- protocol: UDP
|
||||
port: 53
|
||||
- protocol: TCP
|
||||
port: 53
|
||||
- to:
|
||||
- ipBlock:
|
||||
cidr: 192.168.1.204/32
|
||||
ports:
|
||||
- port: 16443
|
||||
- to:
|
||||
- ipBlock:
|
||||
cidr: 192.168.1.99/32
|
||||
ports:
|
||||
- port: 3900
|
||||
Reference in new issue
Block a user