commit 56d73c7c6f55e6b61fc2a60da61b776603c7e7cc Author: bee Date: Mon Apr 27 15:07:54 2026 +0200 init diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8d95a87 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +registry/ +.ansible/ +.vscode/settings.json +.vault-password \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..1c5e755 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,76 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Create vault inline password", + "type": "shell", + "options": { + "cwd": "${workspaceFolder}" + }, + "command": "ansible-vault encrypt_string ${input:pw} --name ${input:name} --vault-password-file .vault-password", + "problemMatcher": [] + }, + { + "label": "Deploy: nginx", + "type": "shell", + "command": "make deploy-nginx", + "group": "build", + "presentation": { + "reveal": "always", + "panel": "dedicated" + } + }, + { + "label": "Deploy: gitea", + "type": "shell", + "command": "make deploy-gitea", + "group": "build", + "presentation": { + "reveal": "always", + "panel": "dedicated" + } + }, + { + "label": "Deploy: monitoring", + "type": "shell", + "command": "make deploy-monitoring", + "group": "build", + "presentation": { + "reveal": "always", + "panel": "dedicated" + } + }, + { + "label": "Deploy: letsencrypt", + "type": "shell", + "command": "make deploy-letsencrypt", + "group": "build", + "presentation": { + "reveal": "always", + "panel": "dedicated" + } + }, + { + "label": "Build & Push: postfix", + "type": "shell", + "command": "make push-postfix", + "group": "build", + "presentation": { + "reveal": "always", + "panel": "dedicated" + } + } +], + "inputs": [ + { + "id": "pw", + "type": "promptString", + "description": "Password to be encrypted" + }, + { + "id": "name", + "type": "promptString", + "description": "Name for the string" + } + ] +} \ No newline at end of file diff --git a/inventory.yml b/inventory.yml new file mode 100644 index 0000000..bd2afde --- /dev/null +++ b/inventory.yml @@ -0,0 +1,5 @@ +pi: + hosts: + beepi: + ansible_host: beepi.local + ansible_python_interpreter: auto_silent \ No newline at end of file diff --git a/makefile b/makefile new file mode 100644 index 0000000..63b50f4 --- /dev/null +++ b/makefile @@ -0,0 +1,11 @@ +REGISTRY := git.secretbee.buzz +INVENTORY := inventory.yml + +build-%: + podman build --platform linux/arm64 -t $(REGISTRY)/bee/$*:latest playbooks/$* + +push-%: build-% + podman push $(REGISTRY)/bee/$*:latest + +deploy-%: + ansible-playbook -i $(INVENTORY) playbooks/$*/$*.yml --vault-password-file .vault-password \ No newline at end of file diff --git a/playbooks/anope/anope-2.0.19.tar.gz b/playbooks/anope/anope-2.0.19.tar.gz new file mode 100644 index 0000000..f9bfcec Binary files /dev/null and b/playbooks/anope/anope-2.0.19.tar.gz differ diff --git a/playbooks/anope/anope.yml b/playbooks/anope/anope.yml new file mode 100644 index 0000000..47714e3 --- /dev/null +++ b/playbooks/anope/anope.yml @@ -0,0 +1,42 @@ +- name: Nginx + hosts: pi + become: true + vars: + uplink_password: !vault | + $ANSIBLE_VAULT;1.1;AES256 + 34333036613062353533333364656363343539376463343762663637353634313533353164376262 + 3931346362356333616234336635326330333836623932390a646264393563633234393863303730 + 32313462313063613866653939313431356434373962613738653835316461636633346139326166 + 3232616234353765370a303731346238313131666263613462656633363730383437626130653564 + 31656261666436376236333865643532353035316634313231653333646338373636 + anope_oper_password: !vault | + $ANSIBLE_VAULT;1.1;AES256 + 65386331633238633231643964613965633661633038386664326538333538356462653638633164 + 3032373430663534383563383865333438643263653362320a303833306237363163613235313836 + 35306131646163623636666234663137393564626262303933366139613262303766306534396565 + 6638656464616335310a363938646236313164316561303364393839663930663062386166613736 + 31326264663135313337393362396562663439616331386138633532343662626134 + tasks: + - name: Copy services + ansible.builtin.template: + src: services.conf.j2 + dest: /home/anope/services/conf + owner: anope + group: anope + mode: '0644' + + - name: Copy nickserv + ansible.builtin.copy: + src: nickserv.conf + dest: /home/anope/services/conf + owner: anope + group: anope + mode: '0644' + + - name: Copy modules + ansible.builtin.copy: + src: modules.conf + dest: /home/anope/services/conf + owner: anope + group: anope + mode: '0644' diff --git a/playbooks/anope/modules.conf b/playbooks/anope/modules.conf new file mode 100644 index 0000000..62813be --- /dev/null +++ b/playbooks/anope/modules.conf @@ -0,0 +1,27 @@ +module { name = "help" } + +module +{ + name = "m_helpchan" + helpchannel = "#help" +} + +module { name = "m_regex_pcre2" } +module { name = "m_regex_posix" } +module { name = "m_regex_tre" } + +module { name = "m_rewrite" } +command +{ + service = "ChanServ"; name = "CLEAR"; command = "rewrite" + rewrite = yes + rewrite_source = "CLEAR $ USERS" + rewrite_target = "KICK $1 *" + rewrite_description = "Clears all users from a channel" +} + +module +{ + name = "m_sasl" + agent = "NickServ" +} \ No newline at end of file diff --git a/playbooks/anope/nickserv.conf b/playbooks/anope/nickserv.conf new file mode 100644 index 0000000..50c0f72 --- /dev/null +++ b/playbooks/anope/nickserv.conf @@ -0,0 +1,665 @@ +service +{ + /* + * The name of the NickServ client. + * If you change this value, you probably want to change the client directive in the configuration for the nickserv module too. + */ + nick = "NickServ" + + /* + * The username of the NickServ client. + */ + user = "services" + + /* + * The hostname of the NickServ client. + */ + host = "services.host" + + /* + * The realname of the NickServ client. + */ + gecos = "Nickname Registration Service" + + /* + * The modes this client should use. + * Do not modify this unless you know what you are doing. + * + * These modes are very IRCd specific. If left commented, sane defaults + * are used based on what protocol module you have loaded. + * + * Note that setting this option incorrectly could potentially BREAK some, if + * not all, usefulness of the client. We will not support you if this client is + * unable to do certain things if this option is enabled. + */ + #modes = "+o" + + /* + * An optional comma separated list of channels this service should join. Outside + * of log channels this is not very useful, as the service will just idle in the + * specified channels, and will not accept any types of commands. + * + * Prefixes may be given to the channels in the form of mode characters or prefix symbols. + */ + #channels = "@#services,#mychan" +} + +/* + * Core NickServ module. + * + * Provides essential functionality for NickServ. + */ +module +{ + name = "nickserv" + + /* + * The name of the client that should be NickServ. + */ + client = "NickServ" + + /* + * Force users to give an e-mail address when they register a nick. + * + * This directive defaults to "yes" and is recommended to be enabled. + */ + forceemail = no + + /* + * Require users who change their email address to confirm they + * own their new email. + */ + confirmemailchanges = no + + /* + * A message sent to users on connect if they use an unregistered nick. %n will be replaced with the user's nickname. + * + * This directive is optional. + */ + #unregistered_notice = "Your nickname is not registered. To register it, use: /msg NickServ REGISTER password email" + + /* + * The default options for newly registered nicks. Note that changing these options + * will have no effect on nicks which are already registered. The list must be separated + * by spaces. + * + * The options are: + * - killprotect: Kill nick if not identified within 60 seconds + * - kill_quick: Kill nick if not identified within 20 seconds, this one overrides the killprotect + * option and the killprotect option must be specified with this one + * - kill_immed: Kill nick immediately if not identified, this one overrides both the killprotect + * and kill_quick options and the killprotect option must be specified with this one + * - ns_secure: Enable nickname security, requiring the nick's password before any operations + * can be done on it + * - ns_private: Hide the nick from NickServ's LIST command + * - hide_email: Hide the nick's e-mail address from NickServ's INFO command + * - hide_mask: Hide the nick's last or current user@host from NickServ's INFO command + * - hide_status: Hide the nick's services operator access status from NickServ's INFO command + * - hide_quit: Hide the nick's last quit message from NickServ's INFO command + * - memo_signon: Notify user if they have a new memo when they sign into the nick + * - memo_receive: Notify user if they have a new memo as soon as it's received + * - memo_mail: Notify user if they have a new memo by mail + * - autoop: User will be automatically opped in channels they enter and have access to + * - msg: Services messages will be sent as PRIVMSGs instead of NOTICEs, requires + * options:useprivmsg to be enabled as well + * - ns_keep_modes: Enables keepmodes, which retains user modes across sessions + * + * This directive is optional, if left blank, the options will default to ns_secure, memo_signon, and + * memo_receive. If you really want no defaults, use "none" by itself as the option. + */ + defaults = "killprotect ns_secure ns_private hide_email hide_mask memo_signon memo_receive autoop" + + /* + * The minimum length of time between consecutive uses of NickServ's REGISTER command. This + * directive is optional, but recommended. If not set, this restriction will be disabled. + */ + regdelay = 30s + + /* + * The length of time before a nick's registration expires. + * + * This directive is optional, but recommended. If not set, the default is 21 days. + */ + expire = 21d + + /* + * Prevents the use of the ACCESS and CERT (excluding their LIST subcommand), DROP, FORBID, SUSPEND, + * GETPASS and SET PASSWORD commands by services operators on other services operators. + * + * This directive is optional, but recommended. + */ + secureadmins = yes + + /* + * If set, Services will set the channel modes a user has access to upon identifying, assuming + * they are not already set. + * + * This directive is optional. + */ + modeonid = yes + + /* + * If set, Services will set these user modes on any user who identifies. + * + * This directive is optional. + */ + #modesonid = "+R" + + /* + * If set, Services will not show netsplits in the last quit message field + * of NickServ's INFO command. + */ + hidenetsplitquit = no + + /* + * If set, is the length of time NickServ's killquick and kill options wait before + * forcing users off of protected nicknames. + */ + killquick = 20s + kill = 60s + + /* + * If set, forbids the registration of nicks that contain an existing + * nick with Services access. For example, if Tester is a Services Oper, + * you can't register NewTester or Tester123 unless you are an IRC + * Operator. + * + * NOTE: If you enable this, you will have to be logged in as an IRC + * operator in order to register a Services Root nick when setting up + * Anope for the first time. + * + * This directive is optional. + */ + #restrictopernicks = yes + + /* + * The username, and possibly hostname, used for fake users created when Services needs to + * hold a nickname. + */ + enforceruser = "enforcer" + enforcerhost = "services.host" + + /* + * The length of time Services hold nicknames. + * + * This directive is optional, but recommended. If not set it defaults to 1 minute. + */ + releasetimeout = 1m + + /* + * When a user's nick is forcibly changed to enforce a "nick kill", their new nick will start + * with this value. The rest will be made up of 6 or 7 digits. + * Make sure this is a valid nick and Nicklen+7 is not longer than the allowed Nicklen on your ircd. + * + * This directive is optional. If not set it defaults to "Guest" + */ + guestnickprefix = "Guest" + + /* + * If set, Services do not allow ownership of nick names, only ownership of accounts. + */ + nonicknameownership = no + + /* + * The maximum length of passwords + * + * This directive is optional. If not set it defaults to 32. + */ + passlen = 32 +} + +/* + * Core NickServ commands. + * + * In Anope modules can provide (multiple) commands, each of which has a unique command name. Once these modules + * are loaded you can then configure the commands to be added to any client you like with any name you like. + * + * Additionally, you may provide a permission name that must be in the opertype of users executing the command. + * + * Sane defaults are provided below that do not need to be edited unless you wish to change the default behavior. + */ + +/* Command group configuration for NickServ. + * + * Commands may optionally be placed into groups to make NickServ's HELP output easier to understand. + * Remove the following groups to use the old behavior of simply listing all NickServ commands from HELP. + */ +command_group +{ + name = "nickserv/admin" + description = _("Services Operator commands") +} + +/* Give it a help command. */ +command { service = "NickServ"; name = "HELP"; command = "generic/help"; } + +/* + * ns_access + * + * Provides the command nickserv/access. + * + * Used for configuring what hosts have access to your account. + */ +module +{ + name = "ns_access" + + /* + * The maximum number of entries allowed on a nickname's access list. + * If not set, the default is 32. This number cannot be set to 0. + */ + accessmax = 32 + + /* + * If set, Services will add the usermask of registering users to the access list of their + * newly created account. If not set, users will always have to identify to NickServ before + * being recognized, unless they manually add an address to the access list of their account. + * This directive is optional. + */ + addaccessonreg = no +} +command { service = "NickServ"; name = "ACCESS"; command = "nickserv/access"; } + +/* + * ns_ajoin + * + * Provides the command nickserv/ajoin. + * + * Used for configuring channels to join once you identify. + */ +module +{ + name = "ns_ajoin" + + /* + * The maximum number of channels a user can have on NickServ's AJOIN command. + */ + ajoinmax = 10 +} +command { service = "NickServ"; name = "AJOIN"; command = "nickserv/ajoin"; } + +/* + * ns_alist + * + * Provides the command nickserv/alist. + * + * Used for viewing what channels you have access to. + */ +module { name = "ns_alist" } +command { service = "NickServ"; name = "ALIST"; command = "nickserv/alist"; } + +/* + * ns_cert + * + * Provides the command nickserv/cert. + * + * Used for configuring your SSL certificate list, which can be used to automatically identify you. + */ +module +{ + name = "ns_cert" + + /* + * The maximum number of entries allowed on a nickname's certificate fingerprint list. + * The default is 5. This number cannot be set to 0. + */ + max = 5 +} +command { service = "NickServ"; name = "CERT"; command = "nickserv/cert"; } + +/* + * ns_drop + * + * Provides the command nickserv/drop. + * + * Used for unregistering names. + */ +module { name = "ns_drop" } +command { service = "NickServ"; name = "DROP"; command = "nickserv/drop"; } + +/* + * ns_getemail + * + * Provides the command nickserv/getemail. + * + * Used for getting registered accounts by searching for emails. + */ +module { name = "ns_getemail" } +command { service = "NickServ"; name = "GETEMAIL"; command = "nickserv/getemail"; permission = "nickserv/getemail"; group = "nickserv/admin"; } + +/* + * [DEPRECATED] ns_getpass + * + * Provides the command nickserv/getpass. + * + * Used for getting users passwords. + * + * Requires no encryption is being used. + */ +#module { name = "ns_getpass" } +#command { service = "NickServ"; name = "GETPASS"; command = "nickserv/getpass"; permission = "nickserv/getpass"; } + +/* + * ns_group + * + * Provides the commands nickserv/group, nickserv/glist, and nickserv/ungroup. + * + * Used for controlling nick groups. + */ +module +{ + name = "ns_group" + + /* + * The maximum number of nicks allowed in a group. + * + * This directive is optional, but recommended. If not set or set to 0, no limits will be applied. + */ + maxaliases = 16 + + /* + * If set, the NickServ GROUP command won't allow any group changes. This is recommended to + * prevent users from accidentally dropping their nicks, as it forces users to explicitly + * drop their nicks before adding it to another group. + * + * This directive is optional, but recommended. + */ + nogroupchange = yes +} +command { service = "NickServ"; name = "GLIST"; command = "nickserv/glist"; } +command { service = "NickServ"; name = "GROUP"; command = "nickserv/group"; } +command { service = "NickServ"; name = "UNGROUP"; command = "nickserv/ungroup"; } + +/* + * ns_identify + * + * Provides the command nickserv/identify. + * + * Used for identifying to accounts. + */ +module +{ + name = "ns_identify" + + /* + * If set, limits the number of concurrent users that can be logged in as a given account at once. + */ + maxlogins = 10 +} +command { service = "NickServ"; name = "ID"; command = "nickserv/identify"; hide = yes; } +command { service = "NickServ"; name = "IDENTIFY"; command = "nickserv/identify"; } + +/* + * ns_info + * + * Provides the commands: + * nickserv/info. - Used for gathering information about an account. + * nickserv/set/hide, nickserv/saset/hide - Used for configuring which options are publicly shown in nickserv/info. + * + */ +module { name = "ns_info" } +command { service = "NickServ"; name = "INFO"; command = "nickserv/info"; } + +command { service = "NickServ"; name = "SET HIDE"; command = "nickserv/set/hide"; } +command { service = "NickServ"; name = "SASET HIDE"; command = "nickserv/saset/hide"; permission = "nickserv/saset/hide"; } + + +/* + * ns_list + * + * Provides the commands: + * nickserv/list - Used for retrieving and searching the registered account list. + * nickserv/set/private, nickserv/saset/private - Used for configuring whether or a users account shows up in nickserv/list. + * + */ +module +{ + name = "ns_list" + + /* + * The maximum number of nicks to be returned for a NickServ LIST command. + */ + listmax = 50 +} +command { service = "NickServ"; name = "LIST"; command = "nickserv/list"; } + +command { service = "NickServ"; name = "SET PRIVATE"; command = "nickserv/set/private"; } +command { service = "NickServ"; name = "SASET PRIVATE"; command = "nickserv/saset/private"; permission = "nickserv/saset/private"; } + + +/* + * ns_logout + * + * Provides the command nickserv/logout. + * + * Used for logging out of your account. + */ +module { name = "ns_logout" } +command { service = "NickServ"; name = "LOGOUT"; command = "nickserv/logout"; } + +/* + * ns_recover + * + * Provides the command nickserv/recover. + * + * Used for recovering your nick from services or another user. + */ +module +{ + name = "ns_recover" + + /* + * If set, Services will svsnick and svsjoin users who use the recover + * command on an identified user to the nick and channels of the recovered user. + * + * This directive is optional. + */ + restoreonrecover = yes +} +command { service = "NickServ"; name = "RECOVER"; command = "nickserv/recover"; } +# Uncomment below to emulate 1.8's behavior of ghost and release. +#command { service = "NickServ"; name = "GHOST"; command = "nickserv/recover"; } +#command { service = "NickServ"; name = "RELEASE"; command = "nickserv/recover"; } + +/* + * ns_register + * + * Provides the commands nickserv/confirm, nickserv/register, and nickserv/resend. + * + * Used for registering accounts. + */ +module +{ + name = "ns_register" + + /* + * Registration confirmation setting. Set to "none" for no registration confirmation, + * "mail" for email confirmation, and "admin" to have services operators manually confirm + * every registration. Set to "disable" to completely disable all registrations. + */ + registration = "none" + + /* + * The minimum length of time between consecutive uses of NickServ's RESEND command. + * + * This directive is optional, but recommended. If not set, this restriction will be disabled. + */ + resenddelay = 90s + + /* + * Prevents users from registering their nick if they are not connected + * for at least the given number of seconds. + * + * This directive is optional. + */ + #nickregdelay = 30s + + /* + * The length of time a user using an unconfirmed account has + * before the account will be released for general use again. + */ + #unconfirmedexpire = 1d +} +command { service = "NickServ"; name = "CONFIRM"; command = "nickserv/confirm"; } +command { service = "NickServ"; name = "REGISTER"; command = "nickserv/register"; } +command { service = "NickServ"; name = "RESEND"; command = "nickserv/resend"; } + +/* + * ns_resetpass + * + * Provides the command nickserv/resetpass. + * + * Used for resetting passwords by emailing users a temporary one. + */ +module { name = "ns_resetpass" } +command { service = "NickServ"; name = "RESETPASS"; command = "nickserv/resetpass"; } + +/* + * ns_set + * + * Provides the commands: + * nickserv/set, nickserv/saset - Dummy help wrappers for the SET and SASET commands. + * nickserv/set/autoop, nickserv/saset/autoop - Determines whether or not modes are automatically set users when joining a channel. + * nickserv/set/display, nickserv/saset/display - Used for setting a users display name. + * nickserv/set/email, nickserv/saset/email - Used for setting a users email address. + * nickserv/set/keepmodes, nickserv/saset/keepmodes - Configure whether or not services should retain a user's modes across sessions. + * nickserv/set/kill, nickserv/saset/kill - Used for configuring nickname protection. + * nickserv/set/language, nickserv/saset/language - Used for configuring what language services use. + * nickserv/set/message, nickserv/saset/message - Used to configure how services send messages to you. + * nickserv/set/password, nickserv/saset/password - Used for changing a users password. + * nickserv/set/secure, nickserv/saset/secure - Used for configuring whether a user can identify by simply being recognized by nickserv/access. + * nickserv/saset/noexpire - Used for configuring noexpire, which prevents nicks from expiring. + */ +module +{ + name = "ns_set" + + /* + * Allow the use of the IMMED option in the NickServ SET KILL command. + * + * This directive is optional. + */ + #allowkillimmed = yes +} + +command { service = "NickServ"; name = "SET"; command = "nickserv/set"; } +command { service = "NickServ"; name = "SASET"; command = "nickserv/saset"; permission = "nickserv/saset/"; group = "nickserv/admin"; } + +command { service = "NickServ"; name = "SET AUTOOP"; command = "nickserv/set/autoop"; } +command { service = "NickServ"; name = "SASET AUTOOP"; command = "nickserv/saset/autoop"; permission = "nickserv/saset/autoop"; } + +command { service = "NickServ"; name = "SET DISPLAY"; command = "nickserv/set/display"; } +command { service = "NickServ"; name = "SASET DISPLAY"; command = "nickserv/saset/display"; permission = "nickserv/saset/display"; } + +command { service = "NickServ"; name = "SET EMAIL"; command = "nickserv/set/email"; } +command { service = "NickServ"; name = "SASET EMAIL"; command = "nickserv/saset/email"; permission = "nickserv/saset/email"; } + +command { service = "NickServ"; name = "SET KEEPMODES"; command = "nickserv/set/keepmodes"; } +command { service = "NickServ"; name = "SASET KEEPMODES"; command = "nickserv/saset/keepmodes"; permission = "nickserv/saset/keepmodes"; } + +command { service = "NickServ"; name = "SET KILL"; command = "nickserv/set/kill"; } +command { service = "NickServ"; name = "SASET KILL"; command = "nickserv/saset/kill"; permission = "nickserv/saset/kill"; } + +command { service = "NickServ"; name = "SET LANGUAGE"; command = "nickserv/set/language"; } +command { service = "NickServ"; name = "SASET LANGUAGE"; command = "nickserv/saset/language"; permission = "nickserv/saset/language"; } + +command { service = "NickServ"; name = "SET MESSAGE"; command = "nickserv/set/message"; } +command { service = "NickServ"; name = "SASET MESSAGE"; command = "nickserv/saset/message"; permission = "nickserv/saset/message"; } + +command { service = "NickServ"; name = "SET PASSWORD"; command = "nickserv/set/password"; } +command { service = "NickServ"; name = "SASET PASSWORD"; command = "nickserv/saset/password"; permission = "nickserv/saset/password"; } + +command { service = "NickServ"; name = "SET SECURE"; command = "nickserv/set/secure"; } +command { service = "NickServ"; name = "SASET SECURE"; command = "nickserv/saset/secure"; permission = "nickserv/saset/secure"; } + +command { service = "NickServ"; name = "SASET NOEXPIRE"; command = "nickserv/saset/noexpire"; permission = "nickserv/saset/noexpire"; } + + +/* + * ns_set_misc + * + * Provides the command nickserv/set/misc. + * + * Allows you to create arbitrary commands to set data, and have that data show up in nickserv/info. + * A field named misc_description may be given for use with help output. + */ +module { name = "ns_set_misc" } +command { service = "NickServ"; name = "SET URL"; command = "nickserv/set/misc"; misc_description = _("Associate a URL with your account"); } +command { service = "NickServ"; name = "SASET URL"; command = "nickserv/saset/misc"; misc_description = _("Associate a URL with this account"); permission = "nickserv/saset/url"; group = "nickserv/admin"; } +#command { service = "NickServ"; name = "SET DISCORD"; command = "nickserv/set/misc"; misc_description = _("Associate a Discord account with your account"); } +#command { service = "NickServ"; name = "SASET DISCORD"; command = "nickserv/saset/misc"; misc_description = _("Associate a Discord account with this account"); permission = "nickserv/saset/discord"; group = "nickserv/admin"; } +#command { service = "NickServ"; name = "SET MASTODON"; command = "nickserv/set/misc"; misc_description = _("Associate a Mastodon account with your account"); } +#command { service = "NickServ"; name = "SASET MASTODON"; command = "nickserv/saset/misc"; misc_description = _("Associate a Mastodon account with this account"); permission = "nickserv/saset/mastodon"; group = "nickserv/admin"; } +#command { service = "NickServ"; name = "SET TIMEZONE"; command = "nickserv/set/misc"; misc_description = _("Associate a time zone with your account"); } +#command { service = "NickServ"; name = "SASET TIMEZONE"; command = "nickserv/saset/misc"; misc_description = _("Associate a time zone with this account"); permission = "nickserv/saset/timezone"; group = "nickserv/admin"; } + +/* + * ns_status + * + * Provides the nickserv/status command. + * + * Used to determine if a user is recognized or identified by services. + */ +module { name = "ns_status" } +command { service = "NickServ"; name = "STATUS"; command = "nickserv/status"; } + +/* + * ns_suspend + * + * Provides the commands nickserv/suspend and nickserv/unsuspend. + * + * Used to suspend and unsuspend nicknames. Suspended nicknames can not be used but their settings are preserved. + */ +module +{ + name = "ns_suspend" + + /* + * The length of time before a suspended nick becomes unsuspended. + * + * This directive is optional. If not set, the default is never. + */ + #suspendexpire = 90d + + /* + * Settings to show to non-opers in NickServ's INFO output. + * Comment to completely disable showing any information about + * suspended nicknames to non-opers. + */ + show = "suspended, by, reason, on, expires" +} +command { service = "NickServ"; name = "SUSPEND"; command = "nickserv/suspend"; permission = "nickserv/suspend"; group = "nickserv/admin"; } +command { service = "NickServ"; name = "UNSUSPEND"; command = "nickserv/unsuspend"; permission = "nickserv/suspend"; group = "nickserv/admin"; } + +/* + * ns_update + * + * Provides the command nickserv/update. + * + * Used to update your status on all channels, turn on your vHost, etc. + */ +module { name = "ns_update" } +command { service = "NickServ"; name = "UPDATE"; command = "nickserv/update"; } + + +/* + * Extra NickServ related modules. + */ + +/* + * ns_maxemail + * + * Limits how many times the same email address may be used in Anope + * to register accounts. + */ +#module +{ + name = "ns_maxemail" + + /* + * The limit to how many registered nicks can use the same e-mail address. If set to 0 or left + * commented, there will be no limit enforced when registering new accounts or using + * /msg NickServ SET EMAIL. + */ + maxemails = 1 +} diff --git a/playbooks/anope/services.conf.j2 b/playbooks/anope/services.conf.j2 new file mode 100644 index 0000000..980e0a9 --- /dev/null +++ b/playbooks/anope/services.conf.j2 @@ -0,0 +1,202 @@ +define +{ + name = "services.host" + value = "services.secretbee.buzz" +} + +uplink +{ + host = "127.0.0.1" + ipv6 = no + ssl = no + port = 7000 + password = "{{ uplink_password }}" +} + +serverinfo +{ + name = "services.secretbee.buzz" + description = "Beeservices" + pid = "data/services.pid" + motd = "conf/services.motd" +} + +module +{ + name = "inspircd3" + use_server_side_mlock = yes + use_server_side_topiclock = yes +} + + +networkinfo +{ + networkname = "LocalNet" + nicklen = 31 + userlen = 10 + hostlen = 64 + chanlen = 32 + modelistsize = 100 + vhost_chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.-/" + allow_undotted_vhosts = no + disallow_start_or_end = ".-/" +} + +options +{ + user = "anope" + group = "anope" + casemap = "rfc1459" + seed = 1865235 + strictpasswords = yes + badpasslimit = 5 + badpasstimeout = 1h + updatetimeout = 5m + expiretimeout = 30m + readtimeout = 5s + timeoutcheck = 3s + #useprivmsg = yes + #usestrictprivmsg = yes + retrywait = 60s + hideprivilegedcommands = yes + hideregisteredcommands = yes + languages = "ca_ES.UTF-8 de_DE.UTF-8 el_GR.UTF-8 es_ES.UTF-8 fr_FR.UTF-8 hu_HU.UTF-8 it_IT.UTF-8 nl_NL.UTF-8 pl_PL.UTF-8 pt_PT.UTF-8 ru_RU.UTF-8 tr_TR.UTF-8" +} + +include +{ + type = "file" + name = "modules.conf" +} + +include +{ + type = "file" + name = "botserv.example.conf" +} + +include +{ + type = "file" + name = "chanserv.example.conf" +} + +include +{ + type = "file" + name = "global.example.conf" +} + +include +{ + type = "file" + name = "hostserv.example.conf" +} + +include +{ + type = "file" + name = "memoserv.example.conf" +} + +include +{ + type = "file" + name = "nickserv.conf" +} + +include +{ + type = "file" + name = "operserv.example.conf" +} + +log +{ + target = "services.log" + bot = "Global" + logage = 7 + admin = "*" + override = "chanserv/* nickserv/* memoserv/set ~botserv/set botserv/*" + commands = "~operserv/* *" + servers = "*" + users = "connect disconnect nick" + other = "*" + rawio = no + debug = no +} + +log +{ + bot = "Global" + target = "globops" + admin = "global/* operserv/chankill operserv/mode operserv/kick operserv/akill operserv/s*line operserv/noop operserv/jupe operserv/oline operserv/set operserv/svsnick operserv/svsjoin operserv/svspart nickserv/getpass */drop" + servers = "squit" + users = "oper" + other = "expire/* bados akill/*" +} + +opertype +{ + /* The name of this opertype */ + name = "Helper" + + /* What commands (see above) this opertype has */ + commands = "hostserv/*" +} + +opertype +{ + /* The name of this opertype */ + name = "Services Operator" + + /* What opertype(s) this inherits from. Separate with a comma. */ + inherits = "Helper, Another Helper" + + /* What commands (see above) this opertype may use */ + commands = "chanserv/list chanserv/suspend chanserv/topic memoserv/staff nickserv/list nickserv/suspend operserv/mode operserv/chankill operserv/akill operserv/session operserv/modinfo operserv/sqline operserv/oper operserv/kick operserv/ignore operserv/snline" + + /* What privs (see above) this opertype has */ + privs = "chanserv/auspex chanserv/no-register-limit memoserv/* nickserv/auspex nickserv/confirm" +} + +opertype +{ + name = "Services Administrator" + + inherits = "Services Operator" + + commands = "botserv/* chanserv/access/list chanserv/drop chanserv/getkey chanserv/saset/noexpire memoserv/sendall nickserv/saset/* nickserv/getemail operserv/news operserv/jupe operserv/svs operserv/stats operserv/oline operserv/noop operserv/forbid global/*" + + privs = "*" +} + +opertype +{ + name = "Services Root" + + commands = "*" + + privs = "*" +} + +oper +{ + name = "root" + type = "Services Root" + require_oper = yes + password = "{{ anope_oper_password }}" +} + +module +{ + name = "db_flatfile" + database = "anope.db" + keepbackups = 7 + fork = no +} + +module +{ + name = "enc_sha256" +} \ No newline at end of file diff --git a/playbooks/gitea/docker-compose.yml.j2 b/playbooks/gitea/docker-compose.yml.j2 new file mode 100644 index 0000000..a911acf --- /dev/null +++ b/playbooks/gitea/docker-compose.yml.j2 @@ -0,0 +1,43 @@ +networks: + gitea: + external: false + +services: + server: + image: docker.gitea.com/gitea:1.26.0 + container_name: gitea + environment: + - USER_UID=1000 + - USER_GID=1000 + - GITEA__database__DB_TYPE=postgres + - GITEA__database__HOST=db:5432 + - GITEA__database__NAME=gitea + - GITEA__database__USER=gitea + - GITEA__database__PASSWD={{ gitea_db_password }} + - GITEA__server__SSH_LISTEN_PORT=2222 + - GITEA__server__SSH_PORT=2222 + - SSH_LISTEN_PORT=2222 + restart: always + networks: + - gitea + volumes: + - ./gitea:/data + - /etc/timezone:/etc/timezone:ro + - /etc/localtime:/etc/localtime:ro + ports: + - "3000:3000" + - "2222:2222" + depends_on: + - db + + db: + image: docker.io/library/postgres:14 + restart: always + environment: + - POSTGRES_USER=gitea + - POSTGRES_PASSWORD={{ gitea_db_password }} + - POSTGRES_DB=gitea + networks: + - gitea + volumes: + - ./postgres:/var/lib/postgresql/data \ No newline at end of file diff --git a/playbooks/gitea/gitea.yml b/playbooks/gitea/gitea.yml new file mode 100644 index 0000000..c5609ba --- /dev/null +++ b/playbooks/gitea/gitea.yml @@ -0,0 +1,45 @@ +- name: Inspircd + hosts: pi + become: true + vars: + gitea_db_password: !vault | + $ANSIBLE_VAULT;1.1;AES256 + 63336533393735346165633965383866393736336365646330346236356239363737353234383637 + 6261383166323062663033346136633066303462343263320a333932646162336232373530373834 + 65386637336562646135613563356137313239336365653161386434313835633437613233343332 + 3736353865313938300a383266353538666135353866653263663133663232646430323966353134 + 3939 + tasks: + - name: Install podman + ansible.builtin.apt: + name: podman + state: present + + - name: Install podman-compose + ansible.builtin.apt: + name: podman-compose + state: present + + - name: Create gitea directory + ansible.builtin.file: + path: /opt/gitea + state: directory + mode: '0755' + + - name: Copy compose + ansible.builtin.template: + src: docker-compose.yml.j2 + dest: /opt/gitea/docker-compose.yml + mode: '0644' + + - name: Compose down + changed_when: true + ansible.builtin.command: + cmd: podman-compose down + chdir: /opt/gitea + + - name: Compose up + changed_when: true + ansible.builtin.command: + cmd: podman-compose up -d + chdir: /opt/gitea diff --git a/playbooks/inspircd/Containerfile b/playbooks/inspircd/Containerfile new file mode 100644 index 0000000..7982d2e --- /dev/null +++ b/playbooks/inspircd/Containerfile @@ -0,0 +1,13 @@ +FROM debian:bookworm-slim + +RUN apt-get update \ + && apt-get install -y --no-install-recommends inspircd \ + && rm -rf /var/lib/apt/lists/* \ + && mkdir -p /var/run/inspircd \ + && chown irc:irc /var/run/inspircd + +EXPOSE 6697 7000 + +USER irc + +CMD ["/usr/sbin/inspircd", "--nofork"] diff --git a/playbooks/inspircd/inspircd.conf.j2 b/playbooks/inspircd/inspircd.conf.j2 new file mode 100644 index 0000000..ff3b26b --- /dev/null +++ b/playbooks/inspircd/inspircd.conf.j2 @@ -0,0 +1,159 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/playbooks/inspircd/inspircd.motd b/playbooks/inspircd/inspircd.motd new file mode 100644 index 0000000..43f0531 --- /dev/null +++ b/playbooks/inspircd/inspircd.motd @@ -0,0 +1 @@ +Welcome to the hive. \ No newline at end of file diff --git a/playbooks/inspircd/inspircd.yml b/playbooks/inspircd/inspircd.yml new file mode 100644 index 0000000..85d7f00 --- /dev/null +++ b/playbooks/inspircd/inspircd.yml @@ -0,0 +1,94 @@ +- name: Inspircd + hosts: pi + become: true + vars: + inspircd_sendpass: !vault | + $ANSIBLE_VAULT;1.1;AES256 + 38376364613733613839386432376432306531393231383362336465653730656539356362313337 + 6365613062323137323563643963656161666631653938380a373462323834653733376663646134 + 61613831323831353761376337616635336339363830326536383632306139363831643636626566 + 3131623431393438640a356166663965626535383032383232313064363732336164613236393430 + 34343536363838626333626439336662303965643337393563396366393831626339 + inspircd_root_oper_password: !vault | + $ANSIBLE_VAULT;1.1;AES256 + 30343162623465633134623862326134636233633237613864363862316463653733383939636136 + 6263353934666637376563303761376162316334336534390a616336336663316462303862363662 + 64386137653566383962616131663866393966613664623036383834633439333162303032656666 + 3731353664653761620a333833336531393266643531323935393364656662623530376166383630 + 62623132626362643061646639303833346366396562613238623338326531346439 + inspircd_bee_oper_password: !vault | + $ANSIBLE_VAULT;1.1;AES256 + 39396337663133313131383464393236316163653631616565613039393364333636656537393663 + 6439383936333837313536636536643063356630306130360a343533326133396632313631336136 + 37346330363231623130353432333134313963653365326633383461613834643634303566303230 + 3032326237653034350a333934623834656666333234616364663537383166626566653039393661 + 62336462613030313532333566363164336262376635633131313133386531333962 + tasks: + - name: Install podman + ansible.builtin.apt: + name: podman + state: present + + - name: Create inspircd config directory + ansible.builtin.file: + path: /etc/inspircd + state: directory + mode: '0755' + + - name: Copy config + ansible.builtin.template: + src: inspircd.conf.j2 + dest: /etc/inspircd/inspircd.conf + mode: '0644' + notify: Reload inspircd + + - name: Copy motd + ansible.builtin.copy: + src: inspircd.motd + dest: /etc/inspircd/inspircd.motd + mode: '0644' + notify: Reload inspircd + + - name: Create container build context directory + ansible.builtin.file: + path: /tmp/inspircd-build + state: directory + mode: '0755' + + - name: Copy Containerfile to build context + ansible.builtin.copy: + src: Containerfile + dest: /tmp/inspircd-build/Containerfile + mode: '0644' + + - name: Build inspircd container image + ansible.builtin.command: + cmd: podman build -t inspircd:local /tmp/inspircd-build + changed_when: true + + - name: Stop and remove existing inspircd container + ansible.builtin.command: + cmd: podman rm -f inspircd + failed_when: false + changed_when: false + + - name: Run inspircd container + changed_when: true + ansible.builtin.command: + cmd: >- + podman run -d + --name inspircd + --network host + --restart always + -v /etc/inspircd/inspircd.conf:/etc/inspircd/inspircd.conf:ro + -v /etc/inspircd/inspircd.motd:/etc/inspircd/inspircd.motd:ro + -v /etc/inspircd/cert.pem:/etc/inspircd/cert.pem:ro + -v /etc/inspircd/key.pem:/etc/inspircd/key.pem:ro + inspircd:local + + handlers: + - name: Reload inspircd + changed_when: true + ansible.builtin.command: + cmd: podman kill --signal USR1 inspircd + failed_when: false diff --git a/playbooks/letsencrypt/irc-post-hook.sh b/playbooks/letsencrypt/irc-post-hook.sh new file mode 100644 index 0000000..34ba471 --- /dev/null +++ b/playbooks/letsencrypt/irc-post-hook.sh @@ -0,0 +1,32 @@ +#!/bin/sh +set -e + +# The location your renewal tool places your certificates. +CERT_DIR="/etc/letsencrypt/live/irc.secretbee.buzz" + +# The location of the InspIRCd config directory. +INSPIRCD_CONFIG_DIR="/etc/inspircd" + +# The location of the InspIRCd pid file. +INSPIRCD_PID_FILE="/var/run/inspircd/inspircd.pid" + +# The user:group that owns the inspircd config directory on the host. +INSPIRCD_OWNER="root:root" + +if [ -e ${CERT_DIR} -a -e ${INSPIRCD_CONFIG_DIR} ] +then + cp "${CERT_DIR}/fullchain.pem" "${INSPIRCD_CONFIG_DIR}/cert.pem" + cp "${CERT_DIR}/privkey.pem" "${INSPIRCD_CONFIG_DIR}/key.pem" + chown ${INSPIRCD_OWNER} "${INSPIRCD_CONFIG_DIR}/cert.pem" "${INSPIRCD_CONFIG_DIR}/key.pem" + + if podman container exists inspircd 2>/dev/null + then + podman kill --signal USR1 inspircd + elif [ -r ${INSPIRCD_PID_FILE} ] + then + kill -USR1 $(cat ${INSPIRCD_PID_FILE}) + elif [ -d /lib/systemd ] && systemctl --quiet is-active inspircd + then + systemctl kill --signal USR1 inspircd + fi +fi \ No newline at end of file diff --git a/playbooks/letsencrypt/letsencrypt.yml b/playbooks/letsencrypt/letsencrypt.yml new file mode 100644 index 0000000..1033523 --- /dev/null +++ b/playbooks/letsencrypt/letsencrypt.yml @@ -0,0 +1,49 @@ +- name: Letsencrypt + hosts: pi + become: true + tasks: + - name: Install Certbot + ansible.builtin.apt: + name: python3-certbot-nginx + state: present + + - name: Request root certificates + changed_when: true + ansible.builtin.command: certbot certonly --nginx -m secretbumblebee@proton.me --agree-tos -n --domains secretbee.buzz + + - name: Request root certificates + changed_when: true + ansible.builtin.command: certbot certonly --nginx -m secretbumblebee@proton.me --agree-tos -n --domains lounge.secretbee.buzz + + - name: Request irc certificates + changed_when: true + ansible.builtin.command: certbot certonly --nginx -m secretbumblebee@proton.me --agree-tos -n --domains irc.secretbee.buzz + + - name: Request git certificates + changed_when: true + ansible.builtin.command: certbot certonly --nginx -m secretbumblebee@proton.me --agree-tos -n --domains git.secretbee.buzz + + - name: Request grafana certificates + changed_when: true + ansible.builtin.command: certbot certonly --nginx -m secretbumblebee@proton.me --agree-tos -n --domains grafana.secretbee.buzz + + - name: Add post hook script + ansible.builtin.copy: + src: irc-post-hook.sh + dest: /opt/irc-post-hook.sh + owner: root + group: root + mode: '0755' + + - name: Renew for post hook + changed_when: true + ansible.builtin.command: certbot renew --cert-name irc.secretbee.buzz --deploy-hook /opt/irc-post-hook.sh --force-renewal + + - name: Change permission on live + ansible.builtin.file: + path: /etc/letsencrypt/live/ + mode: '0755' + - name: Change permission on live + ansible.builtin.file: + path: /etc/letsencrypt/archive/ + mode: '0755' diff --git a/playbooks/letsencrypt/nginx-post-hook.sh b/playbooks/letsencrypt/nginx-post-hook.sh new file mode 100644 index 0000000..214c554 --- /dev/null +++ b/playbooks/letsencrypt/nginx-post-hook.sh @@ -0,0 +1,7 @@ +#!/bin/sh +set -e + +if podman container exists nginx 2>/dev/null +then + podman kill --signal HUP nginx +fi diff --git a/playbooks/monitoring/docker-compose.yml.j2 b/playbooks/monitoring/docker-compose.yml.j2 new file mode 100644 index 0000000..cc5318f --- /dev/null +++ b/playbooks/monitoring/docker-compose.yml.j2 @@ -0,0 +1,33 @@ +networks: + monitoring: + external: false + +services: + prometheus: + image: docker.io/prom/prometheus + container_name: prometheus + restart: unless-stopped + ports: + - '9090:9090' + volumes: + - prometheus-data:/prometheus + - /opt/monitoring/prometheus.yml:/etc/prometheus/prometheus.yml + networks: + - monitoring + + grafana: + image: docker.io/grafana/grafana + container_name: grafana + restart: unless-stopped + ports: + - '4000:4000' + volumes: + - grafana-storage:/var/lib/grafana + environment: + - GF_SECURITY_SECRET_KEY={{ grafana_secret }} + - GF_SERVER_DOMAIN=grafana.secretbee.buzz + - GF_SERVER_ROOT_URL=https://grafana.secretbee.buzz/ + - GF_SERVER_HTTP_PORT=4000 + - GF_SERVER_PROTOCOL=HTTP + networks: + - monitoring \ No newline at end of file diff --git a/playbooks/monitoring/monitoring.yml b/playbooks/monitoring/monitoring.yml new file mode 100644 index 0000000..3a3d9db --- /dev/null +++ b/playbooks/monitoring/monitoring.yml @@ -0,0 +1,56 @@ +- name: Prometheus + hosts: pi + become: true + vars: + grafana_secret: !vault | + $ANSIBLE_VAULT;1.1;AES256 + 38343463346232356634353236356534666166626665663237306130623336373062363563386537 + 6665356165346134643235383164383266373363316262310a613332386564636430643561373463 + 36346437353963333839656163623933616662326132373036636166616538343966356361636164 + 3035656163616631330a663363343363643065356535356633623638376430326539363536666638 + 65333930613135333364383462363233386662386531323935353932373465363234 + tasks: + - name: Install podman + ansible.builtin.apt: + name: podman + state: present + + - name: Install podman-compose + ansible.builtin.apt: + name: podman-compose + state: present + + - name: Install prometheus-node-exporter + ansible.builtin.apt: + name: prometheus-node-exporter + state: present + + - name: Create prometheus config directory + ansible.builtin.file: + path: /opt/monitoring + state: directory + mode: '0755' + + - name: Copy config + ansible.builtin.copy: + src: prometheus_config.yml + dest: /opt/monitoring/prometheus.yml + mode: '0644' + + - name: Copy compose + ansible.builtin.template: + src: docker-compose.yml.j2 + dest: /opt/monitoring/docker-compose.yml + mode: '0644' + + - name: Compose down + changed_when: true + ansible.builtin.command: + cmd: podman-compose down + chdir: /opt/monitoring + + - name: Compose up + changed_when: true + ansible.builtin.command: + cmd: podman-compose up -d + chdir: /opt/monitoring diff --git a/playbooks/monitoring/prometheus_config.yml b/playbooks/monitoring/prometheus_config.yml new file mode 100644 index 0000000..b3db8ea --- /dev/null +++ b/playbooks/monitoring/prometheus_config.yml @@ -0,0 +1,11 @@ +global: + scrape_interval: 15s # Set the scrape interval to every 15 seconds. Default is every 1 minute. + evaluation_interval: 15s # Evaluate rules every 15 seconds. The default is every 1 minute. + +scrape_configs: + - job_name: 'prometheus' + static_configs: + - targets: ['localhost:9090'] + - job_name: node + static_configs: + - targets: ['host.containers.internal:9100'] \ No newline at end of file diff --git a/playbooks/nginx/html/icon.ico b/playbooks/nginx/html/icon.ico new file mode 100644 index 0000000..e91eaa1 Binary files /dev/null and b/playbooks/nginx/html/icon.ico differ diff --git a/playbooks/nginx/html/icon.png b/playbooks/nginx/html/icon.png new file mode 100644 index 0000000..51f59c2 Binary files /dev/null and b/playbooks/nginx/html/icon.png differ diff --git a/playbooks/nginx/html/index.html b/playbooks/nginx/html/index.html new file mode 100644 index 0000000..351bb37 --- /dev/null +++ b/playbooks/nginx/html/index.html @@ -0,0 +1,16 @@ + + + + Secretbee + + + +

bee

+

Welcome to the hive.

+

Available services:

+

 

+

irc

+

Grab yourself an irc client (for example mirc) and connect to irc.secretbee.buzz and port 6697.

+

All traffic is encrypted and secured.

+ + \ No newline at end of file diff --git a/playbooks/nginx/nginx.yml b/playbooks/nginx/nginx.yml new file mode 100644 index 0000000..b120082 --- /dev/null +++ b/playbooks/nginx/nginx.yml @@ -0,0 +1,26 @@ +- name: Nginx + hosts: pi + become: true + tasks: + - name: Install nginx + ansible.builtin.apt: + name: nginx + state: present + + - name: Delete default + ansible.builtin.file: + path: /etc/nginx/sites-enabled/default + state: absent + + - name: Copy sites + ansible.builtin.copy: + src: sites-enabled/ + dest: /etc/nginx/sites-enabled/ + owner: root + group: root + mode: '0644' + + - name: Restart nginx + ansible.builtin.service: + name: nginx + state: restarted diff --git a/playbooks/nginx/sites-enabled/git.secretbee.buzz b/playbooks/nginx/sites-enabled/git.secretbee.buzz new file mode 100644 index 0000000..5c3cdd5 --- /dev/null +++ b/playbooks/nginx/sites-enabled/git.secretbee.buzz @@ -0,0 +1,32 @@ +## Redirect all HTTP traffic to HTTPS +server { + listen 80; + server_name git.secretbee.buzz; + + location / { + return 301 https://$host$request_uri; + } +} + +server { + listen 443 ssl; + server_name git.secretbee.buzz; + + add_header Strict-Transport-Security "max-age=31536000" always; + + ssl_certificate /etc/letsencrypt/live/git.secretbee.buzz/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/git.secretbee.buzz/privkey.pem; + ssl_protocols TLSv1.2 TLSv1.3; + ssl_ciphers HIGH:!aNULL:!MD5; + + location / { + client_max_body_size 512M; + proxy_pass http://localhost:3000; + proxy_set_header Connection $http_connection; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } +} diff --git a/playbooks/nginx/sites-enabled/grafana.secretbee.buzz b/playbooks/nginx/sites-enabled/grafana.secretbee.buzz new file mode 100644 index 0000000..ced0093 --- /dev/null +++ b/playbooks/nginx/sites-enabled/grafana.secretbee.buzz @@ -0,0 +1,42 @@ +map $http_upgrade $connection_upgrade { + default upgrade; + '' close; +} + +upstream grafana { + server localhost:4000; +} + +server { + listen 80; + server_name grafana.secretbee.buzz; + + location / { + return 301 https://$host$request_uri; + } +} + +server { + listen 443; + server_name grafana.secretbee.buzz; + + add_header Strict-Transport-Security "max-age=31536000" always; + + ssl_certificate /etc/letsencrypt/live/grafana.secretbee.buzz/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/grafana.secretbee.buzz/privkey.pem; + ssl_protocols TLSv1.2 TLSv1.3; + ssl_ciphers HIGH:!aNULL:!MD5; + + location / { + proxy_set_header Host $host; + proxy_pass http://localhost:4000; + } + + location /api/live/ { + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection $connection_upgrade; + proxy_set_header Host $host; + proxy_pass http://localhost:4000; + } +} \ No newline at end of file diff --git a/playbooks/nginx/sites-enabled/lounge.secretbee.buzz b/playbooks/nginx/sites-enabled/lounge.secretbee.buzz new file mode 100644 index 0000000..b977fa4 --- /dev/null +++ b/playbooks/nginx/sites-enabled/lounge.secretbee.buzz @@ -0,0 +1,36 @@ +## Redirect all HTTP traffic to HTTPS +server { + listen 80; + server_name lounge.secretbee.buzz; + + location / { + return 301 https://$host$request_uri; + } +} + +server { + listen 443 ssl; + server_name lounge.secretbee.buzz; + + add_header Strict-Transport-Security "max-age=31536000" always; + + ssl_certificate /etc/letsencrypt/live/lounge.secretbee.buzz/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/lounge.secretbee.buzz/privkey.pem; + ssl_protocols TLSv1.2 TLSv1.3; + ssl_ciphers HIGH:!aNULL:!MD5; + + location / { + proxy_pass http://127.0.0.1:9000/; + proxy_http_version 1.1; + proxy_set_header Connection "upgrade"; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header X-Forwarded-For $remote_addr; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_read_timeout 1d; + } + + location /folder/ { + proxy_pass http://127.0.0.1:9000/uploads/; + proxy_set_header X-Forwarded-For $remote_addr; + } +} diff --git a/playbooks/nginx/sites-enabled/secretbee.buzz b/playbooks/nginx/sites-enabled/secretbee.buzz new file mode 100644 index 0000000..508c447 --- /dev/null +++ b/playbooks/nginx/sites-enabled/secretbee.buzz @@ -0,0 +1,32 @@ +limit_req_zone $binary_remote_addr zone=mylimit:10m rate=10r/s; + +server { + listen 80 default_server; + server_name secretbee.buzz; + limit_req zone=mylimit burst=20; + + location / { + return 301 https://$host$request_uri; + } +} + +server { + listen 443 ssl; + server_name secretbee.buzz; + + limit_req zone=mylimit burst=20; + + root /var/www; + + add_header Strict-Transport-Security "max-age=31536000" always; + + ssl_certificate /etc/letsencrypt/live/secretbee.buzz/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/secretbee.buzz/privkey.pem; + ssl_protocols TLSv1.2 TLSv1.3; + ssl_ciphers HIGH:!aNULL:!MD5; + + location / { + default_type "text/html"; + try_files $uri $uri.html $uri/index.html index.html; + } +} diff --git a/playbooks/postfix/Containerfile b/playbooks/postfix/Containerfile new file mode 100644 index 0000000..a1aed49 --- /dev/null +++ b/playbooks/postfix/Containerfile @@ -0,0 +1,13 @@ +FROM debian:bookworm-slim + +RUN apt-get update \ + && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ + postfix \ + libsasl2-modules \ + && rm -rf /var/lib/apt/lists/* + +COPY main.cf /etc/postfix/main.cf + +EXPOSE 25 587 + +CMD ["postfix", "start-fg"] diff --git a/playbooks/postfix/main.cf b/playbooks/postfix/main.cf new file mode 100644 index 0000000..882b7df --- /dev/null +++ b/playbooks/postfix/main.cf @@ -0,0 +1,29 @@ +# Postfix main configuration +# https://www.postfix.org/postconf.5.html + +# --- Identity --- +myhostname = mail.secretbee.buzz +mydomain = secretbee.buzz +myorigin = $mydomain + +# --- Network --- +inet_interfaces = all +inet_protocols = ipv4 + +# --- Local delivery --- +mydestination = $myhostname, localhost.$mydomain, localhost + +# --- Relay --- +mynetworks = 127.0.0.0/8 +relayhost = + +# --- TLS (outbound) --- +smtp_tls_security_level = may +smtp_tls_loglevel = 1 +smtp_tls_CAfile = /etc/ssl/certs/ca-certificates.crt + +# --- Mailbox --- +home_mailbox = Maildir/ + +# --- Limits --- +message_size_limit = 52428800 diff --git a/playbooks/thelounge/config.js b/playbooks/thelounge/config.js new file mode 100644 index 0000000..361c8ef --- /dev/null +++ b/playbooks/thelounge/config.js @@ -0,0 +1,465 @@ +"use strict"; +module.exports = { + // ## Server settings + // ### `public` + // + // When set to `true`, The Lounge starts in public mode. When set to `false`, + // it starts in private mode. + // + // - A **public server** does not require authentication. Anyone can connect + // to IRC networks in this mode. All IRC connections and channel + // scrollbacks are lost when a user leaves the client. + // - A **private server** requires users to log in. Their IRC connections are + // kept even when they are not using or logged in to the client. All joined + // channels and scrollbacks are available when they come back. + // + // This value is set to `false` by default. + public: true, + // ### `host` + // + // IP address or hostname for the web server to listen to. For example, set it + // to `"127.0.0.1"` to accept connections from localhost only. + // + // For UNIX domain sockets, use `"unix:/absolute/path/to/file.sock"`. + // + // This value is set to `undefined` by default to listen on all interfaces. + host: "127.0.0.1", + // ### `port` + // + // Set the port to listen to. + // + // This value is set to `9000` by default. + port: 9000, + // ### `bind` + // + // Set the local IP to bind to for outgoing connections. + // + // This value is set to `undefined` by default to let the operating system + // pick its preferred one. + bind: undefined, + // ### `reverseProxy` + // + // When set to `true`, The Lounge is marked as served behind a reverse proxy + // and will honor the `X-Forwarded-For` header. + // + // This value is set to `false` by default. + reverseProxy: true, + // ### `maxHistory` + // + // Defines the maximum number of history lines that will be kept in memory per + // channel/query, in order to reduce the memory usage of the server. Setting + // this to `-1` will keep unlimited amount. + // + // This value is set to `10000` by default. + maxHistory: 10000, + // ### `https` + // + // These settings are used to run The Lounge's web server using encrypted TLS. + // + // If you want more control over the webserver, + // [use a reverse proxy instead](https://thelounge.chat/docs/guides/reverse-proxies). + // + // The available keys for the `https` object are: + // + // - `enable`: when set to `false`, HTTPS support is disabled + // and all other values are ignored. + // - `key`: Path to the private key file. + // - `certificate`: Path to the certificate. + // - `ca`: Path to the CA bundle. + // + // The value of `enable` is set to `false` to disable HTTPS by default, in + // which case the other two string settings are ignored. + https: { + enable: false, + key: "", + certificate: "", + ca: "", + }, + // ## Client settings + // ### `theme` + // + // Set the default theme to serve to new users. They will be able to select a + // different one in their client settings among those available. + // + // The Lounge ships with two themes (`default` and `morning`) and can be + // extended by installing more themes. Read more about how to manage them + // [here](https://thelounge.chat/docs/guides/theme-creation). + // + // This value needs to be the package name and not the display name. For + // example, the value for Morning would be `morning`, and the value for + // Solarized would be `thelounge-theme-solarized`. + // + // This value is set to `"default"` by default. + theme: "default", + // ### `prefetch` + // + // When set to `true`, The Lounge will load thumbnails and site descriptions + // from URLs posted in channels and private messages. + // + // This value is set to `false` by default. + prefetch: false, + // ### `disableMediaPreview` + // + // When set to `true`, The Lounge will not preview media (images, video and + // audio) hosted on third-party sites. This ensures the client does not + // make any requests to external sites. If `prefetchStorage` is enabled, + // images proxied via the The Lounge will be previewed. + // + // This has no effect if `prefetch` is set to `false`. + // + // This value is set to `false` by default. + disableMediaPreview: false, + // ### `prefetchStorage` + // When set to `true`, The Lounge will store and proxy prefetched images and + // thumbnails on the filesystem rather than directly display the content at + // the original URLs. + // + // This option primarily exists to resolve mixed content warnings by not + // loading images from http hosts. This option does not work for video + // or audio as The Lounge will only load these from https hosts. + // + // If storage is enabled, The Lounge will fetch and store images and thumbnails + // in the `${THELOUNGE_HOME}/storage` folder. + // + // Images are deleted when they are no longer referenced by any message + // (controlled by `maxHistory`), and the folder is cleaned up when The Lounge + // restarts. + // + // This value is set to `false` by default. + prefetchStorage: false, + // ### `prefetchMaxImageSize` + // + // When `prefetch` is enabled, images will only be displayed if their file + // size does not exceed this limit. + // + // This value is set to `2048` kilobytes by default. + prefetchMaxImageSize: 2048, + // ### prefetchMaxSearchSize + // + // This value sets the maximum response size allowed when finding the Open + // Graph tags for link previews. The entire response is temporarily stored + // in memory and for some sites like YouTube this can easily exceed 300 + // kilobytes. + // + // This value is set to `50` kilobytes by default. + prefetchMaxSearchSize: 50, + // ### `prefetchTimeout` + // + // When `prefetch` is enabled, this value sets the number of milliseconds + // before The Lounge gives up attempting to fetch a link. This can be useful + // if you've increased the `prefetchMaxImageSize`. + // + // Take caution, however, that an inordinately large value may lead to + // performance issues or even a denial of service, since The Lounge will not + // be able to clean up outgoing connections as quickly. Usually the default + // value is appropriate, so only change it if necessary. + // + // This value is set to `5000` milliseconds by default. + prefetchTimeout: 5000, + // ### `fileUpload` + // + // Allow uploading files to the server hosting The Lounge. + // + // Files are stored in the `${THELOUNGE_HOME}/uploads` folder, do not expire, + // and are not removed by The Lounge. This may cause issues depending on your + // hardware, for example in terms of disk usage. + // + // The available keys for the `fileUpload` object are: + // + // - `enable`: When set to `true`, files can be uploaded on the client with a + // drag-and-drop or using the upload dialog. + // - `maxFileSize`: When file upload is enabled, users sending files above + // this limit will be prompted with an error message in their browser. A value of + // `-1` disables the file size limit and allows files of any size. **Use at + // your own risk.** This value is set to `10240` kilobytes by default. + // - `baseUrl`: If you want to change the URL where uploaded files are accessed, + // you can set this option to `"https://example.com/folder/"` and the final URL + // would look like `"https://example.com/folder/aabbccddeeff1234/name.png"`. + // If you use this option, you must have a reverse proxy configured, + // to correctly proxy the uploads URLs back to The Lounge. + // This value is set to `null` by default. + fileUpload: { + enable: false, + maxFileSize: 10240, + baseUrl: null, + }, + // ### `transports` + // + // Set `socket.io` transports. + // + // This value is set to `["polling", "websocket"]` by default. + transports: ["polling", "websocket"], + // ### `leaveMessage` + // + // Set users' default `quit` and `part` messages if they are not providing + // one. + // + // This value is set to `"The Lounge - https://thelounge.chat"` by + // default. + leaveMessage: "Buzz...", + // ## Default network + // ### `defaults` + // + // Specifies default network information that will be used as placeholder + // values in the *Connect* window. + // + // The available keys for the `defaults` object are: + // + // - `name`: Name to display in the channel list of The Lounge. This value is + // not forwarded to the IRC network. + // - `host`: IP address or hostname of the IRC server. + // - `port`: Usually 6667 for unencrypted connections and 6697 for + // connections encrypted with TLS. + // - `password`: Connection password. If the server supports SASL capability, + // then this password will be used in SASL authentication. + // - `tls`: Enable TLS connections + // - `rejectUnauthorized`: Whether the server certificate should be verified + // against the list of supplied Certificate Authorities (CAs) by your + // Node.js installation. + // - `nick`: Nick name. Percent signs (`%`) will be replaced by random + // numbers from 0 to 9. For example, `Guest%%%` may become `Guest123`. + // - `username`: User name. + // - `realname`: Real name displayed by some clients. Defaults to the nick if set to "" + // - `leaveMessage`: Network specific leave message (overrides global leaveMessage) + // - `join`: Comma-separated list of channels to auto-join once connected. + // + // This value is set to connect to the official channel of The Lounge on + // Libera.Chat by default: + // + // ```js + // defaults: { + // name: "Libera.Chat", + // host: "irc.libera.chat", + // port: 6697, + // password: "", + // tls: true, + // rejectUnauthorized: true, + // nick: "thelounge%%", + // username: "thelounge", + // realname: "The Lounge User", + // join: "#thelounge" + // } + // ``` + defaults: { + name: "secretbee", + host: "irc.secretbee.buzz", + port: 6697, + password: "", + tls: true, + rejectUnauthorized: true, + nick: "bee%%", + username: "bee", + realname: "", + join: "#thehive", + leaveMessage: "", + }, + // ### `lockNetwork` + // + // When set to `true`, users will not be able to modify host, port and TLS + // settings and will be limited to the configured network. + // These fields will also be hidden from the UI. + // + // This value is set to `false` by default. + lockNetwork: true, + // ## User management + // ### `messageStorage` + // The Lounge can log user messages, for example to access them later or to + // reload messages on server restart. + // Set this array with one or multiple values to enable logging: + // - `text`: Messages per network and channel will be stored as text files. + // **Messages will not be reloaded on restart.** + // - `sqlite`: Messages are stored in SQLite database files, one per user. + // + // Logging can be disabled globally by setting this value to an empty array + // `[]`. Logging is also controlled per user individually in the `log` key of + // their JSON configuration file. + // + // This value is set to `["sqlite", "text"]` by default. + messageStorage: ["sqlite", "text"], + // ### `storagePolicy` + // When the sqlite storage is in use, control the maximum storage duration. + // A background task will periodically clean up messages older than the limit. + // The available keys for the `storagePolicy` object are: + // + // - `enabled`: If this is false, the cleaning task is not running. + // - `maxAgeDays`: Maximum age of an entry in days. + // - `deletionPolicy`: Controls what types of messages are being deleted. + // Valid options are: + // - `statusOnly`: Only delete message types which are status related (e.g. away, back, join, parts, mode, ctcp...) + // but keep actual messages from nicks. This keeps the DB size down while retaining "precious" messages. + // - `everything`: Delete everything, including messages from irc nicks + storagePolicy: { + enabled: false, + maxAgeDays: 7, + deletionPolicy: "statusOnly", + }, + // ### `useHexIp` + // + // When set to `true`, users' IP addresses will be encoded as hex. + // + // This is done to share the real user IP address with the server for host + // masking purposes. This is encoded in the `username` field and only supports + // IPv4. + // + // This value is set to `false` by default. + useHexIp: true, + // ## WEBIRC support + // + // When enabled, The Lounge will pass the connecting user's host and IP to the + // IRC server. Note that this requires to obtain a password from the IRC + // network that The Lounge will be connecting to and generally involves a lot + // of trust from the network you are connecting to. + // + // There are 2 ways to configure the `webirc` setting: + // + // - **Basic**: an object where keys are IRC hosts and values are passwords. + // For example: + // + // ```json + // webirc: { + // "irc.example.net": "thisiswebircpassword1", + // "irc.example.org": "thisiswebircpassword2", + // }, + // ``` + // + // - **Advanced**: an object where keys are IRC hosts and values are functions + // that take two arguments (`webircObj`, `network`) and return an + // object to be directly passed to `irc-framework`. `webircObj` contains the + // generated object which you can modify. For example: + // + // ```js + // webirc: { + // "irc.example.com": (webircObj, network) => { + // webircObj.password = "thisiswebircpassword"; + // webircObj.hostname = `webirc/${webircObj.hostname}`; + // return webircObj; + // }, + // }, + // ``` + // + // This value is set to `null` to disable WEBIRC by default. + webirc: null, + // ## identd and oidentd support + // ### `identd` + // + // Run The Lounge with `identd` support. + // + // The available keys for the `identd` object are: + // + // - `enable`: When `true`, the identd daemon runs on server start. + // - `port`: Port to listen for ident requests. + // + // The value of `enable` is set to `false` to disable `identd` support by + // default, in which case the value of `port` is ignored. The default value of + // `port` is 113. + identd: { + enable: false, + port: 113, + }, + // ### `oidentd` + // + // When this setting is a string, this enables `oidentd` support using the + // configuration file located at the given path. + // + // This is set to `null` by default to disable `oidentd` support. + oidentd: null, + // ## LDAP support + // These settings enable and configure LDAP authentication. + // + // They are only being used in private mode. To know more about private mode, + // see the `public` setting above. + // + // The authentication process works as follows: + // + // 1. The Lounge connects to the LDAP server with its system credentials. + // 2. It performs an LDAP search query to find the full DN associated to the + // user requesting to log in. + // 3. The Lounge tries to connect a second time, but this time using the + // user's DN and password. Authentication is validated if and only if this + // connection is successful. + // + // The search query takes a couple of parameters in `searchDN`: + // + // - a base DN `searchDN/base`. Only children nodes of this DN will likely + // be returned; + // - a search scope `searchDN/scope` (see LDAP documentation); + // - the query itself, built as `(&(=) )` + // where `` is the user name provided in the log in request, + // `` is provided by the config and `` is a filtering + // complement also given in the config, to filter for instance only for + // nodes of type `inetOrgPerson`, or whatever LDAP search allows. + // + // Alternatively, you can specify the `bindDN` parameter. This will make The + // Lounge ignore `searchDN` options and assume that the user DN is always + // `,=`, where `` is the user name + // provided in the log in request, and `` and `` are + // provided by the configuration. + // + // The available keys for the `ldap` object are: + ldap: { + // - `enable`: when set to `false`, LDAP support is disabled and all other + // values are ignored. + enable: false, + // - `url`: A url of the form `ldaps://:`. + // For plain connections, use the `ldap` scheme. + url: "ldaps://example.com", + // - `tlsOptions`: LDAP connection TLS options (only used if scheme is + // `ldaps://`). It is an object whose values are Node.js' `tls.connect()` + // options. It is set to `{}` by default. + // For example, this option can be used in order to force the use of IPv6: + // ```js + // { + // host: 'my::ip::v6', + // servername: 'example.com' + // } + // ``` + tlsOptions: {}, + // - `primaryKey`: LDAP primary key. It is set to `"uid"` by default. + primaryKey: "uid", + // - `baseDN`: LDAP base DN, alternative to `searchDN`. For example, set it + // to `"ou=accounts,dc=example,dc=com"`. + // When unset, the LDAP auth logic with use `searchDN` instead to locate users. + // - `searchDN`: LDAP search DN settings. This defines the procedure by + // which The Lounge first looks for the user DN before authenticating them. + // It is ignored if `baseDN` is specified. It is an object with the + // following keys: + searchDN: { + // - `rootDN`: This bind DN is used to query the server for the DN of + // the user. This is supposed to be a system user that has access in + // read-only to the DNs of the people that are allowed to log in. + // It is set to `"cn=thelounge,ou=system-users,dc=example,dc=com"` by + // default. + rootDN: "cn=thelounge,ou=system-users,dc=example,dc=com", + // - `rootPassword`: Password of The Lounge LDAP system user. + rootPassword: "1234", + // - `filter`: it is set to `"(&(objectClass=person)(memberOf=ou=accounts,dc=example,dc=com))"` + // by default. + filter: "(&(objectClass=person)(memberOf=ou=accounts,dc=example,dc=com))", + // - `base`: LDAP search base (search only within this node). It is set + // to `"dc=example,dc=com"` by default. + base: "dc=example,dc=com", + // - `scope`: LDAP search scope. It is set to `"sub"` by default. + scope: "sub", + }, + }, + // ## Debugging settings + // The `debug` object contains several settings to enable debugging in The + // Lounge. Use them to learn more about an issue you are noticing but be aware + // this may produce more logging or may affect connection performance so it is + // not recommended to use them by default. + // + // All values in the `debug` object are set to `false`. + debug: { + // ### `debug.ircFramework` + // + // When set to true, this enables extra debugging output provided by + // [`irc-framework`](https://github.com/kiwiirc/irc-framework), the + // underlying IRC library for Node.js used by The Lounge. + ircFramework: false, + // ### `debug.raw` + // + // When set to `true`, this enables logging of raw IRC messages into each + // server window, displayed on the client. + raw: false, + }, +}; \ No newline at end of file diff --git a/playbooks/thelounge/thelounge.yml b/playbooks/thelounge/thelounge.yml new file mode 100644 index 0000000..a258936 --- /dev/null +++ b/playbooks/thelounge/thelounge.yml @@ -0,0 +1,29 @@ +- name: Thelounge + hosts: pi + become: true + tasks: + - name: Install nodejs + ansible.builtin.apt: + name: nodejs + state: present + + - name: Copy install + ansible.builtin.copy: + src: thelounge_4.4.3_all.deb + dest: /tmp/thelounge_4.4.3_all.deb + owner: root + group: root + mode: '0755' + + - name: Copy config + ansible.builtin.copy: + src: config.js + dest: /etc/thelounge/config.js + owner: thelounge + group: thelounge + mode: '0660' + + - name: Restart service + ansible.builtin.service: + name: thelounge + state: restarted diff --git a/playbooks/thelounge/thelounge_4.4.3_all.deb b/playbooks/thelounge/thelounge_4.4.3_all.deb new file mode 100644 index 0000000..33d77c1 Binary files /dev/null and b/playbooks/thelounge/thelounge_4.4.3_all.deb differ