mirror of
https://github.com/OpenStaticFish/MicroServices.git
synced 2026-10-13 06:35:12 +00:00
Integrate Doppler secrets
This commit is contained in:
@@ -16,6 +16,7 @@ All project CLI dependencies are provided by `flake.nix`, including:
|
|||||||
- `docker`
|
- `docker`
|
||||||
- `helm`
|
- `helm`
|
||||||
- `go`
|
- `go`
|
||||||
|
- `doppler`
|
||||||
|
|
||||||
## Quick Start
|
## Quick Start
|
||||||
|
|
||||||
@@ -71,6 +72,7 @@ tilt up
|
|||||||
tilt down
|
tilt down
|
||||||
kubectl get pods
|
kubectl get pods
|
||||||
docker ps
|
docker ps
|
||||||
|
doppler secrets
|
||||||
```
|
```
|
||||||
|
|
||||||
## Project Commands
|
## Project Commands
|
||||||
@@ -227,11 +229,31 @@ The response includes:
|
|||||||
|
|
||||||
CDNs can hide the real origin. If a site is proxied through Cloudflare, the public DNS and IPs usually identify Cloudflare rather than the origin host. Origin detection is therefore best-effort and depends on leaked signals such as CSP entries, headers, HTML references, or provider-specific domains.
|
CDNs can hide the real origin. If a site is proxied through Cloudflare, the public DNS and IPs usually identify Cloudflare rather than the origin host. Origin detection is therefore best-effort and depends on leaked signals such as CSP entries, headers, HTML references, or provider-specific domains.
|
||||||
|
|
||||||
|
## Secrets
|
||||||
|
|
||||||
|
Runtime secrets are managed with Doppler in the `openstaticfish-microservices` project using the `dev` config.
|
||||||
|
|
||||||
|
Initial setup after cloning:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
doppler login
|
||||||
|
doppler setup --no-interactive
|
||||||
|
```
|
||||||
|
|
||||||
|
Run commands with secrets injected:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
doppler run -- your-command
|
||||||
|
```
|
||||||
|
|
||||||
|
Tilt uses Doppler to create the local Kubernetes `webshare-api` secret for the scraper service. Do not commit local `.env` files; `.env` and `.env.*` are ignored by Git.
|
||||||
|
|
||||||
## Layout
|
## Layout
|
||||||
|
|
||||||
```text
|
```text
|
||||||
.
|
.
|
||||||
├── flake.nix
|
├── flake.nix
|
||||||
|
├── doppler.yaml
|
||||||
├── Tiltfile
|
├── Tiltfile
|
||||||
├── k8s/
|
├── k8s/
|
||||||
│ ├── scraper.yaml
|
│ ├── scraper.yaml
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ k8s_yaml('./k8s/site-analyzer.yaml')
|
|||||||
|
|
||||||
local_resource(
|
local_resource(
|
||||||
'webshare-secret',
|
'webshare-secret',
|
||||||
cmd='set -a; [ ! -f .env ] || . ./.env; set +a; if [ -n "$WEBSHARE_API_KEY" ] && [ -n "$WEBSHARE_PROXY_USERNAME" ] && [ -n "$WEBSHARE_PROXY_PASSWORD" ]; then kubectl create secret generic webshare-api --from-literal=api-key="$WEBSHARE_API_KEY" --from-literal=proxy-username="$WEBSHARE_PROXY_USERNAME" --from-literal=proxy-password="$WEBSHARE_PROXY_PASSWORD" --dry-run=client -o yaml | kubectl apply -f -; else echo "Webshare runtime config missing; country scraping unavailable"; fi',
|
cmd='doppler run -- bash ./scripts/apply-webshare-secret',
|
||||||
labels=['utility'],
|
labels=['utility'],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
setup:
|
||||||
|
- project: openstaticfish-microservices
|
||||||
|
config: dev
|
||||||
@@ -36,6 +36,9 @@
|
|||||||
docker
|
docker
|
||||||
docker-compose
|
docker-compose
|
||||||
|
|
||||||
|
# --- Secrets Management ---
|
||||||
|
doppler
|
||||||
|
|
||||||
# --- Language Toolchain ---
|
# --- Language Toolchain ---
|
||||||
go
|
go
|
||||||
|
|
||||||
@@ -46,12 +49,6 @@
|
|||||||
];
|
];
|
||||||
|
|
||||||
shellHook = ''
|
shellHook = ''
|
||||||
if [ -f .env ]; then
|
|
||||||
set -a
|
|
||||||
. ./.env
|
|
||||||
set +a
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "''${OPENSTATICFISH_QUIET_SHELL:-}" != "1" ]; then
|
if [ "''${OPENSTATICFISH_QUIET_SHELL:-}" != "1" ]; then
|
||||||
echo "🐟 OpenStaticFish MicroServices Dev Shell"
|
echo "🐟 OpenStaticFish MicroServices Dev Shell"
|
||||||
echo "-----------------------------------------"
|
echo "-----------------------------------------"
|
||||||
@@ -101,6 +98,7 @@
|
|||||||
echo " tilt down - Stop dev environment"
|
echo " tilt down - Stop dev environment"
|
||||||
echo " kubectl ... - Interact with k8s cluster"
|
echo " kubectl ... - Interact with k8s cluster"
|
||||||
echo " docker ... - Interact with containers"
|
echo " docker ... - Interact with containers"
|
||||||
|
echo " doppler ... - Manage project secrets"
|
||||||
echo ""
|
echo ""
|
||||||
fi
|
fi
|
||||||
'';
|
'';
|
||||||
|
|||||||
Executable
+13
@@ -0,0 +1,13 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
if [ -n "${WEBSHARE_API_KEY:-}" ] && [ -n "${WEBSHARE_PROXY_USERNAME:-}" ] && [ -n "${WEBSHARE_PROXY_PASSWORD:-}" ]; then
|
||||||
|
kubectl create secret generic webshare-api \
|
||||||
|
--from-literal=api-key="$WEBSHARE_API_KEY" \
|
||||||
|
--from-literal=proxy-username="$WEBSHARE_PROXY_USERNAME" \
|
||||||
|
--from-literal=proxy-password="$WEBSHARE_PROXY_PASSWORD" \
|
||||||
|
--dry-run=client \
|
||||||
|
-o yaml | kubectl apply -f -
|
||||||
|
else
|
||||||
|
echo "Webshare runtime config missing; country scraping unavailable"
|
||||||
|
fi
|
||||||
@@ -193,12 +193,16 @@ func analyzeDNS(domain string, dnsInfo *DNSInfo, hosting *HostingInfo) error {
|
|||||||
firstErr = err
|
firstErr = err
|
||||||
}
|
}
|
||||||
|
|
||||||
if nsRecords, err := net.LookupNS(domain); err == nil {
|
nsDomain, nsRecords, err := lookupNameservers(domain)
|
||||||
|
if err == nil {
|
||||||
for _, ns := range nsRecords {
|
for _, ns := range nsRecords {
|
||||||
dnsInfo.Nameservers = append(dnsInfo.Nameservers, strings.TrimSuffix(strings.ToLower(ns.Host), "."))
|
dnsInfo.Nameservers = append(dnsInfo.Nameservers, strings.TrimSuffix(strings.ToLower(ns.Host), "."))
|
||||||
}
|
}
|
||||||
dnsInfo.Nameservers = sortedUnique(dnsInfo.Nameservers)
|
dnsInfo.Nameservers = sortedUnique(dnsInfo.Nameservers)
|
||||||
dnsInfo.Provider = detectDNSProvider(dnsInfo.Nameservers)
|
dnsInfo.Provider = detectDNSProvider(dnsInfo.Nameservers)
|
||||||
|
if nsDomain != domain {
|
||||||
|
dnsInfo.Records["NS_DOMAIN"] = []string{nsDomain}
|
||||||
|
}
|
||||||
} else if firstErr == nil {
|
} else if firstErr == nil {
|
||||||
firstErr = err
|
firstErr = err
|
||||||
}
|
}
|
||||||
@@ -219,6 +223,20 @@ func analyzeDNS(domain string, dnsInfo *DNSInfo, hosting *HostingInfo) error {
|
|||||||
return firstErr
|
return firstErr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func lookupNameservers(domain string) (string, []*net.NS, error) {
|
||||||
|
labels := strings.Split(domain, ".")
|
||||||
|
var lastErr error
|
||||||
|
for i := 0; i <= len(labels)-2; i++ {
|
||||||
|
candidate := strings.Join(labels[i:], ".")
|
||||||
|
records, err := net.LookupNS(candidate)
|
||||||
|
if err == nil && len(records) > 0 {
|
||||||
|
return candidate, records, nil
|
||||||
|
}
|
||||||
|
lastErr = err
|
||||||
|
}
|
||||||
|
return "", nil, lastErr
|
||||||
|
}
|
||||||
|
|
||||||
func fetchSite(targetURL string) (*fetchedSite, error) {
|
func fetchSite(targetURL string) (*fetchedSite, error) {
|
||||||
client := &http.Client{Timeout: 20 * time.Second}
|
client := &http.Client{Timeout: 20 * time.Second}
|
||||||
req, err := http.NewRequest(http.MethodGet, targetURL, nil)
|
req, err := http.NewRequest(http.MethodGet, targetURL, nil)
|
||||||
@@ -287,8 +305,19 @@ func detectTechnologies(site *fetchedSite) TechnologyInfo {
|
|||||||
tech.CMS = "Shopify"
|
tech.CMS = "Shopify"
|
||||||
detected["Shopify"] = true
|
detected["Shopify"] = true
|
||||||
}
|
}
|
||||||
|
if strings.Contains(lowerHTML, "/umbraco/") || strings.Contains(lowerHTML, "umb-client") || strings.Contains(lowerHTML, "umbracoforms") {
|
||||||
|
tech.CMS = "Umbraco"
|
||||||
|
tech.ProgrammingLanguage = ".NET"
|
||||||
|
detected["Umbraco"] = true
|
||||||
|
detected[".NET"] = true
|
||||||
|
}
|
||||||
|
|
||||||
if strings.Contains(lowerHTML, "__next_data__") || strings.Contains(lowerHTML, "/_next/static/") || site.Header.Get("X-Nextjs-Cache") != "" {
|
if strings.Contains(lowerHTML, "<!--blazor:") || strings.Contains(lowerHTML, "_framework/blazor") || strings.Contains(lowerHTML, "blazor.server.js") {
|
||||||
|
tech.Framework = "Blazor"
|
||||||
|
tech.ProgrammingLanguage = ".NET"
|
||||||
|
detected["Blazor"] = true
|
||||||
|
detected[".NET"] = true
|
||||||
|
} else if strings.Contains(lowerHTML, "__next_data__") || strings.Contains(lowerHTML, "/_next/static/") || site.Header.Get("X-Nextjs-Cache") != "" {
|
||||||
tech.Framework = "Next.js"
|
tech.Framework = "Next.js"
|
||||||
detected["Next.js"] = true
|
detected["Next.js"] = true
|
||||||
detected["React"] = true
|
detected["React"] = true
|
||||||
@@ -309,6 +338,9 @@ func detectTechnologies(site *fetchedSite) TechnologyInfo {
|
|||||||
tech.Framework = "SvelteKit"
|
tech.Framework = "SvelteKit"
|
||||||
detected["SvelteKit"] = true
|
detected["SvelteKit"] = true
|
||||||
}
|
}
|
||||||
|
if strings.Contains(lowerHTML, "_content/mudblazor/") || strings.Contains(lowerHTML, "mudblazor") {
|
||||||
|
detected["MudBlazor"] = true
|
||||||
|
}
|
||||||
|
|
||||||
doc, err := goquery.NewDocumentFromReader(strings.NewReader(string(site.Body)))
|
doc, err := goquery.NewDocumentFromReader(strings.NewReader(string(site.Body)))
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@@ -323,7 +355,7 @@ func detectTechnologies(site *fetchedSite) TechnologyInfo {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, header := range []string{"CF-Ray", "X-Vercel-ID", "X-GitHub-Request-Id", "Fly-Request-Id"} {
|
for _, header := range []string{"CF-Ray", "X-Vercel-ID", "X-GitHub-Request-Id", "Fly-Request-Id", "X-Coolify"} {
|
||||||
if site.Header.Get(header) != "" {
|
if site.Header.Get(header) != "" {
|
||||||
detected[detectTechnologyFromHeader(header)] = true
|
detected[detectTechnologyFromHeader(header)] = true
|
||||||
}
|
}
|
||||||
@@ -405,6 +437,16 @@ func detectOriginProvider(site *fetchedSite) (string, []string) {
|
|||||||
}
|
}
|
||||||
return "Pantheon", evidence
|
return "Pantheon", evidence
|
||||||
}
|
}
|
||||||
|
if strings.Contains(lowerHTML, "coolify") || strings.Contains(lowerHeaders, "coolify") {
|
||||||
|
var evidence []string
|
||||||
|
if strings.Contains(lowerHTML, "coolify") {
|
||||||
|
evidence = append(evidence, "html mentions coolify")
|
||||||
|
}
|
||||||
|
if strings.Contains(lowerHeaders, "coolify") {
|
||||||
|
evidence = append(evidence, "response headers mention coolify")
|
||||||
|
}
|
||||||
|
return "Coolify", evidence
|
||||||
|
}
|
||||||
|
|
||||||
return "", nil
|
return "", nil
|
||||||
}
|
}
|
||||||
@@ -471,6 +513,8 @@ func detectTechnologyFromHeader(header string) string {
|
|||||||
return "GitHub Pages"
|
return "GitHub Pages"
|
||||||
case "Fly-Request-Id":
|
case "Fly-Request-Id":
|
||||||
return "Fly.io"
|
return "Fly.io"
|
||||||
|
case "X-Coolify":
|
||||||
|
return "Coolify"
|
||||||
default:
|
default:
|
||||||
return header
|
return header
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user