Skip to content

Encryption

Encryption

How application uses configuration files according to encryption

Encryption is done using age as CLI and golang library for encryption/decryption. Under the hood it uses ChaCha20+Poly1305 as AEAD encryption and store encrypted files with pre-defined header age-encryption.org/v1

When application loads config from any source, at first it check is it encrypted (has header). If it is encrypted then tries to decrypt using keys stored in ENCRYPTION_KEYS env variable and iteratively try every key until successful decryption or skip config. If it wasn't encrypted then return as is.

You can encrypt every config file with separate keys and run application specifying all keys at once as a list with separator: &.

Separator & was chosen to allow pass base64 strings as keys that can encode random keys (binary values).

Encrypt new default config

Prepare JSON config

Let's take next config for example:

{
  "jobs": [
    {
      "type": "slow-loris",
      "args": {
        "address": "127.0.0.1:53",
        "ContentLength": 1000,
        "DialWorkersCount": 1,
        "RampUpInterval": 1,
        "SleepInterval": 1000,
        "DurationSeconds": 1000,
        "Path": "https://example.com"
      }
    }
  ]
}

and save it as /home/user/config.json

Prepare key for encryption

Use some of stored in src/utils/crypto.go.ENCRYPTION_KEYS or generate new one. For example lets use: some long password to encrypt config

Encrypt config

Using make
make DEFAULT_CONFIG=/home/user/config.json encrypt_config

Enter passphrase (leave empty to autogenerate a secure one): some long password to encrypt config
Confirm passphrase: some long password to encrypt config
Saved in file: /tmp/fileMx6JFo
Save value as env variable:
export DEFAULT_CONFIG_VALUE='YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNjcnlwdCAwS1pOUXlLM004L1NxcDRwL21CaUl3IDE4Cjc1cEZtcmZZZHJieFRvd0hhM0RVVVMxb2VMY0RmQzBkUkJQMXE2UWdyMUEKLS0tIHlSK1VFMkNOSHovbzRqUlJ3RW5VclphRy9TU0NQSG8vMzJUZ1c4RUozZncKD6zE4MONozWBfQYn9HG31DW100o2oFpn6iACQAvCDyXkgSeuQtRFjPwCIW5q2Dltq7Srkc8b81/ZynC59uqkmDJefGyNPzTk3ilRl6wcLOhCP1TD7YtCtZ/7ZpoGpNMiDD6XhKnOmz10sBSy1SXt54+zFVcuQ1ITRi4E2WmiFRjTa8T+ZMwurW+F+iwOu6+z8/0sKQaG5SrKA74GI9D6iRQnqiPg2Abr97Vq7X2Fjvz2NqFjcB0dD29XijHcLCdXQ1DcI3gx94SdMmmfeU5ub2ArsH/4nA8XlS7YE7BirUihgHD4/KIr52dc+Fst6i7SBH433d/Y3Pmhi89FHY8+sGyPFXNG+SeLLHafcR6bLLGyk0iGa2bZaBqUGovYNojni8KSrLRPXTgCyeNAOS7Gpamwi1Xco7m7nEEmAv9vpEvtOUx83pGBOkgu3oSV0t3jmp+OUvcwMMQ='

Encrypted config file saved to /tmp/fileMx6JFo file. And can be saved and distributed anywhere.

Using age
age --encrypt -p --output=encrypted_config.json /home/user/config.json
Enter passphrase (leave empty to autogenerate a secure one): some long password to encrypt config
Confirm passphrase: some long password to encrypt config
Converting to base64
cat encrypted_config.json | base64 | tr -d '\n'

YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNjcnlwdCAwS1pOUXlLM004L1NxcDRwL21CaUl3IDE4Cjc1cEZtcmZZZHJieFRvd0hhM0RVVVMxb2VMY0RmQzBkUkJQMXE2UWdyMUEKLS0tIHlSK1VFMkNOSHovbzRqUlJ3RW5VclphRy9TU0NQSG8vMzJUZ1c4RUozZncKD6zE4MONozWBfQYn9HG31DW100o2oFpn6iACQAvCDyXkgSeuQtRFjPwCIW5q2Dltq7Srkc8b81/ZynC59uqkmDJefGyNPzTk3ilRl6wcLOhCP1TD7YtCtZ/7ZpoGpNMiDD6XhKnOmz10sBSy1SXt54+zFVcuQ1ITRi4E2WmiFRjTa8T+ZMwurW+F+iwOu6+z8/0sKQaG5SrKA74GI9D6iRQnqiPg2Abr97Vq7X2Fjvz2NqFjcB0dD29XijHcLCdXQ1DcI3gx94SdMmmfeU5ub2ArsH/4nA8XlS7YE7BirUihgHD4/KIr52dc+Fst6i7SBH433d/Y3Pmhi89FHY8+sGyPFXNG+SeLLHafcR6bLLGyk0iGa2bZaBqUGovYNojni8KSrLRPXTgCyeNAOS7Gpamwi1Xco7m7nEEmAv9vpEvtOUx83pGBOkgu3oSV0t3jmp+OUvcwMMQ=

Encrypt only part of the config

You can encrypt a single job and add it to plaintext config (nothing stopping you from encrypting it further afterwards) via encrypted job entry:

{
  "jobs": [
    {
      "type": "encrypted",
      "args": {
        "format": "json",
        "data": "YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNjcnlwdCB5eCtiMzQ5RWlZRXo4dTNpRE8veHdRIDE4CmYyb2d0YXlnaXptS25sbUJlQUVaUHpRbngwaUdBYUpJRStHbFltdUVNNkUKLS0tIG5oUUVCd041TWJoNWNCQjhvODk4eUFpUldmUFUvaStpanRsdCtWR0RrSVkK2ehc+JYVl+f5VgLKV0mG/J4CQrtHn+FFV5AAcKiLEAjU6MNDaVqBI6Qm9RunLZ51wAA13DLZkPJH39DcsS77H3HmgLpRQ7DMFG2AIDxWysIt2Yi2hVVn9Ogea73twGa8FOpk2kk0Z7NSHCCcpTJd1Db4cwYJiIFaqfBXR+VZtNk3qBgUMStN1CiOyJxvHbnc6tbfeqq042LImKsaLvFzB2y5H/ec9BonHimrP/aZv6dhequs"
      }
    },
    {
      "type": "tcp",
      "count": 100,
      "args": {
        "address": "localhost:9090",
        "body": "more_test",
        "interval_ms": 1000
      }
    }
  ]
}

Where args.format represents the encoding format you used for the data before encryption and args.data is a single ecrypted job.

To encrypt a single job use same steps as to encrypt the whole config but use a file that contains just the job definition:

{
  "type": "tcp",
  "count": 100,
  "args": {
    "address": "localhost:9090",
    "body": "more_test",
    "interval_ms": 1000
  }
}

Note: each single decryption needs at least 256mb of RAM to set up an encryption key (this is implemented by age as hardening against bruteforce attempts). Due to that encrypting multiple jobs separately may be inefficient and it's advised to use parallel job to group multiple independed jobs into one:

{
  "type": "parallel",
  "args": {
    "jobs": [
      {
        "type": "tcp",
        "count": 100,
        "args": {
          "address": "localhost:9090",
          "body": "payload",
          "interval_ms": 1000
        }
      },
      {
        "type": "tcp",
        "count": 100,
        "args": {
          "address": "localhost:9091",
          "body": "payload",
          "interval_ms": 1000
        }
      }
    ]
  }
}

Embedding encrypted config as default backup config into binary

Export env variable as it printed:

export DEFAULT_CONFIG_VALUE='YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNjcnlwdCAwS1pOUXlLM004L1NxcDRwL21CaUl3IDE4Cjc1cEZtcmZZZHJieFRvd0hhM0RVVVMxb2VMY0RmQzBkUkJQMXE2UWdyMUEKLS0tIHlSK1VFMkNOSHovbzRqUlJ3RW5VclphRy9TU0NQSG8vMzJUZ1c4RUozZncKD6zE4MONozWBfQYn9HG31DW100o2oFpn6iACQAvCDyXkgSeuQtRFjPwCIW5q2Dltq7Srkc8b81/ZynC59uqkmDJefGyNPzTk3ilRl6wcLOhCP1TD7YtCtZ/7ZpoGpNMiDD6XhKnOmz10sBSy1SXt54+zFVcuQ1ITRi4E2WmiFRjTa8T+ZMwurW+F+iwOu6+z8/0sKQaG5SrKA74GI9D6iRQnqiPg2Abr97Vq7X2Fjvz2NqFjcB0dD29XijHcLCdXQ1DcI3gx94SdMmmfeU5ub2ArsH/4nA8XlS7YE7BirUihgHD4/KIr52dc+Fst6i7SBH433d/Y3Pmhi89FHY8+sGyPFXNG+SeLLHafcR6bLLGyk0iGa2bZaBqUGovYNojni8KSrLRPXTgCyeNAOS7Gpamwi1Xco7m7nEEmAv9vpEvtOUx83pGBOkgu3oSV0t3jmp+OUvcwMMQ='

This base64 value is encrypted config with specified password (hashed with scrypt against brute force). Build new binary with new config:

make build_encrypted

GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -ldflags="-X 'github.com/Arriven/db1000n/src/job/config.DefaultConfig=YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNjcnlwdCAwS1pOUXlLM004L1NxcDRwL21CaUl3IDE4Cjc1cEZtcmZZZHJieFRvd0hhM0RVVVMxb2VMY0RmQzBkUkJQMXE2UWdyMUEKLS0tIHlSK1VFMkNOSHovbzRqUlJ3RW5VclphRy9TU0NQSG8vMzJUZ1c4RUozZncKD6zE4MONozWBfQYn9HG31DW100o2oFpn6iACQAvCDyXkgSeuQtRFjPwCIW5q2Dltq7Srkc8b81/ZynC59uqkmDJefGyNPzTk3ilRl6wcLOhCP1TD7YtCtZ/7ZpoGpNMiDD6XhKnOmz10sBSy1SXt54+zFVcuQ1ITRi4E2WmiFRjTa8T+ZMwurW+F+iwOu6+z8/0sKQaG5SrKA74GI9D6iRQnqiPg2Abr97Vq7X2Fjvz2NqFjcB0dD29XijHcLCdXQ1DcI3gx94SdMmmfeU5ub2ArsH/4nA8XlS7YE7BirUihgHD4/KIr52dc+Fst6i7SBH433d/Y3Pmhi89FHY8+sGyPFXNG+SeLLHafcR6bLLGyk0iGa2bZaBqUGovYNojni8KSrLRPXTgCyeNAOS7Gpamwi1Xco7m7nEEmAv9vpEvtOUx83pGBOkgu3oSV0t3jmp+OUvcwMMQ='" -o main ./main.go

Your new binary saved as main with new encrypted and embedded default config. To turn on decryption new config you should pass encryption keys as list of keys separated with & symbol:

export ENCRYPTION_KEYS='some long password to encrypt config&another key'

It will override default encryption keys

Embedding encryption keys into binary

You can embed keys into binary in same way:

export ENCRYPTION_KEYS='some long password to encrypt config&another key'
make build_encrypted

GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -ldflags="-X 'github.com/Arriven/db1000n/src/utils.EncryptionKeys=some long password to encrypt config&another key' -X 'github.com/Arriven/db1000n/src/job/config.DefaultConfig=YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNjcnlwdCAwS1pOUXlLM004L1NxcDRwL21CaUl3IDE4Cjc1cEZtcmZZZHJieFRvd0hhM0RVVVMxb2VMY0RmQzBkUkJQMXE2UWdyMUEKLS0tIHlSK1VFMkNOSHovbzRqUlJ3RW5VclphRy9TU0NQSG8vMzJUZ1c4RUozZncKD6zE4MONozWBfQYn9HG31DW100o2oFpn6iACQAvCDyXkgSeuQtRFjPwCIW5q2Dltq7Srkc8b81/ZynC59uqkmDJefGyNPzTk3ilRl6wcLOhCP1TD7YtCtZ/7ZpoGpNMiDD6XhKnOmz10sBSy1SXt54+zFVcuQ1ITRi4E2WmiFRjTa8T+ZMwurW+F+iwOu6+z8/0sKQaG5SrKA74GI9D6iRQnqiPg2Abr97Vq7X2Fjvz2NqFjcB0dD29XijHcLCdXQ1DcI3gx94SdMmmfeU5ub2ArsH/4nA8XlS7YE7BirUihgHD4/KIr52dc+Fst6i7SBH433d/Y3Pmhi89FHY8+sGyPFXNG+SeLLHafcR6bLLGyk0iGa2bZaBqUGovYNojni8KSrLRPXTgCyeNAOS7Gpamwi1Xco7m7nEEmAv9vpEvtOUx83pGBOkgu3oSV0t3jmp+OUvcwMMQ='" -o main ./main.go