<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Michael Chenetz</title>
    <description>The latest articles on Forem by Michael Chenetz (@mchenetz).</description>
    <link>https://forem.com/mchenetz</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F906489%2F3fa1a40d-9421-4dc3-b49b-1eb1a7e7025c.jpg</url>
      <title>Forem: Michael Chenetz</title>
      <link>https://forem.com/mchenetz</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/mchenetz"/>
    <language>en</language>
    <item>
      <title>Understanding Kubernetes Authentication and Authorization</title>
      <dc:creator>Michael Chenetz</dc:creator>
      <pubDate>Thu, 30 Nov 2023 15:24:21 +0000</pubDate>
      <link>https://forem.com/mchenetz/understanding-kubernetes-authentication-and-authorization-5bg9</link>
      <guid>https://forem.com/mchenetz/understanding-kubernetes-authentication-and-authorization-5bg9</guid>
      <description>&lt;p&gt;Setting up user permissions properly in Kubernetes involves understanding and effectively using Kubernetes' Role-Based Access Control (RBAC) system. RBAC in Kubernetes allows you to regulate access to resources in the cluster based on the roles of individual users or groups of users. Here's a step-by-step guide to setting up user permissions:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Understand Kubernetes Authentication and Authorization
&lt;/h3&gt;

&lt;p&gt;Firstly, understand that Kubernetes separates authentication (verifying who you are) from authorization (determining what you can do):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Authentication&lt;/strong&gt;: You can authenticate users via certificates, tokens, basic auth, external identity providers like LDAP, etc.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Authorization&lt;/strong&gt;: Once authenticated, authorization determines what actions the user can perform. Kubernetes uses RBAC for authorization.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. Define Users and Groups
&lt;/h3&gt;

&lt;p&gt;In Kubernetes, users are not created through the Kubernetes API but should be managed externally. For instance, you can have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Service Accounts&lt;/strong&gt; for processes in pods.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Normal Users&lt;/strong&gt; managed externally (e.g., via an identity provider).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Groups&lt;/strong&gt;, which are collections of users.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. Use Role and ClusterRole
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Role&lt;/strong&gt;: A &lt;code&gt;Role&lt;/code&gt; in Kubernetes is used to grant permissions within a specific namespace. It contains rules that represent a set of permissions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ClusterRole&lt;/strong&gt;: A &lt;code&gt;ClusterRole&lt;/code&gt; is like a Role, but for the entire cluster. It's useful for granting permissions for non-namespaced resources (like nodes) or for namespaced resources across all namespaces.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4. Create RoleBindings and ClusterRoleBindings
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;RoleBinding&lt;/strong&gt;: A &lt;code&gt;RoleBinding&lt;/code&gt; grants the permissions defined in a Role to a user or set of users. It applies only within a specific namespace.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ClusterRoleBinding&lt;/strong&gt;: A &lt;code&gt;ClusterRoleBinding&lt;/code&gt; grants the permissions defined in a ClusterRole to a user or set of users cluster-wide.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  5. Define Roles and Bindings
&lt;/h3&gt;

&lt;p&gt;Create YAML files to define &lt;code&gt;Roles&lt;/code&gt;/&lt;code&gt;ClusterRoles&lt;/code&gt; and &lt;code&gt;RoleBindings&lt;/code&gt;/&lt;code&gt;ClusterRoleBindings&lt;/code&gt;. For example:&lt;/p&gt;

&lt;h4&gt;
  
  
  Role Example
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Role&lt;/span&gt;
&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;rbac.authorization.k8s.io/v1&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;default&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pod-reader&lt;/span&gt;
&lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;apiGroups&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pods"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="na"&gt;verbs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;get"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;watch"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;list"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  RoleBinding Example
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;RoleBinding&lt;/span&gt;
&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;rbac.authorization.k8s.io/v1&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;read-pods&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;default&lt;/span&gt;
&lt;span class="na"&gt;subjects&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;User&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;jane&lt;/span&gt;
  &lt;span class="na"&gt;apiGroup&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;rbac.authorization.k8s.io&lt;/span&gt;
&lt;span class="na"&gt;roleRef&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Role&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pod-reader&lt;/span&gt;
  &lt;span class="na"&gt;apiGroup&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;rbac.authorization.k8s.io&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  6. Apply the Configuration
&lt;/h3&gt;

&lt;p&gt;Apply these configurations using &lt;code&gt;kubectl apply -f &amp;lt;filename&amp;gt;.yaml&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  7. Test the Permissions
&lt;/h3&gt;

&lt;p&gt;Verify that the permissions are correctly set up by attempting to perform operations in the cluster as the specified users.&lt;/p&gt;

&lt;h3&gt;
  
  
  8. Regular Audits and Updates
&lt;/h3&gt;

&lt;p&gt;Regularly audit and update the permissions to ensure they align with current operational requirements and security best practices.&lt;/p&gt;

&lt;h3&gt;
  
  
  9. Consider Using a Management Tool
&lt;/h3&gt;

&lt;p&gt;For complex environments, consider using a Kubernetes management tool that offers a more user-friendly interface for managing roles and permissions.&lt;/p&gt;

&lt;p&gt;Remember, proper setup of user permissions is crucial for the security of your Kubernetes cluster. Always follow the principle of least privilege, granting users only the permissions they need to perform their tasks.&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>cloudnative</category>
      <category>security</category>
    </item>
    <item>
      <title>What is Rego and how do you use it?</title>
      <dc:creator>Michael Chenetz</dc:creator>
      <pubDate>Thu, 30 Nov 2023 15:20:39 +0000</pubDate>
      <link>https://forem.com/mchenetz/what-is-rego-and-how-do-you-use-it-fbg</link>
      <guid>https://forem.com/mchenetz/what-is-rego-and-how-do-you-use-it-fbg</guid>
      <description>&lt;p&gt;The Rego language, used primarily with the Open Policy Agent (OPA), is a high-level declarative language for writing policy as code. Here's a basic illustration of how to use Rego:&lt;/p&gt;

&lt;h3&gt;
  
  
  Example Scenario: User Access Control
&lt;/h3&gt;

&lt;p&gt;Suppose we have a system where we need to control user access based on their roles.&lt;/p&gt;

&lt;h4&gt;
  
  
  Data Model
&lt;/h4&gt;

&lt;p&gt;First, define a simple data model. In a real-world scenario, this could be JSON data representing user roles and permissions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"users"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"alice"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"admin"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"bob"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"developer"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"eve"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"intern"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Policy Definition
&lt;/h4&gt;

&lt;p&gt;Next, write a Rego policy to specify who can access what. For instance, we might want only admins to access sensitive data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package example

default allow = false

allow {
  input.user.role == "admin"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this policy:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;package&lt;/code&gt; keyword defines a namespace (&lt;code&gt;example&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;default allow = false&lt;/code&gt; sets the default decision to deny access.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;allow&lt;/code&gt; rule permits access if the user's role is "admin".&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Query
&lt;/h4&gt;

&lt;p&gt;You'd then query this policy with input data to make access decisions. The input might look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"user"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"admin"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You'd ask OPA a question like: "Given this input, should access be allowed?" If the input user role is "admin", the policy allows access, returning &lt;code&gt;true&lt;/code&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Use in Code
&lt;/h4&gt;

&lt;p&gt;In application code, you'd typically integrate OPA as a service or library. The application sends input data (e.g., user information) to OPA and gets back a decision based on your Rego policies.&lt;/p&gt;

&lt;p&gt;This example is simplistic but illustrates the basic use of Rego. Real-world scenarios often involve more complex policies, multiple data sources, and integration with services like Kubernetes for dynamic policy enforcement.&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>cloudnative</category>
      <category>security</category>
    </item>
    <item>
      <title>Is your Cloud Native Application Secure?</title>
      <dc:creator>Michael Chenetz</dc:creator>
      <pubDate>Fri, 17 Feb 2023 17:01:09 +0000</pubDate>
      <link>https://forem.com/ciscoemerge/is-your-cloud-native-application-secure-1njg</link>
      <guid>https://forem.com/ciscoemerge/is-your-cloud-native-application-secure-1njg</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feot6rrnvpvk87l4miqwz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feot6rrnvpvk87l4miqwz.png" alt=" " width="795" height="447"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Description
&lt;/h2&gt;

&lt;p&gt;The first thing we do when we learn a new technology is to get things up and running. There are so many things to learn and it's hard enough for us to just wrap our heads around the concepts. Add in all the complexity of cloud native and we can quickly see why there are about one million compromised Kubernetes clusters in the wild.&lt;/p&gt;

&lt;p&gt;One of the biggest problems in this area is lack of education. It is assumed that Kubernetes is secure out of the box and this is just not true. This article will hopefully, help in highlighting some of the security vulnerabilities inherent in Kubernetes and how to remediate them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Insecure by Default
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Typical insecure default configurations of managed Kubernetes clusters include:&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Using default account credentials to access the cluster API&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Containers running unmasked, granting them complete access to a node’s procedures (/proc). This can allow malicious access to kernel parameters at runtime, or retrieval of sensitive information stored on the host.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Insecure volume configuration leading to leakage of information about other containers in the cluster&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Below illustrates some of the components available to help you secure your cloud native app.&lt;/p&gt;

&lt;h2&gt;
  
  
  Security Context
&lt;/h2&gt;

&lt;p&gt;The following is taken from the Kubernetes Docs&lt;br&gt;
&lt;a href="https://Kubernetes.io/docs/tasks/configure-pod-container/security-context/" rel="noopener noreferrer"&gt;https://Kubernetes.io/docs/tasks/configure-pod-container/security-context/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Configure a Security Context for a Pod or Container&lt;br&gt;
A security context defines privilege and access control settings for a Pod or Container. Security context settings include, but are not limited to:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Discretionary Access Control:&lt;/strong&gt;  Permission to access an object, like a file, is based on user ID (UID) and group ID (GID).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Security Enhanced Linux (SELinux):&lt;/strong&gt; Objects are assigned security labels.&lt;/p&gt;

&lt;p&gt;Running as privileged or unprivileged.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Linux Capabilities:&lt;/strong&gt; Give a process some privileges, but not all the privileges of the root user.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AppArmor:&lt;/strong&gt; Use program profiles to restrict the capabilities of individual programs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Seccomp:&lt;/strong&gt; Filter a process's system calls.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;allowPrivilegeEscalation:&lt;/strong&gt; Controls whether a process can gain more privileges than its parent process. This bool directly controls whether the no_new_privs flag gets set on the container process. allowPrivilegeEscalation is always true when the container:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;is run as privileged, or has CAP_SYS_ADMIN &lt;/li&gt;
&lt;li&gt;readOnlyRootFilesystem: Mounts the container's root filesystem as read-only.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The above bullets are not a complete set of security context settings -- please see SecurityContext for a comprehensive list.&lt;/p&gt;
&lt;h2&gt;
  
  
  In practice
&lt;/h2&gt;

&lt;p&gt;(taken from Kubernetes docs - &lt;a href="https://Kubernetes.io/docs/tasks/configure-pod-container/security-context/" rel="noopener noreferrer"&gt;https://Kubernetes.io/docs/tasks/configure-pod-container/security-context/&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;Below is an example of what a security context policy looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;apiVersion: v1

kind: Pod

metadata:
  name: security-context-demo
spec:
  securityContext:
    runAsUser: 1000
    runAsGroup: 3000
    fsGroup: 2000
  volumes:
  - name: sec-ctx-vol
    emptyDir: {}
  containers:
  - name: sec-ctx-demo
    image: busybox:1.28
    command: [ "sh", "-c", "sleep 1h" ]
    volumeMounts:
    - name: sec-ctx-vol
      mountPath: /data/demo
    securityContext:
      allowPrivilegeEscalation: false
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the configuration file, the runAsUser field specifies that for any containers in the Pod, all processes run with user ID 1000. The runAsGroup field specifies the primary group ID of 3000 for all processes within any containers of the Pod. If this field is omitted, the primary group ID of the containers will be root(0). Any files created will also be owned by user 1000 and group 3000 when runAsGroup is specified. Since fsGroup field is specified, all processes of the container are also part of the supplementary group ID 2000. The owner for volume /data/demo and any files created in that volume will be Group ID 2000.&lt;/p&gt;

&lt;h2&gt;
  
  
  Managing Security Context in bulk
&lt;/h2&gt;

&lt;p&gt;Creating security context and applying it to a single cluster is relatively easy. What happens when we need to apply it on multiple clusters over multiple pods/containers? It just so happens that there are many tools that make this process a lot easier. Below is a solution we have that allows you to set policy and apply it to multiple Kubernetes clusters, pods and containers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Handle Security by Policy
&lt;/h2&gt;

&lt;p&gt;The first thing you will notice is that you can see the policy of your pods/containers by just clicking on one.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm671yddtykr9po2q58bt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm671yddtykr9po2q58bt.png" alt="Panoptica Security Context" width="800" height="423"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The second screen illustrates baseline policy that can be deployed based on standard guidelines. You can also duplicate the policy and/or create your own here.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqxi0cvuw9n4fo767egkp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqxi0cvuw9n4fo767egkp.png" alt="Panoptica Base Policy" width="800" height="262"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The real power comes in the form of a policy advisor, illustrated below in which the application analyzes the pods/containers and provides recommendations on the correct policy.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F679yhj7i443ta64cy7c0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F679yhj7i443ta64cy7c0.png" alt="Panoptica Policy Advisor" width="480" height="709"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Lastly, the solution has a central policy that allows you to define the standard policy that includes elements around security context API security and vulnerabilities. We will discuss vulnerabilities and API security in our upcoming articles.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkpc9z96glbg1e1yyaubj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkpc9z96glbg1e1yyaubj.png" alt="Panoptica Deploy Rule" width="800" height="800"&gt;&lt;/a&gt;&lt;br&gt;
&lt;br&gt;
&lt;/p&gt;




&lt;p&gt;If you are interested in securing your cloud native workloads then try out Panoptica at &lt;a href="https://panoptica.app" rel="noopener noreferrer"&gt;Panoptica.app&lt;/a&gt;. There is a free tier that is always free!&lt;/p&gt;






&lt;p&gt;Additionally, check out our Open Source in this area:&lt;/p&gt;

&lt;p&gt;OpenClarity (&lt;a href="https://openclarity.io/" rel="noopener noreferrer"&gt;https://openclarity.io/&lt;/a&gt;) - the first open-source solution that forms a  cloud native application protection platform. Security needs to shift left into your development pipeline. OpenClarity allows you to understand and manage vulnerabilities and security configuration of Kubernetes, Serverless and APIs.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;KubeClarity&lt;/strong&gt; – Understand your software bill of materials (SBOM) and the vulnerabilities within. Scan and understand results from different CI stages and detect vulnerabilities in different stages of deployment. Group scanned resources (images/directories) under defined applications to navigate the object tree dependencies (applications, resources, packages, vulnerabilities)&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;APIClarity&lt;/strong&gt; – Analyzes the API endpoint to illustrate intended usage, deprecated API calls, and undocumented features. Additionally, executes trace analyzers fuzzers and BFLA detection to detect potential security threats such as Broken Object Level Authorizations (BOLA), Broken Function Level Authorization(BFLA), and other security issues.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;FunctionClarity&lt;/strong&gt; - a code integrity solution for serverless functions. It allows users to sign their serverless functions and verify their integrity prior to their execution in their cloud environments. FunctionClarity includes a CLI tool, complemented by a "verification" function deployed in the target cloud account. The solution is designed for CI/CD insertion, where the serverless function code/images can be signed and uploaded before the function is created in the cloud repository.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>kubernetes</category>
      <category>containers</category>
      <category>shiftleft</category>
      <category>security</category>
    </item>
    <item>
      <title>How did we get from legacy to microservices?</title>
      <dc:creator>Michael Chenetz</dc:creator>
      <pubDate>Fri, 20 Jan 2023 20:22:31 +0000</pubDate>
      <link>https://forem.com/ciscoemerge/how-did-we-get-from-legacy-to-microservices-4eml</link>
      <guid>https://forem.com/ciscoemerge/how-did-we-get-from-legacy-to-microservices-4eml</guid>
      <description>&lt;p&gt;Ever wonder why a microservices based architecture came to exist? I often find it useful to understand the evolution of how technology came to be. In doing so, there is great understanding.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffptm1q21amevuqt9u6xs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffptm1q21amevuqt9u6xs.png" alt="Legacy to Microservices" width="568" height="288"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If we go back to the before times then we will find that application architecture was relatively simple. Most applications used a 3 tier design that would consist of a client, server and database. You had the client that the user used to connect to the server and display information. You had the database that contained all of the information that needed to be stored. Finally, you had the server that contained the logic for both backend and frontend calls. &lt;/p&gt;

&lt;p&gt;We created multiple environments in order to have a level of redundancy and keep some semblance of uptime. A load-balancer would front-end these servers and send traffic to the different environments. &lt;/p&gt;

&lt;p&gt;This was an okay solution when applications were updated quarterly and there was a lot of time to plan and test. Times have changed and needs have shifted. We now live in world ruled by SaaS (Software As A Service). The rules of engagement have changed. Consumers of applications need the latest features ASAP in order to push out their new widget and track that widget and ascertain the full lifecycle of that widget. I digress. But, You get the point.&lt;/p&gt;

&lt;p&gt;Application design and architecture has changed to. At one time all calls were internal to the server and very little communicated to outside services. Now we live in a API centric world in which we consume most services through them. Smart developers are also looking to get the job done the easiest and quickest so APIs allow that to happen.&lt;/p&gt;

&lt;p&gt;Developers are now having an easier time implementing APIs because of standards like OpenAPI spec. Developers started to think about loosely coupling their apps and deconstructing them like a trendy entree. What used to be libraries that were called from a single monolithic app are now designed to be micro-services and are split out by function. This allows developers to work on a single component of an application and  they can update that piece, by itself, without effecting other components. &lt;/p&gt;

&lt;p&gt;We can now decide where services should run and how they should communicate now that the services are split out by function. Additionally services can be scaled out so that they can handle additional load and traffic. We can now introduce services slowly and quickly phase them out if they are functioning improperly. &lt;/p&gt;

&lt;p&gt;Hopefully this article highlights some of the core aspects of the shifts that occurred that led to micro-services based architecture.&lt;/p&gt;

&lt;p&gt;Now that you a little bit of the history here is an article with some great information on how to determine the resource limits for a new microservice.&lt;br&gt;
&lt;a href="https://dev.to/ciscoemerge/determine-resource-limits-for-a-new-microservice-471"&gt;https://dev.to/ciscoemerge/determine-resource-limits-for-a-new-microservice-471&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Check out some of our open-source solutions:&lt;br&gt;
&lt;a href="https://eti.cisco.com/open-source" rel="noopener noreferrer"&gt;https://eti.cisco.com/open-source&lt;/a&gt;&lt;/p&gt;

</description>
      <category>react</category>
      <category>typescript</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Gitea, DroneCI and Portainer</title>
      <dc:creator>Michael Chenetz</dc:creator>
      <pubDate>Fri, 30 Sep 2022 21:05:57 +0000</pubDate>
      <link>https://forem.com/mchenetz/gitea-droneci-and-portainer-3ig0</link>
      <guid>https://forem.com/mchenetz/gitea-droneci-and-portainer-3ig0</guid>
      <description>&lt;h1&gt;
  
  
  Intro
&lt;/h1&gt;

&lt;p&gt;You are tasked to create a build environment and you want something relatively easy and something that is repeatable.&lt;/p&gt;

&lt;p&gt;On my journey to accomplish the above task i came across many great solutions. There are all in one solutions like Gitlab and Github but i wanted a solution that was open-source, free and easy. &lt;/p&gt;

&lt;h2&gt;
  
  
  Gitea
&lt;/h2&gt;

&lt;p&gt;&lt;a href="//gitea.io"&gt;Gitea&lt;/a&gt;&lt;br&gt;
Gitea is a open-source version of something like Github that is easy to deploy and makes a great on-prem repository. An added advantage is it has out of the box integration with Drone that is configurable to trigger web-hooks on selected events.&lt;/p&gt;
&lt;h2&gt;
  
  
  Drone
&lt;/h2&gt;

&lt;p&gt;&lt;a href="//drone.io"&gt;Drone&lt;/a&gt;&lt;br&gt;
Drone is a open-source CI tool that will take simple commands in the form of yaml and execute on them as a pipeline. They have a concept of runners that do all the work. You install the runners you need on Docker, or Kubernetes or even on a host and they will listen for jobs.&lt;/p&gt;
&lt;h3&gt;
  
  
  Runners:
&lt;/h3&gt;

&lt;p&gt;Docker&lt;br&gt;
Kubernetes&lt;br&gt;
Exec&lt;br&gt;
SSH&lt;br&gt;
VM&lt;/p&gt;
&lt;h2&gt;
  
  
  Portainer
&lt;/h2&gt;

&lt;p&gt;&lt;a href="//portainer.io"&gt;Portainer&lt;/a&gt;&lt;br&gt;
Portainer is a container management platform that allows you to manage containers across clouds and allows you to manage multiple container platforms (Kubernetes, Docker, Docker Swarm, and Nomad)&lt;/p&gt;
&lt;h1&gt;
  
  
  Solution:
&lt;/h1&gt;

&lt;p&gt;I am using VSCode as my IDE in which i have created a little python webserver that will be encapsulated into a docker image. The Docker image will then be created, sent to a repo and then consumed by a Kubernetes deployment. The deployment will be triggered and deployed.&lt;/p&gt;

&lt;p&gt;Here is what it will look like:&lt;/p&gt;

&lt;p&gt;[VSCode] -&amp;gt; [Gitea] -&amp;gt; [Drone] -&amp;gt; [Portainer] -&amp;gt; [Deploy]&lt;/p&gt;
&lt;h2&gt;
  
  
  Gitea Install:
&lt;/h2&gt;

&lt;p&gt;Kubernetes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;helm install gitea gitea-charts/gitea -n gitea --values ./gitea_values.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;gitea-values.yml&lt;/em&gt; (Changes)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ingress:
  enabled: true
  # className: nginx
  className:
  annotations: {}
    # kubernetes.io/ingress.class: nginx
    # kubernetes.io/tls-acme: "true"
  hosts:
    - host: git.chenetz.net
      paths:
        - path: /
          pathType: Prefix
  tls:
    - secretName: git-chenetz-tls
      hosts:
        - git.chenetz.net
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Drone Install:
&lt;/h2&gt;

&lt;p&gt;Kubernetes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;helm upgrade my-drone drone/drone -n drone --values ./drone-values.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;drone-values.yaml&lt;/em&gt; (changes)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ingress:
  enabled: true
  annotations: {}
    # kubernetes.io/ingress.class: nginx
    # kubernetes.io/tls-acme: "true"
  hosts:
    - host: drone.chenetz.net
      paths:
        - path: /
          pathType: Prefix
  tls: 
   - secretName: chenetz-drone-tls
     hosts:
       - drone.chenetz.net
nv:
  DRONE_WEBHOOK_SKIP_VERIFY: true
  ## REQUIRED: Set the user-visible Drone hostname, sans protocol.
  ## Ref: https://docs.drone.io/installation/reference/drone-server-host/
  ##
  DRONE_SERVER_HOST: drone.chenetz.net
  ## The protocol to pair with the value in DRONE_SERVER_HOST (http or https).
  ## Ref: https://docs.drone.io/installation/reference/drone-server-proto/
  ##
  DRONE_SERVER_PROTO: https

  ## REQUIRED: Set the secret secret token that the Drone server and its Runners will use
  ## to authenticate. This is commented out in order to leave you the ability to set the
  ## key via a separately provisioned secret (see existingSecretName above).
  ## Ref: https://docs.drone.io/installation/reference/drone-rpc-secret/
  ##
  DRONE_RPC_SECRET: drone-runner

  ## If you'd like to use a DB other than SQLite (the default), set a driver + DSN here.
  ## Ref: https://docs.drone.io/installation/storage/database/
  ##
  # DRONE_DATABASE_DRIVER:
  # DRONE_DATABASE_DATASOURCE:

  ## If you are going to store build secrets in the Drone database, it is suggested that
  ## you set a database encryption secret. This must be set before any secrets are stored
  ## in the database.
  ## Ref: https://docs.drone.io/installation/storage/encryption/
  ##
  # DRONE_DATABASE_SECRET:

  ## If you are using self-hosted GitHub or GitLab, you'll need to set this to true.
  ## Ref: https://docs.drone.io/installation/reference/drone-git-always-auth/
  ##
  # DRONE_GIT_ALWAYS_AUTH: false

  ## ===================================================================================
  ##                         Provider Directives (select ONE)
  ## -----------------------------------------------------------------------------------
  ## Select one provider (and only one). Refer to the corresponding documentation link
  ## before filling the values in. Also note that you can use the 'secretMounts' value
  ## if you'd rather not have secrets in Kubernetes Secret instead of a ConfigMap.
  ## ===================================================================================

  ## GitHub-specific variables. See the provider docs here:
  ## Ref: https://docs.drone.io/installation/providers/github/
  ##
  # DRONE_GITHUB_CLIENT_ID:
  # DRONE_GITHUB_CLIENT_SECRET:

  ## GitLab-specific variables. See the provider docs here:
  ## Ref: https://docs.drone.io/installation/providers/gitlab/
  ##
  # DRONE_GITLAB_CLIENT_ID:
  # DRONE_GITLAB_CLIENT_SECRET:
  # DRONE_GITLAB_SERVER:

  ## Bitbucket Cloud-specific variables. See the provider docs here:
  ## Ref: https://docs.drone.io/installation/providers/bitbucket-cloud/
  ##
  # DRONE_BITBUCKET_CLIENT_ID:
  # DRONE_BITBUCKET_CLIENT_SECRET:

  ## Bitbucket-specific variables. See the provider docs here:
  ## Ref: https://docs.drone.io/installation/providers/bitbucket-server/
  ##
  # DRONE_GIT_USERNAME:
  # DRONE_GIT_PASSWORD:
  # DRONE_STASH_CONSUMER_KEY:
  # DRONE_STASH_PRIVATE_KEY:
  # DRONE_STASH_SERVER:

  ## Gitea-specific variables. See the provider docs here:
  ## Ref: https://docs.drone.io/installation/providers/gitea/
  ##
  DRONE_GITEA_CLIENT_ID: [client id]
  DRONE_GITEA_CLIENT_SECRET: [secret]
  DRONE_GITEA_SERVER: https://git.chenetz.net
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Drone Docker Runner Install:
&lt;/h2&gt;

&lt;p&gt;Kubernetes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;helm install my-drone-runner-docker drone/drone-runner-docker -n drone --values ./drone-runner-docker.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;drone-runner-docker.yml&lt;/em&gt; (changes)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;env:
  ## The docker host to be used by the drone-runner-docker when connecting
  ## to the docker daemon, defaults to the dind(sidecar) docker daemon
  DOCKER_HOST: "tcp://localhost:2375"
  ## REQUIRED: Set the secret secret token that the Docker runner will use
  ## to authenticate. This is commented out in order to leave you the ability to set the
  ## key via a separately provisioned secret (see extraSecretNamesForEnvFrom above).
  ## Ref: https://docs.drone.io/runner/docker/configuration/reference/drone-rpc-secret/
  ##
  DRONE_RPC_SECRET: drone-runner

  ## The hostname/IP (and optionally the port) for your Kubernetes runner. Defaults to the "drone"
  ## service that the drone server Chart creates by default.
  ## Ref: https://docs.drone.io/runner/docker/configuration/reference/drone-rpc-host/
  ##
  DRONE_RPC_HOST: drone.chenetz.net

  ## The protocol to use for communication with Drone server.
  ## Ref: https://docs.drone.io/runner/docker/configuration/reference/drone-rpc-proto/
  ##
  DRONE_RPC_PROTO: https
  DRONE_NAMESPACE_DEFAULT: drone
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Configuration:
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Gitea:
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0ykqk0htkt721cu17ley.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0ykqk0htkt721cu17ley.png" alt="Gitea" width="800" height="397"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxkt0umhxv0wnxgbavw3r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxkt0umhxv0wnxgbavw3r.png" alt="Gitea 2" width="800" height="472"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Drone
&lt;/h3&gt;

&lt;p&gt;After getting everything up and communicating, i added the .values.yml file to my repo. The following drone pipeline file creates the docker image after it gets a trigger from either a push or pull in Gitea. The second step triggers a webhook that is configured in Portainer to trigger a deployment of the manifest.yml in the repo.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kind: pipeline
name: default
type: docker 
steps:
- name: docker  
  image: plugins/docker
  settings:
    username:
      from_secret: docker-username
    password:
      from_secret: docker-password
    repo: mchenetz/test
    tags:
    - latest
- name: send
  image: plugins/webhook
  settings:
    urls: https://192.168.10.12:9443/api/stacks/webhooks/
    content_type: application/json
    template: |
      {
        "owner": "{{ repo.owner }}",
        "repo": "{{ repo.name }}",
        "status": "{{ build.status }}",
      }
    skip_verify: true
    debug: true
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Dockerfile
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FROM python:latest
WORKDIR /src

COPY . .

CMD [ "python", "./test.py"]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Source (test.py)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from http.server import BaseHTTPRequestHandler, HTTPServer
import time

hostName = "localhost"
serverPort = 80

class MyServer(BaseHTTPRequestHandler):
    def do_GET(self):
        self.send_response(200)
        self.send_header("Content-type", "text/html")
        self.end_headers()
        self.wfile.write(bytes("&amp;lt;html&amp;gt;&amp;lt;head&amp;gt;&amp;lt;title&amp;gt;https://portainer.io&amp;lt;/title&amp;gt;&amp;lt;/head&amp;gt;", "utf-8"))
        self.wfile.write(bytes("&amp;lt;p&amp;gt;Request: %s&amp;lt;/p&amp;gt;" % self.path, "utf-8"))
        self.wfile.write(bytes("&amp;lt;body&amp;gt;", "utf-8"))
        self.wfile.write(bytes("&amp;lt;p&amp;gt;This is an example web server on k8s.&amp;lt;/p&amp;gt;", "utf-8"))
        self.wfile.write(bytes("&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;", "utf-8"))

if __name__ == "__main__":        
    webServer = HTTPServer((hostName, serverPort), MyServer)
    print("Server started http://%s:%s" % (hostName, serverPort))

    try:
        webServer.serve_forever()
    except KeyboardInterrupt:
        pass

    webServer.server_close()
    print("Server stopped.")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example of automated trigger on push:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv0kxq6tdicpr95ph3m42.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv0kxq6tdicpr95ph3m42.png" alt="Drone" width="800" height="340"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Docker Repo:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9wti5r5hx2xk6mziadje.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9wti5r5hx2xk6mziadje.png" alt="Docker" width="800" height="498"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Portainer Trigger:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqj1v81kjp20elmbw7yd8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqj1v81kjp20elmbw7yd8.png" alt="Portainer Trigger" width="800" height="275"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion:
&lt;/h2&gt;

&lt;p&gt;At the end of the day... It may sound like a few steps to setup but i now have a complete environment for my coding pipeline and any changes i make will automatically build the image and deploy. That may not always be what you want and the pipeline can be modified to insert stops for approval and to change to pull which is more common. &lt;/p&gt;

</description>
      <category>git</category>
      <category>devops</category>
      <category>cicd</category>
      <category>python</category>
    </item>
    <item>
      <title>Managing multiple Kube clusters (the easy way)</title>
      <dc:creator>Michael Chenetz</dc:creator>
      <pubDate>Fri, 16 Sep 2022 20:00:34 +0000</pubDate>
      <link>https://forem.com/mchenetz/managing-multiple-kube-clusters-the-easy-way-4ao</link>
      <guid>https://forem.com/mchenetz/managing-multiple-kube-clusters-the-easy-way-4ao</guid>
      <description>&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/5WcchF0K3qE"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;If you have ever tried to manage users on multiple Kubernetes clusters then you know the pain i am about to talk about.&lt;/p&gt;

&lt;p&gt;The first thing you need to do after you bring up a cluster is define some privileges. You accomplish that by setting up roles and cluster roles.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  annotations:
    rbac.authorization.kubernetes.io/autoupdate: "true"
  creationTimestamp: "2022-07-20T23:53:17Z"
  labels:
    kubernetes.io/bootstrapping: rbac-defaults
  name: cluster-admin
  resourceVersion: "76"
  uid: 15d14062-0879-4232-9f42-51b79c0835f9
rules:
- apiGroups:
  - '*'
  resources:
  - '*'
  verbs:
  - '*'
- nonResourceURLs:
  - '*'
  verbs:
  - '*'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After you have defined the rules in a role (outside of the scope of this article) then you need to create a clusterrolebinding or rolebinding that associated a particular user with a role or clusterrole. If this sounds overly complicated it's because it probably is.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  annotations:
    rbac.authorization.kubernetes.io/autoupdate: "true"
  creationTimestamp: "2022-07-20T23:53:17Z"
  labels:
    kubernetes.io/bootstrapping: rbac-defaults
  name: cluster-admin
  resourceVersion: "138"
  uid: 5ef726c1-01df-4ecd-8951-909698ef5472
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: cluster-admin
subjects:
- apiGroup: rbac.authorization.k8s.io
  kind: Group
  name: system:masters
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Oh yeah, and if forgot... You need to do this for every cluster you bring up.&lt;/p&gt;

&lt;p&gt;I am here to tell you there is an easier way!&lt;/p&gt;

&lt;h3&gt;
  
  
  Easy Button
&lt;/h3&gt;

&lt;p&gt;Using Portainer BE you can setup your users in the GUI.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftt6nrtcgd56y9mnx63el.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftt6nrtcgd56y9mnx63el.png" alt=" " width="800" height="247"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can then assign the users to groups, called teams.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffegjt1qlzt7oz2cpyr1k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffegjt1qlzt7oz2cpyr1k.png" alt=" " width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then you can associate the roles to groups per environment&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgzxvtb4fjzkfi33p89hh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgzxvtb4fjzkfi33p89hh.png" alt=" " width="800" height="223"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fasi2o2xvx33kencxpy0w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fasi2o2xvx33kencxpy0w.png" alt=" " width="800" height="438"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can now assign these same teams and roles to each envirnment. Kubernetes gets the user association in the backgrounds and the cluster roles are pushed down to each cluster. Now the only thing to do is to grab a kubeconfig file that is associated with that user.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs4zksi4eh3pnwgu8d4wg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs4zksi4eh3pnwgu8d4wg.png" alt=" " width="800" height="365"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fon5ttu0ay2na4iliqsy8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fon5ttu0ay2na4iliqsy8.png" alt=" " width="800" height="668"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;All the clusters that user is associated with will be part of the kubeconfig and the kubeconfig points to a proxy that manages all of the calls. If a user is taken out of a team in portainer then those changes are immediate across clusters.&lt;/p&gt;

&lt;p&gt;You can se how much easier it is to manage multiple cluster access this way. For more of a demo then checkout the video attached to this post.&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>devops</category>
    </item>
    <item>
      <title>Can DevOps and ITOps Coexist?</title>
      <dc:creator>Michael Chenetz</dc:creator>
      <pubDate>Tue, 16 Aug 2022 20:57:13 +0000</pubDate>
      <link>https://forem.com/mchenetz/can-devops-and-itops-coexist-bi9</link>
      <guid>https://forem.com/mchenetz/can-devops-and-itops-coexist-bi9</guid>
      <description>&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/gS67LgBNItY"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;It's the age old conflict between ITOps and DevOps. IT works bottom up and handles all of the infra related tasks from the network to the VM. DevOps works top down and looks at things from the application down to the container. Somewhere in the middle they each have to co-exist.&lt;/p&gt;

&lt;p&gt;Really, there should be no conflict. What we think of as conflict is just consuming things in a way that makes sense for the associated audience. If we say that something is very IT centric then what we are really saying is that we are looking for some type of management interface that gives us nice dashboards and allows us to manage a bunch of resources at a time. If we say something is DevOps centric then what we are saying is that we are looking for a high level of automation. Usually that automation exhibits in the form of CLI commands and APIs.&lt;/p&gt;

&lt;p&gt;The question remains on whether there can be a single solution to tie things together. The answer is that there can be. The best tools allow people to consume their infra and apps in a way they are accustomed to. At the end of the day, there are a few elements that are necessary for both ITOps and DevOps. Some of these elements are RBAC (Role Based Access Control) and automation.&lt;/p&gt;

&lt;p&gt;In this episode of Portainerd, I go through an example on how the two different teams can work together and how RBAC enables it.&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>docker</category>
      <category>devops</category>
      <category>itops</category>
    </item>
    <item>
      <title>Up and running with K0s and Portainer</title>
      <dc:creator>Michael Chenetz</dc:creator>
      <pubDate>Wed, 10 Aug 2022 15:26:00 +0000</pubDate>
      <link>https://forem.com/mchenetz/up-and-running-with-k0s-and-portainer-3n35</link>
      <guid>https://forem.com/mchenetz/up-and-running-with-k0s-and-portainer-3n35</guid>
      <description>&lt;h2&gt;
  
  
  Prerequisites:
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Physical server&lt;/li&gt;
&lt;li&gt;Ubuntu Linux x 4&lt;/li&gt;
&lt;li&gt;network&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Intro:
&lt;/h2&gt;

&lt;p&gt;In this tutorial I will explain how to get started with K0s and Portainer. K0s is a great orchestrator for installing Kubernetes and makes it so that Kubernetes can be installed the same way many times. Portainer is a platform that runs on and can interact with most container platforms and allows you to manage your apps and micro-services.&lt;/p&gt;

&lt;p&gt;In this tutorial we will be using Portainer BE which is free for up to 5 nodes. The BE edition has some great functionality around RBAC, Edge, and Cloud provisioning of Kubernetes clusters.&lt;/p&gt;

&lt;p&gt;This intro does not cover the installation of Ubuntu onto a server and assumes that has already been done.&lt;/p&gt;

&lt;h2&gt;
  
  
  Installing K0s:
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3xq2zi9w17x8qka6brag.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3xq2zi9w17x8qka6brag.png" alt="K0s Digram" width="542" height="225"&gt;&lt;/a&gt;&lt;br&gt;
The first thing that you need to know is that K0S is a distributed install. You use the k0sctl tool to connect over ssh to your hosts and it will provision everything for you. &lt;/p&gt;

&lt;p&gt;To start, install the k0sctl binary on the system you want to orchestrate from. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;on Mac&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;brew install k0sproject/tap/k0sctl
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;On Windows&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;choco install k0sctl
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;On Linux/Bash&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;k0sctl completion &amp;gt; /etc/bash_completion.d/k0sctl
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;On Linux/Zsh&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;k0sctl completion &amp;gt; /usr/local/share/zsh/site-functions/_k0sctl
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  generating and deploying an SSH key
&lt;/h2&gt;

&lt;p&gt;on the host that you are orchestrating from, do the following to create the key&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ssh-keygen -t ed25519
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Just hit enter until everything all prompts are gone and the key is created&lt;/p&gt;

&lt;p&gt;to deploy the key do the following. The user should be whatever user you created on the ubuntu host and the ip should be the ip of each ubuntu host:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ssh-copy-id -i ~/.ssh/id_ed25519 user@host
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Configuring K0s:
&lt;/h2&gt;

&lt;p&gt;K0s needs a yaml file that holds the configuration of your cluster. K0s will actually create this yaml file for you in order to start out with. To do this, run the following command in a new directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;k0sctl init &amp;gt; k0sctl.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once you have the file then edit it with the editor of your choice. I tend to choose vscode. Below is an example of a 4 node cluster with one controller and three workers. Each host has an ip address, a user name, the path of the ssh key(on the orchestrating machine) to authenticate to the host on connection, and the role of that host. The nice part about this is that once this is configured you can run it over and over.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;apiVersion: k0sctl.k0sproject.io/v1beta1
kind: Cluster
metadata:
  name: k0s-cluster
spec:
  hosts:
  - ssh:
      address: 192.168.10.1
      user: user
      port: 22
      keyPath: /Users/user/.ssh/id_ed25519
    role: controller
  - ssh:
      address: 192.168.10.2
      user: user
      port: 22
      keyPath: /Users/user/.ssh/id_ed25519
    role: worker
  - ssh:
      address: 192.168.10.3
      user: user
      port: 22
      keyPath: /Users/user/.ssh/id_ed25519
    role: worker
  - ssh:
      address: 192.168.10.4
      user: user
      port: 22
      keyPath: /Users/user/.ssh/id_ed25519
    role: worker
  k0s:
    version: 1.24.2+k0s.0
    dynamicConfig: false
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once you have everything that you need configured then run the following command to bring everything up:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mac/Linux&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;k0sctl apply --config ./k0sctl.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Windows&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;k0sctl apply --config k0sctl.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You will get a bunch of output from that command that looks like the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;⠀⣿⣿⡇⠀⠀⢀⣴⣾⣿⠟⠁⢸⣿⣿⣿⣿⣿⣿⣿⡿⠛⠁⠀⢸⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠀█████████ █████████ ███
⠀⣿⣿⡇⣠⣶⣿⡿⠋⠀⠀⠀⢸⣿⡇⠀⠀⠀⣠⠀⠀⢀⣠⡆⢸⣿⣿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀███          ███    ███
⠀⣿⣿⣿⣿⣟⠋⠀⠀⠀⠀⠀⢸⣿⡇⠀⢰⣾⣿⠀⠀⣿⣿⡇⢸⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠀███          ███    ███
⠀⣿⣿⡏⠻⣿⣷⣤⡀⠀⠀⠀⠸⠛⠁⠀⠸⠋⠁⠀⠀⣿⣿⡇⠈⠉⠉⠉⠉⠉⠉⠉⠉⢹⣿⣿⠀███          ███    ███
⠀⣿⣿⡇⠀⠀⠙⢿⣿⣦⣀⠀⠀⠀⣠⣶⣶⣶⣶⣶⣶⣿⣿⡇⢰⣶⣶⣶⣶⣶⣶⣶⣶⣾⣿⣿⠀█████████    ███    ██████████

INFO k0sctl 0.0.0 Copyright 2021, Mirantis Inc.
INFO Anonymized telemetry will be sent to Mirantis.
INFO By continuing to use k0sctl you agree to these terms:
INFO https://k0sproject.io/licenses/eula
INFO ==&amp;gt; Running phase: Connect to hosts
INFO [ssh] 10.0.0.1:22: connected
INFO [ssh] 10.0.0.2:22: connected
INFO ==&amp;gt; Running phase: Detect host operating systems
INFO [ssh] 10.0.0.1:22: is running Ubuntu 20.10
INFO [ssh] 10.0.0.2:22: is running Ubuntu 20.10
INFO ==&amp;gt; Running phase: Prepare hosts
INFO [ssh] 10.0.0.1:22: installing kubectl
INFO ==&amp;gt; Running phase: Gather host facts
INFO [ssh] 10.0.0.1:22: discovered 10.12.18.133 as private address
INFO ==&amp;gt; Running phase: Validate hosts
INFO ==&amp;gt; Running phase: Gather k0s facts
INFO ==&amp;gt; Running phase: Download K0s on the hosts
INFO [ssh] 10.0.0.2:22: downloading k0s 0.11.0
INFO [ssh] 10.0.0.1:22: downloading k0s 0.11.0
INFO ==&amp;gt; Running phase: Configure K0s
WARN [ssh] 10.0.0.1:22: generating default configuration
INFO [ssh] 10.0.0.1:22: validating configuration
INFO [ssh] 10.0.0.1:22: configuration was changed
INFO ==&amp;gt; Running phase: Initialize K0s Cluster
INFO [ssh] 10.0.0.1:22: installing k0s controller
INFO [ssh] 10.0.0.1:22: waiting for the k0s service to start
INFO [ssh] 10.0.0.1:22: waiting for kubernetes api to respond
INFO ==&amp;gt; Running phase: Install workers
INFO [ssh] 10.0.0.1:22: generating token
INFO [ssh] 10.0.0.2:22: writing join token
INFO [ssh] 10.0.0.2:22: installing k0s worker
INFO [ssh] 10.0.0.2:22: starting service
INFO [ssh] 10.0.0.2:22: waiting for node to become ready
INFO ==&amp;gt; Running phase: Disconnect from hosts
INFO ==&amp;gt; Finished in 2m2s
INFO k0s cluster version 0.11.0 is now installed
INFO Tip: To access the cluster you can now fetch the admin kubeconfig using:
INFO      k0sctl kubeconfig
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The final output lets you know to run the command k0sctl kubeconfig which will generate the kubeconfig file that is needed to run kubectl (Kubernetes) commands.&lt;/p&gt;

&lt;p&gt;To get the kubeconfig file run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;k0sctl kubeconfig &amp;gt; kubeconfig
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To test the cluster run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubectl get pods --kubeconfig kubeconfig -A
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;NAMESPACE     NAME                                       READY   STATUS    RESTARTS   AGE
kube-system   calico-kube-controllers-5f6546844f-w8x27   1/1     Running   0          3m50s
kube-system   calico-node-vd7lx                          1/1     Running   0          3m44s
kube-system   coredns-5c98d7d4d8-tmrwv                   1/1     Running   0          4m10s
kube-system   konnectivity-agent-d9xv2                   1/1     Running   0          3m31s
kube-system   kube-proxy-xp9r9                           1/1     Running   0          4m4s
kube-system   metrics-server-6fbcd86f7b-5frtn            1/1     Running   0          3m51s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;K0s Issue&lt;/strong&gt;:&lt;br&gt;
There is an issue in some releases of K0s where certain directories were not in the expected locations for Kubernetes. Before you bang your head on the wall here is a fix. SSH to your worker nodes and run the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo ln -s /var/lib/k0s/kubelet /var/lib/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gives you your base linux install but you still need a couple of other things. The first things is a load balancer. I typically use MetalLB. To install MetalLB run the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.10.2/manifests/namespace.yaml
kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.10.2/manifests/metallb.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To configure metalLB then edit the following with your network info. The network should correspond to free addresses on your subnet that you want to allocate when exposing containers externally through the load-balancer. Place the contents into your editor and save it as, "metallb.yaml"&lt;/p&gt;

&lt;p&gt;metallb.yaml&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;apiVersion: v1
kind: ConfigMap
metadata:
  namespace: metallb-system
  name: config
data:
  config: |
    address-pools:
    - name: default
      protocol: layer2
      addresses:
      - 192.168.10.240-192.168.10.250
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To apply the configuration run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubectl apply -f ./metallb.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The last thing i like to add is an ingress controller. I typically use Nginx for this. An ingress controller matches URL patterns to kubernetes services in order to save on using IP addresses for everything. You can create a single load balancer and then map DNS to that ip. The ingress will look at the referring DNS and match it. If there is a match it will follow the rules defined to connect the DNS match with a service.&lt;/p&gt;

&lt;p&gt;To add ingress do the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.1.3/deploy/static/provider/baremetal/deploy.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can have multiple ingress classes in your cluster. It's good to define a default. In order to do that run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubectl -n ingress-nginx annotate ingressclasses nginx ingressclass.kubernetes.io/is-default-class="true"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lastly you need to change the default behavior of Nginx that would expose a nodeport to change to a LoadBalancer.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubectl edit service ingress-nginx-controller -n ingress-nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Storage
&lt;/h2&gt;

&lt;p&gt;This is a little outside the scope of the install but I will add it here because it could be useful to some.&lt;/p&gt;

&lt;p&gt;A storage class is needed in Kubernetes in order to have data persistence. There are many ways to accomplish this. Some like to use a Kubernetes cluster as a storage class by using something like longhorn.io or Ceph. I have a NFS storage device that i setup on TrueNAS that i use for this purpose. The following instrutcions will setup a CSI (Container Storage Interface) with NFS:&lt;/p&gt;

&lt;p&gt;Install NFS on the cluster:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;helm repo add csi-driver-nfs https://raw.githubusercontent.com/kubernetes-csi/csi-driver-nfs/master/charts
helm install csi-driver-nfs csi-driver-nfs/csi-driver-nfs --namespace kube-system --version v4.1.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;apply storage class configuration. The only things that really need to be changed are the IP address of the NFS server and the share on that server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: nfs-csi
provisioner: nfs.csi.k8s.io
parameters:
  server: 192.168.10.10
  share: /mnt/default/code/
  # csi.storage.k8s.io/provisioner-secret is only needed for providing mountOptions in DeleteVolume
  # csi.storage.k8s.io/provisioner-secret-name: "mount-options"
  # csi.storage.k8s.io/provisioner-secret-namespace: "default"
reclaimPolicy: Delete
volumeBindingMode: Immediate
mountOptions:
  - nconnect=8  # only supported on linux kernel version &amp;gt;= 5.3
  - nfsvers=4.1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lastly, we want to make that storage class default for the cluster:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubectl patch storageclass nfs-csi -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Notes:
&lt;/h2&gt;

&lt;p&gt;One of the really nice features of K0s is automation. I haven't done it yet but you can automate the above extra steps by using the manifest deployer and helm deployer. Information is available below:&lt;/p&gt;

&lt;p&gt;Manifest Deployer:&lt;br&gt;
&lt;code&gt;https://docs.k0sproject.io/v1.24.3+k0s.0/manifests/&lt;br&gt;
&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Helm Deployer:&lt;br&gt;
&lt;code&gt;https://docs.k0sproject.io/v1.24.3+k0s.0/helm-charts/&lt;br&gt;
&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;
  
  
  Portainer Install:
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;Requirements&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Kubernetes cluster&lt;/li&gt;
&lt;li&gt;Data persistent storage&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Install:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;helm repo add portainer https://portainer.github.io/k8s/
helm repo update
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Installing via load-balancer is the easiest:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;helm install --create-namespace -n portainer portainer portainer/portainer \
    --set service.type=LoadBalancer \
    --set enterpriseEdition.enabled=true \
    --set tls.force=true
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you want to get a little more advanced and have setup ingress and DNS and know the ingress load-balancer ip then run the following. Realize that there are many pre-requisites to get this working. Here is some info on Ingress (&lt;a href="https://kubernetes.io/docs/concepts/services-networking/ingress/):" rel="noopener noreferrer"&gt;https://kubernetes.io/docs/concepts/services-networking/ingress/):&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;helm install --create-namespace -n portainer portainer portainer/portainer \
    --set enterpriseEdition.enabled=true \
    --set service.type=ClusterIP \
    --set tls.force=true \
    --set ingress.enabled=true \
    --set ingress.ingressClassName=&amp;lt;ingressClassName (eg: nginx)&amp;gt; \
    --set ingress.annotations."nginx\.ingress\.kubernetes\.io/backend-protocol"=HTTPS \
    --set ingress.hosts[0].host=&amp;lt;fqdn (eg: portainer.example.io)&amp;gt; \
    --set ingress.hosts[0].paths[0].path="/"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Logging in:
&lt;/h2&gt;

&lt;p&gt;Load Balancer installed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://&amp;lt;loadbalancer IP&amp;gt;:9443/ or http://&amp;lt;loadbalancer IP&amp;gt;:9000/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ingress installed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://&amp;lt;FQDN&amp;gt;/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Creating the first user&lt;/strong&gt;:&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F54phu57bqphe63ybtkjx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F54phu57bqphe63ybtkjx.png" alt=" " width="775" height="639"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Add your free license key&lt;/strong&gt;:&lt;br&gt;
 If you have installed Portainer Business Edition, you will now be asked to provide your license key. You will have been provided this when signing up for Business Edition or the free trial. If you don't have a license key, you can either click the Don't have a license? link or get in touch with our team.&lt;br&gt;
Paste the license key you were provided into the box and click Submit.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyn8ih9401bp9qo97tibs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyn8ih9401bp9qo97tibs.png" alt=" " width="743" height="314"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Import Kubernetes cluster&lt;/strong&gt;:&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fps5iinh5jvd0r88we4yc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fps5iinh5jvd0r88we4yc.png" alt=" " width="800" height="358"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Select the Kubernetes option and click Start Wizard. Then select the Import option.&lt;/p&gt;

&lt;p&gt;Enter a name for cluster then click Select a file to browse for your kubeconfig file.  &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3d5249enkzmg4fbxjkyv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3d5249enkzmg4fbxjkyv.png" alt=" " width="800" height="335"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When you're ready, click the Connect button. If you have other environments to configure click Next to proceed, otherwise click Close to return to the list of environments where you will see the progress of your provision. &lt;/p&gt;

&lt;p&gt;From here you can follow the docs to get everything else setup:&lt;br&gt;
&lt;a href="//docs.portainer.io"&gt;Portainer Docs&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Check our more things you can do with portainer at (&lt;a href="https://www.portainer.io/blog" rel="noopener noreferrer"&gt;https://www.portainer.io/blog&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;Get your 5 nodes free license at (&lt;a href="https://www.portainer.io/pricing/take5" rel="noopener noreferrer"&gt;https://www.portainer.io/pricing/take5&lt;/a&gt;)&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>portainer</category>
      <category>k0s</category>
      <category>containers</category>
    </item>
  </channel>
</rss>
