├── LICENSE ├── README.md ├── banner.gif └── setup.sh /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 2-Clause License 2 | 3 | Copyright (c) 2020, Cristian Souza 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 20 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 |

4 | 5 | # OpenBSD self-hosted 6 | 7 | This is the script I use to deploy my self-hosted services on top of OpenBSD. You are free to use and modify it according to your needs. Contributions are also welcome! 8 | 9 | **Tested on OpenBSD 6.7** 10 | 11 | ## Prerequisites 12 | 13 | 1. This script assumes you are running a fresh OpenBSD system on a public server (e.g., a VPS). 14 | 2. You must have a domain name pointing to your server's IP and subdomains (www, git, cloud, and mail). 15 | 16 | ## Goals 17 | 18 | 1. Self-hosting my most used services for privacy reasons. 19 | 2. Being able to customize my setup and add more features whenever I want. 20 | 21 | ## Software 22 | 23 | ### OpenBSD base system: 24 | 25 | - acme-client(1): for managing Let's Encrypt certificates. 26 | - httpd(8): nice and simple web server. 27 | - smtpd(8): for managing mails. 28 | 29 | ### Ports: 30 | 31 | - dovecot: for IMAP access. 32 | - Git and cgit: for managing source code repositories. 33 | - PHP: for running RainLoop and Nextcloud. 34 | - PostgreSQL: data storage for Nextcloud. 35 | - rspamd: spam filtering system. 36 | 37 | ### Web systems: 38 | 39 | - RainLoop: a nice UI for the email system. 40 | - Nextcloud: a safe place for all your files. 41 | 42 | Please remember to always check the source and not just run some random code on your machine. 43 | 44 | ## Installation 45 | 46 | Just run the following commands as root: 47 | 48 | `chmod +x setup.sh && ./setup.sh` 49 | 50 | You will be prompt for some basic information required for the configuration files. 51 | 52 | ## What you will get after installation: 53 | 54 | A fully functional self-hosted server for your git repositories, files, and mails. 55 | -------------------------------------------------------------------------------- /banner.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cristianzsh/openbsd-selfhosted/38c9cc2e1f41d64dd0d0e0499f90c2974d3e7a96/banner.gif -------------------------------------------------------------------------------- /setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # OpenBSD self-hosted script. 3 | # https://github.com/crhenr/openbsd-selfhosted 4 | 5 | # Get basic information about the user. 6 | echo -n "What is your domain? " 7 | read domain 8 | 9 | echo -n "cgit username: " 10 | read git_title 11 | 12 | echo -n "Email username: " 13 | read email_user 14 | 15 | # Get passwords. 16 | echo -n "Email password (enter to autogenerate): " 17 | stty -echo 18 | read email_passwd 19 | stty echo 20 | 21 | if [ -z "$email_passwd" ] 22 | then 23 | email_passwd=$(openssl rand -base64 12) 24 | fi 25 | email_user_passwd=$(smtpctl encrypt $email_passwd) 26 | 27 | echo -n "\nPostgresql password (enter to autogenerate): " 28 | stty -echo 29 | read postgres_passwd 30 | stty echo 31 | 32 | if [ -z "$postgres_passwd" ] 33 | then 34 | postgres_passwd=$(openssl rand -base64 12) 35 | fi 36 | 37 | echo -n "\nNextcloud password (enter to autogenerate): " 38 | stty -echo 39 | read nextcloud_passwd 40 | stty echo 41 | 42 | if [ -z "$nextcloud_passwd" ] 43 | then 44 | nextcloud_passwd=$(openssl rand -base64 12) 45 | fi 46 | 47 | git_passwd=$(openssl rand -base64 12) 48 | 49 | # Colors: 50 | red="\033[0;31m" 51 | green="\033[0;32m" 52 | reset="\033[0m" 53 | 54 | # PHP options: 55 | memory_limit=-1 56 | max_input_time=180 57 | upload_max_filesize=512M 58 | post_max_size=32M 59 | 60 | 61 | # Install functions. 62 | run_acme() { 63 | echo "\n${green}[*] Creating acme-client.conf...${reset}" 64 | cat << EOF > /etc/acme-client.conf 65 | authority letsencrypt { 66 | api url "https://acme-v02.api.letsencrypt.org/directory" 67 | account key "/etc/acme/letsencrypt-privkey.pem" 68 | } 69 | 70 | authority letsencrypt-staging { 71 | api url "https://acme-staging-v02.api.letsencrypt.org/directory" 72 | account key "/etc/acme/letsencrypt-staging-privkey.pem" 73 | } 74 | 75 | domain $domain { 76 | alternative names { www.$domain, git.$domain cloud.$domain mail.$domain } 77 | domain key "/etc/ssl/private/$domain.key" 78 | domain certificate "/etc/ssl/$domain.crt" 79 | domain full chain certificate "/etc/ssl/$domain.pem" 80 | sign with letsencrypt 81 | } 82 | EOF 83 | 84 | echo "${green}[*] Creating initial httpd.conf...${reset}" 85 | cat << EOF > /etc/httpd.conf 86 | server "$domain" { 87 | listen on * port 80 88 | 89 | location "/.well-known/acme-challenge/*" { 90 | root "/acme" 91 | request strip 2 92 | } 93 | } 94 | 95 | EOF 96 | 97 | mkdir -p -m 700 /etc/ssl/private 98 | mkdir -p -m 755 /var/www/acme 99 | rcctl -f restart httpd 100 | echo "${green}[*] Generating certificates...${reset}" 101 | acme-client -v $domain 102 | 103 | if [[ $? != 0 ]]; then 104 | echo "${red}[-] acme-client failed! Exiting...${reset}" 105 | exit 1 106 | fi 107 | 108 | ocspcheck -N -o /etc/ssl/$domain.ocsp.pem /etc/ssl/$domain.pem 109 | (crontab -l 2>/dev/null; echo "0 0 * * * acme-client $domain && rcctl reload httpd") | crontab - 110 | (crontab -l 2>/dev/null; echo "0 * * * * ocspcheck -N -o /etc/ssl/$domain.ocsp.pem /etc/ssl/$domain.pem && rcctl reload httpd") | crontab - 111 | } 112 | 113 | install_git() { 114 | echo "${green}[*] Installing and configuring git and cgit...${reset}" 115 | pkg_add git cgit 116 | useradd -b /home -m -s /bin/ksh -p $git_passwd git 117 | 118 | cat << EOF > /var/www/conf/cgitrc 119 | # Enable caching of up to 1000 output entries 120 | cache-size=1000 121 | 122 | # Cache time to live 123 | cache-dynamic-ttl=5 124 | cache-repo-ttl=5 125 | 126 | # Specify the CSS URL 127 | css=/cgit.css 128 | 129 | # Show owner on index page 130 | enable-index-owner=0 131 | 132 | # Allow HTTP transport Git clone 133 | enable-http-clone=0 134 | 135 | # Enable ASCII art commit history graph on the log pages 136 | enable-commit-graph=1 137 | 138 | # Show number of affected files per commit on the log pages 139 | enable-log-file-count=1 140 | 141 | # Sort branches by date 142 | branch-sort=age 143 | 144 | # Add a cgit favicon 145 | favicon=/favicon.ico 146 | 147 | # Enable statistics per week, month and quarter 148 | max-stats=quarter 149 | 150 | # Set the title and heading of the repository index page 151 | root-title=$git_title's repositories 152 | 153 | # Set a subheading for the repository index page 154 | root-desc= 155 | 156 | # Allow download of tar.gz, tar.bz2 and zip-files 157 | snapshots=tar.gz 158 | 159 | ## List of common mimetypes 160 | mimetype.gif=image/gif 161 | mimetype.html=text/html 162 | mimetype.jpg=image/jpeg 163 | mimetype.jpeg=image/jpeg 164 | mimetype.pdf=application/pdf 165 | mimetype.png=image/png 166 | mimetype.svg=image/svg+xml 167 | 168 | ## Search for these files in the root of the default branch of repositories 169 | ## for coming up with the about page: 170 | readme=:README 171 | 172 | # Remove .git suffix 173 | remove-suffix=1 174 | 175 | scan-path=/srv/git 176 | 177 | # Disable adhoc downloads of this repo 178 | repo.snapshots=0 179 | 180 | # Disable line-counts for this repo 181 | repo.enable-log-linecount=0 182 | 183 | # Restrict the max statistics period for this repo 184 | repo.max-stats=month 185 | EOF 186 | 187 | cat <> /etc/httpd.conf 188 | # Git settings 189 | server "git.$domain" { 190 | listen on * tls port 443 191 | 192 | tls { 193 | certificate "/etc/ssl/$domain.pem" 194 | key "/etc/ssl/private/$domain.key" 195 | } 196 | 197 | location "/.well-known/acme-challenge/*" { 198 | root "/acme" 199 | request strip 2 200 | } 201 | 202 | location "/cgit.*" { 203 | root "/cgit" 204 | no fastcgi 205 | } 206 | 207 | root "/cgi-bin/cgit.cgi" 208 | fastcgi socket "/run/slowcgi.sock" 209 | } 210 | 211 | server "git.$domain" { 212 | listen on * port 80 213 | block return 301 "https://git.$domain\$REQUEST_URI" 214 | } 215 | 216 | EOF 217 | 218 | mkdir -p /var/www/srv/git 219 | chown git:git /var/www/srv/git 220 | mkdir -p /var/www/cgit/cache 221 | chown www:www /var/www/cgit/cache 222 | 223 | echo "slowcgi_flags=" >> /etc/rc.conf.local 224 | echo "httpd_flags=" >> /etc/rc.conf.local 225 | 226 | rcctl enable slowcgi httpd 227 | rcctl -f restart slowcgi httpd 228 | } 229 | 230 | install_nextcloud() { 231 | echo "${green}[*] Installing Nextcloud dependencies...${reset}" 232 | pkg_add postgresql-server php-7.3.19 php-curl-7.3.19 php-gd-7.3.19 php-intl-7.3.19 php-pdo_pgsql-7.3.19 php-zip-7.3.19 233 | 234 | cp /etc/php-7.3.sample/* /etc/php-7.3 235 | mkdir -p /var/www/etc/ssl 236 | cp /etc/resolv.conf /var/www/etc/resolv.conf 237 | cp /etc/ssl/cert.pem /var/www/etc/ssl/cert.pem 238 | cp /etc/ssl/openssl.cnf /var/www/etc/ssl/openssl.cnf 239 | chown -R www:www /var/www/etc 240 | 241 | echo "${green}[*] Configuring PostgreSQL...${reset}" 242 | mkdir /var/postgresql/data 243 | chown _postgresql:_postgresql /var/postgresql/data 244 | 245 | echo $postgres_passwd > /tmp/pwfile.txt 246 | su _postgresql -c 'initdb -D /var/postgresql/data -U postgres -A md5 --pwfile=/tmp/pwfile.txt' 247 | rm /tmp/pwfile.txt 248 | 249 | export PGPASSWORD=$postgres_passwd 250 | rcctl start postgresql 251 | su _postgresql -c "psql -d template1 -U postgres -c \"CREATE USER nextcloud WITH PASSWORD '$nextcloud_passwd';\"" 252 | su _postgresql -c "psql -d template1 -U postgres -c \"CREATE DATABASE nextcloud\"" 253 | su _postgresql -c "psql -d template1 -U postgres -c \"GRANT ALL PRIVILEGES ON DATABASE nextcloud to nextcloud;\"" 254 | unset PGPASSWORD 255 | 256 | echo "${green}[*] Downloading Nextcloud...${reset}" 257 | ftp https://download.nextcloud.com/server/releases/nextcloud-18.0.6.tar.bz2 258 | tar xjvf nextcloud-18.0.6.tar.bz2 -C /var/www/ 259 | rm nextcloud-18.0.6.tar.bz2 260 | 261 | echo "${green}[*] Configuring Nextcloud...${reset}" 262 | cat << EOF > /var/www/nextcloud/config/custom.config.php 263 | ((php_sapi_name() == 'cli') ? '/var/www' : '') . '/nextcloud/data', 266 | ); 267 | EOF 268 | 269 | cat << EOF >> /etc/httpd.conf 270 | # Nextcloud settings 271 | server "cloud.$domain" { 272 | listen on * tls port 443 273 | root "/nextcloud" 274 | directory index "index.php" 275 | 276 | tls { 277 | certificate "/etc/ssl/$domain.pem" 278 | key "/etc/ssl/private/$domain.key" 279 | } 280 | 281 | connection max request body 537919488 282 | location "/.well-known/acme-challenge/*" { 283 | root "/acme" 284 | request strip 2 285 | } 286 | 287 | # Deny access to the specified files 288 | location "/db_structure.xml" { block } 289 | location "/.ht*" { block } 290 | location "/README" { block } 291 | location "/data*" { block } 292 | location "/config*" { block } 293 | location "/build*" { block } 294 | location "/tests*" { block } 295 | location "/config*" { block } 296 | location "/lib*" { block } 297 | location "/3rdparty*" { block } 298 | location "/templates*" { block } 299 | location "/data*" { block } 300 | location "/.user*" { block } 301 | location "/autotest*" { block } 302 | location "/occ*" { block } 303 | location "/issue*" { block } 304 | location "/indie*" { block } 305 | location "/db_*" { block } 306 | location "/console*" { block } 307 | 308 | location "/*.php*" { 309 | fastcgi socket "/run/php-fpm.sock" 310 | } 311 | 312 | location "/.well-known/host-meta" { 313 | block return 301 "/public.php?service=host-meta" 314 | } 315 | 316 | location "/.well-known/host-meta.json" { 317 | block return 301 "/public.php?service=host-meta-json" 318 | } 319 | 320 | location "/.well-known/webfinger" { 321 | block return 301 "/public.php?service=webfinger" 322 | } 323 | 324 | location "/.well-known/carddav" { 325 | block return 301 "/remote.php/dav/" 326 | } 327 | 328 | location "/.well-known/caldav" { 329 | block return 301 "/remote.php/dav/" 330 | } 331 | } 332 | 333 | server "cloud.$domain" { 334 | listen on * port 80 335 | block return 301 "https://cloud.$domain\$REQUEST_URI" 336 | } 337 | 338 | EOF 339 | 340 | sed -i "s/\(memory_limit *=*\).*/\1 $memory_limit/" /etc/php-7.3.ini 341 | sed -i "s/\(max_input_time *=*\).*/\1 $max_input_time/" /etc/php-7.3.ini 342 | sed -i "s/\(upload_max_filesize *=*\).*/\1 $upload_max_filesize/" /etc/php-7.3.ini 343 | sed -i "s/\(post_max_size *=*\).*/\1 $post_max_size/" /etc/php-7.3.ini 344 | 345 | sed -i "/opcache.enable=1/s/^;//" /etc/php-7.3.ini 346 | sed -i "/opcache.memory_consumption=128/s/^;//" /etc/php-7.3.ini 347 | sed -i "/opcache.interned_strings_buffer=8/s/^;//" /etc/php-7.3.ini 348 | sed -i "/opcache.max_accelerated_files=10000/s/^;//" /etc/php-7.3.ini 349 | sed -i "/opcache.save_comments=1/s/^;//" /etc/php-7.3.ini 350 | 351 | sed -i "s/;opcache.enable_cli=0/opcache.enable_cli=1/" /etc/php-7.3.ini 352 | sed -i "s/;opcache.revalidate_freq=2/opcache.revalidate_freq=1/" /etc/php-7.3.ini 353 | 354 | chown -R www:www /var/www/nextcloud 355 | rcctl enable postgresql php73_fpm httpd 356 | rcctl restart postgresql php73_fpm httpd 357 | } 358 | 359 | configure_email() { 360 | echo "${green}[*] Installing email dependencies...${reset}" 361 | pkg_add opensmtpd-extras opensmtpd-filter-rspamd dovecot dovecot-pigeonhole rspamd redis 362 | 363 | echo "${green}[*] Configuring OpenSMTPD...${reset}" 364 | cat << EOF > /etc/mail/smtpd.conf 365 | pki "mail.$domain" cert "/etc/ssl/$domain.crt" 366 | pki "mail.$domain" key "/etc/ssl/private/$domain.key" 367 | 368 | table aliases file:/etc/mail/aliases 369 | table credentials passwd:/etc/mail/credentials 370 | table virtuals file:/etc/mail/virtuals 371 | 372 | filter "rspamd" proc-exec "/usr/local/libexec/smtpd/filter-rspamd" 373 | 374 | listen on all tls pki "mail.$domain" hostname "mail.$domain" filter "rspamd" 375 | listen on egress port submission tls-require pki "mail.$domain" hostname "mail.$domain" auth filter "rspamd" 376 | 377 | action "local_mail" mbox alias 378 | action "domain_mail" maildir "/var/vmail/$domain/%{dest.user}" virtual 379 | action "outbound" relay 380 | 381 | match from any for domain "$domain" action "domain_mail" 382 | match from local for local action "local_mail" 383 | 384 | match from local for any action "outbound" 385 | match auth from any for any action "outbound" 386 | EOF 387 | 388 | printf "$email_user@$domain:$email_user_passwd:vmail:2000:2000:/var/vmail/$domain/$email_user::userdb_mail=maildir:/var/vmail/$domain/$email_user\n" > /etc/mail/credentials 389 | 390 | echo "${green}[*] Configuring Dovecot...${reset}" 391 | chmod 0440 /etc/mail/credentials 392 | useradd -c "Virtual Mail Account" -d /var/vmail -s /sbin/nologin -u 2000 -g =uid -L staff vmail 393 | mkdir /var/vmail 394 | chown _smtpd:_dovecot /etc/mail/credentials 395 | chown vmail:vmail /var/vmail 396 | 397 | cat << EOF > /etc/mail/virtuals 398 | abuse@$domain: $email_user@$domain 399 | hostmaster@$domain: $email_user@$domain 400 | postmaster@$domain: $email_user@$domain 401 | webmaster@$domain: $email_user@$domain 402 | $email_user@$domain: vmail 403 | EOF 404 | 405 | cat << EOF >> /etc/login.conf 406 | 407 | dovecot:\\ 408 | :openfiles-cur=1024:\\ 409 | :openfiles-max=2048:\\ 410 | :tc=daemon: 411 | EOF 412 | 413 | cat << EOF > /etc/dovecot/local.conf 414 | auth_mechanisms = plain 415 | first_valid_uid = 2000 416 | first_valid_gid = 2000 417 | mail_location = maildir:/var/vmail/%d/%n 418 | mail_plugin_dir = /usr/local/lib/dovecot 419 | managesieve_notify_capability = mailto 420 | managesieve_sieve_capability = fileinto reject envelope encoded-character vacation subaddress comparator-i;ascii-numeric relational regex imap4flags copy include variables body enotify environment mailbox date index ihave duplicate mime foreverypart extracttext imapsieve vnd.dovecot.imapsieve 421 | mbox_write_locks = fcntl 422 | mmap_disable = yes 423 | namespace inbox { 424 | inbox = yes 425 | location = 426 | mailbox Archive { 427 | auto = subscribe 428 | special_use = \\Archive 429 | } 430 | mailbox Drafts { 431 | auto = subscribe 432 | special_use = \\Drafts 433 | } 434 | mailbox Junk { 435 | auto = subscribe 436 | special_use = \\Junk 437 | } 438 | mailbox Sent { 439 | auto = subscribe 440 | special_use = \\Sent 441 | } 442 | mailbox Trash { 443 | auto = subscribe 444 | special_use = \\Trash 445 | } 446 | prefix = 447 | } 448 | passdb { 449 | args = scheme=CRYPT username_format=%u /etc/mail/credentials 450 | driver = passwd-file 451 | name = 452 | } 453 | plugin { 454 | imapsieve_mailbox1_before = file:/usr/local/lib/dovecot/sieve/report-spam.sieve 455 | imapsieve_mailbox1_causes = COPY 456 | imapsieve_mailbox1_name = Junk 457 | imapsieve_mailbox2_before = file:/usr/local/lib/dovecot/sieve/report-ham.sieve 458 | imapsieve_mailbox2_causes = COPY 459 | imapsieve_mailbox2_from = Junk 460 | imapsieve_mailbox2_name = * 461 | sieve = file:~/sieve;active=~/.dovecot.sieve 462 | sieve_global_extensions = +vnd.dovecot.pipe +vnd.dovecot.environment 463 | sieve_pipe_bin_dir = /usr/local/lib/dovecot/sieve 464 | sieve_plugins = sieve_imapsieve sieve_extprograms 465 | } 466 | protocols = imap sieve 467 | service imap-login { 468 | inet_listener imaps { 469 | port = 0 470 | } 471 | } 472 | service managesieve-login { 473 | inet_listener sieve { 474 | port = 4190 475 | } 476 | inet_listener sieve_deprecated { 477 | port = 2000 478 | } 479 | } 480 | ssl_cert = /usr/local/lib/dovecot/sieve/report-ham.sieve 493 | require ["vnd.dovecot.pipe", "copy", "imapsieve", "environment", "variables"]; 494 | 495 | if environment :matches "imap.mailbox" "*" { 496 | set "mailbox" "\${1}"; 497 | } 498 | 499 | if string "\${mailbox}" "Trash" { 500 | stop; 501 | } 502 | 503 | if environment :matches "imap.user" "*" { 504 | set "username" "\${1}"; 505 | } 506 | 507 | pipe :copy "sa-learn-ham.sh" [ "\${username}" ]; 508 | EOF 509 | 510 | cat << EOF > /usr/local/lib/dovecot/sieve/report-spam.sieve 511 | require ["vnd.dovecot.pipe", "copy", "imapsieve", "environment", "variables"]; 512 | 513 | if environment :matches "imap.user" "*" { 514 | set "username" "\${1}"; 515 | } 516 | 517 | pipe :copy "sa-learn-spam.sh" [ "\${username}" ]; 518 | EOF 519 | 520 | sed -i "/ssl_cert*/s/^/#/" /etc/dovecot/conf.d/10-ssl.conf 521 | sed -i "/ssl_key*/s/^/#/" /etc/dovecot/conf.d/10-ssl.conf 522 | 523 | sievec /usr/local/lib/dovecot/sieve/report-ham.sieve 524 | sievec /usr/local/lib/dovecot/sieve/report-spam.sieve 525 | 526 | cat << EOF > /usr/local/lib/dovecot/sieve/sa-learn-ham.sh 527 | #!/bin/sh 528 | exec /usr/local/bin/rspamc -d "\${1}" learn_ham 529 | EOF 530 | 531 | cat << EOF > /usr/local/lib/dovecot/sieve/sa-learn-spam.sh 532 | #!/bin/sh 533 | exec /usr/local/bin/rspamc -d "\${1}" learn_spam 534 | EOF 535 | 536 | chmod 0755 /usr/local/lib/dovecot/sieve/sa-learn-ham.sh 537 | chmod 0755 /usr/local/lib/dovecot/sieve/sa-learn-spam.sh 538 | 539 | mkdir /etc/mail/dkim && cd /etc/mail/dkim 540 | openssl genrsa -out private.key 1024 541 | openssl rsa -in private.key -pubout -out public.key 542 | chmod 0440 private.key 543 | chown root:_rspamd private.key && cd 544 | 545 | cat << EOF > /etc/rspamd/local.d/dkim_signing.conf 546 | domain { 547 | $domain { 548 | path = "/etc/mail/dkim/$domain.key"; 549 | selector = "changethis"; 550 | } 551 | } 552 | EOF 553 | 554 | echo "\n${green}[*] Installing RainLoop dependencies...${reset}" 555 | pkg_add php-7.3.19 php-curl-7.3.19 php-pdo_sqlite-7.3.19 php-zip-7.3.19 pecl73-mcrypt unzip-6.0p13 556 | 557 | echo "\n${green}[*] Downloading RainLoop...${reset}" 558 | ftp https://www.rainloop.net/repository/webmail/rainloop-latest.zip 559 | unzip rainloop-latest.zip -d /var/www/htdocs/rainloop 560 | rm rainloop-latest.zip 561 | 562 | cat << EOF >> /etc/httpd.conf 563 | # RainLoop settings 564 | server "mail.$domain" { 565 | listen on * tls port 443 566 | root "/htdocs/rainloop" 567 | directory index "index.php" 568 | 569 | tcp { nodelay, backlog 10 } 570 | 571 | tls { 572 | certificate "/etc/ssl/$domain.crt" 573 | key "/etc/ssl/private/$domain.key" 574 | } 575 | 576 | hsts { 577 | max-age 31556952 578 | preload 579 | } 580 | 581 | connection max request body 26214400 582 | 583 | location "/data*" { 584 | block return 403 585 | } 586 | 587 | location "*.php*" { 588 | fastcgi socket "/run/php-fpm.sock" 589 | } 590 | } 591 | 592 | server "mail.$domain" { 593 | listen on * port 80 594 | block return 301 "https://mail.$domain\$REQUEST_URI" 595 | } 596 | 597 | EOF 598 | 599 | mkdir /var/www/etc 600 | cp /etc/resolv.conf /var/www/etc/resolv.conf 601 | chown -R www:www /var/www/htdocs/rainloop 602 | rcctl enable smtpd dovecot redis rspamd httpd 603 | rcctl restart smtpd dovecot redis rspamd httpd 604 | } 605 | 606 | run_acme 607 | install_git 608 | install_nextcloud 609 | configure_email 610 | 611 | echo " 612 | 613 | *************************************************************************************** 614 | --> Important information 615 | 616 | ${green}[*]${reset} Domain: ${green}$domain${reset} 617 | ${green}[*]${reset} Email address: $email_user@$domain 618 | Password: ${red}$email_passwd${reset} 619 | 620 | ${green}[*]${reset} Passwords: 621 | - Git: ${red}$git_passwd${reset} 622 | - Postgres: ${red}$postgres_passwd${reset} 623 | - Nextcloud database: ${red}$nextcloud_passwd${reset} 624 | 625 | ${green}[*]${reset} Next steps: 626 | 1) Configure Nextcloud at cloud.$domain 627 | - Database user: nextcloud 628 | - Database password: $nextcloud_passwd 629 | - Database name: nextcloud 630 | 2) Change the RainLoop admin password at mail.$domain/?admin (default is 12345) 631 | 3) Add your public SSH key at /home/git/.ssh/authorized_keys 632 | 4) Create the DKIM and DMARC records: 633 | - Check the /etc/rspamd/local.d/dkim_signing.conf and /etc/mail/dkim/public.key 634 | files and follow the instructions given by your registrar. 635 | 636 | *************************************************************************************** 637 | 638 | OpenBSD self-hosted script. 639 | Made with <3 by @crhenr 640 | https://github.com/crhenr/openbsd-selfhosted 641 | " 642 | --------------------------------------------------------------------------------