{"id":7164,"date":"2021-03-31T07:04:50","date_gmt":"2021-03-31T07:04:50","guid":{"rendered":"https:\/\/berg-software.com\/?p=7164"},"modified":"2022-07-20T13:06:29","modified_gmt":"2022-07-20T13:06:29","slug":"dynamic-ip-remote-access-solution-python-terraform-gitlab","status":"publish","type":"post","link":"https:\/\/www.berg-software.com\/en\/dynamic-ip-remote-access-solution-python-terraform-gitlab\/","title":{"rendered":"Dynamic IP remote access for software developers with custom-made solution (Python +Terraform +GitLab)"},"content":{"rendered":"<p>[et_pb_section fb_built=&#8221;1&#8243; fullwidth=&#8221;on&#8221; _builder_version=&#8221;4.4.1&#8243;][et_pb_fullwidth_post_title meta=&#8221;off&#8221; featured_placement=&#8221;background&#8221; _builder_version=&#8221;4.7.7&#8243; title_font=&#8221;||||||||&#8221; title_text_color=&#8221;#ff6317&#8243; title_font_size=&#8221;3.5em&#8221; meta_font=&#8221;|300|||||||&#8221; meta_text_color=&#8221;#ffffff&#8221; meta_font_size=&#8221;1em&#8221; background_enable_color=&#8221;off&#8221; use_background_color_gradient=&#8221;on&#8221; background_color_gradient_start=&#8221;rgba(248,248,248,0.85)&#8221; background_color_gradient_end=&#8221;rgba(248,248,248,0.75)&#8221; background_color_gradient_overlays_image=&#8221;on&#8221; min_height=&#8221;20vh&#8221; height=&#8221;400px&#8221; custom_padding=&#8221;6vh||6vh||false|false&#8221; global_module=&#8221;403&#8243; locked=&#8221;off&#8221;][\/et_pb_fullwidth_post_title][\/et_pb_section][et_pb_section fb_built=&#8221;1&#8243; admin_label=&#8221;intro&#8221; _builder_version=&#8221;4.7.7&#8243; custom_padding=&#8221;|||0px||&#8221; locked=&#8221;off&#8221;][et_pb_row use_custom_gutter=&#8221;on&#8221; _builder_version=&#8221;4.4.6&#8243; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;6vh||||false|false&#8221; border_color_left=&#8221;rgba(0,0,0,0)&#8221;][et_pb_column type=&#8221;4_4&#8243; _builder_version=&#8221;4.4.1&#8243;][et_pb_text _builder_version=&#8221;4.7.7&#8243; text_font=&#8221;||||||||&#8221; text_font_size=&#8221;1.1em&#8221; text_line_height=&#8221;1.6em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;16px&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;||||false|false&#8221; hover_enabled=&#8221;0&#8243; border_color_left=&#8221;#ff6317&#8243; sticky_enabled=&#8221;0&#8243;]<\/p>\n<h2>The Dynamic IP challenge<\/h2>\n<p>At Berg Software, we created a Dynamic IP solution that would help us overcome the challenges associated with remote work. When working from home (like everyone else these days), Berg Software\u2019s developers use a VPN to get a secure connection to our on-premises infrastructure. In some cases, this is perfectly sufficient. However, when accessing the non-prod infrastructure, things get a bit more challenging:<\/p>\n<ul>\n<li>First, it\u2019s challenging because of <span style=\"text-decoration: underline;\">the current state of technology<\/span>: hosting the non-prod infrastructure in the cloud (i.e., \u201cIaC\/infrastructure as code\u201d) is currently the norm, not just one of the many options \u2013 so there\u2019s no way around it.<\/li>\n<li>Then, it\u2019s about <span style=\"text-decoration: underline;\">the stakes<\/span>: the non-prod machines (where development, testing, and QA happen) are not something that anyone outside the dev team should have access to (and this includes the client, indexing bots etc.). The non-prod machines are therefore cut from the internet, which makes them generally inaccessible.<\/li>\n<li>Third but not least, <span style=\"text-decoration: underline;\">the VPN itself<\/span>: in order to allow access to a non-prod machine, all firewalls only allow access to specific, precise IP addresses. However, an individual\u2019s IP is not always 100% fixed: one might reset their router or have their service provider reset it \u201cjust because.\u201d In these cases, our Ops team needs to update the IP list \u2013 which can become tedious, slow, and open to error.<\/li>\n<\/ul>\n<p>We, therefore, imagined a Dynamic IP solution that should do two things:<\/p>\n<ul>\n<li>Transform something which we know (i.e., a FQDN\/Fully Qualified Domain Name) into an IP address (needed by the firewalls to create the access rules).<\/li>\n<li>Use a Dynamic DNS service to update the FQDN to the new IP address of the developer machine.<\/li>\n<\/ul>\n<p>Our Dynamic IP solution was an automated, scheduled tool that automates the refreshment of the firewall\/IP list, thus eliminating Ops intervention and reducing deadtimes.<\/p>\n<p>[\/et_pb_text][\/et_pb_column][\/et_pb_row][et_pb_row use_custom_gutter=&#8221;on&#8221; _builder_version=&#8221;4.7.7&#8243; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;6vh||||false|false&#8221; border_color_left=&#8221;rgba(0,0,0,0)&#8221;][et_pb_column type=&#8221;4_4&#8243; _builder_version=&#8221;4.4.1&#8243;][et_pb_text _builder_version=&#8221;4.7.7&#8243; text_font=&#8221;||||||||&#8221; text_font_size=&#8221;1.1em&#8221; text_line_height=&#8221;1.6em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;16px&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_color_left=&#8221;#ff6317&#8243;]<\/p>\n<h2>The dynamic DNS<\/h2>\n<p>In order to select a DNS service, we used that moment\u2019s status quo to develop the requirements:<\/p>\n<ul>\n<li>It should allow us to use subdomains under our main domain name.<\/li>\n<li>It should allow the Ops team to create\/remove subdomains as needed.<\/li>\n<li>It should not require developers to create their own accounts and allow the Ops team to generate the updated, personalised link for each developer into a centralised console.<\/li>\n<li>It should not require the use of any application (\/any complicated process) to update FQDN.<\/li>\n<li>The generated links should have no automatic expiration date. If any of our developers has a static IP address, they should not be required to update their FQDN.<\/li>\n<\/ul>\n<p>Based on these, we went with the Premium DNS package of ClouDNS: it requires our developers to bookmark a link in their regular browser, then click it\/open it to update their respective FQDN entry.<\/p>\n<p>Once the FQDN entry with info on the developer\u2019s updated IP was available, we needed to update the firewall information as soon as the IP address changed. By analysing our regular tools and technologies, we got to combine three of them as follows:<\/p>\n<ul>\n<li><span style=\"text-decoration: underline;\">Python<\/span>: translate the FQDN into an IP address, and keep a list of the last known IP addresses so we can check if anything changed. (Then, only if changed: execute the scripts to update the firewall.)<\/li>\n<li><span style=\"text-decoration: underline;\">Terraform<\/span>: update the firewall on our machines.<\/li>\n<li><span style=\"text-decoration: underline;\">GitLab CI\/CD Scheduler<\/span>: execute the workflow every few minutes.<\/li>\n<\/ul>\n<p>So, let\u2019s look into each one:[\/et_pb_text][\/et_pb_column][\/et_pb_row][et_pb_row use_custom_gutter=&#8221;on&#8221; _builder_version=&#8221;4.7.7&#8243; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;6vh||0px||false|false&#8221; border_color_left=&#8221;rgba(0,0,0,0)&#8221;][et_pb_column type=&#8221;4_4&#8243; _builder_version=&#8221;4.4.1&#8243;][et_pb_text _builder_version=&#8221;4.7.7&#8243; text_font=&#8221;||||||||&#8221; text_font_size=&#8221;1.1em&#8221; text_line_height=&#8221;1.6em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;16px&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_color_left=&#8221;#ff6317&#8243;]<\/p>\n<h2>Python<\/h2>\n<p>We decided to use Python to solve the DNS part of the challenge because:<\/p>\n<ul>\n<li>we have the socket library available,<\/li>\n<li>and we can use <em>socket.gethostbyname(hostname)<\/em> to retrieve the corresponding IP address based on the FQDN.<\/li>\n<\/ul>\n<p>Our script has three main goals:<\/p>\n<ul>\n<li>transform all FQDNs into IP addresses,<\/li>\n<li>check if any of the IP addresses do not match the previous run status, and update if changed,<\/li>\n<li>create a new terraform file based on a template containing the OpenTelekomCloud security group info that includes the updated IP address.<\/li>\n<\/ul>\n<p>Here\u2019s the script extract:[\/et_pb_text][et_pb_text _builder_version=&#8221;4.7.7&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1em&#8221; text_line_height=&#8221;1.3em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;1em&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; background_color=&#8221;#f8f8f8&#8243; custom_margin=&#8221;|5vw||5vw|false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_width_left=&#8221;1px&#8221; border_color_left=&#8221;#bbbbbb&#8221; locked=&#8221;off&#8221;]<\/p>\n<pre class=\"python\" style=\"font-family: monospace;\"><span style=\"color: #808080; font-style: italic;\">#!\/usr\/bin\/env python3<\/span>\n<span style=\"color: #ff7700; font-weight: bold;\">import<\/span> <span style=\"color: #dc143c;\">socket<\/span>\n<span style=\"color: #ff7700; font-weight: bold;\">import<\/span> <span style=\"color: #dc143c;\">sys<\/span>\n<span style=\"color: #ff7700; font-weight: bold;\">import<\/span> <span style=\"color: #dc143c;\">os<\/span>\nipfile <span style=\"color: #66cc66;\">=<\/span> <span style=\"color: #483d8b;\">'ci\/dynip\/oldips'<\/span>\nhostnames <span style=\"color: #66cc66;\">=<\/span> <span style=\"color: black;\">[<\/span><span style=\"color: #483d8b;\">\"DeveloperFirstName.DeveloperLastName.dyn.myDomain.com\"<\/span><span style=\"color: black;\">]<\/span>\nips<span style=\"color: #66cc66;\">=<\/span><span style=\"color: #483d8b;\">\"\"<\/span>\nold_ips<span style=\"color: #66cc66;\">=<\/span><span style=\"color: #483d8b;\">\"\"<\/span>\ni<span style=\"color: #66cc66;\">=<\/span><span style=\"color: #ff4500;\">1<\/span>\n<span style=\"color: #ff7700; font-weight: bold;\">for<\/span> hostname <span style=\"color: #ff7700; font-weight: bold;\">in<\/span> hostnames:\n    <span style=\"color: #ff7700; font-weight: bold;\">print<\/span><span style=\"color: black;\">(<\/span>hostname<span style=\"color: black;\">)<\/span>\n    ip <span style=\"color: #66cc66;\">=<\/span> <span style=\"color: #dc143c;\">socket<\/span>.<span style=\"color: black;\">gethostbyname<\/span><span style=\"color: black;\">(<\/span>hostname<span style=\"color: black;\">)<\/span>\n    ips<span style=\"color: #66cc66;\">=<\/span>ips+<span style=\"color: #483d8b;\">'{ip = \"'<\/span>+ip+<span style=\"color: #483d8b;\">'\/32\",name = \"'<\/span>+hostname+<span style=\"color: #483d8b;\">'\"}'<\/span>\n    <span style=\"color: #ff7700; font-weight: bold;\">if<\/span> i <span style=\"color: #66cc66;\">!=<\/span> <span style=\"color: #008000;\">len<\/span><span style=\"color: black;\">(<\/span>hostnames<span style=\"color: black;\">)<\/span>:\n        ips<span style=\"color: #66cc66;\">=<\/span>ips+<span style=\"color: #483d8b;\">\",<span style=\"color: #000099; font-weight: bold;\">\\n<\/span>\"<\/span>\n    i<span style=\"color: #66cc66;\">=<\/span>i+<span style=\"color: #ff4500;\">1<\/span>\n<span style=\"color: #ff7700; font-weight: bold;\">print<\/span><span style=\"color: black;\">(<\/span><span style=\"color: #483d8b;\">'Discovered ips:'<\/span><span style=\"color: black;\">)<\/span>\n<span style=\"color: #ff7700; font-weight: bold;\">print<\/span><span style=\"color: black;\">(<\/span>ips<span style=\"color: black;\">)<\/span>\n<span style=\"color: #808080; font-style: italic;\">### Here tyo add check if the string ips is different to the one we have in the status file. <\/span>\n<span style=\"color: #808080; font-style: italic;\">### If different then continue - else exit<\/span>\n<span style=\"color: #ff7700; font-weight: bold;\">def<\/span> inplace_change<span style=\"color: black;\">(<\/span>filename_tmpl<span style=\"color: #66cc66;\">,<\/span> filename_dest<span style=\"color: #66cc66;\">,<\/span> old_string<span style=\"color: #66cc66;\">,<\/span> new_string<span style=\"color: black;\">)<\/span>:\n    <span style=\"color: #808080; font-style: italic;\"># Safely read the input filename using 'with'<\/span>\n    <span style=\"color: #ff7700; font-weight: bold;\">with<\/span> <span style=\"color: #008000;\">open<\/span><span style=\"color: black;\">(<\/span>filename_tmpl<span style=\"color: black;\">)<\/span> <span style=\"color: #ff7700; font-weight: bold;\">as<\/span> fr:\n        s <span style=\"color: #66cc66;\">=<\/span> fr.<span style=\"color: black;\">read<\/span><span style=\"color: black;\">(<\/span><span style=\"color: black;\">)<\/span>\n        <span style=\"color: #ff7700; font-weight: bold;\">if<\/span> old_string <span style=\"color: #ff7700; font-weight: bold;\">not<\/span> <span style=\"color: #ff7700; font-weight: bold;\">in<\/span> s:\n            <span style=\"color: #ff7700; font-weight: bold;\">print<\/span><span style=\"color: black;\">(<\/span><span style=\"color: #483d8b;\">'\"{old_string}\" not found in {filename}.'<\/span>.<span style=\"color: black;\">format<\/span><span style=\"color: black;\">(<\/span>**<span style=\"color: #008000;\">locals<\/span><span style=\"color: black;\">(<\/span><span style=\"color: black;\">)<\/span><span style=\"color: black;\">)<\/span><span style=\"color: black;\">)<\/span>\n            <span style=\"color: #ff7700; font-weight: bold;\">return<\/span>\n\u00a0\n        <span style=\"color: #808080; font-style: italic;\"># Safely write the changed content, if found in the file<\/span>\n        <span style=\"color: #ff7700; font-weight: bold;\">with<\/span> <span style=\"color: #008000;\">open<\/span><span style=\"color: black;\">(<\/span>filename_dest<span style=\"color: #66cc66;\">,<\/span> <span style=\"color: #483d8b;\">'w'<\/span><span style=\"color: black;\">)<\/span> <span style=\"color: #ff7700; font-weight: bold;\">as<\/span> fw:\n            <span style=\"color: #ff7700; font-weight: bold;\">print<\/span><span style=\"color: black;\">(<\/span><span style=\"color: #483d8b;\">'Changing \"{old_string}\" to \"{new_string}\" in {filename_tmpl}'<\/span>.<span style=\"color: black;\">format<\/span><span style=\"color: black;\">(<\/span>**<span style=\"color: #008000;\">locals<\/span><span style=\"color: black;\">(<\/span><span style=\"color: black;\">)<\/span><span style=\"color: black;\">)<\/span><span style=\"color: black;\">)<\/span>\n            s <span style=\"color: #66cc66;\">=<\/span> s.<span style=\"color: black;\">replace<\/span><span style=\"color: black;\">(<\/span>old_string<span style=\"color: #66cc66;\">,<\/span> new_string<span style=\"color: black;\">)<\/span>\n            fw.<span style=\"color: black;\">write<\/span><span style=\"color: black;\">(<\/span>s<span style=\"color: black;\">)<\/span>\n<span style=\"color: #808080; font-style: italic;\">## inplace_change('ci\/dynip\/secgroups-dynip-tmpl','secgroups-dynip.tf','!!!ADDRESSES!!!',ips)<\/span>\n<span style=\"color: #ff7700; font-weight: bold;\">def<\/span> read_file<span style=\"color: black;\">(<\/span>filePathOldIps<span style=\"color: black;\">)<\/span>:\n\t<span style=\"color: #ff7700; font-weight: bold;\">with<\/span> <span style=\"color: #008000;\">open<\/span><span style=\"color: black;\">(<\/span>filePathOldIps<span style=\"color: black;\">)<\/span> <span style=\"color: #ff7700; font-weight: bold;\">as<\/span> fr:\n\t\ts <span style=\"color: #66cc66;\">=<\/span> fr.<span style=\"color: black;\">read<\/span><span style=\"color: black;\">(<\/span><span style=\"color: black;\">)<\/span>\n\t\t<span style=\"color: #ff7700; font-weight: bold;\">print<\/span><span style=\"color: black;\">(<\/span><span style=\"color: #483d8b;\">'Old ips:'<\/span><span style=\"color: black;\">)<\/span>\n\t\t<span style=\"color: #ff7700; font-weight: bold;\">print<\/span><span style=\"color: black;\">(<\/span><span style=\"color: #483d8b;\">'\"{s}\" found in \"{filePathOldIps}\".'<\/span>.<span style=\"color: black;\">format<\/span><span style=\"color: black;\">(<\/span>**<span style=\"color: #008000;\">locals<\/span><span style=\"color: black;\">(<\/span><span style=\"color: black;\">)<\/span><span style=\"color: black;\">)<\/span><span style=\"color: black;\">)<\/span>\n\t\t<span style=\"color: #ff7700; font-weight: bold;\">return<\/span> s\n<span style=\"color: #ff7700; font-weight: bold;\">def<\/span> write_file<span style=\"color: black;\">(<\/span>filePathOldIps<span style=\"color: #66cc66;\">,<\/span> newIps<span style=\"color: black;\">)<\/span>:\n\t<span style=\"color: #ff7700; font-weight: bold;\">with<\/span> <span style=\"color: #008000;\">open<\/span><span style=\"color: black;\">(<\/span>filePathOldIps<span style=\"color: #66cc66;\">,<\/span> <span style=\"color: #483d8b;\">'w+'<\/span><span style=\"color: black;\">)<\/span> <span style=\"color: #ff7700; font-weight: bold;\">as<\/span> fw:\n\t\tfw.<span style=\"color: black;\">write<\/span><span style=\"color: black;\">(<\/span>newIps<span style=\"color: black;\">)<\/span>\n\t\t<span style=\"color: #ff7700; font-weight: bold;\">print<\/span><span style=\"color: black;\">(<\/span><span style=\"color: #483d8b;\">'ips written in {filename}.'<\/span><span style=\"color: black;\">)<\/span>\n<span style=\"color: #ff7700; font-weight: bold;\">if<\/span> <span style=\"color: #dc143c;\">os<\/span>.<span style=\"color: black;\">path<\/span>.<span style=\"color: black;\">isfile<\/span><span style=\"color: black;\">(<\/span>ipfile<span style=\"color: black;\">)<\/span>:\n\told_ips <span style=\"color: #66cc66;\">=<\/span> read_file<span style=\"color: black;\">(<\/span>ipfile<span style=\"color: black;\">)<\/span>\n<span style=\"color: #ff7700; font-weight: bold;\">if<\/span> old_ips <span style=\"color: #66cc66;\">==<\/span> ips:\n\t<span style=\"color: #ff7700; font-weight: bold;\">print<\/span><span style=\"color: black;\">(<\/span><span style=\"color: #483d8b;\">'no ip was changed'<\/span><span style=\"color: black;\">)<\/span>\n\t<span style=\"color: #dc143c;\">sys<\/span>.<span style=\"color: black;\">exit<\/span><span style=\"color: black;\">(<\/span><span style=\"color: #ff4500;\">666<\/span><span style=\"color: black;\">)<\/span>\n<span style=\"color: #ff7700; font-weight: bold;\">else<\/span>:\n\t<span style=\"color: #ff7700; font-weight: bold;\">print<\/span><span style=\"color: black;\">(<\/span><span style=\"color: #483d8b;\">'ips were changed'<\/span><span style=\"color: black;\">)<\/span>\n\twrite_file<span style=\"color: black;\">(<\/span>ipfile<span style=\"color: #66cc66;\">,<\/span>ips<span style=\"color: black;\">)<\/span>\n\tinplace_change<span style=\"color: black;\">(<\/span><span style=\"color: #483d8b;\">'ci\/dynip\/secgroups-dynip-tmpl'<\/span><span style=\"color: #66cc66;\">,<\/span><span style=\"color: #483d8b;\">'secgroups-dynip.tf'<\/span><span style=\"color: #66cc66;\">,<\/span><span style=\"color: #483d8b;\">'!!!ADDRESSES!!!'<\/span><span style=\"color: #66cc66;\">,<\/span>ips<span style=\"color: black;\">)<\/span><\/pre>\n<p>[\/et_pb_text][\/et_pb_column][\/et_pb_row][et_pb_row use_custom_gutter=&#8221;on&#8221; _builder_version=&#8221;4.7.7&#8243; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;6vh||0px||false|false&#8221; border_color_left=&#8221;rgba(0,0,0,0)&#8221;][et_pb_column type=&#8221;4_4&#8243; _builder_version=&#8221;4.4.1&#8243;][et_pb_text _builder_version=&#8221;4.7.7&#8243; text_font=&#8221;||||||||&#8221; text_font_size=&#8221;1.1em&#8221; text_line_height=&#8221;1.6em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;16px&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_color_left=&#8221;#ff6317&#8243;]<\/p>\n<h2>Terraform<\/h2>\n<p>To help with the maintenance and change management of our cloud infrastructure, we have been using infrastructure as a code solution from Terraform with the OpenTelekomCloud modules. (This is only because we are using the OTC cloud. The same result can be achieved by AWS \/ Azure with their respective modules.)<\/p>\n<p>Here we got to the next challenges. As the script was run automatically, we needed the possibility to automatically plan, approve and apply the changes only to the specific security group. All other changes would be ignored (e.g., any changes that could be caused by a new base image, or any incompletely checked change committed in the repo).<\/p>\n<p>Terraform is offering an elegant solution to this challenge, as it allows us to only execute a specific terraform file and to automatically approve the application of changes.<\/p>\n<p>This can be achieved using:[\/et_pb_text][et_pb_text _builder_version=&#8221;4.7.7&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1em&#8221; text_line_height=&#8221;1.3em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;1em&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; background_color=&#8221;#f8f8f8&#8243; custom_margin=&#8221;|5vw||5vw|false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_width_left=&#8221;1px&#8221; border_color_left=&#8221;#bbbbbb&#8221; locked=&#8221;off&#8221;]<\/p>\n<pre class=\"text\" style=\"font-family: monospace;\">.\/ci\/app\/terraform  plan -target=opentelekomcloud_compute_secgroup_v2.secgrp_everithyng_dynip -out=project-plan02.dat |&amp; tee -a .\/logs\/plan-dyn-$today.log\n\n.\/ci\/app\/terraform apply -var-file=\".\/ci\/secrets\/secret.tfvars\" -auto-approve -target=opentelekomcloud_compute_secgroup_v2.secgrp_everithyng_dynip |&amp; tee -a .\/logs\/planautopply-dyn-$today.log<\/pre>\n<p>[\/et_pb_text][et_pb_text _builder_version=&#8221;4.7.7&#8243; text_font=&#8221;||||||||&#8221; text_font_size=&#8221;1.1em&#8221; text_line_height=&#8221;1.6em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;16px&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_color_left=&#8221;#ff6317&#8243;]After the terraform changes are applied to the infrastructure, we also have to make sure the terraform.tfstate is automatically committed to the git. This way, next time when terraform gets to execute, it will be aware of the correct status.[\/et_pb_text][\/et_pb_column][\/et_pb_row][et_pb_row use_custom_gutter=&#8221;on&#8221; _builder_version=&#8221;4.7.7&#8243; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;6vh||0px||false|false&#8221; border_color_left=&#8221;rgba(0,0,0,0)&#8221;][et_pb_column type=&#8221;4_4&#8243; _builder_version=&#8221;4.4.1&#8243;][et_pb_text _builder_version=&#8221;4.7.7&#8243; text_font=&#8221;||||||||&#8221; text_font_size=&#8221;1.1em&#8221; text_line_height=&#8221;1.6em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;16px&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_color_left=&#8221;#ff6317&#8243;]<\/p>\n<h2>GitLab CI\/CD<\/h2>\n<p>We use GitLab.com for source control and to run the CI\/CD pipelines from our project. We, therefore, had to enhance the <em>gitlabci<\/em> script to add a new step that can be executed based on a condition. This step should be the only one that\u2019s executed when we start the pipeline from the scheduler.<\/p>\n<p>Step details:[\/et_pb_text][et_pb_text _builder_version=&#8221;4.7.7&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1em&#8221; text_line_height=&#8221;1.3em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;1em&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; background_color=&#8221;#f8f8f8&#8243; custom_margin=&#8221;|5vw||5vw|false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_width_left=&#8221;1px&#8221; border_color_left=&#8221;#bbbbbb&#8221; locked=&#8221;off&#8221;]<\/p>\n<pre class=\"text\" style=\"font-family: monospace;\">dynip-otc:\n  stage: dynip\n  before_script:\n    - apk --no-cache add bash git openssl py-pip\n  script:\n    - .\/ci\/bin\/x_dynsecurity.sh\n  tags:\n    - project\n  artifacts:\n    paths:\n      - .terraform\/\n      - project-plan02.dat\n      - logs\/\n    expire_in: 1 week\n  only:\n    refs:\n        - master\n    variables:\n      - $SCHEDULED_DYNIP =~ \/^true*\/<\/pre>\n<p>[\/et_pb_text][et_pb_text _builder_version=&#8221;4.7.7&#8243; text_font=&#8221;||||||||&#8221; text_font_size=&#8221;1.1em&#8221; text_line_height=&#8221;1.6em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;16px&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_color_left=&#8221;#ff6317&#8243;]We also needed to create a new scheduler in GitLab to execute this step. This can be done from the GitLab interface:[\/et_pb_text][et_pb_image src=&#8221;https:\/\/berg-software.com\/wp-content\/uploads\/DynamicIP-remote-access-GitLab-01.jpg&#8221; alt=&#8221;DynamicIP remote access GitLab 01&#8243; title_text=&#8221;DynamicIP remote access GitLab 01&#8243; _builder_version=&#8221;4.7.7&#8243; max_width=&#8221;60vw&#8221; custom_padding=&#8221;2vh||2vh|2vw|false|false&#8221; locked=&#8221;off&#8221;][\/et_pb_image][et_pb_text _builder_version=&#8221;4.7.7&#8243; text_font=&#8221;||||||||&#8221; text_font_size=&#8221;1.1em&#8221; text_line_height=&#8221;1.6em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;16px&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_color_left=&#8221;#ff6317&#8243;]Finally, we needed to add a new schedule with the following details:[\/et_pb_text][et_pb_image src=&#8221;https:\/\/berg-software.com\/wp-content\/uploads\/DynamicIP-remote-access-GitLab-02.jpg&#8221; alt=&#8221;DynamicIP remote access GitLab 02&#8243; title_text=&#8221;DynamicIP remote access GitLab 02&#8243; _builder_version=&#8221;4.7.7&#8243; max_width=&#8221;60vw&#8221; custom_padding=&#8221;2vh||2vh|2vw|false|false&#8221; locked=&#8221;off&#8221;][\/et_pb_image][\/et_pb_column][\/et_pb_row][et_pb_row use_custom_gutter=&#8221;on&#8221; _builder_version=&#8221;4.7.7&#8243; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;6vh||0px||false|false&#8221; border_color_left=&#8221;rgba(0,0,0,0)&#8221;][et_pb_column type=&#8221;4_4&#8243; _builder_version=&#8221;4.4.1&#8243;][et_pb_text _builder_version=&#8221;4.7.7&#8243; text_font=&#8221;||||||||&#8221; text_font_size=&#8221;1.1em&#8221; text_line_height=&#8221;1.6em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;16px&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_color_left=&#8221;#ff6317&#8243;]<\/p>\n<h2>Conclusion<\/h2>\n<p>By using no more than three simple and accessible tools (Python, Terraform, GitLab), we were able to:<\/p>\n<ul>\n<li>create a Dynamic IP solution that allows our <a href=\"https:\/\/www.berg-software.com\/\">software developers<\/a> uninterrupted work from home conditions,<\/li>\n<li>with virtually real-time IP updates,<\/li>\n<li>with no expansive\/complicated systems,<\/li>\n<li>and no dedicated personnel for manual actions\/updates.<\/li>\n<\/ul>\n<p>How do *you* do it? <a href=\"https:\/\/berg-software.com\/contact-berg-software\/\">Care to share your solution?<\/a>[\/et_pb_text][\/et_pb_column][\/et_pb_row][\/et_pb_section][et_pb_section fb_built=&#8221;1&#8243; _builder_version=&#8221;4.4.1&#8243; custom_padding=&#8221;|||0px||&#8221; locked=&#8221;off&#8221;][et_pb_row use_custom_gutter=&#8221;on&#8221; gutter_width=&#8221;3&#8243; admin_label=&#8221;\uff3f&#8221; _builder_version=&#8221;4.4.6&#8243; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;4vh||8vh||false|false&#8221; border_color_left=&#8221;rgba(0,0,0,0)&#8221;][et_pb_column type=&#8221;4_4&#8243; _builder_version=&#8221;4.4.6&#8243;][et_pb_text _builder_version=&#8221;4.7.7&#8243; text_font=&#8221;||||||||&#8221; text_font_size=&#8221;1.1em&#8221; text_line_height=&#8221;1.6em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;16px&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_color_left=&#8221;#ff6317&#8243;]<\/p>\n<h2>\uff3f<\/h2>\n<p>[\/et_pb_text][\/et_pb_column][\/et_pb_row][\/et_pb_section][et_pb_section fb_built=&#8221;1&#8243; _builder_version=&#8221;3.22&#8243; background_color=&#8221;#eeeeee&#8221; custom_padding=&#8221;50px||50px||false|false&#8221; border_color_top=&#8221;#ff6317&#8243; global_module=&#8221;1642&#8243;][et_pb_row column_structure=&#8221;1_3,1_3,1_3&#8243; _builder_version=&#8221;4.4.1&#8243; custom_padding=&#8221;0px|||||&#8221; locked=&#8221;off&#8221;][et_pb_column type=&#8221;1_3&#8243; _builder_version=&#8221;4.4.1&#8243;][et_pb_social_media_follow _builder_version=&#8221;4.4.4&#8243; text_orientation=&#8221;left&#8221;][et_pb_social_media_follow_network social_network=&#8221;linkedin&#8221; url=&#8221;https:\/\/www.linkedin.com\/company\/berg-computers-srl\/&#8221; _builder_version=&#8221;4.4.4&#8243; background_color=&#8221;#007bb6&#8243; follow_button=&#8221;off&#8221; url_new_window=&#8221;on&#8221;]linkedin[\/et_pb_social_media_follow_network][et_pb_social_media_follow_network social_network=&#8221;twitter&#8221; url=&#8221;https:\/\/twitter.com\/berg_software&#8221; _builder_version=&#8221;4.4.4&#8243; background_color=&#8221;#00aced&#8221; follow_button=&#8221;off&#8221; url_new_window=&#8221;on&#8221;]twitter[\/et_pb_social_media_follow_network][et_pb_social_media_follow_network social_network=&#8221;facebook&#8221; url=&#8221;https:\/\/www.facebook.com\/bergCOMPUTERS&#8221; _builder_version=&#8221;4.4.4&#8243; background_color=&#8221;#3b5998&#8243; follow_button=&#8221;off&#8221; url_new_window=&#8221;on&#8221;]facebook[\/et_pb_social_media_follow_network][et_pb_social_media_follow_network social_network=&#8221;instagram&#8221; url=&#8221;https:\/\/www.instagram.com\/berg_software\/&#8221; _builder_version=&#8221;4.4.4&#8243; background_color=&#8221;#ea2c59&#8243; follow_button=&#8221;off&#8221; url_new_window=&#8221;on&#8221;]instagram[\/et_pb_social_media_follow_network][\/et_pb_social_media_follow][\/et_pb_column][et_pb_column type=&#8221;1_3&#8243; _builder_version=&#8221;4.4.1&#8243;][et_pb_post_nav in_same_term=&#8221;on&#8221; show_next=&#8221;off&#8221; _builder_version=&#8221;4.4.1&#8243; title_text_color=&#8221;#ff6317&#8243; custom_padding=&#8221;|25px|||false|false&#8221;][\/et_pb_post_nav][\/et_pb_column][et_pb_column type=&#8221;1_3&#8243; _builder_version=&#8221;4.4.1&#8243;][et_pb_post_nav in_same_term=&#8221;on&#8221; show_prev=&#8221;off&#8221; _builder_version=&#8221;4.4.1&#8243; title_text_color=&#8221;#ff6317&#8243; custom_padding=&#8221;|||25px|false|false&#8221;][\/et_pb_post_nav][\/et_pb_column][\/et_pb_row][\/et_pb_section][et_pb_section fb_built=&#8221;1&#8243; admin_label=&#8221;CONTACT&#8221; _builder_version=&#8221;4.4.1&#8243; background_color=&#8221;#d2d2d2&#8243; custom_padding=&#8221;75px||75px||false|false&#8221;][et_pb_row column_structure=&#8221;1_4,3_4&#8243; admin_label=&#8221;Service Section Title&#8221; _builder_version=&#8221;4.4.1&#8243; custom_padding=&#8221;||25px||false|false&#8221; animation_direction=&#8221;top&#8221; locked=&#8221;off&#8221;][et_pb_column type=&#8221;1_4&#8243; _builder_version=&#8221;3.25&#8243; custom_padding=&#8221;|||&#8221; custom_padding__hover=&#8221;|||&#8221;][\/et_pb_column][et_pb_column type=&#8221;3_4&#8243; _builder_version=&#8221;3.25&#8243; custom_padding=&#8221;|||&#8221; custom_padding__hover=&#8221;|||&#8221;][et_pb_text _builder_version=&#8221;4.4.1&#8243; text_font=&#8221;|300|||||||&#8221; text_text_color=&#8221;#ffffff&#8221; text_line_height=&#8221;1.1em&#8221; header_5_font=&#8221;|600|||||||&#8221; header_5_text_color=&#8221;#ffffff&#8221; header_5_font_size=&#8221;14px&#8221; header_5_line_height=&#8221;1.5em&#8221; custom_margin=&#8221;||||false|false&#8221;]29 years in business | 2700 software projects | 760 clients | 24 countries<\/p>\n<h5>We turn ideas into software. What is yours?<\/h5>\n<p>[\/et_pb_text][et_pb_text admin_label=&#8221;Title&#8221; _builder_version=&#8221;4.4.6&#8243; header_text_align=&#8221;center&#8221; header_2_font=&#8221;|300|||||||&#8221; header_2_text_align=&#8221;left&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;50px&#8221; header_2_line_height=&#8221;0.9em&#8221; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;25px||25px||false|false&#8221;]<\/p>\n<h2 id=\"Getintouch\">Get in touch<\/h2>\n<p>[\/et_pb_text][\/et_pb_column][\/et_pb_row][et_pb_row column_structure=&#8221;1_4,3_4&#8243; _builder_version=&#8221;4.4.4&#8243;][et_pb_column type=&#8221;1_4&#8243; _builder_version=&#8221;4.4.4&#8243;][\/et_pb_column][et_pb_column type=&#8221;3_4&#8243; _builder_version=&#8221;4.4.4&#8243;][et_pb_contact_form email=&#8221;contact@bergsoftprod.wpengine.com&#8221; custom_message=&#8221;WEBFORM MESSAGE||et_pb_line_break_holder||\uff3f||et_pb_line_break_holder||FROM: %%Name%%||et_pb_line_break_holder||EMAIL: %%Email%%||et_pb_line_break_holder||PHONE NUMBER: %%Phone_number%%||et_pb_line_break_holder||COMPANY: %%Company_name%%||et_pb_line_break_holder||TERMS &amp; CONDITIONS: %%Terms_and_Conditions%%||et_pb_line_break_holder||DATA PRIVACY POLICY: %%Data_Privacy_Policy%%||et_pb_line_break_holder||\uff3f||et_pb_line_break_holder||MESSAGE:||et_pb_line_break_holder||%%Message%%&#8221; success_message=&#8221;Thank you for reaching out! Your message was sent. We will get back to you right away.&#8221; _builder_version=&#8221;4.4.4&#8243; form_field_background_color=&#8221;#d2d2d2&#8243; form_field_text_color=&#8221;#ffffff&#8221; form_field_focus_background_color=&#8221;#ffffff&#8221; form_field_focus_text_color=&#8221;#000000&#8243; title_level=&#8221;h2&#8243; title_font=&#8221;|300|||||||&#8221; title_text_color=&#8221;#ff6317&#8243; title_font_size=&#8221;30px&#8221; form_field_line_height=&#8221;1.5em&#8221; custom_button=&#8221;on&#8221; button_text_size=&#8221;14px&#8221; button_text_color=&#8221;#ffffff&#8221; button_bg_color=&#8221;#ff6317&#8243; button_border_width=&#8221;0px&#8221; button_border_radius=&#8221;0px&#8221; button_icon=&#8221;%%3%%&#8221; button_on_hover=&#8221;off&#8221; border_color_all=&#8221;#ffffff&#8221; border_width_bottom=&#8221;1px&#8221;][et_pb_contact_field field_id=&#8221;Name&#8221; field_title=&#8221;Name *&#8221; fullwidth_field=&#8221;on&#8221; _builder_version=&#8221;4.4.4&#8243; form_field_background_color=&#8221;#d2d2d2&#8243; form_field_text_color=&#8221;#ffffff&#8221; form_field_focus_background_color=&#8221;#ffffff&#8221; form_field_focus_text_color=&#8221;#000000&#8243; border_width_bottom=&#8221;1px&#8221; button_text_size__hover_enabled=&#8221;off&#8221; button_one_text_size__hover_enabled=&#8221;off&#8221; button_two_text_size__hover_enabled=&#8221;off&#8221; button_text_color__hover_enabled=&#8221;off&#8221; button_one_text_color__hover_enabled=&#8221;off&#8221; button_two_text_color__hover_enabled=&#8221;off&#8221; button_border_width__hover_enabled=&#8221;off&#8221; button_one_border_width__hover_enabled=&#8221;off&#8221; button_two_border_width__hover_enabled=&#8221;off&#8221; button_border_color__hover_enabled=&#8221;off&#8221; button_one_border_color__hover_enabled=&#8221;off&#8221; button_two_border_color__hover_enabled=&#8221;off&#8221; button_border_radius__hover_enabled=&#8221;off&#8221; button_one_border_radius__hover_enabled=&#8221;off&#8221; button_two_border_radius__hover_enabled=&#8221;off&#8221; button_letter_spacing__hover_enabled=&#8221;off&#8221; button_one_letter_spacing__hover_enabled=&#8221;off&#8221; button_two_letter_spacing__hover_enabled=&#8221;off&#8221; button_bg_color__hover_enabled=&#8221;off&#8221; button_one_bg_color__hover_enabled=&#8221;off&#8221; button_two_bg_color__hover_enabled=&#8221;off&#8221;][\/et_pb_contact_field][et_pb_contact_field field_id=&#8221;Email&#8221; field_title=&#8221;Email address *&#8221; field_type=&#8221;email&#8221; fullwidth_field=&#8221;on&#8221; _builder_version=&#8221;4.4.4&#8243; button_text_size__hover_enabled=&#8221;off&#8221; button_one_text_size__hover_enabled=&#8221;off&#8221; button_two_text_size__hover_enabled=&#8221;off&#8221; button_text_color__hover_enabled=&#8221;off&#8221; button_one_text_color__hover_enabled=&#8221;off&#8221; button_two_text_color__hover_enabled=&#8221;off&#8221; button_border_width__hover_enabled=&#8221;off&#8221; button_one_border_width__hover_enabled=&#8221;off&#8221; button_two_border_width__hover_enabled=&#8221;off&#8221; button_border_color__hover_enabled=&#8221;off&#8221; button_one_border_color__hover_enabled=&#8221;off&#8221; button_two_border_color__hover_enabled=&#8221;off&#8221; button_border_radius__hover_enabled=&#8221;off&#8221; button_one_border_radius__hover_enabled=&#8221;off&#8221; button_two_border_radius__hover_enabled=&#8221;off&#8221; button_letter_spacing__hover_enabled=&#8221;off&#8221; button_one_letter_spacing__hover_enabled=&#8221;off&#8221; button_two_letter_spacing__hover_enabled=&#8221;off&#8221; button_bg_color__hover_enabled=&#8221;off&#8221; button_one_bg_color__hover_enabled=&#8221;off&#8221; button_two_bg_color__hover_enabled=&#8221;off&#8221;][\/et_pb_contact_field][et_pb_contact_field field_id=&#8221;Phone_number&#8221; field_title=&#8221;Phone number&#8221; required_mark=&#8221;off&#8221; fullwidth_field=&#8221;on&#8221; _builder_version=&#8221;4.4.4&#8243;][\/et_pb_contact_field][et_pb_contact_field field_id=&#8221;Company_name&#8221; field_title=&#8221;Company name&#8221; fullwidth_field=&#8221;on&#8221; _builder_version=&#8221;4.4.4&#8243;][\/et_pb_contact_field][et_pb_contact_field field_id=&#8221;Message&#8221; field_title=&#8221;Message *&#8221; field_type=&#8221;text&#8221; fullwidth_field=&#8221;on&#8221; _builder_version=&#8221;4.4.4&#8243; form_field_background_color=&#8221;#d2d2d2&#8243; form_field_focus_background_color=&#8221;#ffffff&#8221; form_field_focus_text_color=&#8221;#000000&#8243; border_width_bottom=&#8221;1px&#8221; button_text_size__hover_enabled=&#8221;off&#8221; button_one_text_size__hover_enabled=&#8221;off&#8221; button_two_text_size__hover_enabled=&#8221;off&#8221; button_text_color__hover_enabled=&#8221;off&#8221; button_one_text_color__hover_enabled=&#8221;off&#8221; button_two_text_color__hover_enabled=&#8221;off&#8221; button_border_width__hover_enabled=&#8221;off&#8221; button_one_border_width__hover_enabled=&#8221;off&#8221; button_two_border_width__hover_enabled=&#8221;off&#8221; button_border_color__hover_enabled=&#8221;off&#8221; button_one_border_color__hover_enabled=&#8221;off&#8221; button_two_border_color__hover_enabled=&#8221;off&#8221; button_border_radius__hover_enabled=&#8221;off&#8221; button_one_border_radius__hover_enabled=&#8221;off&#8221; button_two_border_radius__hover_enabled=&#8221;off&#8221; button_letter_spacing__hover_enabled=&#8221;off&#8221; button_one_letter_spacing__hover_enabled=&#8221;off&#8221; button_two_letter_spacing__hover_enabled=&#8221;off&#8221; button_bg_color__hover_enabled=&#8221;off&#8221; button_one_bg_color__hover_enabled=&#8221;off&#8221; button_two_bg_color__hover_enabled=&#8221;off&#8221;][\/et_pb_contact_field][et_pb_contact_field field_id=&#8221;Terms_and_Conditions&#8221; field_title=&#8221; &#8221; field_type=&#8221;checkbox&#8221; checkbox_options=&#8221;%91{%22value%22:%22I have read and accepted the Terms and Conditions%22,%22checked%22:0,%22dragID%22:-1}%93&#8243; fullwidth_field=&#8221;on&#8221; _builder_version=&#8221;4.4.4&#8243;][\/et_pb_contact_field][et_pb_contact_field field_id=&#8221;Data_Privacy_Policy&#8221; field_title=&#8221; &#8221; field_type=&#8221;checkbox&#8221; checkbox_options=&#8221;%91{%22value%22:%22I have read and accepted the Data Privacy Policy%22,%22checked%22:0,%22dragID%22:-1}%93&#8243; fullwidth_field=&#8221;on&#8221; _builder_version=&#8221;4.4.4&#8243;][\/et_pb_contact_field][\/et_pb_contact_form][\/et_pb_column][\/et_pb_row][\/et_pb_section]<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Software developers\u2019 remote work is challenged by frequent IP changes? We got a simple custom-made solution w\/ Python, Terraform and GitLab.<\/p>\n","protected":false},"author":16,"featured_media":7166,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_et_pb_use_builder":"on","_et_pb_old_content":"","_et_gb_content_width":"2880","footnotes":""},"categories":[138,33],"tags":[],"class_list":["post-7164","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-how-to","category-insights"],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v16.1.1 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Dynamic IP remote-access solution w\/ Python, Terraform, GitLab<\/title>\n<meta name=\"description\" content=\"The Dynamic IP challenge: When working from home, Berg Software\u2019s developers use a VPN to get a secure connection to our on-premises infrastructure.\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.berg-software.com\/en\/dynamic-ip-remote-access-solution-python-terraform-gitlab\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Dynamic IP remote-access solution w\/ Python, Terraform, GitLab\" \/>\n<meta property=\"og:description\" content=\"The Dynamic IP challenge: When working from home, Berg Software\u2019s developers use a VPN to get a secure connection to our on-premises infrastructure.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.berg-software.com\/en\/dynamic-ip-remote-access-solution-python-terraform-gitlab\/\" \/>\n<meta property=\"og:site_name\" content=\"Berg Software\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/bergCOMPUTERS\/\" \/>\n<meta property=\"article:published_time\" content=\"2021-03-31T07:04:50+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2022-07-20T13:06:29+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.berg-software.com\/wp-content\/uploads\/DynamicIP-custom-made-solution-with-Python-Terraform-GitLab-1200x600px.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"1200\" \/>\n\t<meta property=\"og:image:height\" content=\"600\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@berg_software\" \/>\n<meta name=\"twitter:site\" content=\"@berg_software\" \/>\n<meta name=\"twitter:label1\" content=\"Est. reading time\">\n\t<meta name=\"twitter:data1\" content=\"9 minutes\">\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Organization\",\"@id\":\"https:\/\/www.berg-software.com\/en\/#organization\",\"name\":\"Berg Software\",\"url\":\"https:\/\/www.berg-software.com\/en\/\",\"sameAs\":[\"https:\/\/www.facebook.com\/bergCOMPUTERS\/\",\"https:\/\/www.instagram.com\/berg_software\/\",\"https:\/\/www.linkedin.com\/company\/berg-computers-srl\/\",\"https:\/\/www.youtube.com\/channel\/UCw1FfcRJnC-CoKPwlcM10Iw\",\"https:\/\/twitter.com\/berg_software\"],\"logo\":{\"@type\":\"ImageObject\",\"@id\":\"https:\/\/www.berg-software.com\/en\/#logo\",\"inLanguage\":\"en-US\",\"url\":\"https:\/\/berg-software.com\/wp-content\/uploads\/berg-software-logo.png\",\"contentUrl\":\"https:\/\/berg-software.com\/wp-content\/uploads\/berg-software-logo.png\",\"width\":512,\"height\":512,\"caption\":\"Berg Software\"},\"image\":{\"@id\":\"https:\/\/www.berg-software.com\/en\/#logo\"}},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/www.berg-software.com\/en\/#website\",\"url\":\"https:\/\/www.berg-software.com\/en\/\",\"name\":\"Berg Software\",\"description\":\"We turn ideas into software.\",\"publisher\":{\"@id\":\"https:\/\/www.berg-software.com\/en\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":\"https:\/\/www.berg-software.com\/en\/?s={search_term_string}\",\"query-input\":\"required name=search_term_string\"}],\"inLanguage\":\"en-US\"},{\"@type\":\"ImageObject\",\"@id\":\"https:\/\/www.berg-software.com\/en\/dynamic-ip-remote-access-solution-python-terraform-gitlab\/#primaryimage\",\"inLanguage\":\"en-US\",\"url\":\"https:\/\/www.berg-software.com\/wp-content\/uploads\/DynamicIP-custom-made-solution-with-Python-Terraform-GitLab-1200x600px.jpg\",\"contentUrl\":\"https:\/\/www.berg-software.com\/wp-content\/uploads\/DynamicIP-custom-made-solution-with-Python-Terraform-GitLab-1200x600px.jpg\",\"width\":1200,\"height\":600,\"caption\":\"DynamicIP custom-made solution with Python Terraform GitLab\"},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.berg-software.com\/en\/dynamic-ip-remote-access-solution-python-terraform-gitlab\/#webpage\",\"url\":\"https:\/\/www.berg-software.com\/en\/dynamic-ip-remote-access-solution-python-terraform-gitlab\/\",\"name\":\"Dynamic IP remote-access solution w\/ Python, Terraform, GitLab\",\"isPartOf\":{\"@id\":\"https:\/\/www.berg-software.com\/en\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/www.berg-software.com\/en\/dynamic-ip-remote-access-solution-python-terraform-gitlab\/#primaryimage\"},\"datePublished\":\"2021-03-31T07:04:50+00:00\",\"dateModified\":\"2022-07-20T13:06:29+00:00\",\"description\":\"The Dynamic IP challenge: When working from home, Berg Software\\u2019s developers use a VPN to get a secure connection to our on-premises infrastructure.\",\"breadcrumb\":{\"@id\":\"https:\/\/www.berg-software.com\/en\/dynamic-ip-remote-access-solution-python-terraform-gitlab\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.berg-software.com\/en\/dynamic-ip-remote-access-solution-python-terraform-gitlab\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/www.berg-software.com\/en\/dynamic-ip-remote-access-solution-python-terraform-gitlab\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"item\":{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.berg-software.com\/en\/\",\"url\":\"https:\/\/www.berg-software.com\/en\/\",\"name\":\"Home\"}},{\"@type\":\"ListItem\",\"position\":2,\"item\":{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.berg-software.com\/en\/category\/insights\/\",\"url\":\"https:\/\/www.berg-software.com\/en\/category\/insights\/\",\"name\":\"Insights\"}},{\"@type\":\"ListItem\",\"position\":3,\"item\":{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.berg-software.com\/en\/dynamic-ip-remote-access-solution-python-terraform-gitlab\/\",\"url\":\"https:\/\/www.berg-software.com\/en\/dynamic-ip-remote-access-solution-python-terraform-gitlab\/\",\"name\":\"Dynamic IP remote access for software developers with custom-made solution (Python +Terraform +GitLab)\"}}]},{\"@type\":\"Article\",\"@id\":\"https:\/\/www.berg-software.com\/en\/dynamic-ip-remote-access-solution-python-terraform-gitlab\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/www.berg-software.com\/en\/dynamic-ip-remote-access-solution-python-terraform-gitlab\/#webpage\"},\"author\":{\"@id\":\"https:\/\/www.berg-software.com\/en\/#\/schema\/person\/89bdf6c90f75385d3302334f6171ccd8\"},\"headline\":\"Dynamic IP remote access for software developers with custom-made solution (Python +Terraform +GitLab)\",\"datePublished\":\"2021-03-31T07:04:50+00:00\",\"dateModified\":\"2022-07-20T13:06:29+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/www.berg-software.com\/en\/dynamic-ip-remote-access-solution-python-terraform-gitlab\/#webpage\"},\"publisher\":{\"@id\":\"https:\/\/www.berg-software.com\/en\/#organization\"},\"image\":{\"@id\":\"https:\/\/www.berg-software.com\/en\/dynamic-ip-remote-access-solution-python-terraform-gitlab\/#primaryimage\"},\"articleSection\":\"How to,Insights\",\"inLanguage\":\"en-US\"},{\"@type\":\"Person\",\"@id\":\"https:\/\/www.berg-software.com\/en\/#\/schema\/person\/89bdf6c90f75385d3302334f6171ccd8\",\"name\":\"Eugen Fleseriu\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","_links":{"self":[{"href":"https:\/\/www.berg-software.com\/en\/wp-json\/wp\/v2\/posts\/7164","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.berg-software.com\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.berg-software.com\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.berg-software.com\/en\/wp-json\/wp\/v2\/users\/16"}],"replies":[{"embeddable":true,"href":"https:\/\/www.berg-software.com\/en\/wp-json\/wp\/v2\/comments?post=7164"}],"version-history":[{"count":0,"href":"https:\/\/www.berg-software.com\/en\/wp-json\/wp\/v2\/posts\/7164\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.berg-software.com\/en\/wp-json\/wp\/v2\/media\/7166"}],"wp:attachment":[{"href":"https:\/\/www.berg-software.com\/en\/wp-json\/wp\/v2\/media?parent=7164"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.berg-software.com\/en\/wp-json\/wp\/v2\/categories?post=7164"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.berg-software.com\/en\/wp-json\/wp\/v2\/tags?post=7164"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}