granny-devops 0.4.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. granny/__init__.py +19 -0
  2. granny/analyze/__init__.py +6 -0
  3. granny/analyze/lambdas.py +59 -0
  4. granny/analyze/vpcs.py +57 -0
  5. granny/cdn/__init__.py +9 -0
  6. granny/cdn/bunny.py +231 -0
  7. granny/cli/__init__.py +0 -0
  8. granny/cli/analyze.py +66 -0
  9. granny/cli/cdn.py +210 -0
  10. granny/cli/create.py +94 -0
  11. granny/cli/credentials.py +99 -0
  12. granny/cli/dns.py +290 -0
  13. granny/cli/docker.py +165 -0
  14. granny/cli/edge.py +106 -0
  15. granny/cli/email.py +224 -0
  16. granny/cli/main.py +98 -0
  17. granny/cli/serverless.py +278 -0
  18. granny/cli/storage.py +249 -0
  19. granny/create/__init__.py +4 -0
  20. granny/create/auto_certificate.py +1899 -0
  21. granny/create/cloudfront-security-headers.js +53 -0
  22. granny/create/manage-dns.sh +321 -0
  23. granny/create/manage_mailjet_contacts.py +619 -0
  24. granny/create/registrars.py +363 -0
  25. granny/create/setup_aws_cloudfront.py +2808 -0
  26. granny/create/setup_bunny_edge_script.py +923 -0
  27. granny/create/setup_bunny_storage.py +1719 -0
  28. granny/create/setup_cognito_identity_pool.py +740 -0
  29. granny/create/setup_hetzner_bunny.py +1482 -0
  30. granny/create/setup_mailjet_dns.py +1103 -0
  31. granny/create/setup_private_cdn.py +547 -0
  32. granny/create/setup_s3_website.py +1512 -0
  33. granny/create/setup_scaleway_faas.py +1165 -0
  34. granny/create/setup_workmail.py +1217 -0
  35. granny/create/www-redirect-function.js +17 -0
  36. granny/credentials/__init__.py +15 -0
  37. granny/credentials/secrets.py +403 -0
  38. granny/dns/__init__.py +22 -0
  39. granny/dns/base.py +113 -0
  40. granny/dns/bunny.py +150 -0
  41. granny/dns/cloudflare.py +192 -0
  42. granny/dns/cloudns.py +162 -0
  43. granny/dns/desec.py +152 -0
  44. granny/dns/factory.py +72 -0
  45. granny/dns/hetzner.py +165 -0
  46. granny/dns/manual.py +64 -0
  47. granny/dns/records.py +29 -0
  48. granny/docker/__init__.py +5 -0
  49. granny/docker/build_base.py +204 -0
  50. granny/edge/__init__.py +5 -0
  51. granny/edge/bunny.py +147 -0
  52. granny/email/__init__.py +7 -0
  53. granny/email/mailjet.py +119 -0
  54. granny/email/mailjet_contacts.py +115 -0
  55. granny/email/ses_forwarding.py +281 -0
  56. granny/email/workmail.py +145 -0
  57. granny/report.py +128 -0
  58. granny/serverless/__init__.py +5 -0
  59. granny/serverless/scaleway.py +264 -0
  60. granny/storage/__init__.py +7 -0
  61. granny/storage/aws.py +113 -0
  62. granny/storage/bunny.py +98 -0
  63. granny/storage/hetzner.py +118 -0
  64. granny_devops-0.4.0.dist-info/METADATA +445 -0
  65. granny_devops-0.4.0.dist-info/RECORD +68 -0
  66. granny_devops-0.4.0.dist-info/WHEEL +4 -0
  67. granny_devops-0.4.0.dist-info/entry_points.txt +2 -0
  68. granny_devops-0.4.0.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,53 @@
1
+ function handler(event) {
2
+ var response = event.response;
3
+ var headers = response.headers;
4
+
5
+ // Strict Transport Security - Force HTTPS for 1 year
6
+ headers['strict-transport-security'] = {
7
+ value: 'max-age=31536000; includeSubDomains; preload'
8
+ };
9
+
10
+ // Content Security Policy - Allow your domains and required resources
11
+ headers['content-security-policy'] = {
12
+ value: "default-src 'self' https:; " +
13
+ "script-src 'self' 'unsafe-eval' 'unsafe-inline' https: blob:; " +
14
+ "style-src 'self' 'unsafe-inline' https:; " +
15
+ "img-src 'self' https: data: blob:; " +
16
+ "font-src 'self' https: data:; " +
17
+ "connect-src 'self' https: wss:; " +
18
+ "media-src 'self' https: blob:; " +
19
+ "object-src 'none'; " +
20
+ "base-uri 'self'; " +
21
+ "form-action 'self' https:; " +
22
+ "frame-ancestors 'none'; " +
23
+ "worker-src 'self' blob:; " +
24
+ "manifest-src 'self';"
25
+ };
26
+
27
+ // Prevent MIME type sniffing
28
+ headers['x-content-type-options'] = {
29
+ value: 'nosniff'
30
+ };
31
+
32
+ // Prevent clickjacking
33
+ headers['x-frame-options'] = {
34
+ value: 'DENY'
35
+ };
36
+
37
+ // Control referrer information
38
+ headers['referrer-policy'] = {
39
+ value: 'strict-origin-when-cross-origin'
40
+ };
41
+
42
+ // Permissions Policy - Disable sensitive APIs by default
43
+ headers['permissions-policy'] = {
44
+ value: 'geolocation=(), microphone=(), camera=(), payment=(), usb=(), magnetometer=(), gyroscope=()'
45
+ };
46
+
47
+ // X-XSS-Protection (legacy but still useful)
48
+ headers['x-xss-protection'] = {
49
+ value: '1; mode=block'
50
+ };
51
+
52
+ return response;
53
+ }
@@ -0,0 +1,321 @@
1
+ #!/bin/bash
2
+ #
3
+ # Cloudflare DNS Record Manager
4
+ # Manages DNS A records for pseekoo.io and pseekoo.com subdomains
5
+ #
6
+ # Usage:
7
+ # ./manage-dns.sh add <subdomain> # Add/update A record with auto-detected IP (pseekoo.io)
8
+ # ./manage-dns.sh add <subdomain> <ip> # Add/update A record with specific IP
9
+ # ./manage-dns.sh add <subdomain> --domain <domain> # Use specific domain
10
+ # ./manage-dns.sh delete <subdomain> # Delete A record
11
+ # ./manage-dns.sh list [--domain <domain>] # List all DNS records
12
+ # ./manage-dns.sh get <subdomain> # Get specific record details
13
+ #
14
+ # Environment:
15
+ # DOMAIN=pseekoo.com ./manage-dns.sh add foo # Alternative way to set domain
16
+ #
17
+ # Examples:
18
+ # ./manage-dns.sh add qdrant # Creates qdrant.pseekoo.io
19
+ # ./manage-dns.sh add api 1.2.3.4 # Creates api.pseekoo.io with specific IP
20
+ # ./manage-dns.sh add holylist --domain pseekoo.com # Creates holylist.pseekoo.com
21
+ # ./manage-dns.sh delete qdrant # Deletes qdrant.pseekoo.io
22
+ #
23
+
24
+ set -e
25
+
26
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
27
+ ENV_FILE="$SCRIPT_DIR/.env"
28
+ DOMAIN="${DOMAIN:-pseekoo.io}" # Default domain, can be overridden via env or --domain flag
29
+
30
+ # Colors
31
+ RED='\033[0;31m'
32
+ GREEN='\033[0;32m'
33
+ BLUE='\033[0;34m'
34
+ YELLOW='\033[0;33m'
35
+ NC='\033[0m'
36
+
37
+ # Load environment
38
+ if [[ -f "$ENV_FILE" ]]; then
39
+ source "$ENV_FILE"
40
+ else
41
+ echo -e "${RED}Error: .env file not found at $ENV_FILE${NC}"
42
+ exit 1
43
+ fi
44
+
45
+ if [[ -z "$CLOUDFLARE_API_TOKEN" ]]; then
46
+ echo -e "${RED}Error: CLOUDFLARE_API_TOKEN not set in .env${NC}"
47
+ exit 1
48
+ fi
49
+
50
+ CF_API="https://api.cloudflare.com/client/v4"
51
+
52
+ # Helper function for API calls
53
+ cf_api() {
54
+ local method="$1"
55
+ local endpoint="$2"
56
+ local data="$3"
57
+
58
+ if [[ -n "$data" ]]; then
59
+ curl -s -X "$method" "$CF_API$endpoint" \
60
+ -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \
61
+ -H "Content-Type: application/json" \
62
+ --data "$data"
63
+ else
64
+ curl -s -X "$method" "$CF_API$endpoint" \
65
+ -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \
66
+ -H "Content-Type: application/json"
67
+ fi
68
+ }
69
+
70
+ # Get zone ID for domain
71
+ get_zone_id() {
72
+ local response
73
+ response=$(cf_api GET "/zones?name=$DOMAIN")
74
+
75
+ if ! echo "$response" | jq -e '.success' > /dev/null 2>&1; then
76
+ echo -e "${RED}Error: Failed to query Cloudflare API${NC}"
77
+ echo "$response" | jq . 2>/dev/null || echo "$response"
78
+ exit 1
79
+ fi
80
+
81
+ local zone_id
82
+ zone_id=$(echo "$response" | jq -r '.result[0].id')
83
+
84
+ if [[ "$zone_id" == "null" || -z "$zone_id" ]]; then
85
+ echo -e "${RED}Error: Zone '$DOMAIN' not found${NC}"
86
+ exit 1
87
+ fi
88
+
89
+ echo "$zone_id"
90
+ }
91
+
92
+ # Get public IP of this server
93
+ get_public_ip() {
94
+ local ip
95
+ ip=$(curl -s https://api.ipify.org 2>/dev/null || \
96
+ curl -s https://ifconfig.me 2>/dev/null || \
97
+ curl -s https://icanhazip.com 2>/dev/null)
98
+
99
+ if [[ -z "$ip" ]]; then
100
+ echo -e "${RED}Error: Could not detect public IP${NC}"
101
+ exit 1
102
+ fi
103
+
104
+ echo "$ip"
105
+ }
106
+
107
+ # Get record ID by name
108
+ get_record_id() {
109
+ local zone_id="$1"
110
+ local name="$2"
111
+ local full_name="${name}.${DOMAIN}"
112
+
113
+ local response
114
+ response=$(cf_api GET "/zones/$zone_id/dns_records?type=A&name=$full_name")
115
+
116
+ echo "$response" | jq -r '.result[0].id // empty'
117
+ }
118
+
119
+ # Add or update DNS record
120
+ cmd_add() {
121
+ local subdomain="$1"
122
+ local ip="$2"
123
+
124
+ if [[ -z "$subdomain" ]]; then
125
+ echo -e "${RED}Error: Subdomain required${NC}"
126
+ echo "Usage: $0 add <subdomain> [ip]"
127
+ exit 1
128
+ fi
129
+
130
+ if [[ -z "$ip" ]]; then
131
+ echo -e "${BLUE}Detecting public IP...${NC}"
132
+ ip=$(get_public_ip)
133
+ fi
134
+
135
+ local full_name="${subdomain}.${DOMAIN}"
136
+ echo -e "${BLUE}Managing DNS record for ${full_name} -> ${ip}${NC}"
137
+
138
+ local zone_id
139
+ zone_id=$(get_zone_id)
140
+
141
+ local record_id
142
+ record_id=$(get_record_id "$zone_id" "$subdomain")
143
+
144
+ local data
145
+ data=$(jq -n \
146
+ --arg type "A" \
147
+ --arg name "$full_name" \
148
+ --arg content "$ip" \
149
+ --argjson proxied "$PROXIED" \
150
+ --argjson ttl 1 \
151
+ '{type: $type, name: $name, content: $content, proxied: $proxied, ttl: $ttl}')
152
+
153
+ local response
154
+ if [[ -n "$record_id" ]]; then
155
+ echo -e "${YELLOW}Updating existing record...${NC}"
156
+ response=$(cf_api PUT "/zones/$zone_id/dns_records/$record_id" "$data")
157
+ else
158
+ echo -e "${YELLOW}Creating new record...${NC}"
159
+ response=$(cf_api POST "/zones/$zone_id/dns_records" "$data")
160
+ fi
161
+
162
+ if echo "$response" | jq -e '.success' > /dev/null 2>&1; then
163
+ echo -e "${GREEN}DNS record configured:${NC}"
164
+ echo "$response" | jq '{name: .result.name, type: .result.type, content: .result.content, proxied: .result.proxied}'
165
+ else
166
+ echo -e "${RED}Error: Failed to configure DNS record${NC}"
167
+ echo "$response" | jq '.errors' 2>/dev/null || echo "$response"
168
+ exit 1
169
+ fi
170
+ }
171
+
172
+ # Delete DNS record
173
+ cmd_delete() {
174
+ local subdomain="$1"
175
+
176
+ if [[ -z "$subdomain" ]]; then
177
+ echo -e "${RED}Error: Subdomain required${NC}"
178
+ echo "Usage: $0 delete <subdomain>"
179
+ exit 1
180
+ fi
181
+
182
+ local full_name="${subdomain}.${DOMAIN}"
183
+ echo -e "${BLUE}Deleting DNS record for ${full_name}${NC}"
184
+
185
+ local zone_id
186
+ zone_id=$(get_zone_id)
187
+
188
+ local record_id
189
+ record_id=$(get_record_id "$zone_id" "$subdomain")
190
+
191
+ if [[ -z "$record_id" ]]; then
192
+ echo -e "${YELLOW}Record not found: ${full_name}${NC}"
193
+ exit 0
194
+ fi
195
+
196
+ local response
197
+ response=$(cf_api DELETE "/zones/$zone_id/dns_records/$record_id")
198
+
199
+ if echo "$response" | jq -e '.success' > /dev/null 2>&1; then
200
+ echo -e "${GREEN}DNS record deleted: ${full_name}${NC}"
201
+ else
202
+ echo -e "${RED}Error: Failed to delete DNS record${NC}"
203
+ echo "$response" | jq '.errors' 2>/dev/null || echo "$response"
204
+ exit 1
205
+ fi
206
+ }
207
+
208
+ # List all DNS records
209
+ cmd_list() {
210
+ echo -e "${BLUE}Listing DNS records for ${DOMAIN}${NC}"
211
+
212
+ local zone_id
213
+ zone_id=$(get_zone_id)
214
+
215
+ local response
216
+ response=$(cf_api GET "/zones/$zone_id/dns_records?per_page=100")
217
+
218
+ if echo "$response" | jq -e '.success' > /dev/null 2>&1; then
219
+ echo "$response" | jq -r '.result[] | "\(.type)\t\(.name)\t\(.content)\t\(if .proxied then "proxied" else "dns-only" end)"' | column -t
220
+ else
221
+ echo -e "${RED}Error: Failed to list DNS records${NC}"
222
+ echo "$response" | jq '.errors' 2>/dev/null || echo "$response"
223
+ exit 1
224
+ fi
225
+ }
226
+
227
+ # Get specific record
228
+ cmd_get() {
229
+ local subdomain="$1"
230
+
231
+ if [[ -z "$subdomain" ]]; then
232
+ echo -e "${RED}Error: Subdomain required${NC}"
233
+ echo "Usage: $0 get <subdomain>"
234
+ exit 1
235
+ fi
236
+
237
+ local full_name="${subdomain}.${DOMAIN}"
238
+ echo -e "${BLUE}Getting DNS record for ${full_name}${NC}"
239
+
240
+ local zone_id
241
+ zone_id=$(get_zone_id)
242
+
243
+ local response
244
+ response=$(cf_api GET "/zones/$zone_id/dns_records?name=$full_name")
245
+
246
+ if echo "$response" | jq -e '.success' > /dev/null 2>&1; then
247
+ local count
248
+ count=$(echo "$response" | jq '.result | length')
249
+ if [[ "$count" -eq 0 ]]; then
250
+ echo -e "${YELLOW}No records found for ${full_name}${NC}"
251
+ else
252
+ echo "$response" | jq '.result[] | {name, type, content, proxied, ttl, created_on, modified_on}'
253
+ fi
254
+ else
255
+ echo -e "${RED}Error: Failed to get DNS record${NC}"
256
+ echo "$response" | jq '.errors' 2>/dev/null || echo "$response"
257
+ exit 1
258
+ fi
259
+ }
260
+
261
+ # Show usage
262
+ show_usage() {
263
+ echo "Cloudflare DNS Record Manager"
264
+ echo ""
265
+ echo "Usage:"
266
+ echo " $0 add <subdomain> [ip] [--domain <domain>] Add/update A record"
267
+ echo " $0 delete <subdomain> [--domain <domain>] Delete A record"
268
+ echo " $0 list [--domain <domain>] List all DNS records"
269
+ echo " $0 get <subdomain> [--domain <domain>] Get specific record details"
270
+ echo ""
271
+ echo "Options:"
272
+ echo " --domain <domain> Use specified domain (default: pseekoo.io)"
273
+ echo " --no-proxy DNS-only mode (no Cloudflare proxy)"
274
+ echo ""
275
+ echo "Examples:"
276
+ echo " $0 add qdrant Creates qdrant.pseekoo.io"
277
+ echo " $0 add api 1.2.3.4 Creates api.pseekoo.io with specific IP"
278
+ echo " $0 add holylist --domain pseekoo.com Creates holylist.pseekoo.com"
279
+ echo " $0 list --domain pseekoo.com Lists pseekoo.com records"
280
+ echo " $0 delete qdrant Deletes qdrant.pseekoo.io"
281
+ }
282
+
283
+ # Parse flags from arguments
284
+ FILTERED_ARGS=()
285
+ PROXIED=true
286
+ while [[ $# -gt 0 ]]; do
287
+ case "$1" in
288
+ --domain)
289
+ DOMAIN="$2"
290
+ shift 2
291
+ ;;
292
+ --no-proxy)
293
+ PROXIED=false
294
+ shift
295
+ ;;
296
+ *)
297
+ FILTERED_ARGS+=("$1")
298
+ shift
299
+ ;;
300
+ esac
301
+ done
302
+
303
+ # Main
304
+ case "${FILTERED_ARGS[0]:-}" in
305
+ add)
306
+ cmd_add "${FILTERED_ARGS[1]:-}" "${FILTERED_ARGS[2]:-}"
307
+ ;;
308
+ delete)
309
+ cmd_delete "${FILTERED_ARGS[1]:-}"
310
+ ;;
311
+ list)
312
+ cmd_list
313
+ ;;
314
+ get)
315
+ cmd_get "${FILTERED_ARGS[1]:-}"
316
+ ;;
317
+ *)
318
+ show_usage
319
+ exit 1
320
+ ;;
321
+ esac