1
0
Fork 0

firefly-iii: Deploy Firefly III

[Firefly III][0] is a free and open source, web-based personal finance
management application.  It features a double-entry bookkeeping system
for tracking transactions, plus other classification options like
budgets, categories, and tags.  It has a rule engine that can
automatically manipulate transactions, plus several other really useful
features.

The application itself is mostly standard browser-based GUI written in
PHP.  There is an official container image, though it is not
particularly well designed and must be run as root (it does drop
privileges before launching the actual application, thankfully).  I may
decide to create a better image later.

Along with the main application, there is a separate tool for importing
transactions from a CSV file.  Its design is rather interesting: though
it is a web-based application, it does not have any authentication or
user management, but uses a user API key to access the main Firefly III
application.  This effectively requires us to have one instance of the
importer per user.  While not ideal, it isn't particularly problematic
since there are only two of us (and Tabitha may not even end up using
it; she seems to like YNAB).

[0]: https://www.firefly-iii.org/
dch-webhooks-secrets
Dustin 2023-04-30 22:04:12 -05:00
parent ffffe9d3c8
commit 5d5b69a629
11 changed files with 583 additions and 0 deletions

View File

@ -8,6 +8,12 @@ access_control:
rules:
- domain: paperless.pyrocufflink.blue
policy: two_factor
- domain: firefly.pyrocufflink.blue
resources:
- '^/api/'
policy: bypass
- domain: firefly.pyrocufflink.blue
policy: two_factor
- domain: scan.pyrocufflink.blue
networks:
- internal
@ -69,6 +75,10 @@ session:
expiration: 1d
inactivity: 4h
server:
buffers:
read: 16384
storage:
local:
path: /var/lib/authelia/db.sqlite3

4
firefly-iii/.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
*.access-token
app.key
cron.token
db.password

View File

@ -0,0 +1,3 @@
TZ=America/Chicago
TRUSTED_PROXIES=172.30.0.160/28

View File

@ -0,0 +1,30 @@
APP_ENV=local
SITE_OWNER=dustin@hatch.name
TZ=America/Chicago
TRUSTED_PROXIES=172.30.0.160/28
DB_CONNECTION=pgsql
DB_HOST=default.postgresql
DB_PORT=5432
DB_USERNAME=firefly-iii.firefly
DB_DATABASE=firefly
CACHE_DRIVER=redis
SESSION_DRIVER=redis
REDIS_SCHEME=tcp
REDIS_HOST=redis
REDIS_PORT=6379
AUTHENTICATION_GUARD=remote_user_guard
AUTHENTICATION_GUARD_HEADER=Remote-User
AUTHENTICATION_GUARD_EMAIL=Remote-Email
MAIL_MAILER=smtp
MAIL_HOST=mail.pyrocufflink.blue
MAIL_PORT=25
MAIL_ENCRYPTION=null
MAIL_FROM=firefly-iii@pyrocufflink.net

View File

@ -0,0 +1,149 @@
apiVersion: v1
kind: Namespace
metadata:
name: firefly-iii
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
labels:
app.kubernetes.io/component: firefly-iii
app.kubernetes.io/instance: firefly-iii
app.kubernetes.io/name: firefly-iii
app.kubernetes.io/part-of: firefly-iii
name: firefly-iii
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 2Gi
---
apiVersion: v1
kind: Service
metadata:
labels:
app.kubernetes.io/component: firefly-iii
app.kubernetes.io/instance: firefly-iii
app.kubernetes.io/name: firefly-iii
app.kubernetes.io/part-of: firefly-iii
name: firefly-iii
spec:
ports:
- port: 8080
name: http
selector:
app.kubernetes.io/component: firefly-iii
app.kubernetes.io/instance: firefly-iii
app.kubernetes.io/name: firefly-iii
---
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app.kubernetes.io/component: firefly-iii
app.kubernetes.io/instance: firefly-iii
app.kubernetes.io/name: firefly-iii
app.kubernetes.io/part-of: firefly-iii
name: firefly-iii
spec:
selector:
matchLabels:
app.kubernetes.io/component: firefly-iii
app.kubernetes.io/instance: firefly-iii
app.kubernetes.io/name: firefly-iii
template:
metadata:
labels:
app.kubernetes.io/component: firefly-iii
app.kubernetes.io/instance: firefly-iii
app.kubernetes.io/name: firefly-iii
app.kubernetes.io/part-of: firefly-iii
spec:
containers:
- name: firefly-iii
image: docker.io/fireflyiii/core:version-6.0.8
envFrom:
- configMapRef:
name: firefly-iii
optional: true
env:
- name: APP_KEY_FILE
value: /run/secrets/firefly-iii/app.key
- name: DB_PASSWORD_FILE
value: /run/secrets/firefly-iii/db.password
- name: STATIC_CRON_TOKEN_FILE
value: /run/secrets/firefly-iii/cron.token
ports:
- containerPort: 8080
name: http
readinessProbe:
httpGet:
port: 8080
path: /health
failureThreshold: 3
periodSeconds: 60
successThreshold: 1
timeoutSeconds: 1
startupProbe:
httpGet:
port: 8080
path: /health
failureThreshold: 30
periodSeconds: 3
initialDelaySeconds: 3
successThreshold: 1
timeoutSeconds: 1
volumeMounts:
- name: firefly-iii-secrets
mountPath: /run/secrets/firefly-iii
readOnly: true
- name: firefly-iii-data
mountPath: /var/www/html/storage/upload
subPath: upload
securityContext:
fsGroup: 33
volumes:
- name: firefly-iii-secrets
secret:
secretName: firefly-iii
defaultMode: 0440
- name: firefly-iii-data
persistentVolumeClaim:
claimName: firefly-iii
---
apiVersion: batch/v1
kind: CronJob
metadata:
name: firefly-iii
spec:
timeZone: America/Chicago
schedule: '0 3 * * *'
jobTemplate:
spec:
template:
spec:
containers:
- image: docker.io/library/busybox
name: wget
command:
- wget
args:
- http://$(FIREFLY_III_SERVICE_HOST):$(FIREFLY_III_SERVICE_PORT)/api/v1/cron/$(STATIC_CRON_TOKEN)
- -O
- /dev/null
env:
- name: STATIC_CRON_TOKEN
valueFrom:
secretKeyRef:
name: firefly-iii
key: cron.token
securityContext:
runAsGroup: 1000
runAsNonRoot: true
runAsUser: 1000
restartPolicy: Never

View File

@ -0,0 +1,72 @@
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: firefly-iii-importer-dustin
labels:
app.kubernetes.io/name: firefly-iii-importer-dustin
app.kubernetes.io/component: firefly-iii-importer
app.kubernetes.io/instance: dustin
app.kubernetes.io/part-of: firefly-iii-importer
annotations:
cert-manager.io/cluster-issuer: zerossl
nginx.ingress.kubernetes.io/proxy-buffer-size: "8k"
nginx.ingress.kubernetes.io/auth-method: GET
nginx.ingress.kubernetes.io/auth-url: http://authelia.authelia.svc.cluster.local:9091/api/verify
nginx.ingress.kubernetes.io/auth-signin: https://auth.pyrocufflink.blue/?rm=$request_method
nginx.ingress.kubernetes.io/auth-response-headers: Remote-User,Remote-Name,Remote-Groups,Remote-Email
nginx.ingress.kubernetes.io/auth-snippet: |
proxy_set_header X-Forwarded-Method $request_method;
spec:
ingressClassName: nginx
tls:
- hosts:
- '*.import.firefly.pyrocufflink.blue'
secretName: firefly-import-cert
rules:
- host: dustin.import.firefly.pyrocufflink.blue
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: firefly-iii-importer-dustin
port:
name: http
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: firefly-iii-importer-tabitha
labels:
app.kubernetes.io/name: firefly-iii-importer-tabitha
app.kubernetes.io/component: firefly-iii-importer
app.kubernetes.io/instance: tabitha
app.kubernetes.io/part-of: firefly-iii-importer
annotations:
nginx.ingress.kubernetes.io/proxy-buffer-size: "8k"
nginx.ingress.kubernetes.io/auth-method: GET
nginx.ingress.kubernetes.io/auth-url: http://authelia.authelia.svc.cluster.local:9091/api/verify
nginx.ingress.kubernetes.io/auth-signin: https://auth.pyrocufflink.blue/?rm=$request_method
nginx.ingress.kubernetes.io/auth-response-headers: Remote-User,Remote-Name,Remote-Groups,Remote-Email
nginx.ingress.kubernetes.io/auth-snippet: |
proxy_set_header X-Forwarded-Method $request_method;
spec:
ingressClassName: nginx
tls:
- hosts:
- '*.import.firefly.pyrocufflink.blue'
secretName: firefly-import-cert
rules:
- host: tabitha.import.firefly.pyrocufflink.blue
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: firefly-iii-importer-tabitha
port:
name: http

124
firefly-iii/importer.yaml Normal file
View File

@ -0,0 +1,124 @@
apiVersion: v1
kind: Service
metadata:
labels:
app.kubernetes.io/component: firefly-iii-importer
app.kubernetes.io/instance: dustin
app.kubernetes.io/name: firefly-iii-importer-dustin
app.kubernetes.io/part-of: firefly-iii
name: firefly-iii-importer-dustin
spec:
ports:
- port: 8080
name: http
selector:
app.kubernetes.io/component: firefly-iii-importer
app.kubernetes.io/instance: dustin
app.kubernetes.io/name: firefly-iii-importer-dustin
---
apiVersion: v1
kind: Service
metadata:
labels:
app.kubernetes.io/component: firefly-iii-importer
app.kubernetes.io/instance: tabitha
app.kubernetes.io/name: firefly-iii-importer-tabitha
app.kubernetes.io/part-of: firefly-iii
name: firefly-iii-importer-tabitha
spec:
ports:
- port: 8080
name: http
selector:
app.kubernetes.io/component: firefly-iii-importer
app.kubernetes.io/instance: tabitha
app.kubernetes.io/name: firefly-iii-importer-tabitha
---
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app.kubernetes.io/component: firefly-iii-importer
app.kubernetes.io/instance: dustin
app.kubernetes.io/name: firefly-iii-importer-dustin
app.kubernetes.io/part-of: firefly-iii
name: firefly-iii-importer-dustin
spec:
selector:
matchLabels:
app.kubernetes.io/component: firefly-iii-importer
app.kubernetes.io/instance: dustin
app.kubernetes.io/name: firefly-iii-importer-dustin
template:
metadata:
labels:
app.kubernetes.io/component: firefly-iii-importer
app.kubernetes.io/instance: dustin
app.kubernetes.io/name: firefly-iii-importer-dustin
app.kubernetes.io/part-of: firefly-iii-importer
spec:
containers:
- name: firefly-iii
image: docker.io/fireflyiii/data-importer:version-1.2.2
envFrom:
- configMapRef:
name: firefly-iii-importer
optional: true
env:
- name: FIREFLY_III_URL
value: http://firefly-iii:8080
- name: FIREFLY_III_ACCESS_TOKEN
valueFrom:
secretKeyRef:
name: firefly-iii-importer
key: dustin.access-token
ports:
- containerPort: 8080
name: http
---
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app.kubernetes.io/component: firefly-iii-importer
app.kubernetes.io/instance: tabitha
app.kubernetes.io/name: firefly-iii-importer-tabitha
app.kubernetes.io/part-of: firefly-iii
name: firefly-iii-importer-tabitha
spec:
selector:
matchLabels:
app.kubernetes.io/component: firefly-iii-importer
app.kubernetes.io/instance: tabitha
app.kubernetes.io/name: firefly-iii-importer-tabitha
template:
metadata:
labels:
app.kubernetes.io/component: firefly-iii-importer
app.kubernetes.io/instance: tabitha
app.kubernetes.io/name: firefly-iii-importer-tabitha
app.kubernetes.io/part-of: firefly-iii-importer
spec:
containers:
- name: firefly-iii
image: docker.io/fireflyiii/data-importer:version-1.2.2
envFrom:
- configMapRef:
name: firefly-iii-importer
optional: true
env:
- name: FIREFLY_III_URL
value: http://firefly-iii:8080
- name: FIREFLY_III_ACCESS_TOKEN
valueFrom:
secretKeyRef:
name: firefly-iii-importer
key: tabitha.access-token
ports:
- containerPort: 8080
name: http

33
firefly-iii/ingress.yaml Normal file
View File

@ -0,0 +1,33 @@
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: firefly-iii
labels:
app.kubernetes.io/name: firefly-iii
app.kubernetes.io/component: firefly-iii
app.kubernetes.io/instance: firefly-iii
app.kubernetes.io/part-of: firefly-iii
annotations:
nginx.ingress.kubernetes.io/auth-method: GET
nginx.ingress.kubernetes.io/auth-url: http://authelia.authelia.svc.cluster.local:9091/api/verify
nginx.ingress.kubernetes.io/auth-signin: https://auth.pyrocufflink.blue/?rm=$request_method
nginx.ingress.kubernetes.io/auth-response-headers: Remote-User,Remote-Name,Remote-Groups,Remote-Email
nginx.ingress.kubernetes.io/auth-snippet: |
proxy_set_header X-Forwarded-Method $request_method;
spec:
ingressClassName: nginx
tls:
- hosts:
- firefly.pyrocufflink.blue
rules:
- host: firefly.pyrocufflink.blue
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: firefly-iii
port:
name: http

View File

@ -0,0 +1,64 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: firefly-iii
resources:
- redis.yaml
- firefly-iii.yaml
- ingress.yaml
- importer.yaml
- importer-ingress.yaml
configMapGenerator:
- name: firefly-iii
envs:
- firefly-iii.env
options:
disableNameSuffixHash: true
- name: firefly-iii-importer
envs:
- firefly-iii-importer.env
options:
disableNameSuffixHash: true
secretGenerator:
- name: firefly-iii
files:
- app.key
- cron.token
options:
disableNameSuffixHash: true
- name: firefly-iii-importer
files:
- dustin.access-token
- tabitha.access-token
options:
disableNameSuffixHash: true
patches:
# This patch changes the source secret for the PostgreSQL database
# password from the default (`db.password` inside `firefly-iii`) to
# a secret managed by the postgres operator.
- patch: |-
apiVersion: apps/v1
kind: Deployment
metadata:
name: firefly-iii
spec:
template:
spec:
containers:
- name: firefly-iii
env:
- name: DB_PASSWORD_FILE
value: /run/secrets/postgresql/password
volumeMounts:
- name: db-secret
mountPath: /run/secrets/postgresql
readOnly: true
volumes:
- name: db-secret
secret:
secretName: firefly-iii.firefly.default.credentials.postgresql.acid.zalan.do
defaultMode: 0440

91
firefly-iii/redis.yaml Normal file
View File

@ -0,0 +1,91 @@
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: redis
namespace: firefly-iii
labels:
app.kubernetes.io/name: redis
app.kubernetes.io/component: redis
app.kubernetes.io/instance: firefly-iii
app.kubernetes.io/part-of: firefly-iii
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 2Gi
---
apiVersion: v1
kind: Service
metadata:
labels:
app.kubernetes.io/name: redis
app.kubernetes.io/component: redis
app.kubernetes.io/instance: firefly-iii
app.kubernetes.io/part-of: firefly-iii
name: redis
namespace: firefly-iii
spec:
ports:
- name: redis
port: 6379
selector:
app.kubernetes.io/name: redis
app.kubernetes.io/component: redis
app.kubernetes.io/instance: firefly-iii
type: ClusterIP
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: redis
namespace: firefly-iii
labels:
app.kubernetes.io/name: redis
app.kubernetes.io/component: redis
app.kubernetes.io/instance: firefly-iii
app.kubernetes.io/part-of: firefly-iii
spec:
serviceName: redis
selector:
matchLabels:
app.kubernetes.io/name: redis
app.kubernetes.io/component: redis
app.kubernetes.io/instance: firefly-iii
template:
metadata:
labels:
app.kubernetes.io/name: redis
app.kubernetes.io/component: redis
app.kubernetes.io/instance: firefly-iii
spec:
containers:
- name: redis
image: docker.io/library/redis:7
imagePullPolicy: IfNotPresent
ports:
- name: redis
containerPort: 6379
securityContext:
runAsNonRoot: true
readOnlyRootFilesystem: true
runAsUser: 1000
runAsGroup: 1000
volumeMounts:
- name: redisdata
mountPath: /data
subPath: data
- name: tmp
mountPath: /tmp
securityContext:
fsGroup: 1000
volumes:
- name: redisdata
persistentVolumeClaim:
claimName: redis
- name: tmp
emptyDir:

View File

@ -14,5 +14,8 @@ spec:
dustin:
- superuser
- createdb
firefly-iii.firefly:
- login
databases:
dustin: dustin
firefly: firefly-iii.firefly