14 Commits

Author SHA1 Message Date
347dff8ea8 Clarify our use versions and improve guidance for tagging
git push --tags sends all the tags to the server.
git push origin <tag> sends only the new tag to the server.
2020-02-06 09:08:06 -07:00
82dacc05d9 Upgrade to wiki 0.21.0 2020-02-06 08:56:49 -07:00
f67be8dbb8 Install and unseal vault for experiments with secrets management 2020-01-20 00:19:05 -07:00
c4bd44f3f4 Provide example farm configuration for friends security plugin 2020-01-19 15:22:03 -07:00
9d83a6768d Ensure localhost is included in k8s cluster TLS cert
See explanation here:
https://github.com/rancher/k3d/issues/143\#issuecomment-552634281

--server-arg passes the --tls-san=... argument through to k3s server.
Docs for that arg are here:
https://rancher.com/docs/k3s/latest/en/installation/install-options/
2020-01-19 15:13:24 -07:00
45dbb893d1 Simplest wiki command that could possibly work
Now that we've figured out how to mount local directories into the k8s
cluster, we have moved all the configuration into ~/.wiki-k8s/config.json
2020-01-19 15:11:12 -07:00
eaa86fd867 Simplify persistence configuration & expose macos folders
We now map ~/.wiki-k8s in MacOS into the .wiki folder inside the
container and similarly with MacOS ~/workspace/fedwiki

First, when we create the k3d cluster, we include directives that are
passed through to docker to mount the MacOS directories into the
kubernetes host.

Second, we use hostPath volumes in the kubernetes deployment config.

These will work great for the primary use case of a local wiki.
Deployments to remote kubernetes clusters will want to do this with
the PersistentVolumeClaim that was removed with this change.

One luxury of using hostPath and the legacy_security is that we no
longer require an init container.
2020-01-12 19:29:41 -07:00
37d6785ee2 Change the insecure wiki to a farm for *.simple.localtest.me 2020-01-12 16:55:14 -07:00
27a80c7b25 Start an insecure wiki under simple.localtest.me
Bootstrapping a simpler development environment
2020-01-12 15:30:42 -07:00
1eb8c99d2c Upgrade to wiki 0.20.0 2020-01-09 22:41:29 -07:00
5c326ed1ae Add help to remember how to start all the things 2019-12-25 13:08:08 -07:00
fb2aa0f67c Add example kubernetes deployment
This configuration partially works with kubernetes 1.15 running
locally using Docker Desktop for Mac and kind (k8s in docker).

For completeness, we installed kind & created a cluster like this:

    cd /tmp/ && GO111MODULE="on" go get sigs.k8s.io/kind
    kind create cluster --name workshop
    export KUBECONFIG="$(kind get kubeconfig-path --name="workshop")"

We describe finicky details discovered while creating wiki.yaml.

The persistent volume when mounted in wiki-config begins its life with
all files owned by root. This prevented our node user inside the
container from creating the config files inside .wiki. It took a while
to discover the correct securityContext for the wiki-config container.

We tested this configuration as follows:

    alias k=kubectl
    k apply -f wiki.yaml
    export POD=$(k get pod -lapp=wiki -o jsonpath='{.items[*].metadata.name}')
    export PASSWORD=$(k exec svc/wiki-service -- jq -r .admin .wiki/config.json)
    k port-forward svc/wiki-service 3000:80 > /dev/null &
    pbcopy <<<"$PASSWORD"
    open http://localhost:3000
    # click lock icon in the browser to login to wiki page
    # paste the password from the clipboard
    # click wiki to toggle editing on
    # make a few edits to the wiki page

Something about authentication is NOT working for anything except
localhost. When we try the same tests using http://localtest.me or
configuring foo.local in the MacOS /etc/hosts file, for some reason
the cookies don't seem to be passed through to the server. All edits
on other pages end up in browser localStorage.

Nevertheless, I'll commit what I have for now.
2019-11-22 12:24:34 -07:00
fb81d51e29 Use default node user & dumb-init & remove config assumptions
By default the wiki will run in farm mode with friends security
2019-11-16 16:49:43 -07:00
5e6f732fed Bump wiki to version 0.19.0 2019-08-14 12:07:10 -04:00
10 changed files with 404 additions and 156 deletions

View File

@ -1,20 +1,16 @@
FROM node:10-alpine
FROM node:lts-alpine
RUN adduser -D -h /home/app app \
&& apk add --update --no-cache \
bash \
jq \
git
WORKDIR /home/app
ARG WIKI_PACKAGE=wiki@0.17.0
RUN su app -c "npm install -g --prefix . $WIKI_PACKAGE"
RUN su app -c "mkdir .wiki"
COPY configure-wiki set-owner-name ./
RUN chown app configure-wiki set-owner-name
VOLUME "/home/app/.wiki"
ENV DOMAIN=localhost
ENV OWNER_NAME="The Owner"
ENV COOKIE=insecure
RUN apk add --update --no-cache \
dumb-init \
git \
jq
WORKDIR "/home/node"
ARG WIKI_PACKAGE=wiki@0.21.0
RUN su node -c "npm install -g --prefix . $WIKI_PACKAGE"
RUN su node -c "mkdir -p .wiki"
VOLUME "/home/node/.wiki"
EXPOSE 3000
USER app
CMD ["/home/app/bin/wiki"]
USER node
ENV PATH="${PATH}:/home/node/bin"
ENTRYPOINT ["dumb-init"]
CMD ["wiki", "--farm", "--security_type=friends"]

View File

@ -1,49 +1,52 @@
# Federated Wiki Farm
http://fed.wiki.org
Start Playing Federated Wiki: http://start.fed.wiki
Although this container can run alone, I use and develop it with
a reverse proxy. See: https://github.com/dobbs/wiki-tls
### Run a local wiki farm
See also http://local-farm.wiki.dbbs.co for many more details.
docker run -p 3000:3000 -it --rm \
dobbs/farm
### Get acquainted with wiki.
Visit http://localhost:3000 and http://anything.localtest.me:3000
Launch the container:
``` bash
docker run -p 3000:3000 -it --rm \
dobbs/farm
```
### Run a local wiki that will survive a reboot
Visit http://localhost:3000
docker run -p 3000:3000 -it --rm \
-v ~/.wiki:/home/node/.wiki \
dobbs/farm
### Make your wiki survive a reboot
Your wiki pages and configuration will be saved in the ~/.wiki folder.
Create a volume:
# Release Notes for 1.0.0
``` bash
docker volume create dot-wiki
```
This is a significant **breaking** change from pre-1.0 releases. Especially:
Launch the container:
``` bash
docker run -p 3000:3000 -it --rm \
-v dot-wiki:/home/app/.wiki \
dobbs/farm
```
* changed the user from `app` (`uid=1001(app) gid=1001(app) groups=1001(app)`)
to `node` (`uid=1000(node) gid=1000(node) groups=1000(node),1000(node)`)
Visit http://localhost:3000
* no longer installing `bash`, `configure-wiki`, nor `set-owner-name`
* no longer creating `/home/app/.wiki/wiki.json`
Those changes in particular will impose some work on authors upgrading
from previous versions.
The last non-breaking revision is 0.52.0 https://github.com/dobbs/farm/tree/0.52.0#readme
# Development
This image's tag does not match the version of the included wiki software.
This image's tag does not match the version of the included wiki
software. Our version indicates the scale of changes in this tiny
devops pipeline. For example, when we changed the `USER` directive and
removed the wiki config generation scripts, we bumped the major
version from 0.50.x to 1.0.x.
Notes to self:
``` bash
docker build --tag dobbs/farm:0.51.0 .
git tag -am "" '0.51.0'
git push --tags
docker build --tag dobbs/farm:1.0.2 .
git tag -am "" '1.0.2'
git push origin '1.0.2'
```
The repos in Dockerhub and GitHub are configured to automatically build new tags.

View File

@ -1,72 +0,0 @@
#!/bin/bash -eu
set -o pipefail
main() {
initialize-environment-vars
assert-file-privileges || report-errors-and-exit
ensure-owner-file
ensure-config-file
show-configs
}
initialize-environment-vars() {
ERRORS=''
readonly OWNER_FILE=/home/app/.wiki/$DOMAIN.owner.json
readonly CONFIG_FILE=/home/app/.wiki/config.json
}
assert-file-privileges() {
[ -w /home/app/.wiki ] \
|| ERRORS="app cannot write to /home/app/.wiki\n${ERRORS}"
[ ${#ERRORS} == 0 ]
}
report-errors-and-exit() {
echo -e $ERRORS
echo "exiting."
exit 1
}
ensure-owner-file() {
if [ ! -r "$OWNER_FILE" ]; then
jq -n --arg name "$OWNER_NAME" --arg secret $(random-string) \
'.name = $name | .friend.secret = $secret' > $OWNER_FILE
fi
}
ensure-config-file() {
if [ ! -r "$CONFIG_FILE" ]; then
> $CONFIG_FILE \
jq -n -M \
--arg admin $(jq -r .friend.secret $OWNER_FILE) \
--arg random $(random-string) \
--arg cookie $COOKIE \
--arg domain $DOMAIN \
--arg owner $OWNER_FILE \
'
.admin = $admin
| .autoseed = true
| .farm = true
| .cookieSecret = $random
| .secure_cookie = ("secure" == $cookie)
| .security_type = "friends"
| .wikiDomains[$domain].id = "/home/app/.wiki/\($domain).owner.json"
'
fi
}
random-string() {
node -e 'console.log(require("crypto").randomBytes(64).toString("hex"))'
}
show-configs() {
set -x
ls -l $OWNER_FILE $CONFIG_FILE
cat $OWNER_FILE
cat $CONFIG_FILE
set +x
}
main

53
examples/k8s/README.md Normal file
View File

@ -0,0 +1,53 @@
# Wiki Farm in Kubernetes
There are easier ways to get started with federated wiki. Here we are
using wiki to drive some learning about kubernetes.
# We're using MacOS, Docker Desktop, and k3d
brew cask install docker
brew install k3d
mkdir -p ~/.wiki-k8s ~/workspace/fedwiki
k3d create \
--server-arg --tls-san="127.0.0.1" \
--publish 80:80 \
-v "$HOME/.wiki-k8s:/macos/.wiki-k8s" \
-v "$HOME/workspace/fedwiki:/macos/fedwiki" \
--name wiki
# example ~/.wiki-k8s/config.json
{
"admin": "any memorable password",
"autoseed": true,
"farm": true,
"cookieSecret": "any random string",
"secure_cookie": false,
"security_type": "friends",
"wikiDomains": {
"simple.localtest.me": {
"id": "/home/node/.wiki/config.owner.json"
}
}
}
# example ~/.wiki-k8s/config.owner.json
`.friend.secret` must match the `.admin` field from `config.json`
{
"name": "The Owner",
"friend": {
"secret": "any memorable password"
}
}
# Deploy Wiki
kubectl apply -f wiki.yaml
# Play with the wiki
open http://simple.localtest.me

View File

@ -0,0 +1,29 @@
# HashiCorp Vault in kubernetes
HashiCorp recomend installing vault via helm. Your author prefers
plain old kubernetes configs.
So we generated the yaml via helm's template command.
helm template incubator/vault \
--name-template=vault \
--replicaCount=1 \
--set vault.dev=false \
--set vault.config.storage.file.path=/macos/.wiki-k8s/vault \
| egrep -v 'heritage: "?Helm"?' \
> vault.html
kubectl apply -k .
kubectl port-forward svc/vault 8200:8200 &> /dev/null &
export VAULT_ADDR=http://127.0.0.1:8200
vault status
vault operator init
vault operator unseal
# paste key-fragment 1
vault operator unseal
# paste key-fragment 2
vault operator unseal
# paste key-fragment 3
vault login
# paste root token

View File

@ -0,0 +1,16 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: vault
spec:
template:
spec:
containers:
- name: vault
volumeMounts:
- name: vault-data
mountPath: /macos/.wiki-k8s/vault
volumes:
- name: vault-data
hostPath:
path: /macos/.wiki-k8s/vault

View File

@ -0,0 +1,10 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
images:
- name: vault
newName: vault
newTag: 1.3.1
resources:
- vault.yaml
patchesStrategicMerge:
- deployment-volumes.yaml

View File

@ -0,0 +1,181 @@
---
# Source: vault/templates/clusterrolebinding.yaml
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
name: vault
labels:
app: vault
release: "vault"
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: system:auth-delegator
subjects:
- kind: ServiceAccount
name: vault
namespace: default
---
# Source: vault/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: "vault-config"
labels:
app: "vault"
release: "vault"
data:
config.json: |
{"listener":{"tcp":{"address":"[::]:8200","cluster_address":"[::]:8201","tls_disable":true}},"storage":{"file":{"path":"/macos/.wiki-k8s/vault"}}}
---
# Source: vault/templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: vault
labels:
app: vault
release: vault
annotations:
{}
spec:
selector:
matchLabels:
app: vault
release: vault
replicas: 1
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 1
template:
metadata:
labels:
app: vault
release: vault
annotations:
checksum/config: 6868eb00aa48ca9485c365c3523ae431e7031233a1c046817a32c61e24ea817d
spec:
containers:
- name: vault
image: "vault:1.2.3"
imagePullPolicy: IfNotPresent
command: ["vault", "server", "-config", "/vault/config/config.json"]
ports:
- containerPort: 8200
name: api
- containerPort: 8201
name: cluster-address
livenessProbe:
# Alive if Vault is successfully responding to requests
httpGet:
path: /v1/sys/health?standbyok=true&uninitcode=204&sealedcode=204&
port: 8200
scheme: HTTP
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
# Ready depends on preference
httpGet:
path: /v1/sys/health?standbycode=204&uninitcode=204&
port: 8200
scheme: HTTP
initialDelaySeconds: 10
periodSeconds: 10
securityContext:
readOnlyRootFilesystem: true
capabilities:
add:
- IPC_LOCK
env:
- name: POD_IP
valueFrom:
fieldRef:
fieldPath: status.podIP
- name: VAULT_API_ADDR
value: "http://$(POD_IP):8200"
- name: VAULT_CLUSTER_ADDR
value: "https://$(POD_IP):8201"
- name: VAULT_LOG_LEVEL
value: "info"
resources:
{}
volumeMounts:
- name: vault-config
mountPath: /vault/config/
- name: vault-root
mountPath: /root/
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- podAffinityTerm:
labelSelector:
matchLabels:
app: 'vault'
release: 'vault'
topologyKey: kubernetes.io/hostname
weight: 100
serviceAccountName: vault
volumes:
- name: vault-config
configMap:
name: "vault-config"
- name: vault-root
emptyDir: {}
---
# Source: vault/templates/pdb.yaml
apiVersion: policy/v1beta1
kind: PodDisruptionBudget
metadata:
name: vault
spec:
maxUnavailable: 1
selector:
matchLabels:
app: vault
release: vault
---
# Source: vault/templates/service.yaml
apiVersion: v1
kind: Service
metadata:
name: vault
labels:
app: vault
release: vault
spec:
type: ClusterIP
ports:
- port: 8200
protocol: TCP
targetPort: 8200
name: api
selector:
app: vault
release: vault
---
# Source: vault/templates/serviceaccount.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: vault
labels:
app: vault
release: "vault"
---
# Source: vault/templates/tests/test-vault-status.yaml
apiVersion: v1
kind: Pod
metadata:
name: "vault-vault-status-test"
annotations:
"helm.sh/hook": test-success
spec:
containers:
- name: vault-vault-status-test
image: "vault:1.2.3"
env:
- name: VAULT_ADDR
value: http://vault.default:8200
command: ["sh", "-c", "vault status"]
restartPolicy: Never

71
examples/k8s/wiki.yaml Normal file
View File

@ -0,0 +1,71 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: wiki-deployment
spec:
replicas: 1
selector:
matchLabels:
app: wiki
template:
metadata:
labels:
app: wiki
spec:
securityContext:
runAsUser: 1000
runAsGroup: 1000
fsGroup: 1000
containers:
- name: farm
image: dobbs/farm:1.0.1
command: ["wiki"]
ports:
- containerPort: 3000
volumeMounts:
- name: dot-wiki
mountPath: /home/node/.wiki
- name: fedwiki
mountPath: /home/node/fedwiki
volumes:
- name: dot-wiki
hostPath:
path: /macos/.wiki-k8s
- name: fedwiki
hostPath:
path: /macos/fedwiki
---
apiVersion: v1
kind: Service
metadata:
name: wiki-service
spec:
ports:
- name: http
targetPort: 3000
port: 80
selector:
app: wiki
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: simple-wiki
annotations:
kubernetes.io/ingress.class: traefik
spec:
rules:
- host: simple.localtest.me
http:
paths:
- path: /
backend:
serviceName: wiki-service
servicePort: http
- host: "*.simple.localtest.me"
http:
paths:
- path: /
backend:
serviceName: wiki-service
servicePort: http

View File

@ -1,39 +0,0 @@
#!/bin/bash -eu
set -o pipefail
usage() {
cat <<EOF
Usage: $(basename $0) NAME
replaces the owner's name in $OWNER_FILE
EOF
}
main() {
initialize-environment-vars $@ || { usage; exit 1; }
backup-and-save-name
report-success
}
initialize-environment-vars() {
readonly OWNER_FILE=/home/app/.wiki/$DOMAIN.owner.json
readonly OWNER_BACKUP_FILE=$OWNER_FILE-saved-$(date --iso-8601=minutes)
readonly NAME=${@:-missing}
[ ! "$NAME" == "missing" ]
}
backup-and-save-name() {
mv $OWNER_FILE $OWNER_BACKUP_FILE
jq ".name = \"$NAME\"" $OWNER_BACKUP_FILE > $OWNER_FILE
}
report-success() {
cat <<EOF
Owner's name changed to "$NAME"
Previous config is saved in ${OWNER_BACKUP_FILE##$PWD/}
EOF
}
main "$@"