Skip to content

Instantly share code, notes, and snippets.

@mb-0
Last active December 12, 2021 09:09
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save mb-0/606840ace38a056109f526dd0e5c6762 to your computer and use it in GitHub Desktop.
Save mb-0/606840ace38a056109f526dd0e5c6762 to your computer and use it in GitHub Desktop.
Gogs on DDWRT / ASUS RT-AC88U (armv7)

This ended up to be an intermediate guide that requires you to understand linux, the differences between embedded and server (or desktop) systems, some of how router firmwares work. This can surely consume up to a few hours of your day.

It is important that you understand your architecture. The AC88U Router I used in this example is an armv7l.

uname -m
armv7l

Unless you use an armv7, this guide may not be 100% applicable to you, so expect changes for amd64, armv5, armv6. For other architectures such as mips, you may need to build gogs, or run into not being supported.

Read this for GoLang: https://github.com/golang/go/wiki/MinimumRequirements Gogs tends to run on all architectures Go supports.

Note: this may in large part be applicable to gitea (https://docs.gitea.io/en-us/) too.

Prerequisites

Prepare DDWRT

You should be all set when you are reading this, but to recap, all we need here is a working DDWRT installation, a disk (yeah a spinning old-school HDD. I would advise against using NAND/flash memory/SDCard/Pendrive) in and entware fully functioning.

Install GoLang

There are multiple packages available in entware, but note that your router will come with emulated FPU/HW FPU unavailable. Package you need therefore is not the normal go, but go_nohf (No Hard Float) instead.

opkg install go_nohf
Installing go_nohf (1.12.5-1) to root...
Downloading http://bin.entware.net/armv7sf-k3.2/go_nohf_1.12.5-1_armv7-3.2.ipk
Configuring go_nohf.
Please add /opt/bin/go/bin to your PATH
Please set GOROOT=/opt/bin/go environment variable to use GO compiler

After this, add the set GOROOT, GOPATH and add these to your path.

if [ -d /opt/bin/go ]; then export GOROOT=/opt/bin/go; export GOPATH=$HOME/srcs/go; export PATH=$PATH:$GOROOT/bin; fi

Add this to be configured at boot time for your user, e.g. your /tmp/root/.profile should have this. The easiest way to achieve this is to add the above line to your startup commands:

echo "if [ -d /opt/bin/go ]; then export GOROOT=/opt/bin/go; export GOPATH=/opt/home/go; export PATH=$PATH:$GOROOT/bin; fi" >> /tmp/root/.profile

Don't forget to:

mkdir -p /opt/home/go

Now the reason we do this is to avoid having this in /tmp/root as that can vanish easily (like after every router power cut/reboot). At this point, go should run just fine, give it a go:

> go version
go version go1.12.5 linux/arm

Fix your CAs

For HTTPS magic you will need a few packages to establish your cert store.

opkg install ca-bundle ca-certificates

Listing that /opt/etc/ssl/certs directory should contain a bunch of certificates at this point. Now export that SSL dir, as we're gonna need that in future:

export SSL_CERT_DIR='/opt/etc/ssl/certs'

PS: you can add your own custom RootCAs here too - but that's another trick. So you may want to add this export for your user at boot time: add to startup commands:

echo "export SSL_CERT_DIR='/opt/etc/ssl/certs'" >> /tmp/root/.profile

Some more SSL magic here: https://github.com/Neilpang/acme.sh/wiki/How-to-run-on-DD-WRT-with-lighttpd (this actually runs the ddwrt UI for you under your own RootCAs)

Add git

Use the git-http package as that allows you to fetch from http and https too. And test.

opkg install git-http
git --version
git version 2.21.0

Get some SQL

I'll be using MariaDB (MySQL) for this installation, but note that gogs supports posgresql and sqlite too. For MySQL to work though, you will need to get a working swap in place. Note though that it is a very bad idea to install SWAP on NAND (remember that I recommended using a HDD above? here it comes again: use a HDD).

Install these:

opkg install mariadb-client mariadb-server

Check if you have mkswap and swapon utils available on your ddwrt edition:

which mkswap
/sbin/mkswap

which swapon
/sbin/swapon

In case you don't see these packages install swap-utils with opkg.

Creating a 1GB swap on your disk would be:

dd if=/dev/zero of=/tmp/mnt/sda1/swap bs=1024k count=1000
mkswap /tmp/mnt/sda1/swap

Now check free:

free
              total        used        free      shared  buff/cache   available
Mem:         514568       91836       72908           0      349824      307376

Turn on your swap and check free again:

swapon -p1 /tmp/mnt/sda1/swap
free
              total        used        free      shared  buff/cache   available
Mem:         514568       91836       72908           0      349824      307376
Swap:       1023996         176     1023820

And you may want to add this to a attach at boot time (e.g. startup commands):

# add this to startup commands:
if [ -f '/tmp/mnt/sda1/swap' ]; then swapon -p1 /tmp/mnt/sda1/swap; fi

More swap magic here: https://wiki.dd-wrt.com/wiki/index.php/Linux_SWAP

Now onto the MySQL configs - you'll need /opt/etc/mysql/conf.d/50-server.cnf first. Modify the datadir and tmpdir, make sure you use a HDD. Also, this router in particular doesn't support Native AIO, and we don't need the server to keep reminding us, let's turn that off too.

# /opt/etc/mysql/conf.d/50-server.cnf
datadir         = /tmp/mnt/sda1/mysql
tmpdir          = /tmp/mnt/sda1/tmp/mysql
innodb_use_native_aio = 0

Now get data init:

mysql_install_db --force

Some problems can be resolved by ln -s /opt/etc/mysql/my.cnf ~/.my.cnf (https://forum.dd-wrt.com/phpBB2/viewtopic.php?p=931282) This should run without any problems now, and mysql should run at the end of the process. Do the usual magic as you would do with mysql elsewhere: set a root pass, configure to match memory limits, etc.

Get GOGS

From source

Now I haven't been able to find a gogs build that was working fine on ddwrt (binaries are built on Pi for armv7 I think, and libs are diff / elsewhere); so I ended up building from source.

Note: given the alternate caching options, it can be OK to keep GOGS running from NAND, and have the data piece sitting on the HDD.

go get -u -tags "cert" github.com/gogs/gogs

Now you can customize the tags, above is using "cert" only, but you can go with -tags "cert sqlite pam" whatever you need.

At present, the following tags are available:

  • sqlite: SQLite3 database support
  • pam: PAM authentication support
  • cert: Generate self-signed certificates support
  • miniwinsvc: Builtin windows service support (or you can use NSSM to create a service)

This may change tho, so keep an eye out to: https://golang.org/pkg/go/build/#hdr-Build_Constraints

Should you have built gogs previously, you may notice the /gogs/gogs instead of /gogits/gogs. That is intentional, repo name has changed (and thus many of the guides out there are out of date currently; like this will be in a years time lol).

So this may take about 2-3 minutes to complete.

Let's build:

cd $GOPATH/src/github.com/gogs/gogs
go build -tags "cert"

Building with cert takes about a minute on this router. You can tell this was successful when you have a gogs binary available in the same directory, and it actually returns some info:

$GOPATH/src/github.com/gogs/gogs/gogs --help
NAME:
   Gogs - A painless self-hosted Git service

USAGE:
   gogs [global options] command [command options] [arguments...]

VERSION:
   0.11.91.0811

COMMANDS:
     web      Start web server
     serv     This command should only be called by SSH shell
     hook     Delegate commands to corresponding Git hooks
     cert     Generate self-signed certificate
     admin    Perform admin operations on command line
     import   Import portable data as local Gogs data
     backup   Backup files and database
     restore  Restore files and database from backup
     help, h  Shows a list of commands or help for one command

GLOBAL OPTIONS:
   --help, -h     show help
   --version, -v  print the version

Using binaries

This page will have the latest: https://gogs.io/docs/installation/install_from_binary. Grab that tar.gz and tar -zxvf it into $GOPATH/srcs/go/github.com/gogits/, so you end up with a $GOPATH/src/github.com/gogs/gogs directory with about 47MB of content.

Try running the $GOPATH/src/github.com/gogs/gogs/gogs binary with --help to see that it's working fine.

Conf GOGS

Setup the DB

Let's create a database first and grant privs. Note that I'm using zpz2ecPn3gxPuxhkPxEdppGJCkbvGE7g as the user and ZvDTdomLrpMk77tZe9quZKcNxg9bem6PRcpQDNPA93W95ahncAq5hg5qYy5GLkKy as the password in this example - you may want to change those.

mysql -u root -p
Enter password:
Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MariaDB connection id is 8
Server version: 10.2.24-MariaDB Source distribution

Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

MariaDB [(none)]> CREATE DATABASE IF NOT EXISTS gogs_0_11_91;
Query OK, 1 row affected (0.39 sec)

MariaDB [(none)]> GRANT ALL ON gogs_0_11_91.* TO zpz2ecPn3gxPuxhkPxEdppGJCkbvGE7g@localhost IDENTIFIED BY 'ZvDTdomLrpMk77tZe9quZKcNxg9bem6PRcpQDNPA93W95ahncAq5hg5qYy5GLkKy';

Query OK, 0 rows affected (0.49 sec)

MariaDB [(none)]> quit;
Bye

GOGS Config

You'll find the default configuration saved in $GOPATH/src/github.com/gogs/gogs/conf/app.ini It's recommended to leave that untouched and come up with a custom config:

cd $GOPATH/src/github.com/gogs/gogs
mkdir -p custom/conf
cp conf/app.ini custom/conf/app.ini

Decide where you repos are going to be. I placed them on HDD, but a low-fequency git is also OK on NAND (the DB and caching will take the heavy load here). I ended up with something like /tmp/mnt/sda1/data.gogs

And now open it in your favorite editor, and make several changes. Look into these for now:

  • APP_NAME whatever domain you host this on - should make sense though as certs would need to match this in their CN.
  • RUN_USER given a few quirks, you are only allowed to run this as root (see securing your install section in case you're not happy with this).
  • SSH_PORT defaults to 22, you may want to change this, or the one you set for ddwrt.
  • DATA is your app data (e.g. attachments and stuff will end up here, can consume space if you let it down below), I've set it to be in my gogs data dir on the HDD /tmp/mnt/sda1/data.gogs/data
  • ROOT is your repo root, in my case it will be /tmp/mnt/sda1/data.gogs/repos
  • DB_TYPE = mysql (unless you are using something else)
  • HOST = 127.0.0.1:3306 (or incase you're using socket: /var/run/mysqld/mysqld.sock or wherever your socket file is at)
  • NAME = gogs_0_11_91 (or whatever else you set)
  • USER = zpz2ecPn3gxPuxhkPxEdppGJCkbvGE7g or whatever you set this to
  • PASSWD = ZvDTdomLrpMk77tZe9quZKcNxg9bem6PRcpQDNPA93W95ahncAq5hg5qYy5GLkKy or whatever you set this to
  • SECRET_KEY = yes, change this to something bad like 9pGojnQZbqRhzYUKB2yg74qi5SrVyn3fWTQKrYKnqS5dGyjHA9PNU8WjypWj4gUR
  • MODE under log is one to look at: keep it as "console", or create a logging directory on your HDD (this can be intensive wear for NAND).

This is a dry run, so we don't worry about certificates for now :)

More on configs on gogs pages: https://gogs.io/docs/installation/configuration_and_run The most interesting piece will be https://gogs.io/docs/advanced/configuration_cheat_sheet

Run GOGS

Test your drive like:

cd $GOPATH/src/github.com/gogs/gogs
./gogs web

Opening your http://gogsdomain:3000 should show you gogs for advanced configuration.

Running gogs as a service

Securing your GOGS Installation

There is a few things around this.

Running with a dedicated user (shadow)

So you may not keep this running as "root", but at the same time, the users your router provides (like "nobody") may be insufficient to proceed with.

Installing shadown on a ddwrt router can be difficult, but here goes my logic:

So think this: you can get gogs to run as "nobody" and that is fine. On the other hand, when you want to use a non-root SSH to push and pull and fetch and what not, you may need something else. Like shadow, the first step towards real unix users and groups. Stop and make that decision here.

WARNING WARNING WARNING It is easy to lock yourself out this way. You may want to create an nvram backup and be prepared to reset your router to factory default should it come to that.

Packages needed:

opkg install shadow-common shadow-groups shadow-nologin
Installing shadow-common (4.6-1) to root...
Downloading http://bin.entware.net/armv7sf-k3.2/shadow-common_4.6-1_armv7-3.2.ipk
Installing shadow-groups (4.6-1) to root...
Downloading http://bin.entware.net/armv7sf-k3.2/shadow-groups_4.6-1_armv7-3.2.ipk
Installing shadow-nologin (4.6-1) to root...
Downloading http://bin.entware.net/armv7sf-k3.2/shadow-nologin_4.6-1_armv7-3.2.ipk
Configuring shadow-common.
Configuring shadow-nologin.
Configuring shadow-groups.

This gets you /opt/etc/passwd and /opt/etc/group.

A bunch you may need until you're setup and ready to go (these can be removed after settings & config and done, and used only when they're really required).

 opkg install shadow-groupadd shadow-groupdel shadow-useradd shadow-userdel
Installing shadow-groupadd (4.6-1) to root...
Downloading http://bin.entware.net/armv7sf-k3.2/shadow-groupadd_4.6-1_armv7-3.2.ipk
Installing shadow-groupdel (4.6-1) to root...
Downloading http://bin.entware.net/armv7sf-k3.2/shadow-groupdel_4.6-1_armv7-3.2.ipk
Installing shadow-useradd (4.6-1) to root...
Downloading http://bin.entware.net/armv7sf-k3.2/shadow-useradd_4.6-1_armv7-3.2.ipk
Installing shadow-userdel (4.6-1) to root...
Downloading http://bin.entware.net/armv7sf-k3.2/shadow-userdel_4.6-1_armv7-3.2.ipk
Configuring shadow-useradd.
Configuring shadow-groupadd.
Configuring shadow-userdel.
Configuring shadow-groupdel.

After this initial setup, we end up with /opt/etc/passwd.1 and /opt/etc/group.1 as backups, and symlinks pointing from /opt/etc to /etc:

/opt/etc/passwd -> /etc/passwd
/opt/etc/group -> /etc/group

For our purpose, we need to turn this around, so go ahead and remove those symlinks and copy them over physically:

rm /opt/etc/passwd
cp /etc/passwd /opt/etc
rm /opt/etc/group
cp /etc/group /opt/etc

Now let's make sure we have the users and groups defaulted to what ddwrt has. Unless we bindmount these to /etc/passwd and /etc/group, they are not much use, but note: we need the current users (and these may change depending on ddwrt build, or even after an update should that decision be made).

To migitage this risk, I ended up with a small script called shadow-update.sh that merges passwd and group contents from /etc to /opt/etc, and bindmounts it back to /etc, so we can default to that.

Test this separately without adding to boot first (that way you can recover easily with a reboot).

#!/bin/bash

###
### update users from /etc/passwd to /etc/opt/passwd so we follow system wide user / pwd changes.
###

function update_shadow {
   # validate and reassign var
   if [[ "$1" == "" || "$2" == "" ]]; then return 1; else n="$2"; fi
   # iterate through our original content ($1)
   for o in `echo "$1" |sed 's#:.*##'`; do
       # get each individual line from our original content ($1)
       upln=`echo "$1" |grep ^$o`
       # update these lines in the alternate content ($2)
       n=`echo "$n" |sed "s#^$o.*#$upln#"`
  done
}

# get pwd updated and when update is successful, write new file and bindmount to override.
n=""; opwd=`cat /etc/passwd` && npwd=`cat /opt/etc/passwd` && update_shadow "$opwd" "$npwd"
if [[ "$n" != "" ]]; then echo "$n" > /opt/etc/passwd && umount /etc/passwd > /dev/null 2>&1; mount --bind /opt/etc/passwd /etc/passwd && passwd_status="Successful" || passwd_status="Failed"; fi

# get grp updated and when update is successful, write new file and bindmount to override.
n=""; ogrp=`cat /etc/group` && ngrp=`cat /opt/etc/group` && update_shadow "$ogrp" "$ngrp"
if [[ "$n" != "" ]]; then echo "$n" > /opt/etc/group && umount /etc/group > /dev/null 2>&1; mount --bind /opt/etc/group /etc/group && group_status="Successful" || group_status="Failed"; fi

So let's test this, look into /etc/passwd and /opt/etc/passwd after creating a user and set it a nice long password (we need this for ssh later on):

useradd gogs -s /bin/bash -M -d /tmp/mnt/sda1/data.gogs/home
passwd gogs
Changing password for gogs
Enter the new password (minimum of 5, maximum of 8 characters)
Please use a combination of upper and lower case letters and numbers.
New password:
Re-enter new password:
passwd: password changed.

cat /etc/passwd
root:$blahblah:0:0:Root User,,,:/tmp/root:/bin/sh
reboot:$blahblah:0:0:Root User,,,:/tmp/root:/sbin/reboot
nobody:x:99:99:Nobody:/:/bin/false
crontabs:*:0:0:Contab User,,,:/var:/bin/false
zabbix:*:65533:65533:zabbix:/var:/bin/false

cat /opt/etc/passwd
root:$blahblah:0:0:Root User,,,:/tmp/root:/bin/sh
reboot:$blahblah:0:0:Root User,,,:/tmp/root:/sbin/reboot
nobody:x:99:99:Nobody:/:/bin/false
crontabs:*:0:0:Contab User,,,:/var:/bin/false
zabbix:*:65533:65533:zabbix:/var:/bin/false
gogs:/blahblah:1000:1000::/tmp/mnt/sda1/data.gogs/home:/bin/false

We see what we expect: gogs user is not in /etc/passwd, but is in /opt/etc/passwd. Now run that script above, and validate the results:

/opt/bin/shadow-update.sh

cat /etc/passwd
root:$blahblah:0:0:Root User,,,:/tmp/root:/bin/sh
reboot:$blahblah:0:0:Root User,,,:/tmp/root:/sbin/reboot
nobody:x:99:99:Nobody:/:/bin/false
crontabs:*:0:0:Contab User,,,:/var:/bin/false
zabbix:*:65533:65533:zabbix:/var:/bin/false
gogs:/blahblah:1000:1000::/tmp/mnt/sda1/data.gogs/home:/bin/false

So sone this is set, you can run gogs with a gogs user, and make all data ownership dedicated to that folk. At this point, you may also want to consider making further changes to your system. More users and dedicated service users is a generally good starting point - pretty much all router firmwares tend to run everything as root for simplicity.

Make config changes:

RUN_USER = gogs

Note, that after changing the run user, you will need to amend some permissions too. Remember, my data dir was /tmp/mnt/sda1/data.gogs

chown -R gogs:gogs /tmp/mnt/sda1/data.gogs

Also, at this point, we can set this to be: owned by gogs, can read and execute by anyone in the gogs group, but everybody else can go grab a beer:

find /tmp/mnt/sda1/data.gogs -type d -exec chmod 750 {} \;
find /tmp/mnt/sda1/data.gogs -type f -exec chmod 640 {} \;

Give your changes a "go" to see if any errors are turned up to be fixed, before we proceed:

sudo -u gogs ./gogs web
2019/08/18 14:41:24 [TRACE] Custom path: /opt/go/src/github.com/gogs/gogs/custom
2019/08/18 14:41:24 [TRACE] Log path: /tmp/mnt/sda1/data.gogs/logs
2019/08/18 14:41:24 [ INFO] gogs.mb 0.11.91.0811
2019/08/18 14:41:24 [ INFO] Cache Service Enabled
2019/08/18 14:41:24 [ INFO] Session Service Enabled
2019/08/18 14:41:25 [ INFO] Git Version: 2.21.0
2019/08/18 14:41:25 [ INFO] Run Mode: Production

Now in case you selected to use the internal SSH server, you will see an error saying [FATAL] Fail to start SSH server: listen tcp 0.0.0.0:22: bind: permission denied. That is because non-root user will not be allowed to bind, unless set with setcap (part of libcap on linux), but we don't have that option from entware. That leaves us with two ways: a) build libcap from source (likely a bit overkill for the purpose b) use dropbear as that's the default, c) install openssh.

I went with option c) given I'd like to maintain the ability to restrict those services with separate rules, so here goes.

Installing OpenSSH

Easy go:

opkg install openssh-server
Downloading http://bin.entware.net/armv7sf-k3.2/openssh-server_8.0p1-1_armv7-3.2.ipk
Configuring openssh-server.

Make config changes in /opt/etc/ssh/sshd_config

  • PermitRootLogin no
  • #HostKey /opt/etc/ssh/ssh_host_ecdsa_key - comment these out, RSA is enough
  • #HostKey /opt/etc/ssh/ssh_host_ed25519_key - comment these out, RSA is enough

You will also need to create a user for privilige separation called sshd, and you will need to update shadow (shadow-update.sh script is somewhere above). And test that /etc/passwd has it correctly.

useradd sshd -s /bin/false -M -d /opt/var/empty
/opt/bin/shadow-update.sh

cat /etc/passwd |grep ^sshd
sshd:!:1001:1001::/opt/var/empty:/bin/false

You need to generate an RSA host key too:

ssh-keygen -t rsa -b 4096 -f /opt/etc/ssh/ssh_host_rsa_key
Generating public/private rsa key pair.
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /opt/etc/ssh/ssh_host_rsa_key.
Your public key has been saved in /opt/etc/ssh/ssh_host_rsa_key.pub.
The key fingerprint is:
SHA256:blahblah root@myhost
The key's randomart image is:
+---[RSA 4096]----+
...
+----[SHA256]-----+

At this point, your SSH server should be OK to start up. Test it too:

/opt/etc/init.d/S40sshd start
starting sshd...

netstat -apn |grep sshd
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      12409/sshd

See how our GOGS is doing with these settings:

Make the following modifications in your $GOPATH/src/github.com/gogs/gogs/custom/conf/app.ini:

  • START_SSH_SERVER set to false.

Also, please make sure you have absolute paths at all places where files are written, so we can individually ensure permissions are all correct: ssh root

  • SSH_ROOT_PATH = /tmp/mnt/sda1/data.gogs/home

app data

  • APP_DATA_PATH = /tmp/mnt/sda1/data.gogs/data

repos root

  • ROOT = /tmp/mnt/sda1/data.gogs/repos

temp uploads

  • TEMP_PATH = /tmp/mnt/sda1/data.gogs/data/tmp/uploads

attachemnts

  • PATH = /tmp/mnt/sda1/data.gogs/data/attachments

sqlite

  • PATH = /tmp/mnt/sda1/data.gogs/data/gogs.db

avatars and repo avatars:

  • PROVIDER_CONFIG = /tmp/mnt/sda1/data.gogs/data/avatars
  • AVATAR_UPLOAD_PATH = /tmp/mnt/sda1/data.gogs/data/avatars repo avatars:
  • REPOSITORY_AVATAR_UPLOAD_PATH = /tmp/mnt/sda1/data.gogs/data/repo-avatars attachments:
  • PATH = /tmp/mnt/sda1/data.gogs/data/attachments logs:
  • ROOT_PATH = /tmp/mnt/sda1/data.gogs/logs

Try starting gogs:

cd $GOPATH/src/github.com/gogs/gogs/
sudo -u gogs ./gogs web

Init script

The following example was submitted for gogs / develop branch as gogs/gogs#5795 This is taking rc.func default functions and extends it with the magic we need for gogs, and will avoid calling it after launch. Usual gogs start/stop/check will work just fine.

Note: you will need sudo and daemonize present from opkg.

#!/bin/sh

### Custom user script for gogs
### First param is:
###  "start" (call at start entware),
###  "stop" (call before stop entware),
###
### Note the additional requirements for gogs on ddwrt: shadow user, group, sudo, daemonize

PIDFILE="/opt/var/run/gogs.pid"
USER="gogs"
GOROOT="/opt/bin/go"
GOPATH="/opt/go"

ENABLED=yes
PROC="gogs"
DESC=$PROC
PREARGS="/opt/bin/sudo -u $USER /opt/bin/daemonize"
GOGSBIN="$GOPATH/src/github.com/gogs/gogs/gogs"
ARGS="web"

ansi_red="\033[1;31m";
ansi_white="\033[1;37m";
ansi_green="\033[1;32m";
ansi_yellow="\033[1;33m";
ansi_blue="\033[1;34m";
ansi_bell="\007";
ansi_blink="\033[5m";
ansi_std="\033[m";
ansi_rev="\033[7m";
ansi_ul="\033[4m";

case "$1" in
start)
        # start gogs web
        if [ -f "$PIDFILE" ]
        then
                echo "$DESC is already running ...`pidof $PROC`"
        else
                echo -e -n "$ansi_white Starting $DESC... $ansi_std"
                export GOROOT=$GOROOT
                export GOPATH=$GOPATH
                export PATH=$PATH:$GOROOT/bin

                $PREARGS $GOGSBIN $ARGS > /dev/null 2>&1 &

                COUNTER=0
                LIMIT=10
                while [ -z "`pidof $PROC`" -a "$COUNTER" -le "$LIMIT" ]; do
                        sleep 1;
                        COUNTER=`expr $COUNTER + 1`
                done

                if [ -z "`pidof $PROC`" ]
                then
                        echo -e "            $ansi_red failed. $ansi_std"
                        logger "Failed to start $DESC from $CALLER."
                        return 255
                else
                        echo -e "            $ansi_green done. $ansi_std"
                        logger "Started $DESC from $CALLER."
                        echo `pidof $PROC` > "$PIDFILE"
                        return 0
                fi
        fi
        ;;
stop)
        echo -e -n "$ansi_white Shutting down $PROC... $ansi_std"
        killall $PROC 2>/dev/null
        if [ -f "$PIDFILE" ]
        then
                rm "$PIDFILE"
        fi
        COUNTER=0
        LIMIT=10
        while [ -n "`pidof $PROC`" -a "$COUNTER" -le "$LIMIT" ]; do
                sleep 1;
                COUNTER=`expr $COUNTER + 1`
        done
        ;;
kill)
            echo -e -n "$ansi_white Killing $PROC... $ansi_std"
            killall -9 $PROC 2>/dev/null
        ;;

status | check)
    echo -e -n "$ansi_white Checking $DESC... "
    if [ -n "`pidof $PROC`" ]
    then
        echo -e "            $ansi_green alive. $ansi_std";
        return 0
    else
        echo -e "            $ansi_red dead. $ansi_std";
        return 1
    fi

    ;;
*)
        echo "Usage: $0 {start|stop|status}"
        exit 1
        ;;
esac

Further config changes

Change all of these:

  • COOKIE_USERNAME - something that isn't the default
  • COOKIE_REMEMBER_NAME - something that isn't the default
  • COOKIE_SECURE - true maybe? :)
  • OFFLINE_MODE - set to true in case you want to limit exposure (will disable gravatar :( )

SSL and Nginx Reverse Proxy

So applying SSL to gogs directly didn't look like a good idea to me, so I ended up adding an Nginx reverse proxy, and limiting gogs' http service to localhost. Not getting into much details here, this is what my site.enabled.conf has:

server {
  listen 80;
  server_name gogs.lan git.lan github.lan source.lan src.lan sources.lan code.lan repo.lan repos.lan;
  return 301 https://gogs.lan$request_uri;
  location / {
    try_files $uri $uri/ =404;
    add_header Cache-Control 'no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0';
    if_modified_since off;
    expires off;
    etag off;
  }
}

server {
  listen 443 ssl;
  server_name gogs.lan;

  ssl_certificate /opt/myssl/host_gogs.lan.crt;
  ssl_certificate_key /opt/myssl/host_gogs.lan.key;
  ssl_dhparam /opt/myssl/host_gogs.lan.dhparam.2048.pem;
  ssl_session_cache shared:SSL:50m;
  ssl_session_timeout 10s;
  ssl_prefer_server_ciphers on;
  ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
  ssl_ciphers 'AESGCM:HIGH:!aNULL:!MD5';

  # performance tuning
  gzip off;
  client_max_body_size 128M;

  # static error pages
  error_page 401 /401.html; error_page 402 /402.html; error_page 403 /403.html; error_page 404 /404.html;
  error_page 500 /500.html;

  # static files caching, don't log to accesslog
  location ~* ^/(/.+\.(jpg|jpeg|gif|bmp|ico|png|css|js|swf|pdf))$ {       expires 30d; access_log off; }

  # deny a bunch of files we don't need to access via nginx ever.
  location ~ ^/(?:\.htaccess|data|config|db_structure\.xml|README) { deny  all; }

  location / {
    allow from 192.168.0.0; # lol, example
    deny all;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_pass http://localhost:3000;
 }
}

And at the same time, limit access to gogs and update domains properly in your $GOPATH/src/github.com/gogs/gogs/custom/conf/app.ini

  • PROTOCOL = http
  • DOMAIN = gogs.lan
  • ROOT_URL = https://gogs.lan/
  • HTTP_ADDR = 127.0.0.1
  • HTTP_PORT = 3000

By this, you're limiting http address to 127.0.0.1 (which is OK as gogs.lan and https://gogs.lan will be used). When restarting gogs with this config, you should have no access to gogs.lan on port 3000 anymore.

Firewall

Gogs listen on 3000 that you can allow in. You may also decide to limit that option if you're running the nginx proxy, you won't be connecting to 3000 directly under normal circumstances.

Remarks

This is based on https://blog.meinside.dev/Gogs-on-Raspberry-Pi/ but obvs changes had to be made as of DDWRT.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment