From ef143eb39a7cc088ff6f70640e6084c5ff689baa Mon Sep 17 00:00:00 2001 From: Peter Sanchez Date: Wed, 24 Jul 2024 08:35:13 -0600 Subject: [PATCH] Adding self-host document --- build.md | 2 +- self-host.md | 331 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 332 insertions(+), 1 deletion(-) create mode 100644 self-host.md diff --git a/build.md b/build.md index 163a521..bcae00b 100644 --- a/build.md +++ b/build.md @@ -7,7 +7,7 @@ description: 'How to build the LinkTaco (links) project' Requirements: -- Go 1.20 +- Go 1.21 - GNU Make - PostgreSQL (any modern version should work) diff --git a/self-host.md b/self-host.md new file mode 100644 index 0000000..1cf98ec --- /dev/null +++ b/self-host.md @@ -0,0 +1,331 @@ +--- +title: 'How to self-host the LinkTaco (links) project' +description: 'How to self-host the LinkTaco (links) project' +--- + +# How to self-host the LinkTaco (links) project + +We don't have a Docker file and probably will never provide one. If you want to +create one, please do and share it with the community. We're happy to link to +it. + +To self host we're going to assume that you have access to a server and at +least 4 IP addresses you can use. Why 4 IP's? Because since each service can +run for user's custom domains they each need to have their own IP address. The +4th IP is for the GraphQL server. This can share an IP with other hosts on the +same system if there are others. + +Requirements: + +- Root access to Server / VPS running Unix like operating system where you can + install the application. Linux or BSD is fine. +- 4 IP addresses +- Go 1.21+ +- PostgreSQL (any modern-ish version should work) installed and running. +- supervisord installed +- nginx installed +- Caddy installed +- Some basic system admin skills to get through this process. + +Notes: + +- The example commands in this document are based on the FreeBSD operating + system (OS), so if you're installing to Linux or some other Unix like OS, + please refer to your OS documentation for the equivalent commands or file + locations. +- This is just one way to deploy this software. We are not saying this is the + best way. Please review your own network, requirements, environments, etc. + to determine your best deployment strategy. + +## Initial setup + +We will create a `links` user and then build and run this software as this +user. + +``` +root ~ # adduser +Username: links +Full name: LinkTaco.com +Uid (Leave empty for default): +Login group [links]: +Login group is links. Invite links into other groups? []: +Login class [default]: +Shell (sh csh tcsh bash rbash git-shell nologin) [sh]: bash +Home directory [/home/links]: +Home directory permissions (Leave empty for default): +Use password-based authentication? [yes]: +Use an empty password? (yes/no) [no]: +Use a random password? (yes/no) [no]: yes +Lock out the account after creation? [no]: +Username : links +Password : +Full Name : LinkTaco.com +Uid : 1004 +Class : +Groups : links +Home : /home/links +Home Mode : +Shell : /usr/local/bin/bash +Locked : no +OK? (yes/no): yes +adduser: INFO: Successfully added (links) to the user database. +adduser: INFO: Password for (links) is: XXXXXXXXXXXX +Add another user? (yes/no): no +Goodbye! +root ~ # +``` + +Now let's create a new PostgreSQL user and database for the application. + +``` +root ~ # createuser -U postgres -W links +Password: +root ~ # createdb -U postgres -O links links +root ~ # +``` + +Great! Now we've got a new user. Let's go into this users home directory and +clone the repo. + +``` +root ~ # su - links +links ~ $ git clone https://git.code.netlandish.com/~netlandish/links +links ~ $ cd links +links ~/links $ make all links-admin links-domains +``` + +We won't go into the build steps here. Please see the [Build +documentation](build.md) for this step. + +## nginx + +We'll first setup the nginx configuration because it's pretty straight forward +and most people are familiar with it's syntax. + +In this case, we have the following in our `/usr/local/etc/nginx/nginx.conf': + +``` +http { + ... + ... + include /usr/local/etc/nginx/Includes/*.conf; +} +``` + +This allows us to place `links-api.conf` in the `/usr/local/etc/nginx/Includes` +directory (create it if it doesn't exist) and we can put the api service config +in this file. + +Now create the `/usr/local/etc/nginx/Includes/links-api.conf` file and put the +following (obviously, change the relevant pieces): + +``` + server { + listen 123.456.789.100:443 ssl http2; + server_name api.YOURDOMAIN.com; + + ssl_certificate /etc/letsencrypt/live/api.YOURDOMAIN.com/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/api.YOURDOMAIN.com/privkey.pem; + ssl_protocols TLSv1.2; # don't use SSLv3 ref: POODLE + ssl_ciphers "ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECD +HE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA- +AES256-SHA384"; + ssl_ecdh_curve X25519:prime256v1:secp521r1:secp384r1; + + # openssl dhparam -dsaparam -out /usr/local/etc/nginx/ssl/dhparams_4096.pem 4096 + ssl_dhparam /usr/local/etc/nginx/ssl/dhparams_4096.pem; + + ssl_prefer_server_ciphers on; + + gzip off; + + add_header Strict-Transport-Security "max-age=63072000; includeSubdomains" always; + #add_header Content-Security-Policy "default-src 'none'; script-src 'self'; connect-src 'self'; img-src 'self'; style-src 'self';" always; + #add_header Referrer-Policy "no-referrer"; + add_header X-Frame-Options "SAMEORIGIN" always; + add_header X-XSS-Protection "1; mode=block" always; + add_header X-Content-Type-Options "nosniff" always; + add_header Feature-Policy "geolocation none; midi none; notifications none; push none; sync-xhr none; microphone none; camera none; magnetometer none; gyroscope none; speaker none; vibrate none; fullscreen self; payment none; usb none;"; + + add_header Allow "GET, POST, HEAD" always; + if ($request_method !~ ^(GET|POST|HEAD)$) { + return 405; + } + + location / { + # Run through api daemon, Change port to whatever you have the + # api service configured to run on + proxy_pass http://127.0.0.1:5001; + 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; + } + } +``` + +Note: The nginx config above assumes the existence of a LetsEncrypt SSL cert +for your api domain. Please make sure the certificate exists at the path you +use in the config snippet above. Obviously you can change it to meet whatever +path you have on your system. + +## Caddy + +While links software itself supports auto TLS generation in this case we will +use Caddy to be the front for each of the other 3 services. So let's edit +the `/usr/local/etc/caddy/Caddyfile`: + +``` +{ + # This IP:Port should be where the "links-domains" service is configured to run + # locally. Generally this is localhost but your situation will vary + on_demand_tls { + ask http://localhost:5004/_check/domain + } +} + +# links service +https:// { + bind 123.456.789.101 + tls { + on_demand + } + reverse_proxy localhost:5000 + log { + output file /var/log/caddy/links-access.log + format console + } +} + +# short service +https:// { + bind 123.456.789.102 + tls { + on_demand + } + reverse_proxy localhost:5002 + log { + output file /var/log/caddy/short-access.log + format console + } +} + +# list service +https:// { + bind 123.456.789.103 + tls { + on_demand + } + reverse_proxy localhost:5003 + log { + output file /var/log/caddy/list-access.log + format console + } +} +``` + +## supervisord + +Finally let's setup the supervisord services. Edit your +`/usr/local/etc/supervisord.conf` file and place the following below in the +`[include]` section of the file: + +``` +files = supervisord/*.conf +``` + +If the `/usr/local/etc/supervisord` directory does not yet exist then create +it. Now let's create the `/usr/local/etc/supervisord/links.conf` file and add +the following: + +``` +[program:links] +command = /home/links/links/links +directory = /home/links/links +user = links +priority = 1 +stopwaitsecs = 60 +stopsignal = QUIT +stdout_logfile = /var/log/links.log +stderr_logfile = /var/log/links.log + +[program:links-api] +command = /home/links/links/links-api +directory = /home/links/links +user = links +priority = 1 +stopwaitsecs = 60 +stopsignal = QUIT +stdout_logfile = /var/log/links_api.log +stderr_logfile = /var/log/links_api.log + +[program:links-short] +command = /home/links/links/links-short +directory = /home/links/links +user = links +priority = 1 +stopwaitsecs = 60 +stopsignal = QUIT +stdout_logfile = /var/log/links_short.log +stderr_logfile = /var/log/links_short.log + +[program:links-list] +command = /home/links/links/links-list +directory = /home/links/links +user = links +priority = 1 +stopwaitsecs = 60 +stopsignal = QUIT +stdout_logfile = /var/log/links_list.log +stderr_logfile = /var/log/links_list.log + +[program:links-domains] +command = /home/links/links/links-domains +directory = /home/links/links +user = links +priority = 1 +stopwaitsecs = 60 +stopsignal = QUIT +stdout_logfile = /var/log/links_domains.log +stderr_logfile = /var/log/links_domains.log +``` + +## Starting services + +Now we should be ready to restart (or start) all the services. You can run the +following: + +``` +root ~ # service nginx restart +nginx: the configuration file /usr/local/etc/nginx/nginx.conf syntax is ok +nginx: configuration file /usr/local/etc/nginx/nginx.conf test is successful +Stopping nginx. +nginx: the configuration file /usr/local/etc/nginx/nginx.conf syntax is ok +nginx: configuration file /usr/local/etc/nginx/nginx.conf test is successful +Starting nginx. +root ~ # service caddy restart +Stopping caddy... done +Starting caddy... done +Log: /var/log/caddy/caddy.log +root ~ # supervisorctl udpate +adding new process: links +links: started +adding new process: links-api +links-api: started +adding new process: links-short +links-short: started +adding new process: links-list +links-list: started +adding new process: links-domains +links-domains: started +root ~ # +``` + +Note: The supervisorctl `update` command is just to let the daemon know there +are new commands configured. In the future, to restart a running supervisord +service, you would use the `restart` command. For example: `supervisorctl +restart links-api`. See the supervisord docs for more info. + +## Done. + +You should be all setup and running now. Try loading your root domain in a +browser. It may have a slight delay as the SSL certificate is generated -- 2.45.2