Maybe Financials

Maybe Financials

Maybe, Just Maybe, someone will enjoy Maybe Financials. It’s a powerfull self hosted financial system and it’s something I need. I’m switching from my factory IT job to a School system. The school system moves me from a weekly paycheck to a monthly paycheck and a pay cut and a big pay cut. However, it’s worth it as this is my end goal. One thing I have always struggled with is knowing when money will leave the account. Last time paid my amazon card, it took a full 2 weeks to come out of my account. This hurt me when coming down to prodictable budgeting. I tried different tools and all of them just couldn’t cut it. The core issue is I’m a spender.

Thus, being monthly, it’s time to try another software. However, I want something self hosted, something I can reach by a domain name. Thus, I tried a few out and landed with Maybe Financials. This guide will show you how to install it and set it up so a ssl will work. I am running it through my apache reverse proxy that lives in the cloud and hosting in docker on a local computer. Lets get started.

Installing Maybe

The setup

We are assuming you have a docker setup. This install is pretty quick and simple as well. Here is the offical documentation. First, you want to create your directory for your maybe install. Then you want to download the compose example into that folder. I suggest being in it. After which you will want to secure it. Lets begin with the compose file.

mkdir /maybe
cd maybe
curl -o compose.yml https://raw.githubusercontent.com/maybe-finance/maybe/main/compose.example.yml

Now we have the example file. We need to set the passwords needed for it. If you open the compose.yml file, you will find postgres and rails are part of the system. Items like ${blah:something} are variables that will be found in the .env file. As you can see, the password and hash are both there. So, it’s time to grab a hash and a password.

openssl rand -hex 64

Now we take that information and put it inside the env file by using the following command.

nano .env

The file should look like this:

SECRET_KEY_BASE="replace me with the generated string from the prior step"
POSTGRES_PASSWORD="replace me with your desired database password"

Another way to do this is by using this one liner. It will prompt for a custom password.

read -s -p "Enter your desired Postgres password: " DB_PASS && echo -e "SECRET_KEY_BASE=\"$(openssl rand -hex 64)\"\nPOSTGRES_PASSWORD=\"$DB_PASS\"" > .env && echo -e "\n.env file created."

Note:

  • This is when you can change the port number you want maybe to run on. Do it by editing the compose.yml file with nano. Change the Port from 3000:3000 to 3000:<Your Custom Port>

Run docker

Once you have the env file in place, it’s time to run the docker commands.

docker compose up -d
docker compose ls

Now, you can navigate to yoru machines IP address at the port you wanted. For example http://192.168.0.5:3000. You will setup your email from this point.

Accessing it from a greater network.

If you want to go to your instance of Maybe Financials outside your home, you will have to set it up to do so. In my case, if you haven’t followed, I’m Gnatted. So, I have a reverse proxy up in the cloud that points back. For me, this is the process I went through to set it up to be reached from the outside. I have already pointed my dns to the reverse proxy.

  1. cd /etc/apache2/sites-available.
  2. nano maybe.therandomadmin.com.conf
  3. Pasted the below
<VirtualHost *:80>
    ServerName maybe.therandomadmin.com

    ProxyPreserveHost On
    ProxyPass / http://100.100.100.100:3000/
    ProxyPassReverse / http://100.100.100.100:3000/

    # Optional headers
    RequestHeader set X-Forwarded-Proto "http"
    RequestHeader set X-Forwarded-Port "80"

    ErrorLog ${APACHE_LOG_DIR}/maybe.therandomadmin.com-error.log
    CustomLog ${APACHE_LOG_DIR}/maybe.therandomadmin.com-access.log combined
</VirtualHost>

This is a basic reverse proxy virtual host. It’s very important to see the requestheader. If it doesn’t like such, it will not work. Not sure why, it’s soemthing to do with rails.

  1. a2ensite maybe.therandomadmin.com.conf
  2. systemctl reload apache2
  3. certbot –apache
  4. Selected the maybe.therandomadmin.com
  5. Once it finished, I was able to navigate to the site.

This is where the requestheaders were important. Take a look at the virutal host that was generated by the cert bot:

<IfModule mod_ssl.c>
<VirtualHost *:443>
    ServerName maybe.therandomadmin.com

    ProxyPreserveHost On
    ProxyPass / http://100.100.100.100:3000/
    ProxyPassReverse / http://100.100.100.100:3000/

    # Optional headers
    RequestHeader set X-Forwarded-Proto "https"
    RequestHeader set X-Forwarded-For "%{REMOTE_ADDR}s"

    ErrorLog ${APACHE_LOG_DIR}/maybe.therandomadmin.com-error.log
    CustomLog ${APACHE_LOG_DIR}/maybe.therandomadmin.com-access.log combined
                           <Items removed due to security>
</VirtualHost>
</IfModule>

Notice the port is not there. It’s important to change it and then reload apache. Maybe Financials should allow you to log in without any issues.

Remember, if you don’t change the requestheaders, you will get the following error message

What can we learn as a person

Knowing is half the battle. It took me about 30 minutes to figure out the error message I got. The change you wanted was rejected was as useful as a box of pins in a bollon shop. I found an abscure reference to ngnix proxy which lead me to figure out the requestheader issue. Since the logs are very unhelpful. If you tried installing maybe, you might run into the same issue when giving it it’s domain name. But thanks to this blog, you have the answer. Thus, no time wasted.

To solve a problem, the more you know at the start of the problem, the quicker you will resolve the issue. I have a problem with two credit cards. I used them to buy things that were needed in the house. At the time, I didn’t realize it can take 3 weeks to process a single payment. Which is ugly to say the least. This little program has already helped me understand some spending habbits I have. Can you guess what eats my money? Eating out. No suprise, an IT guy likes to eat. With knowing these patterns, I now have a plan that will get me debt free by this time next year while still having christmas.

Almost every single issue can be quickly resolved by having preknowledge. Take a step back, breath and see what’s going on.

Upcoming OneDrive changes

Upcoming OneDrive changes

Oh Microsoft, we love you, and sometimes we can’t stand you. Announced on Microsoft Roadmaps, Upcoming OneDrive changes are going to be a mess. (Link) The skinny on this one is simple, You will be able to sync your personal one drive files with your company OneDrive files.

The danger

Let’s talk about how dangerous this is.

Think about users like me who have personal projects that are not safe for work. For example, I have a little .NET program in my personal OneDrive that places a form on the screen and blocks all input but still lets you see the display. It’s great when I’m watching a fractal render and don’t want my cat stepping on the keyboard and messing it up.

Now imagine this program syncing to my company’s OneDrive. Imagine that executable file getting loose in a business environment. Yeah … exactly.

Then there’s HIPAA, PII, and more to consider. If Bob from Accounting links his personal OneDrive to his business OneDrive, his child’s medical documents or sensitive family files might be exposed to corporate systems. Most personal OneDrives contain things like resumes, tax documents, and payment info. That means the company could suddenly have access to those files, whether it wants to or not.

Here’s the biggest problem: file accountability.

Let’s say a user unknowingly syncs illicit files. Now those files are on the business network. Who’s responsible for them? The user? The company? Anyone with access? It’s a legal and security nightmare. This is how companies get wrecked and Microsoft seems to be ignoring the massive risk.

The solution so far

Microsoft has proposed a fix, but it’s far from elegant. You can create a Device Configuration policy in Intune to block personal OneDrive syncing. Here’s how:

How to Block Personal OneDrive in Intune

  1. Go to https://intune.microsoft.com
  2. Click Devices
  3. Under By Platform, select Windows
  4. Under Manage, select Configuration
  5. Click Create > New Policy
  6. In the “Create a Profile” pane:
    • Platform: Windows 10 and later
    • Profile Type: Settings Catalog
    • Click Create
  7. Name the policy (e.g., OneDrive – Block Personal Sync)
  8. Add a description (optional)
  9. Click Next
  10. Click Add Settings
    • Search for OneDrive (scroll past the “Microsoft” section)
    • Check Prevent users from syncing personal OneDrive accounts (User)
  11. Set the toggle from Disabled to Enabled
  12. Click Next (Scope Tags)
    • You can leave this blank unless you use tags for special groups like the CEO
  13. Click Next (Assignments)
    • Click Add All Users
    • Add any exclusion groups as needed
  14. Click Next (Review + Create)
  15. If everything looks good, click Create

What does this setting do?

This is what microsoft offically says: This setting lets you block users from signing in with a Microsoft account to sync their personal OneDrive files. If you enable this setting, users will be prevented from setting up a sync relationship for their personal OneDrive account. Users who are already syncing their personal OneDrive when you enable this setting won’t be able to continue syncing (and will be shown a message that syncing has stopped), but any files synced to the computer will remain on the computer. If you disable or do not configure this setting, users can sync their personal OneDrive accounts.

These Upcoming OneDrive changes sucks for some, and is awesome for others. Lets keep ourselves protected by stopping it before it gets any worse.

Update

While searching for other options, I did find a GPO. GPO works great with internal items like desktops, and devices that don’t leave. However, if a GPO fails to load, and it’s your only option, then it will fail. The GPO is located Users Configuration > Policies > Administrative Templates > OneDrive > Prevent Users From synchronizing personal OneDrive Accounts. Just enabled this policy and apply it to your users, and it will do the same.

What can we learn as a person?

Let’s do some math. We all get 24 hours in a day. We need at least 8 hours of sleep, leaving us with 16. If you work an 8–5 job, that’s 9 hours gone. Add commute time, say 1 hour round trip, and you’re now down 11 hours.

Now subtract 1.5 hours for meals. You’re left with 3.5 hours per day. That’s all the time you have for your family, friends, hobbies, self-care, or personal growth. I’m not even counting weekends here. Most Americans live on the weekends and just exist during the week.

And here’s the part I want to challenge. Growing up, I was told, “Don’t make friends at work.” Honestly, that’s dumb. Who do you spend the most time with? Your coworkers. It’s okay to build friendships with them. It’s healthy to enjoy your work culture.

Additional Items to read

Uncover Starred-Out Passwords

Uncover Starred-Out Passwords

I was inside my unifi controller a while back and the new update had starred out the radius password. The documentation had yet to occur on the radius password. So, I used a simple HTML trick with my firefox to change the stars to clear text. This is how you can Uncover Starred-Out Passwords in firefox.

Developer Options

F12 will trigger the developer options. Inside Google Chrome, the developer window will appear on the right-hand side. In Firefox, the developer window will appear at the bottom of the window. These options allow you to change your current view of the site. If you want to change the background to hot pink, you can. if you want to change a password field to plain text, you can.

Developer Options in Firefox

What we are looking at

Stared-Out Passwords
The view area.

This is the page we are viewing. I want the starred-out shared secret that you see in the red box. Click the edit button to the right of the shared secret. Notice the password is still starred out. To get this information, we need to start the Developer options. Inside the developer options on the left-hand side, you will see a mouse cursor in a box, also known as an inspector. We still have Starred-Out Passwords

Once you click the inspector tool, you can move the mouse over the shared secret and click on it. Inside the developer options, you will see the HTML itself highlighted. Notice the highlighted HTML code is the input object. We are looking for the word “type”. All we have to do is replace type=”password” to type=”text”. Once you type in text, click enter. The password is now exposed.

That’s pretty much it. The key secret is to make sure you are editing the input and not just the div. I you see div code instead of input code, then you do not have it set in edit mode. This process can be used for just about any website out there. This is how we Uncover Starred-Out Passwords.

Continue Reading

Playing with Logs – Regex

Playing with Logs – Regex

Recently I was playing with some sql event viewer logs. These logs don’t have properties that we can pull from to make life easier. So, everything has to be parsed through the string. Which isn’t bad, but it’s a challenge to think about. Here is what one of the strings looks like:

Login failed for user 'db_admin'. Reason: Password did not match that for the login provided. [CLIENT: 10.0.80.55]

I wanted three items from this list, the username, the reason, and the IP address. The username is inside the single quotes. The reason goes from the word reason to the [ for client. The IP address is inside the brackets for the client. Lets get started. First lets get the data.

$test = invoke-command -ComputerName servername -ScriptBlock {Get-WinEvent -FilterHashTable @{logname='application';providername='MSSQLSERVER';Keywords='4503599627370496'}}

Grabing Everything between double single qoutes.

Now we have the data, it’s time to get the username. As I said, the username is found inside the single quotes. So we want to select the string with a pattern that pulls the double single quotes.

($t.message | Select-String -Pattern "'.*?'" -AllMatches).Matches.Value -replace "'",""

‘.*?’

This bad boy here grabs everything between the single quotes. The ‘—‘ is the boundaries. the . says to match any character except the terminators. Yeah, we don’t want skynet. Then the X? tells it to match the previous token between 0 and forever times. The select starts off with the first and then moves to the next character. which is a wild card. Then we search all wildcards forever until the next with the *? characters.

We select every item in the string that matches that pattern using the -AllMatches. Then we grab the Matches by using the ().Matches. We want those values so we select the value from the matches. ().Matches.Value. This still selects the double single quotes and we really don’t want this. So we simply remove them by using the -replace command. We replace the ‘ by saying -replace “‘”,””. Looks a little confusing but it works.

Grabbing Text after a word and before a symbol.

The next part is to grab the reason. This is basically grabbing everything after a single word and before something different. The logic is the same as before, but this time we are grabbing it based off a word.

Reason =  (($t.Message | Select-String -Pattern "Reason:.*\[" -AllMatches).Matches.Value -replace ' \[','') -replace 'Reason: ',''

“Reason:.*\[“

In this instance we are searching for the word Reason:. Once we find that word, we select the first object in front of it using a wild card again. The wild card is a . like before. Then we tell it to continue searching using the * until we reach special character of [. Notice the \ is before the [. The reason for this is because in the world of Regex the bracket, [, is a special character used for searching. Thus this code is saying, start with the word reason: and search everything until you reach the square bracket.

Once we have the pattern we select all the matches like before with the -allmatches. We then select the matches and the values using the ().matches.value commands. From there we want to remove the square bracket and the word reason:. We do that with replace commands to remove the word reason we use -replace (“Reason: “,”) and to remove the extra space and square bracket we us -replace (‘ \[‘,”). Notice once again, the \ before the square bracket.

Pulling an IP address from a string

The next thing we need is the IP address of the log. The IP address is located in the square brackets with the word client inside of it. The key here is not to search those brackets. We want the IP address of any and all strings. We want all the IP addresses and not just one.

IPAddress = ($t.message |  Select-String -Pattern "\d{1,3}(\.\d{1,3}){3}" -AllMatches).Matches.Value

\d{1,3}(\.\d{1,3}){3}

This one is much more complex than the last one. The first thing we do is look for is up to three digits side by side. \d means digits. The {1,3} means between 1 and 3. We do this until we reach a . mark. Then we repeat the process again 3 times. We use the () to create a group. Inside that group, we have the \. which is the decimal point followed by the \d{1,3} again. Saying after the decimal point looks for up to three digits again. Finally, we tell the code to do this 3 times with the {3} tag at the end of the group.

Like before we use the -allmatches flag to get all the matches and pipe it out using the ().Matches.value method. But wait! This only pulls the IP address format, not the IP address. This works for an IP address of 512.523.252.1 which we all know is an invalid IP address. To do that we have to dive much deeper into regex. This next part is complex.

Validate an IP address

The above gives an idea of what we want to look for, a starting point, here is the big fish that we need to break down. This code is a bit longer. This code is broken up between whatif structures. Which is pretty cool. It’s going to take some effort to explain it all. So we will take one group at a time with each whatif | statement. Remember each group represented with () is for a single octet. I am going to try my best at explaining this one. I’m not fully sure, but once again, I will try.

^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-5][0-9]|[01]?[0-9][0-9]?)$

25[0-5]|

Our first what if statement is the 25[0-5]. The maximum number for an octet is 255. In fact, 255.255.255.255 is the broadcast address. In this what-if statement, we are looking at matching the first two characters of an octet as 25 then we are looking for any number after that as a 0 to 5. So, anything like 256 would be ignored.

2[0-4][0-9]|

The next one is for the middle of the 255 range. This tests to see if the range is 2xy. The x is between 0 and 4 and the y is 0 and 9. I’ll be honest, When I changed out the 4 with a 5, the value of 256 was accepted when it shouldn’t have been. There is something special later on on this one.

[01]?[0-9][0-9]?

Now we are matching if the first character is a 1. So, this will cover the 100-199 ranges. If there is a 1 there, if not, then it will cover the 0-99. The ? matches the previous item between 0 and one times. So the previous item is a 1 or a number. This creates the nice 0-199 range.

\.){3}

The \ symbol is our break symbol to break the regex processing on the next item which is our . symbol. Normally . means a wild card. In this case it means a period or decimal point. Then we close the group with our closing parentheses, ). Now we have made a group that determines if an object is between 1 and 255 with a period at the end, we need to do this 3 times. There is multiple ways to do this. We can repeat the code 3 times or we can just say {3}. That’s what we did here.

(25[0-5]|2[0-5][0-9]|[01]?[0-9][0-9]?)

Finally, we repeat the code again. This time without the \. at the end. The reason we do this is that the last octet doesn’t have a period at the end. This matches between 1-255 without the period.

Grabbing Mac Addresses from a string

Another item I pull from logs is mac addresses. Most of the time these are security logs from a firewall. However, it’s important to be able to pull a match address. The big thing between a mac address and an IP address is the mac address requires letters and numbers. They also come in all forms of delimiters. the most common are :, ., , and a space for the truly evil people. Thus, you have to address each of these items. Here is the code:

([0-9a-fA-F]{2}[: \.-]){5}([0-9a-fA-F]{2})

[0-9a-fA-F]{2}

The first part of the code is looking for the numbers 0 – 9. For example 0F:69:0F:FE:00:01 is the code. The first section is 0F. The 0 – 9 helps us find the 0. The next part a – f helps us find a,b,c,d,e, and f. The A – F helps us find A,B,C,D,E, and F. This way we don’t have to worry about case sensitivity when searching for the logs as some logs don’t capitalize the letters. Finally, we are going to search this two times before our next symbol.

[: \.-]

This next part is the search for the different symbols. Notice we have the common ones in there. The first is the standard colon :. It is followed by a space and then the escape character because the period is a wild card character. Which is then followed by the hyphen as ipconfig /all gives you hypens. It’s all within the search brackets.

(){5}

We then close up the group with our () marks. This will search for at least one of those that match. We want 5 items in a row for a mac address. Mac addresses contain 6 sections. So, the next code is important to find that 6th. We search for the 5 in the row by the {5} mark.

([0-9a-fA-F]{2})

We repeat the code over again. This way we get that last section of the mac address. This time tho, we don’t have the search for the unique symbols as the last section of a mac address doesn’t have one.

Putting these in a script

If you read my blog often, you know I like functions. Here are two functions you can add to your tool bag.

Get-SHDIPFromString

Function Get-SHDIPFromString{
    [cmdletbinding()]
    Param (
        [string]$String
    )
    foreach ($string in $string) {
        ($String | Select-String -Pattern "((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)" -AllMatches).Matches.Value
    }
}

Example:

PS> Get-SHDIPFromString -String "This message contains 192.168.25.5 as an IP address." 
192.168.25.5  

Get-SHDMacFromString

function Get-SHDMacFromString {
    [cmdletbinding()]
    Param (
        [string[]]$String
    )
    foreach ($string in $String) {
        ($String | Select-String -Pattern '(?<mac>([0-9a-fA-F]{2}[: \.-]){5}([0-9a-fA-F]{2}))' -AllMatches).matches.value
    }
}

Example:

PS> Get-SHDMacFromString -String "Physical Address. . . . . . . . . : 11-35-AF-FE-11-A1"
11-35-AF-FE-11-A1