Referendums and Concensus https://shell.c-base.org/v
  • Go 81.1%
  • HTML 18.5%
  • JavaScript 0.2%
  • CSS 0.2%
Find a file
Brian Wiborg 3f005e5581
🐛 Scope GoVote static assets under /v
- serve static assets from /v/static
- update the stylesheet URL
- document /v-only Apache proxying
- add coverage for the scoped asset path
2026-08-09 20:17:21 +02:00
cmd 🔒️ Secure voting frontend and add LDAP elector groups 2026-08-09 19:10:22 +02:00
directory 🔒️ Secure voting frontend and add LDAP elector groups 2026-08-09 19:10:22 +02:00
http 🐛 Scope GoVote static assets under /v 2026-08-09 20:17:21 +02:00
store 🔒️ Secure voting frontend and add LDAP elector groups 2026-08-09 19:10:22 +02:00
tmpl 🐛 Scope GoVote static assets under /v 2026-08-09 20:17:21 +02:00
utils 🔒️ Secure voting frontend and add LDAP elector groups 2026-08-09 19:10:22 +02:00
voting 🔒️ Secure voting frontend and add LDAP elector groups 2026-08-09 19:10:22 +02:00
.gitignore 🐛 Remmove static/ folder 2024-05-13 14:41:07 +02:00
global.css 🎉🚀 May the concensus be with us! 2024-05-13 00:47:24 +02:00
go.mod 🔒️ Secure voting frontend and add LDAP elector groups 2026-08-09 19:10:22 +02:00
go.sum 🔒️ Secure voting frontend and add LDAP elector groups 2026-08-09 19:10:22 +02:00
main.go 🗑️ Use replace yarn in favor of pnpm 2024-05-24 21:37:00 +02:00
package.json 🎉🚀 May the concensus be with us! 2024-05-13 00:47:24 +02:00
pnpm-lock.yaml 🗑️ Use replace yarn in favor of pnpm 2024-05-24 21:37:00 +02:00
README.md 🐛 Scope GoVote static assets under /v 2026-08-09 20:17:21 +02:00
tailwind.config.js 🎉🚀 May the concensus be with us! 2024-05-13 00:47:24 +02:00

GoVote

Referendums & Consensus

A voting tool for the web, written in Go.

Develop

yarn install
go generate
go run ./main

Run behind Apache

The server trusts an authenticated username supplied by Apache. It listens on 127.0.0.1:3000 by default so that clients cannot bypass Apache and forge that header. Keep the backend private even when using a different bind address.

Configuration:

  • BASE_URL is the public URL used in receipt QR codes. Set it to the HTTPS URL in production; it also enables the Secure flag on the CSRF cookie.
  • REMOTE_USER_HEADER selects the trusted username header and defaults to X-Remote-User.
  • GOVOTE_DB selects the SQLite database path and defaults to ./govote.db.

Apache must replace any client-supplied username header with its authenticated identity before proxying the request. Do not use a "set if empty" rule. The app also rejects requests for which this header is empty.

On Debian 13, enable the required modules with:

sudo a2enmod authnz_ldap headers proxy proxy_http ssl

Use the LDAP attribute authorization provider for the user's direct memberOf value. A broad Require ldap-filter memberOf=... search returns every member of the group rather than the single authenticated entry and therefore fails authorization:

<Location /v>
    AuthType Basic
    AuthBasicProvider ldap
    AuthName "Crew"
    AuthLDAPURL "ldap://meridian.c-base.org:389/ou=crew,dc=c-base,dc=org?uid,memberOf" STARTTLS
    AuthLDAPRemoteUserAttribute uid

    Require ldap-attribute memberOf="cn=crew,ou=groups,dc=c-base,dc=org"

    RequestHeader unset X-Remote-User
    RequestHeader set X-Remote-User "%{AUTHENTICATE_UID}e"
</Location>

ProxyPass        /v http://127.0.0.1:3000/v
ProxyPassReverse /v http://127.0.0.1:3000/v

Keep certificate verification enabled for LDAP STARTTLS. If the LDAP certificate is issued by a private CA, install that CA in Debian's trust store for the Go application and configure Apache's LDAP trust settings as needed.

An anonymous voting hides elector names in published results. Votes remain linked to elector names in the database because the final vote from each elector supersedes their earlier votes.

LDAP group selection

Set these environment variables to enable the group selector on the new-voting form:

LDAP_URL=ldap://meridian.c-base.org:389
LDAP_STARTTLS=true
LDAP_USER_BASE_DN=ou=crew,dc=c-base,dc=org
LDAP_GROUP_BASE_DN=ou=groups,dc=c-base,dc=org

The defaults match this directory layout: user names are read from uid, group names from cn, and membership from the users' memberOf attribute. The app uses anonymous LDAP access unless both LDAP_BIND_DN and LDAP_BIND_PASSWORD are set. Optional settings are:

  • LDAP_USER_ATTRIBUTE, default uid
  • LDAP_GROUP_ATTRIBUTE, default cn
  • LDAP_MEMBERSHIP_ATTRIBUTE, default memberOf
  • LDAP_TLS_SERVER_NAME, useful when LDAP_URL contains an IP address but the TLS certificate identifies a hostname

The group list is derived from memberOf values on user entries. This works with the current directory ACLs even though some entries below ou=groups are not anonymously visible. The selected group's current members are expanded once, when the voting is created, and stored as individual electors. Manually entered electors are merged with the group and duplicates are removed case-insensitively. This snapshot is intentional: later LDAP membership changes do not silently alter an active voting's electorate or quorum.

Adding memberOf to AuthLDAPURL does retrieve that attribute in Apache's existing lookup, as AUTHENTICATE_MEMBEROF; AuthLDAPRemoteUserAttribute uid also makes REMOTE_USER use the canonical directory value. That information only describes the user currently signing in, however. It cannot populate the selector or expand an arbitrary selected group to all its members, so the separate, read-only LDAP query is still needed.

User systemd service

The example user unit assumes the checkout is at ~/src/code.c-base.org/baccenfutter/govote and the installed executable is at ~/go/bin/govote. Install and start it with:

mkdir -p ~/.config/systemd/user
cp contrib/systemd/user/govote.service ~/.config/systemd/user/
systemctl --user daemon-reload
systemctl --user enable --now govote.service

Inspect it with systemctl --user status govote.service and journalctl --user -u govote.service. To start user services at boot without an interactive login, enable lingering with loginctl enable-linger.