From 544da1ba50d9cbf51a18ca0abf1db66b3baa7460 Mon Sep 17 00:00:00 2001 From: Andrew Opalach Date: Fri, 17 Oct 2025 12:22:00 -0400 Subject: Gentoo desktop --- hosts/station/cgit/cgitrc | 35 ++ hosts/station/cgit/custom.css | 431 +++++++++++++++++++++ hosts/station/cgit/email-libravatar.py | 38 ++ hosts/station/cgit/readme_in_summary_no_about.diff | 154 ++++++++ hosts/station/fail2ban/fail2ban.d/station.local | 2 + hosts/station/fail2ban/jail.d/station.local | 39 ++ hosts/station/git/git-template.zip | Bin 0 -> 2772 bytes hosts/station/git/gitconfig | 3 + hosts/station/init.d/fcgiwrap | 235 +++++++++++ hosts/station/nftables.conf | 23 ++ hosts/station/nginx/akoncity | 82 ++++ hosts/station/scripts/fail2ban-ban-ip | 3 + hosts/station/scripts/fail2ban-unban-ip | 3 + hosts/station/scripts/mail-stats | 2 + hosts/station/scripts/vacumm | 3 + hosts/station/station_maintenance.txt | 80 ++++ hosts/station/systemd/fcgiwrap.service | 15 + hosts/station/wg-rod.conf.in | 10 + 18 files changed, 1158 insertions(+) create mode 100644 hosts/station/cgit/cgitrc create mode 100644 hosts/station/cgit/custom.css create mode 100755 hosts/station/cgit/email-libravatar.py create mode 100644 hosts/station/cgit/readme_in_summary_no_about.diff create mode 100644 hosts/station/fail2ban/fail2ban.d/station.local create mode 100644 hosts/station/fail2ban/jail.d/station.local create mode 100644 hosts/station/git/git-template.zip create mode 100644 hosts/station/git/gitconfig create mode 100755 hosts/station/init.d/fcgiwrap create mode 100755 hosts/station/nftables.conf create mode 100644 hosts/station/nginx/akoncity create mode 100755 hosts/station/scripts/fail2ban-ban-ip create mode 100755 hosts/station/scripts/fail2ban-unban-ip create mode 100755 hosts/station/scripts/mail-stats create mode 100755 hosts/station/scripts/vacumm create mode 100644 hosts/station/station_maintenance.txt create mode 100644 hosts/station/systemd/fcgiwrap.service create mode 100644 hosts/station/wg-rod.conf.in (limited to 'hosts/station') diff --git a/hosts/station/cgit/cgitrc b/hosts/station/cgit/cgitrc new file mode 100644 index 0000000..1dc4158 --- /dev/null +++ b/hosts/station/cgit/cgitrc @@ -0,0 +1,35 @@ +enable-http-clone=0 +enable-index-owner=0 +enable-commit-graph=1 +repository-sort=age +branch-sort=age + +robots=noindex, nofollow + +logo= +css=/custom.css + +source-filter=/usr/local/lib/cgit/filters/syntax-highlighting.py +email-filter=/usr/local/lib/cgit/filters/email-libravatar.py + +remove-suffix=1 + +virtual-root=/ +clone-url=https://git.akon.city/$CGIT_REPO_URL.git + +root-title=Akon City Software Inc. +root-desc= + +snapshots=tar.gz tar.bz2 zip + +mimetype.gif=image/gif +mimetype.html=text/html +mimetype.jpg=image/jpeg +mimetype.jpeg=image/jpeg +mimetype.pdf=application/pdf +mimetype.png=image/png +mimetype.svg=image/svg+xml + +readme=./README.html + +scan-path=/var/git/ diff --git a/hosts/station/cgit/custom.css b/hosts/station/cgit/custom.css new file mode 100644 index 0000000..2349906 --- /dev/null +++ b/hosts/station/cgit/custom.css @@ -0,0 +1,431 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +@import url("cgit.css"); + +/* @TODO: Create new palette type. Add/Delete/Modifiy (Green/Red/Yellow) + 2-3 Accent colors. + * Then fill out pygments colors. + * */ +:root { + --background: #ffffff; + --black: #000000; + --gray: #808080; + --accent1: #d9d8d1; + --accent2: #edece6; + --accent3: #f6f6f0; + --green: #5bd154; + --red: #d95988; + --yellow: #f1ed73; + --blue: #51a2be; +} + +body { + background-color: var(--background); +} + +/* Readme Formatting */ + +h2 { + border-bottom: 1px solid var(--accent1); +} + +h1, h2 { + margin: 1.0em 0em 0.30em 0em; +} + +h3, h4, h5 { + margin: 0.8em 0em 0.10em 0em; +} + +pre { + margin: 0.50em 0em; +} + +p { + margin: 0.4em 0em; +} + +ul { + padding-left: 1.25rem; +} + +td.tableblock { + border: 1px solid #dedede; +} + +table tr td { + padding: .1em .25em; +} + +/* Code Blocks */ + +.listingblock, +code { + font-family: monospace; + font-size: 10pt; + background-color: var(--accent3); + background-size: cover; +} + +.listingblock { + text-wrap: stable; + word-break: break-all; + display: inline-block; + border: solid 1px var(--accent1); + margin: 0.35em 0em; + padding: 0em 0.4em !important; +} + +p code { + padding: 0em 0.1em; +} + +/* Animated Tag */ + +div#cgit a.tag-annotated-deco { + color: var(--black); + margin: 0px 0.5em; + padding: 0px 0.25em; + border: solid 1px var(--black); + background: linear-gradient(90deg, #eebab3 0%, #f7afab 12%, #eebab3 37%, #ffe5dc 50%, #eebab3 62%, #f7afab 79%, #eebab3 100%); + animation: shine 3.45s infinite; + background-size: 200%; + background-position: left; + background-repeat: repeat-x; + -webkit-animation-timing-function: linear; + -webkit-animation-duration: 3.45s; +} + +div#cgit a:hover.tag-annotated-deco { + color: var(--black) !important; +} + +@keyframes shine { + 0% {background-position:-100%;} + 100% {background-position:100%;} +} + +/* Cgit Overrides */ + +div#cgit { + margin: auto; + font-size: 10pt; + -moz-tab-size: 4; + tab-size: 4; + background: var(--background); +} + +div#cgit table.tabs { + border-bottom: solid 2px var(--accent3); +} + +div#cgit table.tabs td { + padding: 2px 0.75em; +} + +div#cgit table.tabs td a { + padding: 1px 0.75em; + color: var(--gray); + font-size: 110%; +} + +div#cgit table.tabs td a.active { + color: var(--black); + background: var(--background); + border-bottom: solid 2px var(--gray); +} + +div#cgit table#header { + margin-bottom: -1em; +} + +div#cgit table#header td.main { + font-size: 2em; +} + +div#cgit table#header td.main a { + color: var(--black); +} + +div#cgit table#header td.sub { + border-top: none; +} + +div#cgit table#header td.sub.right { + padding-right: 1em; +} + +div#cgit div.content { + border-bottom: none; + padding: 0em; +} + +div#cgit > div.content { + padding: 1.25em; +} + +div#cgit table.list { + max-width: 100ch; +} + +div#cgit table.list th a { + color: inherit; +} + +div#cgit table.list tr:nth-child(even) { + background: inherit; +} + +div#cgit table.list tr:nth-child(odd) { + background: var(--accent3); +} + +div#cgit table.list tr:hover { + background: var(--accent2); +} + +div#cgit table.list tr.nohover { + background: inherit; +} + +div#cgit table.list tr.nohover:hover { + background: inherit; +} + +div#cgit table.list tr.nohover-highlight:hover:nth-child(even) { + background: inherit; +} + +div#cgit a.deco { + background-color: var(--red); + border: solid 1px var(--black); +} + +div#cgit a:hover.deco { + color: var(--black) !important; +} + +div#cgit a.branch-deco { + background-color: var(--green); + border: solid 1px var(--black); +} + +div#cgit a:hover.branch-deco { + color: var(--black) !important; +} + +div#cgit div#summary { + max-width: 100ch; + margin: 0.5em; + overflow-wrap: break-word; +} + +div#cgit table.diffstat { + border-collapse: collapse; + border: solid 1px var(--black); + background-color: var(--accent3); +} + +div#cgit div.diffstat-header a { + color: var(--black); +} + +div#cgit table.diffstat td.graph td.add { + background-color: var(--green); +} + +div#cgit table.diffstat td.graph td.rem { + background-color: var(--red); +} + +div#cgit table.diffstat td.add a { + color: var(--green); +} + +div#cgit table.diffstat td.del a { + color: var(--red); +} + +div#cgit table.diffstat td.upd a { + color: var(--blue); +} + +div#cgit table.diff td div.del { + color: var(--red); +} + +div#cgit table.diff td div.add { + color: var(--green); +} + +div#cgit table.diff td div.hunk { + color: var(--gray); +} + +div#cgit div.cgit-panel table { + border-collapse: collapse; + border: solid 1px var(--black); + background-color: var(--accent3); +} + +div#cgit input, +div#cgit select { + border: solid 1px var(--black); + background-color: var(--accent3); + border-radius: 0.25em; +} + +div#cgit input.txt { + background-color: var(--accent3); +} + +div#cgit div.path { + margin: 0px; + padding: 5px 2em 2px 2em; + color: var(--black); + background-color: var(--background); +} + +div#cgit a { + color: var(--blue); +} + +div#cgit a:hover { + color: var(--gray) !important; +} + +div#cgit a.permalink { + color: inherit; +} + +div#cgit table.list td a.ls-dir { + color: var(--blue); +} + +/* +div#cgit span.age-hours { + color: #080; +} + +div#cgit span.age-days { + color: #040; +} + +div#cgit span.age-weeks { + color: #444; +} + +div#cgit span.age-months { + color: #888; +} + +div#cgit span.age-years { + color: #bbb; +} + +div#cgit span.insertions { + color: #080; +} + +div#cgit span.deletions { + color: #800; +} +*/ + +/* https://git.zx2c4.com/cgit.css:905 */ +div#cgit span.libravatar img.onhover { + display: none; + border: solid 1px var(--black); + padding: 0em; + border-radius: 3px; + width: 12em; + height: 12em; +} + +div#cgit span.libravatar img.inline { + width: 1.3em; + height: 1.3em; + margin-right: 0.2em; + border-radius: 3px; + vertical-align: bottom; +} + +div#cgit span.libravatar:hover > img.onhover { + display: block; + position: absolute; + margin-left: 1.5em; + background-color: var(--background); + box-shadow: 0.15em 0.15em 0.4em var(--black); +} + +pre.pygments .hll { background-color: #ffffcc } +pre.pygments { background: #f8f8f8; } +pre.pygments .tok-c { color: #3D7B7B; font-style: italic } /* Comment */ +pre.pygments .tok-err { border: 1px solid #FF0000 } /* Error */ +pre.pygments .tok-k { color: #008000; font-weight: bold } /* Keyword */ +pre.pygments .tok-o { color: #666666 } /* Operator */ +pre.pygments .tok-ch { color: #3D7B7B; font-style: italic } /* Comment.Hashbang */ +pre.pygments .tok-cm { color: #3D7B7B; font-style: italic } /* Comment.Multiline */ +pre.pygments .tok-cp { color: #9C6500 } /* Comment.Preproc */ +pre.pygments .tok-cpf { color: #3D7B7B; font-style: italic } /* Comment.PreprocFile */ +pre.pygments .tok-c1 { color: #3D7B7B; font-style: italic } /* Comment.Single */ +pre.pygments .tok-cs { color: #3D7B7B; font-style: italic } /* Comment.Special */ +pre.pygments .tok-gd { color: #A00000 } /* Generic.Deleted */ +pre.pygments .tok-ge { font-style: italic } /* Generic.Emph */ +pre.pygments .tok-ges { font-weight: bold; font-style: italic } /* Generic.EmphStrong */ +pre.pygments .tok-gr { color: #E40000 } /* Generic.Error */ +pre.pygments .tok-gh { color: #000080; font-weight: bold } /* Generic.Heading */ +pre.pygments .tok-gi { color: #008400 } /* Generic.Inserted */ +pre.pygments .tok-go { color: #717171 } /* Generic.Output */ +pre.pygments .tok-gp { color: #000080; font-weight: bold } /* Generic.Prompt */ +pre.pygments .tok-gs { font-weight: bold } /* Generic.Strong */ +pre.pygments .tok-gu { color: #800080; font-weight: bold } /* Generic.Subheading */ +pre.pygments .tok-gt { color: #0044DD } /* Generic.Traceback */ +pre.pygments .tok-kc { color: #008000; font-weight: bold } /* Keyword.Constant */ +pre.pygments .tok-kd { color: #008000; font-weight: bold } /* Keyword.Declaration */ +pre.pygments .tok-kn { color: #008000; font-weight: bold } /* Keyword.Namespace */ +pre.pygments .tok-kp { color: #008000 } /* Keyword.Pseudo */ +pre.pygments .tok-kr { color: #008000; font-weight: bold } /* Keyword.Reserved */ +pre.pygments .tok-kt { color: #B00040 } /* Keyword.Type */ +pre.pygments .tok-m { color: #666666 } /* Literal.Number */ +pre.pygments .tok-s { color: #BA2121 } /* Literal.String */ +pre.pygments .tok-na { color: #687822 } /* Name.Attribute */ +pre.pygments .tok-nb { color: #008000 } /* Name.Builtin */ +pre.pygments .tok-nc { color: #0000FF; font-weight: bold } /* Name.Class */ +pre.pygments .tok-no { color: #880000 } /* Name.Constant */ +pre.pygments .tok-nd { color: #AA22FF } /* Name.Decorator */ +pre.pygments .tok-ni { color: #717171; font-weight: bold } /* Name.Entity */ +pre.pygments .tok-ne { color: #CB3F38; font-weight: bold } /* Name.Exception */ +pre.pygments .tok-nf { color: #0000FF } /* Name.Function */ +pre.pygments .tok-nl { color: #767600 } /* Name.Label */ +pre.pygments .tok-nn { color: #0000FF; font-weight: bold } /* Name.Namespace */ +pre.pygments .tok-nt { color: #008000; font-weight: bold } /* Name.Tag */ +pre.pygments .tok-nv { color: #19177C } /* Name.Variable */ +pre.pygments .tok-ow { color: #AA22FF; font-weight: bold } /* Operator.Word */ +pre.pygments .tok-w { color: #bbbbbb } /* Text.Whitespace */ +pre.pygments .tok-mb { color: #666666 } /* Literal.Number.Bin */ +pre.pygments .tok-mf { color: #666666 } /* Literal.Number.Float */ +pre.pygments .tok-mh { color: #666666 } /* Literal.Number.Hex */ +pre.pygments .tok-mi { color: #666666 } /* Literal.Number.Integer */ +pre.pygments .tok-mo { color: #666666 } /* Literal.Number.Oct */ +pre.pygments .tok-sa { color: #BA2121 } /* Literal.String.Affix */ +pre.pygments .tok-sb { color: #BA2121 } /* Literal.String.Backtick */ +pre.pygments .tok-sc { color: #BA2121 } /* Literal.String.Char */ +pre.pygments .tok-dl { color: #BA2121 } /* Literal.String.Delimiter */ +pre.pygments .tok-sd { color: #BA2121; font-style: italic } /* Literal.String.Doc */ +pre.pygments .tok-s2 { color: #BA2121 } /* Literal.String.Double */ +pre.pygments .tok-se { color: #AA5D1F; font-weight: bold } /* Literal.String.Escape */ +pre.pygments .tok-sh { color: #BA2121 } /* Literal.String.Heredoc */ +pre.pygments .tok-si { color: #A45A77; font-weight: bold } /* Literal.String.Interpol */ +pre.pygments .tok-sx { color: #008000 } /* Literal.String.Other */ +pre.pygments .tok-sr { color: #A45A77 } /* Literal.String.Regex */ +pre.pygments .tok-s1 { color: #BA2121 } /* Literal.String.Single */ +pre.pygments .tok-ss { color: #19177C } /* Literal.String.Symbol */ +pre.pygments .tok-bp { color: #008000 } /* Name.Builtin.Pseudo */ +pre.pygments .tok-fm { color: #0000FF } /* Name.Function.Magic */ +pre.pygments .tok-vc { color: #19177C } /* Name.Variable.Class */ +pre.pygments .tok-vg { color: #19177C } /* Name.Variable.Global */ +pre.pygments .tok-vi { color: #19177C } /* Name.Variable.Instance */ +pre.pygments .tok-vm { color: #19177C } /* Name.Variable.Magic */ +pre.pygments .tok-il { color: #666666 } /* Literal.Number.Integer.Long */ diff --git a/hosts/station/cgit/email-libravatar.py b/hosts/station/cgit/email-libravatar.py new file mode 100755 index 0000000..045ca17 --- /dev/null +++ b/hosts/station/cgit/email-libravatar.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python3 + +# This script may be used with the email-filter or repo.email-filter settings in cgitrc. +# +# The following environment variables can be used to retrieve the configuration +# of the repository for which this script is called: +# CGIT_REPO_URL ( = repo.url setting ) +# CGIT_REPO_NAME ( = repo.name setting ) +# CGIT_REPO_PATH ( = repo.path setting ) +# CGIT_REPO_OWNER ( = repo.owner setting ) +# CGIT_REPO_DEFBRANCH ( = repo.defbranch setting ) +# CGIT_REPO_SECTION ( = section setting ) +# CGIT_REPO_CLONE_URL ( = repo.clone-url setting ) +# +# It receives an email address on argv[1] and text on stdin. It prints +# to stdout that text prepended by a gravatar at 10pt. + +import sys +import hashlib +import codecs + +email = sys.argv[1].lower().strip() +if email[0] == "<": + email = email[1:] +if email[-1] == ">": + email = email[0:-1] + +page = sys.argv[2] + +sys.stdin = codecs.getreader("utf-8")(sys.stdin.detach()) +sys.stdout = codecs.getwriter("utf-8")(sys.stdout.detach()) + +md5 = hashlib.md5(email.encode()).hexdigest() +text = sys.stdin.read().strip() + +baseurl = "https://seccdn.libravatar.org" + +print(f"" + text) diff --git a/hosts/station/cgit/readme_in_summary_no_about.diff b/hosts/station/cgit/readme_in_summary_no_about.diff new file mode 100644 index 0000000..73267e0 --- /dev/null +++ b/hosts/station/cgit/readme_in_summary_no_about.diff @@ -0,0 +1,154 @@ +diff --git a/cmd.c b/cmd.c +index 0eb75b1..be316f4 100644 +--- a/cmd.c ++++ b/cmd.c +@@ -49,7 +49,7 @@ static void about_fn(void) + cgit_redirect(redirect, true); + free(currenturl); + free(redirect); +- } else if (ctx.repo->readme.nr) ++ } else if (0) + cgit_print_repo_readme(ctx.qry.path); + else if (ctx.repo->homepage) + cgit_redirect(ctx.repo->homepage, false); +diff --git a/ui-shared.c b/ui-shared.c +index 6fae72d..c5b609a 100644 +--- a/ui-shared.c ++++ b/ui-shared.c +@@ -1082,7 +1082,7 @@ void cgit_print_pageheader(void) + + html("\n"); + } + +-void cgit_print_summary(void) +-{ +- int columns = 3; +- +- if (ctx.repo->enable_log_filecount) +- columns++; +- if (ctx.repo->enable_log_linecount) +- columns++; +- +- cgit_print_layout_start(); +- html("
\n"); + if (ctx.env.authenticated && ctx.repo) { +- if (ctx.repo->readme.nr) ++ if (0) + reporevlink("about", "about", NULL, + hc("about"), ctx.qry.head, NULL, + NULL); +diff --git a/ui-summary.c b/ui-summary.c +index 947812a..23c07aa 100644 +--- a/ui-summary.c ++++ b/ui-summary.c +@@ -40,31 +40,6 @@ static void print_url(const char *url) + html("
"); +- cgit_print_branches(ctx.cfg.summary_branches); +- htmlf("", columns); +- cgit_print_tags(ctx.cfg.summary_tags); +- if (ctx.cfg.summary_log > 0) { +- htmlf("", columns); +- cgit_print_log(ctx.qry.head, 0, ctx.cfg.summary_log, NULL, +- NULL, NULL, 0, 0, 0); +- } +- urls = 0; +- cgit_add_clone_urls(print_url); +- html("
 
 
"); +- cgit_print_layout_end(); +-} +- + /* The caller must free the return value. */ + static char* append_readme_path(const char *filename, const char *ref, const char *path) + { +@@ -99,25 +74,11 @@ static char* append_readme_path(const char *filename, const char *ref, const cha + return full_path; + } + +-void cgit_print_repo_readme(const char *path) ++static void print_readme_file(const char *path) + { +- char *filename, *ref, *mimetype; ++ char *filename, *ref; + int free_filename = 0; + +- mimetype = get_mimetype_for_filename(path); +- if (mimetype && (!strncmp(mimetype, "image/", 6) || !strncmp(mimetype, "video/", 6))) { +- ctx.page.mimetype = mimetype; +- ctx.page.charset = NULL; +- cgit_print_plain(); +- free(mimetype); +- return; +- } +- free(mimetype); +- +- cgit_print_layout_start(); +- if (ctx.repo->readme.nr == 0) +- goto done; +- + filename = ctx.repo->readme.items[0].string; + ref = ctx.repo->readme.items[0].util; + +@@ -125,7 +86,7 @@ void cgit_print_repo_readme(const char *path) + free_filename = 1; + filename = append_readme_path(filename, ref, path); + if (!filename) +- goto done; ++ return; + } + + /* Print the calculated readme, either from the git repo or from the +@@ -142,6 +103,54 @@ void cgit_print_repo_readme(const char *path) + html(""); + if (free_filename) + free(filename); ++} ++ ++void cgit_print_summary(void) ++{ ++ int columns = 3; ++ ++ if (ctx.repo->enable_log_filecount) ++ columns++; ++ if (ctx.repo->enable_log_linecount) ++ columns++; ++ ++ cgit_print_layout_start(); ++ html(""); ++ cgit_print_branches(ctx.cfg.summary_branches); ++ htmlf("", columns); ++ cgit_print_tags(ctx.cfg.summary_tags); ++ if (ctx.cfg.summary_log > 0) { ++ htmlf("", columns); ++ cgit_print_log(ctx.qry.head, 0, ctx.cfg.summary_log, NULL, ++ NULL, NULL, 0, 0, 0); ++ } ++ urls = 0; ++ cgit_add_clone_urls(print_url); ++ html("
 
 
"); ++ if (ctx.repo->readme.nr) ++ print_readme_file(ctx.qry.path); ++ cgit_print_layout_end(); ++} ++ ++void cgit_print_repo_readme(const char *path) ++{ ++ char *mimetype; ++ ++ mimetype = get_mimetype_for_filename(path); ++ if (mimetype && (!strncmp(mimetype, "image/", 6) || !strncmp(mimetype, "video/", 6))) { ++ ctx.page.mimetype = mimetype; ++ ctx.page.charset = NULL; ++ cgit_print_plain(); ++ free(mimetype); ++ return; ++ } ++ free(mimetype); ++ ++ cgit_print_layout_start(); ++ if (ctx.repo->readme.nr == 0) ++ goto done; ++ ++ print_readme_file(path); + + done: + cgit_print_layout_end(); diff --git a/hosts/station/fail2ban/fail2ban.d/station.local b/hosts/station/fail2ban/fail2ban.d/station.local new file mode 100644 index 0000000..b3d98c4 --- /dev/null +++ b/hosts/station/fail2ban/fail2ban.d/station.local @@ -0,0 +1,2 @@ +[DEFAULT] +allowipv6 = true diff --git a/hosts/station/fail2ban/jail.d/station.local b/hosts/station/fail2ban/jail.d/station.local new file mode 100644 index 0000000..2110612 --- /dev/null +++ b/hosts/station/fail2ban/jail.d/station.local @@ -0,0 +1,39 @@ +[DEFAULT] +maxretry = 4 +findtime = 60m +bantime = 24h +backend = systemd +logtarget = SYSTEMD-JOURNAL +banaction = nftables-multiport +banaction_allports = nftables-allports +#mta = sendmail +#destemail = mark@akon.city +#action = %(action_)s +# %(mta)s-whois-matches[sender="%(sender)s", dest="%(destemail)s", chain="%(chain)s"] + +[postfix] +enabled = true +mode = aggressive +journalmatch=_SYSTEMD_UNIT=postfix.service + +[sieve] +enabled = true +journalmatch=_SYSTEMD_UNIT=dovecot.service + +[dovecot] +enabled = true +journalmatch=_SYSTEMD_UNIT=dovecot.service + +[dovecot-pop3imap] +enabled = true +filter = dovecot-pop3imap +journalmatch=_SYSTEMD_UNIT=dovecot.service + +[nginx-limit-req] +enabled = true +journalmatch=_SYSTEMD_UNIT=nginx.service + +[sshd] +enabled = true +mode = aggressive +journalmatch=_SYSTEMD_UNIT=ssh.service diff --git a/hosts/station/git/git-template.zip b/hosts/station/git/git-template.zip new file mode 100644 index 0000000..4ade69c Binary files /dev/null and b/hosts/station/git/git-template.zip differ diff --git a/hosts/station/git/gitconfig b/hosts/station/git/gitconfig new file mode 100644 index 0000000..1082bea --- /dev/null +++ b/hosts/station/git/gitconfig @@ -0,0 +1,3 @@ +[init] + defaultBranch = master + templatedir = /var/git//.git-template diff --git a/hosts/station/init.d/fcgiwrap b/hosts/station/init.d/fcgiwrap new file mode 100755 index 0000000..1dde958 --- /dev/null +++ b/hosts/station/init.d/fcgiwrap @@ -0,0 +1,235 @@ +#!/bin/sh +### BEGIN INIT INFO +# Provides: fcgiwrap +# Required-Start: $remote_fs +# Required-Stop: $remote_fs +# Should-Start: +# Should-Stop: +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: FastCGI wrapper +# Description: Simple server for running CGI applications over FastCGI +### END INIT INFO + +PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin + +SPAWN_FCGI="/usr/bin/spawn-fcgi" +DAEMON="/usr/sbin/fcgiwrap" +NAME="fcgiwrap" +DESC="FastCGI wrapper" + +PIDFILE="/run/$NAME.pid" + +test -x $SPAWN_FCGI || exit 0 +test -x $DAEMON || exit 0 + +# FCGI_APP Variables +FCGI_CHILDREN="1" +FCGI_SOCKET="/run/$NAME.socket" +FCGI_USER="www-data" +FCGI_GROUP="www-data" + +. /lib/lsb/init-functions + +# Default options, these can be overriden by the information +# at /etc/default/$NAME +DAEMON_OPTS="-f" # By default we redirect STDERR output from executed + # CGI through FastCGI, to disable this behaviour set + # DAEMON_OPTS to an empty value in the default's file + +ENV_VARS="PATH='$PATH'" # We reset the environ for spawn-fcgi, but we use the + # contents of this variable as a prefix when calling it + # to export some variables (currently just the PATH) +DIETIME=10 # Time to wait for the server to die, in seconds + # If this value is set too low you might not + # let some servers to die gracefully and + # 'restart' will not work +QDIETIME=0.5 # The same as DIETIME, but a lot shorter for the + # stop case. + +#STARTTIME=2 # Time to wait for the server to start, in seconds + # If this value is set each time the server is + # started (on start or restart) the script will + # stall to try to determine if it is running + # If it is not set and the server takes time + # to setup a pid file the log message might + # be a false positive (says it did not start + # when it actually did) + +# Include defaults if available +if [ -f /etc/default/$NAME ] ; then + . /etc/default/$NAME +fi + +set -e + +running_pid() { +# Check if a given process pid's cmdline matches a given name + pid=$1 + name=$2 + [ -z "$pid" ] && return 1 + [ ! -d /proc/$pid ] && return 1 + cmd="$(cat /proc/$pid/cmdline | tr "\000" "\n"|head -n 1 |cut -d : -f 1)" + # Is this the expected server + [ "$cmd" != "$name" ] && return 1 + return 0 +} + +running() { +# Check if the process is running looking at /proc +# (works for all users) + # No pidfile, probably no daemon present + [ ! -f "$PIDFILE" ] && return 1 + PIDS="$(cat "$PIDFILE")" + for pid in $PIDS; do + if [ -n "$pid" ]; then + running_pid $pid $DAEMON && return 0 || true + fi + done + return 1 +} + +start_server() { + ARGS="-P $PIDFILE" + # Adjust NUMBER of processes + if [ -n "$FCGI_CHILDREN" ]; then + ARGS="$ARGS -F '$FCGI_CHILDREN'" + fi + # Adjust SOCKET or PORT and ADDR + if [ -n "$FCGI_SOCKET" ]; then + ARGS="$ARGS -s '$FCGI_SOCKET'" + elif [ -n "$FCGI_PORT" ]; then + if [ -n "$FCGI_ADDR" ]; then + ARGS="$ARGS -a '$FCGI_ADDR'" + fi + ARGS="$ARGS -p '$FCGI_PORT'" + fi + # Adjust user + if [ -n "$FCGI_USER" ]; then + ARGS="$ARGS -u '$FCGI_USER'" + if [ -n "$FCGI_SOCKET" ]; then + if [ -n "$FCGI_SOCKET_OWNER" ]; then + ARGS="$ARGS -U '$FCGI_SOCKET_OWNER'" + else + ARGS="$ARGS -U '$FCGI_USER'" + fi + fi + fi + # Adjust group + if [ -n "$FCGI_GROUP" ]; then + ARGS="$ARGS -g '$FCGI_GROUP'" + if [ -n "$FCGI_SOCKET" ]; then + if [ -n "$FCGI_SOCKET_GROUP" ]; then + ARGS="$ARGS -G '$FCGI_SOCKET_GROUP'" + else + ARGS="$ARGS -G '$FCGI_GROUP'" + fi + fi + fi + eval $(echo env -i $ENV_VARS $SPAWN_FCGI $ARGS -- $DAEMON $DAEMON_OPTS) \ + > /dev/null + errcode="$?" + return $errcode +} + +stop_server() { + # Force the process to die killing it manually + [ ! -e "$PIDFILE" ] && return + PIDS="$(cat "$PIDFILE")" + for pid in $PIDS; do + if running_pid $pid $DAEMON; then + kill -15 $pid + # Is it really dead? + sleep "$QDIETIME"s + if running_pid $pid $DAEMON; then + kill -9 $pid + sleep "$QDIETIME"s + if running_pid $pid $DAEMON; then + echo "Cannot kill $NAME (pid=$pid)!" + exit 1 + fi + fi + fi + done + rm -f "$PIDFILE" + if [ -n "$FCGI_SOCKET" ]; then + rm -f "$FCGI_SOCKET" + fi +} + +case "$1" in + start) + log_daemon_msg "Starting $DESC" "$NAME" + # Check if it's running first + if running ; then + log_progress_msg "apparently already running" + log_end_msg 0 + exit 0 + fi + if start_server ; then + # NOTE: Some servers might die some time after they start, + # this code will detect this issue if STARTTIME is set + # to a reasonable value + [ -n "$STARTTIME" ] && sleep $STARTTIME # Wait some time + if running ; then + # It's ok, the server started and is running + log_end_msg 0 + else + # It is not running after we did start + log_end_msg 1 + fi + else + # Either we could not start it + log_end_msg 1 + fi + ;; + stop|force-stop) + log_daemon_msg "Stopping $DESC" "$NAME" + if running ; then + # Only stop the server if we see it running + errcode=0 + stop_server || errcode=$? + log_end_msg $errcode + else + # If it's not running don't do anything + log_progress_msg "apparently not running" + log_end_msg 0 + exit 0 + fi + ;; + restart|force-reload) + log_daemon_msg "Restarting $DESC" "$NAME" + errcode=0 + stop_server || errcode=$? + # Wait some sensible amount, some server need this + [ -n "$DIETIME" ] && sleep $DIETIME + start_server || errcode=$? + [ -n "$STARTTIME" ] && sleep $STARTTIME + running || errcode=$? + log_end_msg $errcode + ;; + status) + + log_daemon_msg "Checking status of $DESC" "$NAME" + if running ; then + log_progress_msg "running" + log_end_msg 0 + else + log_progress_msg "apparently not running" + log_end_msg 1 + exit 1 + fi + ;; + # Use this if the daemon cannot reload + reload) + log_warning_msg "Reloading $NAME daemon: not implemented, as the daemon" + log_warning_msg "cannot re-read the config file (use restart)." + ;; + *) + N=/etc/init.d/$NAME + echo "Usage: $N {start|stop|force-stop|restart|force-reload|status}" >&2 + exit 1 + ;; +esac + +exit 0 diff --git a/hosts/station/nftables.conf b/hosts/station/nftables.conf new file mode 100755 index 0000000..8f736ce --- /dev/null +++ b/hosts/station/nftables.conf @@ -0,0 +1,23 @@ +#!/usr/sbin/nft -f + +flush ruleset + +table inet filter { + chain input_ipv4 { + icmp type echo-request limit rate 5/second accept + } + + chain input_ipv6 { + icmpv6 type { nd-neighbor-solicit, nd-router-advert, nd-neighbor-advert } accept + icmpv6 type echo-request limit rate 5/second accept + } + + chain input { + type filter hook input priority filter; policy drop; + ct state vmap { established : accept, related : accept, invalid : drop } + iifname lo accept + meta protocol vmap { ip : jump input_ipv4, ip6 : jump input_ipv6 } + tcp dport { ssh, http, https, 993, 465, 25, 587 } accept + log prefix "(nftables) input denied: " counter drop + } +} diff --git a/hosts/station/nginx/akoncity b/hosts/station/nginx/akoncity new file mode 100644 index 0000000..0f33432 --- /dev/null +++ b/hosts/station/nginx/akoncity @@ -0,0 +1,82 @@ +limit_req_zone $binary_remote_addr zone=main:1m rate=10r/s; # This is completely untuned. + +server { + error_log syslog:server=unix:/dev/log; + access_log syslog:server=unix:/dev/log; + + server_name akon.city www.akon.city; + + root /var/www/akoncity; + index index.html; + + location / { + limit_req zone=main burst=20 nodelay; + limit_req_log_level error; + try_files $uri $uri/ =404; + } + + listen [::]:443 ssl ipv6only=on; # managed by Certbot + listen 443 ssl; # managed by Certbot + ssl_certificate /etc/letsencrypt/live/akon.city/fullchain.pem; # managed by Certbot + ssl_certificate_key /etc/letsencrypt/live/akon.city/privkey.pem; # managed by Certbot + include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot + ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot +} + +server { + error_log syslog:server=unix:/dev/log; + access_log syslog:server=unix:/dev/log; + + server_name git.akon.city; + + root /var/www/htdocs/cgit; + try_files $uri @cgit; + + location ~ ^.*(/.+|/.+\.git)/(HEAD|info/refs|objects/info/.*|git-(upload|receive)-pack)$ { + client_max_body_size 0; + include fastcgi_params; + fastcgi_param SCRIPT_FILENAME /usr/lib/git-core/git-http-backend; + fastcgi_param GIT_HTTP_EXPORT_ALL "1"; + fastcgi_param GIT_PROJECT_ROOT /var/git; + fastcgi_param PATH_INFO $uri; + fastcgi_param REQUEST_METHOD $request_method; + fastcgi_param QUERY_STRING $query_string; + fastcgi_pass unix:/var/run/fcgiwrap.socket; + } + + location @cgit { + index cgit.cgi; + include fastcgi_params; + fastcgi_param DOCUMENT_ROOT $document_root; + fastcgi_param SCRIPT_FILENAME $document_root/cgit.cgi; + fastcgi_param PATH_INFO $uri; + fastcgi_param QUERY_STRING $args; + fastcgi_param HTTP_HOST $server_name; + fastcgi_pass unix:/var/run/fcgiwrap.socket; + } + + listen [::]:443 ssl; + listen 443 ssl; + ssl_certificate /etc/letsencrypt/live/git.akon.city/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/git.akon.city/privkey.pem; +} + +server { + if ($host = www.akon.city) { + return 301 https://$host$request_uri; + } # managed by Certbot + + if ($host = akon.city) { + return 301 https://$host$request_uri; + } # managed by Certbot + + if ($host = git.akon.city) { + return 301 https://$host$request_uri; + } + + listen 80; + listen [::]:80; + + server_name akon.city www.akon.city; + return 404; # managed by Certbot +} diff --git a/hosts/station/scripts/fail2ban-ban-ip b/hosts/station/scripts/fail2ban-ban-ip new file mode 100755 index 0000000..b1b97c6 --- /dev/null +++ b/hosts/station/scripts/fail2ban-ban-ip @@ -0,0 +1,3 @@ +#! /usr/bin/env sh +# Get jail names: sudo fail2ban-client status +fail2ban-client set $1 banip $2 diff --git a/hosts/station/scripts/fail2ban-unban-ip b/hosts/station/scripts/fail2ban-unban-ip new file mode 100755 index 0000000..a562cbc --- /dev/null +++ b/hosts/station/scripts/fail2ban-unban-ip @@ -0,0 +1,3 @@ +#! /usr/bin/env sh +# Get jail names: sudo fail2ban-client status +fail2ban-client set $1 unbanip $2 diff --git a/hosts/station/scripts/mail-stats b/hosts/station/scripts/mail-stats new file mode 100755 index 0000000..69e7c9a --- /dev/null +++ b/hosts/station/scripts/mail-stats @@ -0,0 +1,2 @@ +#! /usr/bin/env sh +sudo journalctl --no-tail _SYSTEMD_UNIT=postfix.service | sudo pflogsumm diff --git a/hosts/station/scripts/vacumm b/hosts/station/scripts/vacumm new file mode 100755 index 0000000..70185e1 --- /dev/null +++ b/hosts/station/scripts/vacumm @@ -0,0 +1,3 @@ +#! /usr/bin/env sh +sudo journalctl --rotate +sudo journalctl --vacuum-time=1s diff --git a/hosts/station/station_maintenance.txt b/hosts/station/station_maintenance.txt new file mode 100644 index 0000000..1ca3925 --- /dev/null +++ b/hosts/station/station_maintenance.txt @@ -0,0 +1,80 @@ += Station + +== Libvirt +virsh start station +virsh console station +'e' on grub menu +add `console=tty0 console=ttyS0,115200` to the cmdline +add ipv6 to default network +virsh net-edit default +--- + + + + + +--- +virsh net-destroy default +virsh net-start default + +== Debian +apt install rsync +disable sudo mail_badpass + +=== Wireguard +apt install wireguard wireguard-tools +cp wg-rod.conf /etc/wireguard/ +cp nftables.conf /etc/nftables.conf +sudo systemctl enable wg-quick@wg-rod +-apt install resolvconf- +apt install systemd-resolved +various things rely on resolv.conf +spamassassin has an issue with dns, systemd-resolved seems to avoid it. + +=== SSL +apt install nginx python3-certbox-nginx fcgiwrap +cp letsencrypt.zip /etc/letsencrypt +unzip letsencrypt.zip +cp akoncity /etc/nginx/sites-available +ln -s /etc/nginx/sites-available/akoncity /etc/nginx/sites +-enabled/ +sudo usermod --append --groups www-data git +sudo usermod --append --groups git www-data + +=== fail2ban +cp fail2ban.d/station.local /etc/fail2ban/fail2ban.d/ +cp jail.d/station.local /etc/fail2ban/jail.d/ +filter.d/dovecot-pop3imap.conf +https://doc.dovecot.org/main/howto/fail2ban.html + +=== email +apt install postfix postfix-pcre dovecot-imapd dovecot-pop3d dovecot-sieve opendkim opendkim-tools spamassassin spamc net-tools bind9-host +apt install pflogsumm +sudo postaliases /etc/aliases +sudo postmap [hash: files] +sievec default.sieve +chown dovecot:dovecot /var/lib/dovecot +sudo chown -R root:opendkim /etc/postfix/dkim +sudo groupadd -g 1001 vmail +sudo useradd -m -d /var/vmail -s /bin/false -u 1001 -g vmail vmail +sudo chown vmail:vmail /var/vmail +sudo chmod 2770 /var/vmail/ + +=== git +sudo groupadd git +sudo useradd -m -d /var/git -s /bin/bash -g git git +sudo chmod 755 /var/git +sudo cp git-config /var/git/.git-config +sudo unzip git-template.zip -d /var/git/.git-template +add .ssh/authorized_keys +sudo apt install asciidoctor python3-pygments ruby-pygments.rb + +=== cgit +git clone --recursive https://git.zx2c4.com/cgit +git apply ../../denpa/cgit/readme_in_summary_no_about.diff +apt install build-essential libcurl4-openssl-dev +make NO_LUA=1 +sudo make NO_LUA=1 install +cp custom.css /var/www/htdocs/cgit/ +cp cgitrc /etc/cgitrc +cp email-libravatar.py /usr/local/lib/cgit/filters/ diff --git a/hosts/station/systemd/fcgiwrap.service b/hosts/station/systemd/fcgiwrap.service new file mode 100644 index 0000000..f36f65f --- /dev/null +++ b/hosts/station/systemd/fcgiwrap.service @@ -0,0 +1,15 @@ +[Unit] +Description=Simple CGI Server +Documentation=man:fcgiwrap(8) +After=nss-user-lookup.target +Requires=fcgiwrap.socket + +[Service] +Environment=DAEMON_OPTS=-f +EnvironmentFile=-/etc/default/fcgiwrap +ExecStart=/usr/sbin/fcgiwrap $DAEMON_OPTS +User=www-data +Group=www-data + +[Install] +Also=fcgiwrap.socket diff --git a/hosts/station/wg-rod.conf.in b/hosts/station/wg-rod.conf.in new file mode 100644 index 0000000..dc295c7 --- /dev/null +++ b/hosts/station/wg-rod.conf.in @@ -0,0 +1,10 @@ +[Interface] +Address = /24, /64 +ListenPort = +PrivateKey = + +[Peer] +AllowedIPs = 0.0.0.0/0, ::/0 +Endpoint = : +PersistentKeepalive = 25 +PublicKey = -- cgit v1.2.3-101-g0448