Compare commits
No commits in common. "781c9479051ec7a096a36f51b96030b8cd49b165" and "df9fd3da41d367e8c44b962bd32d20c247ba8fc1" have entirely different histories.
781c947905
...
df9fd3da41
64 changed files with 1776 additions and 472 deletions
1
.envrc
1
.envrc
|
@ -1 +0,0 @@
|
|||
use flake
|
43
.github/workflows/build-image.yml
vendored
43
.github/workflows/build-image.yml
vendored
|
@ -1,43 +0,0 @@
|
|||
name: Build Image
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: ['master']
|
||||
|
||||
env:
|
||||
REGISTRY: ghcr.io
|
||||
IMAGE_NAME: ${{ github.repository }}
|
||||
|
||||
jobs:
|
||||
build-and-push-image:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Log in to the Container registry
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Extract metadata (tags, labels) for Docker
|
||||
id: meta
|
||||
uses: docker/metadata-action@v3
|
||||
with:
|
||||
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
||||
|
||||
- name: Build and push Docker image
|
||||
uses: docker/build-push-action@v2
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
7
.gitignore
vendored
7
.gitignore
vendored
|
@ -1,8 +1,7 @@
|
|||
public/
|
||||
resources/
|
||||
|
||||
.DS_Store
|
||||
_site
|
||||
.jekyll-assets-cache
|
||||
_drafts
|
||||
*~
|
||||
*.swp
|
||||
*.swo
|
||||
.hugo_build.lock
|
||||
|
|
3
.gitmodules
vendored
3
.gitmodules
vendored
|
@ -1,3 +0,0 @@
|
|||
[submodule "themes/terminal"]
|
||||
path = themes/terminal
|
||||
url = https://github.com/panr/hugo-theme-terminal.git
|
9
404.html
Normal file
9
404.html
Normal file
|
@ -0,0 +1,9 @@
|
|||
---
|
||||
layout: default
|
||||
title: "404: Page not found"
|
||||
---
|
||||
|
||||
<div class="page">
|
||||
<h1 class="page-title">404: Page not found</h1>
|
||||
<p class="lead">Sorry, we've misplaced that URL or it's pointing to something that doesn't exist. <a href="{{ site.baseurl }}">Head back home</a> to try finding it again.</p>
|
||||
</div>
|
|
@ -1,5 +0,0 @@
|
|||
FROM klakegg/hugo:ext-alpine-onbuild AS hugo
|
||||
|
||||
FROM nginx:mainline-alpine
|
||||
|
||||
COPY --from=hugo /target /usr/share/nginx/html
|
8
Gemfile
Normal file
8
Gemfile
Normal file
|
@ -0,0 +1,8 @@
|
|||
source 'https://rubygems.org'
|
||||
|
||||
gem 'jekyll', '~> 2.5.3'
|
||||
|
||||
group :extensions do
|
||||
gem 'jekyll-assets', '~> 0.14.0'
|
||||
gem 'yui-compressor', '~> 0.12.0'
|
||||
end
|
99
Gemfile.lock
Normal file
99
Gemfile.lock
Normal file
|
@ -0,0 +1,99 @@
|
|||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
addressable (2.3.8)
|
||||
blankslate (2.1.2.4)
|
||||
celluloid (0.16.0)
|
||||
timers (~> 4.0.0)
|
||||
classifier-reborn (2.0.3)
|
||||
fast-stemmer (~> 1.0)
|
||||
coffee-script (2.4.1)
|
||||
coffee-script-source
|
||||
execjs
|
||||
coffee-script-source (1.9.1.1)
|
||||
colorator (0.1)
|
||||
execjs (2.5.2)
|
||||
fast-stemmer (1.0.2)
|
||||
fastimage (1.7.0)
|
||||
addressable (~> 2.3, >= 2.3.5)
|
||||
ffi (1.9.10)
|
||||
hike (1.2.3)
|
||||
hitimes (1.2.2)
|
||||
jekyll (2.5.3)
|
||||
classifier-reborn (~> 2.0)
|
||||
colorator (~> 0.1)
|
||||
jekyll-coffeescript (~> 1.0)
|
||||
jekyll-gist (~> 1.0)
|
||||
jekyll-paginate (~> 1.0)
|
||||
jekyll-sass-converter (~> 1.0)
|
||||
jekyll-watch (~> 1.1)
|
||||
kramdown (~> 1.3)
|
||||
liquid (~> 2.6.1)
|
||||
mercenary (~> 0.3.3)
|
||||
pygments.rb (~> 0.6.0)
|
||||
redcarpet (~> 3.1)
|
||||
safe_yaml (~> 1.0)
|
||||
toml (~> 0.1.0)
|
||||
jekyll-assets (0.14.0)
|
||||
fastimage (~> 1.6)
|
||||
jekyll (~> 2.0)
|
||||
mini_magick (~> 4.1)
|
||||
sass (~> 3.2)
|
||||
sprockets (~> 2.10)
|
||||
sprockets-helpers
|
||||
sprockets-sass
|
||||
jekyll-coffeescript (1.0.1)
|
||||
coffee-script (~> 2.2)
|
||||
jekyll-gist (1.2.1)
|
||||
jekyll-paginate (1.1.0)
|
||||
jekyll-sass-converter (1.3.0)
|
||||
sass (~> 3.2)
|
||||
jekyll-watch (1.2.1)
|
||||
listen (~> 2.7)
|
||||
kramdown (1.8.0)
|
||||
liquid (2.6.2)
|
||||
listen (2.10.1)
|
||||
celluloid (~> 0.16.0)
|
||||
rb-fsevent (>= 0.9.3)
|
||||
rb-inotify (>= 0.9)
|
||||
mercenary (0.3.5)
|
||||
mini_magick (4.2.7)
|
||||
multi_json (1.11.2)
|
||||
parslet (1.5.0)
|
||||
blankslate (~> 2.0)
|
||||
posix-spawn (0.3.11)
|
||||
pygments.rb (0.6.3)
|
||||
posix-spawn (~> 0.3.6)
|
||||
yajl-ruby (~> 1.2.0)
|
||||
rack (1.6.4)
|
||||
rb-fsevent (0.9.5)
|
||||
rb-inotify (0.9.5)
|
||||
ffi (>= 0.5.0)
|
||||
redcarpet (3.3.2)
|
||||
safe_yaml (1.0.4)
|
||||
sass (3.4.16)
|
||||
sprockets (2.12.4)
|
||||
hike (~> 1.2)
|
||||
multi_json (~> 1.0)
|
||||
rack (~> 1.0)
|
||||
tilt (~> 1.1, != 1.3.0)
|
||||
sprockets-helpers (1.2.1)
|
||||
sprockets (>= 2.2)
|
||||
sprockets-sass (1.3.1)
|
||||
sprockets (~> 2.0)
|
||||
tilt (~> 1.1)
|
||||
tilt (1.4.1)
|
||||
timers (4.0.1)
|
||||
hitimes
|
||||
toml (0.1.2)
|
||||
parslet (~> 1.5.0)
|
||||
yajl-ruby (1.2.1)
|
||||
yui-compressor (0.12.0)
|
||||
|
||||
PLATFORMS
|
||||
ruby
|
||||
|
||||
DEPENDENCIES
|
||||
jekyll (~> 2.5.3)
|
||||
jekyll-assets (~> 0.14.0)
|
||||
yui-compressor (~> 0.12.0)
|
29
Makefile
Normal file
29
Makefile
Normal file
|
@ -0,0 +1,29 @@
|
|||
all: compile compress upload superfeedr
|
||||
|
||||
compile: clean
|
||||
@echo "=== Generating static files"
|
||||
@bundle exec jekyll build --lsi
|
||||
@echo "Done."
|
||||
|
||||
compress:
|
||||
@echo "=== Compressing generated files"
|
||||
@find ./_site -type f | xargs zopfli --gzip --i30
|
||||
@echo "Done."
|
||||
|
||||
upload:
|
||||
@echo "=== Syncing files"
|
||||
@rsync -avz --no-o --no-g -e ssh --chmod=og=r -p --delete _site/ kempkens.io:/iocage/jails/506fd9f8-15c0-11e5-adf5-477a0b920463/root/var/www/blog
|
||||
@echo "Done."
|
||||
@echo "=== Changing permissions"
|
||||
@ssh kempkens.io chmod 755 /iocage/jails/506fd9f8-15c0-11e5-adf5-477a0b920463/root/var/www/blog
|
||||
@ssh kempkens.io find /iocage/jails/506fd9f8-15c0-11e5-adf5-477a0b920463/root/var/www/blog -type d -exec chmod 755 {} +
|
||||
@echo "Done."
|
||||
|
||||
superfeedr:
|
||||
@echo "=== Notifying Superfeedr"
|
||||
@curl -X POST https://kempkens.superfeedr.com -d "hub.mode=publish" -d "hub.url=https://blog.kempkens.io/feed.xml"
|
||||
@curl -X POST https://kempkens.superfeedr.com -d "hub.mode=publish" -d "hub.url=https://blog.kempkens.io/feed-with-links.xml"
|
||||
@echo "Done."
|
||||
|
||||
clean:
|
||||
@rm -rf ./_site
|
Before Width: | Height: | Size: 228 KiB After Width: | Height: | Size: 228 KiB |
Before Width: | Height: | Size: 80 KiB After Width: | Height: | Size: 80 KiB |
5
_assets/stylesheets/app.css
Normal file
5
_assets/stylesheets/app.css
Normal file
|
@ -0,0 +1,5 @@
|
|||
/*
|
||||
*= require poole
|
||||
*= require syntax
|
||||
*= require hyde
|
||||
*/
|
250
_assets/stylesheets/hyde.css
Normal file
250
_assets/stylesheets/hyde.css
Normal file
|
@ -0,0 +1,250 @@
|
|||
/*
|
||||
* __ __
|
||||
* /\ \ /\ \
|
||||
* \ \ \___ __ __ \_\ \ __
|
||||
* \ \ _ `\/\ \/\ \ /'_` \ /'__`\
|
||||
* \ \ \ \ \ \ \_\ \/\ \_\ \/\ __/
|
||||
* \ \_\ \_\/`____ \ \___,_\ \____\
|
||||
* \/_/\/_/`/___/> \/__,_ /\/____/
|
||||
* /\___/
|
||||
* \/__/
|
||||
*
|
||||
* Designed, built, and released under MIT license by @mdo. Learn more at
|
||||
* https://github.com/poole/hyde.
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* Contents
|
||||
*
|
||||
* Global resets
|
||||
* Sidebar
|
||||
* Container
|
||||
* Reverse layout
|
||||
* Themes
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* Global resets
|
||||
*
|
||||
* Update the foundational and global aspects of the page.
|
||||
*/
|
||||
|
||||
html {
|
||||
font-family: "PT Sans", Helvetica, Arial, sans-serif;
|
||||
}
|
||||
@media (min-width: 48em) {
|
||||
html {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
@media (min-width: 58em) {
|
||||
html {
|
||||
font-size: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Sidebar
|
||||
*
|
||||
* Flexible banner for housing site name, intro, and "footer" content. Starts
|
||||
* out above content in mobile and later moves to the side with wider viewports.
|
||||
*/
|
||||
|
||||
.sidebar {
|
||||
text-align: center;
|
||||
padding: 2rem 1rem;
|
||||
color: rgba(255,255,255,.5);
|
||||
background-color: #202020;
|
||||
}
|
||||
@media (min-width: 48em) {
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
width: 18rem;
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
|
||||
/* Sidebar links */
|
||||
.sidebar a {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
/* About section */
|
||||
.sidebar-about h1 {
|
||||
color: #fff;
|
||||
margin-top: 0;
|
||||
font-family: "Abril Fatface", serif;
|
||||
font-size: 3.25rem;
|
||||
}
|
||||
|
||||
/* Sidebar nav */
|
||||
.sidebar-nav {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.sidebar-nav-item {
|
||||
display: block;
|
||||
line-height: 1.75;
|
||||
}
|
||||
a.sidebar-nav-item:hover,
|
||||
a.sidebar-nav-item:focus {
|
||||
text-decoration: underline;
|
||||
}
|
||||
.sidebar-nav-item.active {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* Sticky sidebar
|
||||
*
|
||||
* Add the `sidebar-sticky` class to the sidebar's container to affix it the
|
||||
* contents to the bottom of the sidebar in tablets and up.
|
||||
*/
|
||||
|
||||
@media (min-width: 48em) {
|
||||
.sidebar-sticky {
|
||||
position: absolute;
|
||||
right: 1rem;
|
||||
bottom: 1rem;
|
||||
left: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Container
|
||||
*
|
||||
* Align the contents of the site above the proper threshold with some margin-fu
|
||||
* with a 25%-wide `.sidebar`.
|
||||
*/
|
||||
|
||||
.content {
|
||||
padding-top: 4rem;
|
||||
padding-bottom: 4rem;
|
||||
}
|
||||
|
||||
@media (min-width: 48em) {
|
||||
.content {
|
||||
max-width: 38rem;
|
||||
margin-left: 20rem;
|
||||
margin-right: 2rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 64em) {
|
||||
.content {
|
||||
margin-left: 22rem;
|
||||
margin-right: 4rem;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Reverse layout
|
||||
*
|
||||
* Flip the orientation of the page by placing the `.sidebar` on the right.
|
||||
*/
|
||||
|
||||
@media (min-width: 48em) {
|
||||
.layout-reverse .sidebar {
|
||||
left: auto;
|
||||
right: 0;
|
||||
}
|
||||
.layout-reverse .content {
|
||||
margin-left: 2rem;
|
||||
margin-right: 20rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 64em) {
|
||||
.layout-reverse .content {
|
||||
margin-left: 4rem;
|
||||
margin-right: 22rem;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Themes
|
||||
*
|
||||
* As of v1.1, Hyde includes optional themes to color the sidebar and links
|
||||
* within blog posts. To use, add the class of your choosing to the `body`.
|
||||
*/
|
||||
|
||||
/* Base16 (http://chriskempson.github.io/base16/#default) */
|
||||
|
||||
/* Red */
|
||||
.theme-base-08 .sidebar {
|
||||
background-color: #ac4142;
|
||||
}
|
||||
.theme-base-08 .content a,
|
||||
.theme-base-08 .related-posts li a:hover {
|
||||
color: #ac4142;
|
||||
}
|
||||
|
||||
/* Orange */
|
||||
.theme-base-09 .sidebar {
|
||||
background-color: #d28445;
|
||||
}
|
||||
.theme-base-09 .content a,
|
||||
.theme-base-09 .related-posts li a:hover {
|
||||
color: #d28445;
|
||||
}
|
||||
|
||||
/* Yellow */
|
||||
.theme-base-0a .sidebar {
|
||||
background-color: #f4bf75;
|
||||
}
|
||||
.theme-base-0a .content a,
|
||||
.theme-base-0a .related-posts li a:hover {
|
||||
color: #f4bf75;
|
||||
}
|
||||
|
||||
/* Green */
|
||||
.theme-base-0b .sidebar {
|
||||
background-color: #90a959;
|
||||
}
|
||||
.theme-base-0b .content a,
|
||||
.theme-base-0b .related-posts li a:hover {
|
||||
color: #90a959;
|
||||
}
|
||||
|
||||
/* Cyan */
|
||||
.theme-base-0c .sidebar {
|
||||
background-color: #75b5aa;
|
||||
}
|
||||
.theme-base-0c .content a,
|
||||
.theme-base-0c .related-posts li a:hover {
|
||||
color: #75b5aa;
|
||||
}
|
||||
|
||||
/* Blue */
|
||||
.theme-base-0d .sidebar {
|
||||
background-color: #6a9fb5;
|
||||
}
|
||||
.theme-base-0d .content a,
|
||||
.theme-base-0d .related-posts li a:hover {
|
||||
color: #6a9fb5;
|
||||
}
|
||||
|
||||
/* Magenta */
|
||||
.theme-base-0e .sidebar {
|
||||
background-color: #aa759f;
|
||||
}
|
||||
.theme-base-0e .content a,
|
||||
.theme-base-0e .related-posts li a:hover {
|
||||
color: #aa759f;
|
||||
}
|
||||
|
||||
/* Brown */
|
||||
.theme-base-0f .sidebar {
|
||||
background-color: #8f5536;
|
||||
}
|
||||
.theme-base-0f .content a,
|
||||
.theme-base-0f .related-posts li a:hover {
|
||||
color: #8f5536;
|
||||
}
|
465
_assets/stylesheets/poole.css
Normal file
465
_assets/stylesheets/poole.css
Normal file
|
@ -0,0 +1,465 @@
|
|||
/*
|
||||
* ___
|
||||
* /\_ \
|
||||
* _____ ___ ___\//\ \ __
|
||||
* /\ '__`\ / __`\ / __`\\ \ \ /'__`\
|
||||
* \ \ \_\ \/\ \_\ \/\ \_\ \\_\ \_/\ __/
|
||||
* \ \ ,__/\ \____/\ \____//\____\ \____\
|
||||
* \ \ \/ \/___/ \/___/ \/____/\/____/
|
||||
* \ \_\
|
||||
* \/_/
|
||||
*
|
||||
* Designed, built, and released under MIT license by @mdo. Learn more at
|
||||
* https://github.com/poole/poole.
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* Contents
|
||||
*
|
||||
* Body resets
|
||||
* Custom type
|
||||
* Messages
|
||||
* Container
|
||||
* Masthead
|
||||
* Posts and pages
|
||||
* Pagination
|
||||
* Reverse layout
|
||||
* Themes
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* Body resets
|
||||
*
|
||||
* Update the foundational and global aspects of the page.
|
||||
*/
|
||||
|
||||
* {
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
html {
|
||||
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
font-size: 16px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
@media (min-width: 38em) {
|
||||
html {
|
||||
font-size: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
body {
|
||||
color: #515151;
|
||||
background-color: #fff;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
-ms-text-size-adjust: 100%;
|
||||
}
|
||||
|
||||
/* No `:visited` state is required by default (browsers will use `a`) */
|
||||
a {
|
||||
color: #268bd2;
|
||||
text-decoration: none;
|
||||
}
|
||||
a strong {
|
||||
color: inherit;
|
||||
}
|
||||
/* `:focus` is linked to `:hover` for basic accessibility */
|
||||
a:hover,
|
||||
a:focus {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
/* Headings */
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
margin-bottom: .5rem;
|
||||
font-weight: bold;
|
||||
line-height: 1.25;
|
||||
color: #313131;
|
||||
text-rendering: optimizeLegibility;
|
||||
}
|
||||
h1 {
|
||||
font-size: 2rem;
|
||||
}
|
||||
h2 {
|
||||
margin-top: 1rem;
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
h3 {
|
||||
margin-top: 1.5rem;
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
h4, h5, h6 {
|
||||
margin-top: 1rem;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
/* Body text */
|
||||
p {
|
||||
margin-top: 0;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
strong {
|
||||
color: #303030;
|
||||
}
|
||||
|
||||
|
||||
/* Lists */
|
||||
ul, ol, dl {
|
||||
margin-top: 0;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
dt {
|
||||
font-weight: bold;
|
||||
}
|
||||
dd {
|
||||
margin-bottom: .5rem;
|
||||
}
|
||||
|
||||
/* Misc */
|
||||
hr {
|
||||
position: relative;
|
||||
margin: 1.5rem 0;
|
||||
border: 0;
|
||||
border-top: 1px solid #eee;
|
||||
border-bottom: 1px solid #fff;
|
||||
}
|
||||
|
||||
abbr {
|
||||
font-size: 85%;
|
||||
font-weight: bold;
|
||||
color: #555;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
abbr[title] {
|
||||
cursor: help;
|
||||
border-bottom: 1px dotted #e5e5e5;
|
||||
}
|
||||
|
||||
/* Code */
|
||||
code,
|
||||
pre {
|
||||
font-family: Menlo, Monaco, "Courier New", monospace;
|
||||
}
|
||||
code {
|
||||
padding: .25em .5em;
|
||||
font-size: 85%;
|
||||
color: #bf616a;
|
||||
background-color: #f9f9f9;
|
||||
border-radius: 3px;
|
||||
}
|
||||
pre {
|
||||
display: block;
|
||||
margin-top: 0;
|
||||
margin-bottom: 1rem;
|
||||
padding: 1rem;
|
||||
font-size: .8rem;
|
||||
line-height: 1.4;
|
||||
white-space: pre;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-all;
|
||||
word-wrap: break-word;
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
pre code {
|
||||
padding: 0;
|
||||
font-size: 100%;
|
||||
color: inherit;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
/* Pygments via Jekyll */
|
||||
.highlight {
|
||||
margin-bottom: 1rem;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.highlight pre {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
/* Gist via GitHub Pages */
|
||||
.gist .gist-file {
|
||||
font-family: Menlo, Monaco, "Courier New", monospace !important;
|
||||
}
|
||||
.gist .markdown-body {
|
||||
padding: 15px;
|
||||
}
|
||||
.gist pre {
|
||||
padding: 0;
|
||||
background-color: transparent;
|
||||
}
|
||||
.gist .gist-file .gist-data {
|
||||
font-size: .8rem !important;
|
||||
line-height: 1.4;
|
||||
}
|
||||
.gist code {
|
||||
padding: 0;
|
||||
color: inherit;
|
||||
background-color: transparent;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
/* Quotes */
|
||||
blockquote {
|
||||
padding: .5rem 1rem;
|
||||
margin: .8rem 0;
|
||||
color: #7a7a7a;
|
||||
border-left: .25rem solid #e5e5e5;
|
||||
}
|
||||
blockquote p:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
@media (min-width: 30em) {
|
||||
blockquote {
|
||||
padding-right: 5rem;
|
||||
padding-left: 1.25rem;
|
||||
}
|
||||
}
|
||||
|
||||
img {
|
||||
display: block;
|
||||
max-width: 100%;
|
||||
margin: 0 0 1rem;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
/* Tables */
|
||||
table {
|
||||
margin-bottom: 1rem;
|
||||
width: 100%;
|
||||
border: 1px solid #e5e5e5;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
td,
|
||||
th {
|
||||
padding: .25rem .5rem;
|
||||
border: 1px solid #e5e5e5;
|
||||
}
|
||||
tbody tr:nth-child(odd) td,
|
||||
tbody tr:nth-child(odd) th {
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Custom type
|
||||
*
|
||||
* Extend paragraphs with `.lead` for larger introductory text.
|
||||
*/
|
||||
|
||||
.lead {
|
||||
font-size: 1.25rem;
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Messages
|
||||
*
|
||||
* Show alert messages to users. You may add it to single elements like a `<p>`,
|
||||
* or to a parent if there are multiple elements to show.
|
||||
*/
|
||||
|
||||
.message {
|
||||
margin-bottom: 1rem;
|
||||
padding: 1rem;
|
||||
color: #717171;
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Container
|
||||
*
|
||||
* Center the page content.
|
||||
*/
|
||||
|
||||
.container {
|
||||
max-width: 38rem;
|
||||
padding-left: 1rem;
|
||||
padding-right: 1rem;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Masthead
|
||||
*
|
||||
* Super small header above the content for site name and short description.
|
||||
*/
|
||||
|
||||
.masthead {
|
||||
padding-top: 1rem;
|
||||
padding-bottom: 1rem;
|
||||
margin-bottom: 3rem;
|
||||
}
|
||||
.masthead-title {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
color: #505050;
|
||||
}
|
||||
.masthead-title a {
|
||||
color: #505050;
|
||||
}
|
||||
.masthead-title small {
|
||||
font-size: 75%;
|
||||
font-weight: 400;
|
||||
color: #c0c0c0;
|
||||
letter-spacing: 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Posts and pages
|
||||
*
|
||||
* Each post is wrapped in `.post` and is used on default and post layouts. Each
|
||||
* page is wrapped in `.page` and is only used on the page layout.
|
||||
*/
|
||||
|
||||
.page,
|
||||
.post {
|
||||
margin-bottom: 4em;
|
||||
}
|
||||
|
||||
/* Blog post or page title */
|
||||
.page-title,
|
||||
.post-title,
|
||||
.post-title a {
|
||||
color: #303030;
|
||||
}
|
||||
.page-title,
|
||||
.post-title {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
/* Meta data line below post title */
|
||||
.post-date {
|
||||
display: block;
|
||||
margin-top: -.5rem;
|
||||
margin-bottom: 1rem;
|
||||
color: #9a9a9a;
|
||||
}
|
||||
|
||||
/* Related posts */
|
||||
.related {
|
||||
padding-top: 2rem;
|
||||
padding-bottom: 2rem;
|
||||
border-top: 1px solid #eee;
|
||||
}
|
||||
.related-posts {
|
||||
padding-left: 0;
|
||||
list-style: none;
|
||||
}
|
||||
.related-posts h3 {
|
||||
margin-top: 0;
|
||||
}
|
||||
.related-posts li small {
|
||||
font-size: 75%;
|
||||
color: #999;
|
||||
}
|
||||
.related-posts li a:hover {
|
||||
color: #268bd2;
|
||||
text-decoration: none;
|
||||
}
|
||||
.related-posts li a:hover small {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Pagination
|
||||
*
|
||||
* Super lightweight (HTML-wise) blog pagination. `span`s are provide for when
|
||||
* there are no more previous or next posts to show.
|
||||
*/
|
||||
|
||||
.pagination {
|
||||
overflow: hidden; /* clearfix */
|
||||
margin-left: -1rem;
|
||||
margin-right: -1rem;
|
||||
font-family: "PT Sans", Helvetica, Arial, sans-serif;
|
||||
color: #ccc;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* Pagination items can be `span`s or `a`s */
|
||||
.pagination-item {
|
||||
display: block;
|
||||
padding: 1rem;
|
||||
border: 1px solid #eee;
|
||||
}
|
||||
.pagination-item:first-child {
|
||||
margin-bottom: -1px;
|
||||
}
|
||||
|
||||
/* Only provide a hover state for linked pagination items */
|
||||
a.pagination-item:hover {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
|
||||
/* Custom */
|
||||
.post-date a {
|
||||
color: #9a9a9a;
|
||||
}
|
||||
.post-date a:hover {
|
||||
color: #303030;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.post-link > span {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.footer {
|
||||
margin-top: 3rem;
|
||||
font-size: 75%;
|
||||
}
|
||||
.footer-links {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style-type: none;
|
||||
text-align: center;
|
||||
}
|
||||
.footer-links li {
|
||||
display: inline;
|
||||
}
|
||||
.footer-links li.divider:after {
|
||||
content: "|";
|
||||
color: #ccc;
|
||||
}
|
||||
.footer-links li a {
|
||||
color: #515151;
|
||||
}
|
||||
|
||||
@media (min-width: 30em) {
|
||||
.pagination {
|
||||
margin: 3rem 0;
|
||||
}
|
||||
.pagination-item {
|
||||
float: left;
|
||||
width: 50%;
|
||||
}
|
||||
.pagination-item:first-child {
|
||||
margin-bottom: 0;
|
||||
border-top-left-radius: 4px;
|
||||
border-bottom-left-radius: 4px;
|
||||
}
|
||||
.pagination-item:last-child {
|
||||
margin-left: -1px;
|
||||
border-top-right-radius: 4px;
|
||||
border-bottom-right-radius: 4px;
|
||||
}
|
||||
}
|
65
_assets/stylesheets/syntax.css
Normal file
65
_assets/stylesheets/syntax.css
Normal file
|
@ -0,0 +1,65 @@
|
|||
.highlight .hll { background-color: #ffc; }
|
||||
.highlight .c { color: #999; } /* Comment */
|
||||
.highlight .err { color: #a00; background-color: #faa } /* Error */
|
||||
.highlight .k { color: #069; } /* Keyword */
|
||||
.highlight .o { color: #555 } /* Operator */
|
||||
.highlight .cm { color: #09f; font-style: italic } /* Comment.Multiline */
|
||||
.highlight .cp { color: #099 } /* Comment.Preproc */
|
||||
.highlight .c1 { color: #999; } /* Comment.Single */
|
||||
.highlight .cs { color: #999; } /* Comment.Special */
|
||||
.highlight .gd { background-color: #fcc; border: 1px solid #c00 } /* Generic.Deleted */
|
||||
.highlight .ge { font-style: italic } /* Generic.Emph */
|
||||
.highlight .gr { color: #f00 } /* Generic.Error */
|
||||
.highlight .gh { color: #030; } /* Generic.Heading */
|
||||
.highlight .gi { background-color: #cfc; border: 1px solid #0c0 } /* Generic.Inserted */
|
||||
.highlight .go { color: #aaa } /* Generic.Output */
|
||||
.highlight .gp { color: #009; } /* Generic.Prompt */
|
||||
.highlight .gs { } /* Generic.Strong */
|
||||
.highlight .gu { color: #030; } /* Generic.Subheading */
|
||||
.highlight .gt { color: #9c6 } /* Generic.Traceback */
|
||||
.highlight .kc { color: #069; } /* Keyword.Constant */
|
||||
.highlight .kd { color: #069; } /* Keyword.Declaration */
|
||||
.highlight .kn { color: #069; } /* Keyword.Namespace */
|
||||
.highlight .kp { color: #069 } /* Keyword.Pseudo */
|
||||
.highlight .kr { color: #069; } /* Keyword.Reserved */
|
||||
.highlight .kt { color: #078; } /* Keyword.Type */
|
||||
.highlight .m { color: #f60 } /* Literal.Number */
|
||||
.highlight .s { color: #d44950 } /* Literal.String */
|
||||
.highlight .na { color: #4f9fcf } /* Name.Attribute */
|
||||
.highlight .nb { color: #366 } /* Name.Builtin */
|
||||
.highlight .nc { color: #0a8; } /* Name.Class */
|
||||
.highlight .no { color: #360 } /* Name.Constant */
|
||||
.highlight .nd { color: #99f } /* Name.Decorator */
|
||||
.highlight .ni { color: #999; } /* Name.Entity */
|
||||
.highlight .ne { color: #c00; } /* Name.Exception */
|
||||
.highlight .nf { color: #c0f } /* Name.Function */
|
||||
.highlight .nl { color: #99f } /* Name.Label */
|
||||
.highlight .nn { color: #0cf; } /* Name.Namespace */
|
||||
.highlight .nt { color: #2f6f9f; } /* Name.Tag */
|
||||
.highlight .nv { color: #033 } /* Name.Variable */
|
||||
.highlight .ow { color: #000; } /* Operator.Word */
|
||||
.highlight .w { color: #bbb } /* Text.Whitespace */
|
||||
.highlight .mf { color: #f60 } /* Literal.Number.Float */
|
||||
.highlight .mh { color: #f60 } /* Literal.Number.Hex */
|
||||
.highlight .mi { color: #f60 } /* Literal.Number.Integer */
|
||||
.highlight .mo { color: #f60 } /* Literal.Number.Oct */
|
||||
.highlight .sb { color: #c30 } /* Literal.String.Backtick */
|
||||
.highlight .sc { color: #c30 } /* Literal.String.Char */
|
||||
.highlight .sd { color: #c30; font-style: italic } /* Literal.String.Doc */
|
||||
.highlight .s2 { color: #c30 } /* Literal.String.Double */
|
||||
.highlight .se { color: #c30; } /* Literal.String.Escape */
|
||||
.highlight .sh { color: #c30 } /* Literal.String.Heredoc */
|
||||
.highlight .si { color: #a00 } /* Literal.String.Interpol */
|
||||
.highlight .sx { color: #c30 } /* Literal.String.Other */
|
||||
.highlight .sr { color: #3aa } /* Literal.String.Regex */
|
||||
.highlight .s1 { color: #c30 } /* Literal.String.Single */
|
||||
.highlight .ss { color: #fc3 } /* Literal.String.Symbol */
|
||||
.highlight .bp { color: #366 } /* Name.Builtin.Pseudo */
|
||||
.highlight .vc { color: #033 } /* Name.Variable.Class */
|
||||
.highlight .vg { color: #033 } /* Name.Variable.Global */
|
||||
.highlight .vi { color: #033 } /* Name.Variable.Instance */
|
||||
.highlight .il { color: #f60 } /* Literal.Number.Integer.Long */
|
||||
|
||||
.css .o,
|
||||
.css .o + .nt,
|
||||
.css .nt + .nt { color: #999; }
|
38
_config.yml
Normal file
38
_config.yml
Normal file
|
@ -0,0 +1,38 @@
|
|||
# Dependencies
|
||||
markdown: kramdown
|
||||
highlighter: pygments
|
||||
|
||||
# Permalinks
|
||||
permalink: /:categories/:title/
|
||||
relative_permalinks: true
|
||||
|
||||
# Setup
|
||||
title: tail call
|
||||
tagline: Ramblings in software development
|
||||
description: The ramblings of a 25-year-old software developer from Germany.
|
||||
url: https://blog.kempkens.io
|
||||
hub_url: https://kempkens.superfeedr.com
|
||||
baseurl: /
|
||||
|
||||
author:
|
||||
name: Daniel Kempkens
|
||||
location: Wassenberg, North Rhine-Westphalia, Germany
|
||||
url: https://kempkens.io
|
||||
email: daniel+blog@kempkens.io
|
||||
twitter: '@nifoc_'
|
||||
|
||||
paginate: 5
|
||||
|
||||
include: [".htaccess"]
|
||||
exclude: ["tmp", "Gemfile", "Gemfile.lock", "Makefile"]
|
||||
|
||||
# Custom
|
||||
assets:
|
||||
gzip: []
|
||||
css_compressor: yui
|
||||
|
||||
disqus_shortname: blog-kempkens-io
|
||||
|
||||
footer:
|
||||
github: nifoc
|
||||
ipv6_check: kempkens.io
|
16
_includes/analytics.html
Normal file
16
_includes/analytics.html
Normal file
|
@ -0,0 +1,16 @@
|
|||
<script>
|
||||
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
||||
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
|
||||
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
|
||||
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
|
||||
|
||||
(function() {
|
||||
var host = window.location.hostname;
|
||||
|
||||
if (host !== 'localhost' && host !== '127.0.0.1') { // No tracking on localhost
|
||||
ga('create', 'UA-28082386-3', host);
|
||||
ga('set', 'anonymizeIp', true);
|
||||
ga('send', 'pageview');
|
||||
}
|
||||
})();
|
||||
</script>
|
36
_includes/feed.xml
Normal file
36
_includes/feed.xml
Normal file
|
@ -0,0 +1,36 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<feed xmlns="http://www.w3.org/2005/Atom">
|
||||
<title>{{ site.title | append: include.title }}</title>
|
||||
<link rel="self" href="{{ include.path | prepend: site.url }}" />
|
||||
<link href="{{ site.url }}/" />
|
||||
<link rel="hub" href="{{ site.hub_url }}/" />
|
||||
<updated>{{ site.time | date_to_xmlschema }}</updated>
|
||||
<id>{{ site.url | append: include.id }}</id>
|
||||
<author>
|
||||
<name>{{ site.author.name }}</name>
|
||||
<email>{{ site.author.email }}</email>
|
||||
</author>
|
||||
|
||||
{% for post in include.posts %}
|
||||
<entry>
|
||||
<id>{{ site.url }}{{ post.id }}</id>
|
||||
<title>{% if post.category == "links" %}Link: {% endif %}{{ post.title }}</title>
|
||||
<link href="{{ post.url | prepend: site.url }}" />
|
||||
<published>{{ post.date | date_to_xmlschema }}</published>
|
||||
{% if post.modified != nil %}
|
||||
<updated>{{ post.modified | date_to_xmlschema }}</updated>
|
||||
{% else %}
|
||||
<updated>{{ post.date | date_to_xmlschema }}</updated>
|
||||
{% endif %}
|
||||
{% if post.category == "links" %}
|
||||
<content type="html">
|
||||
{% capture linktext %}<p><strong>Link:</strong> <a href="{{ post.link }}">{{ post.link | hostname }}</a></p>{% endcapture %}
|
||||
{{ linktext | xml_escape }}
|
||||
{{ post.content | xml_escape }}
|
||||
</content>
|
||||
{% else %}
|
||||
<content type="html">{{ post.content | xml_escape }}</content>
|
||||
{% endif %}
|
||||
</entry>
|
||||
{% endfor %}
|
||||
</feed>
|
15
_includes/foot.html
Normal file
15
_includes/foot.html
Normal file
|
@ -0,0 +1,15 @@
|
|||
<div class="footer">
|
||||
<ul class="footer-links">
|
||||
<li><a href="{{ "feed.xml" | prepend: site.baseurl }}">Feed</a></li>
|
||||
<li class="divider"></li>
|
||||
<li><a href="{{ "feed-with-links.xml" | prepend: site.baseurl }}">Feed (with links)</a></li>
|
||||
<li class="divider"></li>
|
||||
<li><a href="{{ site.footer.github | prepend: "https://github.com/" }}">GitHub</a></li>
|
||||
<li class="divider"></li>
|
||||
<li><a href="{{ site.footer.ipv6_check | prepend: "http://ip6.nl/#!" }}">IPv6</a></li>
|
||||
<li class="divider"></li>
|
||||
<li><a href="https://www.freebsd.org">FreeBSD</a></li>
|
||||
<li class="divider"></li>
|
||||
<li><a href="https://www.vultr.com/?ref=6819371" title="Affiliate link">Vultr</a></li>
|
||||
</ul>
|
||||
</div>
|
51
_includes/head.html
Normal file
51
_includes/head.html
Normal file
|
@ -0,0 +1,51 @@
|
|||
<head>
|
||||
<link href="http://gmpg.org/xfn/11" rel="profile">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta http-equiv="content-type" content="text/html; charset=utf-8">
|
||||
|
||||
<!-- Enable responsiveness on mobile devices-->
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1">
|
||||
|
||||
<title>
|
||||
{% if page.title == "Home" %}
|
||||
{{ site.title }} · {{ site.tagline }}
|
||||
{% else %}
|
||||
{{ page.title }} · {{ site.title }}
|
||||
{% endif %}
|
||||
</title>
|
||||
|
||||
<!-- CSS -->
|
||||
{% stylesheet app %}
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=PT+Sans:400,400italic,700|Abril+Fatface">
|
||||
|
||||
<!-- Icons -->
|
||||
<link rel="shortcut icon" href="{{ "favicon.ico" | prepend: site.baseurl }}">
|
||||
<link rel="icon" type="image/png" href="{{ "favicon.png" | prepend: site.baseurl }}" />
|
||||
|
||||
<!-- RSS -->
|
||||
<link rel="alternate" type="application/atom+xml" title="{{ site.title }} Feed" href="{{ "/feed.xml" | prepend: site.url }}">
|
||||
<link rel="alternate" type="application/atom+xml" title="{{ site.title }} Feed (with links)" href="{{ "/feed-with-links.xml" | prepend: site.url }}">
|
||||
|
||||
<!-- humans.txt -->
|
||||
<link rel="author" href="{{ "humans.txt" | prepend: site.baseurl }}">
|
||||
|
||||
{% if page.category == "posts" %}
|
||||
<!-- Twitter -->
|
||||
<meta name="twitter:card" content="summary">
|
||||
<meta name="twitter:site" content="@nifoc_">
|
||||
<meta name="twitter:title" content="{{ page.title }}">
|
||||
<meta name="twitter:description" content="{{ page.description }}">
|
||||
<meta name="twitter:url" content="{{ site.url }}{{ page.url }}">
|
||||
|
||||
<!-- Facebook -->
|
||||
<meta property="og:type" content="website">
|
||||
<meta property="og:site_name" content="{{ site.title }}">
|
||||
<meta property="og:title" content="{{ page.title }}">
|
||||
<meta property="og:description" content="{{ page.description }}">
|
||||
<meta property="og:url" content="{{ site.url }}{{ page.url }}">
|
||||
|
||||
<!-- Schema.org -->
|
||||
<meta itemprop="name" content="{{ page.title }}">
|
||||
<meta itemprop="description" content="{{ page.description }}">
|
||||
{% endif %}
|
||||
</head>
|
32
_includes/sidebar.html
Normal file
32
_includes/sidebar.html
Normal file
|
@ -0,0 +1,32 @@
|
|||
<div class="sidebar">
|
||||
<div class="container sidebar-sticky">
|
||||
<div class="sidebar-about">
|
||||
<h1>
|
||||
<a href="{{ site.baseurl }}">
|
||||
{{ site.title }}
|
||||
</a>
|
||||
</h1>
|
||||
<p class="lead">{{ site.description }}</p>
|
||||
</div>
|
||||
|
||||
<nav class="sidebar-nav">
|
||||
<a class="sidebar-nav-item{% if page.url == site.baseurl %} active{% endif %}" href="{{ site.baseurl }}">Home</a>
|
||||
|
||||
{% comment %}
|
||||
The code below dynamically generates a sidebar nav of pages with
|
||||
`layout: page` in the front-matter. See readme for usage.
|
||||
{% endcomment %}
|
||||
|
||||
{% assign pages_list = site.pages %}
|
||||
{% for node in pages_list %}
|
||||
{% if node.title != null %}
|
||||
{% if node.layout == "page" %}
|
||||
<a class="sidebar-nav-item{% if page.url == node.url %} active{% endif %}" href="{{ node.url }}">{{ node.title }}</a>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</nav>
|
||||
|
||||
<p>© {{ site.time | date: '%Y' }}. All rights reserved.</p>
|
||||
</div>
|
||||
</div>
|
18
_layouts/default.html
Normal file
18
_layouts/default.html
Normal file
|
@ -0,0 +1,18 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en-us">
|
||||
|
||||
{% include head.html %}
|
||||
|
||||
<body>
|
||||
|
||||
{% include sidebar.html %}
|
||||
|
||||
<div class="content container">
|
||||
{{ content }}
|
||||
|
||||
{% include foot.html %}
|
||||
</div>
|
||||
|
||||
{% include analytics.html %}
|
||||
</body>
|
||||
</html>
|
12
_layouts/link.html
Normal file
12
_layouts/link.html
Normal file
|
@ -0,0 +1,12 @@
|
|||
---
|
||||
layout: default
|
||||
---
|
||||
|
||||
<div class="post">
|
||||
<h1 class="post-title">⚓ {{ page.title }}</h1>
|
||||
<span class="post-date">{{ page.date | date_to_string }}</span>
|
||||
|
||||
<p class="post-link"><span>Link:</span> <a href="{{ page.link }}" rel="external">{{ page.link | hostname }}</a></p>
|
||||
|
||||
{{ content }}
|
||||
</div>
|
8
_layouts/page.html
Normal file
8
_layouts/page.html
Normal file
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
layout: default
|
||||
---
|
||||
|
||||
<div class="page">
|
||||
<h1 class="page-title">{{ page.title }}</h1>
|
||||
{{ content }}
|
||||
</div>
|
45
_layouts/post.html
Normal file
45
_layouts/post.html
Normal file
|
@ -0,0 +1,45 @@
|
|||
---
|
||||
layout: default
|
||||
---
|
||||
|
||||
<div class="post">
|
||||
<h1 class="post-title">{{ page.title }}</h1>
|
||||
<span class="post-date">{{ page.date | date_to_string }}</span>
|
||||
{{ content }}
|
||||
</div>
|
||||
|
||||
<div class="related">
|
||||
<h2>Related Posts</h2>
|
||||
<ul class="related-posts">
|
||||
{% for post in site.related_posts limit:3 %}
|
||||
<li>
|
||||
<h3>
|
||||
<a href="{{ post.url }}">
|
||||
{% if post.category == "links" %}⚓{% endif %}
|
||||
{{ post.title }}
|
||||
<small>{{ post.date | date_to_string }}</small>
|
||||
</a>
|
||||
</h3>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
{% if page.comments %}
|
||||
<div class="comments">
|
||||
<h2>Comments</h2>
|
||||
<div id="disqus_thread"></div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
/* * * CONFIGURATION VARIABLES: EDIT BEFORE PASTING INTO YOUR WEBPAGE * * */
|
||||
var disqus_shortname = '{{ site.disqus_shortname }}';
|
||||
|
||||
/* * * DON'T EDIT BELOW THIS LINE * * */
|
||||
(function() {
|
||||
var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true;
|
||||
dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js';
|
||||
(document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq);
|
||||
})();
|
||||
</script>
|
||||
{% endif %}
|
2
_plugins/ext.rb
Normal file
2
_plugins/ext.rb
Normal file
|
@ -0,0 +1,2 @@
|
|||
require 'bundler'
|
||||
Bundler.require(:extensions)
|
12
_plugins/hostname.rb
Normal file
12
_plugins/hostname.rb
Normal file
|
@ -0,0 +1,12 @@
|
|||
require 'uri'
|
||||
|
||||
module Jekyll
|
||||
module HostnameFilter
|
||||
def hostname(input)
|
||||
parsed_uri = URI.parse(input)
|
||||
parsed_uri.host
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Liquid::Template.register_filter(Jekyll::HostnameFilter)
|
|
@ -1,19 +1,18 @@
|
|||
---
|
||||
date: "2014-02-16T15:30:00Z"
|
||||
description: Description and implementation of a function that joins a list of binaries.
|
||||
tags:
|
||||
- erlang
|
||||
- programming
|
||||
- english
|
||||
slug: joining-a-list-of-binaries-in-erlang
|
||||
layout: post
|
||||
title: Joining a List of Binaries in Erlang
|
||||
description: "Description and implementation of a function that joins a list of binaries."
|
||||
date: 2014-02-16 15:30:00 CET
|
||||
category: posts
|
||||
tags: [erlang, programming, english]
|
||||
comments: true
|
||||
---
|
||||
|
||||
The binary module in Erlang provides an easy way to split binaries using `split/2,3`, but what if you want to join a list of binaries back together?
|
||||
|
||||
There is no built-in function to do this, so I've decided to write my own.
|
||||
|
||||
{{< highlight erlang >}}
|
||||
{% highlight erlang %}
|
||||
-spec binary_join([binary()], binary()) -> binary().
|
||||
binary_join([], _Sep) ->
|
||||
<<>>;
|
||||
|
@ -21,13 +20,13 @@ binary_join([Part], _Sep) ->
|
|||
Part;
|
||||
binary_join([Head|Tail], Sep) ->
|
||||
lists:foldl(fun (Value, Acc) -> <<Acc/binary, Sep/binary, Value/binary>> end, Head, Tail).
|
||||
{{< / highlight >}}
|
||||
{% endhighlight %}
|
||||
|
||||
It works just like you would expect:
|
||||
|
||||
{{< highlight erlang >}}
|
||||
{% highlight erlang %}
|
||||
binary_join([<<"Hello">>, <<"World">>], <<", ">>) % => <<"Hello, World">>
|
||||
binary_join([<<"Hello">>], <<"...">>) % => <<"Hello">>
|
||||
{{< / highlight >}}
|
||||
{% endhighlight %}
|
||||
|
||||
Hope you find this useful!
|
|
@ -1,26 +1,24 @@
|
|||
---
|
||||
date: "2014-02-17T21:45:00Z"
|
||||
description: Simple nginx configuration that allows you to proxy most of Microsoft Exchange.
|
||||
tags:
|
||||
- nginx
|
||||
- exchange
|
||||
- ops
|
||||
- english
|
||||
slug: exchange-reverse-proxy-using-nginx
|
||||
layout: post
|
||||
title: Exchange Reverse Proxy Using nginx
|
||||
description: "Simple nginx configuration that allows you to proxy most of Microsoft Exchange."
|
||||
date: 2014-02-17 21:45:00 CET
|
||||
category: posts
|
||||
tags: [nginx, exchange, ops, english]
|
||||
comments: true
|
||||
---
|
||||
|
||||
As it turns out, setting up [nginx](http://nginx.org) as a reverse proxy for Microsoft Exchange is not as easy as [some](http://blog.friedlandreas.net/2013/07/reverseproxy-fur-eas-exchange-activesync-und-owa-outlookwebapp-mit-nginx/) [posts](http://www.administrator.de/wissen/ngnix-als-reverse-proxy-für-exchange-2010-192711.html) suggest.
|
||||
|
||||
The issue that for some calls (Autodiscovery, RPC, …) IIS asks for an `Authorization` header, which nginx can pass through by doing:
|
||||
|
||||
{{< highlight nginx >}}
|
||||
{% highlight nginx %}
|
||||
proxy_pass_header Authorization;
|
||||
{{< / highlight >}}
|
||||
{% endhighlight %}
|
||||
|
||||
Only problem is: It doesn't work. Thankfully someone on StackOverflow already had a [solution](http://stackoverflow.com/a/19714696) for this:
|
||||
|
||||
{{< highlight nginx >}}
|
||||
{% highlight nginx %}
|
||||
proxy_http_version 1.1;
|
||||
proxy_pass_request_headers on;
|
||||
proxy_pass_header Date;
|
||||
|
@ -33,12 +31,12 @@ more_set_input_headers 'Authorization: $http_authorization';
|
|||
proxy_set_header Accept-Encoding "";
|
||||
more_set_headers -s 401 'WWW-Authenticate: Basic realm="your.mail.host"';
|
||||
proxy_pass https://your.mail.host;
|
||||
{{< / highlight >}}
|
||||
{% endhighlight %}
|
||||
|
||||
You'll need the [HttpHeadersMore](http://wiki.nginx.org/HttpHeadersMoreModule) module for this to work. On Ubuntu 12.04 (using the [nginx/stable PPA](https://launchpad.net/~nginx/+archive/stable)) all you need to install is:
|
||||
|
||||
{{< highlight bash >}}
|
||||
{% highlight bash %}
|
||||
apt-get install nginx-extras
|
||||
{{< / highlight >}}
|
||||
{% endhighlight %}
|
||||
|
||||
And you're good to go!
|
|
@ -1,18 +1,16 @@
|
|||
---
|
||||
date: "2014-02-24T22:00:00Z"
|
||||
description: Some links regarding Telegram and its security model.
|
||||
tags:
|
||||
- telegram
|
||||
- whatsapp
|
||||
- app
|
||||
- english
|
||||
slug: telegram-and-security
|
||||
layout: post
|
||||
title: Telegram and Security
|
||||
description: "Some links regarding Telegram and its security model."
|
||||
date: 2014-02-24 22:00:00 CET
|
||||
category: posts
|
||||
tags: [telegram, whatsapp, app, english]
|
||||
comments: true
|
||||
---
|
||||
|
||||
Ever since [Facebook bought WhatsApp](http://techcrunch.com/2014/02/19/facebook-buying-whatsapp-for-16b-in-cash-and-stock-plus-3b-in-rsus/) more and more of the people I know switch to [Telegram](https://telegram.org). One of the main reasons they choose Telegram is because it's "secure" and Telegram itself seems really keen on being seen as a secure, privacy-focused WhatsApp alternative.
|
||||
|
||||
People that are way smarter than I am when it comes to cryptography have a slightly different opinion on the "secure, privacy-focused" part …
|
||||
People that are way smarter than I am when it comes to cryptography have a sligtly different opinion on the "secure, privacy-focused" part …
|
||||
|
||||
- [Telegram, AKA "Stand back, we have Math PhDs!"](http://unhandledexpression.com/2013/12/17/telegram-stand-back-we-know-maths/)
|
||||
- [A Crypto Challenge For The Telegram Developers](http://thoughtcrime.org/blog/telegram-crypto-challenge/)
|
|
@ -1,19 +1,18 @@
|
|||
---
|
||||
date: "2014-03-23T20:00:00Z"
|
||||
description: Blow up a polyline to search inside the generated polygon.
|
||||
tags:
|
||||
- javascript
|
||||
- programming
|
||||
- english
|
||||
slug: buffered-polyline
|
||||
layout: post
|
||||
title: Buffered Polyline
|
||||
description: "Blow up a polyline to search inside the generated polygon."
|
||||
date: 2014-03-23 20:00:00 CET
|
||||
category: posts
|
||||
tags: [javascript, programming, english]
|
||||
comments: true
|
||||
---
|
||||
|
||||
At work, we needed a simple way to buffer a polyline in order to search for stuff along the route from A to B. I'll explain how we used Google's Maps API and [JSTS](https://github.com/bjornharrtell/jsts) in order to achieve this easily.
|
||||
|
||||
The first thing we had to do was to "transform" each element in the `overview_path` (which the `DirectionsService` returns) to GeoJSON, because that's what JSTS understands.
|
||||
|
||||
{{< highlight javascript "linenos=table" >}}
|
||||
{% highlight javascript linenos %}
|
||||
var overviewPath = response.routes[0].overview_path,
|
||||
overviewPathGeo = [];
|
||||
for(var i = 0; i < overviewPath.length; i++) {
|
||||
|
@ -21,11 +20,11 @@ for(var i = 0; i < overviewPath.length; i++) {
|
|||
[overviewPath[i].lng(), overviewPath[i].lat()]
|
||||
);
|
||||
}
|
||||
{{< / highlight >}}
|
||||
{% endhighlight %}
|
||||
|
||||
The next step was getting `overviewPathGeo` into JSTS and letting it do the work of buffering the line.
|
||||
|
||||
{{< highlight javascript "linenos=table" >}}
|
||||
{% highlight javascript linenos %}
|
||||
var distance = 10/111.12, // Roughly 10km
|
||||
geoInput = {
|
||||
type: "LineString",
|
||||
|
@ -35,7 +34,7 @@ var geoReader = new jsts.io.GeoJSONReader(),
|
|||
geoWriter = new jsts.io.GeoJSONWriter();
|
||||
var geometry = geoReader.read(geoInput).buffer(distance);
|
||||
var polygon = geoWriter.write(geometry);
|
||||
{{< / highlight >}}
|
||||
{% endhighlight %}
|
||||
|
||||
The `polygon` variable now contains a polygon that neatly fits around the `overviewPath`, with a *distance* (buffer size) of roughly 10km.
|
||||
|
||||
|
@ -43,4 +42,4 @@ Since it is nested, you need to call `polygon.coordinates[0]` in order to get ba
|
|||
|
||||
You could then use the the coordinates to draw the generated polygon on the map (along with the route), in order to produce something like this:
|
||||
|
||||
![Example image of a buffered polyline](/posts/buffered-polyline-1.png)
|
||||
{% image buffered-polyline-1.png %}
|
|
@ -1,12 +1,11 @@
|
|||
---
|
||||
date: "2014-03-26T13:55:00Z"
|
||||
description: Collection of some interesting DEF CON videos.
|
||||
tags:
|
||||
- defcon
|
||||
- videos
|
||||
- english
|
||||
slug: some-defcon-videos
|
||||
layout: post
|
||||
title: Some DEF CON Videos
|
||||
description: "Collection of some interesting DEF CON videos."
|
||||
date: 2014-03-26 13:55:00 CET
|
||||
category: posts
|
||||
tags: [defcon, videos, english]
|
||||
comments: true
|
||||
---
|
||||
|
||||
Over the weekend I watched some [DEF CON](https://www.defcon.org) videos on [YouTube](https://www.youtube.com/user/DEFCONConference/videos) and thought I should share some of the interesting ones on here. The two talks on Bitsquatting (by Stucke and Schultz) are especially entertaining and scary.
|
189
_posts/2014-03-30-ocsp-stapling-with-nginx.md
Normal file
189
_posts/2014-03-30-ocsp-stapling-with-nginx.md
Normal file
|
@ -0,0 +1,189 @@
|
|||
---
|
||||
layout: post
|
||||
title: OCSP Stapling with nginx
|
||||
description: "A general explanation of how to set up OCSP stapling with nginx."
|
||||
date: 2014-03-30 21:30:00 CEST
|
||||
modified: 2014-03-31 23:10:00 CEST
|
||||
category: posts
|
||||
tags: [nginx, ocsp, ssl, ops, english]
|
||||
comments: true
|
||||
---
|
||||
|
||||
Setting up [OCSP stapling](https://en.wikipedia.org/wiki/OCSP_stapling) with [nginx](http://nginx.org/) is more or less straightforward, but depending on what's in your `ssl_certificate` you might run into some issues with it silently failing. So I've decided to write about how I set up OCSP stapling with certificates from [StartSSL](https://www.startssl.com/) and [CAcert](http://www.cacert.org/).
|
||||
|
||||
I have only tested this using nginx version 1.5.12, but the configuration options I'm using have been in nginx since version 1.3.7. If it does not work for some reason, feel free to leave a comment below.
|
||||
|
||||
To enable OCSP stapling, you simply have to add the following lines to your (already SSL enabled) site:
|
||||
|
||||
{% highlight nginx %}
|
||||
resolver 8.8.8.8 8.8.4.4 208.67.222.222 208.67.220.220 valid=300s;
|
||||
resolver_timeout 10s;
|
||||
|
||||
ssl_stapling on;
|
||||
ssl_stapling_verify on;
|
||||
ssl_trusted_certificate /etc/nginx/certs/startssl.stapling.crt;
|
||||
{% endhighlight %}
|
||||
|
||||
The important thing here is the usage of `ssl_trusted_certificate`. The [nginx documentation](http://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_stapling_verify) states that:
|
||||
|
||||
> For verification to work, the certificate of the server certificate issuer, **the root certificate, and all intermediate certificates** should be configured as trusted using the ssl_trusted_certificate directive.
|
||||
|
||||
You have to include the root certificate (and intermediate certificates) for OCSP stapling to work, which is why `ssl_trusted_certificate` will be set to a *special* certificate file that includes those. If your `ssl_certificate` already does, you might be able to skip using `ssl_trusted_certificate`.
|
||||
|
||||
## CAcert
|
||||
|
||||
For CAcert (and unless you have a Class 3 certificate) you only have to include the Class 1 certificate in your stapling file.
|
||||
|
||||
{% highlight text %}
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIHPTCCBSWgAwIBAgIBADANBgkqhkiG9w0BAQQFADB5MRAwDgYDVQQKEwdSb290
|
||||
IENBMR4wHAYDVQQLExVodHRwOi8vd3d3LmNhY2VydC5vcmcxIjAgBgNVBAMTGUNB
|
||||
IENlcnQgU2lnbmluZyBBdXRob3JpdHkxITAfBgkqhkiG9w0BCQEWEnN1cHBvcnRA
|
||||
Y2FjZXJ0Lm9yZzAeFw0wMzAzMzAxMjI5NDlaFw0zMzAzMjkxMjI5NDlaMHkxEDAO
|
||||
BgNVBAoTB1Jvb3QgQ0ExHjAcBgNVBAsTFWh0dHA6Ly93d3cuY2FjZXJ0Lm9yZzEi
|
||||
MCAGA1UEAxMZQ0EgQ2VydCBTaWduaW5nIEF1dGhvcml0eTEhMB8GCSqGSIb3DQEJ
|
||||
ARYSc3VwcG9ydEBjYWNlcnQub3JnMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC
|
||||
CgKCAgEAziLA4kZ97DYoB1CW8qAzQIxL8TtmPzHlawI229Z89vGIj053NgVBlfkJ
|
||||
8BLPRoZzYLdufujAWGSuzbCtRRcMY/pnCujW0r8+55jE8Ez64AO7NV1sId6eINm6
|
||||
zWYyN3L69wj1x81YyY7nDl7qPv4coRQKFWyGhFtkZip6qUtTefWIonvuLwphK42y
|
||||
fk1WpRPs6tqSnqxEQR5YYGUFZvjARL3LlPdCfgv3ZWiYUQXw8wWRBB0bF4LsyFe7
|
||||
w2t6iPGwcswlWyCR7BYCEo8y6RcYSNDHBS4CMEK4JZwFaz+qOqfrU0j36NK2B5jc
|
||||
G8Y0f3/JHIJ6BVgrCFvzOKKrF11myZjXnhCLotLddJr3cQxyYN/Nb5gznZY0dj4k
|
||||
epKwDpUeb+agRThHqtdB7Uq3EvbXG4OKDy7YCbZZ16oE/9KTfWgu3YtLq1i6L43q
|
||||
laegw1SJpfvbi1EinbLDvhG+LJGGi5Z4rSDTii8aP8bQUWWHIbEZAWV/RRyH9XzQ
|
||||
QUxPKZgh/TMfdQwEUfoZd9vUFBzugcMd9Zi3aQaRIt0AUMyBMawSB3s42mhb5ivU
|
||||
fslfrejrckzzAeVLIL+aplfKkQABi6F1ITe1Yw1nPkZPcCBnzsXWWdsC4PDSy826
|
||||
YreQQejdIOQpvGQpQsgi3Hia/0PsmBsJUUtaWsJx8cTLc6nloQsCAwEAAaOCAc4w
|
||||
ggHKMB0GA1UdDgQWBBQWtTIb1Mfz4OaO873SsDrusjkY0TCBowYDVR0jBIGbMIGY
|
||||
gBQWtTIb1Mfz4OaO873SsDrusjkY0aF9pHsweTEQMA4GA1UEChMHUm9vdCBDQTEe
|
||||
MBwGA1UECxMVaHR0cDovL3d3dy5jYWNlcnQub3JnMSIwIAYDVQQDExlDQSBDZXJ0
|
||||
IFNpZ25pbmcgQXV0aG9yaXR5MSEwHwYJKoZIhvcNAQkBFhJzdXBwb3J0QGNhY2Vy
|
||||
dC5vcmeCAQAwDwYDVR0TAQH/BAUwAwEB/zAyBgNVHR8EKzApMCegJaAjhiFodHRw
|
||||
czovL3d3dy5jYWNlcnQub3JnL3Jldm9rZS5jcmwwMAYJYIZIAYb4QgEEBCMWIWh0
|
||||
dHBzOi8vd3d3LmNhY2VydC5vcmcvcmV2b2tlLmNybDA0BglghkgBhvhCAQgEJxYl
|
||||
aHR0cDovL3d3dy5jYWNlcnQub3JnL2luZGV4LnBocD9pZD0xMDBWBglghkgBhvhC
|
||||
AQ0ESRZHVG8gZ2V0IHlvdXIgb3duIGNlcnRpZmljYXRlIGZvciBGUkVFIGhlYWQg
|
||||
b3ZlciB0byBodHRwOi8vd3d3LmNhY2VydC5vcmcwDQYJKoZIhvcNAQEEBQADggIB
|
||||
ACjH7pyCArpcgBLKNQodgW+JapnM8mgPf6fhjViVPr3yBsOQWqy1YPaZQwGjiHCc
|
||||
nWKdpIevZ1gNMDY75q1I08t0AoZxPuIrA2jxNGJARjtT6ij0rPtmlVOKTV39O9lg
|
||||
18p5aTuxZZKmxoGCXJzN600BiqXfEVWqFcofN8CCmHBh22p8lqOOLlQ+TyGpkO/c
|
||||
gr/c6EWtTZBzCDyUZbAEmXZ/4rzCahWqlwQ3JNgelE5tDlG+1sSPypZt90Pf6DBl
|
||||
Jzt7u0NDY8RD97LsaMzhGY4i+5jhe1o+ATc7iwiwovOVThrLm82asduycPAtStvY
|
||||
sONvRUgzEv/+PDIqVPfE94rwiCPCR/5kenHA0R6mY7AHfqQv0wGP3J8rtsYIqQ+T
|
||||
SCX8Ev2fQtzzxD72V7DX3WnRBnc0CkvSyqD/HMaMyRa+xMwyN2hzXwj7UfdJUzYF
|
||||
CpUCTPJ5GhD22Dp1nPMd8aINcGeGG7MW9S/lpOt5hvk9C8JzC6WZrG/8Z7jlLwum
|
||||
GCSNe9FINSkYQKyTYOGWhlC0elnYjyELn8+CkcY7v2vcB5G5l1YjqrZslMZIBjzk
|
||||
zk6q5PYvCdxTby78dOs6Y5nCpqyJvKeyRKANihDjbPIky/qbn3BHLt4Ui9SyIAmW
|
||||
omTxJBzcoTWcFbLUvFUufQb1nA5V9FrWk9p2rSVzTMVD
|
||||
-----END CERTIFICATE-----
|
||||
{% endhighlight %}
|
||||
|
||||
## StartSSL
|
||||
|
||||
StartSSL - using a Class 1 certificate again - has an intermediate certificate in their chain, so you have to include this one in your stapling file, too.
|
||||
|
||||
{% highlight text %}
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIGNDCCBBygAwIBAgIBGDANBgkqhkiG9w0BAQUFADB9MQswCQYDVQQGEwJJTDEW
|
||||
MBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwg
|
||||
Q2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMgU3RhcnRDb20gQ2VydGlmaWNh
|
||||
dGlvbiBBdXRob3JpdHkwHhcNMDcxMDI0MjA1NDE3WhcNMTcxMDI0MjA1NDE3WjCB
|
||||
jDELMAkGA1UEBhMCSUwxFjAUBgNVBAoTDVN0YXJ0Q29tIEx0ZC4xKzApBgNVBAsT
|
||||
IlNlY3VyZSBEaWdpdGFsIENlcnRpZmljYXRlIFNpZ25pbmcxODA2BgNVBAMTL1N0
|
||||
YXJ0Q29tIENsYXNzIDEgUHJpbWFyeSBJbnRlcm1lZGlhdGUgU2VydmVyIENBMIIB
|
||||
IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtonGrO8JUngHrJJj0PREGBiE
|
||||
gFYfka7hh/oyULTTRwbw5gdfcA4Q9x3AzhA2NIVaD5Ksg8asWFI/ujjo/OenJOJA
|
||||
pgh2wJJuniptTT9uYSAK21ne0n1jsz5G/vohURjXzTCm7QduO3CHtPn66+6CPAVv
|
||||
kvek3AowHpNz/gfK11+AnSJYUq4G2ouHI2mw5CrY6oPSvfNx23BaKA+vWjhwRRI/
|
||||
ME3NO68X5Q/LoKldSKqxYVDLNM08XMML6BDAjJvwAwNi/rJsPnIO7hxDKslIDlc5
|
||||
xDEhyBDBLIf+VJVSH1I8MRKbf+fAoKVZ1eKPPvDVqOHXcDGpxLPPr21TLwb0pwID
|
||||
AQABo4IBrTCCAakwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYD
|
||||
VR0OBBYEFOtCNNCYsKuf9BtrCPfMZC7vDixFMB8GA1UdIwQYMBaAFE4L7xqkQFul
|
||||
F2mHMMo0aEPQQa7yMGYGCCsGAQUFBwEBBFowWDAnBggrBgEFBQcwAYYbaHR0cDov
|
||||
L29jc3Auc3RhcnRzc2wuY29tL2NhMC0GCCsGAQUFBzAChiFodHRwOi8vd3d3LnN0
|
||||
YXJ0c3NsLmNvbS9zZnNjYS5jcnQwWwYDVR0fBFQwUjAnoCWgI4YhaHR0cDovL3d3
|
||||
dy5zdGFydHNzbC5jb20vc2ZzY2EuY3JsMCegJaAjhiFodHRwOi8vY3JsLnN0YXJ0
|
||||
c3NsLmNvbS9zZnNjYS5jcmwwgYAGA1UdIAR5MHcwdQYLKwYBBAGBtTcBAgEwZjAu
|
||||
BggrBgEFBQcCARYiaHR0cDovL3d3dy5zdGFydHNzbC5jb20vcG9saWN5LnBkZjA0
|
||||
BggrBgEFBQcCARYoaHR0cDovL3d3dy5zdGFydHNzbC5jb20vaW50ZXJtZWRpYXRl
|
||||
LnBkZjANBgkqhkiG9w0BAQUFAAOCAgEAIQlJPqWIbuALi0jaMU2P91ZXouHTYlfp
|
||||
tVbzhUV1O+VQHwSL5qBaPucAroXQ+/8gA2TLrQLhxpFy+KNN1t7ozD+hiqLjfDen
|
||||
xk+PNdb01m4Ge90h2c9W/8swIkn+iQTzheWq8ecf6HWQTd35RvdCNPdFWAwRDYSw
|
||||
xtpdPvkBnufh2lWVvnQce/xNFE+sflVHfXv0pQ1JHpXo9xLBzP92piVH0PN1Nb6X
|
||||
t1gW66pceG/sUzCv6gRNzKkC4/C2BBL2MLERPZBOVmTX3DxDX3M570uvh+v2/miI
|
||||
RHLq0gfGabDBoYvvF0nXYbFFSF87ICHpW7LM9NfpMfULFWE7epTj69m8f5SuauNi
|
||||
YpaoZHy4h/OZMn6SolK+u/hlz8nyMPyLwcKmltdfieFcNID1j0cHL7SRv7Gifl9L
|
||||
WtBbnySGBVFaaQNlQ0lxxeBvlDRr9hvYqbBMflPrj0jfyjO1SPo2ShpTpjMM0InN
|
||||
SRXNiTE8kMBy12VLUjWKRhFEuT2OKGWmPnmeXAhEKa2wNREuIU640ucQPl2Eg7PD
|
||||
wuTSxv0JS3QJ3fGz0xk+gA2iCxnwOOfFwq/iI9th4p1cbiCJSS4jarJiwUW0n6+L
|
||||
p/EiO/h94pDQehn7Skzj0n1fSoMD7SfWI55rjbRZotnvbIIp3XUZPD9MEI3vu3Un
|
||||
0q6Dp6jOW6c=
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIHyTCCBbGgAwIBAgIBATANBgkqhkiG9w0BAQUFADB9MQswCQYDVQQGEwJJTDEW
|
||||
MBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwg
|
||||
Q2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMgU3RhcnRDb20gQ2VydGlmaWNh
|
||||
dGlvbiBBdXRob3JpdHkwHhcNMDYwOTE3MTk0NjM2WhcNMzYwOTE3MTk0NjM2WjB9
|
||||
MQswCQYDVQQGEwJJTDEWMBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMi
|
||||
U2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMgU3Rh
|
||||
cnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUA
|
||||
A4ICDwAwggIKAoICAQDBiNsJvGxGfHiflXu1M5DycmLWwTYgIiRezul38kMKogZk
|
||||
pMyONvg45iPwbm2xPN1yo4UcodM9tDMr0y+v/uqwQVlntsQGfQqedIXWeUyAN3rf
|
||||
OQVSWff0G0ZDpNKFhdLDcfN1YjS6LIp/Ho/u7TTQEceWzVI9ujPW3U3eCztKS5/C
|
||||
Ji/6tRYccjV3yjxd5srhJosaNnZcAdt0FCX+7bWgiA/deMotHweXMAEtcnn6RtYT
|
||||
Kqi5pquDSR3l8u/d5AGOGAqPY1MWhWKpDhk6zLVmpsJrdAfkK+F2PrRt2PZE4XNi
|
||||
HzvEvqBTViVsUQn3qqvKv3b9bZvzndu/PWa8DFaqr5hIlTpL36dYUNk4dalb6kMM
|
||||
Av+Z6+hsTXBbKWWc3apdzK8BMewM69KN6Oqce+Zu9ydmDBpI125C4z/eIT574Q1w
|
||||
+2OqqGwaVLRcJXrJosmLFqa7LH4XXgVNWG4SHQHuEhANxjJ/GP/89PrNbpHoNkm+
|
||||
Gkhpi8KWTRoSsmkXwQqQ1vp5Iki/untp+HDH+no32NgN0nZPV/+Qt+OR0t3vwmC3
|
||||
Zzrd/qqc8NSLf3Iizsafl7b4r4qgEKjZ+xjGtrVcUjyJthkqcwEKDwOzEmDyei+B
|
||||
26Nu/yYwl/WL3YlXtq09s68rxbd2AvCl1iuahhQqcvbjM4xdCUsT37uMdBNSSwID
|
||||
AQABo4ICUjCCAk4wDAYDVR0TBAUwAwEB/zALBgNVHQ8EBAMCAa4wHQYDVR0OBBYE
|
||||
FE4L7xqkQFulF2mHMMo0aEPQQa7yMGQGA1UdHwRdMFswLKAqoCiGJmh0dHA6Ly9j
|
||||
ZXJ0LnN0YXJ0Y29tLm9yZy9zZnNjYS1jcmwuY3JsMCugKaAnhiVodHRwOi8vY3Js
|
||||
LnN0YXJ0Y29tLm9yZy9zZnNjYS1jcmwuY3JsMIIBXQYDVR0gBIIBVDCCAVAwggFM
|
||||
BgsrBgEEAYG1NwEBATCCATswLwYIKwYBBQUHAgEWI2h0dHA6Ly9jZXJ0LnN0YXJ0
|
||||
Y29tLm9yZy9wb2xpY3kucGRmMDUGCCsGAQUFBwIBFilodHRwOi8vY2VydC5zdGFy
|
||||
dGNvbS5vcmcvaW50ZXJtZWRpYXRlLnBkZjCB0AYIKwYBBQUHAgIwgcMwJxYgU3Rh
|
||||
cnQgQ29tbWVyY2lhbCAoU3RhcnRDb20pIEx0ZC4wAwIBARqBl0xpbWl0ZWQgTGlh
|
||||
YmlsaXR5LCByZWFkIHRoZSBzZWN0aW9uICpMZWdhbCBMaW1pdGF0aW9ucyogb2Yg
|
||||
dGhlIFN0YXJ0Q29tIENlcnRpZmljYXRpb24gQXV0aG9yaXR5IFBvbGljeSBhdmFp
|
||||
bGFibGUgYXQgaHR0cDovL2NlcnQuc3RhcnRjb20ub3JnL3BvbGljeS5wZGYwEQYJ
|
||||
YIZIAYb4QgEBBAQDAgAHMDgGCWCGSAGG+EIBDQQrFilTdGFydENvbSBGcmVlIFNT
|
||||
TCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTANBgkqhkiG9w0BAQUFAAOCAgEAFmyZ
|
||||
9GYMNPXQhV59CuzaEE44HF7fpiUFS5Eyweg78T3dRAlbB0mKKctmArexmvclmAk8
|
||||
jhvh3TaHK0u7aNM5Zj2gJsfyOZEdUauCe37Vzlrk4gNXcGmXCPleWKYK34wGmkUW
|
||||
FjgKXlf2Ysd6AgXmvB618p70qSmD+LIU424oh0TDkBreOKk8rENNZEXO3SipXPJz
|
||||
ewT4F+irsfMuXGRuczE6Eri8sxHkfY+BUZo7jYn0TZNmezwD7dOaHZrzZVD1oNB1
|
||||
ny+v8OqCQ5j4aZyJecRDjkZy42Q2Eq/3JR44iZB3fsNrarnDy0RLrHiQi+fHLB5L
|
||||
EUTINFInzQpdn4XBidUaePKVEFMy3YCEZnXZtWgo+2EuvoSoOMCZEoalHmdkrQYu
|
||||
L6lwhceWD3yJZfWOQ1QOq92lgDmUYMA0yZZwLKMS9R9Ie70cfmu3nZD0Ijuu+Pwq
|
||||
yvqCUqDvr0tVk+vBtfAii6w0TiYiBKGHLHVKt+V9E9e4DGTANtLJL4YSjCMJwRuC
|
||||
O3NJo2pXh5Tl1njFmUNj403gdy3hZZlyaQQaRwnmDwFWJPsfvw55qVguucQJAX6V
|
||||
um0ABj6y6koQOdjQK/W/7HW/lwLFCRsI3FU34oH7N4RDYiDK51ZLZer+bMEkkySh
|
||||
NOsF/5oirpt9P/FlUQqmMGqz9IgcgA38corog14=
|
||||
-----END CERTIFICATE-----
|
||||
{% endhighlight %}
|
||||
|
||||
After you've done all that, you can restart nginx or reload the configuration and you should be good to go!
|
||||
|
||||
You can verify that everything works using the `openssl` command line tool.
|
||||
|
||||
{% highlight bash %}
|
||||
# CAcert
|
||||
openssl s_client -servername blog.kempkens.io -connect blog.kempkens.io:443 -tls1 -tlsextdebug -status
|
||||
|
||||
# StartSSL
|
||||
openssl s_client -servername kempkens.io -connect kempkens.io:443 -tls1 -tlsextdebug -status
|
||||
{% endhighlight %}
|
||||
|
||||
Both of those should include a section (with data) named "OCSP Response Data".
|
||||
|
||||
An alternative way to test if OCSP stapling is supported, is by using [Qualys SSL Labs](https://www.ssllabs.com/ssltest/).
|
||||
|
||||
Keep in mind that nginx does not include OCSP data in the first response, because it has to fetch it, too. So you probably have to try at least two times to verify if it works or not.
|
||||
|
||||
**Update #1**
|
||||
|
||||
If you have more than one virtual host with SSL enabled, you have to enable OCSP stapling for every single one. Otherwise nginx will fail silently and not include any stapled OCSP data. (Thanks to [@rmoriz](https://roland.io) for figuring this out)
|
|
@ -1,14 +1,11 @@
|
|||
---
|
||||
date: "2014-04-06T20:15:00Z"
|
||||
description: Property-based testing in Erlang, with some basic usage examples.
|
||||
tags:
|
||||
- erlang
|
||||
- quickcheck
|
||||
- testing
|
||||
- programming
|
||||
- english
|
||||
slug: property-based-testing-with-proper
|
||||
layout: post
|
||||
title: Property-based Testing with PropEr
|
||||
description: "Property-based testing in Erlang, with some basic usage examples."
|
||||
date: 2014-04-06 20:15:00 CEST
|
||||
category: posts
|
||||
tags: [erlang, quickcheck, testing, programming, english]
|
||||
comments: true
|
||||
---
|
||||
|
||||
[PropEr](http://proper.softlab.ntua.gr/) is a [QuickCheck](https://en.wikipedia.org/wiki/QuickCheck)-inspired property-based testing tool for Erlang. In contrast to traditional testing methodologies, the tester only has to provide the generic structure of valid inputs and some properties, which specify the relation between input and output. The testing tool will then produce progressively more complex valid inputs and compare the results it gets to what it expected (based on the defined properties).
|
||||
|
@ -16,28 +13,28 @@ PropEr is one such testing tool for Erlang. It is [open-source](https://github.c
|
|||
|
||||
Assuming you already have your `$ERL_LIBS` set up, you can simply run the following commands to install PropEr and make it available *globally*.
|
||||
|
||||
{{< highlight bash "linenos=table" >}}
|
||||
{% highlight bash linenos %}
|
||||
cd $ERL_LIBS
|
||||
git clone git://github.com/manopapad/proper.git
|
||||
cd proper
|
||||
make fast
|
||||
{{< / highlight >}}
|
||||
{% endhighlight %}
|
||||
|
||||
After that, you can use PropEr in *all* your Erlang projects.
|
||||
|
||||
I like using [EUnit](http://www.erlang.org/doc/apps/eunit/chapter.html) as the runner for my property based tests. There are [some caveats](https://github.com/manopapad/proper#using-proper-in-conjunction-with-eunit), but all in all it works without any issues.
|
||||
What I don't like is defining my property-based tests in the same module as my unit tests, because it feels wrong to do so. I generally define one `_test_` function that calls `proper:module/2`.
|
||||
|
||||
{{< highlight erlang "linenos=table" >}}
|
||||
{% highlight erlang linenos %}
|
||||
proper_module_test_() ->
|
||||
{timeout, 180, ?_assertEqual([], proper:module(example_utils_prop, [long_result, {to_file, user}]))}.
|
||||
{{< / highlight >}}
|
||||
{% endhighlight %}
|
||||
|
||||
This allows me to have all of the unit tests in `example_utils_test` and all of the property-based tests in `example_utils_prop`.
|
||||
|
||||
Let's write a simple property-based test for the `binary_join` function I've [introduced in February]({{ site.url }}/posts/joining-a-list-of-binaries-in-erlang/).
|
||||
|
||||
{{< highlight erlang "linenos=table" >}}
|
||||
{% highlight erlang linenos %}
|
||||
prop_binary_join() ->
|
||||
?FORALL({Bs, S}, {list(binary()), binary()}, begin
|
||||
ExpectedLength = if
|
||||
|
@ -47,7 +44,7 @@ prop_binary_join() ->
|
|||
end,
|
||||
ExpectedLength =:= byte_size(example_utils:binary_join(Bs, S))
|
||||
end).
|
||||
{{< / highlight >}}
|
||||
{% endhighlight %}
|
||||
|
||||
This will test if the size of the output of `example_utils:binary_join/2` is what we would expect:
|
||||
|
||||
|
@ -63,11 +60,11 @@ The `?FORALL` macro takes three aguments:
|
|||
|
||||
If you actually run the test, the output will look something like this:
|
||||
|
||||
{{< highlight text >}}
|
||||
{% highlight text %}
|
||||
Testing example_utils_prop:prop_binary_join/0
|
||||
....................................................................................................
|
||||
OK: Passed 100 test(s).
|
||||
{{< / highlight >}}
|
||||
{% endhighlight %}
|
||||
|
||||
By default, PropEr will generate 100 random valid inputs and compare the output to the (length) properties we defined above.
|
||||
|
|
@ -1,12 +1,11 @@
|
|||
---
|
||||
date: "2014-04-12T21:25:00Z"
|
||||
description: Short description of how to install Erlang/OTP 17.0 using kerl on Mac OS X.
|
||||
tags:
|
||||
- erlang
|
||||
- programming
|
||||
- english
|
||||
slug: installing-erlang-17-0-using-kerl
|
||||
layout: post
|
||||
title: Installing Erlang 17.0 On Mac OS X
|
||||
description: "Short description of how to install Erlang/OTP 17.0 using kerl on Mac OS X."
|
||||
date: 2014-04-12 21:25:00 CEST
|
||||
category: posts
|
||||
tags: [erlang, programming, english]
|
||||
comments: true
|
||||
---
|
||||
|
||||
It's been a few days since [Erlang/OTP 17.0](http://www.erlang.org/news/73) has been released. Installing 64 Bit Erlang (with [Observer](http://www.erlang.org/doc/man/observer.html) support) on Mac OS X has always been a bit tricky, but with 17.0 it has gotten significantly easier.
|
||||
|
@ -19,36 +18,36 @@ First you need to install/get some prerequisites:
|
|||
|
||||
After you've gotten these three, we start by installing *wxWidgets*. It's required in order to use the Observer GUI.
|
||||
|
||||
{{< highlight bash >}}
|
||||
{% highlight bash %}
|
||||
brew install wxmac
|
||||
{{< / highlight >}}
|
||||
{% endhighlight %}
|
||||
|
||||
Compiling *wxWidgets* might take some time. After it's done, you should create a `~/.kerlrc` file. It's basically a configuration file for *kerl*, which will be used for every Erlang version you compile. You can find the list of available options in the [*kerl* readme](https://github.com/spawngrid/kerl#tuning). Mine looks like this:
|
||||
|
||||
{{< highlight bash "linenos=table" >}}
|
||||
{% highlight bash linenos %}
|
||||
CPPFLAGS="-march=native -mtune=native -O3 -g"
|
||||
KERL_CONFIGURE_OPTIONS="--disable-debug --without-javac --enable-shared-zlib --enable-dynamic-ssl-lib --enable-smp-support --enable-threads --enable-hipe --enable-kernel-poll --enable-darwin-64bit --with-wx"
|
||||
KERL_DEFAULT_INSTALL_DIR="$KERL_BASE_DIR/installs"
|
||||
{{< / highlight >}}
|
||||
{% endhighlight %}
|
||||
|
||||
We can now move on to actually installing Erlang/OTP 17.0.
|
||||
|
||||
{{< highlight bash >}}
|
||||
{% highlight bash %}
|
||||
kerl update releases
|
||||
kerl build 17.0 17.0
|
||||
kerl install 17.0 17.0
|
||||
{{< / highlight >}}
|
||||
{% endhighlight %}
|
||||
|
||||
After the installation is finished, *kerl* will output instructions on how to enable a specific Erlang version globally. In general, you will put something like the following in your `~/.zshrc` or `~/.bash_profile`:
|
||||
|
||||
{{< highlight bash >}}
|
||||
{% highlight bash %}
|
||||
source $HOME/.kerl/installs/17.0/activate
|
||||
{{< / highlight >}}
|
||||
{% endhighlight %}
|
||||
|
||||
The following `alias` makes it easier to start the Observer from a terminal. The only command you have to remember is `erlobserver`.
|
||||
|
||||
{{< highlight bash >}}
|
||||
{% highlight bash %}
|
||||
alias erlobserver='erl -sname observer -run observer -detached'
|
||||
{{< / highlight >}}
|
||||
{% endhighlight %}
|
||||
|
||||
![Image of a running Observer instance](/posts/installing-erlang-17-0-using-kerl-1.png)
|
||||
{% image installing-erlang-17-0-using-kerl-1.png %}
|
|
@ -1,12 +1,11 @@
|
|||
---
|
||||
date: "2014-04-18T20:25:00Z"
|
||||
description: Simple workaround for supporting deprecated types and warnings_as_errors.
|
||||
tags:
|
||||
- erlang
|
||||
- programming
|
||||
- english
|
||||
slug: erlang-17-0-supporting-deprecated-types-without-removing-warnings_as_errors
|
||||
layout: post
|
||||
title: 'Erlang 17.0: Supporting Deprecated Types Without Removing warnings_as_errors'
|
||||
description: "Simple workaround for supporting deprecated types and warnings_as_errors."
|
||||
date: 2014-04-18 20:25:00 CEST
|
||||
category: posts
|
||||
tags: [erlang, programming, english]
|
||||
comments: true
|
||||
---
|
||||
|
||||
Erlang 17.0 [deprecated some pre-defined types](http://www.erlang.org/download/otp_src_17.0.readme) like `dict()` and `gb_tree()` in favor of `dict:dict()` and `gb_tree:tree()`. The workaround they suggest (`nowarn_deprecated_type`) works in 17.0, but would break once the deprecated types are removed.
|
||||
|
@ -26,22 +25,22 @@ A rather nice solution to this issue an option that [rebar](https://github.com/r
|
|||
|
||||
This allows us to set a `namespaced_types` option on 17.0+ only.
|
||||
|
||||
{{< highlight erlang >}}
|
||||
{% highlight erlang %}
|
||||
{erl_opts, [
|
||||
{platform_define, "^[0-9]+", namespaced_types},
|
||||
debug_info,
|
||||
warnings_as_errors
|
||||
]}.
|
||||
{{< / highlight >}}
|
||||
{% endhighlight %}
|
||||
|
||||
In your source files you can then check if the option is set and define an (internal) type accordingly.
|
||||
|
||||
{{< highlight erlang "linenos=table" >}}
|
||||
{% highlight erlang linenos %}
|
||||
-ifdef(namespaced_types).
|
||||
-type xxx_dict() :: dict:dict().
|
||||
-else.
|
||||
-type xxx_dict() :: dict().
|
||||
-endif.
|
||||
{{< / highlight >}}
|
||||
{% endhighlight %}
|
||||
|
||||
I learned about this trick by looking at how [meck](https://github.com/eproxus/meck) handles Erlang 17.0 support.
|
|
@ -1,14 +1,11 @@
|
|||
---
|
||||
date: "2014-05-04T21:55:00Z"
|
||||
description: Introductory post about the Riemann client I've written.
|
||||
tags:
|
||||
- erlang
|
||||
- programming
|
||||
- riemann
|
||||
- katja
|
||||
- english
|
||||
slug: katja-riemann-client-written-in-erlang
|
||||
layout: post
|
||||
title: 'Katja: Riemann Client Written In Erlang'
|
||||
description: "Introductory post about the Riemann client I've written."
|
||||
date: 2014-05-04 21:55:00 CEST
|
||||
category: posts
|
||||
tags: [erlang, programming, riemann, katja, english]
|
||||
comments: true
|
||||
---
|
||||
|
||||
[Riemann](http://riemann.io) is a network monitoring system written in Clojure, it offers a rather simple [protobuf](https://de.wikipedia.org/wiki/Protocol_Buffers)-based API. I have just tagged [Katja](https://github.com/nifoc/katja) [version 0.1](https://github.com/nifoc/katja/tree/v0.1), my Riemann client written in Erlang.
|
||||
|
@ -25,12 +22,12 @@ Katja only supports Erlang/OTP R16B01+. This is mostly because in releases befor
|
|||
|
||||
## Sending Events
|
||||
|
||||
{{< highlight erlang "linenos=table" >}}
|
||||
{% highlight erlang linenos %}
|
||||
Event = [{service, "katja demo"}, {metric, 9000.1}],
|
||||
ok = katja:send_event(Event),
|
||||
Event2 = [{service, <<"katja demo">>}, {metric, 9000.1}, {tags, ["demo"]}],
|
||||
ok = katja:send_events([Event, Event2]).
|
||||
{{< / highlight >}}
|
||||
{% endhighlight %}
|
||||
|
||||
Katja allows you to send either a single event or multiple ones. Events are simple property lists with the following (possible) keys: time, state, service, host, description, tags, ttl, attributes, metric.
|
||||
|
||||
|
@ -38,12 +35,12 @@ The entire `katja:event()` type definition can be found on [GitHub](https://gith
|
|||
|
||||
## Sending States
|
||||
|
||||
{{< highlight erlang "linenos=table" >}}
|
||||
{% highlight erlang linenos %}
|
||||
State = [{service, "katja demo"}, {state, "testing"}],
|
||||
ok = katja:send_state(State),
|
||||
State2 = [{service, "katja demo"}, {state, "testing"}, {tags, ["demo"]}],
|
||||
ok = katja:send_states([State, State2]).
|
||||
{{< / highlight >}}
|
||||
{% endhighlight %}
|
||||
|
||||
States and events are very similar, so much so that the (possible) keys of a state property list are almost identical to the keys of an event property list: time, state, service, host, description, tags, ttl, once.
|
||||
|
||||
|
@ -51,9 +48,9 @@ Once again, the entire `katja:state()` type definition can be found on [GitHub](
|
|||
|
||||
## Querying Events
|
||||
|
||||
{{< highlight erlang "linenos=table" >}}
|
||||
{% highlight erlang linenos %}
|
||||
{ok, Events} = katja:query("service = \"katja demo\"").
|
||||
{{< / highlight >}}
|
||||
{% endhighlight %}
|
||||
|
||||
`katja:query/1` will return a list of events. Events are a property list of type `katja:event()`, so what you send to Riemann is also what you get back when querying. There is one important thing to keep in mind: All `undefined` or `[]` values will be removed from the returned property list(s).
|
||||
|
||||
|
@ -61,11 +58,11 @@ You can find example queries in the [Riemann test suite](https://github.com/aphy
|
|||
|
||||
## Sending Entities
|
||||
|
||||
{{< highlight erlang "linenos=table" >}}
|
||||
{% highlight erlang linenos %}
|
||||
Event = [{service, "katja demo"}, {metric, 9000.1}],
|
||||
State = [{service, "katja demo"}, {state, "testing"}],
|
||||
ok = katja:send_entities([{events, [Event]}, {states, [State]}]).
|
||||
{{< / highlight >}}
|
||||
{% endhighlight %}
|
||||
|
||||
Katja also allows you to send mutiple events and/or states in a single request via `katja:send_entities/1`.
|
||||
|
|
@ -1,14 +1,11 @@
|
|||
---
|
||||
date: "2014-12-07T22:59:00Z"
|
||||
description: Example GitLab CI setup that can run tests for Erlang projects.
|
||||
tags:
|
||||
- erlang
|
||||
- testing
|
||||
- programming
|
||||
- gitlabci
|
||||
- english
|
||||
slug: gitlab-ci-for-erlang-projects
|
||||
layout: post
|
||||
title: GitLab CI for Erlang Projects
|
||||
description: "Example GitLab CI setup that can run tests for Erlang projects."
|
||||
date: 2014-12-07 22:59:00 CET
|
||||
category: posts
|
||||
tags: [erlang, testing, programming, gitlabci, english]
|
||||
comments: true
|
||||
---
|
||||
|
||||
[GitLab CI](https://about.gitlab.com/gitlab-ci/) is GitLab's continuous integration software. It integrates with [GitLab](https://about.gitlab.com) and runs your tests every time a commit is pushed to the remote repository. Getting it to run tests for projects written in Erlang can be a bit of hassle, so in this post I will share and explain my setup.
|
||||
|
@ -19,7 +16,7 @@ The CI runner (v5.0.0) is installed on an Ubuntu 14.04 machine and it talks to G
|
|||
|
||||
The build script that is used to run Erlang tests looks like this:
|
||||
|
||||
{{< highlight bash >}}
|
||||
{% highlight bash %}
|
||||
# Setup
|
||||
source "$HOME/.kerl/installs/17.3/activate"
|
||||
gitlabdir=$(basename "$(pwd)")
|
||||
|
@ -42,7 +39,7 @@ ls -rt $(find ./logs -name "cover.html") | tail -n 1 | xargs cat | grep -F 'Tota
|
|||
cd ..
|
||||
mv "$appdir" "$gitlabdir"
|
||||
cd "$gitlabdir"
|
||||
{{< / highlight >}}
|
||||
{% endhighlight %}
|
||||
|
||||
**Setup**
|
||||
|
||||
|
@ -62,9 +59,9 @@ My projects use [erlang.mk](https://github.com/ninenines/erlang.mk), which is wh
|
|||
|
||||
The test coverage is extracted from the latest `cover.html` file. It will be printed to *stdout* like this:
|
||||
|
||||
{{< highlight text >}}
|
||||
{% highlight text %}
|
||||
Total97 %742
|
||||
{{< / highlight >}}
|
||||
{% endhighlight %}
|
||||
|
||||
Since we're only interest in the `97`, we can set the "Test coverage parsing" option to this regular expression: `Total(\d+)\s\%`.
|
||||
|
|
@ -1,29 +1,28 @@
|
|||
---
|
||||
date: "2014-12-22T19:45:00Z"
|
||||
description: Choosing a small number of newer ciphers prevents SSH bots from connecting.
|
||||
tags:
|
||||
- ssh
|
||||
- ops
|
||||
- english
|
||||
slug: ssh-bots-do-not-know-about-my-ciphers
|
||||
layout: post
|
||||
title: SSH Bots Don't Know about My Ciphers
|
||||
description: "Choosing a small number of newer ciphers prevents SSH bots from connecting."
|
||||
date: 2014-12-22 19:45:00 CET
|
||||
category: posts
|
||||
tags: [ssh, ops, english]
|
||||
comments: true
|
||||
---
|
||||
|
||||
Last weekend I decided to update my `sshd_config` to include a very limited set of ciphers, MACs and key exchange algorithms. I did this to tighten the security of my `sshd` and not because I wanted to prevent bots from trying (and failing) to log in to my servers. I'm already using [fail2ban](http://www.fail2ban.org) for that.
|
||||
However, after I updated my configuration I noticed failed login attempts basically dropped to zero, because all these bots do not support my very restrictive set of ciphers.
|
||||
|
||||
{{< highlight text >}}
|
||||
{% highlight text %}
|
||||
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com
|
||||
MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,umac-128-etm@openssh.com
|
||||
KexAlgorithms curve25519-sha256@libssh.org,diffie-hellman-group-exchange-sha256
|
||||
{{< / highlight >}}
|
||||
{% endhighlight %}
|
||||
|
||||
In order to use these settings, you need a recent version of [OpenSSH](http://www.openssh.com). I'm running 6.6 locally and on my servers, the minimum version that supports these settings is 6.4.
|
||||
|
||||
After these changes your `auth.log` will probably contain this line rather often:
|
||||
|
||||
{{< highlight text >}}
|
||||
{% highlight text %}
|
||||
fatal: no matching cipher found: client aes128-ctr,aes192-ctr,aes256-ctr,aes256-cbc,rijndael-cbc@lysator.liu.se,aes192-cbc,aes128-cbc,blowfish-cbc,arcfour128,arcfour,cast128-cbc,3des-cbc server chacha20-poly1305@openssh.com,aes256-gcm@openssh.com [preauth]
|
||||
{{< / highlight >}}
|
||||
{% endhighlight %}
|
||||
|
||||
Please keep in mind that this will not prevent bots from attacking you (in the future) and that you have to mitigate these attacks by other means. I only wrote this post because I thought it was kind of interesting that SSH bots do not support these settings *right now*.
|
|
@ -1,40 +1,39 @@
|
|||
---
|
||||
date: "2014-12-24T13:25:00Z"
|
||||
description: Simple workaround for supporting deprecated types and warnings_as_errors with erlang.mk.
|
||||
tags:
|
||||
- erlang
|
||||
- programming
|
||||
- english
|
||||
slug: supporting-deprecated-types-with-erlang-mk
|
||||
layout: post
|
||||
title: Supporting Deprecated Types with erlang.mk
|
||||
description: "Simple workaround for supporting deprecated types and warnings_as_errors with erlang.mk."
|
||||
date: 2014-12-24 13:25:00 CET
|
||||
category: posts
|
||||
tags: [erlang, programming, english]
|
||||
comments: true
|
||||
---
|
||||
|
||||
In April I wrote about how I use the `platform_define` feature of [rebar](https://github.com/rebar/rebar) to make deprecated types work without removing `warnings_as_errors`. I have switched to [erlang.mk](https://github.com/ninenines/erlang.mk) since then, but needed a similar feature for [one of my libraries](https://github.com/nifoc/noesis).
|
||||
|
||||
Since erlang.mk only uses [make](http://en.wikipedia.org/wiki/Make_(software)), adding support for a `platform_define`-like feature is very straightforward.
|
||||
|
||||
{{< highlight makefile >}}
|
||||
{% highlight makefile %}
|
||||
otp_release = $(shell erl -noshell -eval 'io:format("~s", [erlang:system_info(otp_release)]), init:stop()')
|
||||
otp_17plus = $(shell echo $(otp_release) | grep -q -E "^[[:digit:]]+$$" ; echo $$?)
|
||||
{{< / highlight >}}
|
||||
{% endhighlight %}
|
||||
|
||||
The first variable (`otp_release`) will contain the OTP version (e.g. `17`). The second variable (`otp_17plus`) will be either `0` or `1`, depending on wether or not `otp_release` matches a regular expression. The regular expression checks if `otp_release` is just a number (e.g. `17`) or not (e.g. `R16B03-1`).
|
||||
|
||||
That's enough to conditionally add options to `erlc`.
|
||||
|
||||
{{< highlight makefile >}}
|
||||
{% highlight makefile %}
|
||||
ifeq ($(otp_17plus),0)
|
||||
ERLC_OPTS += -Dnamespaced_types=1
|
||||
TEST_ERLC_OPTS += -Dnamespaced_types=1
|
||||
endif
|
||||
{{< / highlight >}}
|
||||
{% endhighlight %}
|
||||
|
||||
This will define `namespaced_types` only on Erlang 17+, allowing us to use the same `ifdef`-switch from April.
|
||||
|
||||
{{< highlight erlang "linenos=table" >}}
|
||||
{% highlight erlang linenos %}
|
||||
-ifdef(namespaced_types).
|
||||
-type xxx_dict() :: dict:dict().
|
||||
-else.
|
||||
-type xxx_dict() :: dict().
|
||||
-endif.
|
||||
{{< / highlight >}}
|
||||
{% endhighlight %}
|
|
@ -1,14 +1,11 @@
|
|||
---
|
||||
date: "2014-12-31T16:33:00Z"
|
||||
description: 'Link: [media.ccc.de](http://media.ccc.de/browse/congress/2014/31c3_-_6369_-_en_-_saal_1_-_201412272145_-_ecchacks_-_djb_-_tanja_lange.html#video)'
|
||||
tags:
|
||||
- links
|
||||
url: links/a-gentle-introduction-to-elliptic-curve-cryptography
|
||||
layout: link
|
||||
title: A Gentle Introduction to Elliptic-Curve Cryptography
|
||||
date: 2014-12-31 16:33:00 CET
|
||||
category: links
|
||||
link: http://media.ccc.de/browse/congress/2014/31c3_-_6369_-_en_-_saal_1_-_201412272145_-_ecchacks_-_djb_-_tanja_lange.html#video
|
||||
---
|
||||
|
||||
Link: [media.ccc.de](http://media.ccc.de/browse/congress/2014/31c3_-_6369_-_en_-_saal_1_-_201412272145_-_ecchacks_-_djb_-_tanja_lange.html#video)
|
||||
|
||||
A talk by djb and Tanja Lange.
|
||||
|
||||
> This talk will explain how to work with elliptic curves constructively to obtain secure and efficient implementations, and will highlight pitfalls that must be avoided when implementing elliptic-curve crypto (ECC). The talk will also explain what all the buzz in curve choices for TLS is about. This talk does not require any prior exposure to ECC.
|
|
@ -1,14 +1,11 @@
|
|||
---
|
||||
date: "2015-01-05T17:39:00Z"
|
||||
description: 'Link: [www.marco.org](http://www.marco.org/2015/01/04/apple-lost-functional-high-ground)'
|
||||
tags:
|
||||
- links
|
||||
url: links/apple-has-lost-the-functional-high-ground
|
||||
layout: link
|
||||
title: Apple Has Lost the Functional High Ground
|
||||
date: 2015-01-05 17:39:00 CET
|
||||
category: links
|
||||
link: http://www.marco.org/2015/01/04/apple-lost-functional-high-ground
|
||||
---
|
||||
|
||||
Link: [www.marco.org](http://www.marco.org/2015/01/04/apple-lost-functional-high-ground)
|
||||
|
||||
Marco Arment's view on the rapid decline in Apple's software quality.
|
||||
|
||||
> I fear that Apple’s leadership doesn’t realize quite how badly and deeply their software flaws have damaged their reputation, because if they realized it, they’d make serious changes that don’t appear to be happening. Instead, the opposite appears to be happening: the pace of rapid updates on multiple product lines seems to be expanding and accelerating.
|
|
@ -1,13 +1,11 @@
|
|||
---
|
||||
date: "2015-02-01T20:59:00Z"
|
||||
description: Explanation of how to use spiped to encrypt HTTP traffic to backend systems.
|
||||
tags:
|
||||
- spiped
|
||||
- nginx
|
||||
- ops
|
||||
- english
|
||||
slug: securing-http-traffic-with-spiped
|
||||
layout: post
|
||||
title: Securing HTTP Traffic with spiped
|
||||
description: "Explanation of how to use spiped to encrypt HTTP traffic to backend systems."
|
||||
date: 2015-02-01 20:59:00 CET
|
||||
category: posts
|
||||
tags: [spiped, nginx, ops, english]
|
||||
comments: true
|
||||
---
|
||||
|
||||
[spiped](http://www.tarsnap.com/spiped.html) - the secure pipe daemon - is a utility for creating symmetrically encrypted and authenticated pipes between socket addresses. I recently used it to encrypt HTTP traffic from my main webserver to a Raspberry Pi that I run at home.
|
||||
|
@ -18,25 +16,25 @@ I'm using [nginx](http://nginx.org) on the main webserver and the Raspberry Pi,
|
|||
|
||||
After installing spiped, the first thing you have to do is to create a (private) key. I ran the following command on my webserver:
|
||||
|
||||
{{< highlight bash >}}
|
||||
{% highlight bash %}
|
||||
dd if=/dev/urandom bs=32 count=1 of=/usr/local/etc/nginx/certs/spiped.key
|
||||
{{< / highlight >}}
|
||||
{% endhighlight %}
|
||||
|
||||
After that, the key has to be securely copied to the backend machine. You can - for example - do this using `scp`. I copied it to `/etc/nginx/certs/spiped.key`, but your paths may vary.
|
||||
|
||||
That's it. You can now start spiped.
|
||||
|
||||
{{< highlight bash >}}
|
||||
{% highlight bash %}
|
||||
spiped -D -e -s [127.0.0.1]:9080 -t rpi.kempkens.io:9080 -k /usr/local/etc/nginx/certs/spiped.key
|
||||
{{< / highlight >}}
|
||||
{% endhighlight %}
|
||||
|
||||
That command tells spiped to listen on `127.0.0.1:9080`, encrypt the incoming data and send it to `rpi.kempkens.io:9080`.
|
||||
|
||||
To set up the receiving end of the pipe, you have to run something like this:
|
||||
|
||||
{{< highlight bash >}}
|
||||
{% highlight bash %}
|
||||
spiped -D -d -s [0.0.0.0]:9080 -t [127.0.0.1]:80 -k /etc/nginx/certs/spiped.key
|
||||
{{< / highlight >}}
|
||||
{% endhighlight %}
|
||||
|
||||
spiped will listen for incoming connections on port 9080, decrypt the data it receives and forward it to `127.0.0.1:80`.
|
||||
|
||||
|
@ -44,11 +42,11 @@ And that's it.
|
|||
|
||||
On the main webserver I have defined an `upstream` that forwards traffic to `127.0.0.1:9080`.
|
||||
|
||||
{{< highlight nginx >}}
|
||||
{% highlight nginx %}
|
||||
upstream rpi {
|
||||
server 127.0.0.1:9080;
|
||||
}
|
||||
{{< / highlight >}}
|
||||
{% endhighlight %}
|
||||
|
||||
The nginx installation on the Raspberry Pi listens on port 80, so there is nothing special you have to set up there. It will simply receive the decrypted HTTP traffic from spiped. The only thing that you have to keep in mind is that the server name (`Host`-header) has to be the same on the main webserver and the Raspberry Pi.
|
||||
|
|
@ -1,12 +1,11 @@
|
|||
---
|
||||
date: "2015-03-12T22:03:00Z"
|
||||
description: Description and implementation of functions that encode and decode polylines.
|
||||
tags:
|
||||
- erlang
|
||||
- programming
|
||||
- english
|
||||
slug: encoding-and-decoding-polylines-with-erlang
|
||||
layout: post
|
||||
title: Encoding and Decoding Polylines with Erlang
|
||||
description: "Description and implementation of functions that encode and decode polylines."
|
||||
date: 2015-03-12 22:03:00 CET
|
||||
category: posts
|
||||
tags: [erlang, programming, english]
|
||||
comments: true
|
||||
---
|
||||
|
||||
If you have ever worked with the [Google Directions API](https://developers.google.com/maps/documentation/directions/?csw=1) you probably came across [encoded polylines](https://developers.google.com/maps/documentation/utilities/polylinealgorithm). I wanted to decode and encode these using Erlang but was unable to find an existing implementation. So I decided to write my own.
|
||||
|
@ -15,14 +14,14 @@ If you have ever worked with the [Google Directions API](https://developers.goog
|
|||
* A path, i.e. a decoded polyline, is represented as a list of point tuples
|
||||
* An encoded polyline is represented as a simple binary string
|
||||
|
||||
{{< highlight erlang >}}
|
||||
{% highlight erlang %}
|
||||
Point = {Lng, Lat},
|
||||
Path = [{Lng, Lat}, {Lng2, Lat2}].
|
||||
{{< / highlight >}}
|
||||
{% endhighlight %}
|
||||
|
||||
The `encode/1` function takes a path and returns an encoded polyline.
|
||||
|
||||
{{< highlight erlang "linenos=table" >}}
|
||||
{% highlight erlang linenos %}
|
||||
encode(Path) -> encode_acc(Path, 0, 0, <<>>).
|
||||
|
||||
% Private
|
||||
|
@ -42,11 +41,11 @@ encode_part(Num, Result) when Num < 32 -> <<Result/binary, (Num + 63)>>;
|
|||
encode_part(Num, Result) ->
|
||||
Value = (32 bor (Num band 31)) + 63,
|
||||
encode_part(Num bsr 5, <<Result/binary, Value>>).
|
||||
{{< / highlight >}}
|
||||
{% endhighlight %}
|
||||
|
||||
The `decode/1` function takes an encoded polyline and returns a path.
|
||||
|
||||
{{< highlight erlang "linenos=table" >}}
|
||||
{% highlight erlang linenos %}
|
||||
decode(Line) -> decode_acc(Line, 0, 0, []).
|
||||
|
||||
% Private
|
||||
|
@ -69,7 +68,7 @@ decode_part(<<C:8, Rest/binary>>, _OldB, Shift, Result) ->
|
|||
B = C - 63,
|
||||
Result2 = Result bor ((B band 31) bsl Shift),
|
||||
decode_part(Rest, B, Shift + 5, Result2).
|
||||
{{< / highlight >}}
|
||||
{% endhighlight %}
|
||||
|
||||
I have written these functions for [noesis](https://github.com/nifoc/noesis), which is a library that contains useful utility functions. Right now the implementation is only available in the [development branch](https://github.com/nifoc/noesis/blob/development/src/noesis_polyline.erl). It is tested using [EUnit](https://github.com/nifoc/noesis/blob/f3e9ae21d53e09bea9ca48fe4a56ddd006952e0a/test/noesis_polyline_test.erl) and [QuickCheck](https://github.com/nifoc/noesis/blob/f3e9ae21d53e09bea9ca48fe4a56ddd006952e0a/test/noesis_polyline_triq.erl).
|
||||
|
|
@ -1,19 +1,18 @@
|
|||
---
|
||||
date: "2015-07-12T18:46:00Z"
|
||||
description: Introducing my port of the pcsensor utility to FreeBSD.
|
||||
tags:
|
||||
- pcsensor
|
||||
- freebsd
|
||||
- english
|
||||
slug: porting-pcsensor-to-freebsd
|
||||
layout: post
|
||||
title: Porting pcsensor to FreeBSD
|
||||
description: "Introducing my port of the pcsensor utility to FreeBSD."
|
||||
date: 2015-07-12 18:46:00 CEST
|
||||
category: posts
|
||||
tags: [pcsensor, freebsd, english]
|
||||
comments: true
|
||||
---
|
||||
|
||||
Last week, it got rather hot where I live and so I got interested in measuring the temperature of the room where I keep my NAS and various other devices. I started looking for cheap USB thermometers and quickly found [this one](http://www.amazon.de/gp/product/B009RETJIO). It has some decent reviews and costs only around 16€, which seemed perfect to simply play around with.
|
||||
|
||||
The device only comes bundled with Windows software, but there is an open source utility called `pcsensor` which allows you to use it via the command line on Linux. I don't have any Linux devices in the room that I wanted to measure. Since the source code was pretty straightforward and only minimal changes were required to port the utility to FreeBSD, I did just that! You can find the ported source code on [GitHub](https://github.com/nifoc/pcsensor-freebsd).
|
||||
|
||||
{{< highlight text >}}
|
||||
{% highlight text %}
|
||||
$ pcsensor -h
|
||||
pcsensor version 1.0.3
|
||||
Aviable options:
|
||||
|
@ -27,6 +26,6 @@ pcsensor version 1.0.3
|
|||
-d output with Bus and Device number
|
||||
-D display device list
|
||||
-D[n] specific device number
|
||||
{{< / highlight >}}
|
||||
{% endhighlight %}
|
||||
|
||||
If you want to read the temperature from the device, you might have to prefix the call to `pcsensor` with `sudo`.
|
23
archives/posts.html
Normal file
23
archives/posts.html
Normal file
|
@ -0,0 +1,23 @@
|
|||
---
|
||||
layout: page
|
||||
title: Post Archive
|
||||
---
|
||||
|
||||
{% for post in site.posts %}
|
||||
{% capture month %}{{ post.date | date: '%m%Y' }}{% endcapture %}
|
||||
{% capture nmonth %}{{ post.next.date | date: '%m%Y' }}{% endcapture %}
|
||||
|
||||
{% if month != nmonth %}
|
||||
{% if forloop.index != 1 %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
|
||||
<h3>{{ post.date | date: '%B %Y' }}</h3>
|
||||
<ul>
|
||||
{% endif %}
|
||||
|
||||
<li>
|
||||
{% if post.category == "links" %}⚓{% endif %}
|
||||
<a href="{{ post.url }}">{{ post.title }}</a>
|
||||
</li>
|
||||
{% endfor %}
|
20
archives/tags.html
Normal file
20
archives/tags.html
Normal file
|
@ -0,0 +1,20 @@
|
|||
---
|
||||
layout: page
|
||||
title: Tag Archive
|
||||
---
|
||||
|
||||
{% capture site_tags %}{% for tag in site.tags %}{{ tag | first }}{% unless forloop.last %},{% endunless %}{% endfor %}{% endcapture %}
|
||||
{% assign tags_list = site_tags | split:',' | sort %}
|
||||
|
||||
{% for tag in tags_list %}
|
||||
<h2 id="tag-{{ tag }}">{{ tag }}</h2>
|
||||
<ul>
|
||||
{% for post in site.tags[tag] %}
|
||||
{% if post.title != null %}
|
||||
<li>
|
||||
<a href="{{ post.url }}">{{ post.title }}</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endfor %}
|
38
config.toml
38
config.toml
|
@ -1,38 +0,0 @@
|
|||
baseURL = "https://blog.kempkens.io"
|
||||
languageCode = "en-en"
|
||||
title = "tail call"
|
||||
theme = "terminal"
|
||||
paginate = 5
|
||||
|
||||
[params]
|
||||
contentTypeName = "posts"
|
||||
themeColor = "orange"
|
||||
showMenuItems = 2
|
||||
showLanguageSelector = false
|
||||
fullWidthTheme = false
|
||||
centerTheme = true
|
||||
showLastUpdated = false
|
||||
|
||||
[languages]
|
||||
[languages.en]
|
||||
languageName = "English"
|
||||
title = "tail call"
|
||||
subtitle = "The ramblings of a software developer from Germany"
|
||||
owner = "Daniel Kempkens"
|
||||
keywords = ""
|
||||
copyright = ""
|
||||
menuMore = "Show more"
|
||||
readMore = "Read more"
|
||||
readOtherPosts = "Read other posts"
|
||||
newerPosts = "Newer posts"
|
||||
olderPosts = "Older posts"
|
||||
missingContentMessage = "Page not found..."
|
||||
missingBackButtonLabel = "Back to home page"
|
||||
|
||||
[languages.en.params.logo]
|
||||
logoText = "tail call"
|
||||
logoHomeLink = "/"
|
||||
|
||||
[outputFormats]
|
||||
[outputFormats.RSS]
|
||||
baseName = "feed"
|
|
@ -1,6 +0,0 @@
|
|||
https://blog.kempkens.io/*
|
||||
X-Frame-Options: DENY
|
||||
X-XSS-Protection: 1; mode=block
|
||||
X-Content-Type-Options: nosniff
|
||||
Referrer-Policy: no-referrer
|
||||
Content-Security-Policy: default-src 'none'; manifest-src https://blog.kempkens.io; script-src 'self'; img-src 'self'; style-src 'self' 'unsafe-inline'; font-src 'self'; form-action 'none'; frame-ancestors 'none'; base-uri 'self'
|
|
@ -1,74 +0,0 @@
|
|||
---
|
||||
date: "2014-03-30T21:30:00Z"
|
||||
description: A general explanation of how to set up OCSP stapling with nginx.
|
||||
modified: 2015-08-03 20:55:00 CEST
|
||||
tags:
|
||||
- nginx
|
||||
- ocsp
|
||||
- ssl
|
||||
- ops
|
||||
- english
|
||||
slug: ocsp-stapling-with-nginx
|
||||
title: OCSP Stapling with nginx
|
||||
---
|
||||
|
||||
Setting up [OCSP stapling](https://en.wikipedia.org/wiki/OCSP_stapling) with [nginx](http://nginx.org/) is more or less straightforward, but depending on what's in your `ssl_certificate` you might run into some issues with it silently failing. So I've decided to write about how I set up OCSP stapling with certificates from [StartSSL](https://www.startssl.com/) and [CAcert](http://www.cacert.org/).
|
||||
|
||||
I have only tested this using nginx version 1.5.12, but the configuration options I'm using have been in nginx since version 1.3.7. If it does not work for some reason, feel free to leave a comment below.
|
||||
|
||||
To enable OCSP stapling, you simply have to add the following lines to your (already SSL enabled) site:
|
||||
|
||||
{{< highlight nginx >}}
|
||||
resolver 8.8.8.8 8.8.4.4 208.67.222.222 208.67.220.220 valid=300s;
|
||||
resolver_timeout 10s;
|
||||
|
||||
ssl_stapling on;
|
||||
ssl_stapling_verify on;
|
||||
ssl_trusted_certificate /etc/nginx/certs/startssl.stapling.crt;
|
||||
{{< / highlight >}}
|
||||
|
||||
The important thing here is the usage of `ssl_trusted_certificate`. The [nginx documentation](http://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_stapling_verify) states that:
|
||||
|
||||
> For verification to work, the certificate of the server certificate issuer, **the root certificate, and all intermediate certificates** should be configured as trusted using the ssl_trusted_certificate directive.
|
||||
|
||||
You have to include the root certificate (and intermediate certificates) for OCSP stapling to work, which is why `ssl_trusted_certificate` will be set to a *special* certificate file that includes those. If your `ssl_certificate` already does, you might be able to skip using `ssl_trusted_certificate`.
|
||||
|
||||
## CAcert
|
||||
|
||||
For CAcert (and unless you have a Class 3 certificate) you only have to include the Class 1 certificate in your stapling file.
|
||||
|
||||
{{< highlight text >}}
|
||||
03/08/2015 Removed. Get the Class 1 certificate here: http://www.cacert.org/index.php?id=3
|
||||
{{< / highlight >}}
|
||||
|
||||
## StartSSL
|
||||
|
||||
StartSSL - using a Class 1 certificate again - has an intermediate certificate in their chain, so you have to include this one in your stapling file, too.
|
||||
|
||||
{{< highlight text >}}
|
||||
03/08/2015: Removed. Get the root certificate and the intermediate certificate here: https://www.startssl.com/certs/
|
||||
{{< / highlight >}}
|
||||
|
||||
After you've done all that, you can restart nginx or reload the configuration and you should be good to go!
|
||||
|
||||
You can verify that everything works using the `openssl` command line tool.
|
||||
|
||||
{{< highlight bash >}}
|
||||
openssl s_client -servername blog.kempkens.io -connect blog.kempkens.io:443 -tls1 -tlsextdebug -status
|
||||
{{< / highlight >}}
|
||||
|
||||
Both of those should include a section (with data) named "OCSP Response Data".
|
||||
|
||||
An alternative way to test if OCSP stapling is supported, is by using [Qualys SSL Labs](https://www.ssllabs.com/ssltest/).
|
||||
|
||||
Keep in mind that nginx does not include OCSP data in the first response, because it has to fetch it, too. So you probably have to try at least two times to verify if it works or not.
|
||||
|
||||
**Update #1**
|
||||
|
||||
If you have more than one virtual host with SSL enabled, you have to enable OCSP stapling for every single one. Otherwise nginx will fail silently and not include any stapled OCSP data. (Thanks to [@rmoriz](https://roland.io) for figuring this out)
|
||||
|
||||
**Update #2**
|
||||
|
||||
This post has been quite popular, so I decided to update it a little bit. The biggest change is that I have removed the certificate chains that were originally included in this post. I did this to not confuse people too much and because I don't want to keep these chains up to date.
|
||||
|
||||
The most important thing to remember when setting up OCSP stapling with any CA is to make sure that the entire trust chain (including the root certificate) is included in the `ssl_trusted_certificate` file.
|
|
@ -1,27 +0,0 @@
|
|||
---
|
||||
date: "2016-01-31T17:46:00Z"
|
||||
description: Description of how to easily anonymize IPs using HAProxy.
|
||||
tags:
|
||||
- haproxy
|
||||
- ops
|
||||
- english
|
||||
slug: anonymizing-ips-using-haproxy
|
||||
title: Anonymizing IPs Using HAProxy
|
||||
---
|
||||
|
||||
At work, I had to come up with an easy way to anonymize the last octet of a logged IP address in order to comply with German data protection laws. If you're using [HAProxy](http://www.haproxy.org) (1.5+), you can do this in one line.
|
||||
|
||||
If you want to forward the source IP address to a backend server, you would usually use `option forwardfor`. Sadly you can't set or change the forwarded IP using that option, so instead you have to set the `X-Forwarded-For` header manually.
|
||||
|
||||
{{< highlight text >}}
|
||||
http-request set-header X-Forwarded-For %[src,ipmask(24)]
|
||||
{{< / highlight >}}
|
||||
|
||||
This will set the last octet of the source IP address to zero.
|
||||
|
||||
The HAProxy documentation has more information on the various things I used in this post:
|
||||
|
||||
* [option forwardfor](https://cbonte.github.io/haproxy-dconv/configuration-1.5.html#4-option%20forwardfor)
|
||||
* [http-request](https://cbonte.github.io/haproxy-dconv/configuration-1.5.html#4-http-request)
|
||||
* [src sample](https://cbonte.github.io/haproxy-dconv/configuration-1.5.html#7.3.3-src)
|
||||
* [ipmask converter](https://cbonte.github.io/haproxy-dconv/configuration-1.5.html#7.3.1-ipmask)
|
BIN
favicon.ico
Normal file
BIN
favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.1 KiB |
BIN
favicon.png
Normal file
BIN
favicon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.5 KiB |
4
feed-with-links.xml
Normal file
4
feed-with-links.xml
Normal file
|
@ -0,0 +1,4 @@
|
|||
---
|
||||
layout: null
|
||||
---
|
||||
{% include feed.xml id="/feed-with-links.xml" path="/feed-with-links.xml" title=" (with links)" posts=site.posts %}
|
4
feed.xml
Normal file
4
feed.xml
Normal file
|
@ -0,0 +1,4 @@
|
|||
---
|
||||
layout: null
|
||||
---
|
||||
{% include feed.xml path="/feed.xml" posts=site.categories.posts %}
|
43
flake.lock
43
flake.lock
|
@ -1,43 +0,0 @@
|
|||
{
|
||||
"nodes": {
|
||||
"flake-utils": {
|
||||
"locked": {
|
||||
"lastModified": 1652776076,
|
||||
"narHash": "sha256-gzTw/v1vj4dOVbpBSJX4J0DwUR6LIyXo7/SuuTJp1kM=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "04c1b180862888302ddfb2e3ad9eaa63afc60cf8",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1653326962,
|
||||
"narHash": "sha256-W8feCYqKTsMre4nAEpv5Kx1PVFC+hao/LwqtB2Wci/8=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "41cc1d5d9584103be4108c1815c350e07c807036",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixpkgs-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"flake-utils": "flake-utils",
|
||||
"nixpkgs": "nixpkgs"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
18
flake.nix
18
flake.nix
|
@ -1,18 +0,0 @@
|
|||
{
|
||||
description = "blog.kempkens.io build environment";
|
||||
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
|
||||
inputs.flake-utils.url = "github:numtide/flake-utils";
|
||||
|
||||
outputs = { self, nixpkgs, flake-utils }:
|
||||
flake-utils.lib.eachDefaultSystem (system:
|
||||
let
|
||||
pkgs = nixpkgs.legacyPackages.${system};
|
||||
in
|
||||
{
|
||||
devShells.default = pkgs.mkShell {
|
||||
packages = with pkgs; [
|
||||
hugo
|
||||
];
|
||||
};
|
||||
});
|
||||
}
|
12
humans.txt
Normal file
12
humans.txt
Normal file
|
@ -0,0 +1,12 @@
|
|||
---
|
||||
layout: null
|
||||
---
|
||||
/* TEAM */
|
||||
Author: {{ site.author.name }}
|
||||
Contact: {{ site.author.email }}
|
||||
Twitter: {{ site.author.twitter }}
|
||||
From: {{ site.author.location }}
|
||||
|
||||
/* SITE */
|
||||
Last update: {{ site.time | date: '%Y/%m/%d' }}
|
||||
Software: Jekyll, MacVim
|
49
impressum.md
Normal file
49
impressum.md
Normal file
|
@ -0,0 +1,49 @@
|
|||
---
|
||||
layout: page
|
||||
permalink: /impressum/
|
||||
title: Impressum
|
||||
---
|
||||
|
||||
Daniel Kempkens
|
||||
Kugelsberger Weg 6
|
||||
41849 Wassenberg
|
||||
|
||||
Email: {{ site.author.email }}
|
||||
|
||||
## Domains
|
||||
|
||||
* [blog.kempkens.io](https://blog.kempkens.io)
|
||||
* [daniel.xxx](https://daniel.xxx)
|
||||
|
||||
## Security
|
||||
|
||||
### Email
|
||||
|
||||
My public PGP key can be found on [Keybase](https://keybase.io/nifoc) ([.asc file](https://keybase.io/nifoc/key.asc)) and on [pgp.mit.edu](http://pgp.mit.edu/pks/lookup?op=vindex&search=0xB0680B61D48AE85C).
|
||||
Public key fingerprint: `E1B8 971D D5B5 EF35 90B7 730D B068 0B61 D48A E85C`
|
||||
|
||||
### HTTP
|
||||
|
||||
This blog makes use of [HSTS](http://en.wikipedia.org/wiki/HTTP_Strict_Transport_Security), [HPKP](https://developer.mozilla.org/en-US/docs/Web/Security/Public_Key_Pinning), [strong ciphers](https://www.ssllabs.com/ssltest/analyze.html?d={{ site.url | hostname }}&hideResults=on) and various other security-related technologies.
|
||||
|
||||
*German law demands the following statements, too.*
|
||||
|
||||
## Datenschutzerklärung
|
||||
|
||||
Die Nutzung unserer Webseite ist in der Regel ohne Angabe personenbezogener Daten möglich. Soweit auf unseren Seiten personenbezogene Daten (beispielsweise Name, Anschrift oder eMail-Adressen) erhoben werden, erfolgt dies, soweit möglich, stets auf freiwilliger Basis.
|
||||
|
||||
Der Nutzung von im Rahmen der Impressumspflicht veröffentlichten Kontaktdaten durch Dritte zur Übersendung von nicht ausdrücklich angeforderter Werbung und Informationsmaterialien wird hiermit ausdrücklich widersprochen. Die Betreiber der Seiten behalten sich ausdrücklich rechtliche Schritte im Falle der unverlangten Zusendung von Werbeinformationen, etwa durch Spam-Mails, vor.
|
||||
|
||||
### Disqus
|
||||
|
||||
Die Disqus-Kommentierungsfunktion wird von der Big Head Labs, Inc., San Francisco/USA als Dienstleistung zur Verfügung gestellt. Disqus ist ein interaktives Kommentarsystem, das es dem Nutzer ermöglicht, mit nur einer Anmeldung auf allen Internetangeboten, die Disqus als Kommentarsystem verwenden, zu kommentieren. Außerdem können sich die Nutzer über bestehende Accounts bei Facebook (über Facebook Connect), Twitter, Yahoo, Google und OpenID anmelden. Auch die Erstellung von Kommentare ohne Registrierung und Anmeldung (als “Gast”) ist möglich. Nähere Informationen zu Disqus und seinen Funktionen finden Sie unter [www.disqus.com](https://www.disqus.com).
|
||||
|
||||
Ihre Daten werden von Disqus verarbeitet und auf deren System gespeichert. Für die Erhebung, Verarbeitung und Nutzung der betreffenden Daten gelten die Nutzungsbedingungen und Datenschutzhinweise von Disqus, zu finden unter [http://docs.disqus.com/help/30/](http://docs.disqus.com/help/30/) und [http://docs.disqus.com/help/29/](http://docs.disqus.com/help/29/). Wenn Sie sich über Ihren Facebook-, Twitter-, Yahoo-, Google oder OpenID-Account anmelden, werden auch von diesen Anbietern möglicherweise Daten erhoben, gespeichert und genutzt. Einzelheiten dazu finden sich in den Datenschutzbestimmungen des jeweiligen Anbieters.
|
||||
|
||||
### Google Analytics
|
||||
|
||||
Diese Website benutzt Google Analytics, einen Webanalysedienst der Google Inc. ("Google"). Google Analytics verwendet sog. "Cookies", Textdateien, die auf Ihrem Computer gespeichert werden und die eine Analyse der Benutzung der Website durch Sie ermöglichen. Die durch den Cookie erzeugten Informationen über Ihre Benutzung dieser Website werden in der Regel an einen Server von Google in den USA übertragen und dort gespeichert. Im Falle der Aktivierung der IP-Anonymisierung auf dieser Webseite wird Ihre IP-Adresse von Google jedoch innerhalb von Mitgliedstaaten der Europäischen Union oder in anderen Vertragsstaaten des Abkommens über den Europäischen Wirtschaftsraum zuvor gekürzt.
|
||||
|
||||
Nur in Ausnahmefällen wird die volle IP-Adresse an einen Server von Google in den USA übertragen und dort gekürzt. Im Auftrag des Betreibers dieser Website wird Google diese Informationen benutzen, um Ihre Nutzung der Website auszuwerten, um Reports über die Websiteaktivitäten zusammenzustellen und um weitere mit der Websitenutzung und der Internetnutzung verbundene Dienstleistungen gegenüber dem Websitebetreiber zu erbringen. Die im Rahmen von Google Analytics von Ihrem Browser übermittelte IP-Adresse wird nicht mit anderen Daten von Google zusammengeführt.
|
||||
|
||||
Sie können die Speicherung der Cookies durch eine entsprechende Einstellung Ihrer Browser-Software verhindern; wir weisen Sie jedoch darauf hin, dass Sie in diesem Fall gegebenenfalls nicht sämtliche Funktionen dieser Website vollumfänglich werden nutzen können. Sie können darüber hinaus die Erfassung der durch das Cookie erzeugten und auf Ihre Nutzung der Website bezogenen Daten (inkl. Ihrer IP-Adresse) an Google sowie die Verarbeitung dieser Daten durch Google verhindern, indem sie das unter dem folgenden Link verfügbare Browser-Plugin herunterladen und installieren: [http://tools.google.com/dlpage/gaoptout?hl=de](http://tools.google.com/dlpage/gaoptout?hl=de).
|
47
index.html
Normal file
47
index.html
Normal file
|
@ -0,0 +1,47 @@
|
|||
---
|
||||
layout: default
|
||||
title: Home
|
||||
---
|
||||
|
||||
<div class="posts">
|
||||
{% for post in paginator.posts %}
|
||||
<div class="post">
|
||||
<h1 class="post-title">
|
||||
{% if post.category == "links" %}
|
||||
⚓ <a href="{{ post.link }}" rel="external">{{ post.title }}</a>
|
||||
{% else %}
|
||||
<a href="{{ post.url }}">{{ post.title }}</a>
|
||||
{% endif %}
|
||||
</h1>
|
||||
|
||||
<span class="post-date">
|
||||
{{ post.date | date_to_string }}
|
||||
·
|
||||
<a href="{{ post.url }}" title="Permalink">∞</a>
|
||||
</span>
|
||||
|
||||
{% if post.category == "links" %}
|
||||
{{ post.content }}
|
||||
{% else %}
|
||||
{{ post.excerpt }}
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<div class="pagination">
|
||||
{% if paginator.next_page %}
|
||||
<a class="pagination-item older" href="{{ site.baseurl }}page{{paginator.next_page}}">Older</a>
|
||||
{% else %}
|
||||
<span class="pagination-item older">Older</span>
|
||||
{% endif %}
|
||||
{% if paginator.previous_page %}
|
||||
{% if paginator.page == 2 %}
|
||||
<a class="pagination-item newer" href="{{ site.baseurl }}">Newer</a>
|
||||
{% else %}
|
||||
<a class="pagination-item newer" href="{{ site.baseurl }}page{{paginator.previous_page}}">Newer</a>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<span class="pagination-item newer">Newer</span>
|
||||
{% endif %}
|
||||
</div>
|
|
@ -1,4 +0,0 @@
|
|||
<div>
|
||||
<a href="https://keyoxide.org/028BCE9BABB5145AAAA1FB8410BE1D47E5ADFF92" rel="me">Keyoxide</a>
|
||||
<a href="https://mastodon.kempkens.io/@daniel" rel="me">Mastodon</a>
|
||||
</div>
|
6
robots.txt
Normal file
6
robots.txt
Normal file
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
layout: null
|
||||
---
|
||||
User-agent: *
|
||||
Allow: /
|
||||
Sitemap: {{ "/sitemap.xml" | prepend: site.url }}
|
29
sitemap.xml
Normal file
29
sitemap.xml
Normal file
|
@ -0,0 +1,29 @@
|
|||
---
|
||||
layout: null
|
||||
---
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
||||
<url>
|
||||
<loc>{{ site.url }}</loc>
|
||||
<lastmod>{{ site.time | date_to_xmlschema }}</lastmod>
|
||||
</url>
|
||||
|
||||
{% for page in site.pages %}
|
||||
{% if page.title != null and page.url != '/404.html' %}
|
||||
<url>
|
||||
<loc>{{ site.url }}{{ page.url }}</loc>
|
||||
</url>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
{% for post in site.posts %}
|
||||
<url>
|
||||
<loc>{{ site.url }}{{ post.url }}</loc>
|
||||
{% if post.modified != nil %}
|
||||
<lastmod>{{ post.modified | date_to_xmlschema }}</lastmod>
|
||||
{% else %}
|
||||
<lastmod>{{ post.date | date_to_xmlschema }}</lastmod>
|
||||
{% endif %}
|
||||
</url>
|
||||
{% endfor %}
|
||||
</urlset>
|
|
@ -1 +0,0 @@
|
|||
Subproject commit e0213b0d4eaa399770a11be1faff89529b2ced0b
|
Loading…
Reference in a new issue