Blog
Mert Özen's blog posts on software development, backend, frontend, and DevOps.
Monitoring Certificates and Preventing Outages: Inventory, Expiry Alerts, TLS Testing and Real Incident Examples
To prevent certificate outages you measure the certificate the client sees from the outside, not the file on disk: a lifetime-based alert on the blackbox exporter's last_chain metric, a probe_success alert, and an inventory with CT logs and CAA. In the lab we reproduce an unreloaded renewal, a missing intermediate, a wrong SAN, a cross certificate that misleads earliest_cert_expiry and clock skew, and test the alerts with promtool and the TLS config with testssl.sh.
mTLS and Internal Certificates: Client Certificate Authentication in ASP.NET Core and Running Your Own CA
In mTLS the client also proves its identity with a certificate and a signature made with its private key; because public CAs are dropping the client authentication EKU, you need your own internal CA with an offline root. In ASP.NET Core we set up AddCertificate with CustomRootTrust and issuer-and-CN partner mapping, and with real runs we see Kestrel's silent rejection at the TLS layer, revocation, and how Nginx forwarding the certificate in a header can be faked without the private key.
Enforcing HTTPS: HTTP→HTTPS Redirects, Rolling Out HSTS Gradually, Preload and Mixed Content
To enforce HTTPS you redirect at the layer where TLS terminates, reject HTTP with 400 instead of redirecting on APIs, and roll HSTS out gradually from 5 minutes to a year; a subdomain inventory is a must before includeSubDomains. With real runs we see the UseHttpsRedirection loop and the non-secure cookie behind a proxy, 301 turning a POST into a GET, HSTS locking an HTTP-only subdomain, and an absolute http:// URL in an Angular build being blocked as mixed content.
Certificates on Managed Platforms: Caddy, Azure App Service, Key Vault, Front Door and cert-manager on Kubernetes
On managed platforms the platform obtains and renews the certificate, but each has a limit that will catch you: Caddy hits rate limits without a /data volume, the App Service managed certificate has no wildcard, Front Door wants a direct CNAME, and cert-manager changes the key on every renewal. With real runs of Caddy, cert-manager and Envoy Gateway against Pebble, we watch pinning break on renewal.
Certificates in .NET and Kestrel: dev-certs, Loading PEM/PFX, SNI, Reload on Renewal and HttpClient Validation
Kestrel reloads a PEM or PFX certificate from configuration without a restart, but it watches only Path, notices the change in about five seconds and restarts the endpoint; as a non-root user it cannot read Certbot's files, so a deploy hook is needed. With real runs we look at dev-certs' SSL_CERT_DIR setting on Linux, SNI with ServerCertificateSelector, and trusting an internal CA in HttpClient with CustomRootTrust instead of turning validation off.
Automatic Certificates with Let's Encrypt and ACME: Certbot, Nginx, Wildcards and Renewal with ARI
Getting a certificate with ACME takes account, order, authorization, challenge and finalize steps; Certbot does it on Nginx with webroot or the nginx plugin, gets wildcards with DNS-01, and thanks to ARI renews when the CA suggests. With real runs on Pebble we watch a revoked certificate renew itself through ARI, and look at profiles, 6-day and IP certificates, and the cost of closing port 80.
The Certificate Lifecycle: CSR, Domain Validation, Certificate Transparency, Revocation and the 47-Day Future
A certificate is requested with a CSR, the CA validates the domain with HTTP-01, DNS-01 or TLS-ALPN-01, the certificate is written to CT logs, and revocation now rests on CRLs and short lifetimes rather than OCSP. With SC-081v3 the maximum lifetime drops to 200 days on 15 March 2026, 100 in 2027 and 47 in 2029; through crt.sh, a real CRL and CSR generation in .NET we see why manual renewal is over.
What's Inside a TLS Certificate? X.509, SAN, Chain of Trust and File Formats
A TLS certificate is an X.509 document, signed by a CA, that binds a public key to the names in its SAN list; the client trusts it because the chain of signatures reaches a root in its trust store. With real runs we read the fields with openssl and .NET's X509CertificateLoader, and see the one-level limit of wildcards, Let's Encrypt's cross-signed Generation Y chain, and how a missing intermediate breaks curl but is hidden in .NET by AIA.
What's New in Kubernetes 1.33–1.37: Native Sidecars, In-Place Resize, preStop Sleep, Gateway API and Life After ingress-nginx
The changes in Kubernetes 1.33–1.37 that matter most to app developers are native sidecars (1.33), preStop sleep (1.34), in-place resize (1.35), user namespaces (1.36) and HPA tolerance (1.37). With real output we see a sidecar ignoring SIGTERM stretch a Job to 39 seconds, the externalIPs and Endpoints deprecations, KYAML, the ingress-nginx retirement and Gateway API v1.7.
Reading Old Kubernetes Manifests: dockershim, PodSecurityPolicy and Removed APIs (1.16–1.25)
Converting 2018–2021-era manifests takes more than changing apiVersion: apps/v1 requires a selector, kubectl convert writes the old defaults (infinite progressDeadlineSeconds, maxUnavailable: 1) into the manifest, and Ingress backends changed shape. With real output we see that the dockershim removal didn't affect Docker images, replace PodSecurityPolicy with Pod Security Admission and scan with pluto.
End to End: Taking a .NET + Angular App to Managed Kubernetes (AKS, EKS, GKE) and a Production Checklist
On managed Kubernetes the control plane stays with the provider; if you use node identity for image pulls and workload identity for cloud access, not a single cloud secret is left in the pipeline or the cluster. We cover AKS, EKS and GKE differences, an OIDC deploy-to-AKS workflow validated with actionlint, deprecated API clients that pluto can't see, and a production checklist as code that runs on kind.
Debugging in Kubernetes: CrashLoopBackOff, ImagePullBackOff, Pending and kubectl debug
Pod failures always follow the same order: read the symptom with get, the events with describe, the app's voice with logs --previous, and if needed bring an ephemeral container into a shell-less pod with kubectl debug. With real output we cover Pending, ImagePullBackOff, CrashLoopBackOff, OOMKilled and a port mismatch, a .NET process hanging as PID 1, and dotnet-dump killing a pod that runs near its limit.