Sunday, March 16, 2025

Basic Queue in TypeScript

/**
* @class
* Class representing an Item in a Queue
*/
class Item {
constructor(v) {
this.v = v;
this.n = null;
}
val() {
return this.v;
}
setNext(i) {
this.n = i;
}
next() {
return this.n;
}
}

/**
* @class
* Class representing a Queue
*
* This is a basic linked list impelementation for a FIFO queue.
*/
class Queue {
constructor(item) {
if (undefined == item) {
this.items = 0;
this.start = null;
this.end = null;
this.item = null;
return;
}
this.items = 1;
this.item = item;
this.start = item;
this.end = item;
}
add(item) {
if (this.items == 0) {
this.items = 1;
this.item = item;
this.start = item;
this.end = item;
} else {
this.items++;
let tail = this.end;
tail.setNext(item);
this.end = item;
}
}
pop() {
let item = this.start;
if (null != item.next()) {
this.start = item.next();
}
this.items--;
return item;
}
}

Min Priority Queue in TypeScript

/**
* @class
* Class representing a min/max PriorityQueue
*
* The default is a minPriorityQueue if no value is used in the constructor.
* If you want a maxPriorityQueue, pass true in the constructor e.g.
*
* let maxPQ = new PriorityQueue(true);
* let minPQ = new PriorityQueue();
*
* AppsScripts won't let you define private methods, so here are the public methds
*
* add(v {int}) // adds a new value into the queue
* contains(v {int}) // returns true if the queue contains the value
* isEmpty() {bool} // returns true if there are no ints in the queue
* pop() {int} // returns the min/max value in the queue
*
*/
class PriorityQueue {
constructor(isMax) {
if (undefined == isMax || null == isMax || false == isMax) {
this.isMax = false;
} else {
this.isMax = true;
}
this.heap = [];
this.index = new Map();
this.size = this.heap.length;
}
contains(v) {
return this.index.has(v);
}
exch(i, j) {
let t = this.heap[i];
this.heap[i] = this.heap[j];
this.index.set(t, j);
this.index.set(this.heap[j], i);
this.heap[j] = t;
}
add(v) {
if (this.contains(v)) return;
this.size++;
this.heap[this.size] = v;
this.swim(this.size);
this.index.set(v, this.size);
}
isEmpty() {
return this.size == 0;
}
length() {
return this.size;
}
less(i, j) {
if (this.isMax) {
return this.heap[i] < this.heap[j];
}
return this.heap[i] > this.heap[j];
}
pop() {
let max = this.heap[1];
this.exch(1, this.size--);
this.heap.pop();
this.sink(1);
return max;
}
sink(n) {
while (n*2 <= this.size) {
let j = n*2;
if (j < this.size && this.less(j, j+1)) j++;
if (!this.less(n, j)) break;
this.exch(n, j);
n = j;
}
}
swim(n) {
while (n > 1 && this.less(Math.floor(n/2), n)) {
this.exch(Math.floor(n/2), n);
n = Math.floor(n/2);
}
}
}

function testArray() {
console.log("Testing a min PQ.");
let t = new PriorityQueue(false);
console.log("Inserting 7, 5, 9, 3, 10, 1");
t.add(7);
t.add(5);
t.add(9);
t.add(7);
t.add(3);
t.add(10);
t.add(1);
console.log(`Does the queue contain 5? : ${t.contains(5)}`);
console.log(`Does the queue contain 3? : ${t.contains(3)}`);
let incr = 0;
let output = "";
while (!t.isEmpty() && incr++ < 10)
output += `${t.pop()} `;
console.log(`Output: ${output}`);
console.log("Testing a max PQ.");
console.log("Inserting 7, 5, 9, 3, 10, 1");
output = "";
t = new PriorityQueue(true);
t.add(7);
t.add(5);
t.add(9);
t.add(3);
t.add(10);
t.add(1);
console.log(`Does the queue contain 5? : ${t.contains(5)}`);
console.log(`Does the queue contain 3? : ${t.contains(3)}`);
incr = 0;
while (!t.isEmpty() && incr++ < 10)
output += `${t.pop()} `;
console.log(`Output: ${output}`);
}

Thursday, January 2, 2025

Updating your expired GPG keys

 If you ever need to update your expired gpg keys, it's not terrible. First thing to do is to figure out which key you're working with, use

$> gpg --list-keys


Which should show you something like this

-----------------------------

pub   rsa3072 2020-12-29 [SC] [expired: 2024-12-31]

      09CF4ABCD7487EF21E9AFC859B4CE836EAAF3E31

uid   [ expired] Russell Simpkins <russellsimpkins@gmail.com>


Then you can edit the key using the ID

$> gpg --edit-key 09CF4ABCD7487EF21E9AFC859B4CE836EAAF3E31

gpg (GnuPG) 2.2.19; Copyright (C) 2019 Free Software Foundation, Inc.

This is free software: you are free to change and redistribute it.

There is NO WARRANTY, to the extent permitted by law.

Secret key is available.

sec  rsa3072/9B4CE836EAAF3E31

     created: 2020-12-29  expires: 2026-01-02  usage: SC

     trust: ultimate      validity: ultimate

ssb  rsa3072/CC533814855BD92B

     created: 2020-12-29  expired: 2024-12-31  usage: E

[ultimate] (1). Russell Simpkins <russellsimpkins@gmail.com>


To change or update the expiration time, type the following

$> expire


It will prompt you to choose, I like to update mine yearly but you can pick whatever option you want. If you have a sub key like I do, then you will want to update that as well. Just pick the key using the following command

$> key 1

gpg> key 1

sec  rsa3072/9B4CE836EAAF3E31

     created: 2020-12-29  expires: 2026-01-02  usage: SC

     trust: ultimate      validity: ultimate

ssb* rsa3072/CC533814855BD92B

     created: 2020-12-29  expired: 2024-12-31  usage: E

[ultimate] (1). Russell Simpkins <russellsimpkins@gmail.com>


Notice that ssb has an asterisks next to it, that's how you know you're editing the sub key. Follow the same and type "expire" to set it's expiration date. That's it. Type "quit" to exit.

Wednesday, March 23, 2022

App Scripts are pretty neat

I was doing some analysis and looking to create a graph in my Google Spreadsheet. The data had gaps and I was looking for a quick/easy way to set all empty cells to zero. I found an example after searching that I turned into the following:

function fillBlanks() {
var sheet = SpreadsheetApp.getActiveSheet();
var sheetLR = sheet.getLastRow();
var sheetLC = sheet.getLastColumn();
var range = sheet.getRange(1, 1, sheetLR, sheetLC);
var values = range.getValues();
for (var r = 1; r < sheetLR; r++) {
for (var c = 0; c < sheetLC; c++) {
if(String(values[r][c]).trim() == "") {
values[r][c] = 0;
}
}
}
range.setValues(values);
}
fillBlanks()

Have your spreadsheet open, then run it from App Scripts. Feel free to take, modify and re-use as you see fit.

Sunday, March 14, 2021

gRPC SSL certs on Windows with WSL2

The past few days has been one hell of a nightmare. I have been creating gRPC gateway services and we wanted to implement server side HTTP2 push. Turns out HTTP2 & gRPC requires you to implement SSL to take advantage of HTTP2 push in the browser. Implementing SSL is a pain in the a$$. You need to make sure everything is running with the correct hostnames and almost all of the documentation out there is for folks who have said the heck with it and opted for self signed certificates. I couldn't find any good tutorials for setting up a valid SSL cert. 

I ran into too many issues. The first one that took many hours to identify, if you are doing your development on Windows 10 + WSL2, it's not straight forward how best to update /etc/hosts. Recall that for HTTPS to work, you need the hostname to match which means you need DNS to resolve. Here's how to fix this. Open up a terminal on your linux (I use Ubuntu) and run ifconfig. Take the result of that and update your /etc/hosts file (this requires root/sudo.) Then open up notepad as an Administrator and update your c:\Windows\System32\drives\etc\hosts and save that. 

If you do this, you can successfully open up your web browser and use  your custom domain name AND use curl to test your custom domain name. 

I lost countless hours on this one part - trying like hell to figure out why my custom domain wasn't resolving to 127.0.0.1. I think Windows/WSL2 have something about 127.0.0.1 and the secret was getting the IP for the WSL2 Linux distro.

Once you've got your hostnames configured in /etc/hosts, you can move on to securing your golang services. One thing you may need to do is to concatenate your SSL certificate with the providers intermediary certificate. This isn't terribly difficult. Simple create a new text file, have your valid cert in PEM format then a new line and the intermediary. If done correctly, it will look like this: (these are not real - this is just an example)

-----BEGIN CERTIFICATE-----
t1befnVnPZlIlyEMDw/WPyR4Bfi1cemqjaKSxzR+lEtCTQC7xKM8678RMHBtZ7/v
NsfRUitk6otcQnBX/sErZmFxqdPyl7aOpifkj+pQV0mfn3bGTPhSPfc6BM84lZo8
W2HRMbWsw5Z5ZIwEfMEbaCtbtw==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDdzCCAl+gAwIBAgIEAgAAuTANBgkqhkiG9w0BAQUFADBaMQswCQYDVQQGEwJJ
RTESMBAGA1UEChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYD
-----END CERTIFICATE-----


You can start your gRPC service over TLS like this:

serverOptions := []grpc.ServerOption{
    grpc.UnaryInterceptor(interceptor.Unary()),
    grpc.StreamInterceptor(interceptor.Stream()),
}
if enableTLS {
    tlsCredentials, err := credentials.NewServerTLSFromFile(serverCertFile, serverKeyFile)
    if err != nil {
        return fmt.Errorf("cannot load TLS credentials: %w", err)
    }
    serverOptions = append(serverOptions, grpc.Creds(tlsCredentials))
}
s := grpc.NewServer(serverOptions...)

That will load the gRPC service over TLS in manner that any client can validate the SSL certificate. It's important to note that you may not need to add the intermediary certificate. Try starting your gRPC service and then connecting to it. I found this handy curl command for testing gRPC HTTP2 POSTs to get Streaming responses.

curl -i --http2 -H "Content-Type: application/grpc" -H "TE: trailers" -X POST https://youhost.com/grpc.Service/Message

Feel free to add -vvv to that if you're having issues. Now that you have your golang gRPC service running under TLS, you just need to adjust your gateway client to handle HTTPS connections. Here's a snippet:

config := &tls.Config{
    InsecureSkipVerify: false,
}
return grpc.DialContext(ctx, addr, grpc.WithTransportCredentials(credentials.NewTLS(config)))


With that, your gateway can connect to the gRPC service using TLS. If you've got Nginx in front of everything, you may find this blog helpful. I found that it was 90% correct. They share some configuration that mostly works, but my version of Nginx didn't like putting the locations outside of the server entry. 

Wednesday, December 30, 2020

Managing passwords with GPG

I recently moved back to working on Windows. I've been really enjoying the development setup of Windows 10 with WDL 2 which allows me to have Ubuntu right at hand. The one downside is that I can't open my Apple encrypted .dmg file. I have been in the habit of keeping my passwords in a clear text file, but storing it on an encrypted disk image that I keep on my cloud back up drive. I know that these companies claim to not check the contents, but this makes it nearly impossible. Now that I'm on Windows, I needed another solution and decided to get back to gpg. I created the following bash functions to let me view my password file quickly and easily.

Here's the code in a gist. I put the logic in my ~/.bashrc so that I can access the file by typing "pw". When I'm done getting information or adding a new password, the code re-encrypts the file, cleans up the temporary file and maintains a minimum backup of previous encrypted files.


Monday, December 21, 2020

Fun things to do with Docker

I recently got back to doing some dev work and I'm using docker. Docker is nice in that you can make build boxes and run them which can be really handy if you're developing on a mac but your code is destined for some linux variant.

One thing I found helpful today was to mount my local project so that I could run inside my Docker centos box. I did it with something like this:

$> docker run -di --mount type=bind,source="/path/to/project/folder",target=/var/opt/project"image:tag"

When run in that manner, I can have a container running that has my local project in /var/opt/project. I can edit the code and run it after shelling onto the box. Other common commands I use a lot.

$> docker ps
$> docker exec -it <id> /bin/bash
$> docker stop <id>


Saturday, October 14, 2017

Starting out right with Python

If you're new to python or you don't program in python as often as you'd like to, it's key to start off by setting up your environment. Make sure you install a virtual environment tool. I like to use virtualenvwrapper. Virtualenvwrapper helps keep your pip installs clean and separate. It's super easy to set up and use.

$ pip install virtualenvwrapper

That's all you need to do for installation, simple. Once you have it installed you need to configure it. I work on a mac and on the terminal in linux environments. I normally use a projects folder and under that a python-projects folder. If you don't have a preference, go ahead and follow suit.

mkdir -p ~/projects/python-projects
Now you can go ahead and update your bashrc or your bash_profile depending on your preferences.

$ echo "source /usr/local/bin/virtualenvwrapper.sh" >> ~/.bash_profile
$ echo "export WORKON_HOME=$HOME/projects/python-projects" >> ~/.bash_profile 
$ source ~/.bash_profile


Once that's done you can start using virtualenvwrapper.


$ mkvirtualenv sample
$ workon sample 


The first command will create a new virtual environment directory e.g. $HOME/projects/python-projects/sample. Now if you run any pip installs, the libraries and binaries will be in that directory and will not pollute any of your core system libraries. This will help you in the future, especially if you decide to share your code because all of your pip dependencies can now be easily listed e.g.

pip list --format columns

That's it. Happy python coding.

Thursday, September 22, 2016

Packing perl modules into RPMs

Yesterday I was tasked with packing up some perl code into RPM. That was the easy part. The hard part was the dependency graph I created for myself. Maybe there's an easy way to tell an RPM that it can get it's files from /usr/share/RPM, but that part eluded me. Instead I decided I should package up my modules into RPMs. Here's a few things that made this task easier. For starters, I had some perl on the machine, so I didn't have to configure cpan. 

1. Install cpanm 

$> perl -MCPAN -e shell
cpan[1]> install App::cpanminus
cpan[1]> exit 

2. Install cpan2rpm. There was a bug in the source. So, I had to fix the file before it would install. I got this <file> 

http://search.cpan.org/CPAN/authors/id/E/EC/ECALDER/cpan2rpm-2.028.tar.gz

Here's the steps:

$> cd /usr/local/src
$> wget <file>
$> tar -zxf cpan2rpm-2.028.tar.gz
$> cd cpan2rpm-2.028
$> sed -i"" "s|Pod::Text|Pod::PlainText|g" cpan2rpm
$> cd ../ && tar -czf cpan2rpm-2.028.tar.gz cpan2rpm-2.028
$> cpanm ./cpan2rpm-2.028.tar.gz

Cpan2prm is a pretty nice utility. It will run through the paces, creating an rpm and a source rpm. I ran into one issue while building the namespace::clean module. For some reason, the RPM added a _Util.pm file, that it provided, but then created a "Requires: perl(namespace::clean::_Util)" dependency. The solution I used was to extract the source from the source RPM and then modify the spec file to manually add a "Provides" e.g.

provides: perl(ExtUtils::HasCompiler) = 0.014 
provides: perl(namespace::clean) = 0.27

provides: perl(namespace::clean::_Util) = 0.27

Here's how you can extract a source rpm, edit the file and then build using the spec file:

$> cd ~/rpmbuild/SRPMS
$> mkdir perl-namespace-clean && cd perl-namespace-clean
$> rpm2cpio ../perl-namespace-clean-0.27-1.src.rpm|cpio -idmv
$> emacs namespace-clean.spec
$> cp namespace-clean-0.27.tar.gz ~/rpmbuild/SOURCES
$> rpmbuild -ba namespace-clean.spec

Friday, July 22, 2016

Making selinux work for you

I'm no selinux expert, and I got asked to figure out why all these audit logs were showing up in /var/log/messages that looked like this:

Jul 22 21:21:46 du-proc01 kernel: type=1400 audit(1469222506.799:118232): avc:  denied  { read } for  pid=25450 comm="httpd" name="feed_status.json" dev=xvdj ino=4325992 scontext=unconfined_u:system_r:httpd_t:s0 tcontext=system_u:object_r:var_t:s0 tclass=file

It turns out that my auditd daemon was dead and selinux was set to permissive mode. When selinux is in permissive mode, it writes permission failures, like the one above, to /var/log/audit/audit.log, or if auditd is dead to /var/log/messages. 

After some interesting back and forth on slack, I wanted to know if there was an easy way to enable selinux, without causing a bunch of headaches for my colleagues. Yes, there is.

Selinux will run in permissive mode. When in permissive mode, selinux will log all access violations. Put selinux into permissive mode with the following:

setenforce 0

Then let your system run for a while. Or, if you have integration or acceptance tests, go ahead and run them. Try to execute all of the possible operations that might be blocked by selinux. You can generate a rule to fix all of your broken permissions with following command:

cat  /var/log/audit/audit.log|audit2allow -m

That command will generate the rule in human readable form, so you can verify what rules would need to be added. To generate the module, run:

cat  /var/log/audit/audit.log|audit2allow -M <module_name>

Where <module_name> makes sense to you. Once that is run, you can turn it on with 

semodule -i <module_name>

If you generate the module with -M, there will be a file you can copy onto other machines. The .pp should be installed here:

/etc/selinux/targeted/modules/active/modules

Once you're satisfied with your module and your system isn't generating any selinux access denied messages you can start enforcing, but don't just re-enable selinux. You will need to relabel the file system

touch /.autorelabel
shutdown -r now

Then you can start enforcing:

setenforce 1

It's just that easy to take an existing system and get it working with selinux. 

Wednesday, June 29, 2016

Testing Varnish-Cache

Originally posted here.
Our team has many RestFul APIs written in PHP that serve information stored in a MySQL database. The content we serve supports many sections of the paper and that means we get a lot of traffic. The majority of our content is not personalized and we control how often that content is updated, which makes it attractive to cache. Before adding Varnish to our stack, we spent significant effort dealing with load spikes, especially at the database layer. The applications that were causing the most pain had a very large, complicated code base and fixing that code or even replacing that code was estimated at six or more months. We wanted a solution that we could implement quickly and didn't require a major code rewrite. We wanted a solution that would take the pressure off of our application servers and our database servers to remove the distractions of dealing with production load alerts. Varnish was the perfect solution to our problem. By adding Varnish in front of our API servers, we took a huge load off of our API and database servers.

At first we added basic caching, but as we used the software and got more experience we found different things we could do in the varnish layer. Varnish has a lot of features and capabilities. Taking advantage of the software inevitably results in a lot of configuration and tweaking. Some functionality can be achieved by editing the varnish configuration language (VCL) and sometimes you need to add a module or create your own module. Writing VCL or working with varnish modules (vmods) leads to testing, which is what I'm writing about today; how to test Varnish. This post will focus on how to test VCL and if I have time, I will add another post to talk about how to test varnish modules.

Let’s imagine a real-life feature and how we can go about testing the feature. Our javascript programmers use JQuery and sometimes turn on JQuery cache busting. When enabled, JQuery adds a timestamp e.g. “_1331829184859=” to the query string in an attempt to bust the cache. So, if I strip the query string parameter, I can prevent JQuery from busting our cache. Here's one way I could clean our URL using VCL:


The first “if” statement strips the “_1331829184859=” and the rest of the lines are there to clean the url so it isn't left with unneeded characters. The URL is reset on line 11.

How can I test this code? I could start a varnish server with a backend apache or nginx instance that logs requests, issue a variety of curl requests and then manually verify the logs, but there’s an easier way. Varnish ships with the ability to test using the testing tool varnishtest. Varnishtest gives you the ability to write VCL tests you can run on the command line or as part of your build process. Here's an example:


Line 1 is just for documentation purposes. Lines 2 - 6 define a server that will accept a request and issue a response. Since I intend to clean the URL before it get’s passed to a backend server, I added a test to verify the backend URL sent to the server with the “expect” syntax on line 4. If the value doesn’t match, the test stops and you get lots of debugging style output.

Line 7 is the syntax to add a backend definition and VCL. Line 8 is optional. I added it to illustrate you're allowed to import vmods. At line 9 I define the vcl_recv subroutine and lines 10 – 16 has the logic I need to remove the cache busting parameter.

Lines 19 – 21 define vcl_deliver to set a response header. I did this to illustrate one way to validate logic during a client request. Line 23 is where I define a client. The client's are where you issue requests with the txreq (transmit request) and receive the response with rxresp (receive response.) At line 26, I use expect to verify the test variable is as expected. Adding the "expect" in the client logic is a better way to test multiple inputs. I kept the example short, but it's trivial to add multiple txreq -url, rxresp, expect lines to test different inputs. You can also copy and paste the client c1 to create c2, c3...cN clients.

Line 28 is where I run the client logic.

Our tests are saved in files e.g. test01.vtc. Assuming you compiled and installed varnish in the standard locations, running varnishtest is this easy:


When the test fails, you get a lot of output to look at. Normally, your good tests produce very little output. However, you can run your tests in “verbose” mode (-v) to get full output. For the simple test above, a passing test in verbose mode produces 253 lines of output. A failing tests produces less lines of output, but only a few less: 189.

Varnishtest allows you some flexibility as well. If you’re building and testing tweaks to varnish, you can specify what varnishd to use with –D e.g.


You also use the -D option to your advantage and pass variables to your test, since ${varnishd} (or anything else defined with –D) will be available to your VCL when the test is compiled and run. For example, when building and compiling custmo varnish modules, you can import the library from the build directory e.g.



Conclusion


Testing with varnishtest made a huge impact when working with my VCL. I can quickly and easily add logic and test theories on my local vagrant box. There is a bit of a learning curve to get started with varnishtest, but the documentation is getting better and there are good examples to follow on the internet.

Wednesday, April 13, 2016

crontab reminder

# For details see man 4 crontabs
# Example of job definition:
# .---------------- minute (0 - 59)
# | .------------- hour (0 - 23)
# | | .---------- day of month (1 - 31)
# | | | .------- month (1 - 12) OR jan,feb,mar,apr ...
# | | | | .---- day of week (0 - 6) (Sunday=0 or 7) OR sun,mon,tue,wed,thu,fri,sat
# | | | | |
# * * * * * user-name command to be executed

Tuesday, April 12, 2016

Getting Started with RPMs

Red Hat Package Manager (RPM) is a way to package and deploy software on Red Hat variants that’s similar to Advanced Packaging Tool (APT) on Ubuntu variants. To create RPM files, you can use third party tools like Ant or FPM, but with a little bit of effort you can quickly write your own spec files. This paper isn’t intended to be a complete reference on RPM files. There are a couple of great resources out there to get the full scoop on RPMs and I’ve added them at the end of this paper.
To make an RPM you need two things, a spec file and the rpmbuild program. You can get rpm-build easily enough e.g.



$> yum install –y rpm-build rpmdevtools
$> rpmdev-setuptree


The last command will create your rpm build tree e.g.

/home/user/rpmbuild
/home/user/rpmbuild/RPMS
/home/user/rpmbuild/SPECS
/home/user/rpmbuild/BUILD
/home/user/rpmbuild/SOURCES
/home/user/rpmbuild/SRPMS


If you put your tree somewhere else or if you want to be explicit, add an .rpmmacros file to your home directory.

$> echo “%packager First Last 
%vendor Your Company
%_topdir ${HOME}/rpmbuild” > ~/.rpmmacros

Now you have the basic requirements and we can start writing spec files. However, before we dive into the spec file it helps to understand that spec files have macros and that macros can be a single value or a multi-line value. Here is how we define a variable in a spec file:

%define name value

You can conditionally set the variable like so:

%{!?_version: %define _version 1.0.0}


When you use the variable you use you use the %{} syntax e.g.

Version: %{_version}


You can conditionally use a variable e.g.

Release: 1%{?dist}


The %{?dist} syntax means, output the value of “dist” if it’s set, otherwise output nothing. rpmbuild will fail if you reference macros that are not defined. If you want to see what the current value is of a predefined variable you can run the following command:

$> rpm --eval '%{_rpmdir}'


To get a list of all pre-defined variables, see the links at the end. If you want to see everything you can use either of these two commands:

$> rpm --showrc | less
$> rpmbuild --showrc | less


I piped them to less in my example because there’s a lot of information when you run either of those commands. Since we can define variables and show them, if you wanted to play around you can:

$> rpm --define '_some value' --eval '%{_some}'
value


You can also include variables listed in a separate file

%include some.spec.file


When you define variables in a separate file, you can define macros that span multiple lines. Let’s assume you have a set of application specific preparation steps. Create your steps in a separate file e.g. mysteps.spec Once you do that you can define your macro e.g. Now you have a macro “prepsteps” that can be used in any step after it’s defined. Here’s how you can use it in the “prep” phase e.g.

%prep
%prepsteps


Let’s pull some of this together with a simple example spec file:

$> rpmdev-newspec ex.spec
$> cat ex.spec
Name:           ex
Version:      
Release:        1%{?dist}
Summary:      
Group:        
License:      
URL:          
Source0:      
BuildRequires:
Requires:     
%description
 
%prep
%setup -q
 
%build
%configure
make %{?_smp_mflags}
 
%install
rm -rf $RPM_BUILD_ROOT
make install DESTDIR=$RPM_BUILD_ROOT
 
%clean
rm -rf $RPM_BUILD_ROOT
 
%files
%defattr(-,root,root,-)
%doc
%changelog


If you run that with rpmbuild you’re going to get your first taste of errors. Since we’re playing around, let’s just try to run the prep phase.

$> rpmbuild -bp ex.spec
error: line 2: Empty tag: Version:


Unfortunately, rpmbuild only reports the first error and not the rest. Here is what it looks like if we fill it out some more:

Name:           ex
Version:        1.0.0
Release:        1%{?dist}
Summary:        This is an example
Group:          Example group
License:        GNU
URL:            http://example
Source0:        ex-%{version}.tar.gz
%description
 
%prep
%setup -q
 
%build
%configure
make %{?_smp_mflags}
 
%install
rm -rf $RPM_BUILD_ROOT
make install DESTDIR=$RPM_BUILD_ROOT
 
%clean
rm -rf $RPM_BUILD_ROOT
 
%files
%defattr(-,root,root,-)
%doc
%changelog


Building RPMS: http://www.rpm.org/max-rpm/index.html 
Building RPMS: https://fedoraproject.org/wiki/How_to_create_an_RPM_package 
Naming guidelines: http://fedoraproject.org/wiki/Packaging:NamingGuidelines

Friday, October 30, 2015

Consul: Adding TLS to Consul using Self Signed Certificates

I'm currently working on setting up TLS for Consul. As of this writing, I'm still in the experimentation/set-up phase, but we plan to roll consul out into production with TLS support. So, this document may get updated but I wanted to capture what I had to do while it's fresh in my mind.

Consul's documents are a little light on specifics, which made this endeavor more difficult than I anticipated. I will post links at the bottom of this article. The following steps were used to create a self signed certificate on Centos 6.6. 

Make a directory to hold our files, create the certificate authority (ca) conf file, seed our index and create a cert index file:


> mkdir -p /opt/consul/ssl
> cat << EOF > /opt/consul/ssl/demo.conf
[ ca ]
default_ca = demo

[ crl_ext ]
# issuerAltName=issuer:copy  #this would copy the issuer name to altname
authorityKeyIdentifier=keyid:always

[ demo ]
new_certs_dir = /tmp
unique_subject = no
certificate = /opt/consul/ssl/demo-root.cer
database = /opt/consul/ssl/certindex
private_key = /opt/consul/ssl/privkey.pem
serial = /opt/consul/ssl/serial
default_days = 365
default_md = sha1
policy = demo_policy
x509_extensions = demo_extensions

[ demo_policy ]
commonName = supplied
stateOrProvinceName = supplied
countryName = supplied
emailAddress = optional
organizationName = supplied
organizationalUnitName = optional

[ demo_extensions ]
basicConstraints = CA:false
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always
keyUsage = digitalSignature,keyEncipherment
extendedKeyUsage = serverAuth,clientAuth
crlDistributionPoints = URI:http://path.to.crl/demo.crl
EOF

> touch /opt/consul/ssl/certindex
> echo 000a > /opt/consul/ssl/serial
> cd /opt/consul/ssl

NOTE: You may need this step to ensure the certs work with Consul. Edit the /etc/pki/tls/openssl.cnf file and add the following:

extendedKeyUsage=serverAuth,clientAuth

Below I have one way to make the change. Read more about extended key usage here: https://www.openssl.org/docs/manmaster/apps/x509v3_config.html#extended_key_usage_

> cp /etc/pki/tls/openssl.cnf  /etc/pki/tls/openssl.cnf.bak
> sed -i"" 's|# extendedKeyUsage = critical,timeStamping|extendedKeyUsage=serverAuth,clientAuth|' /etc/pki/tls/openssl.cnf


Generate the root certificate:

> openssl req -newkey rsa:2048 -days 3650 -x509 -nodes -out /opt/consul/ssl/demo-root.cer -keyout /opt/consul/ssl/private.pem 
Country Name (2 letter code) [XX]:US
State or Province Name (full name) []:New York
Locality Name (eg, city) [Default City]:New York
Organization Name (eg, company) [Default Company Ltd]:Demo Company
Organizational Unit Name (eg, section) []:Demo
Common Name (eg, your name or your server's hostname) []:
Email Address []:


Consul want's the certs and the servers to be server.<data center>.consul - just adjust the request below as I used dc1 as my datacenter. Generate a certificate signer request (csr):

> openssl req -newkey rsa:1024 -nodes -out /opt/consul/ssl/server.csr -keyout /opt/consul/ssl/server.key
Country Name (2 letter code) [AU]:US
State or Province Name (full name) [Some-State]:New-York
Locality Name (eg, city) []:New York
Organization Name (eg, company) [Internet Widgits Pty Ltd]:Demo Company
Organizational Unit Name (eg, section) []:Demo
Common Name (e.g. server FQDN or YOUR name) []:server.dc1.consul
Email Address []:

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:


Generate the self signed cert:

> openssl ca -batch -config /opt/consul/ssl/demo.conf -notext -in /opt/consul/ssl/server.csr -out /opt/consul/ssl/server.cer

To verify your certificate use the following command and make sure the "X509v3 Extended Key Usage" matches:

> openssl x509 -noout -text -in /opt/consul/ssl/server.cer
.....
            X509v3 Extended Key Usage: 
                TLS Web Server Authentication, TLS Web Client Authentication
.....

Now you can configure your consul server to use the self signed certs. These lines were take out of my consul.json file:

    "ca_file": "/opt/consul/ssl/demo-root.cer",
    "cert_file": "/opt/consul/ssl/server.cer",
    "key_file": "/opt/consul/ssl/server.key",

On your agents, you're going to need to specify the "ca_file" and set "verify_outgoing":true in your consul configs. 

If you get errors about trusting the signing authority, you will need to trust the demo-root.cer. To trust the root certificate on your server(s) do the following:

1. Install the ca-certificates package
2. Enable the dynamic CA configuration feature
3. Add it as a new file to /etc/pki/ca-trust/source/anchors/:
4. Use command:

> yum install -y ca-certificates
> update-ca-trust enable
> cp /opt/consul/ssl/demo-root.cer /etc/pki/ca-trust/source/anchors/
> update-ca-trust extract

Here are the documents I had to read:

Wednesday, October 14, 2015

Generating a public key for SSH using your private RSA key

In order to ssh onto a server using public private key pairs, you need a specific type of public key. If you have the private key you can generate the public to install into the ~/.ssh/authorized_keys file with the following

echo "actual private key data" > private
chmod 600 private
ssh-keygen -y -f private

You can generate a public key with the following

 openssl rsa -in private.pem -pubout > public

But it won't work for using ssh e.g. ssh -i private

Tuesday, June 2, 2015

Changing commit logs in git

It sounds like something you shouldn't do, but sometimes you may want to adjust who made a commit. Maybe you did a commit on a vagrant box or maybe you fat fingered your name or email address while typing too fast. To change the committer I found this handy
#!/bin/sh
 
git filter-branch --env-filter '

OLD_EMAIL="bad@emailaddress"
CORRECT_NAME="Russell Simpkins"
CORRECT_EMAIL="russellsimpkins@real-domain"

if [ "$GIT_COMMITTER_EMAIL" = "$OLD_EMAIL" ]
then
    export GIT_COMMITTER_NAME="$CORRECT_NAME"
    export GIT_COMMITTER_EMAIL="$CORRECT_EMAIL"
fi
if [ "$GIT_AUTHOR_EMAIL" = "$OLD_EMAIL" ]
then
    export GIT_AUTHOR_NAME="$CORRECT_NAME"
    export GIT_AUTHOR_EMAIL="$CORRECT_EMAIL"
fi
' --tag-name-filter cat -- --branches --tags

Simply adjust the OLD_EMAIL, CORRECT_NAME and CORRECT_EMAIL and stuff that into a bash script. Then issue a git push --force

Thursday, May 28, 2015

Resizing an ext4, ebs volume

You use lsblk and see your EBS volume is the right size, but running df -h shows the device is smaller. To fix this, the command to use is resize2fs 

resize2fs /dev/xvdf

While you can do it on a mounted device, these things are often better done when it's unmounted, just to be safe.

https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/6/html/Storage_Administration_Guide/ext4grow.html

Monday, April 13, 2015

Fun with GPG


I once had a desire to create a team GPG key that I could use for signing RPMs. I've moved in a different direction, but I want to capture the steps in case I decide to use this again in the future.

You can import a private GPG with the following:

gpg --allow-secret-key-import --import private.key.file
gpg --list-keys
gpg --edit-key <ID> 

Once you run --edit-key you're able to trust the key. Execute **trust** and choose level **5**

With that done, you can decrypt using the key - assuming you know the password.

gpg -d -u "name <email>" encrypted.file.gpg > outputfile


To encrypt for the team key to unlock:

gpg -se -r "name <email>" -u "name <email>" encrypted.file

Thursday, February 5, 2015

Port Forwarding on Mac OSX

If your running vagrant and you're forwarding traffic to vagrant over 8080, but you really prefer to hit port 80, you can use Mac's pfctl function. 

Here's a couple of links that you might find helpful
https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man8/pfctl.8.html
http://krypted.com/mac-os-x/a-cheat-sheet-for-using-pf-in-os-x-lion-and-up/

I was reading this article http://salvatore.garbesi.com/vagrant-port-forwarding-on-mac/ and it suggested adding a vagrant plugin, but it's a ruby gem. Hard to imagine, but the gem failed to install.

You can still implement port forwarding. Create a pfctl.conf file in your vagrant folder:

echo "rdr pass on lo0 inet proto tcp from any to 127.0.0.1 port 80 -> 127.0.0.1 port 8080
rdr pass on lo0 inet proto tcp from any to 127.0.0.1 port 443 -> 127.0.0.1 port 8443" > pfctl.conf

To run this, you first need to enable pfctl

pfctl -e -f pfctl.conf

Once you enable the firewall rules, you can hit your vagrant box by going against localhost. 

To disable pfctl: 

pfctl -d



Wednesday, January 21, 2015

Searching Log files with AWK

I was digging through my notes and came across this little nugget. Say you have a log file and that log file entry has URL encoded values that you would prefer to see decoded. Here's the AWK I used to URL decode:

awk -F ^C '$4 ~ /SearchingFor/ {print $4}' access_log | awk '
{
    str = $0
    while (match(str,/%/)) {
      L = substr(str,1,RSTART-1) # chars to left of "%"
      M = substr(str,RSTART+1,2) # 2 chars to right of "%"
      R = substr(str,RSTART+3)   # chars to right of "%xx"
      str = sprintf("%s%c%s",L,hex2dec(M),R)
    }
    printf("%s\n",str)
    
}
function hex2dec(s,  num) {
    num = index("0123456789ABCDEF",toupper(substr(s,length(s)))) - 1
    sub(/.$/,"",s)
    return num + (length(s) ? 16*hex2dec(s) : 0)
}'

I found this on the web and I post it here so I don't loose it. I would love to give credit if I could remember where I got it.