Friday, February 7, 2025

Search by action all AWS IAM Policies in your account

Find all policies that contain a specific action(grep by string)

AWS Iam manage access to AWS cloud trough different entities that narrow down to policies that have actions.

One such situation be the following, you want to find all policies that contain the action ec2:CreateVolume under your AWS account.

To find all policies that contain ec2:CreateVolume using the Web Ui can be time consuming and error prone when you need to search multiple policies, especially if you have inline and managed (AWS or customer).

So cli to the rescue !

Examples

To search trough use aws iam and get-account-authorization-details.

All policies

To note this output will not include inline policies.

In the case of ec2:CreateVolume this should do

aws iam \
    get-account-authorization-details \
    --query 'Policies[?contains(PolicyVersionList[].Document[].Statement[].Action[], `ec2:CreateVolume`)].{Arn:Arn, Path:Path}'

If any policy contains the action ec2:CreateVolume will show something like


[
    {
        "Arn": "arn:aws:iam::000000000000:policy/path/subPath/NameOfPolicy",
        "Path": "/path/subPath/"
    },
    {
        "Arn": "arn:aws:iam::aws:policy/AmazonEKSClusterPolicy",
        "Path": "/"
    },
    {
        "Arn": "arn:aws:iam::aws:policy/service-role/AmazonEBSCSIDriverPolicy",
        "Path": "/service-role/"
    }
]

So the first entry is a customer managed policy, the account id is zeroed in this example but will show the actual account id, the path and subPath are namespaces as where the policy was written.

The second and third don’t have an account id and the name starts with Amazon, these are managed by AWS.

All groups with inline policies

aws iam \
    get-account-authorization-details \
    --query 'GroupDetailList[?GroupPolicyList[].PolicyDocument[].Statement[].Action!=null && contains(GroupPolicyList[].PolicyDocument[].Statement[].Action[], `ec2:CreateVolume`)].{GroupArn:Arn, PolicyName:GroupPolicyList[].PolicyName}'

All users with inline policies

aws iam \
    get-account-authorization-details \
    --query 'UserDetailList[?UserPolicyList[].PolicyDocument[].Statement[].Action!=null && contains(UserPolicyList[].PolicyDocument[].Statement[].Action[], `ec2:CreateVolume`)].{UserArn:Arn, PolicyName:UserPolicyList[].PolicyName}[]' 

All roles with inline policies

aws iam \
    get-account-authorization-details \
    --query 'RoleDetailList[?RolePolicyList[].PolicyDocument[].Statement[].Action!=null && contains(RolePolicyList[].PolicyDocument[].Statement[].Action[], `ec2:CreateVolume`)].{RoleArn:Arn, PolicyName:RolePolicyList[].PolicyName}'

Wednesday, January 22, 2025

Change storage class in a running StateFull Set (sts)

Change the storage class of a running STS in k8s

K8s doesn’t allow to change the StorageClass for an active StateFull Set.

However there are times when this is needed.

This article explains how to change the storage class for a running sts.

For this will use

  • sts workload
  • sc (currently in use) default
  • sc (we want to change to) premium

As environment I’m using Azure Kubernetes Service (AKS)

Storage classes

Default storage class using Standard SDD with ZRS (zonal replication).

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  annotations:
    storageclass.kubernetes.io/is-default-class: "true"
  labels:
    addonmanager.kubernetes.io/mode: EnsureExists
    kubernetes.io/cluster-service: "true"
  name: default
parameters:
  skuname: StandardSSD_ZRS
provisioner: disk.csi.azure.com
reclaimPolicy: Delete
volumeBindingMode: WaitForFirstConsumer
allowVolumeExpansion: true

Premium storage class using Premium SSD

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  labels:
    addonmanager.kubernetes.io/mode: EnsureExists
    kubernetes.io/cluster-service: "true"
  name: managed-csi-premium
parameters:
  skuname: Premium_ZRS
provisioner: disk.csi.azure.com
reclaimPolicy: Delete
volumeBindingMode: WaitForFirstConsumer
allowVolumeExpansion: true

The two classes above are created by AKS, in case more control over the IOPS is needed a custom class will do

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
   name: premium2-disk-sc
parameters:
# fine tune
   cachingMode: None
   skuName: PremiumV2_LRS
   DiskIOPSReadWrite: "4000"
   DiskMBpsReadWrite: "1000"
provisioner: disk.csi.azure.com
reclaimPolicy: Delete
volumeBindingMode: Immediate  # note - creates a volume when a PVC is present.
allowVolumeExpansion: true

Current state

Manifests

Deployment of two nginx pods with default storage class under the namespace sts-change-sc.

---
apiVersion: v1
kind: Namespace
metadata:
  name: sts-change-sc
---
apiVersion: v1
kind: Service
metadata:
  name: nginx
  labels:
    app: nginx
  namespace: sts-change-sc
spec:
  ports:
  - port: 80
    name: web
  clusterIP: None
  selector:
    app: nginx
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: web
  namespace: sts-change-sc
spec:
  selector:
    matchLabels:
      app: nginx # has to match .spec.template.metadata.labels
  serviceName: "nginx"
  replicas: 2 # by default is 1
  minReadySeconds: 10 # by default is 0
  template:
    metadata:
      labels:
        app: nginx # has to match .spec.selector.matchLabels
    spec:
      terminationGracePeriodSeconds: 10
      containers:
      - name: nginx
        image: registry.k8s.io/nginx-slim:0.24
        ports:
        - containerPort: 80
          name: web
        volumeMounts:
        - name: www
          mountPath: /usr/share/nginx/html
  volumeClaimTemplates:
  - metadata:
      name: www
    spec:
      accessModes: [ "ReadWriteOnce" ]
# initial storage class
      storageClassName: "default"  # technically not needed specified for this demo.
      resources:
        requests:
          storage: 1Gi
$ kubectl get pv -n sts-change-sc| grep web
pvc-87813cdf-2f48-4e4e-b121-8ba2bfa04aca  1Gi RWO  Delete Bound  sts-change-sc/www-web-0  default  <unset>  28m
pvc-ad285352-93bc-422c-be14-3628482fa9ba  1Gi RWO  Delete Bound  sts-change-sc/www-web-1  default  <unset>  119s

PVC

$ kubectl get pvc -n sts-change-sc| grep web
www-web-0  Bound  pvc-87813cdf-2f48-4e4e-b121-8ba2bfa04aca  1Gi  RWO  default  <unset>  29m
www-web-1  Bound  pvc-ad285352-93bc-422c-be14-3628482fa9ba  1Gi  RWO  default  <unset>  2m8s

STS

$ kubectl get sts -n sts-change-sc
NAME   READY   AGE
web    2/2     6m4s

SC used by STS

$ kubectl get sts -n sts-change-sc -o json | jq .items[].spec.volumeClaimTemplates[].spec.storageClassName
"default"

Change storage class used by sts

Changing from default to managed-csi-premium.

Manifests

---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: web
  namespace: sts-change-sc
spec:
  selector:
    matchLabels:
      app: nginx # has to match .spec.template.metadata.labels
  serviceName: "nginx"
  replicas: 3 # by default is 1
  minReadySeconds: 10 # by default is 0
  template:
    metadata:
      labels:
        app: nginx # has to match .spec.selector.matchLabels
    spec:
      terminationGracePeriodSeconds: 10
      containers:
      - name: nginx
        image: registry.k8s.io/nginx-slim:0.24
        ports:
        - containerPort: 80
          name: web
        volumeMounts:
        - name: www
          mountPath: /usr/share/nginx/html
  volumeClaimTemplates:
  - metadata:
      name: www
    spec:
      accessModes: [ "ReadWriteOnce" ]
# changed storage class
      storageClassName: "managed-csi-premium"
      resources:
        requests:
          storage: 1Gi

Try to apply the updated sts

$ kubectl apply -f changed.yaml -n sts-change-sc
The StatefulSet "web" is invalid: spec: Forbidden: updates to statefulset spec for fields other than 'replicas', 'ordinals', 'template', 'updateStrategy', 'persistentVolumeClaimRetentionPolicy' and 'minReadySeconds' are forbidden

Since that didn’t work we need to do the following

  1. Delete the sts with the --cascade=orphan option.
  2. Apply the sts manifest with the changed storage class "managed-csi-premium".
  3. Delete one by one each pod from sts and its pvc (cascade delete its referrenced pv).

Applying this looks like

Step 1

$ kubectl delete sts/web --cascade=orphan -n sts-change-sc
statefulset.apps "web" deleted

At this point the pods are still running but the sts is gone. Let’s see the pods.

$ kubectl get pods -n sts-change-sc
NAME    READY   STATUS    RESTARTS   AGE
web-0   1/1     Running   0          24m
web-1   1/1     Running   0          24m

Step 2

Apply the changed manifest.

$ kubectl apply -f changed.yaml -n sts-change-sc
statefulset.apps/web created

Since the initial pods are still running no new pods are created by the updated sts.

$ kubectl get pods -n sts-change-sc
NAME    READY   STATUS    RESTARTS   AGE
web-0   1/1     Running   0          28m
web-1   1/1     Running   0          28m

Step 3

Replacing one by one each pod, starting with web-0.

First you need its pvc so it can be deleted.

$ kubectl get pvc -n sts-change-sc| grep web-0
www-web-0   Bound    pvc-87813cdf-2f48-4e4e-b121-8ba2bfa04aca   1Gi        RWO            default        <unset>                 56m

As you can see its status is Bound so it will not be deleted unless the pod is deleted.

$ kubectl delete pvc/www-web-0 -n sts-change-sc
persistentvolumeclaim "www-web-0" deleted

$ kubectl get pvc -n sts-change-sc
NAME        STATUS        VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   VOLUMEATTRIBUTESCLASS   AGE
www-web-0   Terminating   pvc-87813cdf-2f48-4e4e-b121-8ba2bfa04aca   1Gi        RWO            default        <unset>                 63m
www-web-1   Bound         pvc-ad285352-93bc-422c-be14-3628482fa9ba   1Gi        RWO            default        <unset>                 36m

In the case that a finalizer is attached to the pvc, this case has one, you can see it with

k describe pvc/www-web-0 -n sts-change-sc |grep Finalizers
Finalizers:    [kubernetes.io/pvc-protection]

So you need to delete the pod, as k8s is protecting the pvc deletion kubectl delete pvc/www-web-0 -n sts-change-sc and the command never returns.

So will delete the pod as well.

$ kubectl delete pod/web-0 -n sts-change-sc
pod "web-0" deleted

At this point the initial delete of the pvc is finished and since the pod web-0 is under the control of the updated sts it will be recreated with the updated storage class.

List the pods

$ kubectl get pods -n sts-change-sc
NAME    READY   STATUS    RESTARTS   AGE
web-0   1/1     Running   0          88s  ### NEW
web-1   1/1     Running   0          47m

The newly created pod also created a new pvc and volumes under the new storage class.

$ kubectl get pvc -n sts-change-sc| grep web-0
www-web-0  Bound  pvc-40de012d-98d9-4f76-810f-7bcfb2970b37  1Gi RWO managed-csi-premium  <unset>  2m15s

Continue with the pod rotation until all pods are using the updated storage class.

NOTE if you don’t delete the pvc and skip to just the pod delete this will not work for example

$ kubectl delete pod/web-1 -n sts-change-sc
pod "web-1" deleted

$ kubectl get pvc -n sts-change-sc| grep web-1
www-web-1   Bound    pvc-ad285352-93bc-422c-be14-3628482fa9ba   1Gi        RWO            default               <unset>                 55m

So the initial class default is still there.

Final state

Looking again at resources

$ kubectl get pvc -n sts-change-sc
NAME        STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS          VOLUMEATTRIBUTESCLASS   AGE
www-web-0   Bound    pvc-40de012d-98d9-4f76-810f-7bcfb2970b37   1Gi        RWO            managed-csi-premium   <unset>                 27m
www-web-1   Bound    pvc-21600a0f-85ff-4d06-9a60-9958a45177ff   1Gi        RWO            managed-csi-premium   <unset>                 9s

Sts storage class

$ kubectl get sts -n sts-change-sc -o json | jq .items[].spec.volumeClaimTemplates[].spec.storageClassName
"managed-csi-premium"

Final notes

I’ve been using the images provided by registry.k8s.io which is the official registry used by k8s these days.

To note that is no public web interface that you can use to list images and their tags but you can use

curl -sL "https://registry.k8s.io/v2/tags/list" | jq .

ps: an useful tool to further inspect images is gcrane

In the case where the finalizer is causing any issues it can be changed kubectl patch pvc my-pvc -p '{"metadata":{"finalizers":null}}' before deletion this will have the effect that when deleting a pvc the command will return immediatly.

Tuesday, October 29, 2024

Golang clean package cache

Cleaning golang (cache)packages on your system

The usual flow is to install packages on your system with the simple command go install url/package@version, for example to install godoc you will run go install golang.org/x/tools/cmd/godoc@latest.

This works well untill you kind of run of disk space …

So how to clean up your disk space ?

First you need to know where the packages are downloaded that is under your $GOMODCACHE this you can find by running

go env and is usually under $HOME/go/pkg/mod.

On my system this looks like this before clean up.

$ cd $HOME/go/pkg/mod
# list size
$ du -sh .
9.6G  # space used

To clean up

# clean the cache
$ go clean -modcache
$ cd $HOME/go/pkg/mod
# list size
$ du -sh .
4.0K

That’s it.

Friday, April 12, 2024

Databricks AWS private link with conditional DNS forwarder

Databricks allows workspace to be accessible via a private ip so not publicly available.
This is useful in some cases where you want to restrict users to used it only if are connected to a VPN or equivalent.
These are the steps to achive this goal
  1. Create a custom VPC in AWS
  2. Connect the VPC to your infrastructure (VPN/Direct Connect)
  3. Create VPC Endpoint for frontend and backend
  4. Register private link in the databricks account and associate it to your workspace
  5. Change access mode to Private from Public in the Private Access Settings
  6. Create a private zone in Route53
  7. Create an inbound resolver in Route53
  8. Add an A entry in the zone that points to the address of the VPC Endpoint (step3)
  9. Add a forwarder in your Private DNS to point to the inbound resolver (step 7) ip address(es)
For more details Databricks documentation explains in more details docs.databricks.com (search for private link). Once all is in place the flow is
+---------+                               +-------------+                                            +-----------+ +-------------------------+                                         +-------------+
| Client  |                               | PrivateDns  |                                            | PublicDns | | Route53InboundResolver  |                                         | Route53Zone |
+---------+                               +-------------+                                            +-----------+ +-------------------------+                                         +-------------+
     |                                           |                                                         |                    |                                                             |
     | my-workspace.cloud.databricks.com         |                                                         |                    |                                                             |
     |------------------------------------------>|                                                         |                    |                                                             |
     |                                           |                                                         |                    |                                                             |
     |                                           | my-workspace.cloud.databricks.com                       |                    |                                                             |
     |                                           |-------------------------------------------------------->|                    |                                                             |
     |                                           |                                                         |                    |                                                             |
     |                                           |       CNAME nvirginia.privatelink.cloud.databricks.com. |                    |                                                             |
     |                                           |<--------------------------------------------------------|                    |                                                             |
     |                                           |                                                         |                    |                                                             |
     |                                           | nvirginia.privatelink.cloud.databricks.com ?            |                    |                                                             |
     |                                           |----------------------------------------------------------------------------->|                                                             |
     |                                           |                                                         |                    |                                                             |
     |                                           |                                                         |                    | nvirginia.privatelink.cloud.databricks.com ?                |
     |                                           |                                                         |                    |------------------------------------------------------------>|
     |                                           |                                                         |                    |                                                             |
     |                                           |                                                         |                    |                                      Address is 172.16.0.10 |
     |                                           |                                                         |                    |<------------------------------------------------------------|
     |                                           |                                                         |                    |-----------------------------------------------------------\ |
     |                                           |                                                         |                    || nvirginia.privatelink.cloud.databricks.com A 172.16.0.10 |-|
     |                                           |                                                         |                    ||----------------------------------------------------------| |
     |                                           |                                                         |                    |                                                             |
     |                                           |                                                Answer address is 172.16.0.10 |                                                             |
     |                                           |<-----------------------------------------------------------------------------|                                                             |
     |                                           |                                                         |                    |                                                             |
     |                    Connect to 172.16.0.10 |                                                         |                    |                                                             |
     |<------------------------------------------|                                                         |                    |                                                             |
     |                                           |                                                         |                    |                                                             |



Tuesday, October 10, 2023

Remove Windows 10 drivers from command line

Normally you would use System Settings -> Apps & features ... but that didn't work for you so this will explain how to uninstall a driver from command line.
First if you searched and tried to remove the files from a location as C:\windows\system32\driverstore\FileRepository\ you might notice that is not possible since you need SYSTEM access.
To know what driver you want to remove you will need to list them.
Open a command prompt or powershell as Administrator than
dism /online /get-drivers /format:table > c:\drivers.txt

Open the file c:\drivers.txt and note the Published Name as per
Version: 10.0.19041.844

Image Version: 10.0.19045.3448

Obtaining list of 3rd party drivers from the driver store...

Driver packages listing:


-------------- | ----------------------------- | ----- | -------------------- | ---------------------------- | ---------- | ----------------
Published Name | Original File Name            | Inbox | Class Name           | Provider Name                | Date       | Version         
-------------- | ----------------------------- | ----- | -------------------- | ---------------------------- | ---------- | ----------------
oem77.inf      | nxdrv.inf                     | No    | Net                  | SonicWall                    | 10/18/2017 | 2.0.6.1         

Now to remove the driver take note of its Published Name as above list.

pnputil.exe /d oem77.inf
That's it.
Other tools that can help are https://learn.microsoft.com/en-us/sysinternals/downloads/autoruns though I found that would work most of the time but in some cases will not be able to remove the driver files but just registry cleanup.

Thursday, January 19, 2023

Tmux cheetsheet

Attach and detach

$ tmux Start new tmux session
$ tmux attach Attach to tmux session running in the background
Ctrl+B d Detach from tmux session, leaving it running in the background
Ctrl+B & Exit and quit tmux
Ctrl+B ? List all key bindings (press Q to exit help screen)

Window management

Ctrl+B C Create new window If you are running more than one tmux session (more than one
PID), you can switch between the two clients.
Ctrl+B N Move to next window
Ctrl+B P Move to previous window
Ctrl+B L Move to last window
Ctrl+B 0-9 Move to window by index number
Ctrl+B ) Move to next session
Ctrl+B ( Move to previous session
Ctrl+B Ctrl+Z Suspend session	

Split window into panes

Ctrl+B % Vertical split (panes side by side)
Ctrl+B " Horizontal split (one pane below the other)
Ctrl+B CTRL+O Interchange pane position
Ctrl+B O Move to other pane
Ctrl+B ! Remove all panes but the current one from the window
Ctrl+B Q Display window index numbers
Ctrl+B Ctrl-Up/Down Resize current pane (due north/south)
Ctrl+B Ctrl-Left/Right Resize current pane (due west/east)

Pane related


join-pane -s 1 -t 0 -p 20  "Join pane source 1 into pane target 0 with 20% usage"
break pane  "remove all other panes like CTRL+B !"

# best to create some key bindings into tmux.conf
# pane movement vertical split
bind-key j command-prompt -p "join pane from:"  "join-pane -h -s '%%'"
bind-key s command-prompt -p "send pane to:"  "join-pane -h -t '%%'"

# pane movement
bind-key J command-prompt -p "join pane from:"  "join-pane -s '%%'"
bind-key S command-prompt -p "send pane to:"  "join-pane -t '%%'"

# move panes around
Ctrl+B <space>

Copy/Paste

CTRL+B [ enter copy mode (user arrows or CTRL+F CTRL+B CTRL+B to move)
SHIFT+v to start select
Movement keys to select
ENTER to copy

CTRL+B ] to paste

q to exit from copy mode

Misc

CTRL+B ? List all bindings
For more details - https://github.com/tmux/tmux/blob/master/key-bindings.c#L345

For all commands see into https://github.com/tmux/tmux/blob/master files that begin with cmd-

Thursday, October 6, 2022

Online openssl private certificate and key with alternative DNS

Openssl added a nice alternative to the config file or extention to create requests with alternative DNS. This will create a key and certificate (not certificate request) with two additional DNS alt1.example.net and alt2.example.net

sudo openssl req -x509 -nodes -days 3650 -newkey rsa:4096 -keyout mykey.key -out mycer.crt  -subj '/CN=main.example.net' -addext 'subjectAltName=DNS:alt1.example.net,DNS:alt2.example.net'


Wednesday, December 29, 2021

Victoria metrics on Aws EC2 instance

Will configure one single EC2 instance as a Victoria Metrics server to be used as Promethues storage.

The access to VM(victoria metrics) is done via port 8247 and is protected by http basic auth. All traffic is encrypted with a self sign certificate.

Installation

Will install manually by downloading the releases from github and configure the local system.

Download binaries

# create a group and user for vm
$ sudo groupadd -r victoriametrics
$ sudo useradd -g victoriametrics victoriametrics
 
# download
$ curl -L https://github.com/VictoriaMetrics/VictoriaMetrics/releases/download/v1.70.0/victoria-metrics-amd64-v1.70.0.tar.gz --output victoria-metrics-amd64-v1.70.0.tar.gz

# unpack and install it
$ sudo tar xvf victoria-metrics-amd64-v1.70.0.tar.gz -C /usr/local/bin/
$ chown root:root /usr/local/bin/victoria-metrics-prod

# create data directory
$ sudo mkdir /var/lib/victoria-metrics-data
$ chown -v victoriametrics:victoriametrics /var/lib/victoria-metrics-data

Configure the service

cat >> /etc/systemd/system/victoriametrics.service <<EOF
[Unit]
Description=High-performance, cost-effective and scalable time series database, long-term remote storage for Prometheus
After=network.target

[Service]
Type=simple
User=victoriametrics
Group=victoriametrics
StartLimitBurst=5
StartLimitInterval=0
Restart=on-failure
RestartSec=1
ExecStart=/usr/local/bin/victoria-metrics-prod \
        -storageDataPath=/var/lib/victoria-metrics-data \
        -httpListenAddr=127.0.0.1:8428 \
        -retentionPeriod=1
ExecStop=/bin/kill -s SIGTERM $MAINPID
LimitNOFILE=65536
LimitNPROC=32000

[Install]
WantedBy=multi-user.target

EOF

At this point your can start the service systemctl enable victoriametrics.service --now, however the port 8428 is not protected in any way nor is encrypted so will add basic authentication and tls encryption with a self sign certificate, any valid certificate will work however. Note that listens only on localhost.

Vmauth

To protect the service will use vmauth which is part of a tool set released by victoria metrics.

# download and install the vm utils

$ curl -L https://github.com/VictoriaMetrics/VictoriaMetrics/releases/download/v1.70.0/vmutils-amd64-v1.70.0.tar.gz --output vmutils-amd64-v1.70.0.tar.gz
$ sudo tar xvf vmutils-amd64-v1.70.0.tar.gz -C /usr/local/bin/
$ chown -v root:root /usr/local/bin/vm*-prod
Configure vmauth

Create a config file (config.yml) to enable basic authentication.

The format of the file is simple, you need a username and a password.

$ sudo mkdir -p /etc/victoriametrics/ssl/
$ sudo chown -vR victoriametrics:victoriametrics /etc/victoriametrics
$ sudo touch /etc/victoriametrics/config.yml
$ sudo chown -v victoriametrics:victoriametrics /etc/victoriametrics/config.yml

# generate a password for our user
$ python3  -c 'import secrets; print(secrets.token_urlsafe())'
KGKK_NoiciEMn6KdBk6CkcLHZt6TpB-Cgt12UFqnutU

# wite the config
$ sudo cat >> /etc/victoriametrics/config.yml <<EOF
> users:
>   - username: "user1"
>     password: "KGKK_NoiciEMn6KdBk6CkcLHZt6TpB-Cgt12UFqnutU"
>     url_prefix: "http://127.0.0.1:8428"
> # end config
> EOF
Install a self sign certificate
$ sudo openssl req -x509 -nodes -days 365 -newkey rsa:4096 -keyout /etc/victoriametrics/ssl/victoriametrics.key -out /etc/victoriametrics/ssl/victoriametrics.crt

$ sudo chown -Rv victoriametrics:victoriametrics /etc/victoriametrics/ssl/
Enable vmauth service
cat >> /etc/systemd/system/vmauth.service <<EOF
[Unit]
Description=Simple auth proxy, router and load balancer for VictoriaMetrics
After=network.target

[Service]
Type=simple
User=victoriametrics
Group=victoriametrics
StartLimitBurst=5
StartLimitInterval=0
Restart=on-failure
RestartSec=1
ExecStart=/usr/local/bin/vmauth-prod \
        --tls=true \
        --auth.config=/etc/victoriametrics/config.yml \
        --httpListenAddr=0.0.0.0:8247 \
        --tlsCertFile=/etc/victoriametrics/ssl/victoriametrics.crt \
        --tlsKeyFile=/etc/victoriametrics/ssl/victoriametrics.key \
ExecStop=/bin/kill -s SIGTERM $MAINPID
LimitNOFILE=65536
LimitNPROC=32000

[Install]
WantedBy=multi-user.target


EOF

Start and enable systemctl enable vmauth.service --now .

To test you will need first to construct a base64 string from the username and password you have written into the config.ymlfile.

For example user vmuser and password secret

$ echo -n 'vmuser:secret' | base64
$ dm11c2VyOnNlY3JldA==

# to test vmauth
$ curl -H 'Authorization: Basic dm11c2VyOnNlY3JldA==' --insecure https://localhost:8247/api/v1/query -d 'query={job=~".*"}'

Operations

Snaphots

List what’s available

curl 'https://localhost:8247/snapshot/list'

{"status":"ok","snapshots":["20211227145126-16C1DDB61673BA11"

Create a new snapshot

curl 'https://localhost:8247/snapshot/create'

{"status":"ok","snapshot":"20211227145526-16C1DDB61673BA12"}

List again the snapshots

curl -s 'https://localhost:8247/snapshot/list' | jq .
{
  "status": "ok",
  "snapshots": [
    "20211227145126-16C1DDB61673BA11",
    "20211227145526-16C1DDB61673BA12"
  ]
}

Backups

The snapshots are located on local disk under data path (parameter -storageDataPath=) on my instance it resolves to storageDataPath=/var/lib/victoria-metrics-data/.

The data into snapshots is compressed with Zstandard.

To push the backups to s3 you can use vmbackup.

$ sudo vmbackup-prod -storageDataPath=/var/lib/victoria-metrics-data  -snapshotName=20211227145526-16C1DDB61673BA12 -dst=s3://BUCKET-NAME/`date +%s`

...

2021-12-29T16:07:20.571Z        info    VictoriaMetrics/app/vmbackup/main.go:105        gracefully shutting down http server for metrics at ":8420"
2021-12-29T16:07:20.572Z        info    VictoriaMetrics/app/vmbackup/main.go:109        successfully shut down http server for metrics in 0.001 seconds

For more info you can see vmbackup.

Friday, December 24, 2021

Postgresql locks

Locks in postgres

Find locks

select pid, state, usename, query, query_start 
from pg_stat_activity 
where pid in (
  select pid from pg_locks l 
  join pg_class t on l.relation = t.oid 
  and t.relkind = 'r' 
  where t.relname = 'search_hit'
);

Killing locks

SELECT pg_cancel_backend(PID);

Haproxy socket stats

Enable stats

Reporting is provided if you enable stats into its config.

The setting is described at https://cbonte.github.io/haproxy-dconv/1.8/configuration.html#4.2-stats%20enable

In this post I describe how to use the socket type.

Enable the stats socket

I enable it into the global section as so

global

  stats socket /var/lib/haproxy/stats group haproxy mode 664

What this does is:

  • enable the stats socket under /var/lib/haproxy/stats
  • the group owner is haproxy (running haproxy as user haproxy)
  • permissions are rw (user), rw(group), r(others)

Note there is an option admin that will allow to control haproxy but I don’t use it.

Reading stats from socket (netcat)

You need to have installed netcat (nc).

$ echo 'show stat' | nc -U /var/lib/haproxy/stats
# pxname,svname,qcur,qmax,scur,smax,slim,
....
http_frontend,
....

Reading stats from socket (socat)

You need to install socat since is not frequently installed.

To use it

$ echo 'show stat' | socat stdio /var/lib/haproxy/stats
# pxname,svname,qcur,qmax,scur,smax,slim,
....
http_frontend,
....

Friday, December 18, 2020

AWS cli filter for security groups

There are times when I want to see the security groups on an AWS region. Nothing special really you can always use the aws cli :)

But wait ... there is so much output especially if you have many groups and many rules.

So this is a simple way to filter on the following values(you can add more values but is mostly what I use)

  • VPC Id
  • Group Name
  • Group Id

Tools that I use

  • aws cli (you need to install it)
  • jq (available on many linux distros)
  • awk (comes with any linux distro)

This is how you put all together

      
      	$ export GROUP='My SG'
        $ aws ec2 describe-security-groups --filters Name=group-name,Values="$GROUP" --output json| jq '.SecurityGroups[]| .VpcId, .GroupName, .GroupId'|  awk '{printf (NR%3==0) ? $0 "\n" : $0}'| sed -e 's/""/ - /g'
        # this will print
        "vpc-xxxxxx - My SG - sg-yyyy"
        # bonus - you can use a regex for GROUP
        $ export GROUP='My*Prod'
        $ aws ec2 describe-security-groups --filters Name=group-name,Values="$GROUP" --output json| jq '.SecurityGroups[]| .VpcId, .GroupName, .GroupId'|  awk '{printf (NR%3==0) ? $0 "\n" : $0}'| sed -e 's/""/ - /g'
        # this will print
        "vpc-xxxxxx - My Prod - sg-yyyy"
        "vpc-xxxxxx - My deprecated Prod - sg-yyyy"
        "vpc-xxxxxx - My whatever Prod - sg-yyyy"
         
      

Friday, December 20, 2019

Tcpdump on docker interfaces

This post shows how you can inspect docker containers traffic with tcpdump on linux.

First find the docker names and the mac addresses.


bash $ for c in `sudo docker ps| grep -v CON| awk '{print $1}'`; do sudo docker inspect $c| jq ". |map({ (.Name): .NetworkSettings.Networks[].MacAddress })"; done

[
  {
    "/docker-demo_cortex2_1": "02:42:ac:12:00:08"
  }
]
[
  {
    "/docker-demo_consul_1": "02:42:ac:12:00:05"
  }
]
[
  {
    "/docker-demo_prometheus2_1": "02:42:ac:12:00:03"
  }
]
[
  {
    "/docker-demo_cortex3_1": "02:42:ac:12:00:09"
  }
]
[
  {
    "/docker-demo_cortex1_1": "02:42:ac:12:00:06"
  }
]
[
  {
    "/docker-demo_prometheus3_1": "02:42:ac:12:00:04"
  }
]
[
  {
    "/docker-demo_prometheus1_1": "02:42:ac:12:00:02"
  }
]
[
  {
    "/docker-demo_grafana_1": "02:42:ac:12:00:07"
  }
]

I want to inspect on /docker-demo_cortex1_1 so I list the forward table (fdb)

bash $ /sbin/bridge fdb |grep 02:42:ac:12:00:06

02:42:ac:12:00:06 dev vethee0ca4e master br-f9c7e5b79104
This says that the dev `vethee0ca4e` forwards to the master bridge `br-f9c7e5b79104`

List what interfaces are into the system

bash$ sbin/ip link show

1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: enp0s3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP mode DEFAULT group default qlen 1000
    link/ether 08:00:27:6f:ce:6d brd ff:ff:ff:ff:ff:ff
3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN mode DEFAULT group default 
    link/ether 02:42:cf:95:1a:17 brd ff:ff:ff:ff:ff:ff
4: br-f9c7e5b79104: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default 
    link/ether 02:42:ae:f7:a0:c6 brd ff:ff:ff:ff:ff:ff
28: veth47b30a5@if27: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-f9c7e5b79104 state UP mode DEFAULT group default 
    link/ether b2:23:05:8a:cd:4e brd ff:ff:ff:ff:ff:ff link-netnsid 0
30: veth95ec404@if29: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-f9c7e5b79104 state UP mode DEFAULT group default 
    link/ether ba:41:85:94:67:39 brd ff:ff:ff:ff:ff:ff link-netnsid 1
32: veth246e156@if31: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-f9c7e5b79104 state UP mode DEFAULT group default 
    link/ether 92:26:8e:09:97:af brd ff:ff:ff:ff:ff:ff link-netnsid 2
34: veth426ba55@if33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-f9c7e5b79104 state UP mode DEFAULT group default 
    link/ether 6a:c0:12:86:30:0f brd ff:ff:ff:ff:ff:ff link-netnsid 5
38: veth91e2bee@if37: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-f9c7e5b79104 state UP mode DEFAULT group default 
    link/ether de:53:75:37:b0:88 brd ff:ff:ff:ff:ff:ff link-netnsid 6
40: veth9199c33@if39: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-f9c7e5b79104 state UP mode DEFAULT group default 
    link/ether e2:d1:fa:61:83:cd brd ff:ff:ff:ff:ff:ff link-netnsid 3
42: vethdb6a7ca@if41: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-f9c7e5b79104 state UP mode DEFAULT group default 
    link/ether ea:51:60:cc:6f:e8 brd ff:ff:ff:ff:ff:ff link-netnsid 4
44: vethee0ca4e@if43: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-f9c7e5b79104 state UP mode DEFAULT group default 
    link/ether ca:b1:72:d1:c7:e2 brd ff:ff:ff:ff:ff:ff link-netnsid 7

As you can see the interface that I want to inspect is listed as 44.

At this point just start a tcpdump on the interface

bash$ sudo tcpdump -nvv -s0 -A -i vethee0ca4e
In case you have multiple bridges configured onto the system it will help to fist find the master bridge you want to find.
bash$ sudo docker network ls

NETWORK ID          NAME                         DRIVER              SCOPE
bedcfa44fe2b        bridge                       bridge              local
f9c7e5b79104        docker-demo_cortex_network   bridge              local
0d3a96789a7f        host                         host                local
1ecffcd51252        none                         null                local

Sunday, April 14, 2019

Making use of Ansible vault from fabric(fabfile)

Ansible provides a convenient solution to encrypt sensitive data such as passwords, secrets, etc. - Ansible Vault. This post shows how to use the ansible vault from Fabric. First you would think why ? First I thought is a crazy idea :) however since I've been using Fabric and Ansible for a long while I said why not - they are both written in python right ?!. So how to use it, you need to have installed Fabric and Ansible obviously. Create a fabfile at the top import a few Ansible modules

from ansible.cli import CLI
from ansible.parsing.vault import VaultLib
from ansible.parsing.dataloader import DataLoader
import yaml
import os

This allows to interface with the VaultLib which in turns will unencrypt the vault. And this is how you use them from a function


def gef_vault_data(vault_pass_file, vault_file):
    secrets = CLI.setup_vault_secrets(
            DataLoader(),
            vault_ids=[],
            vault_password_files=[vault_pass_file])

    v = VaultLib(secrets=secrets)

    data = v.decrypt(open(vault_file, 'rb').read())
    return yaml.load(data)

# in case you keep the password file into your home directory - adjust as required
HOME = os.environ.get("HOME")
VAULT_PASSWORD_FILE = os.path.join(HOME, ".ansible/vault_password_file")

my_vault = get_vault_data(VAULT_PASSWORD_FILE, "/etc/ansible/vault.yml")  

print(my_vault)  # this is the data from the encrypted Ansible vault. 

Monday, May 14, 2018

Python pip install from git with specific revision

There are times when you want to try a specific revision of a package that is under a specific git revision.

The general syntax is

pip install -e git://github.com/{ username }/{ reponame }.git@{ tag name }#egg={ desired egg name }
An this is how to install from tag 3.7.0b0 from github via https

# install
pip install git+https://github.com/mongodb/mongo-python-driver.git@3.7.0b0#egg=pymongo

# use pymongo
import pymongo
pymongo.MongoClient()

# MongoClient(host=['localhost:27017'], document_class=dict, tz_aware=False, connect=True)

Tuesday, November 28, 2017

CentOS 7 Postfix relay (gmail)

How to send emails trough a smart relay that uses SASL and TLS

I used:

  • CentOS Linux release 7.3.1611
  • postfix-2.10.1-6.el7.x86_64
The rpm comes from CentOS yum Base.

The setup

File: /etc/postfix/main.cf
This is the main configuration for postfix in regards to how you would like to behave.

smtpd_banner = $myhostname ESMTP $mail_name
biff = no
append_dot_mydomain = no
readme_directory = no
smtpd_tls_session_cache_timeout=3600s
tls_random_source=dev:/dev/urandom
smtpd_tls_session_cache_database = btree:${data_directory}/smtpd_scache
smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache
smtp_sasl_auth_enable = yes
smtp_sasl_security_options = noanonymous
smtp_sasl_tls_security_options = noanonymous
smtp_sasl_password_maps = hash:/etc/postfix/sasl/password
smtp_use_tls = yes
smtp_tls_CAfile = /etc/ssl/certs/ca-bundle.trust.crt
smtp_tls_loglevel = 1
smtp_tls_security_level = encrypt
smtpd_relay_restrictions = permit_mynetworks permit_sasl_authenticated defer_unauth_destination
myhostname = ${OPTIONAL_HOSTNAME}
alias_maps = hash:/etc/aliases
alias_database = hash:/etc/aliases
myorigin = /etc/mailname
mydestination = $myhostname localhost.$mydomain
relayhost = [${mail.RELAY}]:587
mynetworks = 127.0.0.0/8
mailbox_size_limit = 0
recipient_delimiter = +
inet_interfaces = localhost
inet_protocols = ipv4

# comment these two when done
debug_peer_list = ${mail.RELAY}
debug_peer_level = 3

File: /etc/postfix/sasl/password
Write into the file the username and password that you use to authenticate.
[${mail.RELAY}]    ${user@domain}:${PASSWORD}  
Once you save the file you need to create the database, in this case it's hash
cd /etc/postfix/salsl && postmap password
At this point restart postfix
systemctl restart postfix

The problem

Since all that is configured is ok ... you would expect that now you can send email however ...
smtp_sasl_authenticate: mail.RELAY[IPV4]:587: SASL mechanisms PLAIN LOGIN
warning: SASL authentication failure: No worthy mechs found
...
send attr reason = SASL authentication failed; cannot authenticate to server mail.RELAY[IPV4]: no mechanism available 
The main problem is that the username and password works fine ... you can test by using telnet
# First compute the base64 encoded string. \0 is a null terminated string
printf '${user@domain}\0${user@domain}\0${PASSWORD}' | base64

# telnet to the smtp relay

telnet ${mail.RELAY}
EHLO ${OPTIONAL_HOSTNAME}
250-server.example.com
250-PIPELINING
250-SIZE 10240000
250-ETRN
250-AUTH DIGEST-MD5 PLAIN CRAM-MD5
250 8BITMIME
AUTH PLAIN ${COMPUTED_STRING_FROM_PRINTF}
235 Authentication successful
So what is not working ?! Based on the errors we've seen postfix complains that there is no worthy mechs ... that may lead you to read more into the source code. Bottom line since Postfix uses Cyrus SASL library as per Postfix documentation you actually need to install cyrus-sasl-lib
yum install -y  cyrus-sasl cyrus-sasl-lib cyrus-sasl-plain

# restart postfix

systemctl restart postfix 
At this point if you keep the debug on you will see
....
smtp_sasl_authenticate: ${mail.RELAY}[${IPV4}]:587: SASL mechanisms PLAIN LOGIN
xsasl_cyrus_client_get_user: ${user@domain}
xsasl_cyrus_client_get_passwd: ${PASSWORD}
...
... 235 2.7.0 Authentication successful
 
Note: all symbols ${} should be replace with your relevant information. The value of myhostname is optional into /etc/postfix/main.cf if not present postfix uses your hostname.

Wednesday, November 1, 2017

Zabbix server under Selinux (Centos 7)

Zabbix server under Selinux (CentOS 7)

When running zabbix server under Selinux out of the box when you start
systemctl start zabbix-server
you will get an error like this into /var/log/zabbix/zabbix_server.log


 using configuration file: /etc/zabbix/zabbix_server.conf
 cannot set resource limit: [13] Permission denied
 cannot disable core dump, exiting...
 Starting Zabbix Server. Zabbix 3.0.12 (revision 73586).

 

The problem is related to zabbix policy under Selinux.

How to Fix it

First as the message says zabbix server needs to set some resource limits.
To do so will need to have permissions from selinux. Run the following to see the error and transform it into a format that selinux can load later.
cat /var/log/audit/audit.log | grep zabbix_server | grep denied | audit2allow -M zabbix_server.limits

Two files are created a .pp and a .pe. The .pe file should have content similar to

 module zabbi_server.limits 1.0;

require {
        type zabbix_t;
        class process setrlimit;
}

#============= zabbix_t ==============
allow zabbix_t self:process setrlimit;

 

Load this policy with semodule -i zabbix_server.limits.pp

At this point zabbix server can be started systemctl start zabbix-server
If you need to connect to a database such as mysql/postgress you will need to allow zabbix server again ... (note: I used mysql/mariadb)

cat /var/log/audit/audit.log | grep zabbix_server | grep denied | audit2allow -M zabbix_server.ports

This will create again two files, the .pe file should look like

module zabbix_server_ports 1.0;

require {
        type mysqld_port_t;
        type zabbix_t;
        class process setrlimit;
        class tcp_socket name_connect;
}

#============= zabbix_t ==============

#!!!! This avc can be allowed using the boolean 'zabbix_can_network'
allow zabbix_t mysqld_port_t:tcp_socket name_connect;

#!!!! This avc is allowed in the current policy
allow zabbix_t self:process setrlimit;

    
As you can see the setrlimits is already present and you will need to allow the socket access.
To do so semodule -i zabbix_server.ports.pp

At this point you have two policies loaded and you should restart zabbix server systemctl restart zabbix-server
Note: This may apply to any other version of Linux distros/versions that use Selinux though I only tried on CentOS 7.

Friday, February 10, 2017

MongoDB shell - query collections with special characters

From time to time I found in MongoDB collections that have characters that get interpreted by the mongo shell in a different way and you can't use it as is.

Some example: If your collection name is Items:SubItems and you try to query as you would normally do


mongos> db.Items:SubItems.findOne()
2017-02-10T14:11:17.305+0000 E QUERY    SyntaxError: Unexpected token :

The 'fix' is to use a special javascript notation - so this will work
mongos> db['Items:SubItems'].stats()
{
... 
}

This is called 'Square bracket notation' in javascript.
See Property_accessors for more info.

Tuesday, December 6, 2016

Password recovery on Zabbix server UI

In case you need it ...

Obtain access to the database for read/write (for mysql this is what you need)

update zabbix.users set passwd=md5('mynewpassword') where alias='Admin';

Wednesday, November 16, 2016

Netcat HTTP server

Netcat is a very versatile program used for network communications - the place to find it is .

Often I need to test different programs with a dummy HTTP server, so using netcat for this is very easy.

Lt's say you want to respond with HTTP code 200 ... this is what you do with netcat into a shell


 nc -k  -lp 9000 -c 'echo "HTTP/1.1 200 OK\nContent-Length:0\nContent-Type: text/html; charset=utf-8"' -vvv -o session.txt

To explain the switches used:
  • -k accept multiple connections, won't stop netcat after first connection(default)
  • -l listen TCP on the all interfaces
  • -p the port number to bind
  • -c 'echo "HTTP/1.1 200 OK\nContent-Length:0\nContent-Type: text/html; charset=utf-8"' is the most interesting one ... this responds back to the client with a minimal http header and sets code 200 OK
  • -vvv verbosity level
  • -o session.txt netcat will write into this file all the input and output
Now you have a dummy http server running on port 9000 that will answer 200 OK ALL the time :)

Monday, March 28, 2016

Backups with Duplicity and Dropbox

Dropbox is a very popular service for file storage, the way the service works will synchronize by default
all your files across your devices. This is important to know since you will be backing up data into
Dropbox and you don't want to download the backups on every device you have connected.

What we want to do is to backup files, encrypt them and send them to Dropbox.
All this is achieved with Duplicity.

This is the setup

  • Linux OS, any distro will work I guess but I tried on Ubuntu 14.04 LTS
  • Dropbox account (going pro or business is recommended since backups will typical grow over 2GB basic account)

To encrypt files you will need GPG, in case you don't have a key on your system
we need to do a bit of work, if you do have a gpg key you can skip the next section.

GPG Setup

In this section will create GPG public key/private keys that will be used to encrypt the data you backup to Dropbox.


# install
$ sudo apt-get install gnupg
#
# check if you have any keys
#
$ gpg --list-keys
# if this is empty than you need to create a set of keys
# follow the wizard to create keys
#
$ gpg --gen-key
gpg (GnuPG) 1.4.16; Copyright (C) 2013 Free Software Foundation, Inc.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

gpg: keyring `/home/yourname/.gnupg/secring.gpg' created
Please select what kind of key you want:
   (1) RSA and RSA (default)
   (2) DSA and Elgamal
   (3) DSA (sign only)
   (4) RSA (sign only)
Your selection? 1
RSA keys may be between 1024 and 4096 bits long.
What keysize do you want? (2048) 
Requested keysize is 2048 bits
Please specify how long the key should be valid.
         0 = key does not expire
        = key expires in n days
      w = key expires in n weeks
      m = key expires in n months
      y = key expires in n years
Key is valid for? (0) 
Key does not expire at all
Is this correct? (y/N) y

You need a user ID to identify your key; the software constructs the user ID
from the Real Name, Comment and Email Address in this form:
    "Heinrich Heine (Der Dichter) "

Real name: Your Name
Email address: yourname@gmail.com
Comment: 
You selected this USER-ID:
    "Your Name "

Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? O
You need a Passphrase to protect your secret key.

We need to generate a lot of random bytes. It is a good idea to perform
some other action (type on the keyboard, move the mouse, utilize the
disks) during the prime generation; this gives the random number
generator a better chance to gain enough entropy.


....+++++
..+++++
We need to generate a lot of random bytes. It is a good idea to perform
some other action (type on the keyboard, move the mouse, utilize the
disks) during the prime generation; this gives the random number
generator a better chance to gain enough entropy.
+++++

gpg: checking the trustdb
....

#
#
# At this point the keys are created and saved into your keyring
# list keys
#
#
$ gpg --list-keys
/home/yourname/.gnupg/pubring.gpg
--------------------------------
pub   2048R/999B4B79 2016-03-26
            ^^^^^^^^ /used by duplicity
uid                  Your Name 
sub   2048R/99917D12 2016-03-26 

# Note 999B4B79 which is your keyid

Duplicity install

$ sudo apt-get install duplicity

After installation if you are on Ubuntu 14.04 LTS you will need to apply this patch
http://bazaar.launchpad.net/~ed.so/duplicity/fix.dpbx/revision/965#duplicity/backends/dpbxbackend.py
to /usr/lib/python2.7/dist-packages/duplicity/backends/dpbxbackend.py
If you don't know how to apply the patch is simpler to open the file at line 75 and write the following

 72 def command(login_required=True):
 73     """a decorator for handling authentication and exceptions"""
 74     def decorate(f):
 75         def wrapper(self, *args):
 76             from dropbox import rest  ## line to add
 77             if login_required and not self.sess.is_linked():
 78               log.FatalError("dpbx Cannot login: check your credentials",log.ErrorCode.dpbx_nologin)

Dropbox and duplicity setup

You need to have an account first. Open your browser and login.

Backups with duplicity and dropbox

Since this is the first time you run it need to make a authorization token, this is done as follow


$ duplicity --encrypt-key 999B4B79 full SOURCE dpbx:///
------------------------------------------------------------------------
url: https://www.dropbox.com/1/oauth/authorize?oauth_token=TOKEN_HERE
Please authorize in the browser. After you're done, press enter.

Now into your browser authorize the application. This will create an access token into dropbox.
You can see the apps you have going to Security
Should see under Apps linked backend for duplicity
In case you need to know what token is in use you can see it onto you system ~/.dropbox.token_store.txt


Local and Remote metadata are synchronized, no sync needed.
Last full backup date: none
GnuPG passphrase: 
Retype passphrase to confirm: 
--------------[ Backup Statistics ]--------------
StartTime 1459031263.59 (Sat Mar 26 18:27:43 2016)
EndTime 1459031263.73 (Sat Mar 26 18:27:43 2016)
ElapsedTime 0.14 (0.14 seconds)
SourceFiles 2
SourceFileSize 1732720 (1.65 MB)
NewFiles 2
NewFileSize 1732720 (1.65 MB)
DeletedFiles 0
ChangedFiles 0
ChangedFileSize 0 (0 bytes)
ChangedDeltaSize 0 (0 bytes)
DeltaEntries 2
RawDeltaSize 1728624 (1.65 MB)
TotalDestinationSizeChange 388658 (380 KB)
Errors 0
-------------------------------------------------

Backups

When the first full backup finished you can start making incremental backups, list the backups etc.
# list the backup files
duplicity --encrypt-key 999B4B79 list-current-files dpbx:///
#

## Make an incremental backup

duplicity --encrypt-key 999B4B79 incr SOURCE dpbx:///
.....
.....
.....

duplicity --encrypt-key 999B4B79 list-current-files dpbx:///

Troubleshooting

During a backup if you see something like

Attempt 1 failed. NameError: global name 'rest' is not defined
Attempt 2 failed. NameError: global name 'rest' is not defined

See the note about Ubuntu 14.04 because you need to patch the dpbxbackend.py file

Notes

If you use multiple computers and don't want to download from dropbox all
the backups you need to enable selective sync and exclude the Apps/duplicity
folder from Dropbox.
I haven't used duplicity for long time and heard some mix opinions, some say is excellent and some
say has some design flows (didn't checked) where your full backup will be taken after a while even if
you just do incremental. Remains to be seen.
I guess if this doesn't work well I would look into Borg Backup which seems to be the best these days since
has dedup built in and many other features. One thing that doesn't though is many backends as duplicity which
can use pretty much all cloud storage solutions around :).