diff options
-rw-r--r-- | .vscode/launch.json | 24 | ||||
-rw-r--r-- | LICENSE | 202 | ||||
-rw-r--r-- | README.md | 26 | ||||
-rw-r--r-- | aws-ipblocks-csv/.gitignore | 1 | ||||
-rw-r--r-- | aws-ipblocks-csv/INSTALL.md | 9 | ||||
-rw-r--r-- | aws-ipblocks-csv/README.md | 23 | ||||
-rw-r--r-- | aws-ipblocks-csv/index.html | 115 | ||||
-rw-r--r-- | aws-ipblocks-csv/index.js | 101 | ||||
-rw-r--r-- | aws-ipblocks-csv/package-lock.json | 21 | ||||
-rw-r--r-- | aws-ipblocks-csv/package.json | 14 | ||||
-rw-r--r-- | aws-ipblocks-csv/worker.js | 143 | ||||
-rw-r--r-- | toss-aws-eip/README.md | 85 | ||||
-rwxr-xr-x | toss-aws-eip/multitoss.sh | 103 | ||||
-rwxr-xr-x | toss-aws-eip/toss-aws-eip.py | 378 | ||||
-rw-r--r-- | writeups/headless-vnc.ko/headless-vnc.ko-kr.md | 88 | ||||
-rw-r--r-- | writeups/powershell-email/README.md | 43 | ||||
-rw-r--r-- | writeups/powershell-email/sendmail.ps1 | 181 | ||||
-rw-r--r-- | writeups/selfhosting-email/fuckyou-gmail.en.md | 100 |
18 files changed, 1657 insertions, 0 deletions
diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..58ab099 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,24 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "toss-aws-eip", + "type": "python", + "request": "launch", + "cwd": "${workspaceFolder}/toss-aws-eip", + "program": "${workspaceFolder}/toss-aws-eip/toss-aws-eip.py", + "args": [ + // "-r", "ap-southeast-2", + "-l", "tosser", + "-vvvv", // be very noisy + // "-d", // do a dryrun + "0.0.0.0/32" // accept no addreess + ], + "console": "integratedTerminal", + "justMyCode": false + } + ] +} @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/README.md b/README.md new file mode 100644 index 0000000..44a439f --- /dev/null +++ b/README.md @@ -0,0 +1,26 @@ +# David's Gist Repository +*Stay awhile and listen!* + +Here you can find all the work I've publish on Github Gist. As Gist works as a +blog service for devs and doesn't come with version control, I thought I'd put +all the work in a git repo. Some tools I wrote for gist are actually VC-worthy +projects that I really wouldn't bother making a git repo for each. + +I hope you enjoy your stay! + +## Index +- aws-ipblocks-csv: download AWS Public IP address ranges in CSV format. No + middle man involved - everything done on your browser +- toss-aws-eip: get an Elastic IP address until you get one in the range you + want +- writeups: all the write ups written up + - headless-vnc.ko: setting up a virtual X11 seat to use GUI on cloud instances + (in Korean) + - powershell-email: send emails in Powershell + - selfhosting-email: everything I want to talk about on self-hosting your own + email + +## Copyright +Unless classified otherwise, all the work I do I go by Apache-2.0. Mate, if I +make something serious, I wouldn't be posting it anywhere so feel free to do +whatever you want with whatever I post on Gist! diff --git a/aws-ipblocks-csv/.gitignore b/aws-ipblocks-csv/.gitignore new file mode 100644 index 0000000..b512c09 --- /dev/null +++ b/aws-ipblocks-csv/.gitignore @@ -0,0 +1 @@ +node_modules
\ No newline at end of file diff --git a/aws-ipblocks-csv/INSTALL.md b/aws-ipblocks-csv/INSTALL.md new file mode 100644 index 0000000..5979a27 --- /dev/null +++ b/aws-ipblocks-csv/INSTALL.md @@ -0,0 +1,9 @@ +# Build Guide +Run `npm install`. Distribute the files below. + +``` +index.html \ +index.js \ +worker.js \ +node_modules/csv-stringify/dist/esm/index.js +``` diff --git a/aws-ipblocks-csv/README.md b/aws-ipblocks-csv/README.md new file mode 100644 index 0000000..6b8cf8d --- /dev/null +++ b/aws-ipblocks-csv/README.md @@ -0,0 +1,23 @@ +# AWS Public IP Address Ranges in CSV Format +This is a neat little browser tool that downloads [the JSON +file](https://ip-ranges.amazonaws.com/ip-ranges.json) and convert it to a CSV +for better analysis with spreadsheet software. If you're annoyed because they +only provide it in JSON and don't want to code to make sense of the data, you've +come to the right place! + +The JSON data is probably for anyone who is affected by the Amazon's IP address +changes, namely network admins who have to configure their firewalls for AWS +traffic. Technically speaking, the data is not meant to be consumed by humans, +but I personally had to consume it for [my hobby self-hosting +project](https://gist.github.com/ashegoulding/72a8732d4a1679c343f84fc985ca8de8). +I was particularly interested in EIP address blocks. I figured they're something +AWS cannot easily mess with because that involves "evicting" all the EIP holders +before releasing or repurposing the block. + +This tool is hosted on [my github.io +site](https://ashegoulding.github.io/aws-ipblocks-csv). Bon appetit! + +## Links +https://docs.aws.amazon.com/vpc/latest/userguide/aws-ip-ranges.html +https://aws.amazon.com/blogs/aws/aws-ip-ranges-json/ +https://aws.amazon.com/blogs/developer/querying-the-public-ip-address-ranges-for-aws/ diff --git a/aws-ipblocks-csv/index.html b/aws-ipblocks-csv/index.html new file mode 100644 index 0000000..ee81518 --- /dev/null +++ b/aws-ipblocks-csv/index.html @@ -0,0 +1,115 @@ +<!DOCTYPE html> +<html lang="en"> +<!-- + Copyright (c) 2022 David Timber <dxdt@dev.snart.me> + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +--> +<head> + <meta charset="utf-8"> + <title>AWS Public IP Address Ranges in CSV</title> + + <script src="index.js"></script> + + <style> + .working-animated { + animation-name: blink; + animation-duration: 0.5s; + animation-iteration-count: infinite; + animation-play-state: running; + animation-timing-function: steps(2, start); + } + + .hidden { + visibility: hidden; + } + + .error { + color: red; + font-weight: bold; + } + + @keyframes blink { + to { + visibility: hidden; + } + } + .foot { + text-align: right; + } + </style> +</head> +<body onload="do_load()"> + <h1>AWS Public IP Address Ranges in CSV</h1> + <p> + This tool pulls <a + href="https://ip-ranges.amazonaws.com/ip-ranges.json">the JSON data</a> + from the AWS and convert it to CSV, along with other calculated data + such as the size of each address block. The file can be imported to a + spreadsheet software of your choice to extract the desired data using + filters. + </p> + + <section> + <h2>Tool Options</h2> + <form name="form" method="dialog" onsubmit="do_submit()"> + <p> + <input type="checkbox" name="ipv4" checked> + <label for="ipv4">Pull IPv4 blocks</label> + </p> + <p> + <input type="checkbox" name="ipv6"> + <label for="ipv6">Pull IPv6 blocks</label> + </p> + <p> + <button type="submit" name="submit">Go!</button> + <label for="submit"><- Requires a fair bit of memory!</label> + </p> + </form> + <p> + <span id="working-indicator" class="hidden"></span> + <a id="save-link" target="_blank" href="" class="hidden">Save CSV file</a> + </p> + </section> + + <h2>Format</h2> +<pre> +IPV REGION NETGRP SERVICE NET CIDR SIZE +4 af-south-1 af-south-1 AMAZON 3.2.34.0 26 64 +4 ap-northeast-2 ap-northeast-2 AMAZON 3.5.140.0 22 1024 +4 ap-southeast-4 ap-southeast-4 AMAZON 13.34.37.64 27 32 +4 il-central-1 il-central-1 AMAZON 13.34.65.64 27 32 +4 us-east-1 us-east-1 AMAZON 13.34.66.0 27 32 +4 ca-central-1 ca-central-1 AMAZON 13.34.78.160 27 32 +4 us-west-2 us-west-2 AMAZON 13.34.103.96 27 32 +</pre> + <h3>Where ...</h3> + <ul> + <li><b>IPV</b> is either 4 or 6</li> + <li><b>SIZE</b> is the number of addresses in the block</li> + </ul> + <p>For IPv6 addresses, the CIDR length can be enormous. The tool handles + them using <code>BigInt</code>, but your spreadsheet software can struggle + to handle it. It will most likely show the numbers in scientific + representation.</p> + <p class="foot"> + <small>by David Timber <dxdt@dev.snart.me> (c) 2023</small> + </p> +</body> +</html> diff --git a/aws-ipblocks-csv/index.js b/aws-ipblocks-csv/index.js new file mode 100644 index 0000000..48f150a --- /dev/null +++ b/aws-ipblocks-csv/index.js @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2019-2022 David Timber <dxdt@dev.snart.me> + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +var worker; +var ui = { + f: {} +}; +var url; + +function onerror (e) { + console.log(e); + + ui.working.innerHTML = e.toString(); + ui.working.className = "error"; +} + +function onmessage (evt) { + let blob, url, dlname = []; + + ui.f.submit.disabled = false; + + if (evt.data.error) { + onerror(evt.data.error); + return; + } + + dlname.push("aws-ip_"); + dlname.push(evt.data.meta["createDate"]); + dlname.push("_"); + dlname.push(evt.data.meta["syncToken"]); + dlname.push(".csv"); + + if (url) { + URL.revokeObjectURL(url); + } + + blob = new Blob([ evt.data.payload ], { type: "text/csv" }); + url = URL.createObjectURL(blob); + + ui.working.className = ""; + ui.working.innerHTML = "Done!"; + + ui.savelink.className = ""; + ui.savelink.href = url; + ui.savelink.download = dlname.join(""); +} + +function do_load () { + ui.working = document.getElementById("working-indicator"); + ui.f.ipv4 = document.form.ipv4; + ui.f.ipv6 = document.form.ipv6; + ui.f.submit = document.form.submit; + ui.savelink = document.getElementById("save-link"); + + worker = new Worker("worker.js", { type: "module" }); + worker.onmessage = onmessage; +} + +function do_submit () { + try { + if (!(ui.f.ipv4.checked || ui.f.ipv6.checked)) { + throw "Not pulling anything? (both v4 and v6 unchecked)"; + } + + worker.postMessage({ + task_id: "null", + opt: { + "ipv4": ui.f.ipv4.checked, + "ipv6": ui.f.ipv6.checked + } + }); + + ui.f.submit.disabled = true; + ui.working.className = "working-animated"; + ui.working.innerHTML = "Working ..."; + } + catch (e) { + onerror(e); + } + + return false; +} diff --git a/aws-ipblocks-csv/package-lock.json b/aws-ipblocks-csv/package-lock.json new file mode 100644 index 0000000..398c0d5 --- /dev/null +++ b/aws-ipblocks-csv/package-lock.json @@ -0,0 +1,21 @@ +{ + "name": "aws-ipblocks-csv", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "aws-ipblocks-csv", + "version": "0.0.0", + "license": "Apache-2.0", + "dependencies": { + "csv-stringify": "^6.4.4" + } + }, + "node_modules/csv-stringify": { + "version": "6.4.4", + "resolved": "https://registry.npmjs.org/csv-stringify/-/csv-stringify-6.4.4.tgz", + "integrity": "sha512-NDshLupGa7gp4UG4sSNIqwYJqgSwvds0SvENntxoVoVvTzXcrHvd5gG2MWpbRpSNvk59dlmIe1IwNvSxN4IVmg==" + } + } +} diff --git a/aws-ipblocks-csv/package.json b/aws-ipblocks-csv/package.json new file mode 100644 index 0000000..87e11b9 --- /dev/null +++ b/aws-ipblocks-csv/package.json @@ -0,0 +1,14 @@ +{ + "name": "aws-ipblocks-csv", + "version": "0.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "David Timber <david@snart.me>", + "license": "Apache-2.0", + "dependencies": { + "csv-stringify": "^6.4.4" + } +} diff --git a/aws-ipblocks-csv/worker.js b/aws-ipblocks-csv/worker.js new file mode 100644 index 0000000..9d034d3 --- /dev/null +++ b/aws-ipblocks-csv/worker.js @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2019-2022 David Timber <dxdt@dev.snart.me> + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import { stringify } from "./node_modules/csv-stringify/dist/esm/index.js"; + +function onerror (e, ctx) { + postMessage({ + "task_id": ctx.task_id, + "error": e + }); +} + +function mkstringifier (ctx) { + const ret = stringify(); + const csvdata = []; + + ret.on('readable', function () { + let row; + + while ((row = ret.read()) !== null) { + csvdata.push(row); + } + }); + + ret.on('finish', function () { + postMessage({ + "task_id": ctx.task_id, + "meta": ctx.meta, + "payload": csvdata.join(""), + }); + }); + + return ret; +} + +function procMeta (ctx, data) { + ctx.meta = { + "syncToken": data["syncToken"], + "createDate": data["createDate"] + } +} + +const HEADER = [ + "IPV", + "REGION", + "NETGRP", + "SERVICE", + "NET", + "CIDR", + "SIZE" +] + +function procPrefixes (ctx, data, opt, ipv, prefix_key, cidr_len_f) { + let i, o, p, sep, net, cidr; + + for (i in data) { + o = data[i]; + p = o[prefix_key]; + sep = p.search("/"); + net = p.substring(0, sep); + cidr = parseInt(p.substring(sep + 1)); + + ctx.csv.write([ + ipv, + o["region"], + o["network_border_group"], + o["service"], + net, + cidr, + cidr_len_f(cidr) + ]); + } +} + +function calcCidrLen (whole, cidr) { + return BigInt(1) << BigInt(whole - cidr); +} + + +self.onmessage = async function (evt) { + const ctx = { + task_id: evt.data.task_id + }; + const opt = evt.data.opt ? evt.data.opt : { + "ipv4": true, + "ipv6": true + }; + + try { + ctx.csv = mkstringifier(ctx); + + const r = await fetch('https://ip-ranges.amazonaws.com/ip-ranges.json'); + const json = await r.json() + + procMeta(ctx, json); + + ctx.csv.write(HEADER); // emit header + + if (opt["ipv4"]) { + procPrefixes( + ctx, + json["prefixes"], + opt, + 4, + "ip_prefix", + (cidr) => { return calcCidrLen(32, cidr) }); + } + + if (opt["ipv6"]) { + procPrefixes( + ctx, + json["ipv6_prefixes"], + opt, + 6, + "ipv6_prefix", + (cidr) => { return calcCidrLen(128, cidr) }); + } + + ctx.csv.end(); // The CSV string will be posted in the event handler + } + catch (e) { + onerror(e, ctx); + } +}; diff --git a/toss-aws-eip/README.md b/toss-aws-eip/README.md new file mode 100644 index 0000000..caa0355 --- /dev/null +++ b/toss-aws-eip/README.md @@ -0,0 +1,85 @@ +# Ranged AWS EIP Allocator +When you request an EIP address, the AWS randomly allocates an EIP address from +one of their IPv4 address pools. The list of the IPv4 pools the AWS uses for +their service is publicly available from the following. + +https://ip-ranges.amazonaws.com/ip-ranges.json + +I also made the tool for converting the JSON data to CSV so you can use it in +spreadsheets. + +https://ashegoulding.github.io/aws-ipblocks-csv + +This is the script you're after if you're trying to get an EIP within a specific +range or block to get away from the lousy neighbours who constantly degrade the +reputation of the address block or just to get a series of contiguous EIP for +your EC2 fleet. + +I recommend running it on an EC2 instance rather than on the local machine to +save the trip to the internet. The request process time from the EC2 endpoint is +already over few hundred milliseconds so you definitely want to reduce the trip +through the internet. + +Please check the pricing rules before considering using this. If they charge for +allocation/release of EIPs, you're screwed and the script is basically useless. + +## This is a Bad Idea! +The script has to be used as a last resort after you have failed to get support +from the AWS in getting the EIP's you want. If you're a corporate user, you can +probably get the support you need. + +The big issue with this approach is that there's no way of knowing how saturated +the EIP block you're trying to get addresses from. You may use tools like nmap, +but there's still the problem of unassociated EIP addresses. + +## How to +Make sure you have done your `aws configure` and given allocate_address and +release_address permissions to the IAM account. You may test the permissions +using `-d` option. You'll get an error and the script will exit with code 1 if +the account lacks the necessary permissions. + +Choose the block you wish to get an EIP address from. Multiple ranges can be +specified and the script will exit if an address from any of the ranges is +allocated. + +```bash +./toss-aws-eip.py \ + -r us-west-1 \ # not required if the default region is set in the profile + -l "tosser" \ # resource name for identification purposes + 52.94.249.80/28 \ # range + 52.95.255.96/28 \ # range + 52.94.248.128/28 # range +``` + +In the example, the script will allocate and release EIP addresses until one +from any of the three blocks is acquired. The name tag on the address will be +"tosser". + +You can even run the script in several processes. The process returns 0 when +successful and it also handles `SIGINT` and `SIGTERM` gracefully without leaving +a "residue" EIP. If you want multiple EIP's, simply count the number of +processes that returned 0. + +Run with `-h` option for more. + +## Why? +I was having issues with the reputations of IP addresses allocated for EC2. It +is a known fact that many EC2 instances are hacked and used as bots for +nefarious activities like SSH brute forcing and sending junk mails. The +reputation is especially important for sending mails because companies take +aggressive measures to combat junk mails. + +I started with an EIP without knowing this and getting my EIP already set for +all my self-hosted services was a long and hard process. Companies like Google +and Microsoft keep a public channel via which sysadmins can file complaints to +get their addresses off their blacklist. But Outlook(Microsoft) has the stronger +measure of blacklisting the entire IP address blocks attacks and junk mails +originate from. There is no way that was legal, but I decided to get a clean EIP +from a clean block this time instead of dealing with AWS and Microsoft Support +because I'd never get anything good out of them. + +My idea is that I could be better of having an EIP from a relatively small +block. Even if I end up getting a dirty EIP, I can go through the support +channels again to delist the EIP and there will be less chance of the entire +block getting blacklisted because of the small size. You can only do this in +trial and error. This is where the script comes in. diff --git a/toss-aws-eip/multitoss.sh b/toss-aws-eip/multitoss.sh new file mode 100755 index 0000000..78f9f46 --- /dev/null +++ b/toss-aws-eip/multitoss.sh @@ -0,0 +1,103 @@ +#!/bin/bash +declare nb_proc +declare nb_runs=1 +declare cmdline +declare flag_help=false + +## Func defs + +print_help () { + cat << EOF +Run the command in pararrel ensuring the number of sucessful exits. +Usage: $1 <OPTIONS> <CMDLINE> +Options: + -h print this message and exit gracefully + -p number of processes to spawn (required) + -n number of successful run to count (default: 1) +EOF +} + +parse_params () { + local name + local delta + + while getopts "hp:n:" name + do + case "$name" in + h) flag_help=true ;; + p) let "nb_proc=$OPTARG" ;; + n) let "nb_runs=$OPTARG" ;; + '?') exit 2;; + esac + done + + let "delta = OPTIND - 1" + shift $delta + + cmdline="$@" +} + +spwan_one () { + $cmdline & +} + +main () { + local procs + local ec + local good_runs=0 + local children + + # spwan initial processes + for (( procs = 0; procs < nb_proc; procs += 1 )) + do + spwan_one + done + + while true + do + wait -n + ec=$? + echo $ec + + if [ $ec -eq 0 ]; then + let "good_runs += 1" + if [ $good_runs -ge $nb_runs ]; then + break + else + spwan_one + fi + elif [ $ec -ne 3 ]; then + # error occurred or no more child left. do not continue + break + fi + done + + children="$(jobs -p)" + [ ! -z "$children" ] && kill -TERM $children 2> /dev/null > /dev/null + while wait -n; do : ; done + + return $ec +} + +## Init script +parse_params $@ + +## Parametre check +if $flag_help; then + print_help + exit 0 +fi +if [ -z "$nb_proc" ]; then + cat << EOF >&2 +-p option not set. Run with -h option for help. +EOF + exit 2 +fi +if [ -z "$cmdline" ]; then + cat << EOF >&2 +CMDLINE not set. Run with -h option for help. +EOF +fi + +## Main start +main diff --git a/toss-aws-eip/toss-aws-eip.py b/toss-aws-eip/toss-aws-eip.py new file mode 100755 index 0000000..2029ae0 --- /dev/null +++ b/toss-aws-eip/toss-aws-eip.py @@ -0,0 +1,378 @@ +#!/bin/env python3 + +# Copyright (c) 2023 David Timber <dxdt@dev.snart.me> +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +import getopt +import ipaddress +import math +import signal +import sys +import time +from decimal import Decimal +from enum import Enum +from typing import Callable + +import boto3 + +VER_STR = "0.0.0 (Nov 2023)" + +HELP_STR = '''Usage: {prog} [options] <SPEC> [SPEC ...] +Repeat allocating Elastic IP address until an address in desired range is +acquired. The multiple specs will be OR'd. + +SPEC: <RANGE | NET> + RANGE: <ADDR>-<ADDR> + ADDR: an IPv4 address + NET: <ADDR>/<CIDR> + CIDR: integer [0,32] +Options: + -r <string> aws region name. Used if the default is not set in profile + -p <string> aws profile to use. Use the default profile if unspecified + -l <string> set the name for the allocated EIP. Defaults to an empty string + -x <string> the tag spec in "name=value" format + -n <int> limit number of attempts. Default: -1 + -t <decimal> limit run time in seconds. Default: 'inf' + -d do a dry run + -h print this message and exit gracefully + -v increase verbosity + -q shut up + -V print other info and exit gracefully + +WARNING: check the pricing policy of your region prior to using this tool!''' +V_STR = '''Version: {ver} +by David Timber <dxdt@dev.snart.me> (c) 2023''' + +# Global classes + +class Verbosity (Enum): + '''Verbosity Level Enum''' + Q = ERR = 0 + DEFAULT = WARN = 1 + INFO = 2 + DBG0 = 3 + DBG1 = 4 + +class AddrSpec: + '''The class works in two modes - A to B range mode and CIDR mode. `range()` + and `net()` factory methods can be used to instantiate a working instance. + The default constructor instantiates a unusable dummy instance.''' + def _range_in_op (self, addr: ipaddress.IPv4Address) -> bool: + return self.a <= addr and addr <= self.b + + def _net_in_op (self, addr: ipaddress.IPv4Address) -> bool: + return addr in self.net + + def _range_str_op (self) -> str: + return '''{a}-{b}'''.format(a = str(self.a), b = str(self.b)) + + def _net_str_op (self) -> str: + return str(self.net) + + def range (a: ipaddress.IPv4Address, b: ipaddress.IPv4Address): + ret = AddrSpec() + ret.a = a + ret.b = b + ret.net = None + ret._contains_f = ret._range_in_op + ret._str_f = ret._range_str_op + return ret + + def net (n: ipaddress.IPv4Network): + ret = AddrSpec() + ret.a = ret.b = None + ret.net = n + ret._contains_f = ret._net_in_op + ret._str_f = ret._net_str_op + return ret + + def __init__(self): + self.a = None + self.b = None + self.net = None + + def __contains__ (self, addr: ipaddress.IPv4Address) -> bool: + return self._contains_f(addr) + + def __str__ (self) -> str: + return self._str_f() + +class ProgConf: + '''The program configuration class. The members represent the parametres + from the command line arguments.''' + def __init__(self): + self.range_specs = list[AddrSpec]() + self.profile = None + self.tag_spec = [] + self.nb_runs = math.inf + self.runtime = math.inf + self.verbose = Verbosity.DEFAULT.value + self.help = False + self.dryrun = False + self.region = None + self.ver = False + + def CompVerbosity (self, v: Verbosity) -> bool: + return self.verbose >= v.value + +class EIP: + '''Class for holding the EIP and the allocation id for a successful + iteration. The `str()` operator should return a string that can be more or + less parsed by a YAML parser.''' + def __init__(self, addr: ipaddress.IPv4Address, alloc_id: str): + self.addr = addr + self.alloc_id = alloc_id + + def __str__(self) -> str: + return '''PublicIp: {addr} +AllocationId: {alloc_id}'''.format( + addr = str(self.addr), + alloc_id = self.alloc_id) + +# Exceptions +class FormatError (Exception): + '''Used for cmd line args parse error''' + ... + + +def ParseAddrSpec (x: str) -> AddrSpec: + '''If the string contains a hyphen, try to extract the two addresses. If the + string contains a slash, treat the characters before it as an IP address and + the ones after it a CIDR length.''' + sep = x.find("-") + if sep > 0: # range + a = x[:sep].strip() + b = x[sep + 1:].strip() + return AddrSpec.range(ipaddress.IPv4Address(a), ipaddress.IPv4Address(b)) + + sep = x.find("/") + if sep > 0: # network + return AddrSpec.net(ipaddress.IPv4Network(x)) + + raise FormatError("Invalid address spec: " + x) + +def ParseParam (argv: list[str]) -> ProgConf: + '''The cmd line args parser''' + ret = ProgConf() + opt, args = getopt.getopt(argv, "p:l:x:n:t:hdvqr:V") + + for v in args: + ret.range_specs.append(ParseAddrSpec(v)) + + for k, v in opt: + match k: + case "-r": ret.region = v + case "-p": ret.profile = v + case "-l": ret.tag_spec.append({ 'Key': 'Name', 'Value': v }) + case "-x": + i = v.find("=") + if i < 0: + raise FormatError("Invalid format for option '-x': " + v) + tname = v[:i] + tvalue = v[i + 1:] + ret.tag_spec.append({ 'Key': tname, 'Value': tvalue }) + case '-n': + ret.nb_runs = int(v) + if ret.nb_runs <= 0: ret.nb_runs = math.inf + case '-t': ret.runtime = Decimal(v) * 1000000000 + case '-h': ret.help = True + case '-v': ret.verbose += 1 + case '-q': ret.verbose = Verbosity.Q.value + case '-d': ret.dryrun = True + case '-V': ret.ver = True + + return ret + +def GetRefClock () -> int: + '''A wrapper function for retrieving the monotonic clock tick value used to + measure the process run time.''' + return time.monotonic_ns() + +def DoPrint (s: str, v: Verbosity): + '''Check verbosity level and print the string to stderr.''' + if conf.CompVerbosity(v): + return sys.stderr.write(s) + +try: + conf = ParseParam(sys.argv[1:]) +except (FormatError, ipaddress.AddressValueError) as e: + sys.stderr.write(str(e) + "\n") + sys.exit(2) +run_cnt = 0 # the number of iteration performed +run_start = GetRefClock() # time the process started +it_ret = None # acquired EIP information returned after a successful iteration +flag = True # flag used to stop the main loop to serve exit signals(TERM, INT) + +if conf.help: + print(HELP_STR.format(prog = sys.argv[0])) + sys.exit(0) +if conf.ver: + print(V_STR.format(ver = VER_STR)) + sys.exit(0) + +if not conf.range_specs: + DoPrint("No SPEC specified. Run with '-h' option for help.\n", Verbosity.ERR) + sys.exit(2) + +# Init Boto3 +session = boto3.Session( + region_name = conf.region, + profile_name = conf.profile) +client = session.client("ec2") + +def PrintedCall (func: Callable, fname: str, v: Verbosity, **kwargs): + DoPrint('''CALL {fname}({kwargs})\n'''.format(fname = fname, kwargs = kwargs), v) + return func(**kwargs) + +def PrintReturn (fname: str, ret, v: Verbosity): + return DoPrint('''RET {fname}(): {ret}'''.format(fname = fname, ret = str(ret)), v) + +def ShouldIterate () -> bool: + '''Check if the main loop should continue''' + global run_cnt, conf, flag + + run_elapsed = GetRefClock() - run_start + ret = flag and run_cnt < conf.nb_runs and run_elapsed < conf.runtime + run_cnt += 1 + + return ret + +def OptInSignalHandler (sname: str, handler: Callable): + '''Install the signal handler if the signal with the name exists on the + platform.''' + if hasattr(signal, sname): + return signal.signal(signal.Signals(sname), handler) + +def HandleSignal (sn, sf): + '''Exit signal handler''' + global flag + + flag = False + # Deregister the handler so that the subsequent signals kill the process + signal.signal(sn, signal.SIG_DFL) + + # Signal names are not supported by all platforms + try: + signame = signal.Signals(sn).name + except: + signame = "?" + DoPrint('''CAUGHT {signame}({sn})\n'''.format( + signame = signame, + sn = sn), Verbosity.WARN) + +def ExtractBotoError (e: Exception) -> str: + '''Not many types of Boto3 exceptions are defined for errors. Use the "duck + typing technique" to extract the error code returned from the AWS + endpoint.''' + if (hasattr(e, "response") and + type(e.response) == dict and + type(e.response.get("Error")) == dict): + return e.response["Error"].get("Code") + +def IsDryRunError (e: Exception) -> bool: + return ExtractBotoError(e) == "DryRunOperation" + +def DoIteration () -> EIP | None: + '''Returns the EIP in the desired range if successful. None otherwise.''' + global conf + # Pre-construct the tag spec. + tag_spec = [ { 'ResourceType': 'elastic-ip' , 'Tags': conf.tag_spec } ] if conf.tag_spec else None + + try: + # Send the allocation request! + r = PrintedCall( + client.allocate_address, + "client.allocate_address", + Verbosity.DBG0, + TagSpecifications = tag_spec, + DryRun = conf.dryrun) + PrintReturn("client.allocate_address", r, Verbosity.DBG0) + # The method will return the response object if successful + ip = ipaddress.IPv4Address(r['PublicIp']) + alloc_id = r['AllocationId'] + + DoPrint('''Got {ip}\n'''.format(ip = ip), Verbosity.INFO) + except Exception as e: + if conf.dryrun and IsDryRunError(e): + # This is expected for dry run. Carry on with mock data. + ip = None + alloc_id = "DryIce" + else: + # Propagate other errors + # Could be bad internet connection or insufficient privileges + raise e + + if ip: + # Check if the address allocated is within the desired range + for spec in conf.range_specs: + DoPrint("IS {ip} in {range}?: ".format( + ip = ip, + range = spec + ), Verbosity.DBG1) + ret = ip in spec # this calls `_net_in_op()` or `_range_in_op()` + DoPrint("{verdict}\n".format( + verdict = "yes" if ret else "no"), Verbosity.DBG1) + if ret: + # Instantiate and return the result! + return EIP(ip, alloc_id) + + # Reached because the allocated EIP is not in the desired range + # Release the EIP. + try: + r = PrintedCall( + client.release_address, + "client.release_address", + Verbosity.DBG0, + AllocationId = alloc_id, + DryRun = conf.dryrun) + PrintReturn("client.release_address", r, Verbosity.DBG0) + except Exception as e: + if conf.dryrun and IsDryRunError(e): + # This is expected for dry run. Let the function return. It is + # possible that the user has allocate rights but not release rights. + # If that's the case, this is where the user will find out. + pass + else: + # Propagate other errors + # Could be bad internet connection or insufficient privileges + raise e + +# Catch these normal case signals so that the current iteration can release the +# EIP before coming out of the main loop. +OptInSignalHandler("SIGINT", HandleSignal) +OptInSignalHandler("SIGTERM", HandleSignal) +OptInSignalHandler("SIGHUP", HandleSignal) # multitoss support + +while ShouldIterate(): + DoPrint('''Iteration #{nr_run} ...\n'''.format(nr_run = run_cnt), Verbosity.INFO) + it_ret = DoIteration() + if it_ret: + DoPrint("Got EIP in target range!\n", Verbosity.INFO) + print(it_ret) + break + +run_elapsed = GetRefClock() - run_start +DoPrint( + '''Run complete after {nb_runs} run(s) in {run_elapsed:.3f}\n'''.format( + nb_runs = run_cnt, + run_elapsed = run_elapsed / 1000000000.0 + ), Verbosity.INFO) + +sys.exit(0 if it_ret else 3) diff --git a/writeups/headless-vnc.ko/headless-vnc.ko-kr.md b/writeups/headless-vnc.ko/headless-vnc.ko-kr.md new file mode 100644 index 0000000..05d2486 --- /dev/null +++ b/writeups/headless-vnc.ko/headless-vnc.ko-kr.md @@ -0,0 +1,88 @@ +# 클라우드 리눅스 VM에 GUI 돌리기 + +AWS EC2나 GCP CE의 윈도 머신은 대부분 RDP로 접근하는 반면에, 리눅스 머신은 모두 CUI로만 +사용하도록 되어있음. 리눅스는 셸로 모든 작업을 할 수 있지만 유니티 에디터같은 소프트웨어는 GUI없이 +거의 사용이 불가능하다. 머신에 하드웨어를 추가할 수 없는 환경이라면 이 방법을 추천한다. + +## 개요 +**Xorg X11 dummy video**를 사용해서 가상 fb를 만들고, **TigerVNC X 윈도 서버 모듈**을 +사용해 원격에서 접속할 수 있도록 설정한다. 이 문서는 VM에 GDE를 설치하는 방법을 다룬다. 이 문서는 +VNC 포트를 외부에 공개하지 않고 SSH 터널링을 사용해 서버 VNC에 연결하는 방법을 설명한다. + +## 방법 +### SSH 접속 +원격 VNC 포트를 로컬로 터널링한다. 포트 2개를 사용하는 이유는 추후에 설명: +``` +ssh -L15900:127.0.0.1:5900 -L15901:127.0.0.1:5901 <주소> +``` + +### 패키지 설치 +RPM: + +```xorg-x11-drv-dummy tigervnc-server-module gnome-shell gnome-terminal``` + +DEB: (TODO) + +### 설정 + +**/etc/X11/xorg.conf.d/00-dummy-vnc-video.conf** (새로 만들기): +``` +Section "Module" + Load "vnc" +EndSection + +Section "Device" + Identifier "Configured Video Device" + Driver "dummy" +EndSection + +Section "Monitor" + Identifier "Configured Monitor" + HorizSync 31.5-48.5 + VertRefresh 50-70 +EndSection + +Section "Screen" + Identifier "Default Screen" + Monitor "Configured Monitor" + Device "Configured Video Device" + DefaultDepth 24 + SubSection "Display" + Depth 24 + Modes "1280x720" + EndSubSection + + Option "SecurityTypes" "None" + Option "AlwaysShared" "true" +EndSection +``` + +해상도나 색상 깊이를 알맞게 변경한다. + +`Option "SecurityTypes" "None"` 행은 VNC 접속 시 +암호를 묻지 않도록 설정한다는 의미이다. 암호를 설정하고 싶다면 [이 문서](https://wiki.archlinux.org/index.php/TigerVNC#Expose_the_local_display_directly) +를 참조. + +`Option "AlwaysShared" "true"` 행은 동시 접속을 허용한다는 의미. 원치 않으면 주석처리. + +VNC 포트를 외부에 노출하여 사용하는 것은 안전하지 않다. TigerVNC가 TLS를 지원하나, X11 모듈로 +사용되어 X11 설정으로 옵션을 넘겨주어야 해서 복잡한 설정이 어려울 것이다. 따라서 저자는 VNC 포트를 +개방하지 않고 SSH 터널링으로 VNC 접속하는 방법을 택하였다. + +### X 서버 실행 +``` +systemctl set-default graphical.target +systemctl start graphical.target +``` + +Systemd를 사용하지 않는 배포판에서는 gdm을 enable, start하거나 runlevel을 5로 설정하는 등의 +작업을 하여 gdm을 실행한다. + +### 접속, 로그인 +**127.0.0.1:15900**에 접속하여 원하는 계정으로 로그인한다. 로그인에 성공하면 VNC로 보이는 +화면이 빈 화면으로 유지되면 정상. **127.0.0.1:15901**로 접속하면 로그인된 GUI 세션을 이용할 수 +있다. + +## 외부 링크 +* https://lxtreme.nl/blog/headless-x11/ +* https://wiki.archlinux.org/index.php/TigerVNC#Expose_the_local_display_directly diff --git a/writeups/powershell-email/README.md b/writeups/powershell-email/README.md new file mode 100644 index 0000000..4d815d4 --- /dev/null +++ b/writeups/powershell-email/README.md @@ -0,0 +1,43 @@ +# Sending Mail using Powershell +Turns out, Powershell can be used to send emails through harnessing the power of +C#. I made this script as a POC as to show how far .Net and Powershell have +come. + +Should work on all platforms that support Powershell. + +## Usage +Copied from the script. +```sh +echo 'Hi! it going? Testing my Powershell script.' | \ + smtp_host=smtp.gmail.com \ + smtp_username=example@gmail.com \ + smtp_password='0123456789' \ + mail_from=alice@gmail.com \ + mail_to=bob@example.com \ + mail_subject='Sent using Powershell' \ + sendmail.ps1 \ + doc.pdf +``` + +## Few Tips +### Password +Services like Gmail will require you to get a separate password for external +apps. Google calls this "App password". Refer to the links below. + +* https://support.google.com/accounts/answer/185833 +* https://support.google.com/mail/answer/7126229 + +Even if the normal password for the account can be used, a separate password +should always be used for program access. Always check if your email provider +supports this. + +### TLS +Most services will refuse to serve on unsecure connections. Use `smtp_tls=O` +only as the last resort. + +`smtp_tls_cert` is for TLS CN SASL authentication. if authenticating using this +method, `smtp_username` and `smtp_password` are not required. + +### CC and More +Didn't think about CC and all the advanced composition. Feel free to add more +feature to the script that is already monstrous! diff --git a/writeups/powershell-email/sendmail.ps1 b/writeups/powershell-email/sendmail.ps1 new file mode 100644 index 0000000..e5500ec --- /dev/null +++ b/writeups/powershell-email/sendmail.ps1 @@ -0,0 +1,181 @@ +#!/usr/bin/env pwsh + +# Send an email using Powershell. +# Usage: sendmail.ps1 [attachment 1 [attachment 2 [... attachment N]]] +# +# This is a POC on how to send an email using Powershell. The script should work +# on all platforms. The information required for the script to work is all +# supplied via environment variables. +# +# Env Vars +# smtp_host +# smtp_port (best if you let the implementation decide) +# smtp_tls: +# 'F' to insist on secure connection (default) +# 'O' for opportunistic +# 'N' to disable (default if $smtp_host is "localhost") +# smtp_tls_cert +# smtp_username +# smtp_password +# mail_from (required) +# mail_to (required) +# mail_subject (required) +# +# Example +#```pwsh +# echo 'Hi! it going? Testing my Powershell script.' | \ +# smtp_host=smtp.gmail.com \ +# smtp_username=example@gmail.com \ +# smtp_password='0123456789' \ +# mail_from=alice@gmail.com \ +# mail_to=bob@example.com \ +# mail_subject='Sent using Powershell' \ +# sendmail.ps1 \ +# doc.pdf +#``` +using namespace System +using namespace System.Net +using namespace System.Security.Cryptography + +Set-StrictMode -Version Latest +$ErrorActionPreference = "Stop" +$PSDefaultParameterValues['*:ErrorAction'] = 'Stop' + +<# +.SYNOPSIS +Get an environment variable and unset it + +.PARAMETER Name +The name of the environment variable to read and unset + +.PARAMETER Required +If set, throw FileNotFoundException if the environment variable requested is not +set + +.NOTES +The purpose of the function is to get and scrub off the password passed as an +env var in one go. To preserve the env var, use `GetEnvironmentVariable()` +directly. +#> +function FetchEnv ([string]$Name, [bool]$Required = $false) { + $RetVal = [Environment]::GetEnvironmentVariable($Name) + [Environment]::SetEnvironmentVariable($Name, '') + if ( $RetVal ) { + return $RetVal + } + else { + if ($Required) { + throw New-Object IO.FileNotFoundException ("${Name}: unset env var") + } + else { + return $null + } + } +} + +<# +.SYNOPSIS +Read data from STDIN until EOF and return data as decoded string +#> +function ExhaustStdin () { + $stream = New-Object IO.StreamReader ( [Console]::OpenStandardInput() ) + return $stream.ReadToEnd() +} + + +###################################################################### +# Execution starts here +###################################################################### + +# Compose a message +$mail = New-Object Mail.MailMessage ( + (FetchEnv "mail_from" $true), + (FetchEnv "mail_to" $true), + (FetchEnv "mail_subject" $true), + (ExhaustStdin)) # This is the part where the mail body is read from STDIN + +# Add attachments +foreach ($file in $args) { + [string]$file = $file + + $a = New-Object Mail.Attachment ( + $file, + # Treat all attachments as binary + [System.Net.Mime.MediaTypeNames+Application]::Octet) + # Timestamp support + $a.ContentDisposition.CreationDate = [IO.File]::GetCreationTime($file) + $a.ContentDisposition.ModificationDate = [IO.File]::GetLastWriteTime($file) + $a.ContentDisposition.ReadDate = [IO.File]::GetLastAccessTime($file) + + $mail.Attachments.Add($a) +} + +# Set up credentials +$client_cred = New-Object NetworkCredential ( + (FetchEnv "smtp_username"), + (FetchEnv "smtp_password")) + +# Set up client TLS cert +$tls_cert = FetchEnv("smtp_tls_cert") +if ($null -ne $tls_cert) { + $cert_chain = New-Object X509Certificates.X509Certificate ( $tls_cert ) +} +else { + $cert_chain = $null +} + +# Read target SMTP host +$smtp_host = FetchEnv "smtp_host" +if (!$smtp_host) { + $smtp_host = "localhost" +} + +# Set up SMTP client object +$smtp = New-Object Mail.SmtpClient ($smtp_host) +if ($cert_chain) { + $smtp.ClientCertificates.Add($cert_chain) +} +$smtp.Credentials = $client_cred +$smtp_port = FetchEnv "smtp_port" +if ($smtp_port) { + $smtp.Port = [int]$smtp_port +} + +$tlsmode = FetchEnv "smtp_tls" +if (!$tlsmode) { + # Determine the "tlsmode" to default to + if ($smtp_host -eq "localhost") { + # No need to waste computing power on TLS. + # Unless you're paranoid and don't trust the hosts file. + $tlsmode = "N" + } + else { + # Transmitting plain text data on the internet nowadays is no-brainer. + # Most email services will refuse anyway. + $tlsmode = "F" + } +} + +# Set `$smtp.EnableSsl` based on `$tlsmode` +if ($tlsmode -eq "F" -or $tlsmode -eq "O") { + $smtp.EnableSsl = $true +} +elseif ($tlsmode -eq "N") { + $smtp.EnableSsl = $false +} + +try { + $smtp.Send($mail) +} +catch { + if ($tlsmode -eq "O") { + # Opportunistic tlsmode. Try again with TLS disabled. + # Please think twice and fix the problem before resorting to this bit. + $smtp.EnableSsl = $false + $smtp.Send($mail) + } + else { + # Let the script die + throw $_ + } +} diff --git a/writeups/selfhosting-email/fuckyou-gmail.en.md b/writeups/selfhosting-email/fuckyou-gmail.en.md new file mode 100644 index 0000000..1bdfe7f --- /dev/null +++ b/writeups/selfhosting-email/fuckyou-gmail.en.md @@ -0,0 +1,100 @@ +# What to do when Gmail marks all the mails from your server as spam +If you're self-hosting your services and having trouble getting your emails +through Gmail and infuriated by Google's non-existent support, you're not the +only one. I'd like to share my experiences trying to get it sorted out. + +* https://support.google.com/mail/thread/171517615?hl=en&msgid=172576102 + +I'm the author of the post above. You can tell how arrogant Google employees are +from all the previous posts he made in the past. + +* https://support.google.com/mail/thread/4857692/how-to-delist-my-ip-address-from-gmail-blacklist?hl=en +* https://support.google.com/mail/thread/3745648?hl=en + + + +Seriously, fuck these guys. + +## The Basics +Don't embarrase yourself by setting up your servers wrong. Make sure that emails +have valid DKIM signatures, mail contents are good, rDNS is properly set, MTAs +use TLS 1.3 with valid certificates, and there's no error in TXT records. You +have to get those all "PASS" marks and the padlock icon next to the email +address. This is the very basic. Make sure your servers are complaint before +sending anything. And whenever you change the settings, **TEST IT** or your IP +address can be listed because of broken configuration. + +Here are the tools I use to diagnose. + +- https://www.checktls.com/TestReceiver (most favourable for TLS diagnosis) +- https://www.mail-tester.com/ +- https://mxtoolbox.com/deliverability + +## Getting a clean public IPv4 address +If you're sure you've got everything right and all the other providers respect +the mails from your server, it's most likely Google's internal rep list. + +Contrary to that guy's claim, it is evident that Google does keep an internal IP +reputation list. If the IPv4 address you have been assigned is dirty, all the +email from your server could be marked as spam. Forever. Doesn't matter if +you've delisted your IP from all known blacklists. Not only Google but also all +the other major email service providers do not account for the fact that IP +addresses get passed around and the blacklist entries must expire. **Google DOES +NOT CARE**. It's our job to ensure that we get clean IP addresses. + +There are plenty of posts on the internet on how to check if your IP address is +dirty, but here are the ones I use. + +- https://mxtoolbox.com/blacklists.aspx +- https://dnschecker.org/ip-blacklist-checker.php +- https://whatismyipaddress.com/blacklist-check +- https://cleantalk.org/blacklists + +So basically all the tools that show up on the search result. + +## IPv6 +It could be safer to just use an IPv6 address for sending emails as the IPv6 +address range is wide and the use of IPv6 addresses is not yet widely spread, +hence the less chance of getting a dirty address. But there are still MTA's with +only IPv4 addresses. But at least most of Google's servers use IPv6, so this +could be the solution for you. See the next section if you're using AWS. + +However, if your cloud service provider or your ISP would not support rDNS for +IPv6 , make sure your server does not send any emails using the IPv6 connection. +This can be done in many ways. + +- Don't assign your machine an IPv6 address at all +- Disable the setting. For example, + - Postfix: `smtp_address_preference` or `inet_interfaces` altogether + +There shouldn't be any problem receiving mails via IPv6 connections. But if +you're paranoid, you can disable IPv6 SMTP connectivity on your daemon or +firewall. + +## Dealing with grumpy AWS support rep +**AWS will set up a rDNS record for your IPv6 address on request!** Which is +pretty cool. + +However, sometimes your ticket will be assigned to a grumpy representative who +thinks that they're doing their job right. If your ticket is responded by +something like "do you know what you're doing, mate?", do not attempt to reason +with the rep. Instead, toss the ticket in the bin and retry your luck in 2 weeks +time. Hopefully, your ticket will be assigned to someone generous. It took me 3 +attempts. It's probably because they have to put up with Telstra. It could +depend on how strict the ISPs are in the part of the world you're in. + +This is the link to the tickets I'm talking about: + +- https://support.console.aws.amazon.com/support/contacts#/rdns-limits + +## Gmail Specific Tests +There's this awesome tool made by awesome people that lists all the emails +received by their test accounts. You can use the tool before sending your emails +to real people's Gmail accounts. + +- https://www.gmass.co/inbox + +## FUCK YOU GOOGLE +There I said it. |