Moving to modules. Structural changes.

This commit is contained in:
kat witch 2021-07-05 22:47:28 +01:00
parent 3903bc1766
commit 060d4c6d1e
No known key found for this signature in database
GPG key ID: 1B477797DCA5EC72
258 changed files with 621 additions and 407 deletions

View file

@ -1,57 +0,0 @@
{ config, lib, pkgs, tf, ... }:
with lib;
{
kw.fw.public.tcp.ports = [ 5160 5060 ];
kw.fw.public.udp.ports = [ 5160 5060 ];
kw.fw.public.tcp.ranges = [{
from = 10000;
to = 20000;
}];
kw.fw.public.udp.ranges = [{
from = 10000;
to = 20000;
}];
services.fail2ban.jails = {
asterisk = ''
enabled = true
filter = asterisk
action = nftables-allports
logpath = /var/log/asterisk/messages
maxretry = 4
'';
};
environment.systemPackages = with pkgs; [ asterisk ];
users.groups.asterisk = {
name = "asterisk";
};
users.users.asterisk = {
name = "asterisk";
group = "asterisk";
home = "/var/lib/asterisk";
isSystemUser = true;
};
systemd.services.asterisk = {
description = "Asterisk PBX Server";
wantedBy = [ "multi-user.target" ];
restartIfChanged = false;
serviceConfig = {
ExecStart = "${pkgs.asterisk}/bin/asterisk -U asterisk -C /etc/asterisk/asterisk.conf -F";
ExecReload = "${pkgs.asterisk}/bin/asterisk -x 'core reload'";
Type = "forking";
PIDFile = "/run/asterisk/asterisk.pid";
};
};
}

View file

@ -1,39 +0,0 @@
{ config, pkgs, ... }:
{
services.postgresql = {
ensureDatabases = [ "bitwarden_rs" ];
ensureUsers = [{
name = "bitwarden_rs";
ensurePermissions = { "DATABASE bitwarden_rs" = "ALL PRIVILEGES"; };
}];
};
services.bitwarden_rs = {
enable = true;
dbBackend = "postgresql";
config = {
rocketPort = 4000;
websocketEnabled = true;
signupsAllowed = false;
domain = "https://vault.kittywit.ch";
databaseUrl = "postgresql://bitwarden_rs@/bitwarden_rs";
};
};
services.nginx.virtualHosts."vault.kittywit.ch" = {
enableACME = true;
forceSSL = true;
locations = {
"/".proxyPass = "http://127.0.0.1:4000";
"/notifications/hub".proxyPass = "http://127.0.0.1:3012";
"/notifications/hub/negotiate".proxyPass = "http://127.0.0.1:80";
};
};
deploy.tf.dns.records.kittywitch_vault = {
tld = "kittywit.ch.";
domain = "vault";
cname.target = "athame.kittywit.ch.";
};
}

View file

@ -1,44 +0,0 @@
{ config, pkgs, lib, ... }:
with lib;
let
mailAccounts = config.mailserver.loginAccounts;
htpasswd = pkgs.writeText "radicale.users" (concatStrings
(flip mapAttrsToList mailAccounts
(mail: user: mail + ":" + user.hashedPassword + "\n")));
in
{
services.radicale = {
enable = true;
settings = {
auth = {
type = "htpasswd";
htpasswd_filename = toString htpasswd;
htpasswd_encryption = "bcrypt";
};
};
};
services.nginx.virtualHosts = {
"cal.kittywit.ch" = {
forceSSL = true;
enableACME = true;
locations."/" = {
proxyPass = "http://localhost:5232/";
extraConfig = ''
proxy_set_header X-Script-Name /;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass_header Authorization;
'';
};
};
};
deploy.tf.dns.records.kittywitch_cal = {
tld = "kittywit.ch.";
domain = "cal";
cname.target = "athame.kittywit.ch.";
};
}

View file

@ -1,41 +0,0 @@
{ config, pkgs, ... }:
{
services.fail2ban = {
enable = true;
packageFirewall = pkgs.nftables;
banaction = "nftables-multiport";
banaction-allports = "nftables-allports";
jails = {
default = ''
bantime = 7d
blocktype = DROP
action = nftables-allports
logpath = /var/log/auth.log
'';
ssh = ''
enabled = true
filter = sshd
maxretry = 4
action = nftables-multiport[name=SSH, port=ssh, protocol=tcp]
'';
sshd-ddos = ''
enabled = true
filter = sshd-ddos
maxretry = 4
action = nftables-multiport[name=ssh, port=ssh, protocol=tcp]
'';
};
};
environment.etc."fail2ban/filter.d/sshd-ddos.conf" = {
enable = true;
text = ''
[Definition]
failregex = sshd(?:\[\d+\])?: Did not receive identification string from <HOST>$
ignoreregex =
'';
};
systemd.services.fail2ban.serviceConfig.LimitSTACK = 128 * 1024;
}

View file

@ -1,59 +0,0 @@
{ config, pkgs, ... }:
{
services.postgresql = {
enable = true;
ensureDatabases = [ "gitea" ];
ensureUsers = [{
name = "gitea";
ensurePermissions."DATABASE gitea" = "ALL PRIVILEGES";
}];
};
services.gitea = {
enable = true;
disableRegistration = true;
domain = "git.kittywit.ch";
rootUrl = "https://git.kittywit.ch";
httpAddress = "127.0.0.1";
appName = "kittywitch git";
ssh = { clonePort = 62954; };
database = {
type = "postgres";
name = "gitea";
user = "gitea";
};
settings = {
security = { DISABLE_GIT_HOOKS = false; };
api = { ENABLE_SWAGGER = true; };
mailer = {
ENABLED = true;
MAILER_TYPE = "sendmail";
FROM = "gitea@kittywit.ch";
SENDMAIL_PATH = "${pkgs.system-sendmail}/bin/sendmail";
};
ui = {
THEMES = "gitea,arc-green";
DEFAULT_THEME = "gitea";
THEME_COLOR_META_TAG = "#222222";
};
};
};
systemd.services.gitea.preStart = ''
${pkgs.coreutils}/bin/ln -sfT ${./public} /var/lib/gitea/custom/public
${pkgs.coreutils}/bin/ln -sfT ${./templates} /var/lib/gitea/custom/templates
'';
services.nginx.virtualHosts."git.kittywit.ch" = {
enableACME = true;
forceSSL = true;
locations = { "/".proxyPass = "http://127.0.0.1:3000"; };
};
deploy.tf.dns.records.kittywitch_git = {
tld = "kittywit.ch.";
domain = "git";
cname.target = "athame.kittywit.ch.";
};
}

View file

@ -1 +0,0 @@
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 280 171.19"><defs><style>.cls-1,.cls-3{stroke:#000;stroke-miterlimit:10;}.cls-2{fill:#ccc;}.cls-3,.cls-4{fill:#f2f2f2;}</style></defs><path class="cls-1" d="M403.84,463.72s13.51-20.52,9.51-44.75c0,0-22.62-2.63-38,12.5C361.41,402.18,345.28,369,369.47,345c0,0,8,5.45,8.47,12.71,2,31.08-1.18,31.53-1.18,31.53,25.3-3.77,32.51-45.12,9.89-59.77-55.53-26.12-63.59-4.21-71.3,42.35-3.2,27.07-3.83,41.66-8.19,62.74-13.46-15.72-38.59-14.19-38-14-3.4,25,7.88,45.73,9.45,46.09-15,4.55-37.67,6.71-73,11.28,75.87,17.66,199,11.15,274.58-4.94C468.23,470.85,414.31,471.71,403.84,463.72Z" transform="translate(-202.9 -317.79)"/><polygon class="cls-2" points="98.68 121.45 90.04 110.5 90.44 120.65 75.53 115.4 83.03 130.62 71.94 130.38 81.58 144.37 98.68 121.45"/><path class="cls-3" d="M305.65,436.85" transform="translate(-202.9 -317.79)"/><path class="cls-4" d="M301.65,439.44l-.07-.2a85.43,85.43,0,0,0-6.65-5.65l0,6.41s-6,1.47-10.83-1.78c0,0,7.18,11.33,1.07,11.54a40.37,40.37,0,0,0-5.48.5l4.78,11.9C299.42,459.81,301.81,444.69,301.65,439.44Z" transform="translate(-202.9 -317.79)"/><polygon class="cls-2" points="179.03 119.28 187.68 108.33 187.28 118.47 202.19 113.22 194.69 128.44 205.78 128.21 196.13 142.19 179.03 119.28"/><path class="cls-4" d="M381.87,437.27l.07-.2a83.13,83.13,0,0,1,6.65-5.66l0,6.41s6,1.47,10.83-1.78c0,0-7.18,11.33-1.07,11.55a38.39,38.39,0,0,1,5.48.5L399,460C384.1,457.63,381.71,442.51,381.87,437.27Z" transform="translate(-202.9 -317.79)"/></svg>

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

View file

@ -1,29 +0,0 @@
<div class="ui secondary pointing tabular top attached borderless menu stackable new-menu navbar">
<a class="item {{if .PageIsAdminDashboard}}acty{{end}}" href="{{AppSubUrl}}/admin">
{{.i18n.Tr "admin.dashboard"}}
</a>
<a class="item {{if .PageIsAdminUsers}}acty{{end}}" href="{{AppSubUrl}}/admin/users">
{{.i18n.Tr "admin.users"}}
</a>
<a class="item {{if .PageIsAdminOrganizations}}acty{{end}}" href="{{AppSubUrl}}/admin/orgs">
{{.i18n.Tr "admin.organizations"}}
</a>
<a class="item {{if .PageIsAdminRepositories}}acty{{end}}" href="{{AppSubUrl}}/admin/repos">
{{.i18n.Tr "admin.repositories"}}
</a>
<a class="item {{if .PageIsAdminHooks}}acty{{end}}" href="{{AppSubUrl}}/admin/hooks">
{{.i18n.Tr "admin.hooks"}}
</a>
<a class="item {{if .PageIsAdminAuthentications}}acty{{end}}" href="{{AppSubUrl}}/admin/auths">
{{.i18n.Tr "admin.authentication"}}
</a>
<a class="item {{if .PageIsAdminConfig}}acty{{end}}" href="{{AppSubUrl}}/admin/config">
{{.i18n.Tr "admin.config"}}
</a>
<a class="item {{if .PageIsAdminNotices}}acty{{end}}" href="{{AppSubUrl}}/admin/notices">
{{.i18n.Tr "admin.notices"}}
</a>
<a class="item {{if .PageIsAdminMonitor}}acty{{end}}" href="{{AppSubUrl}}/admin/monitor">
{{.i18n.Tr "admin.monitor"}}
</a>
</div>

View file

@ -1,176 +0,0 @@
<!DOCTYPE html>
<html>
<head data-suburl="{{AppSubUrl}}">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<title>{{if .Title}}{{.Title}} - {{end}}{{AppName}}</title>
<link rel="manifest" href="{{AppSubUrl}}/manifest.json">
<script>
if ('serviceWorker' in navigator) {
window.addEventListener('load', function() {
navigator.serviceWorker.register('{{AppSubUrl}}/serviceworker.js').then(function(registration) {
// Registration was successful
console.log('ServiceWorker registration successful with scope: ', registration.scope);
}, function(err) {
// registration failed :(
console.log('ServiceWorker registration failed: ', err);
});
});
}
</script>
<meta name="theme-color" content="{{ThemeColorMetaTag}}">
<meta name="author" content="{{if .Repository}}{{.Owner.Name}}{{else}}{{MetaAuthor}}{{end}}" />
<meta name="description" content="{{if .Repository}}{{.Repository.Name}}{{if .Repository.Description}} - {{.Repository.Description}}{{end}}{{else}}{{MetaDescription}}{{end}}" />
<meta name="keywords" content="{{MetaKeywords}}">
<meta name="referrer" content="no-referrer" />
<meta name="_csrf" content="{{.CsrfToken}}" />
<meta name="_suburl" content="{{AppSubUrl}}" />
{{if .IsSigned}}
<meta name="_uid" content="{{.SignedUser.ID}}" />
{{end}}
{{if .ContextUser}}
<meta name="_context_uid" content="{{.ContextUser.ID}}" />
{{end}}
{{if .SearchLimit}}
<meta name="_search_limit" content="{{.SearchLimit}}" />
{{end}}
{{if .GoGetImport}}
<meta name="go-import" content="{{.GoGetImport}} git {{.CloneLink.HTTPS}}">
<meta name="go-source" content="{{.GoGetImport}} _ {{.GoDocDirectory}} {{.GoDocFile}}">
{{end}}
<script>
{{SafeJS `/*
@licstart The following is the entire license notice for the
JavaScript code in this page.
Copyright (c) 2016 The Gitea Authors
Copyright (c) 2015 The Gogs Authors
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.
---
Licensing information for additional javascript libraries can be found at:
{{AppSubUrl}}/vendor/librejs.html
@licend The above is the entire license notice
for the JavaScript code in this page.
*/`}}
</script>
<link rel="shortcut icon" href="{{AppSubUrl}}/img/favicon.png" />
<link rel="mask-icon" href="{{AppSubUrl}}/img/gitea-safari.svg" color="#609926">
<link rel="preload" href="{{AppSubUrl}}/vendor/assets/font-awesome/css/font-awesome.min.css" as="style" onload="this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="{{AppSubUrl}}/vendor/assets/font-awesome/css/font-awesome.min.css"></noscript>
<link rel="stylesheet" href="{{AppSubUrl}}/vendor/assets/octicons/octicons.min.css">
{{if .RequireSimpleMDE}}
<link rel="stylesheet" href="{{AppSubUrl}}/vendor/plugins/simplemde/simplemde.min.css">
{{end}}
{{if .RequireGitGraph}}
<!-- graph -->
<link rel="stylesheet" href="{{AppSubUrl}}/vendor/plugins/gitgraph/gitgraph.css">
{{end}}
{{if .RequireTribute}}
<link rel="stylesheet" href="{{AppSubUrl}}/vendor/plugins/tribute/tribute.css">
{{end}}
<!-- Stylesheet -->
<link rel="stylesheet" href="{{AppSubUrl}}/vendor/plugins/semantic/semantic.min.css">
<link rel="stylesheet" href="{{AppSubUrl}}/css/index.css?v={{MD5 AppVer}}">
<noscript>
<style>
.dropdown:hover > .menu { display: block; }
.ui.secondary.menu .dropdown.item > .menu { margin-top: 0; }
</style>
</noscript>
{{if .RequireHighlightJS}}
<link rel="stylesheet" href="{{AppSubUrl}}/vendor/plugins/highlight/github.css">
{{end}}
{{if .RequireMinicolors}}
<link rel="stylesheet" href="{{AppSubUrl}}/vendor/plugins/jquery.minicolors/jquery.minicolors.css">
{{end}}
{{if .RequireDatetimepicker}}
<link rel="stylesheet" href="{{AppSubUrl}}/vendor/plugins/jquery.datetimepicker/jquery.datetimepicker.css">
{{end}}
{{if .RequireDropzone}}
<link rel="stylesheet" href="{{AppSubUrl}}/vendor/plugins/dropzone/dropzone.css">
{{end}}
{{if .EnableHeatmap}}
<link rel="stylesheet" href="{{AppSubUrl}}/vendor/plugins/vue-calendar-heatmap/vue-calendar-heatmap.css">
{{end}}
<style class="list-search-style"></style>
<script src="{{AppSubUrl}}/vendor/plugins/cssrelpreload/loadCSS.min.js"></script>
<script src="{{AppSubUrl}}/vendor/plugins/cssrelpreload/cssrelpreload.min.js"></script>
{{if .PageIsUserProfile}}
<meta property="og:title" content="{{.Owner.Name}}" />
<meta property="og:type" content="profile" />
<meta property="og:image" content="{{.Owner.AvatarLink}}" />
<meta property="og:url" content="{{.Owner.HTMLURL}}" />
<meta property="og:site_name" content="{{AppName}}" />
{{else if .Repository}}
<meta property="og:title" content="{{.Repository.Name}}" />
<meta property="og:type" content="object" />
<meta property="og:image" content="{{.Repository.Owner.AvatarLink}}" />
<meta property="og:url" content="{{.Repository.HTMLURL}}" />
{{if .Repository.Description}}
<meta property="og:description" content="{{.Repository.Description}}" />
{{end}}
<meta property="og:site_name" content="{{AppName}}" />
{{else}}
<meta property="og:title" content="{{AppName}}">
<meta property="og:type" content="website" />
<meta property="og:image" content="{{AppSubUrl}}/img/gitea-lg.png" />
<meta property="og:url" content="{{AppUrl}}" />
<meta property="og:description" content="{{MetaDescription}}">
{{end}}
{{if .IsSigned }}
{{ if ne .SignedUser.Theme "gitea" }}
<link rel="stylesheet" href="{{AppSubUrl}}/css/theme-{{.SignedUser.Theme}}.css">
{{end}}
{{else if ne DefaultTheme "gitea"}}
<link rel="stylesheet" href="{{AppSubUrl}}/css/theme-{{DefaultTheme}}.css">
{{end}}
{{template "custom/header" .}}
</head>
<body>
{{template "custom/body_outer_pre" .}}
<div class="full height">
<noscript>{{.i18n.Tr "enable_javascript"}}</noscript>
{{template "custom/body_inner_pre" .}}
{{if not .PageIsInstall}}
<div class="ui top secondary stackable main menu following bar light">
{{template "base/head_navbar" .}}
</div><!-- end bar -->
{{end}}
{{/*
</div>
</body>
</html>
*/}}

View file

@ -1,16 +0,0 @@
<div class="ui secondary pointing tabular top attached borderless stackable menu navbar">
<a class="item {{if .PageIsExploreRepositories}}acty{{end}}" href="{{AppSubUrl}}/explore/repos">
{{svg "octicon-repo" 16}} {{.i18n.Tr "explore.repos"}}
</a>
<a class="item {{if .PageIsExploreUsers}}acty{{end}}" href="{{AppSubUrl}}/explore/users">
{{svg "octicon-person" 16}} {{.i18n.Tr "explore.users"}}
</a>
<a class="item {{if .PageIsExploreOrganizations}}acty{{end}}" href="{{AppSubUrl}}/explore/organizations">
{{svg "octicon-organization" 16}} {{.i18n.Tr "explore.organizations"}}
</a>
{{if .IsRepoIndexerEnabled}}
<a class="item {{if .PageIsExploreCode}}acty{{end}}" href="{{AppSubUrl}}/explore/code">
{{svg "octicon-code" 16}} {{.i18n.Tr "explore.code"}}
</a>
{{end}}
</div>

View file

@ -1,18 +0,0 @@
{{template "base/head" .}}
<div class="home">
<div class="ui stackable middle very relaxed page grid">
<div class="sixteen wide center aligned centered column">
<div>
<img class="logo" src="{{StaticUrlPrefix}}/img/gitea-lg.png" />
</div>
<div class="hero">
<br />
<h1 class="ui icon header title">
{{AppName}}
</h1>
<a href="https://kittywit.ch"><h2>back to home</h2></a>
</div>
</div>
</div>
</div>
{{template "base/footer" .}}

View file

@ -1,143 +0,0 @@
<div class="header-wrapper">
{{with .Repository}}
<div class="ui container">
<div class="repo-header">
<div class="ui huge breadcrumb repo-title">
{{if .RelAvatarLink}}
<img class="ui avatar image" src="{{.RelAvatarLink}}">
{{else if .IsTemplate}}
{{if .IsPrivate}}
{{svg "octicon-repo-template-private" 32}}
{{else}}
{{svg "octicon-repo-template" 32}}
{{end}}
{{else}}
{{if .IsPrivate}}
{{svg "octicon-lock" 32}}
{{else if .IsMirror}}
{{svg "octicon-repo-clone" 32}}
{{else if .IsFork}}
{{svg "octicon-repo-fork" 32}}
{{else}}
{{svg "octicon-repo" 32}}
{{end}}
{{end}}
<a href="{{AppSubUrl}}/{{.Owner.Name}}">{{.Owner.Name}}</a>
<div class="divider"> / </div>
<a href="{{$.RepoLink}}">{{.Name}}</a>
{{if .RelAvatarLink}}
{{if .IsTemplate}}
{{if .IsPrivate}}
{{svg "octicon-repo-template-private" 32}}
{{else}}
{{svg "octicon-repo-template" 32}}
{{end}}
{{else}}
{{if .IsPrivate}}
{{svg "octicon-lock" 32}}
{{else if .IsMirror}}
{{svg "octicon-repo-clone" 32}}
{{else if .IsFork}}
{{svg "octicon-repo-fork" 32}}
{{else}}
{{svg "octicon-repo" 32}}
{{end}}
{{end}}
{{end}}
{{if .IsArchived}}<i class="archive icon archived-icon"></i>{{end}}
{{if .IsMirror}}<div class="fork-flag">{{$.i18n.Tr "repo.mirror_from"}} <a target="_blank" rel="noopener noreferrer" href="{{if .SanitizedOriginalURL}}{{.SanitizedOriginalURL}}{{else}}{{MirrorAddress $.Mirror}}{{end}}">{{if .SanitizedOriginalURL}}{{.SanitizedOriginalURL}}{{else}}{{MirrorAddress $.Mirror}}{{end}}</a></div>{{end}}
{{if .IsFork}}<div class="fork-flag">{{$.i18n.Tr "repo.forked_from"}} <a href="{{.BaseRepo.Link}}">{{SubStr .BaseRepo.RelLink 1 -1}}</a></div>{{end}}
{{if .IsGenerated}}<div class="fork-flag">{{$.i18n.Tr "repo.generated_from"}} <a href="{{.TemplateRepo.Link}}">{{SubStr .TemplateRepo.RelLink 1 -1}}</a></div>{{end}}
</div>
{{if not .IsBeingCreated}}
<div class="repo-buttons">
<div class="ui labeled button" tabindex="0">
<a class="ui compact basic button" href="{{$.RepoLink}}/action/{{if $.IsWatchingRepo}}un{{end}}watch?redirect_to={{$.Link}}">
<i class="icon fa-eye{{if not $.IsWatchingRepo}}-slash{{end}}"></i>{{if $.IsWatchingRepo}}{{$.i18n.Tr "repo.unwatch"}}{{else}}{{$.i18n.Tr "repo.watch"}}{{end}}
</a>
<a class="ui basic label" href="{{.Link}}/watchers">
{{.NumWatches}}
</a>
</div>
<div class="ui labeled button" tabindex="0">
<a class="ui compact basic button" href="{{$.RepoLink}}/action/{{if $.IsStaringRepo}}un{{end}}star?redirect_to={{$.Link}}">
<i class="icon star{{if not $.IsStaringRepo}} outline{{end}}"></i>{{if $.IsStaringRepo}}{{$.i18n.Tr "repo.unstar"}}{{else}}{{$.i18n.Tr "repo.star"}}{{end}}
</a>
<a class="ui basic label" href="{{.Link}}/stars">
{{.NumStars}}
</a>
</div>
{{if and (not .IsEmpty) ($.Permission.CanRead $.UnitTypeCode)}}
<div class="ui labeled button {{if and ($.IsSigned) (not $.CanSignedUserFork)}}disabled-repo-button{{end}}" tabindex="0">
<a class="ui compact basic button {{if or (not $.IsSigned) (not $.CanSignedUserFork)}}poping up{{end}}" {{if $.CanSignedUserFork}}href="{{AppSubUrl}}/repo/fork/{{.ID}}"{{else if $.IsSigned}} data-content="{{$.i18n.Tr "repo.fork_from_self"}}" {{ else }} data-content="{{$.i18n.Tr "repo.fork_guest_user" }}" rel="nofollow" href="{{AppSubUrl}}/user/login?redirect_to={{AppSubUrl}}/repo/fork/{{.ID}}" {{end}} data-position="top center" data-variation="tiny">
{{svg "octicon-repo-forked" 16}}{{$.i18n.Tr "repo.fork"}}
</a>
<a class="ui basic label" href="{{.Link}}/forks">
{{.NumForks}}
</a>
</div>
{{end}}
</div>
{{end}}
</div><!-- end grid -->
</div><!-- end container -->
{{end}}
<div class="ui tabs container">
{{if not .Repository.IsBeingCreated}}
<div class="ui tabular stackable menu navbar">
{{if .Permission.CanRead $.UnitTypeCode}}
<a class="item {{if .PageIsViewCode}}acty{{end}}" href="{{.RepoLink}}{{if (ne .BranchName .Repository.DefaultBranch)}}/src/{{.BranchNameSubURL | EscapePound}}{{end}}">
{{svg "octicon-code" 16}} {{.i18n.Tr "repo.code"}}
</a>
{{end}}
{{if .Permission.CanRead $.UnitTypeIssues}}
<a class="item {{if .PageIsIssueList}}acty{{end}}" href="{{.RepoLink}}/issues">
{{svg "octicon-issue-opened" 16}} {{.i18n.Tr "repo.issues"}} <span class="ui {{if not .Repository.NumOpenIssues}}gray{{else}}blue{{end}} small label">{{.Repository.NumOpenIssues}}</span>
</a>
{{end}}
{{if .Permission.CanRead $.UnitTypeExternalTracker}}
<a class="item {{if .PageIsIssueList}}acty{{end}}" href="{{.RepoExternalIssuesLink}}" target="_blank" rel="noopener noreferrer">
{{svg "octicon-link-external" 16}} {{.i18n.Tr "repo.issues"}} </span>
</a>
{{end}}
{{if and .Repository.CanEnablePulls (.Permission.CanRead $.UnitTypePullRequests)}}
<a class="item {{if .PageIsPullList}}acty{{end}}" href="{{.RepoLink}}/pulls">
{{svg "octicon-git-pull-request" 16}} {{.i18n.Tr "repo.pulls"}} <span class="ui {{if not .Repository.NumOpenPulls}}gray{{else}}blue{{end}} small label">{{.Repository.NumOpenPulls}}</span>
</a>
{{end}}
{{if and (.Permission.CanRead $.UnitTypeReleases) (not .IsEmptyRepo) }}
<a class="item {{if .PageIsReleaseList}}acty{{end}}" href="{{.RepoLink}}/releases">
{{svg "octicon-tag" 16}} {{.i18n.Tr "repo.releases"}} <span class="ui {{if not .NumReleases}}gray{{else}}blue{{end}} small label">{{.NumReleases}}</span>
</a>
{{end}}
{{if or (.Permission.CanRead $.UnitTypeWiki) (.Permission.CanRead $.UnitTypeExternalWiki)}}
<a class="item {{if .PageIsWiki}}acty{{end}}" href="{{.RepoLink}}/wiki" {{if (.Permission.CanRead $.UnitTypeExternalWiki)}} target="_blank" rel="noopener noreferrer" {{end}}>
{{svg "octicon-book" 16}} {{.i18n.Tr "repo.wiki"}}
</a>
{{end}}
{{if and (.Permission.CanReadAny $.UnitTypePullRequests $.UnitTypeIssues $.UnitTypeReleases) (not .IsEmptyRepo)}}
<a class="item {{if .PageIsActivity}}acty{{end}}" href="{{.RepoLink}}/activity">
{{svg "octicon-pulse" 16}} {{.i18n.Tr "repo.activity"}}
</a>
{{end}}
{{template "custom/extra_tabs" .}}
{{if .Permission.IsAdmin}}
<div class="right menu">
<a class="item {{if .PageIsSettings}}acty{{end}}" href="{{.RepoLink}}/settings">
{{svg "octicon-tools" 16}} {{.i18n.Tr "repo.settings"}}
</a>
</div>
{{end}}
</div>
{{end}}
</div>
<div class="ui tabs divider"></div>
</div>

View file

@ -1,153 +0,0 @@
{{template "base/head" .}}
<div class="repository file list">
{{template "repo/header" .}}
<div class="ui container">
{{template "base/alert" .}}
<div class="ui repo-description">
<div id="repo-desc">
{{if .Repository.DescriptionHTML}}<span class="description has-emoji">{{.Repository.DescriptionHTML}}</span>{{else if .IsRepositoryAdmin}}<span class="no-description text-italic">{{.i18n.Tr "repo.no_desc"}}</span>{{end}}
<a class="link" href="{{.Repository.Website}}">{{.Repository.Website}}</a>
</div>
{{if .RepoSearchEnabled}}
<div class="ui repo-search">
<form class="ui form ignore-dirty" action="{{.RepoLink}}/search" method="get">
<div class="field">
<div class="ui action input">
<input name="q" value="{{.Keyword}}" placeholder="{{.i18n.Tr "repo.search.search_repo"}}">
<button class="ui icon button" type="submit">
<i class="search icon"></i>
</button>
</div>
</div>
</form>
</div>
{{end}}
</div>
<div class="ui" id="repo-topics">
{{range .Topics}}<a class="ui repo-topic small label topic" href="{{AppSubUrl}}/explore/repos?q={{.Name}}&topic=1">{{.Name}}</a>{{end}}
{{if and .Permission.IsAdmin (not .Repository.IsArchived)}}<a id="manage_topic">{{.i18n.Tr "repo.topic.manage_topics"}}</a>{{end}}
</div>
{{if and .Permission.IsAdmin (not .Repository.IsArchived)}}
<div class="ui repo-topic-edit grid form segment error" id="topic_edit" style="display:none">
<div class="fourteen wide column">
<div class="field">
<div class="ui fluid multiple search selection dropdown">
<input type="hidden" name="topics" value="{{range $i, $v := .Topics}}{{.Name}}{{if lt (Add $i 1) (len $.Topics)}},{{end}}{{end}}">
{{range .Topics}}
<div class="ui small label topic transition visible" data-value="{{.Name}}" style="display: inline-block !important; cursor: default;">{{.Name}}<i class="delete icon"></i></div>
{{end}}
<div class="text"></div>
</div>
</div>
</div>
<div class="two wide column">
<a class="ui button primary" href="javascript:;" id="save_topic"
data-link="{{.RepoLink}}/topics">{{.i18n.Tr "repo.topic.done"}}</a>
</div>
</div>
{{end}}
<div class="hide" id="validate_prompt">
<span id="count_prompt">{{.i18n.Tr "repo.topic.count_prompt"}}</span>
<span id="format_prompt">{{.i18n.Tr "repo.topic.format_prompt"}}</span>
</div>
{{if .Repository.IsArchived}}
<div class="ui warning message">
{{.i18n.Tr "repo.archive.title"}}
</div>
{{end}}
{{template "repo/sub_menu" .}}
<div class="ui stackable secondary menu mobile--margin-between-items mobile--no-negative-margins">
{{template "repo/branch_dropdown" .}}
{{ $n := len .TreeNames}}
{{ $l := Subtract $n 1}}
<!-- If home page, show new PR. If not, show breadcrumb -->
{{if eq $n 0}}
{{if and .PullRequestCtx.Allowed .IsViewBranch (not .Repository.IsArchived)}}
<div class="fitted item">
<a href="{{.BaseRepo.Link}}/compare/{{.BaseRepo.DefaultBranch | EscapePound}}...{{if ne .Repository.Owner.Name .BaseRepo.Owner.Name}}{{.Repository.Owner.Name}}:{{end}}{{.BranchName | EscapePound}}">
<button id="new-pull-request" class="ui compact basic button">{{.i18n.Tr "repo.pulls.compare_changes"}}</button>
</a>
</div>
{{end}}
{{else}}
<div class="fitted item"><span class="ui breadcrumb repo-path"><a class="section" href="{{.RepoLink}}/src/{{EscapePound .BranchNameSubURL}}" title="{{.Repository.Name}}">{{EllipsisString .Repository.Name 30}}</a>{{range $i, $v := .TreeNames}}<span class="divider">/</span>{{if eq $i $l}}<span class="active section" title="{{$v}}">{{EllipsisString $v 30}}</span>{{else}}{{ $p := index $.Paths $i}}<span class="section"><a href="{{EscapePound $.BranchLink}}/{{EscapePound $p}}" title="{{$v}}">{{EllipsisString $v 30}}</a></span>{{end}}{{end}}</span></div>
{{end}}
<div class="right fitted item" id="file-buttons">
<div class="ui tiny blue buttons">
{{if .Repository.CanEnableEditor}}
{{if .CanAddFile}}
<a href="{{.RepoLink}}/_new/{{EscapePound .BranchName}}/{{EscapePound .TreePath}}" class="ui button">
{{.i18n.Tr "repo.editor.new_file"}}
</a>
{{end}}
{{if .CanUploadFile}}
<a href="{{.RepoLink}}/_upload/{{EscapePound .BranchName}}/{{EscapePound .TreePath}}" class="ui button">
{{.i18n.Tr "repo.editor.upload_file"}}
</a>
{{end}}
{{end}}
{{if and (ne $n 0) (not .IsViewFile) (not .IsBlame) }}
<a href="{{.RepoLink}}/commits/{{EscapePound .BranchNameSubURL}}/{{EscapePound .TreePath}}" class="ui button">
{{.i18n.Tr "repo.file_history"}}
</a>
{{end}}
</div>
</div>
<div class="fitted item">
{{if eq $n 0}}
{{if .Repository.IsTemplate}}
<div class="ui tiny blue buttons">
<a href="{{AppSubUrl}}/repo/create?template_id={{.Repository.ID}}" class="ui button">
{{.i18n.Tr "repo.use_template"}}
</a>
</div>
{{end}}
{{end}}
</div>
<div class="fitted item">
<!-- Only show clone panel in repository home page -->
{{if eq $n 0}}
<div class="ui action tiny input" id="clone-panel">
{{if not $.DisableHTTP}}
<button class="ui basic clone button" id="repo-clone-https" data-link="{{.CloneLink.HTTPS}}">
{{if UseHTTPS}}HTTPS{{else}}HTTP{{end}}
</button>
{{end}}
{{if and (not $.DisableSSH) (or $.IsSigned $.ExposeAnonSSH)}}
<button class="ui basic clone button" id="repo-clone-ssh" data-link="{{.CloneLink.SSH}}">
SSH
</button>
{{end}}
{{if not $.DisableHTTP}}
<input id="repo-clone-url" value="{{$.CloneLink.HTTPS}}" readonly>
{{else if and (not $.DisableSSH) (or $.IsSigned $.ExposeAnonSSH)}}
<input id="repo-clone-url" value="{{$.CloneLink.SSH}}" readonly>
{{end}}
{{if or (not $.DisableHTTP) (and (not $.DisableSSH) (or $.IsSigned $.ExposeAnonSSH))}}
<button class="ui basic icon button poping up clipboard" id="clipboard-btn" data-original="{{.i18n.Tr "repo.copy_link"}}" data-success="{{.i18n.Tr "repo.copy_link_success"}}" data-error="{{.i18n.Tr "repo.copy_link_error"}}" data-content="{{.i18n.Tr "repo.copy_link"}}" data-variation="inverted tiny" data-clipboard-target="#repo-clone-url">
{{svg "octicon-clippy" 16}}
</button>
{{end}}
<div class="ui basic jump dropdown icon button poping up" data-content="{{.i18n.Tr "repo.download_archive"}}" data-variation="tiny inverted" data-position="top right">
<i class="download icon"></i>
<div class="menu">
<a class="item" href="{{$.RepoLink}}/archive/{{EscapePound $.BranchName}}.zip">{{svg "octicon-file-zip" 16}}&nbsp;ZIP</a>
<a class="item" href="{{$.RepoLink}}/archive/{{EscapePound $.BranchName}}.tar.gz">{{svg "octicon-file-zip" 16}}&nbsp;TAR.GZ</a>
</div>
</div>
</div>
{{end}}
</div>
</div>
{{if .IsViewFile}}
{{template "repo/view_file" .}}
{{else if .IsBlame}}
{{template "repo/blame" .}}
{{else}}
{{template "repo/view_list" .}}
{{end}}
</div>
</div>
{{template "base/footer" .}}

View file

@ -1,29 +0,0 @@
<div class="ui secondary pointing tabular top attached borderless menu stackable new-menu navbar">
<a class="item {{if .PageIsSettingsOptions}}acty{{end}}" href="{{.RepoLink}}/settings">
{{.i18n.Tr "repo.settings.options"}}
</a>
<a class="item {{if .PageIsSettingsCollaboration}}acty{{end}}" href="{{.RepoLink}}/settings/collaboration">
{{.i18n.Tr "repo.settings.collaboration"}}
</a>
{{if not .Repository.IsEmpty}}
<a class="item {{if .PageIsSettingsBranches}}acty{{end}}" href="{{.RepoLink}}/settings/branches">
{{.i18n.Tr "repo.settings.branches"}}
</a>
{{end}}
<a class="item {{if .PageIsSettingsHooks}}acty{{end}}" href="{{.RepoLink}}/settings/hooks">
{{.i18n.Tr "repo.settings.hooks"}}
</a>
{{if .SignedUser.CanEditGitHook}}
<a class="item {{if .PageIsSettingsGitHooks}}acty{{end}}" href="{{.RepoLink}}/settings/hooks/git">
{{.i18n.Tr "repo.settings.githooks"}}
</a>
{{end}}
<a class="item {{if .PageIsSettingsKeys}}acty{{end}}" href="{{.RepoLink}}/settings/keys">
{{.i18n.Tr "repo.settings.deploy_keys"}}
</a>
{{if .LFSStartServer}}
<a class="item {{if .PageIsSettingsLFS}}acty{{end}}" href="{{.RepoLink}}/settings/lfs">
{{.i18n.Tr "repo.settings.lfs"}}
</a>
{{end}}
</div>

View file

@ -1,132 +0,0 @@
{{template "base/head" .}}
<div class="dashboard feeds">
{{template "user/dashboard/navbar" .}}
<div class="ui container">
{{template "base/alert" .}}
<div class="ui mobile reversed stackable grid">
<div class="ui container ten wide column">
{{if .EnableHeatmap}}
<div id="user-heatmap" style="padding-right: 40px">
<activity-heatmap :locale="locale" :suburl="suburl" :user="heatmapUser">
<div slot="loading">
<div class="ui active centered inline indeterminate text loader" id="loading-heatmap">{{.i18n.Tr "user.heatmap.loading"}}</div>
</div>
</activity-heatmap>
<div class="ui divider"></div>
</div>
{{end}}
{{template "user/dashboard/feeds" .}}
</div>
<div id="app" class="six wide column">
<repo-search
:search-limit="searchLimit"
:suburl="suburl"
:uid="uid"
:more-repos-link="'{{.ContextUser.HomeLink}}'"
{{if not .ContextUser.IsOrganization}}
:organizations="[
{{range .ContextUser.Orgs}}
{name: '{{.Name}}', num_repos: '{{.NumRepos}}'},
{{end}}
]"
:is-organization="false"
:organizations-total-count="{{.ContextUser.GetOrganizationCount}}"
:can-create-organization="{{.SignedUser.CanCreateOrganization}}"
{{end}}
inline-template
v-cloak
>
<div>
<div v-if="!isOrganization" class="ui two item tabable menu">
<a :class="{item: true, acty: tab === 'repos'}" @click="changeTab('repos')">{{.i18n.Tr "repository"}}</a>
<a :class="{item: true, acty: tab === 'organizations'}" @click="changeTab('organizations')">{{.i18n.Tr "organization"}}</a>
</div>
<div v-show="tab === 'repos'" class="ui tab active list dashboard-repos">
<h4 class="ui top attached header">
{{.i18n.Tr "home.my_repos"}} <span class="ui grey label">${reposTotalCount}</span>
{{if or (not .ContextUser.IsOrganization) .IsOrganizationOwner}}
<div class="ui right">
<a class="poping up" :href="suburl + '/repo/create{{if .ContextUser.IsOrganization}}?org={{.ContextUser.ID}}{{end}}'" data-content="{{.i18n.Tr "new_repo"}}" data-variation="tiny inverted" data-position="left center">
<i class="plus icon"></i>
<span class="sr-only">{{.i18n.Tr "new_repo"}}</span>
</a>
</div>
{{end}}
</h4>
<div class="ui attached secondary segment repos-search">
<div class="ui fluid icon input" :class="{loading: isLoading}">
<input @input="searchRepos(reposFilter)" v-model="searchQuery" ref="search" placeholder="{{.i18n.Tr "home.search_repos"}}">
<i class="search icon"></i>
</div>
<div class="ui secondary tiny pointing borderless menu center aligned grid repos-filter">
<a class="item" :class="{acty: reposFilter === 'all'}" @click="changeReposFilter('all')">
{{.i18n.Tr "all"}}
<div v-show="reposFilter === 'all'" class="ui circular mini grey label">${repoTypeCount}</div>
</a>
<a class="item" :class="{acty: reposFilter === 'sources'}" @click="changeReposFilter('sources')">
{{.i18n.Tr "sources"}}
<div v-show="reposFilter === 'sources'" class="ui circular mini grey label">${repoTypeCount}</div>
</a>
<a class="item" :class="{acty: reposFilter === 'forks'}" @click="changeReposFilter('forks')">
{{.i18n.Tr "forks"}}
<div v-show="reposFilter === 'forks'" class="ui circular mini grey label">${repoTypeCount}</div>
</a>
<a class="item" :class="{acty: reposFilter === 'mirrors'}" @click="changeReposFilter('mirrors')">
{{.i18n.Tr "mirrors"}}
<div v-show="reposFilter === 'mirrors'" class="ui circular mini grey label">${repoTypeCount}</div>
</a>
<a class="item" :class="{acty: reposFilter === 'collaborative'}" @click="changeReposFilter('collaborative')">
{{.i18n.Tr "collaborative"}}
<div v-show="reposFilter === 'collaborative'" class="ui circular mini grey label">${repoTypeCount}</div>
</a>
</div>
</div>
<div class="ui attached table segment">
<ul class="repo-owner-name-list">
<li v-for="repo in repos" :class="{'private': repo.private}" v-show="showRepo(repo, reposFilter)">
<a :href="suburl + '/' + repo.full_name">
<svg :class="'svg ' + repoClass(repo)" width="16" height="16" aria-hidden="true"><use :xlink:href="staticPrefix + '/img/svg/icons.svg#' + repoClass(repo)" /></svg>
<strong class="text truncate item-name">${repo.full_name}</strong>
<i v-if="repo.archived" class="archive icon archived-icon"></i>
<span class="ui right text light grey">
${repo.stars_count} <span class="rear">{{svg "octicon-star" 16}}</span>
</span>
</a>
</li>
<li v-if="showMoreReposLink">
<a :href="moreReposLink">{{.i18n.Tr "home.show_more_repos"}}</a>
</li>
</ul>
</div>
</div>
<div v-if="!isOrganization" v-show="tab === 'organizations'" class="ui tab active list">
<h4 class="ui top attached header">
{{.i18n.Tr "home.my_orgs"}} <span class="ui grey label">${organizationsTotalCount}</span>
<div v-if="canCreateOrganization" class="ui right">
<a class="poping up" :href="suburl + '/org/create'" data-content="{{.i18n.Tr "new_org"}}" data-variation="tiny inverted" data-position="left center">
<i class="plus icon"></i>
<span class="sr-only">{{.i18n.Tr "new_org"}}</span>
</a>
</div>
</h4>
<div class="ui attached table segment">
<ul class="repo-owner-name-list">
<li v-for="org in organizations">
<a :href="suburl + '/' + org.name">
{{svg "octicon-organization" 16}}
<strong class="text truncate item-name">${org.name}</strong>
<span class="ui right text light grey">
${org.num_repos} <span class="rear">{{svg "octicon-repo" 16}}</span>
</span>
</a>
</li>
</ul>
</div>
</div>
</div>
</repo-search>
</div>
</div>
</div>
</div>
{{template "base/footer" .}}

View file

@ -1,23 +0,0 @@
<div class="ui secondary pointing tabular top attached borderless menu stackable new-menu navbar">
<a class="item {{if .PageIsSettingsProfile}}acty{{end}}" href="{{AppSubUrl}}/user/settings">
{{.i18n.Tr "settings.profile"}}
</a>
<a class="item {{if .PageIsSettingsAccount}}acty{{end}}" href="{{AppSubUrl}}/user/settings/account">
{{.i18n.Tr "settings.account"}}
</a>
<a class="item {{if .PageIsSettingsSecurity}}acty{{end}}" href="{{AppSubUrl}}/user/settings/security">
{{.i18n.Tr "settings.security"}}
</a>
<a class="item {{if .PageIsSettingsApplications}}acty{{end}}" href="{{AppSubUrl}}/user/settings/applications">
{{.i18n.Tr "settings.applications"}}
</a>
<a class="item {{if .PageIsSettingsKeys}}acty{{end}}" href="{{AppSubUrl}}/user/settings/keys">
{{.i18n.Tr "settings.ssh_gpg_keys"}}
</a>
<a class="item {{if .PageIsSettingsOrganization}}acty{{end}}" href="{{AppSubUrl}}/user/settings/organization">
{{.i18n.Tr "settings.organization"}}
</a>
<a class="item {{if .PageIsSettingsRepos}}acty{{end}}" href="{{AppSubUrl}}/user/settings/repos">
{{.i18n.Tr "settings.repos"}}
</a>
</div>

View file

@ -1,36 +0,0 @@
{ config, ... }:
{
services.postgresql = {
ensureDatabases = [ "grafana" ];
ensureUsers = [{
name = "grafana";
ensurePermissions."DATABASE grafana" = "ALL PRIVILEGES";
}];
};
services.grafana = {
enable = true;
port = 3001;
domain = "graph.kittywit.ch";
rootUrl = "https://graph.kittywit.ch/";
database = {
type = "postgres";
host = "/run/postgresql/";
user = "grafana";
name = "grafana";
};
};
services.nginx.virtualHosts."graph.kittywit.ch" = {
enableACME = true;
forceSSL = true;
locations = { "/".proxyPass = "http://127.0.0.1:3001"; };
};
deploy.tf.dns.records.kittywitch_graph = {
tld = "kittywit.ch.";
domain = "graph";
cname.target = "athame.kittywit.ch.";
};
}

View file

@ -1,25 +0,0 @@
{ config, lib, ... }:
with lib;
{
services.logrotate = {
enable = true;
paths = {
nginx = mkIf config.services.nginx.enable {
path = "/var/log/nginx/*.log";
user = "nginx";
group = "nginx";
frequency = "weekly";
keep = 2;
};
asterisk = mkIf config.systemd.services.asterisk.enable {
path = "/var/log/asterisk/messages";
user = "asterisk";
group = "asterisk";
frequency = "daily";
keep = 2;
};
};
};
}

View file

@ -1,62 +0,0 @@
{ config, pkgs, ... }:
{
kw.fw.private.tcp.ports = [ 3100 ];
services.loki = {
enable = true;
configuration = {
auth_enabled = false;
chunk_store_config = { max_look_back_period = "0s"; };
ingester = {
chunk_idle_period = "1h";
chunk_retain_period = "30s";
chunk_target_size = 1048576;
lifecycler = {
address = "0.0.0.0";
final_sleep = "0s";
ring = {
kvstore = { store = "inmemory"; };
replication_factor = 1;
};
};
max_chunk_age = "1h";
max_transfer_retries = 0;
};
limits_config = {
reject_old_samples = true;
reject_old_samples_max_age = "168h";
};
schema_config = {
configs = [{
from = "2020-10-24";
index = {
period = "24h";
prefix = "index_";
};
object_store = "filesystem";
schema = "v11";
store = "boltdb-shipper";
}];
};
compactor = {
working_directory = "/tmp/loki-compactor-boltdb";
shared_store = "filesystem";
};
server = { http_listen_port = 3100; };
storage_config = {
boltdb_shipper = {
active_index_directory = "/var/lib/loki/boltdb-shipper-active";
cache_location = "/var/lib/loki/boltdb-shipper-cache";
cache_ttl = "24h";
shared_store = "filesystem";
};
filesystem = { directory = "/var/lib/loki/chunks"; };
};
table_manager = {
retention_deletes_enabled = false;
retention_period = "0s";
};
};
};
}

View file

@ -1,106 +0,0 @@
{ config, lib, tf, pkgs, sources, ... }:
with lib;
{
imports = [ sources.nixos-mailserver.outPath ];
services.fail2ban.jails = {
postfix = ''
enabled = true
filter = postfix
maxretry = 3
action = nftables-multiport[name=postfix, port=smtp, protocol=tcp]
'';
postfix-sasl = ''
enabled = true
filter = postfix-sasl
port = postfix,imap3,imaps,pop3,pop3s
maxretry = 3
action = nftables-multiport[name=postfix, port=smtp, protocol=tcp]
'';
postfix-ddos = ''
enabled = true
filter = postfix-ddos
maxretry = 3
action = nftables-multiport[name=postfix, port=submission, protocol=tcp]
bantime = 7200
'';
};
environment.etc."fail2ban/filter.d/postfix-sasl.conf" = {
enable = true;
text = ''
# Fail2Ban filter for postfix authentication failures
[INCLUDES]
before = common.conf
[Definition]
daemon = postfix/smtpd
failregex = ^%(__prefix_line)swarning: [-._\w]+\[<HOST>\]: SASL (?:LOGIN|PLAIN|(?:CRAM|DIGEST)-MD5) authentication failed(: [ A-Za-z0-9+/]*={0,2})?\s*$
'';
};
environment.etc."fail2ban/filter.d/postfix-ddos.conf" = {
enable = true;
text = ''
[Definition]
failregex = lost connection after EHLO from \S+\[<HOST>\]
'';
};
deploy.tf.variables.domainkey_kitty = {
type = "string";
value.shellCommand = "bitw get infra/domainkey-kitty";
};
deploy.tf.dns.records.kittywitch_mx = {
tld = "kittywit.ch.";
domain = "@";
mx = {
priority = 10;
target = "athame.kittywit.ch.";
};
};
deploy.tf.dns.records.kittywitch_spf = {
tld = "kittywit.ch.";
domain = "@";
txt.value = "v=spf1 ip4:168.119.126.111 ip6:${
(head config.networking.interfaces.enp1s0.ipv6.addresses).address
} -all";
};
deploy.tf.dns.records.kittywitch_dmarc = {
tld = "kittywit.ch.";
domain = "_dmarc";
txt.value = "v=DMARC1; p=none";
};
deploy.tf.dns.records.kittywitch_domainkey = {
tld = "kittywit.ch.";
domain = "mail._domainkey";
txt.value = tf.variables.domainkey_kitty.ref;
};
mailserver = {
enable = true;
fqdn = "athame.kittywit.ch";
domains = [ "kittywit.ch" "dork.dev" ];
# Use Let's Encrypt certificates. Note that this needs to set up a stripped
# down nginx and opens port 80.
certificateScheme = 3;
# Enable IMAP and POP3
enableImap = true;
enablePop3 = true;
enableImapSsl = true;
enablePop3Ssl = true;
# Enable the ManageSieve protocol
enableManageSieve = true;
# whether to scan inbound emails for viruses (note that this requires at least
# 1 Gb RAM for the server. Without virus scanning 256 MB RAM should be plenty)
virusScanning = false;
};
}

View file

@ -1,148 +0,0 @@
{ config, pkgs, lib, ... }:
with lib;
{
environment.systemPackages = [ pkgs.mx-puppet-discord pkgs.mautrix-whatsapp ];
services.postgresql.initialScript = pkgs.writeText "synapse-init.sql" ''
CREATE ROLE "matrix-synapse" WITH LOGIN PASSWORD 'synapse';
CREATE DATABASE "matrix-synapse" WITH OWNER "matrix-synapse"
TEMPLATE template0
LC_COLLATE = "C"
LC_CTYPE = "C";
'';
services.matrix-synapse = {
enable = true;
max_upload_size = "512M";
server_name = "kittywit.ch";
app_service_config_files = [
"/var/lib/matrix-synapse/telegram-registration.yaml"
"/var/lib/matrix-synapse/discord-registration.yaml"
"/var/lib/matrix-synapse/whatsapp-registration.yaml"
];
rc_messages_per_second = mkDefault "0.1";
rc_message_burst_count = mkDefault "25.0";
url_preview_enabled = mkDefault true;
enable_registration = mkDefault false;
enable_metrics = mkDefault false;
report_stats = mkDefault false;
dynamic_thumbnails = mkDefault true;
allow_guest_access = mkDefault true;
extraConfig = ''
suppress_key_server_warning: true
'';
listeners = [{
port = 8008;
bind_address = "::1";
type = "http";
tls = false;
x_forwarded = true;
resources = [{
names = [ "client" "federation" ];
compress = false;
}];
}];
};
services.mautrix-telegram = {
enable = true;
settings = {
homeserver = {
address = "http://localhost:8008";
domain = "kittywit.ch";
};
appservice = {
provisioning.enabled = false;
id = "telegram";
public = {
enabled = false;
prefix = "/public";
external = "https://kittywit.ch/public";
};
};
bridge = {
relaybot.authless_portals = false;
permissions = {
"@kat:kittywit.ch" = "admin";
"kittywit.ch" = "full";
};
};
};
};
systemd.services.mx-puppet-discord = {
serviceConfig = {
Type = "simple";
Restart = "always";
ExecStart =
"${pkgs.mx-puppet-discord}/bin/mx-puppet-discord -c /var/lib/mx-puppet-discord/config.yaml -f /var/lib/mx-puppet-discord/discord-registration.yaml";
WorkingDirectory = "/var/lib/mx-puppet-discord";
DynamicUser = true;
StateDirectory = "mx-puppet-discord";
UMask = 27;
PrivateTmp = true;
ProtectSystem = "strict";
ProtectHome = true;
ProtectKernelTunables = true;
ProtectKernelModules = true;
ProtectControlGroups = true;
};
requisite = [ "matrix-synapse.service" ];
wantedBy = [ "multi-user.target" ];
after = [ "network.target" ];
};
systemd.services.mautrix-whatsapp = {
serviceConfig = {
Type = "simple";
Restart = "always";
ExecStart =
"${pkgs.mautrix-whatsapp}/bin/mautrix-whatsapp -c /var/lib/mautrix-whatsapp/config.yaml -r /var/lib/mautrix-whatsapp/registration.yaml";
WorkingDirectory = "/var/lib/mautrix-whatsapp";
DynamicUser = true;
StateDirectory = "mautrix-whatsapp";
UMask = 27;
PrivateTmp = true;
ProtectSystem = "strict";
ProtectHome = true;
ProtectKernelTunables = true;
ProtectKernelModules = true;
ProtectControlGroups = true;
};
requisite = [ "matrix-synapse.service" ];
wantedBy = [ "multi-user.target" ];
after = [ "network.target" ];
};
services.nginx.virtualHosts."kittywit.ch" = {
# allegedly fixes https://github.com/poljar/weechat-matrix/issues/240
extraConfig = ''
keepalive_requests 100000;
'';
locations = {
"/_matrix" = { proxyPass = "http://[::1]:8008"; };
"= /.well-known/matrix/server".extraConfig =
let server = { "m.server" = "kittywit.ch:443"; };
in
''
add_header Content-Type application/json;
return 200 '${builtins.toJSON server}';
'';
"= /.well-known/matrix/client".extraConfig =
let
client = {
"m.homeserver" = { "base_url" = "https://kittywit.ch"; };
"m.identity_server" = { "base_url" = "https://vector.im"; };
};
in
''
add_header Content-Type application/json;
add_header Access-Control-Allow-Origin *;
return 200 '${builtins.toJSON client}';
'';
};
};
}

View file

@ -1,60 +0,0 @@
{ config, lib, pkgs, ... }:
with lib;
{
kw.fw.public.tcp.ports = singleton 64738;
kw.fw.public.udp.ports = singleton 64738;
services.murmur = {
enable = true;
hostName = "voice.kittywit.ch";
bandwidth = 130000;
welcometext = "mew!";
extraConfig = ''
sslCert=/var/lib/acme/voice.kittywit.ch/fullchain.pem
sslKey=/var/lib/acme/voice.kittywit.ch/key.pem
'';
};
services.nginx.virtualHosts."voice.kittywit.ch" = {
enableACME = true;
forceSSL = true;
};
users.groups."voice-cert".members = [ "nginx" "murmur" ];
security.acme.certs = { "voice.kittywit.ch" = { group = "voice-cert"; }; };
deploy.tf.dns.records.kittywitch_voice = {
tld = "kittywit.ch.";
domain = "voice";
cname.target = "athame.kittywit.ch.";
};
deploy.tf.dns.records.kittywitch_voice_tcp = {
tld = "kittywit.ch.";
domain = "@";
srv = {
service = "mumble";
proto = "tcp";
priority = 0;
weight = 5;
port = 64738;
target = "voice.kittywit.ch.";
};
};
deploy.tf.dns.records.kittywitch_voice_udp = {
tld = "kittywit.ch.";
domain = "@";
srv = {
service = "mumble";
proto = "udp";
priority = 0;
weight = 5;
port = 64738;
target = "voice.kittywit.ch.";
};
};
}

View file

@ -1,18 +0,0 @@
{ config, ... }:
{
kw.fw.private.tcp.ports = [ 19999 ];
services.netdata = { enable = true; };
services.nginx = {
enable = true;
virtualHosts = {
"${config.networking.hostName}.net.kittywit.ch" = {
useACMEHost = "${config.networking.hostName}.net.kittywit.ch";
forceSSL = true;
locations = { "/netdata" = { proxyPass = "http://[::1]:19999/"; }; };
};
};
};
}

View file

@ -1,43 +0,0 @@
{ config, lib, pkgs, tf, ... }:
with lib;
{
secrets.files.dns_creds = {
text = ''
RFC2136_NAMESERVER='ns1.as207960.net'
RFC2136_TSIG_ALGORITHM='hmac-sha512.'
RFC2136_TSIG_KEY='${tf.variables.glauca_key.ref}'
RFC2136_TSIG_SECRET='${tf.variables.glauca_secret.ref}'
'';
};
kw.fw.public.tcp.ports = [ 443 80 ];
kw.fw.private.tcp.ports = [ 443 80 ];
services.nginx = {
enable = true;
recommendedGzipSettings = true;
recommendedOptimisation = true;
recommendedProxySettings = true;
recommendedTlsSettings = true;
commonHttpConfig = ''
map $scheme $hsts_header {
https "max-age=31536000; includeSubdomains; preload";
}
add_header Strict-Transport-Security $hsts_header;
#add_header Content-Security-Policy "script-src 'self'; object-src 'none'; base-uri 'none';" always;
add_header 'Referrer-Policy' 'origin-when-cross-origin';
#add_header X-Frame-Options DENY;
#add_header X-Content-Type-Options nosniff;
#add_header X-XSS-Protection "1; mode=block";
#proxy_cookie_path / "/; secure; HttpOnly; SameSite=strict";
'';
clientMaxBodySize = "512m";
};
security.acme = {
email = "acme@kittywit.ch";
acceptTerms = true;
};
}

View file

@ -1,15 +0,0 @@
{ config, ... }:
{
kw.fw.private.tcp.ports = [ 9002 ];
services.prometheus = {
exporters = {
node = {
enable = true;
enabledCollectors = [ "systemd" ];
port = 9002;
};
};
};
}

View file

@ -1,5 +0,0 @@
{ config, pkgs, ... }:
{
services.postgresql.enable = true;
}

View file

@ -1,45 +0,0 @@
{ config, hosts, lib, ... }:
with lib;
let
prom_configs =
(mapAttrs (hostName: host: host.config.services.prometheus.exporters.node)
(filterAttrs
(_: host: host.config.services.prometheus.exporters.node.enable)
hosts));
nd_configs = (mapAttrs (hostName: host: host.config.services.netdata)
(filterAttrs (_: host: host.config.services.netdata.enable) hosts));
in
{
services.prometheus = {
enable = true;
scrapeConfigs = [
{
job_name = "boline";
static_configs = [{ targets = [ "boline.net.kittywit.ch:8002" ]; }];
}
{
job_name = "samhain-vm";
metrics_path = "/metrics";
static_configs = [{ targets = [ "samhain.net.kittywit.ch:10445" ]; }];
}
] ++ mapAttrsToList
(hostName: prom: {
job_name = "${hostName}-nd";
metrics_path = "/api/v1/allmetrics";
honor_labels = true;
params = { format = [ "prometheus" ]; };
static_configs = [{ targets = [ "${hostName}.net.kittywit.ch:19999" ]; }];
})
nd_configs ++ mapAttrsToList
(hostName: prom: {
job_name = hostName;
static_configs = [{
targets = [ "${hostName}.net.kittywit.ch:${toString prom.port}" ];
}];
})
prom_configs;
};
}

View file

@ -1,41 +0,0 @@
{ config, lib, pkgs, ... }:
with lib;
let
promtail_config = pkgs.writeText "prom-config.json" (builtins.toJSON {
clients =
[{ url = "http://athame.net.kittywit.ch:3100/loki/api/v1/push"; }];
positions = { filename = "/tmp/positions.yaml"; };
scrape_configs = [{
job_name = "journal";
journal = {
labels = {
host = config.networking.hostName;
job = "systemd-journal";
};
max_age = "12h";
};
relabel_configs = [{
source_labels = [ "__journal__systemd_unit" ];
target_label = "unit";
}];
}];
server = {
grpc_listen_port = 0;
http_listen_port = 28183;
};
});
in
{
systemd.services.promtail = {
description = "Promtail service for Loki";
wantedBy = [ "multi-user.target" ];
serviceConfig = {
ExecStart = ''
${pkgs.grafana-loki}/bin/promtail --config.file ${promtail_config}
'';
};
};
}

View file

@ -1,17 +0,0 @@
{ config, lib, pkgs, ... }:
{
services.restic.backups.tardis = {
passwordFile = "/etc/restic/system";
paths = [ "/home" "/var/lib" ];
pruneOpts = [ "--keep-daily 7" "--keep-weekly 5" "--keep-monthly 12" ];
repository = "";
};
systemd.services."restic-backups-tardis".environment.RESTIC_REPOSITORY_FILE =
"/etc/restic/system.repo";
services.postgresqlBackup = {
enable = config.services.postgresql.enable;
backupAll = true;
startAt = "*-*-* 23:45:00";
};
}

View file

@ -1,60 +0,0 @@
{ config, lib, pkgs, tf, ... }:
with lib;
{
users.users.syncplay = { isSystemUser = true; };
users.groups."sync-cert".members = [ "nginx" "syncplay" ];
security.acme = { certs."sync.kittywit.ch" = { group = "sync-cert"; }; };
kw.fw.public.tcp.ports = singleton 8999;
services.nginx.virtualHosts."sync.kittywit.ch" = {
enableACME = true;
forceSSL = true;
};
deploy.tf.dns.records.kittywitch_sync = {
tld = "kittywit.ch.";
domain = "sync";
cname.target = "athame.kittywit.ch.";
};
deploy.tf.variables.syncplay_pass = {
type = "string";
value.shellCommand = "bitw get infra/syncplay-server -f password";
};
deploy.tf.variables.syncplay_salt = {
type = "string";
value.shellCommand = "bitw get infra/syncplay-salt -f password";
};
secrets.files.syncplay-env = {
text = ''
SYNCPLAY_PASSWORD=${tf.variables.syncplay_pass.ref}
SYNCPLAY_SALT=${tf.variables.syncplay_salt.ref}
'';
owner = "syncplay";
group = "sync-cert";
};
systemd.services.syncplay = {
description = "Syncplay Service";
wantedBy = singleton "multi-user.target";
after = singleton "network-online.target";
serviceConfig = {
EnvironmentFile = config.secrets.files.syncplay-env.path;
ExecStart =
"${pkgs.syncplay}/bin/syncplay-server --port 8999 --tls /var/lib/acme/sync.kittywit.ch/ --disable-ready";
User = "syncplay";
Group = "sync-cert";
};
};
security.acme.certs."sync.kittywit.ch".postRun = ''
cp key.pem privkey.pem
chown acme:voice-cert privkey.pem'';
}

View file

@ -1,14 +0,0 @@
{ config, lib, ... }:
with lib;
{
kw.fw.public.tcp.ports = singleton 53589;
services.taskserver = {
enable = true;
fqdn = "kittywit.ch";
listenHost = "::";
organisations.kittywitch.users = singleton "kat";
};
}

View file

@ -1,21 +0,0 @@
{ config, pkgs, ... }:
{
services.nginx.virtualHosts."irc.kittywit.ch" = {
enableACME = true;
forceSSL = true;
locations = {
"/" = { root = pkgs.glowing-bear; };
"^~ /weechat" = {
proxyPass = "http://127.0.0.1:9000";
proxyWebsockets = true;
};
};
};
deploy.tf.dns.records.kittywitch_irc = {
tld = "kittywit.ch.";
domain = "irc";
cname.target = "athame.kittywit.ch.";
};
}

View file

@ -1,149 +0,0 @@
{ config, pkgs, lib, ... }:
with lib;
{
kw.fw.public.tcp.ports = [ 5000 5222 5223 5269 580 5281 5347 5582 ];
services.postgresql = {
ensureDatabases = [ "prosody" ];
ensureUsers = [{
name = "prosody";
ensurePermissions."DATABASE prosody" = "ALL PRIVILEGES";
}];
};
services.prosody = {
enable = true;
ssl.cert = "/var/lib/acme/prosody/fullchain.pem";
ssl.key = "/var/lib/acme/prosody/key.pem";
admins = [ "kat@kittywit.ch" ];
package =
let
package = pkgs.prosody.override (old: {
withExtraLibs = old.withExtraLibs ++ singleton pkgs.luaPackages.luadbi-postgresql;
}); in
package;
extraConfig = ''
legacy_ssl_ports = { 5223 }
storage = "sql"
sql = {
driver = "PostgreSQL";
host = "";
database = "prosody";
username = "prosody";
}
'';
virtualHosts = {
"xmpp.kittywit.ch" = {
domain = "kittywit.ch";
enabled = true;
ssl.cert = "/var/lib/acme/prosody/fullchain.pem";
ssl.key = "/var/lib/acme/prosody/key.pem";
};
};
muc = [{ domain = "conference.kittywit.ch"; }];
uploadHttp = { domain = "upload.kittywit.ch"; };
};
security.acme.certs.prosody = {
domain = "xmpp.kittywit.ch";
group = "prosody";
dnsProvider = "rfc2136";
credentialsFile = config.secrets.files.dns_creds.path;
postRun = "systemctl restart prosody";
extraDomainNames =
[ "kittywit.ch" "upload.kittywit.ch" "conference.kittywit.ch" ];
};
deploy.tf.dns.records.kittywitch_xmpp = {
tld = "kittywit.ch.";
domain = "xmpp";
a.address = "168.119.126.111";
};
deploy.tf.dns.records.kittywitch_xmpp_v6 = {
tld = "kittywit.ch.";
domain = "xmpp";
aaaa.address =
(lib.head config.networking.interfaces.enp1s0.ipv6.addresses).address;
};
deploy.tf.dns.records.kittywitch_upload = {
tld = "kittywit.ch.";
domain = "upload";
cname.target = "xmpp.kittywit.ch.";
};
deploy.tf.dns.records.kittywitch_conference = {
tld = "kittywit.ch.";
domain = "conference";
cname.target = "xmpp.kittywit.ch.";
};
deploy.tf.dns.records.kittywitch_xmpp_muc = {
tld = "kittywit.ch.";
domain = "conference";
srv = {
service = "xmpp-server";
proto = "tcp";
priority = 0;
weight = 5;
port = 5269;
target = "xmpp.kittywit.ch.";
};
};
deploy.tf.dns.records.kittywitch_xmpp_client = {
tld = "kittywit.ch.";
domain = "@";
srv = {
service = "xmpp-client";
proto = "tcp";
priority = 0;
weight = 5;
port = 5222;
target = "xmpp.kittywit.ch.";
};
};
deploy.tf.dns.records.kittywitch_xmpps_client = {
tld = "kittywit.ch.";
domain = "@";
srv = {
service = "xmpps-client";
proto = "tcp";
priority = 0;
weight = 5;
port = 5223;
target = "xmpp.kittywit.ch.";
};
};
deploy.tf.dns.records.kittywitch_xmpp_server = {
tld = "kittywit.ch.";
domain = "@";
srv = {
service = "xmpp-server";
proto = "tcp";
priority = 0;
weight = 5;
port = 5269;
target = "xmpp.kittywit.ch.";
};
};
services.nginx.virtualHosts = {
"upload.kittywit.ch" = {
useACMEHost = "prosody";
forceSSL = true;
};
"conference.kittywit.ch" = {
useACMEHost = "prosody";
forceSSL = true;
};
};
users.users.nginx.extraGroups = [ "prosody" ];
}

View file

@ -1,14 +0,0 @@
{ config, lib, pkgs, ... }:
{
services.zfs = {
autoScrub.enable = true;
autoSnapshot = {
enable = true;
frequent = 1;
daily = 7;
weekly = 1;
monthly = 1;
};
};
}

View file

@ -1,14 +0,0 @@
{ config, pkgs, ... }:
{
services.znc = {
enable = true;
mutable = false;
useLegacyConfig = false;
openFirewall = false;
modulePackages = with pkgs.zncModules; [
clientbuffer
clientaway
];
};
}