Kevin's Blog [Index]

Return to main site

Building a Production-Ready EKS Cluster with Cilium: Lessons from the Field

[linkstandalone]

Introduction

I was tasked with creating a production-ready AWS Elastic Kubernetes Service (EKS) cluster for a new high traffic, web-browser based scraping service. Typically, we would follow our traditional pattern of using Hashicorp Nomad just like we do for our current production workloads. However, we are creating this service in a separate AWS account from traditional production services. Nomad, while great, has some limitations with our current deployment strategies:

  1. Each Nomad client would have to be a self-managed EC2 instance
  2. Auto-scaling is not as advanced and some features require enterprise
  3. Very limited CNI options limiting performance and observability
  4. Does not integrate natively with AWS services (as easily)
  5. Not as well supported by the community
  6. Not as fast to set up as a managed service

Understanding the limitations with Nomad, we looked towards Kubernetes, which is well established in the industry as the leader for orchestration and already is an existing tool we have used in other environments. Being that these accounts were created in AWS, using a managed service such as EKS was a no-brainer. I wanted to build as reliable and fast of a service as I could complete with observability in our Loki, Grafana, Tempo, and Mimir (LGTM) stack. The system should also be easy to use/manage from a software engineer perspective.

The choice ended up being going with Cilium for the CNI, auto-scaling, and a bunch of Kubernetes controllers/operators. I encountered a number of challenges throughout the way which I will detail in this article.

Cilium

Why Cilium?

The default CNI for AWS EKS is the AWS VPC CNI. It is a very standard Kubernetes CNI with moderate performance. However, I wanted to squeeze out as much performance from this cluster as I could. There will be many simultaneous requests being made from hundreds of pods and on top of that we must have complete observability into what is happening at every stage. The logical choice was to go with Cilium, the standard in eBPF-based CNI. What this allows us is to have introspection into the running jobs network calls as well as implement policy without impacting performance. Additionally, eBPF eliminates the layers of networking between the client and our service providing lower latency between requests providing significant speed ups in our system. Typically, you would have to traverse the Linux network stack on the Kubernetes node, kube-proxy then routes traffic to the appropriate pod based on iptables rules, and then it goes through the Linux network stack inside of the container before making it to our service. Leveraging eBPF, we bypass all of this overhead. Traffic is routed from the AWS Load Balancer directly to the pod’s network interface by Cilium’s eBPF map, cutting out the middleman and providing a “fast path” for our data. While L4 traffic follows this fast path, we also leverage Cilium’s integrated, lightweight Envoy proxy for Layer 7 introspection. This allows us to apply granular network policies and observe protocol-specific metrics (like gRPC or HTTP) via Hubble. At the end of the day, whether you go with L7 introspection or not, this gives us high performance visibility needed in this high-traffic environment without the traditional sidecar overhead in a traditional CNI.

Cilium Challenges I Faced

Due to not reading documentation closely enough - and frankly being the first time I have worked with EKS - I failed to acknowledge that to use a custom CNI such as Cilium, you need to disable the default AWS VPC upon cluster creation. Furthermore, taints must be placed on nodes and tolerations added to core services like CoreDNS. Additionally, kube-proxy needs to be disabled for everything to function as expected.

Our deployment was made via Terraform, so in order to disable the default AWS VPC CNI, we need to set the bootstrap_self_managed_addons variable to false. This comes with the caveat that we must now also manually deploy CoreDNS and kube-proxy, if you are using it (which we are not). What proceeds is the cluster comes up in a bare state with no networking at all so it is imperative that the next step is deploying Cilium. Due to the lack of networking, the nodes will fail to come up healthy enough to have pods which is why we need to have taints on all of our nodes. The Cilium operator provides the node.cilium.io/agent-not-ready taint for this exact reason. Additionally, this taint is required on all nodes for Cilium to be able to manage your workloads. This leads to a “chicken-and-egg” problem: we require CoreDNS to complete the Cilium installation, but CoreDNS cannot be scheduled because of the active taint. To break this deadlock, we added a toleration to the CoreDNS deployment:

tolerations = [
  {
    key      = "node.cilium.io/agent-not-ready"
    operator = "Exists"
    effect   = "NoSchedule"
  }
]

On my initial deployment of the cluster in our development environment I failed to follow these steps and struggled immensely. There was weird behavior where over half of requests made in the cluster were failing with no clear indication why. It was due to having both Cilium and AWS VPC CNI running at the same time. There is an init container that configures the node at startup to leverage the CNI. The AWS init container would constantly overwrite any configs/rules established by Cilium. This was an issue because AWS VPC CNI manages ENIs (Elastic Network Interfaces) differently than Cilium. Additionally, when both run, they fight over who gets to “own” the secondary IP addresses on the node’s interfaces.

The choices were to either rebuild the cluster or simply clean up the AWS CNI. I chose the latter as to reduce the amount of work it would take to fix the mistake. I first removed the AWS CNI from the cluster using the AWS CLI as well as going through deployments and daemonsets to ensure all resources were cleaned up. From there, we still had to clean up the changes that the AWS VPC CNI had done on the nodes and ensure the Cilium config was the primary. I ran a simple Batch job in the kube-system space that had permission to target the node directly and cleared out iptables rules, and any other resources my AI agent could point me to, left behind by the AWS VPC CNI. From there it was simply about doing a rolling restart of the Cilium daemonsets and deployments. In hindsight, simply rebuilding the nodes one by one was the simpler option, and I did end up doing that anyways, but at the time it got me out of the jam pretty quick.

Further down the line, I also observed issues with the L7 introspection, and as you could have guessed, it was due to kube-proxy being installed and a misconfiguration in Cilium. This clean up has to be done in a specific order to not break things. Without kube-proxy or some other alternative, services like CoreDNS will now fail to receive traffic causing your cluster to brick. You must enable the kubeProxyReplacement option in Cilium before destroying kube-proxy so that its Envoy proxy is created first. This allows ClusterIP and NodePorts to be discovered via Cilium’s eBPF map rather than relying on kube-proxy. Once I figured that out, the cluster operated very smoothly.

Observability

For observability, we needed to send our data back to our LGTM stack. It is a perfectly stable interface that is used across our environment so why reinvent the wheel. Traditionally, the Prometheus Operator was the standard approach to do this. It requires you to have separate agents to collect logs and export Open Telemetry (OTLP) metrics. In 2026, Alloy is the Grafana supported path to get observability data into your stack. It is an all-in-one agent if you will that can handle metrics, logs, and traces. You deploy Alloy either as a sidecar to each of your jobs or as a daemonset. Given the number of pods I expected in this cluster, I knew that the sidecar pattern would not scale well. Given 100 pods I would have 100 Alloy sidecar containers as well. Obviously, this increases the blast radius slightly due to the jobs depending on what amounts to 1 Alloy container per node, but when compared to the alternative its worth the risk. Each container is wasted resources that can be allocated to our core workload and a wasted secondary IP.

With Alloy, we were able to get pods metrics scraped that hold a specific annotation, prometheus.io/scrape. Our Cilium deployment had this annotation added to it which allowed us to get Cilium metrics ingested into Mimir as well. Logs and traces are pushed via Open Telemetry to the Alloy deployment from the pod. The deployments have an environment variable that contains the Kubernetes service address of the Alloy deployment.

Alloy Challenges I Faced

One of the first challenges was converting our existing Alloy configuration we use in Nomad to work with Kubernetes. The configuration we use in Nomad has all the pre-existing re-labels and tagging in it which we need. Due to the complexity of the Alloy river format, it took some time to get this right. I had to translate the Nomad resource concepts to Kubernetes. Additionally, the Nomad Alloy deployment follows the sidecar pattern so I had to account for that.

Once I figured that out, it was all about refinement. First issue was duplicate writes due to Alloy pods on multiple nodes scraping the same endpoint. Mimir does not like this duplicate data. I had to restrict the Alloy config to only look at metrics coming from its own node. That was a quick change.

Then came another challenge, corruption of the WAL db on abrupt pod failure. To resolve this I ended up doing a number of things, but ultimately the solution was to add a persistent volume on the node for /var/lib/alloy/data.

Finally, I needed to get more detailed metrics from the nodes themselves for observability reasons and so autoscaling can work. This meant I had to deploy node exporter and kube state metrics which gave us what we needed complete with container level cAdvisor metrics.

Cost Observability

In order to keep track of costs, we leverage CloudZero. This was a quick deployment of a helm chart which exports data to our CloudZero account where we can combine granular pod level costs with top level AWS costs to determine if what we are doing is within budget.

Interacting and Creating AWS resources

Since this cluster was going to be primarily leveraged by software engineers and the requirements were very dynamic and changing, I needed to find a way to not be the blocker for development. I was able to leverage AWS provided Kubernetes Controllers/operators to do so. The AWS Load Balancer controller was the immediate first choice as that pairs nicely with our Cilium deployment. Based on how I configured my Cilium, every Cilium Ingress generates an AWS Application Load Balancer (ALB). If an engineer chooses to use a standard Network Load Balancer (NLB) or leverage the Cilium Ingress, they only need to do so using an annotation in their ingress config. These annotations also configure security groups which are essential to prevent unauthorized access to the cluster. Cilium will create either dedicated load balancers for each ingress or share a single load balancer for the entire cluster. We are leveraging the dedicated mode for this cluster. The benefit of using the Cilium ingress over a standard ingress is getting that native integration with Envoy for the L7 introspection we mentioned earlier as well as access to Envoy’s load balancing algorithms for service-to-pod request distribution.

Next we needed a more friendly CNAME/alias for these load balancers. And guess what? There’s the External DNS controller which will automatically generate the necessary Route53 records for this also based on an annotation.

Finally, we have autoscaling. We deployed the Cluster Autoscaler to ensure we always have enough nodes to run our jobs and nothing more. We also deployed the HorizontalPodAutoscaler (HPA), which uses the LGTM metrics to make scaling decisions. This is still a work in progress as we determine the best metric and thresholds to scale in and out.

A small side-exploration was done with the VerticalPodAutoscaler (VPA) and Goldilocks in recommendation mode to identify usage patterns but ultimately it was determined that we need to wait for real usage to be flowing through the cluster to get some more effective data on instance sizing and number of instances.

IRSA (IAM Roles for Service Accounts)

Additional resources were needed for this service such as S3 buckets, Kafka, Memory DB, and MySQL. These resources were created externally via Terraform. However, the problem remains of how we connect to these services. Of course, there is the classic method of generating service accounts or user/password combos. However, there is a better and more secure solution - identity-based auth.

This is where IAM Roles for Service Accounts (IRSA) comes into play. With IRSA, we simply create a Kubernetes service account for our deployments. An IAM role is created with a policy that allows access to the resources. This IAM role is then associated with the Kubernetes service account through a trust policy. Like magic, your workload is able to access the resources without credentials.

Lessons Learned and Future plans

It was a long journey to go from no experience with EKS to a production cluster. So far it is running relatively flawlessly with little to no maintenance needed on the cluster itself. A lot of the challenges now lie in improvements and our eventual first Kubernetes version upgrade (which I have tested and documented a process for).

Lessons from this project were, as per usual with most of us, read the documentation and read it closely. Like the tortoise taught us, there are no shortcuts and shortcuts ultimately make you take longer. Observability is important at all levels and it is important to get observability early. Being able to look at metrics and logs in a centralized location makes a world of a difference in speed. Also how we implement observability matters, it is not always free. Finally, just because a service is “managed”, does not mean you have a free cake. Effort will still be required to learn how to operate the service appropriately, especially if you stray from the provided path.

Future Plans

The next iteration of work involves optimizing for costs further and improving developer experience.

Fri, 24 Apr 2026

Accessing data from WD My Book Live HDD

[linkstandalone]

Recently, I was asked to access data from a HDD which was previously inside of a WD My Book Live enclosure. I encountered a problem when attempting to mount the HDD on my Linux machine. The drive appeared as a block device with 4 partitions. The first 2 partitions showed as "linux_raid_member", the third appeared as swap and the fourth which was the biggest partition, presumably holding the data, appeared as ext4. Here is a picture of what I saw with lsblk:

When I attempted to mount the 1.8T ext4 partition it would give me a "wrong fs type" error message. The first thing I wondered was whether the filesystem was corrupt and I ran e2fsck. It found some errors, however, I could still not mount the drive.

Noticing that I could still not mount the drive, I attempted to get at the data using debugfs. I was finally able to see the folders inside the filesystem. At this point I understood two things. One, the data still exists and is potentially uncorrupted and two, there was something else funny going on with the filesystem.

My next step was to run dmesg to see if there was anything of use in there. What I found was a bunch of errors referencing block size. This was something new to me. I began to do some googling and found that apparently mount has problems mounting filesystems with a blocksize over 4096. I began to investigate the filesystem further using an assortment of tools. Both dumpe2fs and tune2fs told me that I was dealing with a block size of 65536. This was shocking to discover as I had never really seen different block sizes being used, especially not one so large. I also attempted to retrieve the blocksize using blockdev, however, for some reason I got a 4096 blocksize which I knew was incorrect. I am not entirely sure why this was. Below are pictures of the results.


Upon further research I learned that the kernel has had an ongoing problem of dealing with large block sizes. It has to do with page size not being large enough, since its 4096 by default on most machines, along with other misconfigured parameters in the kernel. The solution to be able to retrieve this data is to either modify your kernel parameters to account for this new block size, which is not necessarily realistic all the time, or to use another solution I found, fuseext2. It will know how to handle the weird block size and will allow you to retrieve your data.

The issue of block size is an interesting one. WD most likely chose this bigger block size since this is a drive meant for backups. What this means is potentially big files being transferred. A larger block size can provide better performance for such a situation. Below are some additional resources to read about the large block size problem with Linux:

Sat, 14 Aug 2021

Configuring OpenSSH to use Kerberos Authentication

[linkstandalone]

This article is a continuation of the last article about setting up an MIT krb5 server. We will configure OpenSSH to work using tickets from this server.

Modern OpenSSH uses GSSAPI to communicate with Kerberos. What this means is that despite the fact that there are configuration options that start with the word Kerberos, we should not be using them. These options are legacy options that only work over SSHv1 (now deprecated).

  1. Set a proper hostname
  2.   hostnamectl set-hostname client.kevco.virt
      
  3. Ensure time is synced using an NTP server
  4. By default, CentOS should have chronyd started and enabled, however, you may want to set up an ntpd server. It is very important that the kerberos server and clients have their time synced up. Otherwise, you will have problems authenticating.

  5. Install the Kerberos packages
  6.   yum install krb5-workstation krb5-libs
      
  7. Edit /etc/krb5.conf
  8. Configure this file in a similar manner to the server. Replace the example domain and realm with your domain and realm. Also make sure that you point to the correct kdc and admin server.

  9. Add an entry into /etc/hosts (optional if DNS configured)
  10. If you do not have DNS configured with the proper SRV and A records, you should add an entry pointing to the hostname of the kerberos server. Make sure that this hostname is the same as the Service Principal Name (SPN) you gave the server. You cannot have an entry in your /etc/hosts that is kerberos instead of kerberos.kevco.virt if you do not have an SPN matching host/[email protected] in your KDC.

  11. Create a service principal for this machine and add it to this machines keytab
  12. Each machine must have its own service principal and have its key stored in its own keytab.

      kadmin -p admin/admin -q "addprinc -randkey host/server.kevco.virt"
      kadmin -p admin/admin -q "ktadd host/server.kevco.virt"
      
  13. Edit /etc/ssh/sshd_config and insert the following
      GSSAPIAuthentication yes
      GSSAPICleanupCredentials yes
      GSSAPIStrictAcceptorCheck yes
      

    As stated before GSSAPI is the interface used by SSHv2 in order to authenticate with kerberos so it must be enabled. The second option is very important. GSSAPICleanupCredentials ensures that your credentials are destroyed on logout instead of staying in the cache. The reason this is important is that if an attacker gets into your machine, they can steal the ticket from this machine and Pass The Ticket to another server to which these credentials may provide access to. Finally, we enable the StrictAcceptorCheck which verifies that the SPN matches the hosts hostname. You can disable this if you have multiple aliases. You should probably disable password authentication at this point as well to reduce the attack surface.

  14. Add approved users to the ~/.k5login file or create a user
  15. There are two options you can use to allow users to log in to an account on your server using kerberos. The first option is to create a .k5login file in the home folder of the user you want the kerberos user to be allowed to log in as. In this case we will put it in the root users folder as this is an example (Please do not allow root user login to your SSH servers). You will place one User Principal Name (UPN) per line:

      [email protected]
      

    The second option is to simply create a new user that matches the username of the User Principal Name (UPN) that will be logging in. For example, [email protected] will be able to log in to the kdiaz user on the server.

  16. Configure the client using steps 1-5 without forgetting to add the SPN matching hostname of the ssh server to your /etc/hosts file as well
  17. Edit the /etc/ssh/ssh_config on the client device
  18.   GSSAPIAuthentication yes
      GSSAPIDelegateCredentials no
      

    Once again we enable GSSAPI authentication so that we can use Kerberos. We also, depending on the environment, will disable GSSAPIDelegateCredentials. For this example, we do not need it. However, if you need the server to to obtain tickets on behalf of you, you can enable it. This may be important/useful in certain scenarios. If you do not need it, keep it off as an infected machine with the ability to request tickets on your behalf can cause you trouble.

  19. Get a ticket and test
  20.   kinit kdiaz
      ssh [email protected]
      

    If all is well, you should now be able to use your ticket to log in to the configured user on your server. It is important that you use the proper hostname that matches the servers SPN to avoid trouble. It is also important that the key version number (kvno) of your SPNs and UPNs match throughout the two machines youre trying to get to communicate. It can be a source of headache. Errors such as this one can be found by running the SSH server in debug mode and attempting to authenticate. If you get an error due to the kvno of your UPN not matching, you can clear your credentials from the cache using kdestroy and reinitialize them with kinit. Additional debugging help can be done by also running the ssh client in verbose mode using the -v flag.

Thu, 04 Feb 2021

Creating An MIT Kerberos 5 Server In CentOS

[linkstandalone]

Kerberos is an authentication protocol which is very commonly used throughout the world. It is most commonly seen through its implementation in Microsoft Active Directory. However, MIT has an implementation of the Kerberos protocol, krb5, which we can use on Linux. It uses symmetric encryption combined with a ticket based system in order to securely authenticate users. I will not spend much time describing the protocol as there are existing resources such as this one which explain it and the terminology in this article very well.

MIT krb5 can be used as a standalone product or can be integrated with a LDAP server, such as OpenLDAP, as a backend. In this article, I will only discuss krb5 as a standalone authentication product. In this configuration, there will be no identity tied to the Kerberos Ticket provided other than the User Principal Name (UPN). If you want a full identiy and authentication solution you should integrate krb5 with LDAP.

The main components of the krb5 server are the Key Distribution Center (KDC), the kadmin server, the database and the keytab file. The KDC is the main server and kadmin is the server that allows you to manage principals in the database as well as manage the keytab. There is also an additional service that is running as part of the kadmin service which is kpasswd. This allows users to reset their password using the kpasswd utility.

Installation and Configuration

  1. Set a proper hostname
  2.   hostnamectl set-hostname kerberos.kevco.virt
      
  3. Ensure time is synced using an NTP server
  4. By default, CentOS should have chronyd started and enabled, however, you may want to set up an ntpd server. It is very important that the kerberos server and clients have their time synced up. Otherwise, you will have problems authenticating.

  5. yum install krb5-server krb5-libs krb5-workstation
  6. Edit /etc/krb5.conf

    Uncomment and replace all lines with references to the example domain and realm. The standard realm name convention is to use your domain name capitalized. Below you will find an example config declaring the realm KEVCO.VIRT on a machine with the hostname kerberos.kevco.virt.

  7.   [logging]
       default = FILE:/var/log/krb5libs.log
       kdc = FILE:/var/log/krb5kdc.log
       admin_server = FILE:/var/log/kadmind.log
    
      [libdefaults]
       default_realm = KEVCO.VIRT
       dns_lookup_realm = false
       dns_lookup_kdc = false
       ticket_lifetime = 24h
       renew_lifetime = 7d
       forwardable = true
       rdns = false
    
      [realms]
       KEVCO.VIRT = {
         kdc = kerberos.kevco.virt
         admin_server = kerberos.kevco.virt
       }
    
      [domain_realm]
       .kevco.virt = KEVCO.VIRT
       kevco.virt = KEVCO.VIRT
     
    Here I set the log file locations in the logging section. In the libdefaults section, the default realm is set to KEVCO.VIRT as you can define multiple realms for a KDC. I disabled DNS lookup as there is no DNS server in this scenario. I also disabled rdns since reverse DNS is not set up in this scenario (because there is no DNS server). Finally, I declared the realm KEVCO.VIRT and provided the hostnames for the kdc and kadmin server which happens to be this same machine. The final section simply defines translations from domain name to realm name. For any additional information check man krb5.conf or MIT documentation.

  8. Edit /var/kerberos/krb5kdc/kdc.conf
  9. This is the file that holds the main configuration for your KDC. Replace the example realm with your own and set any other options you would like. Below is an example of a config you can use. For available options reference the documentation. In this example, I leave the default encryption types enabled, however, you may want to disable the likes of des, des3, and RC4 in favor of AES if possible.

     [kdcdefaults]
     kdc_ports = 88
     kdc_tcp_ports = 88
    
     [realms]
      KEVCO.VIRT = {
       master_key_type = aes256-cts
       acl_file = /var/kerberos/krb5kdc/kadm5.acl
       dict_file = /usr/share/dict/words
       admin_keytab = /var/kerberos/krb5kdc/kadm5.keytab
       supported_enctypes = aes256-cts:normal aes128-cts:normal des3-hmac-sha1:normal arcfour-hmac:normal des-hmac-sha1:normal des-cbc-md5:normal des-cbc-crc:normal
      }
     
  10. Edit /var/kerberos/krb5kdc/kdm5.acl

    This is the ACL file that determines who will be able to do which actions on the kadmin server. You should add permissions for the admin/admin service principal as can be seen below. Without this, you will not be able to do anything on the server remotely, including pulling down the keys into the keytab of a client. In order to restrict permissions down to certain actions see the documentation.

     admin/[email protected]   *
     
  11. Create the kerberos database
  12.  kdb5_util create -s
     
  13. Create the admin service principal
  14.  kadmin.local -q "addprinc admin/admin"
     
  15. Start and enable the kdc and kadmin
  16.  systemctl start krb5kdc kadmin
     systemctl enable krb5kdc kadmin
     
  17. Create a service principal for this computer with a random key and add the keys to the local keytab
  18. All systems that you want to use kerberos authentication should have a service principal (SPN). The standard is host/hostname_in_dns. You can add multiple principals as aliases if you have more than one name for your machine. You must have your own keys stored in your local keytab. You will also need to add that clients own generated keys from their SPN to their keytab if you want things to work properly.

     kadmin -p admin/admin -q "addprinc -randkey host/kerberos.kevco.virt"
     kadmin -p admin/admin -q "ktadd host/kerberos.kevco.virt"
     
  19. Create your own principal and give it whatever access you need in the kadm5.acl file
  20.  kadmin -p admin/admin -q "addprinc kdiaz"
     
  21. Create a test ticket using kinit
  22. You need to get a ticket using kinit for an existing principal (admin in this case) and then you can view it and other stored tickets using klist. Finally, you can destroy this ticket and remove it from the cache using kdestroy.

     kinit kdiaz
     klist
     kdestroy -A
     
  23. Open the proper ports in the firewall

    Port 88 needs to be open primarily on 88/udp. However, you also need to open 88/tcp as kerberos will use this if the Tickets get too big. Other ports include 749/tcp for the kadmin server and 464/udp for the kpasswd service.

       for port in {88/tcp,88/udp,749/tcp,464/udp};do
         firewall-cmd --permanent --add-port $port;done
       firewall-cmd --reload
     
  24. (Optional) Add DNS SRV records
  25. If you have DNS configured in your environment you should add records for your kerberos server. The record names are self explanatory/if you are doing this you likely know what youre doing.

     $ORIGIN _tcp.kevco.virt.
     _kerberos-adm    SRV 0 0 749 kerberos.kevco.virt.
     _kerberos        SRV 0 0 88  kerberos.kevco.virt.
    
     $ORIGIN _udp.kevco.virt.
     _kerberos        SRV 0 0 88  kerberos.kevco.virt.
     _kerberos-master SRV 0 0 88  kerberos.kevco.virt.
     _kpasswd         SRV 0 0 464 kerberos.kevco.virt.
     
Tue, 02 Feb 2021

Linux Authentication Using G-Suite Secure LDAP

[linkstandalone]

Google's G-Suite has been dominating the field of cloud suite services for a long time in both the enterprise and the education world. It is a strong competitor to options such as Microsoft Office 365. Not only can it offer mail, storage, and other related apps which users expect, but it can also offer lots of features to help administrators. It has a very useful interface for centrally managing all of your chromebook devices which has become a large part of the technology used in the education space. It is already essentially an identification service. Google allows us to use this identification service for devices other than chromebooks and apps through Lightweight Directory Access Protocol (LDAP). In this blog post, I will discuss how I managed to set up SSSD to provide authentication via G Suite secure LDAP. This allows for the use of G Suite instead of having to duplicate all your users into a Microsoft Active Directory server simply for authentication or paying for a service. For the sake of brevity, I will only be showing how I did this in CentOS 7. However, it is really easy to adapt these instructions to the distro of your choice. The only real differences will most likely be related to installing the software and configuring SE Linux (since it is not enabled on all distros).

Installing required packages

yum install sssd sssd-tools sssd-utils unzip

Generating Cert and Key in G Suite

  1. Open your G Suite Console
  2. Navigate to Apps>LDAP
  3. Click on "Add Client"
  4. Give the client a name
  5. Either allow access to everyone in the organization or restrict it to certain org units
  6. Allow read permissions for both users and groups
  7. Click "Add LDAP Client"
  8. Download the zip file containing the cert and key
  9. Enable the creds by switching the LDAP client to "on" under "Service status"
  10. Upload the zip file to the client and unzip it
  11. Move the files somewhere such as /var/lib

Configuring sssd.conf

In order to set up /etc/sssd/sssd.conf, its easiest to copy the default config that google recommends and work off of that. You can find it here under the SSSD tab.

Make sure to replace the domain and location of the cert and key. After doing this, we do have to add a few other things so that we can better integrate SSSD as an authentication service across the system. Under the "sssd" section, add sudo at the end of the services option so that we can allow sudo to work with our domain creds. The next thing you can do is modify some settings for offline login. You can create a "pam" section and set numbers for "offline_credentials_expiration", "offline_failed_login_attempts", and "offline_failed_login_delay". These are the options that I have set in my VM, but there are a lot more you can use. Refer to the man page for sssd.conf or the Red Hat documentation linked in the testing section to see what else you can do. Finally, we have to make sure the system will be usable and that the user will not encounter any errors on login. We do this by setting two options to True in the "domain/YOUR_DOMAIN.com" section. The first option is "create_homedir" which ensures that the user will have a home directory created for them when they log in. The other option is "auto_private_groups" which helps with UID and GID errors that may occur since the UID and GID are set from G Suite instead of being locally stored in /etc/passwd. Below you will find the file I used to test in my VM. I replaced my actual domain with "yourdomain.com".

/etc/sssd/sssd.conf


[sssd]
services = nss,pam,sudo
domains = yourdomain.com

[domain/yourdomain.com]
ldap_tls_cert = /var/lib/ldapcreds.crt
ldap_tls_key = /var/lib/ldapcreds.key
ldap_uri = ldaps://ldap.google.com
ldap_search_base = dc=yourdomain,dc=com
id_provider = ldap
auth_provider = ldap
ldap_schema = rfc2307bis
ldap_user_uuid = entryUUID
ldap_groups_use_matching_rule_in_chain = true
ldap_initgroups_use_matching_rule_in_chain = true
create_homedir = True
auto_private_groups = true

[pam]
offline_credentials_expiration = 2
offline_failed_login_attempts = 3
offline_failed_login_delay = 5

Configuring nsswitch.conf

authconfig --enablesssd --enablesssdauth --enablemkhomedir --updateall
Open /etc/nsswitch.conf and add the line
sudoers:    files sss
Everything else should have been configured by the authconfig command.

Permissions and SE Linux

setenforce 0
chcon -t sssd_t ldapcreds.crt
chcon -t sssd_t ldapcreds.key
setenforce 1
chmod 0600 /etc/sssd/sssd.conf
If you are having problems getting things to work after attempting it this way, just disable SE Linux

Enable and start everything

sudo systemctl start sssd
sudo systemctl enable sssd

Testing

The easiest way to test if everything is working is to su into your user account on your domain and see if you can log in using your password. If this works, you should have a home folder created in /home and be able to try a sudo command. By default, it will say you are not allowed to run sudo since your account is not in the sudoers file. The easiest way to give access to sudo commands is to give a group permissions to do things. Your google groups will work just as if you were giving a local group sudo access. However, you can still just give individual users access the same way.

Alternatively, you can use sssctl to do a lookup on a user account in your domain. It is done as follows:

sssctl user-checks USERNAME
This and so many other tools and functionalities can be found in Red Hat's System-level Authentication Guide. If you are having problems make sure to check your /etc/sssd/sssd.conf config file so that it is accurate and has the proper permissions of 0600. Additionally, make sure that SE Linux is not causing you problems. Any other debugging can be done through reading of man pages (sssd, sssd.conf, etc), googling, and looking at google's Support Center page for Secure LDAP.

Mon, 10 Feb 2020

Powershell Remote Management From Linux

[linkstandalone]

When you are an avid linux fan/user in a windows environment, you try to find ways to avoid having to use a windows computer. As I was exploring different methods of remote administration for windows, I decided to learn about Powershell Remoting. I wanted to try and use the Powershell that is now available for linux, Powershell Core. With earlier versions, I was unable to do much, however, newer versions bring much more useful functionality. In this post, I will talk about how to get set up to remotely administer windows systems from Linux using Powershell Core.

The first step is to install the proper version of Powershell. In order for this to work, you need to have a newer version of Powershell installed. As of writing this post, the current version on which this works is 6.2.3. The reason for this is that its a relatively new feature for linux powershell.

Installation

CentOS/RHEL
Add the Microsoft repo
    curl https://packages.microsoft.com/config/rhel/7/prod.repo | sudo tee /etc/yum.repos.d/microsoft.repo

Install the package
    sudo yum install powershell

By default we do not have NTLM authentication so install this package
    yum install -y gssntlmssp
Arch Linux using yay AUR Helper
Install the package
    yay -S powershell-bin

By default we do not have NTLM authentication so install this package
    yay -S gss-ntlmssp
Ubuntu
Download the deb from Microsoft according to your linux version
  wget https://packages.microsoft.com/config/ubuntu//packages-microsoft-prod.deb

Install the package to register the Microsoft repo GPG keys
  dpkg -i packages-microsoft-prod.deb

Update your repo database
  sudo apt update

Install the package
  sudo apt install powershell

By default we do not have NTLM authentication so install this package
  sudo apt install gss-ntlmssp
Debian 9
Install some pre-reqs if you do not have them already
    sudo apt-get install -y curl gnupg apt-transport-https

Import the Microsoft repo GPG keys
    curl https://packages.microsoft.com/keys/microsoft.asc | sudo apt-key add -

Add the Microsoft repo
    sudo sh -c 'echo "deb [arch=amd64] https://packages.microsoft.com/repos/microsoft-debian-stretch-prod stretch main" > /etc/apt/sources.list.d/microsoft.list'

Update your repo database
  sudo apt update

Install the package
  sudo apt install powershell

By default we do not have NTLM authentication so install this package
  sudo apt install gss-ntlmssp
For any other distro please refer to Microsoft's Documentation

Setting up the client with physical access to the system

  1. Check if PS Remoting is enabled
  2.      Get-PSSessionConfiguration
         
  3. Enable PS Remoting
  4.     Enable-PSRemoting -Force
        
  5. Check trusted hosts
  6. In order for you to be able to remotely manage a computer using this method, you must be part of the systems trusted hosts. This serves as a form of access control so that even if a malicious actor gains credentials, they cannot simply remote into the system and start running commands. The next few steps will show you how to manage these trusted hosts.

        Get-Item WSMan:\localhost\Client\TrustedHosts
        
  7. Remove all trusted hosts, if any exist, to allow for a clean slate
  8.     Clear-Item WSMan:\localhost\Client\TrustedHosts
        
  9. Add yourself as a trusted host
  10.     Set-Item WSMan:\localhost\Client\TrustedHosts -Force -Value IP_OR_HOSTNAME_HERE
        winrm s winrm/config/client '@{TrustedHosts="IP_OR_HOSTNAME_HERE"}'
       

    Alternatively you can allow all hosts to PSRemote into this system by setting the "Value" flag to the * wildcard instead of defining a specific IP. This is NOT recommended for security reasons.

  11. Restart the remote management service and make it start at boot
  12.     Restart-Service -Force WinRM
        Set-Service WinRM -StartMode Automatic
        

Setting up the client using PSExec (Windows)

Using psexec, it is possible to remotely execute commands on a system that has the $admin SMB share exposed and open. This is more common than you might think and can be very dangerous. Using psexec, you can run commands as NT/System which is the most powerful user account on a windows computer. This account has more power than the administrator account on your computer. If you are able to use this method without the need for credentials, be aware that a malicious actor will be able to do the same. Passing captured/stolen hashes using psexec is a common tactic used by attackers to pivot to other systems on your network after initial compromise. Unfortunately, I will only cover this from the windows perspective as I have yet to find a modern, working Linux equivalent to these tools. There is the winexe project, but that is outdated and did not work for me on Windows 10 clients. That being said, there are definitely ways to do it from Linux.

In order to get psexec, you need to download PsTools from Microsoft. You will unzip it and find psexec.exe in the extracted folder. After opening a cmd or powershell window and navigating to this folder, you can run the commands from the previous section of this blog just as if you had real physical access to the system using the format shown below.

Without credentials
    psexec.exe \\RemoteComputerGoesHere -s powershell Enable-PSRemoting -Force

With credentials
    psexec.exe \\RemoteComputerGoesHere -u UserName -s powershell Enable-PSRemoting -Force

Opening a remote powershell session

When you are running commands from linux, it is important that you set authentication to negotiate in the flags (as can be seen below). Without this flag, authentication between your Linux machine and the windows machine cannot occur properly.

Save the credentials in a secure environment variable
    $creds = Get-Credential -UserName ADMIN_USERNAME_HERE

Start remote shell with environment variable creds
    Enter-PSSession -ComputerName IP_HERE -Authentication Negotiate -Credential $creds

Start remote shell with username and creds at runtime
    Enter-PSSession -ComputerName IP_HERE -Authentication Negotiate -Credential USERNAME

Invoking commands on a client

Invoke-Command -ComputerName IP_HERE -Authentication Negotiate -Credential $creds `
-ScriptBlock {COMMAND_HERE}

Invoking a PS1 script on a client

Invoke-Command -ComputerName IP_HERE -Authentication Negotiate -Credential $creds `
-FilePath C:\Path\To\Scripts\script.ps1

Managing several clients

You can run either "Enter-PSSession" or "Invoke-Command" with the -AsJob flag and it will run in the background. You will be returned a job id which you can later use to retrieve the job's output using
Receive-Job -id JOB_ID_HERE
If you forgot the job id, you can check it using
Get-PSSession
If you started a background PSSession you can work with it as follows
Accessing session
    Enter-PSSession -id SESSION_ID
Execute command with session
    Invoke-Command -Session (Get-PSSession -id SESSION_ID) -ScriptBlock {COMMAND_HERE}

You can also use other methods such as storing a list of clients in a CSV file or pulling them straight from your Active Directory server.

Running remote commands on several machines from csv of "ComputerName, IP"
    foreach($row in $devices.IP) {
        Invoke-Command -ComputerName $row -Authentication Negotiate `
        -Credential $creds -ScriptBlock{COMMAND_HERE}
    }

Running remote commands on several machines at a time using AD and pipe
    Get-ADComputer -Filter *  -properties name | select @{Name="computername";`
    Expression={$_."name"}} | Invoke-Command -ScriptBlock {COMMMAND_HERE}

Killing background sessions

If you wanted to kill a background session, you would normally run
Get-PSSession -id SESSION_ID | Disconnect-PSSession
However, unfortunately Linux powershell core, at least on 6.2.3, does not have Disconnect-PSSession available as a command. This means that the only way to end a background session is to enter the session and manually type exit. Alternatively you can find and kill the PID of the process.

Where to learn more

There is a lot of information here, some of which may not make sense to you if you have little experience with remote administration over the command line. I highly recommend you start up a windows virtual machine or two and practice the techniques discussed in this post. Additionally, you can use the resources I used to learn the things I am talking about in this post linked below.

Microsoft Powershell Remoting Blog Series
Powershell from Linux
Tue, 14 Jan 2020

QEMU Port Forwarding Using Iptables

[linkstandalone]

Normally, there is no website when I go to my debian server's IP address in my browser. However, I have a web server running in a QEMU VM on that server and would like to access it from my laptop. After following the steps in this guide, I am able to access that web server by going to the IP of my debian server as if it was installed on the server itself. Unless you give the VM its own real IP address from the router, we cannot access that VM from another computer. That being said we may not want to give the VM its own TAP and IP address. The alternative is to forward all requests to the host computer on a specific port to the port on the VM for the service you want to access. I used iptables to do this port forwarding just like we do port forwarding on our home routers. Our routers use NAT to do port forwarding allowing us to access services in our homes from across the internet. We can replicate this in our VM's with the below iptables rules.

NOTE:In the case described below, 192.168.1.250 is the IP address of the debian server and 192.168.122.215 is the ip address of the VM. Both of these devices are on a /24 subnet. The interface on which the debian server connects to my home network is enp2s0.

First we enable this NAT functionality by setting the MASQUERADE option.
sudo iptables -t nat -A POSTROUTING -j MASQUERADE
Then, we set a PREROUTING rule which lets the host device detect any incoming connections on port 80 for our network interface and redirect it to the VM's IP address on port 80 instead of attempting to connect to the hosts own port 80.
sudo iptables -t nat -A PREROUTING -d 192.168.1.250/24 -i enp2s0 -p tcp --dport 80 -j DNAT \
--to-destination 192.168.122.215:80
Finally, we set a FORWARD rule which ensures the packet actually gets sent to port 80 on the VM and that the VM is open to accepting that packet.
sudo iptables -I FORWARD -p tcp -d 192.168.122.215/24 --dport 80 -m state --state \
NEW,RELATED,ESTABLISHED -j ACCEPT
Wed, 08 May 2019

QEMU Host Only Networking

[linkstandalone]

Often times, it is useful to use a host-only network in a lab environment, especially when dealing with certain security labs. What a host-only network allows is for your virtual machines to communicate with each other on their own independent network and communicate to the host computer/hypervisor. However, the VM's will not be able to reach out to other devices on your network and devices on your network will not be able to reach them. In order to set up this isolated environment, you need to create a bridge and tap just like any other VM networking set up. There are two methods to do this, one is manually and the other automatically using the libvirt XML format.

Libvirt XML Method

In order to do this the easy way, you can create an XML file whose contents are the below. This is simply the default network setup but without the forward tags. This makes it so the network is limited to the virtual environment. I renamed the bridge to "virbr1" instead of the default "virbr0" so there is no conflict. I also changed the last byte of the mac address and set an appropriate DHCP range and IP address as to not interfere with the other network. Here I simply changed the IP from the 192.168.122.0/24 subnet to 192.168.123.0/24. In the DHCP range, do not forget to leave out the .255 address since this IP is used for broadcast. Finally, I changed the name to secnet to help me identify it. I called it that because this is the network I use for security labs, often with vulnerable systems, which I want no where near my real network.

    <network>
      <name>secnet</name>
      <uuid>8f49de66-0947-4271-85a4-2bbe88913555</uuid>
      <bridge name='virbr1' stp='on' delay='0'/>
      <mac address='52:54:00:95:26:26'/>
      <ip address='192.168.123.1' netmask='255.255.255.0'>
        <dhcp>
          <range start='192.168.123.30' end='192.168.123.254'/>
        </dhcp>
      </ip>
    </network>
After creating this file, simply run virsh net-define file_name.xml and virsh net-start file_name. If all is well you have officially set up the network and can configure the client. You can do this either through virt-manager by changing the NIC settings from the default networks bridge to your bridge, in this case, virbr1, or you can do virsh edit domain and look for a line with <interface type='bridge'> and modify the value for source. If you have no NIC set up at all, add the following lines to your code, modifying certain values such as the mac address and pci slot under the address tag as necessary. When you boot up the client it should automatically get a DHCP address.
    <interface type='bridge'>
      <mac address='52:54:00:8c:d0:7e'/>
      <source bridge='virbr1'/>
      <model type='virtio'/>
      <address type='pci' domain='0x0000' bus='0x00' slot='0x08' function='0x0'/>
    </interface>

Manual Method

Although you can do the above method and have it work well, there are times when you want to learn what actually happens behind the scenes and how it all works. In order to do this, you have to understand that the virtual network is made up of a bridge and a tap where the tap acts as a virtual NIC for the VM. The bridge is what hosts the network and acts as a router. We need to create this bridge, assign it an IP, then create the tap and make the bridge a slave of the tap. At this point, a functioning network will be established with static IP addresses. If you want DHCP, you can do so using dnsmasq. Finally, add the appropriate settings to the VM as described above in the XML method. From there on out, everything else is simply a matter of configuring the client itself. The only negative to this manual method is that you have to start and stop the network manually, but this is easily scriptable.

    # Create the virtual bridge and name it secnet and bring the interface up
    sudo ip link add secnet type bridge; sudo ip link set secnet up

    # Create the tap and name it secnet-nic (you can call it whatever you want)
    sudo ip tuntap add dev secnet-nic mode tap

    # Bring up the interface in promiscuous mode
    sudo ip link set secnet-nic up promisc on

    # Make secnet-nic a slave of secnet
    sudo ip link set secnet-nic master secnet

    # Give bridge secnet an IP address of 192.168.123.1
    sudo ip addr add 192.168.123.1/24 broadcast 192.168.123.255 dev secnet
DHCP With dnsmasq

Setting up DHCP with dnsmasq is simple. You can either write out the config within the command, as is shown below, or you can create a config file which you can read from, also shown below. The important steps in running dnsmasq are setting the correct interface and DHCP range as well as setting the -p option to 0. When the -p option is set to 0, the DNS function of dnsmasq will no longer start which removes conflicts with any active DNS servers on your host computer. I have this problem since I use dnscrypt on my laptop, however, you may not encounter such conflict. There is no real need to host our own DNS server so nothing is lost by doing this.
Note: The secnet config provided below was generated by libvirt when using the XML method taught in this blog.

    sudo dnsmasq --interface secnet -p 0 --bind-interfaces --dhcp-range=192.168.123.10,192.168.123.254

    or

    sudo dnsmasq --conf-file=/var/lib/libvirt/dnsmasq/secnet.conf --leasefile-ro

    # secnet.conf
    strict-order
    pid-file=test.pid
    except-interface=lo
    bind-dynamic
    interface=secnet
    dhcp-option=3
    no-resolv
    ra-param=*,0,0
    dhcp-range=192.168.123.30,192.168.123.254,255.255.255.0
    dhcp-no-override
    dhcp-authoritative
    dhcp-lease-max=225
    dhcp-hostsfile=test.hostsfile
    addn-hosts=/var/lib/libvirt/dnsmasq/default.addnhosts

If you did not set up DHCP using dnsmasq you will have to manually set up the IP and default gateway route on the client. It is simply two lines if you use the command line and a few changes within the settings panel of whatever desktop environment you're using in the vm.

    # Assign an ip address to the interface
    ip addr add 192.168.123.2/24 dev eth0

    # Add the default gateway route which is the secnet address
    route add default gw 192.168.123.1 eth0

    # Test network connectivity
    ping 1.1.1.1

    # There is no DNS assigned so you have to manually add a server in resolv.conf
    echo 1.1.1.1 > /etc/resolv.conf

    # Final network connectivity test with DNS resolution
    ping google.com

If you followed all the steps in this guide, you will have successfully created an isolated host-only network. To confirm this, try pinging a device in your network such as your phone. This should not work, but you should be able to ping the host computer at 192.168.123.1 and other VM's on the same network.

Fri, 03 May 2019

Running VMware Images in QEMU

[linkstandalone]

In order to keep true to the FLOSS philosophy, I prefer to virtualize things using QEMU KVM. This also allows me to practice with the tools used in a lot of Linux environments for virtualization. Although it is a great tool for deploying your own virtual machines, it gets in the way when I want to open up a VM image for a security lab or some pre-made tool such as SIFT Workstation since they usually come in either virtual box or VMware images. While I could just download those tools, I prefer not to add more programs to my computer unless it is absolutely necessary. Of course, I also just like to find new ways of doing things. I hate seeing people online responding with "just download XYZ program and be done with it". Yes I can download VMware workstation if I needed it at work for something really quick and it will most likely work, but when it comes doing things at home, that mindset is soooo boring.

Normally, VMware images come in a *.ova file. The first thing to realize is that if you run file on the ova, you will notice that it is simply seen as a tar archive. The ova holds multiple files inside including the actual image, normally in a. The ova holds multiple files inside including the actual image, normally in a *.vmdk file, and a *.ovf file which is an XML file with information pertaining to the VM, comparable to the QEMU XML used to configure your VM settings. You may also find other files in there such as an ISO or a file with hashes. The only file we care about though is the *.vmdk file as that is the one with the actual image. If there are more than one, the file which has the name most comparable to the original *.ova filename should be the correct one. If it turns out this one does not work after the following process, you can always try the other one.

We will be converting the vmdk to qcow2. I chose this format simply because its the one I use with my other images and it works well with this conversion process. To convert it, you need to use the qemu-img and its convert function. After this point, we will be able to load the qcow2 image as a regular disk image in QEMU. You can do this through virt-manager, virt-install or copy another VM's XML and change the source for the disk as well as other options like the name, the UUID, and the MAC address. Something else you can try for a quick test is qemu-system-x86_64 but this can sometimes be very slow unless you set a ton of argument options.

Here are the actual steps:

  1. tar -xvf original.ova
  2. qemu-img convert -O qcow2 original.vmdk original.qcow2
  3. Run the qcow2 image in QEMU
  4. If it does not boot, try the other vmdk file if there is one

As you can see, it is pretty simple to do this and so far have used it on 3 different VMware images flawlessly. However, you have to realize it may take some experimentation. Do not give up on it right away and you will be able to avoid downloading extra software and avoid looking for the correct free trial version or getting an expensive license.

Wed, 30 Jan 2019

Detecting The Effect Of A Phishing Attack On Your G Suite Domain

[linkstandalone]

One of the things we have to be weary of as administrators is security. Phishing attacks are constantly becoming harder to detect and defend against. Other times, it is quite easy to detect. In this post I will tell you what to do when you detect a phishing attack on your domain and how to mitigate.

Recently our domain received a phishing attack which told users that they had a new voicemail from someone and to click on a link to view it. When clicked, you were redirected to an outlook login page with your email address already entered in the username field. None of the IT department received the email, but a lot of employees did. We received a question about it from one employee and did not think much of it. I simply recommended that they not open it as I thought it was an isolated incident. I now realize that I should have done more in response. No less than an hour later, I received two more questions about the same email. Luckily, those two employees realized it looked sketchy and did not click on the link. I instantly knew that this was a phishing attack on the domain. All of the emails had the same sender, but slightly different subject lines so I knew that the sender was the constant I needed to use to run my audit.

To run my audit, these are the steps I took:

  1. Log in to G Suite dashboard
  2. Go to "Reports" tab
  3. Scroll down to the audit section on the sidebar and select "Email Log Search"
  4. Enter your desired search parameters, in this case the senders email address
  5. Select an appropriate time frame to check. I checked the last 7 days since it was recent
  6. Save the results as a google sheets file in the upper right corner and share it with your team
Now that we have the logs, we can start mitigating the problem. The first thing I did was to cut the head off the snake by setting a global block on the email address in the G Suite admin console. Afterwards, I prepared an email advising all employees of the situation, what to look for, what happens when you click on the link, and what to do if they have received the email. Using the logs, I was able to also individually verify with those affected if they had clicked on the link and then reset their passwords. Lastly, I was able to detect the time and date of incident which was actually the night before. I was not alerted to it until the next evening so it is possible that multiple people clicked on the link.

Takeaways

There is not a lot we can do about these attacks except deal with them after they occur. A password compromise of a super admin account via a phishing attack could be devastating for your domain as the attacker will have complete control over everything. This is also one of the reasons you should educate your users and administrators on the dangers of such attacks and how to detect them. Next time, I will be sure to run an audit as soon as I see the first message since it is so easy and quick to do. It will help me reduce the number of people clicking on such emails. I will also look into some possible defensive cyber education for users.

Thu, 22 Nov 2018

Using Projectors with i3wm

[linkstandalone]

I use arch linux with i3-gaps on an old w520 I saved from being recycled. This computer and environment has truly helped me increase my efficiency. The downside, is that everything has to be manually configured. I do not mind this at all, however, because it has helped me understand how computers do certain things. If you were to just plug in the vga cord on a computer running i3wm, nothing would happen. You would then run xrandr to see if the vga connection even shows up and you will realize it has not. This is because there is no program running in the background to detect these display changes. You have to manually start the VGA output in xrandr. So, you run xrandr --output VGA-1 --mode 1024x768 and find that now it works, but it looks really wierd. You try scaling the display and shifting it but find no success. Why is this?

If we take a look at a windows computer, or even one running an actual DE like gnome, we can see that the screen resolution for both the output screen and the laptop display both change. In fact they set themselves to the same resolution as one another This seems like a simple idea, but unless you are actually paying attention when you plug in a vga cable, you do not really notice it. What I ended up doing to solve this problem was writing 2 small scripts. One I run when I plug in a vga output and another when I remove the vga output. To activate it, I use xrandr --output LVDS-1-1 --mode 1024x768 --output VGA-0 --mode 1024x768. To disable it, I use xrandr --output LVDS-1-1 --mode 1920x1080 --output VGA-0 --off. One thing I did notice is that the script for turning on the vga output will not work until you run xrandr on its own. It may have something with letting xrandr try and detect the new connection. There is probably a method to automate this process, but until I figure it out, this is what I will do. I only use vga occasionally to test projector and smartboard functionality after a repair.

Mon, 22 Oct 2018

First Entry

[linkstandalone]

This is my first blog entry. In this blog I plan to occassionally update it with tutorials on things I use in my day-to-day life, have learned or practiced in my home lab. I may possibly speak my opinion on certain topics in technology.

Sun, 21 Oct 2018