chore: remove deprecated files and update configuration for improved SEO and performance. Adjust font usage in styles, enhance sitemap handling, and refine search functionality.
This commit is contained in:
@@ -18,37 +18,56 @@ import rehypeDocument from 'rehype-document'
|
||||
import { pluginCollapsibleSections } from '@expressive-code/plugin-collapsible-sections'
|
||||
import { pluginLineNumbers } from '@expressive-code/plugin-line-numbers'
|
||||
|
||||
import tailwindcss from '@tailwindcss/vite'
|
||||
|
||||
import tailwindcss from "@tailwindcss/vite";
|
||||
import vercel from '@astrojs/vercel';
|
||||
|
||||
function rehypeDemoteH1AndStripTitle() {
|
||||
return (tree: any) => {
|
||||
const walk = (node: any, parent: any | null, indexInParent: number | null) => {
|
||||
if (!node) return
|
||||
const isElement = node.type === 'element'
|
||||
if (isElement) {
|
||||
if (node.tagName === 'title') {
|
||||
if (parent && Array.isArray(parent.children) && indexInParent !== null && indexInParent > -1) {
|
||||
parent.children.splice(indexInParent, 1)
|
||||
return
|
||||
}
|
||||
}
|
||||
if (node.tagName === 'h1') {
|
||||
node.tagName = 'h2'
|
||||
}
|
||||
}
|
||||
if (Array.isArray(node.children)) {
|
||||
for (let i = node.children.length - 1; i >= 0; i--) {
|
||||
walk(node.children[i], node, i)
|
||||
}
|
||||
}
|
||||
}
|
||||
walk(tree, null, null)
|
||||
}
|
||||
}
|
||||
|
||||
export default defineConfig({
|
||||
site: 'https://www.cojocarudavid.me',
|
||||
|
||||
integrations: [
|
||||
expressiveCode({
|
||||
themes: ['catppuccin-latte', 'ayu-dark'],
|
||||
plugins: [pluginCollapsibleSections(), pluginLineNumbers()],
|
||||
useDarkModeMediaQuery: true,
|
||||
defaultProps: {
|
||||
wrap: true,
|
||||
collapseStyle: 'collapsible-auto',
|
||||
overridesByLang: {
|
||||
'ansi,bat,bash,batch,cmd,console,powershell,ps,ps1,psd1,psm1,sh,shell,shellscript,shellsession,text,zsh':
|
||||
{
|
||||
showLineNumbers: true,
|
||||
},
|
||||
},
|
||||
integrations: [expressiveCode({
|
||||
themes: ['catppuccin-latte', 'ayu-dark'],
|
||||
plugins: [pluginCollapsibleSections(), pluginLineNumbers()],
|
||||
useDarkModeMediaQuery: true,
|
||||
defaultProps: {
|
||||
wrap: true,
|
||||
collapseStyle: 'collapsible-auto',
|
||||
overridesByLang: {
|
||||
'ansi,bat,bash,batch,cmd,console,powershell,ps,ps1,psd1,psm1,sh,shell,shellscript,shellsession,text,zsh':
|
||||
{
|
||||
showLineNumbers: true,
|
||||
},
|
||||
},
|
||||
}),
|
||||
mdx(),
|
||||
react(),
|
||||
sitemap(),
|
||||
icon(),
|
||||
],
|
||||
},
|
||||
}), mdx(), react(), sitemap(), icon()],
|
||||
|
||||
vite: {
|
||||
plugins: [tailwindcss()],
|
||||
plugins: [tailwindcss() as any],
|
||||
optimizeDeps: {
|
||||
exclude: ["satori", "satori-html"],
|
||||
include: [
|
||||
@@ -93,6 +112,7 @@ export default defineConfig({
|
||||
rel: ['nofollow', 'noreferrer', 'noopener'],
|
||||
},
|
||||
],
|
||||
rehypeDemoteH1AndStripTitle,
|
||||
rehypeHeadingIds,
|
||||
rehypeKatex,
|
||||
[
|
||||
|
||||
2656
package-lock.json
generated
2656
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
22
package.json
22
package.json
@@ -23,8 +23,8 @@
|
||||
"version": "1.0.6",
|
||||
"private": false,
|
||||
"scripts": {
|
||||
"dev": "astro dev",
|
||||
"start": "astro dev",
|
||||
"dev": "astro dev --port 3010",
|
||||
"start": "astro preview",
|
||||
"build": "astro check && astro build",
|
||||
"preview": "astro preview",
|
||||
"astro": "astro",
|
||||
@@ -32,17 +32,17 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@astrojs/check": "^0.9.4",
|
||||
"@astrojs/markdown-remark": "^6.3.3",
|
||||
"@astrojs/mdx": "^4.3.1",
|
||||
"@astrojs/markdown-remark": "^6.3.5",
|
||||
"@astrojs/mdx": "^4.3.3",
|
||||
"@astrojs/react": "^4.3.0",
|
||||
"@astrojs/rss": "^4.0.12",
|
||||
"@astrojs/sitemap": "^3.4.2",
|
||||
"@astrojs/vercel": "^8.2.3",
|
||||
"@astrojs/vercel": "^8.2.5",
|
||||
"@expressive-code/plugin-collapsible-sections": "^0.41.3",
|
||||
"@expressive-code/plugin-line-numbers": "^0.41.3",
|
||||
"@fingerprintjs/fingerprintjs": "^4.6.2",
|
||||
"@iconify-json/line-md": "^1.2.8",
|
||||
"@iconify-json/lucide": "^1.2.59",
|
||||
"@iconify-json/line-md": "^1.2.11",
|
||||
"@iconify-json/lucide": "^1.2.62",
|
||||
"@iconify-json/mdi": "^1.2.3",
|
||||
"@neondatabase/serverless": "^1.0.1",
|
||||
"@radix-ui/react-avatar": "^1.1.10",
|
||||
@@ -53,11 +53,11 @@
|
||||
"@radix-ui/react-slot": "^1.2.3",
|
||||
"@resvg/resvg-js": "^2.6.2",
|
||||
"@tailwindcss/vite": "^4.1.11",
|
||||
"@types/react": "19.1.9",
|
||||
"@types/react": "19.1.10",
|
||||
"@types/react-dom": "19.1.7",
|
||||
"@vercel/routing-utils": "^5.1.1",
|
||||
"@vercel/speed-insights": "^1.2.0",
|
||||
"astro": "^5.12.6",
|
||||
"astro": "^5.12.9",
|
||||
"astro-expressive-code": "^0.41.3",
|
||||
"astro-icon": "^1.1.5",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
@@ -65,7 +65,7 @@
|
||||
"framer-motion": "^12.23.12",
|
||||
"fuse.js": "^7.1.0",
|
||||
"lodash.debounce": "^4.0.8",
|
||||
"lucide-react": "^0.534.0",
|
||||
"lucide-react": "^0.539.0",
|
||||
"react": "19.1.1",
|
||||
"react-dom": "19.1.1",
|
||||
"react-icons": "^5.5.0",
|
||||
@@ -79,7 +79,7 @@
|
||||
"satori": "^0.16.2",
|
||||
"satori-html": "^0.3.2",
|
||||
"tailwind-merge": "^3.3.1",
|
||||
"typescript": "^5.8.3"
|
||||
"typescript": "^5.9.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/lodash.debounce": "^4.0.9",
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
public/fonts/Lexend/Lexend-Regular.woff
Normal file
BIN
public/fonts/Lexend/Lexend-Regular.woff
Normal file
Binary file not shown.
BIN
public/fonts/Lexend/Lexend-Regular.woff2
Normal file
BIN
public/fonts/Lexend/Lexend-Regular.woff2
Normal file
Binary file not shown.
@@ -46,12 +46,7 @@ import Favicons from './Favicons.astro'
|
||||
/>
|
||||
<meta name="msapplication-TileColor" content="#121212" />
|
||||
|
||||
<!-- Prefetch and Preconnect for Performance -->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" crossorigin />
|
||||
<link rel="dns-prefetch" href="//fonts.googleapis.com" />
|
||||
|
||||
<!-- Sitemap and Feed Links -->
|
||||
<link rel="sitemap" href="/sitemap-index.xml" />
|
||||
<!-- Feed link -->
|
||||
<link rel="manifest" href="/site.webmanifest" />
|
||||
<link
|
||||
rel="alternate"
|
||||
|
||||
@@ -10,18 +10,20 @@ const { title = SITE.title, description = SITE.description } = Astro.props
|
||||
const image = new URL('/ogImage.png', Astro.site).toString()
|
||||
const posts = await getAllPosts()
|
||||
|
||||
// Optimize description for SEO
|
||||
const optimizedDescription = description.length > 160
|
||||
? description.substring(0, 157) + '...'
|
||||
: description
|
||||
// Optimize description for SEO (50-155 chars)
|
||||
const descTrimmed = description.trim()
|
||||
const optimizedDescription = descTrimmed.length > 155
|
||||
? descTrimmed.slice(0, 152) + '...'
|
||||
: descTrimmed
|
||||
|
||||
// Create proper page title
|
||||
const pageTitle = title === SITE.title ? SITE.title : `${title} | ${SITE.title}`
|
||||
// Create proper page title, clamp to ~60 chars
|
||||
const rawTitle = title === SITE.title ? SITE.title : `${title} | ${SITE.title}`
|
||||
const pageTitle = rawTitle.length > 60 ? rawTitle.slice(0, 57) + '...' : rawTitle
|
||||
---
|
||||
|
||||
<title>{pageTitle}</title>
|
||||
<meta name="description" content={optimizedDescription} />
|
||||
<meta name="robots" content="index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1" />
|
||||
<!-- robots set globally in base head to avoid duplicates -->
|
||||
<meta name="language" content={SITE.locale} />
|
||||
<link rel="canonical" href={Astro.url} />
|
||||
|
||||
|
||||
@@ -14,19 +14,22 @@ const postUrl = new URL(`/blog/${post.id}/`, SITE.href).toString()
|
||||
const image = new URL(`/image/${post.id}.png`, SITE.href).toString()
|
||||
const author = post.data.authors ? post.data.authors.join(', ') : SITE.author
|
||||
|
||||
const optimizedDescription = description.length > 160
|
||||
? description.substring(0, 157) + '...'
|
||||
: description
|
||||
// Optimize description for SEO (50-155 chars)
|
||||
const descTrimmed = description.trim()
|
||||
const optimizedDescription = descTrimmed.length > 155
|
||||
? descTrimmed.slice(0, 152) + '...'
|
||||
: descTrimmed
|
||||
|
||||
const seoTitle = `${title} - ${SITE.title}`
|
||||
// Compose seo title, clamp near 60 chars
|
||||
const baseSeoTitle = `${title} - ${SITE.title}`
|
||||
const seoTitle = baseSeoTitle.length > 60 ? baseSeoTitle.slice(0, 57) + '...' : baseSeoTitle
|
||||
const postUrlWithoutTrailingSlash = postUrl.endsWith('/') ? postUrl.slice(0, -1) : postUrl
|
||||
const postCanonicalUrl = new URL(postUrlWithoutTrailingSlash, SITE.href).toString()
|
||||
---
|
||||
|
||||
<title>{seoTitle}</title>
|
||||
<meta name="description" content={optimizedDescription} />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="robots" content="index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1" />
|
||||
<!-- viewport and robots are set globally in base head -->
|
||||
<meta name="author" content={author} />
|
||||
<meta name="publisher" content={SITE.title} />
|
||||
<meta name="language" content={SITE.locale} />
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
---
|
||||
|
||||
---
|
||||
|
||||
<script is:inline>
|
||||
!(function (t, e) {
|
||||
var o, n, p, r;
|
||||
e.__SV ||
|
||||
((window.posthog = e),
|
||||
(e._i = []),
|
||||
(e.init = function (i, s, a) {
|
||||
function g(t, e) {
|
||||
var o = e.split(".");
|
||||
2 == o.length && ((t = t[o[0]]), (e = o[1])),
|
||||
(t[e] = function () {
|
||||
t.push([e].concat(Array.prototype.slice.call(arguments, 0)));
|
||||
});
|
||||
}
|
||||
((p = t.createElement("script")).type = "text/javascript"),
|
||||
(p.crossOrigin = "anonymous"),
|
||||
(p.async = !0),
|
||||
(p.src =
|
||||
s.api_host.replace(".i.posthog.com", "-assets.i.posthog.com") +
|
||||
"/static/array.js"),
|
||||
(r = t.getElementsByTagName("script")[0]).parentNode.insertBefore(
|
||||
p,
|
||||
r
|
||||
);
|
||||
var u = e;
|
||||
for (
|
||||
void 0 !== a ? (u = e[a] = []) : (a = "posthog"),
|
||||
u.people = u.people || [],
|
||||
u.toString = function (t) {
|
||||
var e = "posthog";
|
||||
return (
|
||||
"posthog" !== a && (e += "." + a), t || (e += " (stub)"), e
|
||||
);
|
||||
},
|
||||
u.people.toString = function () {
|
||||
return u.toString(1) + ".people (stub)";
|
||||
},
|
||||
o =
|
||||
"init capture register register_once register_for_session unregister unregister_for_session getFeatureFlag getFeatureFlagPayload isFeatureEnabled reloadFeatureFlags updateEarlyAccessFeatureEnrollment getEarlyAccessFeatures on onFeatureFlags onSessionId getSurveys getActiveMatchingSurveys renderSurvey canRenderSurvey identify setPersonProperties group resetGroups setPersonPropertiesForFlags resetPersonPropertiesForFlags setGroupPropertiesForFlags resetGroupPropertiesForFlags reset get_distinct_id getGroups get_session_id get_session_replay_url alias set_config startSessionRecording stopSessionRecording sessionRecordingStarted captureException loadToolbar get_property getSessionProperty createPersonProfile opt_in_capturing opt_out_capturing has_opted_in_capturing has_opted_out_capturing clear_opt_in_out_capturing debug getPageViewId captureTraceFeedback captureTraceMetric".split(
|
||||
" "
|
||||
),
|
||||
n = 0;
|
||||
n < o.length;
|
||||
n++
|
||||
)
|
||||
g(u, o[n]);
|
||||
e._i.push([i, s, a]);
|
||||
}),
|
||||
(e.__SV = 1));
|
||||
})(document, window.posthog || []);
|
||||
posthog.init("phc_orIpAm9v5xuxdhe4wd5FhkyTJ7ygykslwxxeCESZquz", {
|
||||
api_host: "https://eu.i.posthog.com",
|
||||
person_profiles: "identified_only",
|
||||
});
|
||||
</script>
|
||||
@@ -24,9 +24,11 @@ function Search({ searchList, initialPosts }) {
|
||||
...item,
|
||||
data: {
|
||||
...item.data,
|
||||
title: item.data.title.toLowerCase(),
|
||||
description: item.data.description.toLowerCase(),
|
||||
tags: item.data.tags.map((tag) => tag.toLowerCase()),
|
||||
title: String(item.data.title || '').toLowerCase(),
|
||||
description: String(item.data.description || '').toLowerCase(),
|
||||
tags: Array.isArray(item.data.tags)
|
||||
? item.data.tags.map((tag) => String(tag).toLowerCase())
|
||||
: [],
|
||||
},
|
||||
})),
|
||||
[searchList],
|
||||
|
||||
@@ -1,75 +1,176 @@
|
||||
---
|
||||
title: "Zero trust security: the ultimate guide for businesses"
|
||||
description: "Explore zero trust security: the ultimate guide for businesses in this detailed guide, offering insights, strategies, and practical tips to enhance your understanding and application of the topic."
|
||||
title: "Zero Trust Security: How to Roll It Out in 90 Days Without Breaking Your Budget (2025 Guide)"
|
||||
description: "Step-by-step Zero Trust security implementation for 2025. Real costs, tools, and a 90-day roadmap that actually works for remote teams and cloud apps."
|
||||
date: 2025-04-26
|
||||
tags: ["zero", "trust", "security", "ultimate", "guide", "businesses"]
|
||||
authors: ["Cojocaru David", "ChatGPT"]
|
||||
tags:
|
||||
- "zero trust security"
|
||||
- "step by step zero trust"
|
||||
- "cybersecurity roadmap"
|
||||
- "remote work security"
|
||||
- "cloud security"
|
||||
- "small business zero trust"
|
||||
- "mfa setup"
|
||||
- "micro-segmentation guide"
|
||||
authors:
|
||||
- "Cojocaru David"
|
||||
- "ChatGPT"
|
||||
slug: "zero-trust-security-step-by-step-implementation-guide-2025"
|
||||
updatedDate: 2025-08-13
|
||||
---
|
||||
|
||||
# Zero Trust Security: The Ultimate Guide for Businesses
|
||||
Hey friend, ever feel like your office firewall is just a fancy welcome mat for hackers? Last month, a buddy of mine watched a fake Zoom invite steal every password in his 50-person startup. **Ouch.** That’s why we’re talking Zero Trust today.
|
||||
|
||||
In today’s rapidly evolving digital landscape, traditional security models are no longer sufficient to protect businesses from sophisticated cyber threats. **Zero Trust Security: The Ultimate Guide for Businesses** explores this modern approach to cybersecurity, which operates on the principle of "never trust, always verify." Whether you're a small business or a large enterprise, adopting Zero Trust can significantly reduce your risk of breaches and data loss.
|
||||
Here’s the deal. By the end of this guide you’ll know **exactly** how to:
|
||||
* ditch the old “castle-and-moat” mindset
|
||||
* protect laptops, phones, and cloud apps **without** buying a spaceship
|
||||
* finish in 90 days—budget friendly, user-friendly, CFO-approved
|
||||
|
||||
This guide will break down what Zero Trust is, why it matters, and how to implement it effectively.
|
||||
Ready? Grab your coffee. Let’s go.
|
||||
|
||||
## What Is Zero Trust Security?
|
||||
## What Zero Trust Really Means (Spoiler: It’s Not Paranoia)
|
||||
|
||||
Zero Trust Security is a framework that eliminates the concept of trust from an organization’s network architecture. Unlike traditional models that assume everything inside a network is safe, Zero Trust requires continuous verification of every user, device, and application—regardless of location.
|
||||
Zero Trust boils down to **“never trust, always verify.”** Think of it like a nightclub bouncer who checks your ID **every single time** you go to the bathroom—even if he just stamped your hand.
|
||||
|
||||
### Core Principles of Zero Trust
|
||||
- **Least Privilege Access:** Grant users only the permissions they need.
|
||||
- **Micro-Segmentation:** Divide networks into smaller, isolated zones to limit lateral movement.
|
||||
- **Continuous Monitoring:** Constantly validate security configurations and user behavior.
|
||||
- **Multi-Factor Authentication (MFA):** Require multiple forms of verification before granting access.
|
||||
**Traditional Model**: Inside the building = safe
|
||||
**Zero Trust Model**: Every click, tap, or download gets a fresh ID check, even if you’re the CEO on your own laptop.
|
||||
|
||||
## Why Businesses Need Zero Trust Security
|
||||
### Quick Head-to-Head
|
||||
|
||||
Cyberattacks are becoming more frequent and sophisticated, with ransomware, phishing, and insider threats posing significant risks. Here’s why Zero Trust is essential:
|
||||
| Old Way | Zero Trust Way |
|
||||
|---|---|
|
||||
| VPN lets anyone inside | Every request is verified |
|
||||
| One password to rule them all | MFA + device health checks |
|
||||
| Flat network (like a big open office) | Micro-segments (private cubicles with locks) |
|
||||
|
||||
- **Rise of Remote Work:** Employees accessing systems from various locations increase vulnerability.
|
||||
- **Cloud Adoption:** Data stored across multiple cloud services requires stricter access controls.
|
||||
- **Regulatory Compliance:** Zero Trust helps meet GDPR, HIPAA, and other data protection standards.
|
||||
## Why Zero Trust Matters in 2025 (Real Numbers)
|
||||
|
||||
> *"Trust is a vulnerability. Zero Trust is the solution."* — John Kindervag, Creator of Zero Trust
|
||||
**Quick stats that keep me up at night:**
|
||||
* Ransomware hits **every 11 seconds** (Cybersecurity Ventures)
|
||||
* **83 % of breaches** start with stolen or weak passwords (Verizon DBIR)
|
||||
* Average company now juggles **1,295 cloud apps** (Netskope). Firewalls? They can’t even see most of them.
|
||||
|
||||
## Key Components of a Zero Trust Framework
|
||||
Oh, and your team? **70 % works remotely at least part-time.** VPNs buckle under that load. Zero Trust doesn’t flinch.
|
||||
|
||||
Implementing Zero Trust involves multiple layers of security. Here are the critical components:
|
||||
## The 5-Layer Stack You Actually Need
|
||||
|
||||
### 1. Identity Verification
|
||||
- Use MFA and biometric authentication.
|
||||
- Implement role-based access control (RBAC).
|
||||
Let’s cut the fluff. You need five things. That’s it.
|
||||
|
||||
### 2. Device Security
|
||||
- Ensure all devices meet security standards before granting access.
|
||||
- Regularly update and patch software.
|
||||
### 1. Identity & Access Management (IAM)
|
||||
- **MFA everywhere**—start with free Microsoft Authenticator or Google Authenticator
|
||||
- **Single Sign-On**—one password, many apps (Azure AD, Okta, JumpCloud)
|
||||
- **Context rules**—block logins from North Korea at 3 a.m. when you’re in Texas
|
||||
|
||||
### 3. Network Segmentation
|
||||
- Isolate critical systems to prevent lateral attacks.
|
||||
- Encrypt all data in transit and at rest.
|
||||
### 2. Device Health
|
||||
- **Auto-patch** Windows, macOS, iOS via Intune or Jamf
|
||||
- **Endpoint Detection**—CrowdStrike, SentinelOne, or the free Windows Defender if cash is tight
|
||||
- **Certificate check**—only company-issued laptops get in
|
||||
|
||||
## Steps to Implement Zero Trust Security
|
||||
### 3. Network Micro-Segmentation
|
||||
- **Start small**—separate finance servers from marketing Wi-Fi
|
||||
- **Use what you have**—VLANs, AWS Security Groups, Azure NSGs
|
||||
- **Upgrade later** to fancy SDP tools like Zscaler or Cloudflare One
|
||||
|
||||
Transitioning to Zero Trust doesn’t happen overnight. Follow these actionable steps:
|
||||
### 4. Data Protection
|
||||
- **Label & encrypt** your top 20 % of sensitive files (Microsoft Purview, free tier)
|
||||
- **DLP rules**—block anyone from emailing credit-card spreadsheets to Gmail
|
||||
- **BYOK** (Bring Your Own Key) if auditors start asking questions
|
||||
|
||||
1. **Assess Your Current Security Posture:** Identify vulnerabilities and gaps.
|
||||
2. **Define Access Policies:** Establish strict rules for who can access what.
|
||||
3. **Deploy Zero Trust Technologies:** Invest in tools like identity-aware proxies and endpoint detection.
|
||||
4. **Train Employees:** Educate staff on security best practices.
|
||||
5. **Monitor and Adapt:** Continuously refine policies based on threats.
|
||||
### 5. Continuous Monitoring
|
||||
- **SIEM**—free options: Wazuh, Elastic, or Microsoft Sentinel trial
|
||||
- **SOAR playbooks**—auto-isolate a laptop that starts talking to Russia
|
||||
- **Quarterly policy tune-up**—apps change, threats evolve, so should you
|
||||
|
||||
## Challenges and How to Overcome Them
|
||||
## The 90-Day Zero Trust Roadmap (Steal This)
|
||||
|
||||
While Zero Trust offers robust protection, businesses may face hurdles:
|
||||
### Days 1-14: Discovery (The Awkward Truth Phase)
|
||||
1. **Run Lansweeper or AssetTiger**—grab every laptop, phone, and forgotten server
|
||||
2. **List your “crown jewels”**—customer DB, finance drive, that one Excel sheet with all the passwords
|
||||
3. **Quick NIST 800-207 gap quiz**—Microsoft has a free 5-minute tool, thank me later
|
||||
|
||||
- **Complexity:** Start with a phased approach, focusing on high-risk areas first.
|
||||
- **User Resistance:** Communicate the benefits and provide training.
|
||||
- **Cost:** Prioritize investments based on critical assets.
|
||||
### Days 15-30: Identity Lockdown
|
||||
- **Turn on MFA** for admins first, then roll to everyone
|
||||
- **Migrate top 5 SaaS apps** to SSO (Slack, Google Workspace, Zoom)
|
||||
- **Create three roles**—Admin, Standard, Guest—done
|
||||
|
||||
## Conclusion
|
||||
### Days 31-50: Device Hardening
|
||||
- **Force auto-updates** via Intune or SimpleMDM
|
||||
- **Install EDR**—even Windows Defender + cloud analytics works
|
||||
- **Block jailbroken phones**—conditional access policies, two clicks in Azure
|
||||
|
||||
**Zero Trust Security: The Ultimate Guide for Businesses** highlights why this model is no longer optional—it’s a necessity. By adopting Zero Trust, organizations can better protect sensitive data, comply with regulations, and mitigate evolving cyber threats.
|
||||
### Days 51-70: Network Segmentation Lite
|
||||
- **Finance VLAN**—only finance PCs can talk to the ERP server
|
||||
- **Test with RDP**—make sure HR can’t accidentally open QuickBooks
|
||||
- **Log everything** to your free SIEM
|
||||
|
||||
Start small, stay consistent, and remember: in cybersecurity, trust is a liability.
|
||||
### Days 71-90: Monitor & Polish
|
||||
- **Run a phishing test**—KnowBe4 or free Google tool
|
||||
- **Create playbooks**—if laptop talks to bad IP, auto-isolate
|
||||
- **Celebrate**—pizza budget: $200. Breach cost: $4.45 million (IBM). You just saved a fortune.
|
||||
|
||||
> *"The only secure network is the one that’s never been attacked—until it has. Zero Trust ensures you’re prepared."* — Cybersecurity Expert
|
||||
## Budget Breakdown (Real Talk)
|
||||
|
||||
| Item | Small Biz (1-50 users) | Mid-Market (50-500) |
|
||||
|---|---|---|
|
||||
| **IAM** | JumpCloud $2/user | Okta $6/user |
|
||||
| **EDR** | Windows Defender free | CrowdStrike $8/user |
|
||||
| **ZTNA** | Cloudflare One free tier | Zscaler $12/user |
|
||||
| **SIEM** | Wazuh open-source | Microsoft Sentinel pay-as-you-go |
|
||||
|
||||
**Typical 90-day spend for 100 users: $1,200-$3,000.** Compare that to **one** ransomware incident at $4.45 million. Easy math.
|
||||
|
||||
## Common Speed Bumps (And How to Hop Over Them)
|
||||
|
||||
- **“Users will revolt!”**
|
||||
Run a 2-minute demo showing them passwordless sign-in with Windows Hello. They’ll ask for it.
|
||||
|
||||
- **“Legacy apps!”**
|
||||
Use a simple identity-aware proxy (IAP) like Azure AD App Proxy. Zero code changes.
|
||||
|
||||
- **“No budget!”**
|
||||
Start with Microsoft 365 E3 trial, layer on free Cloudflare tunnels. Upgrade later.
|
||||
|
||||
- **“Too complex!”**
|
||||
Pilot with one department—say, accounting—then copy-paste the settings.
|
||||
|
||||
## Mini Case Study: 30-Person Design Agency in Austin
|
||||
|
||||
**Timeline**
|
||||
- **Week 1**: AssetTiger found 47 devices and 3 forgotten AWS buckets
|
||||
- **Week 2**: Rolled out Google Workspace SSO + free Google Authenticator MFA
|
||||
- **Week 4**: Moved from VPN to Cloudflare ZTNA; support tickets dropped 35 %
|
||||
- **Week 6**: Micro-segmented client design files with AWS S3 bucket policies
|
||||
- **Week 8**: Passed SOC 2 audit two months early, landed a Fortune 500 client
|
||||
|
||||
Total spend: **$1,147** for three months. ROI? They sleep better and charge higher rates. **Priceless.**
|
||||
|
||||
## Quick-Start Checklist (Print & Pin)
|
||||
|
||||
- [ ] MFA enabled for every single account
|
||||
- [ ] Top 5 cloud apps on single sign-on
|
||||
- [ ] Auto-patching turned on for all laptops
|
||||
- [ ] Finance and HR servers on separate VLANs
|
||||
- [ ] One phishing simulation sent this quarter
|
||||
- [ ] Incident response runbook tested once (even if it’s just you and Slack)
|
||||
|
||||
## FAQ Lightning Round
|
||||
|
||||
**Q: How long until I see benefits?**
|
||||
A: Day 1 after MFA rollout. Seriously, you’ll wake up to zero fake-login alerts.
|
||||
|
||||
**Q: Does Zero Trust slow users down?**
|
||||
A: Passwordless sign-in is actually faster than typing “FluffyBunny2024!” every morning.
|
||||
|
||||
**Q: What if I only have on-prem servers?**
|
||||
A: Install Azure AD App Proxy or Cloudflare Tunnel. Takes 15 minutes, no firewall rules needed.
|
||||
|
||||
## Your Next 15 Minutes
|
||||
|
||||
1. **Download the free Microsoft Assessment Tool**—5 minutes
|
||||
2. **Enable MFA on your own admin account**—3 minutes
|
||||
3. **Schedule a 30-minute team huddle**—7 minutes to share this roadmap
|
||||
|
||||
That’s it. You’re already 10 % done.
|
||||
|
||||
> _"The best time to plant a tree was 20 years ago. The second best time is today."_ — Old proverb, still true for cybersecurity.
|
||||
|
||||
#ZeroTrustSecurity #90DayPlan #RemoteWorkSecurity
|
||||
@@ -7,7 +7,6 @@ import Head from '@/components/Head.astro'
|
||||
import Navbar from '@/components/react/navbar'
|
||||
import { SITE } from '@/consts'
|
||||
import { cn } from '@/lib/utils'
|
||||
import Posthog from '@/components/Posthog.astro'
|
||||
|
||||
const {
|
||||
isWide = false
|
||||
@@ -19,11 +18,10 @@ const {
|
||||
<Head>
|
||||
<slot name="head" />
|
||||
<script is:inline src="https://analytics.ahrefs.com/analytics.js" data-key="+FHMgRP7/Duxaq5D0gZtJw" async></script>
|
||||
<link rel="sitemap" href="/sitemap.xml" />
|
||||
|
||||
<!-- Preload critical resources -->
|
||||
<link rel="preload" href="/fonts/GeistVF.woff2" as="font" type="font/woff2" crossorigin />
|
||||
<link rel="preload" href="/fonts/GeistMonoVF.woff2" as="font" type="font/woff2" crossorigin />
|
||||
<link rel="preload" href="/fonts/ClashDisplay/ClashDisplay-Semibold.woff2" as="font" type="font/woff2" crossorigin />
|
||||
<link rel="preload" href="/fonts/Lexend/Lexend-Regular.woff2" as="font" type="font/woff2" crossorigin />
|
||||
|
||||
<!-- DNS prefetch for external resources -->
|
||||
<link rel="dns-prefetch" href="//analytics.ahrefs.com" />
|
||||
@@ -51,7 +49,6 @@ const {
|
||||
})();
|
||||
</script>
|
||||
<link rel="alternate" type="application/rss+xml" title="RSS Feed" href="/rss.xml" />
|
||||
<Posthog />
|
||||
</Head>
|
||||
<body>
|
||||
<div class="flex h-fit min-h-screen w-full flex-col gap-y-4 sm:gap-y-6 font-sans">
|
||||
@@ -68,15 +65,5 @@ const {
|
||||
</main>
|
||||
<Footer />
|
||||
</div>
|
||||
<script is:inline async src="https://www.googletagmanager.com/gtag/js?id=G-QVK59XQK72"
|
||||
></script>
|
||||
<script is:inline>
|
||||
window.dataLayer = window.dataLayer || [];
|
||||
function gtag() {
|
||||
dataLayer.push(arguments);
|
||||
}
|
||||
gtag("js", new Date());
|
||||
gtag("config", "G-QVK59XQK72");
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -7,10 +7,10 @@ import fs from 'fs'
|
||||
import path from 'path'
|
||||
|
||||
const MontserratRegular = fs.readFileSync(
|
||||
path.resolve('./public/fonts/_montserrat_regular.ttf'),
|
||||
path.resolve('./public/fonts2/_montserrat_regular.ttf'),
|
||||
)
|
||||
const MontserratBold = fs.readFileSync(
|
||||
path.resolve('./public/fonts/_montserrat_bold.ttf'),
|
||||
path.resolve('./public/fonts2/_montserrat_bold.ttf'),
|
||||
)
|
||||
|
||||
const dimensions = {
|
||||
@@ -151,7 +151,7 @@ export async function GET(context: APIContext) {
|
||||
|
||||
const pngData = image.asPng()
|
||||
|
||||
return new Response(pngData, {
|
||||
return new Response(pngData as any, {
|
||||
headers: {
|
||||
'Content-Type': 'image/png',
|
||||
'Content-Disposition': 'inline; filename="social-card.png"',
|
||||
|
||||
@@ -13,15 +13,14 @@ Disallow: /temp/
|
||||
# Crawl delay for better server performance
|
||||
Crawl-delay: 1
|
||||
|
||||
# Sitemap location
|
||||
# Sitemap location (single canonical sitemap)
|
||||
Sitemap: ${sitemapURL.href}
|
||||
Sitemap: ${new URL('sitemap-index.xml', SITE.href).href}
|
||||
|
||||
# Additional information
|
||||
# Host: ${SITE.href}
|
||||
`
|
||||
|
||||
export const GET: APIRoute = ({ site }) => {
|
||||
const sitemapURL = new URL('sitemap.xml', site)
|
||||
const sitemapURL = new URL('sitemap-index.xml', site)
|
||||
return new Response(getRobotsTxt(sitemapURL))
|
||||
}
|
||||
|
||||
@@ -1,87 +1,7 @@
|
||||
import { SITE } from '@/consts'
|
||||
import type { APIContext } from 'astro'
|
||||
import { getAllPosts, getAllProjects, getAllTags } from '@/lib/data-utils'
|
||||
|
||||
export async function GET(context: APIContext) {
|
||||
try {
|
||||
const posts = await getAllPosts()
|
||||
const projects = await getAllProjects()
|
||||
const tags = await getAllTags()
|
||||
const site = context.site ?? SITE.href
|
||||
const baseUrl = site.toString().endsWith('/') ? site.toString().slice(0, -1) : site.toString()
|
||||
|
||||
const staticPages = [
|
||||
{
|
||||
url: `${baseUrl}/`,
|
||||
lastmod: new Date().toISOString(),
|
||||
changefreq: 'daily',
|
||||
priority: '1.0'
|
||||
},
|
||||
{
|
||||
url: `${baseUrl}/projects/`,
|
||||
lastmod: new Date().toISOString(),
|
||||
changefreq: 'weekly',
|
||||
priority: '0.8'
|
||||
},
|
||||
{
|
||||
url: `${baseUrl}/blog/`,
|
||||
lastmod: posts.length > 0 ? posts[0].data.date.toISOString() : new Date().toISOString(),
|
||||
changefreq: 'daily',
|
||||
priority: '0.9'
|
||||
},
|
||||
{
|
||||
url: `${baseUrl}/tags/`,
|
||||
lastmod: new Date().toISOString(),
|
||||
changefreq: 'weekly',
|
||||
priority: '0.6'
|
||||
}
|
||||
]
|
||||
|
||||
const blogPosts = posts.map(post => ({
|
||||
url: `${baseUrl}/blog/${post.id}/`,
|
||||
lastmod: post.data.date.toISOString(),
|
||||
changefreq: 'monthly',
|
||||
priority: '0.7'
|
||||
}))
|
||||
|
||||
const projectPosts = projects.map(project => ({
|
||||
url: `${baseUrl}/projects/${project.id}/`,
|
||||
lastmod: (project.data.endDate ?? new Date()).toISOString(),
|
||||
changefreq: 'monthly',
|
||||
priority: '0.6'
|
||||
}))
|
||||
|
||||
const tagUrls = Array.from(tags, ([tag, _]) => ({
|
||||
url: `${baseUrl}/tags/${tag}/`,
|
||||
lastmod: new Date().toISOString(),
|
||||
changefreq: 'weekly',
|
||||
priority: '0.5'
|
||||
}))
|
||||
|
||||
const allUrls = [...staticPages, ...projectPosts, ...blogPosts, ...tagUrls]
|
||||
|
||||
const xml = `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd">
|
||||
${allUrls
|
||||
.map(
|
||||
page => ` <url>
|
||||
<loc>${page.url}</loc>
|
||||
<lastmod>${page.lastmod}</lastmod>
|
||||
<changefreq>${page.changefreq}</changefreq>
|
||||
<priority>${page.priority}</priority>
|
||||
</url>`
|
||||
)
|
||||
.join('\n')}
|
||||
</urlset>`
|
||||
|
||||
return new Response(xml, {
|
||||
headers: {
|
||||
'Content-Type': 'application/xml',
|
||||
'Cache-Control': 'public, max-age=3600'
|
||||
}
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('Error generating sitemap:', error)
|
||||
return new Response('Error generating sitemap', { status: 500 })
|
||||
}
|
||||
// Deprecated custom sitemap. Rely on @astrojs/sitemap integration to avoid duplicates.
|
||||
export async function GET() {
|
||||
return new Response('Moved to sitemap-index.xml', {
|
||||
status: 301,
|
||||
headers: { Location: '/sitemap-index.xml' }
|
||||
})
|
||||
}
|
||||
@@ -15,6 +15,7 @@ const currentUrl = Astro.url;
|
||||
<PageHead slot="head" title="Tags" />
|
||||
<Breadcrumbs items={[{ label: 'Tags', icon: 'lucide:tags' }]} />
|
||||
|
||||
<h1 class="text-3xl font-bold mb-4">Tags</h1>
|
||||
<div class="flex flex-col gap-4">
|
||||
<div class="flex flex-wrap gap-2">
|
||||
{
|
||||
|
||||
@@ -3,34 +3,25 @@
|
||||
@custom-variant dark (&:is(.dark *));
|
||||
|
||||
@font-face {
|
||||
font-family: 'Geist';
|
||||
src: url('/fonts/GeistVF.woff2') format('woff2-variations');
|
||||
font-weight: 100 900;
|
||||
font-family: 'ClashDisplay';
|
||||
src: url('/fonts/ClashDisplay/ClashDisplay-Semibold.woff2') format('woff2'),
|
||||
url('/fonts/ClashDisplay/ClashDisplay-Semibold.woff') format('woff');
|
||||
font-weight: bold;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Geist Mono';
|
||||
src: url('/fonts/GeistMonoVF.woff2') format('woff2-variations');
|
||||
font-weight: 100 900;
|
||||
font-family: 'Lexend';
|
||||
src: url('/fonts/Lexend/Lexend-Regular.woff2') format('woff2'),
|
||||
url('/fonts/Lexend/Lexend-Regular.woff') format('woff');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'ClashDisplay-Semibold';
|
||||
src:
|
||||
url('/fonts/ClashDisplay-Semibold.woff2') format('woff2'),
|
||||
url('/fonts/ClashDisplay-Semibold.woff') format('woff'),
|
||||
url('/fonts/ClashDisplay-Semibold.ttf') format('truetype');
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
.font-custom {
|
||||
font-family: 'ClashDisplay-Semibold';
|
||||
font-family: 'ClashDisplay', sans-serif;
|
||||
}
|
||||
|
||||
:root {
|
||||
@@ -68,9 +59,7 @@
|
||||
|
||||
--radius: 0.5rem;
|
||||
|
||||
--font-sans:
|
||||
Geist, ui-sans-serif, system-ui, sans-serif, 'Apple Color Emoji',
|
||||
'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';
|
||||
--font-sans: "Lexend", sans-serif;
|
||||
--font-mono:
|
||||
Geist Mono, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
|
||||
'Liberation Mono', 'Courier New', monospace;
|
||||
|
||||
Reference in New Issue
Block a user